모든 Core Server REST API는 본 문서의 규약을 따른다. OpenAPI 스펙은 springdoc-openapi가 자동 생성하고, 프론트는
openapi-typescript로 타입을 생성한다.
/api/{resource} # 컬렉션
/api/{resource}/{id} # 단일 리소스
/api/{resource}/{id}/{sub} # 서브 리소스
/api/{resource}/{id}:{action} # 액션 (PATCH 대신 명시적 동사)
- 리소스명은 복수형, kebab-case 금지, snake_case 금지 →
repositories,interview-sessions대신sessions(짧고 명확하게) - 액션이 RESTful 동사로 표현 안 될 때만
:start,:end형식 사용
| 동작 | 메서드 | 경로 예시 |
|---|---|---|
| 컬렉션 조회 | GET | /api/sessions |
| 단건 조회 | GET | /api/sessions/{id} |
| 생성 | POST | /api/sessions |
| 부분 수정 | PATCH | /api/sessions/{id} |
| 전체 교체 | PUT | (거의 사용하지 않음) |
| 삭제 (soft) | DELETE | /api/sessions/{id} |
| 액션 트리거 | PATCH | /api/sessions/{id}/start, /api/sessions/{id}/end |
POST /api/auth/github GitHub OAuth 시작 (URL 발급)
GET /api/auth/github/callback OAuth 콜백 처리
POST /api/auth/refresh 토큰 갱신
DELETE /api/auth/logout 로그아웃 (refresh token revoke)
GET /api/users/me 내 정보
PATCH /api/users/me 내 정보 수정 (제한적)
DELETE /api/users/me 회원 탈퇴 (soft)
GET /api/users/me/stats 내 통계 (점수 추이)
GET /api/users/me/consents 동의 이력
POST /api/users/me/consents 동의 제출
GET /api/repositories 내가 등록한 레포 목록
GET /api/repositories/github GitHub의 내 전체 레포 (등록 가능 후보)
POST /api/repositories 등록
GET /api/repositories/{id} 상세 (분석 상태 포함)
DELETE /api/repositories/{id} 소프트 삭제
POST /api/repositories/{id}/reanalyze 재분석 요청
POST /api/resumes 업로드
GET /api/resumes 목록
GET /api/resumes/{id} 상세
DELETE /api/resumes/{id} 소프트 삭제
POST /api/resumes/{id}/reanalyze 재분석
GET /api/documents 분석 문서 목록
GET /api/documents/{id} 분석 문서 상세 (S3 URL 포함)
POST /api/sessions 생성
GET /api/sessions 히스토리 목록
GET /api/sessions/{id} 상세 (메시지/피드백 포함)
PATCH /api/sessions/{id} 메모/제목 수정
PATCH /api/sessions/{id}/start 시작 (READY → IN_PROGRESS)
PATCH /api/sessions/{id}/end 종료
DELETE /api/sessions/{id} 소프트 삭제
GET /api/sessions/{id}/messages 메시지 목록
POST /api/sessions/{id}/messages 메시지 추가 (RealTime → Core 전용)
GET /api/sessions/{id}/feedback 피드백 조회
GET /api/system/health 헬스체크 (PG/MQ/S3/AI 상태)
GET /api/system/version 버전 정보
/api/internal/* prefix. 외부에서 호출 금지 — Spring Security에서 외부 패킷을 차단하고 X-Internal-API-Key 헤더로 검증. AI Server와 RealTime Server만 호출자.
GET /api/internal/users/{userId}/github-token AI → Core
PUT /api/internal/documents/{documentId}/embeddings AI → Core
자세한 요청·응답 스키마와 인증 규약은 §10 내부 API 부록 참조.
| 헤더 | 필수 | 설명 |
|---|---|---|
Authorization: Bearer {token} |
인증 필요 시 | JWT access token |
X-Trace-Id |
권장 | 클라이언트가 부여하지 않으면 Gateway가 부여 |
Content-Type: application/json |
POST/PATCH | multipart/form-data는 파일 업로드 시만 |
GET /api/sessions?page=0&size=20&sort=createdAt,desc
- 0-based page
size최대 100 (초과 시 100으로 clamp)- 정렬:
{field},{asc|desc}형식, 다중 정렬은&sort=반복
GET /api/sessions?status=COMPLETED&jobCategory=BACKEND&from=2026-01-01&to=2026-04-30
- 날짜는 ISO 8601 (
YYYY-MM-DD또는 RFC 3339) - 다중 값은 콤마:
?status=COMPLETED,CANCELLED
단건:
{
"id": 42,
"title": "백엔드 모의면접 #3",
"status": "COMPLETED",
"createdAt": "2026-04-25T14:30:00Z"
}컬렉션 (페이지):
{
"content": [ {...}, {...} ],
"page": 0,
"size": 20,
"totalElements": 137,
"totalPages": 7,
"first": true,
"last": false
}표준 형식:
{
"code": "RESUME_INVALID_FILE_TYPE",
"message": "PDF 파일만 업로드 가능합니다.",
"traceId": "9f4e5b...",
"timestamp": "2026-04-27T15:20:00Z",
"details": {
"uploadedType": "image/jpeg"
}
}| 필드 | 설명 |
|---|---|
code |
도메인 에러 코드 (대문자 SNAKE_CASE) — 클라이언트 분기 기준 |
message |
사용자에게 노출 가능한 한국어 메시지 |
traceId |
로그 추적용, 사용자 문의 시 활용 |
details |
추가 컨텍스트 (validation 실패 필드 등) |
| 상황 | 코드 |
|---|---|
| 성공 (조회/수정) | 200 |
| 생성 성공 | 201 (Location 헤더 포함) |
| 비동기 처리 시작 | 202 |
| 본문 없음 (DELETE 등) | 204 |
| 잘못된 요청 (validation 실패) | 400 |
| 인증 누락/만료 | 401 |
| 권한 부족 | 403 |
| 리소스 없음 | 404 |
| 충돌 (중복 등록 등) | 409 |
| 처리 가능하지만 의미적 오류 | 422 |
| 서버 오류 | 500 |
| 외부 의존성 장애 (LLM API 등) | 502 / 503 |
신규 추가 시 본 카탈로그에 반드시 등록. 같은 코드를 두 도메인에서 사용 금지.
# 인증 (AUTH_*)
AUTH_INVALID_TOKEN 401 토큰 형식 오류
AUTH_EXPIRED_TOKEN 401 만료
AUTH_REVOKED_TOKEN 401 revoke됨
AUTH_GITHUB_OAUTH_FAILED 401 GitHub OAuth 실패
AUTH_CONSENT_REQUIRED 403 필수 동의 미완료
# 사용자 (USER_*)
USER_NOT_FOUND 404
USER_ALREADY_DELETED 410
# 이력서 (RESUME_*)
RESUME_INVALID_FILE_TYPE 400 PDF 외 업로드
RESUME_FILE_TOO_LARGE 400 크기 초과
RESUME_EMPTY_FILE 400
RESUME_NOT_FOUND 404
RESUME_IN_USE 409 활성 세션에서 사용 중
# 레포 (REPO_*)
REPO_NOT_FOUND 404
REPO_ALREADY_REGISTERED 409
REPO_GITHUB_API_FAILED 502
REPO_PRIVATE_NO_ACCESS 403
# 분석 (DOC_*)
DOC_NOT_ANALYZED 422 ANALYZED 상태 아님
DOC_ANALYSIS_FAILED 500
DOC_NOT_FOUND 404
# 세션 (SESSION_*)
SESSION_INVALID_STATE 422 상태 전이 불가
SESSION_MAX_REACHED 422 최대 질문/시간 도달
SESSION_NOT_FOUND 404
SESSION_FORBIDDEN 403 타인 세션 접근
# 시스템 (SYS_*)
SYS_RATE_LIMITED 429
SYS_DEPENDENCY_DOWN 503 RabbitMQ/AI/LLM 다운
SYS_INTERNAL_ERROR 500
POST 요청 중 결제·메시지 발행 등 중복 처리 위험이 있는 경우:
POST /api/sessions
Idempotency-Key: 8b3c5e2a-...
서버는 24시간 동안 같은 키로 들어온 요청에 대해 동일 응답을 반환.
Phase 1에서는 결제 없으므로 필수는 아니나, AI 작업 발행 API(
reanalyze등)에 적용 권장.
- 본 문서 기준 v1. 모든 경로에
/api/v1/...접두는 사용하지 않음 (Breaking Change 발생 시점에 도입). - Breaking Change 정책: 신규 경로 추가 + 구 경로는 최소 1개 phase 유지 후 제거.
- springdoc-openapi
application.properties:springdoc.api-docs.path=/api/v3/api-docs springdoc.swagger-ui.path=/api/swagger-ui.html @Operation,@Schema애노테이션으로 응답 예시 작성
openapi-typescript으로frontend/src/shared/api/generated.ts생성- 빌드 스크립트:
npx openapi-typescript http://localhost:8080/api/v3/api-docs -o src/shared/api/generated.ts
- 변경 시 PR에 포함
POST/PATCH가 즉시 완료되지 않는 경우 (AI 분석 트리거 등):
HTTP/1.1 202 Accepted
{
"taskId": "doc-analyze-42",
"status": "QUEUED",
"statusUrl": "/api/documents/42",
"streamUrl": "/api/stream/documents/42"
}클라이언트는:
- SSE (
streamUrl) 우선 구독 → 상태 푸시 수신 - SSE 끊기면
statusUrl폴링 fallback (5초 간격)
상세: event-stream.md
/api/internal/* endpoint는 다음 규약을 따른다.
- 모든 요청에
X-Internal-API-Key: {key}헤더 필수 - 키는 환경변수
CORE_INTERNAL_API_KEY(Core/AI/RealTime 동일 값) - JWT 사용자 인증 불필요. 단 호출자가 신뢰 영역 안에 있어야 함 (Docker 내부망, K8s ClusterIP 등)
- 키 누락/불일치 →
401 Unauthorized, body:{ "code": "INTERNAL_AUTH_FAILED" }
분석 시점에 사용자 GitHub access token을 짧게 위임. AES-256으로 저장된 평문 token을 복호화해 반환 (메모리/로그/응답 body에만 노출, DB·이벤트엔벨로프에는 절대 동봉하지 않음).
Path parameter
| 이름 | 타입 | 설명 |
|---|---|---|
userId |
long | users.id |
Response 200
{ "accessToken": "ghp_xxx..." }Response 404 — 사용자 없음 또는 token 누락 (USER_NOT_FOUND)
Response 401/403 — 인증 실패 (INTERNAL_AUTH_FAILED)
Response 5xx — Core 일시 장애 (호출자는 retriable로 간주)
analyzed_documents 한 건에 대한 chunk + embedding을 document_embeddings 테이블에 idempotent upsert. AI가 분석 결과를 callback 발행 직전에 호출한다.
Path parameter
| 이름 | 타입 | 설명 |
|---|---|---|
documentId |
long | analyzed_documents.id (Core가 publish 직전 미리 생성) |
Request body
{
"model": "gemini-embedding-001",
"dim": 1536,
"chunks": [
{ "chunkIndex": 0, "chunkText": "...", "embedding": [0.012, -0.003, ...] },
{ "chunkIndex": 1, "chunkText": "...", "embedding": [...] }
]
}| 필드 | 타입 | 비고 |
|---|---|---|
model |
string | 임베딩 모델 ID (감사용) |
dim |
int | 임베딩 차원. DB 컬럼 차원과 일치해야 함 (불일치 → 400) |
chunks[].chunkIndex |
int | 0-base, 같은 document 내 unique |
chunks[].chunkText |
string | 원문 청크 (≤ N KB, 컬럼 길이 제한) |
chunks[].embedding |
float[] | 길이 == dim |
Response 200
{ "upserted": 18 }Response 400 — dim 불일치, payload 검증 실패 (EMBEDDING_BAD_REQUEST)
Response 404 — analyzed_documents.id 없음 (DOCUMENT_NOT_FOUND)
Response 401/403 — INTERNAL_AUTH_FAILED
Response 5xx — Core 일시 장애 (retriable)
Idempotency
- 같은
(documentId, chunkIndex)조합 재호출 시 row를 덮어씀 (INSERT ... ON CONFLICT UPDATE) - 부분 재시도 시 누락 청크가 발생하지 않도록 AI는 전체 청크를 한 번에 보내는 것을 권장