개발 기록 2026-10-17 docs/devlog/2026-10-17-6-인증과-첫-병렬-라운드.md GitHub에서 보기

인증과 첫 병렬 라운드

목표

10/18부터 코드 워커를 병렬로 돌릴 수 있도록 인증을 붙이고, 도메인, 설계, 디자인 문서를 확정한다. 코드 워커는 이 세 문서를 계약으로 삼는다.

한 일

  • mix phx.gen.auth Accounts User users --live로 인증을 붙였다. Phoenix 1.8의 scope 방식(current_scope)을 그대로 쓰고, engagement 권한도 이 scope 위에 얹는다. mix precommit 통과(테스트 112개).
  • 생성기가 AGENTS.md에 인증 가이드라인을 주입했다. 이 블록을 refs/phoenix-guidelines.md로 옮기고, 앞으로도 같은 방식으로 처리한다는 규칙을 AGENTS.md 6장에 적었다.
  • 첫 병렬 라운드: 워커 3명이 문서 3종을 동시에 썼다. 메인 세션은 그동안 스캐폴딩, 인증, 계약 검토를 맡았다.
  • 디자인 문서를 HTML 시안으로도 만들었다. 색 토큰(라이트/다크), 글꼴, 컴포넌트별 상태, 화면 S1-S6을 도메인 문서 7장의 합성 데이터로 채웠다. 브라우저에서 375/768/1280px 폭으로 열어 페이지 가로 스크롤이 없는지 확인했고, 그 과정에서 넘치던 곳 5군데(grid 칸의 자동 폭, 감사 로그 payload의 UUID, 스크롤 영역 밖으로 빠진 sr-only 요소, 키-값 목록의 키 열, 합계 카드)를 고쳤다. 원본은 md이고 HTML은 md를 따른다.
  • GitHub 원격 저장소(basilry/kpmg-elixir)를 연결하고 main을 올렸다. 지금은 비공개다. 지원서에 링크를 넣기 전에 공개로 바꿔야 한다.
워커 산출물 공유 계약
domain-writer 룰 8종, 점수, AI 입출력, 상태 모델, 합성 데이터 명세 MVP 문서 5장 GL 컬럼
architect 컨텍스트, 테이블 13개, Oban, PubSub, AI 클라이언트, 병렬 개발 계획 MVP 문서 5장, 룰 플러그인 계약
designer 색, 타이포그래피, 컴포넌트, 화면 구성 도메인 문서의 상태값 이름

문서 간 충돌과 정리

두 워커가 같은 계약을 서로 다르게 해석한 곳이 7군데 나왔다. 테이블과 상태값은 설계 문서를, 감사 판단 근거는 도메인 문서를 원본으로 정하고 반대쪽을 고쳤다.

주제 도메인 문서 설계 문서 결정 이유
전표 점수 룰 가중치 합 적중 룰의 최대 점수(max_score) 가중치 합, 상한 100 여러 위험 신호가 겹친 전표가 위로 와야 한다. 최대값이면 룰 1개 적중과 4개 적중이 같은 점수가 된다
검토 상태 queued, in_review 등 8단계 open, concluded, approved, rejected 설계 쪽 4단계 + 별도 triage_status AI 처리 상태와 사람의 판단 상태를 섞지 않는다. 결론 제출이 곧 검토 요청이라 in_review가 따로 필요 없다
판단 값 이름 no_issue, approve no_exception, approved 설계 쪽 감사 조서 용어(예외)와 맞고, 상태값과 결정값 표기가 일관된다
AI 출력 confidence 포함 없음 제외 LLM이 스스로 매긴 확신도는 보정되지 않는다. 감사인이 근거 있는 확률로 오해할 수 있다
트리아지 시작 테스트 완료 후 자동 미정 작성자의 일괄 요청 버튼, 1회 100건 비용을 통제하고, 누가 AI를 불렀는지 감사 로그에 남긴다
무효 출력 1회 자동 재시도 미정 재시도 없이 실패, 건별 재요청 같은 입력에 같은 처리를 하도록 결정적으로 둔다. 일시 오류(429, 5xx) 재시도와는 구분한다
counterparty 라인 필드 라인 필드 전표 헤더 필드 MVP 문서 5장에서 같은 전표의 행은 거래처가 같아야 한다고 정했다

