승인
조건부 라이선스(LGPL, MPL, EPL, CDDL)를 보유한 컴포넌트는 승인 워크플로우를 트리거합니다. 빌드는 진행되지만 충분한 권한을 가진 검토자가 처리할 때까지 컴포넌트는 Approvals 페이지에 등장합니다.
승인을 요청하는 엔지니어; 요청을 처리하는 법무·컴플라이언스 검토자 및 team_admin 이상 멤버.
상태 머신
대기 (Pending) ──► 검토 중 (Under Review) ──► 승인 (Approved)
└────► 반려 (Rejected)
| 상태 | 설정 주체 | 의미 |
|---|---|---|
| 대기 | 자동, 조건부 라이선스 컴포넌트 첫 탐지 시. | 검토자 배정 대기. |
| 검토 중 | 검토자(team_admin 이상). | 검토자가 요청을 받아 조사 중. |
| 승인 | 검토자. | 명시된 의무사항을 따른다는 전제로 컴포넌트 사용 승인. |
| 반려 | 검토자. | 컴포넌트 제거 권장; 결정은 감사용으로 기록. 반려 결정 caveat 참고 — v0.10.0에서 빌드 게이트는 반려 결정에 자동으로 차단되지 않습니다. |
전환은 감사 로그에 기록됩니다.
승인 큐
사이드바 → Approvals. 필터: 상태(status)와 requested_at 기반의 날짜 범위.
각 행 표시:
- 컴포넌트 (Component) — 컴포넌트 표시 이름과 두 번째 monospace 줄의 package URL(
purl). 행의 기본 컴포넌트 이름을 해석하지 못한 경우 컴포넌트 UUID 앞 8 자로 폴백합니다. - 프로젝트 (Project) — 요청이 적용되는 프로젝트. 프로젝트 이름과 함께
/projects/{id}로 가는 click-through 링크로 렌더링됩니다(같은 컴포넌트가 여러 프로젝트에 등장하면 프로젝트당 한 행). 이름이 사용 불가능한 경우 프로젝트 UUID 앞부분으로 폴백합니다. 링크는 클릭 전파를 중단하므로 프로젝트를 여는 동작이 행의 드로어를 함께 열지는 않습니다. - 상태 (Status) — 대기 / 검토 중 / 승인 / 반려.
- 요청자 (Requested by) — 요청을 생성한 사용자(또는 시스템).
- 요청 시각 (Requested at) — 요청 타임스탬프.
- 동작 (Actions) — 본인 역할에 맞는 처리 컨트롤(드로어 진입 등).
목록 엔드포인트는 컴포넌트·프로젝트 표시 필드를 두 번의 batched IN(...) 조회로 해석합니다(ComponentApproval 모델은 의도적으로 도메인을 가로지르는 관계를 보유하지 않음). 따라서 수백 행짜리 큐도 한 번의 round-trip으로 렌더됩니다.

승인 요청
스캔 파이프라인이 새로운 조건부 라이선스 컴포넌트를 탐지하면 대기 요청이 자동 생성됩니다. 수동으로 작업할 필요가 없습니다.
스캔이 실행되기 전에 요청을 시드해야 하는 클라이언트(예: 아직 푸시하지 않은 PR에서 의존성 추가)를 위해 포털은 POST /v1/approvals 엔드포인트를 노출합니다. 이를 위한 UI 폼은 이연되었습니다 — 로드맵 참고.
요청 처리
- 행을 클릭해 드로어 열기.
- Start Review 클릭 — 상태가 검토 중으로 전환되며 검토자 필드가 본인으로 설정.
- 라이선스 조건과 포털이 나열한 의무사항 검토.
- Approve 또는 Reject. 둘 다 선택 사항인 결정 노트(
decision_note, 2000자 이하)를 입력받습니다. 노트는 감사용으로 승인 행에 저장됩니다.

대기 상태에서는 검토를 거치지 않고도 Reject를 바로 쓸 수 있어, 명백히 잘못된 요청을 빠르게 처리할 때 유용합니다.
성공적인 처리:
- 해당 프로젝트의 컴포넌트에 대한 결정을 잠금.
- 결정을 감사 로그에 기록.
- 다음 스캔에서 프로젝트 리스크 점수 갱신.
- (알림 트리거 활성화 시) 요청자와 팀에게 이메일 발송.
반려(Rejected) 결정
Rejected로 표시된 승인은 v0.10.0에서 해당 컴포넌트를 빌드
게이트의 forbidden로 자동 재분류하지 않습니다. 게이트는
라이선스의 forbidden 단계만 평가합니다
(apps/backend/services/policy_gate.py 참고). 반려 결정은 승인
행과 감사 로그에 증거로 기록되지만, v0.10.0 에서는 CI를 차단하지
않습니다. 후속 스캔은 컴포넌트를 여전히 conditional로 분류하고
빌드는 진행됩니다. 반려 시 forbidden으로 승격되는 promotion-on-rejection
(로드맵)이 적용되기 전까지는 의사결정을 도구 외부에서 강제하세요.
예를 들어 프로젝트에 추적 이슈를 열고 코드 리뷰에서 의존성을 제거하세요.
프로젝트 간 승인
같은 컴포넌트가 여러 프로젝트에 등장하면 각 프로젝트가 별도 대기 요청을 받습니다. 판정은 저절로 전파되지 않습니다. 이유는 두 가지입니다.
- 프로젝트마다 배포 모델이 다를 수 있음(폐쇄 SaaS vs. 출고 바이너리).
- 같은 라이선스라도 링크 모델에 따라 의무사항이 다름(LGPL static vs. dynamic).
어디서나 답이 같은 컴포넌트라면, 팀마다 같은 질문을 반복하는 대신 조직이 한 번 판정할 수 있습니다.
조직 차원의 판정
조직 판정은 컴포넌트 하나에 대해 조직 전체가 기록하는 답입니다. 스스로 판정하지 않은 모든 프로젝트에 적용되고, 이미 판정한 프로젝트를 덮어쓰지는 않습니다.
판정을 열고 결정하는 것은 슈퍼 관리자만 할 수 있습니다. 답이 모든 팀에 닿기 때문입니다. 판정과 그 사유를 읽는 것은 조직 구성원 누구나 할 수 있습니다. 내 프로젝트에서 승인으로 보이는 컴포넌트가 조직에서 물려받은 것일 수 있고, 그 이유를 볼 수 있어야 하기 때문입니다.
구성원이 읽는 것은 사유이고 심의 내용은 아닙니다. 관리자가 결정하면서 적은 메모, 요청자와 결정자의 이름은 그것을 쓸 수 있는 등급에게만 돌아갑니다. 사유 자체는 조직 전체가 읽는다고 전제하고 쓰세요. 실제로 그렇습니다.
판정은 프로젝트 단위 요청과 같은 네 가지 상태를 거치고, 사유가 필수이며(프로젝트 단위 메모와 달리), 같은 If-Match 버전을 사용해 관리자 두 명의 결정이 서로를 조용히 덮어쓰지 않게 합니다.
프로젝트에 적용되는 순서는 이렇습니다.
- 그 프로젝트가 스스로 결정한 승인이 있으면 그것입니다. 승인이든 거부든 답으로 셉니다.
- 없으면 조직이 결정한 판정입니다.
- 그것도 없으면 아직 아무도 판정하지 않은 상태입니다.
결정이 끝난 답만 내려옵니다. 대기 중이거나 검토 중인 판정은 질문이지 답이 아니므로 어떤 프로젝트에도 적용되지 않습니다.
방향은 의도한 것입니다. 자기 프로젝트에서의 사용 맥락을 놓고 검토한 팀은 조직이 모르는 것을 알고 있으므로 팀의 판정이 이깁니다. 팀이 판정하지 않은 자리에서는 조직이 아무도 답하지 않은 질문에 답하는 것이고, 그것이 기본값의 쓸모입니다.
조직 판정이 하지 않는 일도 분명합니다. 열려 있는 프로젝트 단위 요청을 건드리지 않습니다. 검토 중인 팀은 그 검토를 그대로 이어가고, 결론이 나면 그 답이 이깁니다. 프로젝트에 무언가를 미리 써 두지도 않습니다. 폴백은 읽는 시점에만 일어나므로 판정을 내리는 것도 바꾸는 것도 가볍습니다.
조직의 생각이 바뀌었다면, 이미 결정된 판정은 기록이므로 고치지 않습니다. 같은 컴포넌트에 새 판정을 열어 결정하면 그때부터 새 답이 적용되고, 이전 판정은 그때 무엇을 결정했는지에 대한 증거로 남습니다.
화면에서는 조직에서 물려받은 답인 경우 컴포넌트 드로어에 그 사실과 사유가 표시됩니다. 팀이 직접 판정한 컴포넌트에는 아무것도 덧붙지 않습니다. 상태 표시만으로 충분하기 때문입니다.
사용 전에 묻기
위의 내용은 모두 스캔이 컴포넌트를 찾은 뒤의 이야기입니다. 순서를 반대로 정하는 조직도 있습니다. 이 패키지를 써도 되는지 먼저 묻고, 빌드에 들어가기 전에 답을 받습니다.
이 화면은 배포가 켜지 않으면 없습니다(INTAKE_REQUESTS_ENABLED=true). 꺼져 있다는 것은 있으면서 거절한다는 뜻이 아니라 아예 없다는 뜻입니다. 메뉴에 나타나지 않고 라우트는 404로 답합니다. 패키지를 쓰기 전에 검토할지는 조직이 일하는 방식에 대한 질문이지 포털이 정할 문제가 아니고, 스캔이 찾은 것을 검토하는 조직이라면 위의 승인 큐가 이미 그 역할을 합니다.
켠 배포에서는 사이드바에 [사용 전 신청]이 나타납니다.
- 프로젝트가 속한 팀의 누구나 신청할 수 있습니다. 가장 낮은 등급도 됩니다. 의존성을 넣고 싶은 사람과 결정하는 사람은 보통 다르고, 관리자만 넣을 수 있는 큐는 사람들이 우회하는 큐입니다.
- 신청에는 패키지 이름이 아니라 purl(
pkg:npm/lodash)을 적습니다. 나중에 스캔이 답을 맞춰 볼 대상이 purl이기 때문입니다. 이름으로 적으면 답을 받고도 다시 찾지 못합니다. - 팀 관리자가 승인과 같은 네 가지 상태를 거쳐 답합니다. 같은 패키지에 대한 같은 판단을 더 이른 시점에 하는 것이라 상태도 같습니다.
- 나중에 스캔이 그 패키지를 찾으면 이미 준 답이 그때 열리는 승인으로 옮겨집니다. 먼저 물어본 사람에게 다시 묻지 않습니다. 거절도 함께 옮겨지므로, 팀이 거절한 패키지가 새 질문으로 돌아오지 않습니다.
결정이 끝난 신청만 옮겨집니다. 검토 중인 신청은 답이 아니라 질문이고, 그것을 답으로 취급하면 누군가 검토를 시작했다는 이유로 컴포넌트가 승인된 것이 됩니다.