License Inventory
Overview
저작권 관리를 위해 실제 산출물에 포함되는 라이브러리·에셋의 라이선스 전수 목록을 조사해 문서화하는 Skill.
package.json 전체 의존성 같은 선언값 추정이 아니라, 아래 4계층 실측 기반:
- JS 번들 — Metro release 소스맵
sources(번들에 포함된 모듈만) - Android native — gradle release 런타임 클래스패스 (APK 포함 아티팩트)
- iOS —
Podfile.lock최상위 pod +Pods/<Pod>/LICENSE원문 - npm 메타데이터 —
node_modules/<pkg>/package.json(설치된 실제 버전)
빌드·배포 도구(fastlane, GPP, Metro 빌드타임, babel, sharp)는 산출물 미포함이므로 제외.
When to Use
- "라이선스 조사", "오픈소스 조사", "저작권 관리" 요청 시
- 앱 출시 전 고지(Attribution) 의무·Copyleft 포함 여부 확인 필요 시
- 의존성 대량 변경 후 컴플라이언스 재점검 시
Prerequisites
- React Native 프로젝트 (
android/,ios/디렉토리 존재) - Android: release 빌드 1회 이상 완료 (소스맵 생성용)
- iOS:
pod install완료 (Podfile.lock존재)
Workflow
1. JS 번들 실측 — 소스맵 sources에서 node_modules 패키지 추출
2. Android native 실측 — gradle RuntimeClasspath 의존성 트리
3. iOS 실측 — Podfile.lock + LICENSE 원문 대조
4. npm 라이선스 확인 — 설치 버전 기준
5. 에셋 확인 — 폰트, Lottie JSON
6. licenses.md 문서화 — 분포 요약 + 고지 의무 + 재생성 명령
Implementation
1. JS 번들 — Metro 소스맵
# 소스맵 존재 확인 (variant는 프로젝트 release variant로 치환)
ls android/app/build/generated/sourcemaps/react/prodRelease/
# sources에서 node_modules 패키지 추출
node -e "const m=JSON.parse(require('fs').readFileSync('<소스맵경로>','utf8'));const s=new Set();for(const x of m.sources){const r=x.match(/node_modules\/(@[^/]+\/[^/]+|[^/]+)/);if(r)s.add(r[1]);}console.log([...s].sort().join('\n'))"
- 이 결과가 "번들에 실제 포함된 JS 패키지" 전체 (직접 + transitive)
- package.json dependencies에 있어도 여기 없으면 번들 미포함
2. Android native — gradle 런타임 클래스패스
cd android && ./gradlew :app:dependencies --configuration prodReleaseRuntimeClasspath --console=plain -q
# group:artifact:version 추출 (해정 화살표 -> 기준으로 실제 반영 버전 확인)
grep -oE '[a-zA-Z0-9._-]+:[a-zA-Z0-9._-]+:[a-zA-Z0-9._+,>-]+' <출력> | sort -u
<releaseVariant>RuntimeClasspath= APK에 포함되는 아티팩트- 트리 내 다중 버전 표기는 빌드 시 단일 버전으로 해정되므로
group:artifact단위로 관리
3. iOS — Podfile.lock + LICENSE 원문
grep -E '^ - [A-Za-z]' ios/Podfile.lock # 최상위 pod 전체
find ios/Pods/<Pod> -maxdepth 1 -iname 'licen*' # LICENSE 원문 경로
- LICENSE 탐색은
ls | grep대신find -maxdepth 1 -iname 'licen*'— COPYING 등 변형 파일명 커버
4. npm 라이선스
node -e "const p=require('<패키지>/package.json');console.log(p.name,p.version,p.license)"
license필드와 LICENSE 원문이 다르면 원문 우선
5. 에셋
| 에셋 | 확인 포인트 |
|---|---|
| 폰트 | SIL OFL 1.1 등 — 고지 의무 있음 |
| Lottie JSON | lottiefiles 출처별 상이 — 미확인 시 ⚠️ 표기 |
OSS Licenses plugin 병행 (Android)
Google 공식 plugin — 빌드타임에 POM <licenses>를 수집해 앱 리소스로 생성. 인벤토리 문서(SoT)와 상호 보완하는 자동화 계층. 공식 가이드: developers.google.com/android/guides/opensource
적용
// android/build.gradle — buildscript.dependencies
classpath("com.google.android.gms:oss-licenses-plugin:0.13.0")
// android/app/build.gradle — 최상위 apply 그룹에 추가
apply plugin: "com.google.android.gms.oss-licenses-plugin"
실행
./gradlew :app:<releaseVariant>OssLicensesTask
# 산출: app/build/generated/res/<variant>OssLicensesTask/raw/third_party_licenses (+ _metadata)
인앱 고지 화면은 런타임 라이브러리(play-services-oss-licenses) 추가 후 OssLicensesMenuActivity를 Intent로 호출 — 별도 단계.
가치 / 한계
| 구분 | 내용 |
|---|---|
| 가치 | POM 라이선스 원문 자동 수집. hermes 등 AAR에 정적 링크된 내부 컴포넌트(ICU4C, RE2, zlib, PCRE 등) 발견 — gradle 의존성 트리에는 나오지 않음 |
| 한계 | POM 보유 Maven 좌표만 수집 — autolink 모듈·jitpack 저장소(예: BlurView) 미수집, 빌드 계열(apksig 등) 과다 포함 가능. 항목명이 POM display name이라 버전 정보 없음 |
정리: plugin = gradle 계층 자동화 + 원문 확보, 인벤토리 문서 = 전 계층(JS/autolink/에셋) SoT.
산출물 구조 (licenses.md)
프로젝트 문서 규칙에 맞는 위치에 작성:
- YAML frontmatter + 문서 타입 명시
- 라이선스 분포 요약 표 — Copyleft(GPL/LGPL/AGPL) 포함 여부를 최상단에 명시
- 계층별 상세 표 (패키지 / 버전 / 라이선스 / 확인 방법)
- 고지(Attribution) 의무 요약:
Apache-2.0 = 전문+저작권·NOTICE / MIT = 저작권 문구 /
BSD-3-Clause = 조건+면책 / BSL-1.0 = 저작권 문구 / SIL OFL 1.1 = 폰트 고지
- 관리 항목 TODO (미확인 항목, 제거 검토 대상)
- 재생성 방법 — 위 4계층 실측 명령을 그대로 기록
Common Mistakes
| 실수 | 결과 | 해결 |
|---|---|---|
| package.json 전체 의존성 기준 조사 | 미번들 패키지까지 과다 포함 | 소스맵 sources 실측 기준 |
| Metro를 포함으로 처리 | 빌드 도구 오포함 | 제외 — 단 metro-runtime(번들 내장 폴리필)만 예외 포함 |
| autolink 모듈 누락 | Maven 좌표 없어 gradle 트리에 안 나옴 | npm 소스 빌드 모듈로 별도 섹션 구성 |
| JS import 0개 = 미포함으로 판단 | 네이티브만 포함된 경우 누락 (예: inappbrowser) | "미사용이나 native 포함" 표기 + 제거 검토 대상 분류 |
| transitive 전부 MIT 가정 | BSD-3 등 예외 누락 (hoist-non-react-statics 사례) | 패키지별 개별 확인 |
ls | grep -i licen 탐색 |
COPYING 등 변형명 누락 | find -maxdepth 1 -iname 'licen*' |
| 에셋 제외 | 폰트(LICENSE 고지 의무) 누락 | 폰트·Lottie 별도 섹션 |
plugin id에서 -plugin suffix 누락 |
Plugin with id not found 에러 |
id는 com.google.android.gms.oss-licenses-plugin |
| plugin 0.10.6 사용 | AGP 신버전에서 부분 크래시 — 빈 metadata·잘린 출력 | 0.13.0+ 사용 |
검증 기준
- Copyleft(GPL/LGPL/AGPL) 포함 0건이면 "0건"으로 명시적 기록
- 각 계층 조사가 실측 기반인지 선언값 기반인지 표에 구분 표기
- 의존성 변경 시 재생성 명령으로 동일 결과 재현 가능해야 함