Skip to content

feat/company-dataset-api - #106

Merged
danjee2009 merged 2 commits into
mainfrom
feat/company-dataset-api
Oct 19, 2026
Merged

danjee2009 merged 2 commits into
mainfrom
feat/company-dataset-api

Conversation

@kimwuju

@kimwuju kimwuju commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

연관 이슈 없음 (기업 데이터셋 구현 명세 00·02·05의 API 담당분 P-1~P-3)

Task

  • 면접 생성 중복 방지: POST /api/interviews/create에 선택 헤더 Idempotency-Key(UUID)를 추가했습니다.
    • 사용자·키·설정이 같으면 처음 만든 면접을 돌려줍니다. 기업이 그 사이 숨겨졌어도 마찬가지입니다.
    • 같은 키로 다른 설정을 보내면 409 IDEMPOTENCY_KEY_REUSED입니다.
    • 동시 요청은 UNIQUE(user_id, idempotency_key)가 하나만 남깁니다.
    • 헤더가 없으면 이전 동작 그대로입니다.
  • 생성 트랜잭션: 면접·N:1 면접관 명단·요청 키를 한 트랜잭션에 저장합니다. 검증이 실패하면 면접도 키도 남지 않습니다.
  • 스냅샷 잠금: 스냅샷은 기업 행을 FOR SHARE로 읽고, 가져오기는 FOR UPDATE로 잡고 고칩니다. 이름·요약·facts가 섞인 스냅샷이 생기지 않습니다.
  • 오류 코드: 오류 응답에 선택 필드 code를 추가했습니다. 값이 없으면 키를 내보내지 않아 기존 본문은 그대로입니다.
    • 없는·숨긴 기업: 404 COMPANY_UNAVAILABLE
    • 실행 스냅샷을 만들 수 없는 데이터: 503 COMPANY_CONTEXT_UNAVAILABLE (이전에는 500)
    • 헤더·companyId 형식 오류: 422 INVALID_INTERVIEW_REQUEST (companyId는 1 이상 2^53−1 이하)
  • 가져오기:
    • 파일이 디렉터리에서 빠져도 기업을 숨기지 않습니다. 숨김은 app.company.hidden-codes 설정으로만 합니다.
    • app.company.allow-sample이 false면 SAMPLE 패키지를 가져오지 않습니다. 비워 두면 prod 프로필이 아닐 때만 허용합니다.
    • 글 필드의 앞뒤 공백을 검증 전에 빼고 그 값을 저장합니다(검증과 저장이 같은 값을 봄).
    • 자기 참조·순환 모회사는 관계만 빼고 파일은 반영합니다.
    • 로그에서 URL·식별번호·파서 원문을 뺐습니다.
  • 목록·스냅샷 공개 조건: PUBLISHED이면서 검수 기록이 있거나 허용된 SAMPLE인 기업만 내보냅니다. 정렬은 분류·이름이 같으면 companyId 순입니다.
  • companyFit 근거 검증:
    • 스냅샷에 있는 factId와, 채점 요청 때 보낸 답변 있는 문항만 근거로 남깁니다. 목록은 작업 행과(접수 전 콜백 대비) 면접에 남기며, 목록이 없는 콜백은 어느 문항도 인정하지 않습니다.
    • kind·focus는 모델 값 대신 스냅샷에서 채웁니다.
    • 근거 문장은 150자, 총평은 300자까지입니다. 목록마다 최대 3개, 같은 목록의 중복 factId는 첫 항목만 둡니다.
    • 항목이 있었는데 모두 빠지면 companyFit 전체를 null로 둡니다. 기본 피드백은 그대로 저장합니다.
    • 기업을 고르지 않은 면접은 조회에서도 companyFit이 null입니다.
  • 준비 응답 배지: 면접 시작·재시도·GET /api/questions/tailor 응답에 면접 스냅샷 기준 company 배지를 추가했습니다.
  • V35 마이그레이션:
    • company.sample 컬럼 (기존 행은 출처로 계산해 채움)
    • fact가 자기 기업의 출처만 가리키게 하는 복합 FK
    • interview_create_request 테이블
  • V36 마이그레이션: feedback.requested_question_ids, interview.last_feedback_question_ids (리뷰 반영)
  • V37 마이그레이션: 출처 없는 기업까지 SAMPLE로 표시한 V35 계산을 바로잡음
  • 재채점 콜백 대상: 결과가 접수 응답보다 먼저 와도 세션의 지난 작업 행에 쓰지 않고 이 작업의 행을 새로 만듭니다(기존 결함).

