Yarn을 이용한 오프라인 패키지 관리 (Offline Mirror)

보안이 중요한 금융권이나 폐쇄망 환경의 프로젝트에서는 인터넷을 통해 npm install을 실행할 수 없습니다. 이때 Yarn의 '오프라인 미러(Offline Mirror)' 기능을 사용하면, 프로젝트에 필요한 모든 패키지를 특정 폴더에 저장하고 인터넷 연결 없이도 의존성을 설치할 수 있습니다.


Yarn Classic (v1) 설정 방법 

Yarn Classic (v1.x)을 사용하신다면 아래 방법이 정석입니다. 이해하기 쉽도록 용어와 설명을 조금 더 보강했습니다.

초기 설정

  1. .yarnrc 파일 생성: 프로젝트 최상위 경로(root)에 .yarnrc 파일을 만들고 아래 내용을 추가합니다.
    • yarn-offline-mirror: 패키지 파일(.tgz)들을 저장할 로컬 폴더 경로를 지정합니다. 여기서는 npm-packages-offline 폴더가 됩니다.
    • yarn-offline-mirror-pruning: yarn.lock 파일에서 제거된 패키지가 오프라인 미러 폴더에서도 자동으로 삭제되도록 설정합니다.
  2.  
    # .yarnrc
    yarn-offline-mirror "./npm-packages-offline"
    yarn-offline-mirror-pruning true
    
  3. YAML
     
  4. 오프라인 미러 생성: 터미널에서 다음 명령어를 순서대로 실행합니다.
    • 이제 package.json에 명시된 모든 의존성 패키지 파일들이 npm-packages-offline 폴더에 저장되고, yarn.lock 파일이 생성(또는 업데이트)됩니다.
  5. Bash
    yarn cache clean
    yarn install
    
  6. 형상 관리: .yarnrc, yarn.lock, npm-packages-offline 폴더, package.json을 모두 Git 같은 버전 관리 시스템에 커밋하여 팀원들과 공유합니다.

패키지 추가/업데이트 시

  • 패키지를 변경한 사람 (온라인 환경)
    1. 필요한 패키지를 추가합니다 (yarn add [package-name]).
    2. yarn install을 실행하여 yarn.lock과 npm-packages-offline 폴더를 최신 상태로 업데이트합니다.
    3. 변경된 package.json, yarn.lock, npm-packages-offline 폴더를 커밋합니다.
    💡 Tip: 기존 node_modules를 굳이 삭제할 필요는 없습니다. yarn install이 yarn.lock을 기준으로 변경사항을 똑똑하게 반영해줍니다.
  • 변경사항을 받은 사람 (오프라인 환경)
    1. 최신 버전의 소스 코드를 받습니다 (git pull 등).
    2. 기존 node_modules 폴더를 삭제합니다 (충돌 방지를 위해 권장).
    3. --offline 플래그와 함께 명령어를 실행합니다.이 명령어는 인터넷에 연결하는 대신 npm-packages-offline 폴더의 패키지를 사용하여 node_modules를 구성합니다.
    4.  
      yarn install --offline
      

 

Offline packages 폴더가 200mb 정도 되는데 git commit 해도 문제가 없을까?

200MB 정도의 offline-packages 폴더를 Git에 커밋하는 것은 일반적인 방법이며, 오프라인 환경 구축이라는 목적을 위해서는 괜찮습니다. 하지만 발생할 수 있는 문제점과 더 나은 대안을 명확히 알고 결정해야 합니다.

1. 직접 커밋해도 괜찮은 이유 (목적 부합)

Yarn의 오프라인 미러 기능의 핵심은 코드와 의존성 패키지를 하나로 묶어 관리하는 것입니다. Git에 offline-packages 폴더를 함께 커밋하면 다음과 같은 장점이 있습니다.

  • 단순함: git pull 한 번으로 코드와 모든 의존성 패키지가 준비되어, 별도의 전달 과정 없이 바로 yarn install --offline을 실행할 수 있습니다.
  • 재현성 보장: 특정 커밋 시점의 코드와 그 코드가 사용했던 패키지 버전이 완벽하게 일치하므로, 빌드 환경이 달라져서 생기는 문제를 원천적으로 차단합니다.

200MB는 요즘 Git 리포지토리에서 아주 큰 용량은 아니므로, 프로젝트의 규모가 급격하게 커지지 않는다면 당장 큰 문제가 되지는 않습니다.


2. 발생할 수 있는 문제점 (단점)

하지만 이 방식은 프로젝트가 커지고 오래될수록 다음과 같은 단점을 드러냅니다.

  • ⚠️ 리포지토리 용량 급증: 새로운 패키지를 추가하거나 업데이트할 때마다 리포지토리의 전체 크기가 계속해서 커집니다. git clone 이나 git pull을 할 때마다 전송해야 할 데이터가 많아져 속도가 느려집니다.
  • ⚠️ Git 성능 저하: Git은 텍스트 파일의 변경사항을 추적하는 데 최적화되어 있습니다. 패키지 파일과 같은 바이너리 파일이 많아지면 Git의 내부적인 데이터 처리 효율이 떨어질 수 있습니다.
  • ⚠️ 삭제의 어려움: 나중에 특정 패키지를 삭제해도 Git 히스토리에는 해당 파일이 계속 남아있어 리포지토리 용량은 줄어들지 않습니다. (히스토리를 재작성하는 것은 매우 복잡하고 위험합니다.)

Offline 폴더가 변경이 없으면 이력 관리할 때 용량이 급증하거나 그러지는 않겠지? 변경된 부분만 관리할 것 이니까?

offline 폴더의 내용이 전혀 변경되지 않는다면, 새로운 커밋을 해도 리포지토리의 용량은 거의 늘어나지 않습니다.

Git은 말씀하신 대로 변경된 부분(diff)만 관리하기 때문에, 이미 커밋된 파일에 변화가 없다면 해당 파일에 대한 추가 용량을 차지하지 않습니다. Git은 내부적으로 해당 파일의 이전 커밋 기록을 가리키는 포인터만 추가할 뿐입니다.


하지만 문제가 발생하는 시점은 '변경'이 일어날 때입니다.

사용자의 이해가 맞지만, 장기적인 관점에서 우리가 우려하는 부분은 바로 이 '변경'의 순간입니다.

  • 패키지 업데이트 시: lodash 4.17.20 버전을 4.17.21 버전으로 업데이트했다고 가정해 보겠습니다.
    • Git의 작동 방식: Git은 기존 lodash-4.17.20.tgz 파일을 그대로 히스토리에 남겨둔 채, 완전히 새로운 lodash-4.17.21.tgz 파일을 통째로 추가합니다. Git의 관점에서는 이름이 다른 별개의 바이너리 파일일 뿐입니다.
    • 결과: 리포지토리 용량은 (기존 용량) + (새로운 lodash 패키지 용량) 만큼 증가합니다. 이전 버전의 패키지 파일은 히스토리에서 삭제되지 않고 계속 쌓이게 됩니다.
  • 패키지 추가/삭제 시: 새로운 패키지를 추가하면 그 파일의 용량만큼 리포지토리가 커집니다. 패키지를 삭제하고 커밋해도, 그 파일은 히스토리에서 사라지지 않기 때문에 리포지토리의 전체 용량은 줄어들지 않습니다.
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기