빌드 환경 대청소, 그리고 Node 24
빌드 로그를 보다가 운영 빌드가 Node 20.19.4로 돌고 있다는 걸 알았다. Node 20은 올해 4월 30일에 EOL이라 보안 패치가 더 나오지 않는다. 런타임을 올리기 전에, 그동안 "원래 그런가 보다" 하고 넘겼던 설정부터 하나씩 확인했다.
근거를 붙여 가며 정리한 것들
빌드 설정은 node_modules를 캐시하고 있었는데, 설치 단계가 npm ci였다. npm ci는 시작할 때 기존 node_modules를 지운다. 문서와 npm 소스에서 이 동작을 확인하고 나니, 매 빌드마다 1GB 정도의 캐시를 받아 풀었다가 바로 지우고 있던 셈이었다. npm의 패키지 tarball 캐시를 프로젝트 안 .npm에 두고 --prefer-offline으로 설치하게 바꿨다.
브라우저 Sentry는 통째로 꺼져 있었다. 클라이언트 초기화 코드가 SENTRY_DSN을 읽는데, Next.js는 NEXT_PUBLIC_ 접두사가 붙은 값만 클라이언트 번들에 넣는다. 빌드 산출물을 grep해 보니 DSN 문자열이 0건이었다. 클라이언트만 NEXT_PUBLIC_SENTRY_DSN을 쓰게 하고 서버는 기존 값을 그대로 뒀다. 하나로 합치면 환경 변수 등록을 빠뜨렸을 때 잘 돌던 서버 쪽까지 꺼질 수 있어서다. env 생성 스크립트의 정규식에는 ^ 앵커를 붙였다. 새 이름이 기존 이름을 포함하고 있어서 두 줄이 같이 잡혀 값이 이어 붙었다.
tsconfig의 target: "es5"는 ES2017로 올렸다. 트랜스파일은 SWC가 하고 tsc는 noEmit이라 이 값은 산출물에 아무 영향이 없었다. 타입체커만 막고 있어서, Set을 펼치는 문법을 못 써 Array.from 우회 코드가 생겨 있었다.
jest는 실행 시간대를 Asia/Seoul로 config에 고정했다. 테스트가 기준 시각을 +09:00으로 박아 둔 탓에 UTC 환경에서 12건이 깨졌다. 네 개 시간대에서 돌려 확인했다. React 19에서 더는 매칭되지 않는 console.error 필터도 지웠다. 남아 있으면 모든 스택트레이스가 setup 파일을 가리켰다.
Node 24로
검증은 node-24 브랜치에서만 Node 24로 빌드하도록 조건을 걸어서 했다. 다른 브랜치는 빌드가 그대로다. 실제 배포까지 확인한 뒤 기본값으로 올렸다.
preBuild:
commands:
- nvm install 24 --skip-default-packages
- node -v && npm -v
nvm use 대신 nvm install을 택했다. 빌드 이미지에 들어 있는 24.5.0은 보안 릴리스 4개가 빠진 버전이었다. install은 매 빌드 최신 24.x를 받으니 커밋별 재현성은 조금 잃지만 보안 패치는 따라간다. 그쪽을 골랐다. .nvmrc는 메이저만 적고 engines는 >=24로 뒀다. 정확한 버전만 받을 수 있는 volta 설정은 지웠다. 릴리스가 나올 때마다 조용히 낡아 가는 값이라, Node 20이 EOL까지 남아 있던 것과 같은 일이 반복될 구조였다.
이번 커밋들은 메시지에 근거를 길게 적었다. 문서 줄 번호, 소스 위치, 실측 값까지. 나중에 누가 이 설정을 다시 바꾸려 할 때 같은 조사를 반복하지 않게 하려는 목적이다.