같이 고친 것:

  • 도메인 문서의 중복 전표 주입(I8)은 "원본 +1-5 영업일"이라 추석 연휴에 걸리면 중복 판단 기간(7일)을 넘을 수 있었다. 달력일 7일 상한을 넣고, 넘치면 그 후보를 건너뛰게 했다.
  • 설계 문서가 요청한 GL 검증 규칙 3개(같은 전표의 행 연속, entered_at 형식, 원 단위 금액)를 MVP 문서 5장에 넣었다.
  • HTML 시안을 그리면서 md에서 빠진 것을 찾아 md를 고쳤다. score_meter의 채움 칸 수(ceil(score / 20))를 명시하고, S5 이력 목록 컴포넌트 finding_history/1을 표에 추가했다. 실행 중 시안의 test_progress는 동시 실행 상한(Oban rules: 4)을 넘지 않게 그렸다.
  • 도메인 문서 7장의 기대 건수를 다시 셌다. 트리아지 대상 52건(I5 30건 + 복합 22건), 경미 적중 213건(단일 룰 167건 + 정상인데 걸리는 46건), 전표 약 8,341건, 라인 약 19,794건으로 명세와 맞았다. 10/19 룰 테스트가 이 숫자를 정답으로 쓴다.

메인 세션이 기본값으로 정한 것

architect가 남긴 미결정 사항은 아래 기본값으로 진행한다. 바꾸려면 설계 문서 Changelog에 남긴다.

항목 기본값
LLM 모델 config의 claude-opus-5-5, ANTHROPIC_MODEL로 덮어씀. 기본 클라이언트는 Fake(키 없이 동작)
Anthropic server-side fallback 끔
트리아지 일괄 요청 상한 100건
CSV 적재 상한 20MB, 100,000행
engagement 역할 사용자당 하나
oban_web 대시보드 2단계
시각 표시 KST 고정(+9시간). 사용자별 시간대는 두지 않는다
승인 단위 전표(finding)별. 감사 대상 단위 sign-off는 2단계

진행 방식 메모

  • 고속 모드로 전환하려 했지만 계정의 추가 사용량 설정이 꺼져 있어 거부됐다. 대신 워커마다 작업 시간을 정해 두고, 디자인 문서를 세 번째 워커로 앞당겨 같은 라운드에 넣었다.
  • 워커가 쓴 문서는 메인 세션이 검토한 뒤 상태를 "확정"으로 바꾸고 Changelog에 검토 내용을 남겼다. 다음 라운드부터 코드 워커는 확정 문서만 계약으로 본다.

Elixir 학습 포인트

  • NaiveDateTime.from_iso8601/1은 +09:00 같은 오프셋이 붙어도 오류 없이 무시한다. 계약에서 오프셋을 금지했으니 형식(길이 19자)을 먼저 확인해야 한다.
  • NimbleCSV가 돌려주는 필드는 원본 바이너리의 sub-binary다. 오래 들고 있는 문자열은 :binary.copy/1로 복사해야 원본 전체가 메모리에 남지 않는다.
  • Oban OSS에는 Pro의 Batch가 없다. 룰 job들이 모두 끝났는지는 test_runs 행을 FOR UPDATE로 잠그고 남은 룰 수를 세는 방식으로 판단한다. 마지막으로 끝난 job이 마무리를 맡는다.
  • 감사 로그의 append-only는 애플리케이션 규칙이 아니라 PostgreSQL 트리거로 강제한다. Repo 실수나 수동 SQL로도 UPDATE, DELETE가 막힌다.
  • phx.gen.auth의 Scope는 "현재 누가 요청했는가"를 컨텍스트 함수의 첫 인자로 넘기는 패턴이다. 직무분리 검사도 이 scope를 받아 처리한다.

다음 할 일 (10/18)

  • 메인 세션: oban, nimble_csv 의존성, Oban과 AI 설정, 마이그레이션 1-8, 스키마 모듈, AuditTrail.append/3, Engagements.Policy, 라우트. mix ecto.migrate와 mix ecto.rollback --all이 양방향으로 도는지 확인한다.
  • 그다음 설계 문서 12장대로 코드 워커 3명(Engagements, CSV 적재, 합성 GL 생성기)을 worktree에서 병렬로 돌린다.