의존성 업그레이드: 한 번에 몰아치기 vs 천천히 갈기
dep upgrade 를 한 방에 밀어붙였다가 얻은 교훈과, 그 뒤로 바꾼 전략 정리
한 방에 밀어붙였던 날
작년 말 기준, node_modules 폴더가 마지막 업그레이드로부터 14개월 묵어 있었다. 보안 알림이 쌓이고 있었고, 팀 분위기는 “어차피 한 번에 하는 게 낫지 않아?”였다.
막상 해보니 달랐다. npm-check-updates -u 한 방에 패키지 83개가 올라갔고, CI가 통과했는데 프로덕션 배포 후 특정 페이지에서만 렌더링이 깨졌다. 원인 추적에 이틀 걸렸다. 범인은 react-query v4 → v5 마이그레이션 가이드에 조용히 적혀 있던 breaking change였다. 테스트 커버리지가 얇은 영역이라 CI도 잡지 못했다.
그 경험 이후 “한 번에”는 없다고 정했다.
왜 묵히는지부터 이해해야 한다
솔직히 dep upgrade 를 미루는 이유는 게으름이 아니라 두려움이다. 고쳐야 할 게 쌓일수록 업그레이드 비용이 커진다는 걸 몸이 먼저 안다.
- patch 업데이트조차 안 하는 팀: 이건 대부분 “한 번 건드렸다가 터졌던” 트라우마에서 시작된다. 우리 팀도
axios0.27 → 0.28 패치 때 인터셉터 동작이 미묘하게 달라진 걸 며칠 뒤에야 발견한 적 있다. 그 후 axios 는 아무도 못 건드리는 성역이 됐었다. - major 는 분기에 한 번: “분기에 한 번 몰아서”라는 말은 사실 “영원히 안 한다”의 다른 이름이다. 분기 초마다 항상 더 급한 일이 있었다.
- 보안 픽스만 예외로 처리하는 관행: 이게 제일 현실적인 출발점이다. CVSS 7.0 이상은 무조건 이번 주, 나머지는 다음 스프린트 — 이 기준을 명문화하는 것만으로 팀 불안감이 줄어든다.
분류 기준을 먼저 세우지 않으면 어떤 전략도 실행 단계에서 흐지부지된다.
세 줄기로 나눠서 흘리는 방법
지금 우리가 쓰는 방식은 세 줄기 분리다. 하나의 PR에 모든 변경을 담지 않는다.
- patch/minor 자동화: Renovate 를 붙여 patch 는 자동 머지, minor 는 PR 생성 후 담당자 확인. 처음에는 PR 이 하루에 10개씩 올라와서 소음 취급받았다.
automerge: true+schedule: ["after 10pm on monday"]로 묶어서 주 1회 배치로 돌리니 노이즈가 사라졌다. - major 는 독립 브랜치: major 하나당 브랜치 하나, PR 하나. 이유는 단순하다 — 롤백할 때 어느 변경이 원인인지 즉시 특정해야 한다.
webpackv4 → v5 때 빌드 타임이 오히려 40% 늘었는데, 독립 브랜치였기 때문에 바로 revert 하고 원인 분석을 따로 할 수 있었다. - 프레임워크 core 는 별도 타임박스: React, Next.js 같은 건 분기 한 번 지정일에만 건드린다. 단, 그 날을 달력에 먼저 박아놓고 스프린트 용량을 비워둔다. “분기 마지막 금요일 오전”이라고 고정해두니 실제로 실행률이 올라갔다.
테스트가 없으면 전략이 없다
어떤 업그레이드 전략도 테스트 커버리지가 바닥이면 의미 없다. 뻔한 말이지만, 진짜다.
- 중요 페이지 E2E 먼저: 유닛 테스트 커버리지 80%가 있어도
react-querybreaking change 를 못 잡았다. 결제 플로우, 로그인, 핵심 CRUD — 이 세 가지 E2E가 있었으면 2일 삽질은 없었다. 그 뒤로 Playwright 시나리오 12개를 먼저 만들었고, 그다음 업그레이드 배치에서 실제로 2건을 사전 차단했다. - 스냅샷 테스트는 업그레이드 때 독이 된다: 스냅샷이 많을수록 minor 업데이트 때 diff 가 폭발해서 리뷰어가 무감각해진다. 우리는 스냅샷을 UI 컴포넌트에서 거의 제거하고, 대신 주요 API 응답 형태만 타입 체크로 잡는 방향으로 바꿨다.
커버리지 숫자보다 “이 테스트가 진짜 breaking change 를 잡을 수 있나”가 기준이어야 한다.
롤백 계획이 먼저다
업그레이드 전에 “어떻게 되돌릴 것인가”를 먼저 적는 게 순서다. 사후에 생각하면 항상 늦다.
- 배포 전 DB 마이그레이션 분리: ORM major 업그레이드 때 스키마 변경이 함께 들어간 적 있다. 애플리케이션 롤백은 했는데 DB 는 이미 앞으로 가 있어서 한동안 버전 불일치 상태로 운영했다. 지금은 스키마 변경과 코드 변경을 배포 단계로 분리한다.
- feature flag 로 격리: 기능 단위로 올릴 수 있으면 flag 로 감싸고, 문제 생기면 코드 배포 없이 끈다. 모든 dep 변경에 적용은 못 하지만, 외부 API client 라이브러리 교체 때는 효과가 확실했다.
계획 없는 업그레이드는 계획 없는 롤백으로 이어진다. 롤백이 무서우면 업그레이드도 안 하게 된다.
다음 한 가지
다음 달 Next.js major 업그레이드 전에, 롤백 절차를 README 에 먼저 적고 시작할 것.
🛒 이 글과 어울리는 추천 상품
위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.