의존성 업데이트
목표는 의존성이 최신 상태이고 알려진 취약점이 없으며, 사용자가 신뢰할 수 있는 변경 사항을 제공하는 것입니다. 모든 단계는 프로젝트 자체 검증을 통해 확인되며, 각 위험한 단계는 독립적으로 검토되거나 되돌릴 수 있도록 분리됩니다.
의존성 작업은 일반적으로 두 가지 방식으로 실패합니다. 하나는 '일괄 업그레이드'로, 모든 것을 한 번에 업데이트하다가 문제가 발생하면 어떤 업데이트가 원인인지 알 수 없게 됩니다. 다른 하나는 --force, --legacy-peer-deps, 버전 범위 완화, 또는 권고 사항을 무시하는 등 문제를 숨기는 '수정'입니다. 아래 워크플로는 이 두 가지를 모두 피하도록 설계되었습니다.
0. 요청 사항 파악
- 확인만 ('취약점이 있나요?', '무엇이 오래되었나요?'): 1~3단계를 수행하고, 보고한 후 중단합니다. 파일을 수정하지 마세요.
- 업데이트 ('의존성 업데이트', '감사 경고 수정', '패키지 최신 상태 유지'의 기본값): 전체 워크플로를 수행합니다.
- 하나의 패키지 ('vite를 v8로 업그레이드'): 1
2단계를 수행한 다음, 해당 패키지(함께 업데이트해야 하는 패키지 포함)에 대해 6단계를 수행하고, 78단계를 수행합니다.
1. 사전 점검
-
리포지토리의 기여 문서(
AGENTS.md,CLAUDE.md,CONTRIBUTING.md)와 CI 설정(.github/workflows/*)을 읽어 검증 명령(린트, 타입체크, 테스트, 빌드)과 커밋 메시지 규칙을 파악합니다. CI에서 실제로 실행하는 명령을 사용하세요. 문서화된 것이 없으면 매니페스트에 정의된 스크립트를 사용합니다. -
작업 트리가 깨끗한지 확인합니다. 그렇지 않으면 중단하고 질문합니다. 사용자의 커밋되지 않은 작업을 의존성 커밋에 섞으면 심각한 문제가 발생할 수 있습니다.
-
잠금 파일과 매니페스트를 통해 생태계를 감지합니다(리포지토리에 여러 개 있을 수 있음):
파일 참조 package-lock.json,pnpm-lock.yaml,yarn.lock,bun.lockreferences/node.mdrequirements*.txt,pyproject.toml,uv.lock,poetry.lock,Pipfile.lockreferences/python.mdCargo.toml/Cargo.lockreferences/rust.mdgo.mod/go.sumreferences/go.md기타(Gemfile.lock, composer.lock, ...) 해당 생태계의 자체 도구와 감사용 scripts/osv_check.py사용해당 참조 파일을 읽으세요. 정확한 명령과 해당 생태계의 함정이 나열되어 있습니다.
-
프로젝트가 대상으로 하는 런타임 버전을 찾습니다:
.nvmrc,engines,.python-version,requires-python,rust-version,go지시문, 또는 CI의 버전. 일부 패키지는 최신 릴리스가 아닌 런타임을 따라야 합니다. 예를 들어@types/node는 프로젝트가 실행되는 Node 메이저 버전과 일치해야 하므로, 더 새로운 Node 메이저 버전으로 업데이트하면 코드가 프로덕션에서 충돌하는 API를 사용할 수 있습니다. '최신'을 대상이 아닌 후보로 취급하세요. -
변경하기 전에 검증 명령을 실행하고 결과를 기록합니다. 이미 실패한 것이 있다면 지금 알아야 합니다. 그렇지 않으면 이미 존재했던 실패를 업그레이드 탓으로 돌리거나, 빨간 브랜치를 배포할 수 있습니다. 기준선이 빨간색이면 사용자에게 알리고 계속할지 물어보세요.
2. 목록 작성
생태계 도구를 사용하여 두 가지 목록을 수집합니다(참조 파일 참조):
- 취약점: 기본 감사 도구. 설치되어 있지 않고 임시로 실행할 수 없는 경우 번들 스캐너를 사용하세요:
python3 <skill-dir>/scripts/osv_check.py <lockfile>...(osv.dev에 쿼리, 표준 라이브러리만 사용). 기본 도구와 함께 OSV 스캔을 실행하는 것은 비용이 적게 들고 종종 가치가 있습니다. 기본 도구가 중첩되거나 중복된 패키지 복사본의 권고 사항을 놓칠 수 있기 때문입니다. - 오래된 패키지: 현재 → 원하는 버전(선언된 범위 내) → 최신.
각 취약점에 대해 어떤 직접 의존성이 취약한 패키지를 가져오는지, 그리고 해당 직접 의존성의 어떤 버전이 수정하는지 파악합니다. 사용자는 직접 의존성에 대해 작업합니다. 전이적 패키지 이름만으로는 실행 가능하지 않습니다.
3. 계획 및 보고
모든 항목을 세 개의 버킷으로 분류하고 변경을 수행하기 전에 짧은 표로 계획을 보여줍니다:
- 보안 수정: 권고 사항을 제거하는 모든 것. 가장 시급합니다. 수정에 주요 업데이트가 필요한 경우 주요 업데이트(버킷 3)에 해당하지만, 먼저 나열하고 보안에 의한 것임을 명시하세요.
- 안전한 업데이트: semver 내의 패치/마이너(0.x 패키지의 경우 마이너는 호환성이 깨지는 변경으로 간주).
- 주요 업데이트: 그 외 모든 것, 각각 한 행씩. 함께 업데이트해야 하는 패키지(프레임워크와 플러그인, 도구와 타입 정의, 피어 의존성을 공유하는 패키지)를 그룹화하고 각 그룹을 하나의 단위로 취급합니다.
우선순위는 심각도에 따라 결정되며, 설명에는 영향 범위가 중요합니다. 개발 전용 테스트 러너의 중요 권고 사항도 수정할 가치가 있지만, 사용자에게 경보를 울리기보다는 개발 전용임을 분명히 말하세요. 확인만 요청하는 경우 이 보고서가 결과물입니다. 심각도, 권고 ID, 의존성 경로 및 수정 버전을 포함하세요.
4. 브랜치
최신 기본 브랜치에서 chore/deps-YYYY-MM-DD(오늘 날짜) 브랜치를 생성합니다. 단, 사용자가 이미 이 작업을 위해 만든 브랜치에 있는 경우는 예외입니다.
5. 보안 수정 및 안전한 업데이트
- 범위 내 보안 수정을 적용하고, 검증 명령을 다시 실행한 후 커밋합니다.
- 안전한 패치/마이너 업데이트를 함께 적용하고, 검증 명령을 다시 실행한 후 커밋합니다. 검증에 실패하면 이분법을 수행합니다: 배치의 절반을 되돌리고 원인을 찾을 때까지 재시도한 다음, 해당 업데이트를 분리하여 주요 업데이트처럼 처리합니다.
패키지 매니저가 잠금 파일을 작성하도록 합니다. 잠금 파일을 수동으로 편집하지 마세요.
6. 주요 업데이트, 한 번에 하나씩
각 주요 단위에 대해 순서대로(보안에 의한 것 먼저, 그 다음 의존하는 것보다 먼저 기본 도구, 예: 린터 플러그인보다 컴파일러 먼저):
- 변경 사항을 읽습니다. 건너뛰는 모든 주요 버전에 대한 마이그레이션 가이드 또는 릴리스 노트를 찾습니다(v2→v5는 v3, v4 및 v5를 읽는 것을 의미). 좋은 출처는 프로젝트의
CHANGELOG.md, GitHub 릴리스, 문서 사이트의 '마이그레이션' 페이지입니다. 이 코드베이스에 적용되는 호환성이 깨지는 변경 사항을 추출합니다: 제거되거나 이름이 변경된 API 및 설정 키를 검색합니다. - 피어를 확인합니다. 새 버전이 주변의 모든 피어 및 엔진 요구 사항을 충족하는지 확인합니다. 때로는 플러그인이 따라잡지 못해서 최신 주요 버전을 아직 채택할 수 없습니다. 그런 다음 작동하는 최신 버전을 사용하고, 나머지를 차단하는 내용을 기록합니다.
- 업그레이드 및 수정합니다. 단위를 업데이트하고, 마이그레이션(설정 이름 변경, API 변경, 프로젝트에서 제공하는 경우 codemod)을 적용하고, 검증 명령을 통과할 때까지 다시 실행합니다. 코드를 적절히 수정하세요. 테스트를 약화시키거나,
skip을 추가하거나,any또는@ts-ignore스타일의 이스케이프로 타입 오류를 무음 처리하여 통과시키지 마세요. - 단위를 자체적으로 커밋하고, 본문에 마이그레이션 노트를 포함합니다.
- 중단할 시점을 알아야 합니다. 단위에 대규모 리팩토링이나 제품 결정(제거된 기능, 확인할 수 없는 변경된 런타임 동작)이 필요하거나, 합당한 노력으로 통과할 수 없는 경우, 마지막 커밋으로 완전히 되돌리고(
git checkout -- .) 특정 차단 이유와 함께 연기된 것으로 기록합니다. 명확한 이유가 있는 연기된 주요 업데이트는 좋은 결과입니다. 반쯤 완료된 것은 그렇지 않습니다.
7. 재확인 및 재감사
최종 상태에서 전체 검증 명령과 감사를 다시 실행합니다. 모든 권고 사항은 이제 수정되거나 설명되어야 합니다. 수정할 수 없는 권고 사항:
- 전이적이고 상위 패키지가 수정 버전을 릴리스하지 않은 경우, 패치된 전이적 버전을 고정하는 재정의/해결은 상위 패키지가 예상하는 것과 semver 호환되는 경우에만 허용됩니다. 이를 검증하고 PR에 나중에 제거할 수 있도록 기록하세요.
- 패치된 버전이 어디에도 존재하지 않는 경우, 그렇게 말하고 취약한 코드 경로가 이 프로젝트에서 도달 가능한지 평가하세요.
- 감사를 무음으로 통과시키기 위해 무시/허용 목록 메커니즘을 사용하지 마세요.
8. 전달
커밋은 리포지토리의 규칙을 따릅니다. 리포지토리가 Conventional Commits을 사용하고 더 구체적인 내용을 명시하지 않은 경우, 다음을 사용하세요:
fix(deps): ...: 런타임 의존성에 대한 보안 수정build(deps): ...: 업그레이드 및 개발 전용 도구에 대한 보안 수정- 요약은 패키지와 대상을 명명합니다(예:
build(deps): vitest를 v5로 업데이트). 본문에는 수정된 권고 사항과 수행된 마이그레이션 변경 사항이 나열됩니다.
사용자가 달리 요청하거나 원격이 없는 경우를 제외하고 푸시하고 PR을 엽니다(gh pr create). 다음 본문을 사용하세요:
## 요약
<한두 문장: 이동한 내용, 해결된 권고 사항 수>
## 보안
| 권고 사항 | 심각도 | 패키지 (경로) | 수정 버전 |
|---|---|---|---|
## 업데이트
| 패키지 | 이전 버전 | 이후 버전 | 유형 | 비고 |
|---|---|---|---|---|
<유형 = security / patch / minor / major; 비고 = 수행된 마이그레이션 변경 사항>
## 연기됨
| 패키지 | 최신 버전 | 지금 하지 않는 이유 |
|---|---|---|
<차단된 피어, 리팩토링 필요, 런타임 고정, ...; 없으면 '없음'>
## 남아있는 권고 사항
<없음, 또는 각각의 이유와 영향 범위 평가>
## 검증
<최종 커밋에서 실행된 정확한 명령과 결과>
PR 링크, 수정된 권고 사항 수와 남아있는 권고 사항 수, 그리고 연기된 주요 업데이트에 대한 한 줄 설명을 포함한 짧은 채팅 요약으로 마무리합니다.