검증

  • 변경 전 822개 → 변경 후 ./gradlew test 883개 통과, 실패·오류·건너뜀 0.
  • 환경: JDK 21, CI와 같은 postgres:16-alpine 임시 컨테이너.
  • InterviewCreateIdempotencyTest는 실제 DB에서 다음을 확인합니다.
    • 동시 6건 같은 키 → 면접 1개, 진 요청의 면접관 명단은 롤백
    • 실패 롤백 뒤 같은 키 재사용
    • 가져오기 ↔ 스냅샷이 서로 잠금을 기다리는지(양방향)

ETC (리뷰·배포 때 확인 부탁)

  • 운영 동작 변경: 파일을 지워 기업을 내리던 절차가 있다면 COMPANY_HIDDEN_CODES=code1,code2로 바꿔야 합니다.
  • SAMPLE 허용: COMPANY_ALLOW_SAMPLE을 비우면 prod 프로필이 아닐 때만 샘플을 허용합니다. 운영이 prod 프로필로 뜨지 않는다면 COMPANY_ALLOW_SAMPLE=false를 직접 넣어 주세요. 기동 로그에 판정 결과(SAMPLE 기업 허용=)가 남습니다.
  • 배포 순서: Analysis가 questionIds를 채워 보내도록 먼저 배포돼야 합니다. 빈 questionIds를 보내는 옛 Analysis라면 기업 코멘트가 모두 null이 됩니다(명세 05의 Analysis → Chat → API 순서).
  • V35 FK 사전 확인: 아래 쿼리가 0건인지 배포 전에 확인해 주세요. 위반 행이 있으면 마이그레이션이 실패해 서버가 뜨지 않습니다.
    select f.fact_id from company_fact f join company_source s on s.source_id = f.source_id where s.company_id <> f.company_id;
  • 알려진 한계: 같은 면접관을 한 번은 personaId, 한 번은 personaName으로 보내면 다른 설정으로 봅니다.
  • 범위 밖: Web·Chat·Analysis 변경, 실데이터 검수본 공개, 실제 서버 간 계약 테스트는 이 PR에 없습니다.

🤖 Generated with Claude Code

- 면접 생성: 선택 Idempotency-Key(UUID) 헤더. 같은 사용자·키·설정이면 처음 만든 면접을
  돌려주고, 설정이 다르면 409 IDEMPOTENCY_KEY_REUSED. 면접·면접관·키를 한 트랜잭션에
  저장한다. 헤더·companyId 형식 오류는 422 INVALID_INTERVIEW_REQUEST.
- 스냅샷: 기업 행을 FOR SHARE로 읽고 가져오기는 FOR UPDATE로 고쳐 섞인 스냅샷을 막는다.
  없는·숨긴 기업은 404 COMPANY_UNAVAILABLE, 실행 입력을 만들 수 없으면 500 대신
  503 COMPANY_CONTEXT_UNAVAILABLE.
- 가져오기: 파일이 빠져도 숨기지 않고 app.company.hidden-codes로만 숨긴다.
  app.company.allow-sample(기본 false)이 꺼진 환경은 SAMPLE을 가져오지도 내보내지도 않는다.
  자기 참조·순환 모회사는 관계만 빼고 반영한다. 로그에서 원문 값을 뺐다.
- 목록: 검수 기록이 있거나 허용된 SAMPLE만 내보내고, 이름이 같으면 companyId 순.
- companyFit: 스냅샷 factId와 그 면접의 답변 있는 문항만 근거로 남기고 kind·focus는
  스냅샷에서 채운다. 항목이 모두 빠지면 null. 기업 미선택 면접은 조회에서도 null.
- 오류 응답에 선택 code 필드, 준비 응답에 company 배지 추가.
- V35: company.sample, fact의 자기 기업 출처 복합 FK, interview_create_request.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@danjee2009 danjee2009 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

API 서버의 기업 데이터 가져오기·공개 조건, 면접 생성 멱등성, 스냅샷 기반 기업 적합도 검증을 검토했습니다. ./gradlew test CI가 통과했습니다. 아래 항목은 콜백 처리 시점의 답변 상태가 채점 요청 시점과 달라질 수 있는 데이터 일관성 문제입니다.

- 콜백 때 면접 기록에서 답변 있는 문항을 다시 뽑으면, 요청 뒤 답변 API로 더해진 답변의
  문항까지 근거로 통과했다. 요청에 실은 답변 있는 문항 id를 남기고 그 목록만 인정한다.
- 접수 행(feedback.requested_question_ids)에 작업별 목록을 넣는다. 콜백이 접수 응답보다
  먼저 오는 경우를 위해 보내기 직전 interview.last_feedback_question_ids에도 남긴다.
- 둘 다 없는 콜백(목록을 남기기 전에 보낸 요청)은 어느 문항도 근거로 인정하지 않는다.
- V36 마이그레이션.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@danjee2009
danjee2009 merged commit 9bc0ab3 into main Oct 19, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants