
git submodule add https://github.com/example/shared-ui.git packages/shared-uimy-project/
.gitmodules ← 서브모듈 URL과 경로 매핑 파일
packages/
shared-ui/ ← 서브모듈 디렉토리 (독립된 git 저장소)
.git ← 실제로는 ../.git/modules/shared-ui를 가리키는 파일
src/
package.json[submodule "packages/shared-ui"]
path = packages/shared-ui ← 로컬 경로
url = https://github.com/example/shared-ui.git ← 원격 URL# 부모 저장소에서 git ls-tree로 확인
git ls-tree HEAD packages/
160000 commit a3f9c2d1b4e8... packages/shared-ui
↑ ↑
gitlink 모드 서브모듈이 가리키는 커밋 SHAgit clone https://github.com/example/my-project.git
cd my-project
ls packages/shared-ui/ # → 비어 있음git submodule init # ← .gitmodules를 읽어서 .git/config에 URL 등록
git submodule update # ← 각 서브모듈을 클론하고 기록된 커밋으로 체크아웃git clone --recurse-submodules https://github.com/example/my-project.git
cd packages/shared-ui
git status
# HEAD detached at a3f9c2d ← 브랜치가 없는 상태[서브모듈에 변경이 생겼을 때]
1. 서브모듈 안에서 최신 커밋으로 이동
cd packages/shared-ui
git pull origin main ← 서브모듈 저장소에서 직접 pull
cd ../..
2. 부모 저장소에 "이 커밋으로 바뀐다"고 기록
git add packages/shared-ui ← gitlink 포인터 업데이트
git commit -m "chore: update shared-ui to v2.3"
git pushgit pull
git submodule update ← 바뀐 포인터가 가리키는 커밋으로 이동{
"dependencies": {
"react": "^18.2.0", ← ^ : 마이너/패치 버전 자동 업데이트
"lodash": "~4.17.21", ← ~ : 패치 버전만 자동 업데이트
"axios": "1.4.0" ← : 정확한 버전 고정
}
}[npm install 실행 흐름]
1. package.json의 버전 범위 읽기
"react": "^18.2.0"
↓
2. npm 레지스트리(npmjs.com)에 쿼리
GET https://registry.npmjs.org/react
→ 사용 가능한 버전 목록 + 각 버전의 메타데이터 반환
↓
3. 버전 범위에 맞는 최신 버전 결정
^18.2.0 → 18.x.x 중 최신 = 18.3.1 (가정)
↓
4. package-lock.json에 정확한 버전 기록
"react": { "version": "18.3.1", "resolved": "...", "integrity": "sha512-..." }
↓
5. tarball 다운로드 및 node_modules에 압축 해제
node_modules/react/ ← 소스 코드가 아닌 빌드된 배포 파일[npm v2 — 중첩 구조]
node_modules/
package-a/
node_modules/
package-c@1.0/ ← A의 C
package-b/
node_modules/
package-c@1.0/ ← B의 C (중복)[npm v3+ — flat 구조]
node_modules/
package-a/ ← 최상위로 끌어올림
package-b/
package-c@1.0/ ← 공유 (단일 설치)[버전 충돌 시]
node_modules/
package-a/
node_modules/
package-c@2.0/ ← A는 2.0이 필요 (별도 설치)
package-b/
package-c@1.0/ ← B는 1.0 사용 (최상위)node_modules/react/
index.js ← CJS 빌드
cjs/
react.development.js
react.production.min.js
package.json ← 진입점, exports 필드
LICENSE
README.md
# .ts 파일 없음 ← 소스 코드는 포함되지 않음my-react-app/
packages/
ui-components/ ← 서브모듈 (소스 .tsx 파일 그대로)
src/
Button.tsx
index.ts
package.json ← 여기 dependencies도 있음
src/
App.tsx// App.tsx
import { Button } from '../packages/ui-components/src'# 서브모듈 안에서 따로 설치해야 함
cd packages/ui-components
npm install
# 또는 부모 프로젝트에서 수동으로 같이 설치
# (package.json에 서브모듈 의존성을 중복 선언)// tsconfig.json (부모)
{
"compilerOptions": {
"paths": {
"@ui/*": ["./packages/ui-components/src/*"]
}
},
"include": [
"src",
"packages/ui-components/src" // ← 명시적으로 포함
]
}packages/ui-components/
src/
Button.tsx
dist/ ← 빌드 결과물
index.js
index.d.ts
package.json
"main": "dist/index.js"
"types": "dist/index.d.ts"# CI에서 빌드 순서
cd packages/ui-components && npm install && npm run build # ← 먼저
cd ../../ && npm run build # ← 그 다음항목 | Git 서브모듈 | NPM 라이브러리 |
참조 대상 | 소스 코드 (특정 커밋 SHA) | 빌드된 패키지 (버전 번호) |
버전 고정 방식 | 커밋 SHA 고정 | package-lock.json으로 고정 |
버전 업데이트 | 수동 (pointer commit 변경) | npm update 또는 버전 범위로 자동 |
코드 수정 가능 여부 | 가능 (서브모듈 안에서 직접 편집) | 불가 (빌드 결과물만 존재) |
저장소 접근 권한 | 서브모듈 저장소에 접근 권한 필요 | 레지스트리 접근 권한만 필요 (공개 패키지는 무조건) |
클론 후 초기 설정 | git submodule init && update 필요 | npm install만 실행 |
CI/CD 설정 | 서브모듈 토큰/접근 설정 별도 필요 | 추가 설정 거의 없음 |
히스토리 공유 | 서브모듈의 git 히스토리 전체 포함 | 배포 파일만 포함, 히스토리 없음 |
적합한 팀 규모 | 소수 팀, 내부 공유 코드 | 제한 없음 |
[서브모듈 적합 시나리오]
my-service/
packages/
design-system/ ← 서브모듈
Button.vue ← 여기서 직접 수정
frontend/
App.vue ← 수정된 Button 바로 반영
# 두 저장소를 하나의 작업 단위로 커밋 가능[NPM 적합 시나리오]
# 외부 팀이 소비만 하는 공유 라이브러리
npm install @company/design-system ← 한 줄로 설치 완료
# CI에서도 추가 설정 없음
npm ci ← package-lock.json 기준 설치# .npmrc에 사설 레지스트리 지정
@company:registry=https://npm.pkg.github.com
# 일반 npm install과 동일하게 사용
npm install @company/shared-ui