도메인 API 일괄 전환과 same-origin 프록시
이달 초 파일럿 도메인이 운영 시나리오 검증을 통과했다. 어제는 나머지 도메인을 전부 authFetch로 옮겼고, 오늘은 그 위에 같은 오리진 프록시를 얹었다.
병렬로 나눠 옮기기
도메인은 회원, 상품, 그룹 수업, 직원, 스케줄과 계약서, 메시지, 센터, 통계, 키오스크, 출석, 락커 등이었다. 도메인마다 할 일은 같았다. API 함수가 토큰을 직접 붙이던 걸 authFetch로 바꾸고, 훅과 컴포넌트에 있던 토큰 존재 검사(useAccessToken)를 걷어내고, 실패 응답을 객체로 돌려주던 것을 ApiError throw로 통일한다.
그래서 도메인 단위로 작업을 나눠 에이전트들에게 병렬로 맡겼다. 모두가 같은 작업 계약 문서를 따른다. 엔드포인트는 API 문서와 대조하되 경로가 다르면 고치지 말고 보고만 하게 했다. 나는 통합을 맡았다. 브랜치마다 변경 범위가 계약을 벗어나지 않았는지, tsc와 jest가 통과하는지 확인하고 --no-ff로 합쳤다.
되돌아가지 않게 장치도 걸었다. 백엔드 주소 환경 변수를 직접 읽는 코드를 ESLint no-restricted-syntax로 막았다. 처음엔 warn으로 넣어 176개 파일을 세어 두고, 전환이 끝난 뒤 error로 올렸다. 마지막으로 남은 공통 에러 헬퍼와 토큰 훅을 지웠다. SSE는 authFetch로 감쌀 수 없어서 같은 토큰 캐시에서 헤더를 받아 쓰고, 401이면 한 번 갱신한 뒤 재연결하게 했다.
같은 오리진 프록시
다음 단계는 브라우저가 백엔드를 직접 부르지 않게 하는 것이었다. Next.js 라우트 핸들러로 프록시를 만들고, Authorization은 서버가 httpOnly 쿠키에서 꺼내 붙인다. authFetch 쪽은 baseUrl 한 줄만 바뀌었다. 7월에 이음새를 만들어 둔 덕이다.
// before
baseUrl: process.env.NEXT_PUBLIC_BASE_URL ?? '',
// after: 같은 오리진의 프록시 라우트를 거친다
baseUrl: PROXY_PATH,
붙이자마자 버그가 셋 나왔다. 브라우저는 같은 오리진 GET에 Origin 헤더를 보내지 않아서 CSRF 검사가 모든 GET을 403으로 막았다. Node의 fetch는 204 응답에 body를 실으면 throw해서 삭제 API가 502가 됐다. 경로 세그먼트는 디코딩된 값으로 들어와서 다시 인코딩해야 했다.
preview 환경에서 재 보니 플랫폼 제약도 있었다. 프록시를 거치면 5MB부터 413이 났다. 이미지 여러 장을 한 번에 올리는 요청이 걸려서 업로드 두 종은 bypassProxy로 백엔드에 직접 보낸다. 토큰과 401 복구는 여전히 authFetch가 맡는다. 응답에는 약 30초 상한이 있었다. 응답 전체를 버퍼링하던 걸 스트리밍으로 바꿔 봤지만 SSE는 30초에 청크 0건으로 끊겼다. SSE는 직접 연결로 두고, 오래 걸릴 수 있는 엑셀과 리포트 조회도 우회 목록에 넣었다.
미들웨어가 하던 silent refresh는 지웠다. 이제 토큰 갱신은 authFetch 한 곳에서만 일어난다. 프론트 논의 끝에 프록시는 REST 전용으로 유지하기로 했다.
