2026-09-18 · 우정(백엔드) · 저장소 Seuk-Team/Arda 커밋 4747567 기준
보고서(그림·표 포함): claude.ai/artifact/Ai6YBFXz52VvyP9xH8vhgN
4747567) 이후 main 에는 PR #352~#354 (카메라 박스·무관 질문 거절 기준·로그인 소개 영상 팝업)와 로그아웃 계정 메뉴만 더해졌고, 09-20 해커톤 제출 뒤 코드 동결(마지막 커밋 8071fd9). 스택·API·구조·수치는 최종과 같다(시험 수 제외). 심사는 클라우드 seuk.suvisdev.cloud + Claude 로 운영 중이며, 09-16 구축한 온프레미스(학원 PC 3대·로컬 Qwen)는 09-18 폐쇄했다. §6·§8 의 미해결 항목은 동결 시점 상태 그대로다.| DB 테이블 | 28 |
| alembic 리비전 | 26 |
| API 라우트 | 107 |
| 백엔드 시험 | 1,165 통과 (커밋 a650123 기준) |
| 결정 기록(ADR) | 36편 |
| AI 프롬프트 | 18개 |
백엔드 파이썬 134개 파일 중 51개가 시험 파일이다. 프론트 TS/TSX 62개, Flutter Dart 86개.
| 영역 | 선택 | 고른 이유 |
|---|---|---|
| 백엔드 | FastAPI · Python 3.12 | 타입 힌트가 그대로 검증·문서가 된다. 클라이언트가 셋(웹·앱·공개 링크)이라 Swagger 가 계약의 진실인 구조가 값을 했다 |
| DB | PostgreSQL 16 + pgvector | 벡터 DB 를 따로 세우지 않기 위해서다(ADR-0021). 검색 대상이 이미 Postgres 안의 지원서인데 저장소를 나누면 동기화 문제가 새로 생긴다 |
| 스키마 이행 | alembic (26 리비전) | 손 SQL 시절에는 git pull 로 DB 가 안 따라와 "내 로컬에서만 되는" 상태가 반복됐다. 지금은 한 줄로 따라온다 |
| 프론트 | React 19 · Vite · TS | 팀이 이미 쓸 줄 안다는 것이 근거였다(ADR-0006). 상태 관리 라이브러리 없음, 스타일은 CSS Modules + 토큰 |
| 앱 | Flutter · Dart (Android) | 같은 API 의 두 번째 클라이언트를 만들어 "서버를 몇 줄 고쳤나"를 말하기 위해서다(ADR-0010). iOS 동작은 주장하지 않는다 — Mac 이 없어 검증할 수 없다 |
| AI | Claude API (프롬프트 18개) | 요약을 한 번에 시키지 않고 3단계 체인으로 쪼갰다(ADR-0022). 한 프롬프트가 여섯 가지를 하면 어디서 틀렸는지 알 수 없다 |
| 실시간 전사 | OpenAI 전사 API | 09-15 실측 — t3.large 에서 로컬 large-v3-turbo 가 44초 발화를 180초 안에 못 끝냈고 밀린 시간이 뒤 답변까지 연쇄로 죽였다. API 는 분당 $0.006, 10문항 면접 ≈ $0.06 (ADR-0038). 단 09-17 운영 실측: STT_BACKEND 빈 값이라 업로드 답변·재전사·아르 음성 = whisper-1 API(백엔드가 직접 호출) · 실시간 면접 전사 = CPU 로컬 faster-whisper 로 갈려 돌았다(ADR-0038 「같은 변수, 두 해석」. 09-18 이후 값은 미확인) |
| 보조 신호(표정·음성) | ViT + 음성 특징 (ai/lie-detection) | 오디오를 16kHz PCM 으로 받는다 — webm/opus 는 발화 끝을 알려면 매번 디코딩해야 하고 그게 실시간 예산 1.5초를 갉아먹는다 |
| 메일 | n8n + SMTP | 메일이 SQS·SES·워커 코드에 묶여 있으면 AWS 를 떠날 때 같이 멈춘다. 본문은 우리가 만들고 부치는 일만 맡겨 노드 하나로 공급자가 바뀌게 했다(ADR-0030) |
| 인프라 | EC2 1대(t3.large) · Caddy · docker compose (컨테이너 5개) | K8s 는 뺐다(ADR-0001) — 한 달 규모에서 "굴렸다"고 말할 깊이까지 못 간다. 09-16 온프레미스로 옮겼다가 09-18 폐쇄 — 심사는 이 EC2 에서 받는다 |
| CI | GitHub Actions | ruff(F) → alembic → 스키마 드리프트 → pytest → 프론트 빌드 → AI·앱 시험 |
뺀 것: Kubernetes · 실시간 공동 편집(CRDT·OT) · 3인 이상 다자 화상면접 · SQS 워커(코드는 남김, ADR-0036).
접두사 /api/v1. 요청·응답의 진실은 Swagger(/docs) 이고, 02-api.md 는 "무엇이 있는가"만 유지한다.
| 출입구 | 누가 | 어떻게 |
|---|---|---|
| JWT Bearer | 담당자·면접관 | 12시간 토큰. 비활성 계정은 이미 발급된 토큰도 401 — 로그인만 막으면 만료까지 그대로 쓴다 |
/public/* | 지원자 | 링크 안의 일회성 토큰. 지원 폼·일정 선택·인적성 설문·AI 면접이 전부 같은 패턴 |
/internal/* | 판정 워커·n8n | 서비스 토큰. 없으면 아예 호출하지 않는다 |
권한은 admin·member 둘뿐이다(ADR-0017). 위계가 아니라 조작 권한의 유무다.
| 규칙 | 왜 |
|---|---|
| 마감·만료는 조회 시점에 판정 | 크론을 늘리면 "그게 돌았나"를 확인할 자리가 또 생긴다 |
| 일괄 변경은 전부 성공 아니면 전체 롤백(409) | 200명 중 30명만 바뀌면 담당자가 무엇이 됐는지 알 수 없다 |
| 사용자는 지우지 않는다(비활성화만) | users.id 가 changed_by·evaluator_id 로 박혀 있어 물리 삭제가 이력을 부순다 |
불합격은 reason 없으면 422 | 사유 없는 이력은 나중에 아무도 설명할 수 없다 |
| 단계 역행은 항상 허용, 전진은 한 칸씩 | 사람이 AI 판정을 되돌리는 것이 유일한 안전장치다 |
09-12 폴더 재편의 출발점은 단순했다. models.py 하나를 71개 파일이 직접 참조하고 있어 무엇을 고쳐도 어디가 깨질지 몰랐다.
"하나의 중심"을 잡으려 했으나 Arda 에는 자연스러운 중심이 넷 있었다. 하나로 몰면 나머지 셋이 왜곡되므로 각자의 별로 뒀다.
| 컨텍스트 | 중심 | 안에 있는 것 |
|---|---|---|
hiring | JobPosting | 공고 · 회사 프로필 · 자동 심사 규칙 |
talent | User | 계정 · 인증 · 지원자 앱 로그인 |
application | Application | 가장 큰 별 — 서류 점수 · 단계 이력 · 판정 · 메일 기록 · 에이전트 추적 |
interview | InterviewSession | 면접 세션 · 일정 · 배정 · 실시간 분석 · 채점 |
바깥으로 나가는 것은 포트와 어댑터로 갈랐다 — ports/output/*_port.py 가 약속, adapter/outbound/{pg,llm,mail} 이 구현이다. 메일 발송기가 n8n·smtp·sqs 세 벌인데 부르는 쪽 코드가 하나인 것이 이 구조 덕이다.
ADR 은 "확정"으로 끝나지 않는다. 오너가 개정 ADR 을 쓰면 바뀐다. 아래 넷이 모양을 가장 크게 바꿨다.
원래 뺐던 근거는 강했다 — HireVue 가 2021년 중단(직무성과 예측 기여도 0.25% 미만), 얼굴 움직임으로 감정 추론이 어렵다는 학계 정리, 안면마비·자폐 지원자를 결함으로 점수화하는 차별 위험. 뒤집으면서 그 근거를 폐기하지 않고 문서에 남겼다. 지금의 타협은 표정·음성 수치가 담당자 화면에 보이되 점수 재료가 아니라는 것이다. AI 요약 문장도 측정된 숫자의 재서술만 허용하고, 수치에 없는 숫자나 사람을 단정하는 단어가 나오면 문장을 버리고 고정 틀로 대체한다.
"확정은 사람"이 최종 합불 한 곳으로 좁혀졌다. 서류는 아르가 100점으로 채점해 임계(기본 60) 이상이면 면접으로, 미만이면 탈락으로 옮긴다. 붙인 안전장치 셋:
changed_by=NULL(시스템)과 점수 사유가 남는다예산 $400 과 10/27 종료가 전제였다. 핵심은 지우는 것이 아니라 스위치를 먼저 만든 것이다 — MAIL_DISPATCH · MAIL_TRANSPORT · QUEUE_BACKEND · S3_ENDPOINT_URL. S3 는 코드를 안 고치고 MinIO 로 갈 수 있다.
기획 견적 "시그널링 + TURN + SFU 까지 1명의 4주"는 3인 이상이 전제였다. 둘이면 SFU 가 없어 두 끝점이 직접 붙는다. 전제를 다시 읽고 1:1 만 열었다. 3인 이상은 09-20 시연 뒤 집단 면접으로 다시 본다(동결로 미착수).
정리하면서 확인한, 문서와 코드가 맞지 않는 곳이다. 숨기면 발표에서 먼저 지적당한다.
| 무엇 | 문서 | 실제 | 누가 |
|---|---|---|---|
| 에이전트 모델 | ADR-0011 — 도구 호출은 claude-opus-5, "여기서 아끼면 발표가 깨진다" | 코드 기본값은 채팅·요약 둘 다 claude-haiku-4-5 (anthropic_backend.py). 09-02 키 폐기 뒤 비용을 조인 흔적으로 보이는데 ADR 이 안 따라왔다 | cloverky 확인 요청 09-28: 동결까지 미개정 |
| API 프로세스 | — | --workers 1 고정. 면접 방·입장권·로그인 잠금·STT 모델이 전부 프로세스 메모리라 늘리면 지원자와 담당자가 다른 프로세스에 앉는다 | woojeongalex (Redis 이관은 프리즈 뒤) |
| ADR-0012 서류심사 기준 | 체크박스가 전부 미확정 | 실제 기준은 ADR-0034 가 대신 정했다. 0012 가 정리되지 않은 채 남아 있다 | 정리 필요 |
| 면접관 자동 배정 | 서류 통과 시 공고의 기본 면접관 풀에서 배정 | 배정 0명으로 떨어지는 경우가 있다 | woojeongalex (미수정) |
지금 코드의 장치 대부분은 설계가 아니라 사고의 흔적이다.
| 언제 | 무슨 일 | 그 뒤 생긴 것 |
|---|---|---|
| 09-01 | 깨진 main 을 하루에 두 번, 둘 다 우연히 발견 | CI 도입 — 게이트가 아니라 "깨졌다는 사실만 즉시 보이게" |
| 09-01 | 디스크 고갈로 배포가 죽음 | 컨테이너 로그 상한(20MB×3) · 배포 때 이미지·캐시 prune |
| 09-02 | Docker 가 죽어 pytest 가 24분간 조용히 멈춤 | pytest-timeout 60초 — 멈춤을 침묵이 아니라 실패로 |
| 09-09 | STT 컨테이너 OOM, 메모리 상향 5번 + 인스턴스 이관까지 감 | 근본 원인은 코드 기본값 하나(int8). 고친 뒤 사용량 223MB |
| 09-09 | compose 를 infra/ 로 옮기자 컨테이너·볼륨이 한 벌 더 생김 | name: arda 를 박음 (compose 는 이름이 없으면 폴더명을 쓴다) |
| 09-12 | 미정의 이름 두 건이 프로덕션 500 으로 | CI 에 ruff --select F — 1초에 잡힌다 |
| 09-16 | n8n 이 받고 죽어 메일이 queued 로 남음 | API 가 5분마다 밀린 메일을 SMTP 로 재발송(최대 3회) |
| 09-17 | 면접 링크를 만들자마자 시작하면 422 | 기본 질문 3개를 세션과 같은 커밋에 넣고, 맞춤 질문은 시작 전일 때만 교체 |
09-28 기준 — 09-20 코드 동결 이후 저장소 커밋이 없어 아래 상태는 동결 시점 그대로다.
| 상태 | 무엇 | 왜 | 누가 |
|---|---|---|---|
| 부분 | 면접관 자동 배정 0명 결함 | 서류 통과자가 면접관 없이 면접 단계로 간다 | woojeongalex |
| 계획 | AWS 잔여 5종 폐기 · CloudFormation 스택 삭제 | 10/27 이후에도 서비스가 이어 돌아야 한다 | suvisdev |
| 계획 | 방 상태 Redis 이관 — 워커 1개 제약 해제 | 지금은 API 를 수평으로 못 늘린다 | woojeongalex |
| 계획 | 집단 면접(지원자 여러 명 + 면접관 1명) ADR | 09-20 시연 뒤 착수. 핵심은 AI 판정을 사람별로 가르는 것 | woojeongalex |
| 부분 | 받아쓰기 정확도 — 실제 음성에서 흔들림 | 시연에서 첫 질문 답변이 깨져 나왔다 | woojeongalex · suvisdev |
| 계획 | ADR-0011 을 코드 실제값에 맞춰 개정 | 문서가 코드와 다르면 둘 다 못 믿는다 | cloverky |
| 계획 | DB 권한 분리 (09-22 09:00 예정이었음) | 앱 전용 롤로 낮추기 — 6단계 점검 예정. 준비 코드(권한 스크립트·적용 절차서)는 동결 전에 들어갔고, 적용 여부는 이 문서에서 확인하지 않았다 | suvisdev · woojeongalex(점검) |
| 계획 | GPU 풀가동 10-14~10-27 (g4dn.xlarge · T4) | 해커톤 심사 종료 후에만 .env 스위치(STT_BACKEND·Ollama·VIT_MODEL) 변경 · 제품 시연 10-27 | suvisdev |