왜 라이선스 검사가 문제가 되는가
오픈소스 의존성은 프로젝트당 수백 개에 이르고, 각 패키지는 저마다 다른 라이선스를 가진다. 문제는 대부분의 팀이 이를 코드 리뷰에서 육안으로 확인하지 못한다는 점이다. MIT나 Apache-2.0처럼 사업에 무해한 라이선스만 있으면 다행이지만, GPL·AGPL 계열은 배포 시 소스 공개 의무를 유발할 수 있고, 라이선스가 명시되지 않은(UNKNOWN) 패키지는 법적 회색지대로 남는다.
더 까다로운 것은 전이 의존성(transitive dependency)이다. 직접 추가한 라이브러리는 괜찮아도, 그 라이브러리가 끌고 오는 하위 의존성 어딘가에 문제 라이선스가 숨어 있을 수 있다. 사람이 매번 추적하기란 불가능하므로 CI에서 자동화하는 것이 유일하게 지속 가능한 방법이다.
정책을 먼저 정의한다
도구를 붙이기 전에 팀의 정책을 명문화해야 한다. 최소한 세 가지 목록이 필요하다.
- 허용(allow): MIT, BSD-2/3-Clause, Apache-2.0, ISC 등
- 차단(deny): GPL-3.0, AGPL-3.0 등 카피레프트 강제 조항이 있는 것
- 검토 필요(review): LGPL, MPL-2.0, UNKNOWN — 사람이 판단
정책 없이 도구부터 켜면 경고가 수백 개 쏟아지고 팀은 곧 무시하게 된다. 화이트리스트 방식(허용 목록에 없으면 실패)이 블랙리스트보다 안전하지만, 초기 도입 시에는 오탐이 많아 운영 부담이 크다.
파이썬 프로젝트: pip-licenses로 게이트 만들기
파이썬은 pip-licenses로 설치된 패키지의 라이선스를 JSON으로 뽑아 정책과 대조할 수 있다. 다음은 차단 목록에 걸리면 종료 코드 1로 CI를 실패시키는 스크립트다.
#!/usr/bin/env python3
# check_licenses.py
import json, subprocess, sys
DENY = {"GPL-3.0", "GPL-2.0", "AGPL-3.0", "AGPL-3.0-only"}
REVIEW = {"LGPL-3.0", "MPL-2.0", "UNKNOWN"}
out = subprocess.check_output(
["pip-licenses", "--format=json", "--with-license-file=false"]
)
pkgs = json.loads(out)
blocked, flagged = [], []
for p in pkgs:
lic = p["License"].strip()
name = f'{p["Name"]}=={p["Version"]}'
if lic in DENY:
blocked.append(f"{name} -> {lic}")
elif lic in REVIEW:
flagged.append(f"{name} -> {lic}")
for f in flagged:
print(f"::warning:: 검토 필요: {f}")
if blocked:
print("차단된 라이선스 발견:", file=sys.stderr)
for b in blocked:
print(f" - {b}", file=sys.stderr)
sys.exit(1)
print(f"OK: {len(pkgs)}개 패키지 검사 완료")
GitHub Actions에 연결하기
워크플로에서는 PR 단위로 검사를 돌린다. 캐시로 설치를 빠르게 하고, 검사 실패 시 머지를 막도록 필수 체크로 지정한다.
name: license-check
on:
pull_request:
paths:
- "requirements*.txt"
- "pyproject.toml"
jobs:
licenses:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install deps
run: |
pip install -r requirements.txt
pip install pip-licenses
- name: Run license gate
run: python check_licenses.py
paths 필터로 의존성 파일이 바뀐 PR에서만 돌게 하면 불필요한 실행을 줄일 수 있다.
도구 선택 비교
생태계별로 성숙한 도구가 다르다. 언어가 섞인 모노레포라면 SBOM 기반 도구가 유리하다.
| 도구 | 대상 | 특징 |
|---|---|---|
| pip-licenses | Python | 가볍고 스크립트 연동 쉬움 |
| license-checker | Node.js | npm 트리 전체 스캔 |
| Syft + Grype/정책 | 다언어 | SBOM(SPDX/CycloneDX) 생성 기반 |
| FOSSA / ScanCode | 다언어 | 전이·듀얼 라이선스 정밀 분석 |
운영상의 주의점
자동화가 오히려 신뢰를 잃는 경우가 있다. 몇 가지를 미리 막아야 한다.
- 라이선스 표기의 불일치: 같은 라이선스도
Apache 2.0,Apache-2.0,Apache License 2.0으로 제각각 표기된다. SPDX 식별자로 정규화한 뒤 비교해야 오탐이 준다. - 듀얼 라이선스:
MIT OR GPL-3.0처럼 선택 가능한 경우, 단순 문자열 매칭은 무해한 패키지를 차단한다. SPDX 표현식 파싱이 필요하다. - 예외 목록: 특정 패키지를 법무 검토 후 승인했다면 커밋된 allowlist 파일로 관리하고, 승인 근거와 만료일을 함께 남긴다.
마지막으로, CI 검사는 배포 산출물의 라이선스 준수를 보장하지 않는다. 개발 의존성과 실제 배포에 포함되는 런타임 의존성을 구분하고, 후자에 더 엄격한 정책을 적용하는 것이 현실적이다.