본문으로 건너뛰기

승인

조건부 라이선스(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으로 렌더됩니다.

/approvals 대기열 — Pending / Under Review / Approved / Rejected 상태 배지, 컴포넌트 식별자, 프로젝트, 요청자(actor), 행별 Actions 컬럼이 있는 표

승인 요청​

스캔 파이프라인이 새로운 조건부 라이선스 컴포넌트를 탐지하면 대기 요청이 자동 생성됩니다. 수동으로 작업할 필요가 없습니다.

스캔이 실행되기 전에 요청을 시드해야 하는 클라이언트(예: 아직 푸시하지 않은 PR에서 의존성 추가)를 위해 포털은 POST /v1/approvals 엔드포인트를 노출합니다. 이를 위한 UI 폼은 이연되었습니다 — 로드맵 참고.

요청 처리​

  1. 행을 클릭해 드로어 열기.
  2. Start Review 클릭 — 상태가 검토 중으로 전환되며 검토자 필드가 본인으로 설정.
  3. 라이선스 조건과 포털이 나열한 의무사항 검토.
  4. Approve 또는 Reject. 둘 다 선택 사항인 결정 노트(decision_note, 2000자 이하)를 입력받습니다. 노트는 감사용으로 승인 행에 저장됩니다.

승인 드로어 — Pending 상태와 Start Review / Reject 결정 버튼

대기 상태에서는 검토를 거치지 않고도 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 버전을 사용해 관리자 두 명의 결정이 서로를 조용히 덮어쓰지 않게 합니다.

프로젝트에 적용되는 순서는 이렇습니다.

  1. 그 프로젝트가 스스로 결정한 승인이 있으면 그것입니다. 승인이든 거부든 답으로 셉니다.
  2. 없으면 조직이 결정한 판정입니다.
  3. 그것도 없으면 아직 아무도 판정하지 않은 상태입니다.

결정이 끝난 답만 내려옵니다. 대기 중이거나 검토 중인 판정은 질문이지 답이 아니므로 어떤 프로젝트에도 적용되지 않습니다.

방향은 의도한 것입니다. 자기 프로젝트에서의 사용 맥락을 놓고 검토한 팀은 조직이 모르는 것을 알고 있으므로 팀의 판정이 이깁니다. 팀이 판정하지 않은 자리에서는 조직이 아무도 답하지 않은 질문에 답하는 것이고, 그것이 기본값의 쓸모입니다.

조직 판정이 하지 않는 일도 분명합니다. 열려 있는 프로젝트 단위 요청을 건드리지 않습니다. 검토 중인 팀은 그 검토를 그대로 이어가고, 결론이 나면 그 답이 이깁니다. 프로젝트에 무언가를 미리 써 두지도 않습니다. 폴백은 읽는 시점에만 일어나므로 판정을 내리는 것도 바꾸는 것도 가볍습니다.

조직의 생각이 바뀌었다면, 이미 결정된 판정은 기록이므로 고치지 않습니다. 같은 컴포넌트에 새 판정을 열어 결정하면 그때부터 새 답이 적용되고, 이전 판정은 그때 무엇을 결정했는지에 대한 증거로 남습니다.

화면에서는 조직에서 물려받은 답인 경우 컴포넌트 드로어에 그 사실과 사유가 표시됩니다. 팀이 직접 판정한 컴포넌트에는 아무것도 덧붙지 않습니다. 상태 표시만으로 충분하기 때문입니다.

사용 전에 묻기​

위의 내용은 모두 스캔이 컴포넌트를 찾은 뒤의 이야기입니다. 순서를 반대로 정하는 조직도 있습니다. 이 패키지를 써도 되는지 먼저 묻고, 빌드에 들어가기 전에 답을 받습니다.

이 화면은 배포가 켜지 않으면 없습니다(INTAKE_REQUESTS_ENABLED=true). 꺼져 있다는 것은 있으면서 거절한다는 뜻이 아니라 아예 없다는 뜻입니다. 메뉴에 나타나지 않고 라우트는 404로 답합니다. 패키지를 쓰기 전에 검토할지는 조직이 일하는 방식에 대한 질문이지 포털이 정할 문제가 아니고, 스캔이 찾은 것을 검토하는 조직이라면 위의 승인 큐가 이미 그 역할을 합니다.

켠 배포에서는 사이드바에 [사용 전 신청]이 나타납니다.

  1. 프로젝트가 속한 팀의 누구나 신청할 수 있습니다. 가장 낮은 등급도 됩니다. 의존성을 넣고 싶은 사람과 결정하는 사람은 보통 다르고, 관리자만 넣을 수 있는 큐는 사람들이 우회하는 큐입니다.
  2. 신청에는 패키지 이름이 아니라 purl(pkg:npm/lodash)을 적습니다. 나중에 스캔이 답을 맞춰 볼 대상이 purl이기 때문입니다. 이름으로 적으면 답을 받고도 다시 찾지 못합니다.
  3. 팀 관리자가 승인과 같은 네 가지 상태를 거쳐 답합니다. 같은 패키지에 대한 같은 판단을 더 이른 시점에 하는 것이라 상태도 같습니다.
  4. 나중에 스캔이 그 패키지를 찾으면 이미 준 답이 그때 열리는 승인으로 옮겨집니다. 먼저 물어본 사람에게 다시 묻지 않습니다. 거절도 함께 옮겨지므로, 팀이 거절한 패키지가 새 질문으로 돌아오지 않습니다.

결정이 끝난 신청만 옮겨집니다. 검토 중인 신청은 답이 아니라 질문이고, 그것을 답으로 취급하면 누군가 검토를 시작했다는 이유로 컴포넌트가 승인된 것이 됩니다.

외부 검토 시스템 통합​

포털은 Webhook을 통해 승인 요청을 외부 시스템(예: Jira)에 게시할 수 있습니다. admin 알림 참고. 승인 요청 트리거가 같은 이벤트를 이메일, Slack, Teams, outbound HTTP POST로 연결합니다.

전형적 흐름:

  1. 스캔 파이프라인이 대기 요청을 생성하면 포털이 Jira automation에 POST합니다.
  2. Jira가 티켓을 생성하고 법무 검토자를 배정합니다.
  3. 검토자가 포털에서 처리하면 포털이 결정을 Jira로 POST하고, Jira가 티켓을 종료합니다.

정상 동작 확인​

요청 처리 후:

  1. 상태 배지가 즉시 갱신.
  1. 감사 로그가 target_table=component_approvals&action=update를 previous_status, new_status, decision_note와 함께 diff에 담아 기록.

    SELECT count(*) FROM audit_logs
    WHERE target_table = 'component_approvals'
    AND action = 'update'
    AND diff ? 'previous_status'
    AND diff ? 'new_status'
    AND created_at > now() - interval '1 hour';
  1. 원래 요청자(있다면)가 팀의 알림 설정에 따라 알림 수신.

    SELECT count(*) FROM notifications
    WHERE kind = 'approval_state_changed';
  1. 참고: v0.10.0에서 반려 결정은 다음 스캔의 빌드 게이트에서 컴포넌트를 forbidden으로 자동 승격하지 않습니다 — 수동 후속 조치는 반려 결정 caveat 참고.

트러블슈팅​

조건부 라이선스 컴포넌트가 있는데 승인 큐가 비어 있음​

요청이 이미 처리되었을 수 있습니다(승인·반려). 기본 큐 뷰는 대기·검토 중으로 필터됩니다. 상태 필터를 All로 전환하세요.

요청에 대해 Start Review를 누를 수 없음​

프로젝트 소속 팀에서 team_admin 이상이 필요합니다. 팀 admin에게 위임을 요청하거나 프로젝트의 소속 팀을 변경하세요.

반려 결정이 다음 CI 빌드를 차단하지 않음​

현재 릴리스의 의도된 동작입니다 — 반려 결정 caveat 참고. 빌드 게이트는 forbidden 라이선스 단계만 평가하며, 승인 결정은 컴포넌트의 라이선스 카테고리로 역전파되지 않습니다. 빌드를 차단하려면 의존성을 제거하거나 분류기 사전 패치 경로(Operator 전용)를 통해 해당 라이선스를 forbidden으로 승격하세요.

승인 결정이 다음 스캔에서도 경고로 표시됨​

상태 배지는 즉시 갱신되지만, 프로젝트 리스크 점수와 conditional 경고 표시는 새 스캔이 완료된 뒤에만 갱신됩니다. 결정 시점에 스캔이 이미 실행 중이었다면 그 스캔은 이전 상태를 반영합니다. 새 스캔을 트리거하세요.

로드맵​

매뉴얼이 이전에 약속했으나 v0.10.0에 포함되지 않은 항목.

  • 큐 툴바의 프로젝트 / 라이선스 / 컴포넌트 / 요청자 필터 — 예정. 현재는 상태와 날짜 범위만 노출.
  • 큐 행의 라이선스 / 검토자 / 사유 컬럼 — 예정. 현재는 드로어 안에서만 노출됩니다.
  • "New request" UI 폼(프로젝트 / purl / 사유) — 예정. 현재 수동 요청 시드는 POST /v1/approvals 엔드포인트뿐입니다.
  • team_admin 검토자를 위한 다중 선택 일괄 결정 — 예정.

함께 보기​