[BE] 챗봇 도메인 테크 스펙 - 100-hours-a-week/9-team-Devths-WIKI GitHub Wiki
배경 (Background)
프로젝트 목표
- 사용자별 AI 채팅방을 생성/조회/삭제하고, 채팅방 내 메시지(사용자 발화/AI 응답/시스템 메시지)를 저장한다.
- 메시지 저장을 통해 “대화 기록 기반의 재질문/회고/결과 재조회”를 가능하게 한다.
- 면접 모드의 경우, 면접 세션의 진행 상태(질문 수/진행중/완료)를 별도 엔티티로 관리한다.
설계 및 기술 자료 (Architecture and Technical Documentation)
공통 인증/인가
인증
- 모든 HTTP API는
Authorization: Bearer {accessToken}필수 - Spring Security FilterChain에서 검증
JwtAuthenticationFilter#doFilterInternal()
- 실패 시
AuthenticationEntryPoint에서 401 반환
인가
- AI 채팅방은
ai_chat_rooms.user_id가 곧 소유자이므로, 모든 roomId 기반 요청은 다음 조건을 만족해야 함: aiChatRoom.userId == currentUserId- 불일치 시 403
"인가 실패: 해당 AI 채팅방에 접근 권한이 없습니다."
레이어/클래스 구성
Controller
AiChatRoomControllerAiChatMessageController
Service
AiChatRoomServiceAiChatMessageServiceAiChatAuthorizationService- (면접)
AiInterviewService - (연동)
AiServerClient(FastAPI 호출)
Repository
AiChatRoomRepositoryAiChatMessageRepositoryAiChatInterviewRepository
데이터베이스 스키마(ERD)
https://www.erdcloud.com/d/NyT4nhhqhXwbhRFQP
주요 테이블
ai_chat_rooms
id (PK)user_id (FK): 방 소유자room_uuid (varchar(36), unique): AI 서버/벡터DB와 공유할 고유 키title (varchar(100), nullable): 채팅방 제목(“AI 요약으로 들어가는 채팅방 제목”)created_at,updated_atis_deleted,deleted_at(soft delete)
ai_chat_messages
id (PK)room_id (FK)interview_id (FK, nullable): 면접 세션 연결용role (varchar(20)): USER / ASSISTANT / SYSTEM ...content (LONGTEXT): AI 답변 고려하여 LONGTEXTtype (varchar(10)): NORMAL / REPORT / INTERVIEW (라벨)metadata (JSON, nullable): 응답 메타데이터(점수/요약/강점 등)created_at
ai_chat_interview
id (PK)room_id (FK): 면접이 진행되는 AI 채팅방interview_type (varchar(20)): 인성/기술 등current_question_count (int, default 0): 0~5status (varchar(20), default IN_PROGRESS): 진행중/완료created_at,updated_at
API 명세 (API Specification)
AI 채팅방 목록 조회
GET /api/ai-chatrooms
목적
- 로그인 사용자의 AI 채팅방 목록을 조회한다(soft delete 제외).
상세 구현
- 인증
- FilterChain에서 JWT 검증
- 실패 시 401
- DB 조회
aiChatRoomRepository.findAllByUserIdAndIsDeletedFalseOrderByUpdatedAtDesc(userId)- 정렬 기준: 최근 채팅한 방 먼저 (updated_at DESC)
- DTO 매핑
- roomId, roomUuid, title, createdAt, updatedAt
- 응답
- 200 OK
"AI 채팅방 목록을 성공적으로 조회하였습니다."
AI 채팅방 생성
POST /api/ai-chatrooms
목적
- 사용자 소유의 AI 채팅방을 생성한다.
title기본값은"새 채팅방"
상세 구현
- 인증 → 실패 시 401
- 채팅방 엔티티 생성
roomUuid = UUID.randomUUID().toString()title = "새 채팅방"createdAt = now,updatedAt = nowisDeleted = false
- DB 저장
aiChatRoomRepository.save(room)
- 응답(201)
- roomId, roomUuid, title, createdAt 반환
AI 채팅방 텍스트 전송
POST /api/ai-chatrooms/{roomId}/messages
목적
- 사용자의 메시지를 저장하고, AI 서버(또는 내부 LLM 모듈)에서 응답을 생성하여, USER 메시지 + ASSISTANT 메시지를 함께 저장 후 반환한다.
요청
- path variable:
roomId - body:
{ "content": "메시지 전송" }
상세 구현
- 인증 → 실패 시 401
- PathVariable / Body 검증
- roomId Long 파싱 실패 또는 <=0 → 400
- content null/blank → 400
- 채팅방 조회 + soft delete 체크
aiChatRoomRepository.findByIdAndIsDeletedFalse(roomId)- 없으면 404
"해당 AI 채팅방을 찾을 수 없습니다."
- 인가(소유자 체크)
room.userId == currentUserId확인- 불일치면 403
- USER 메시지 저장
AiChatMessage userMsg = AiChatMessage.ofUser(roomId, interviewId, content)- role = USER
- type = NORMAL (스펙상 기본)
- metadata = null
- createdAt = now
aiChatMessageRepository.save(userMsg)→ messageId 확보
- AI 서버 호출(응답 생성)
AiServerClient.generateReply(roomUuid, content, optionalContext)- AI 서버/벡터DB에서 대화 컨텍스트를 room_uuid 기준으로 연결
- 응답:
aiContent,metadata(json)등
- ASSISTANT 메시지 저장
AiChatMessage aiMsg = AiChatMessage.ofAssistant(roomId, interviewId, aiContent, metadata)- role = ASSISTANT
- type = NORMAL (or REPORT/INTERVIEW)
- metadata = json(없으면 null)
aiChatMessageRepository.save(aiMsg)
- 채팅방 updatedAt 갱신
room.touchUpdatedAt(now)aiChatRoomRepository.save(room)또는 dirty-check
- 응답(201)
- data.userMessage / data.aiResponse 형태로 반환
"AI 챗봇이 성공적으로 응답을 생성했습니다."
예외/에러 매핑
- 400: roomId/content 검증 실패, 바인딩 오류
- 401: 인증 실패
- 403: 소유자 불일치
- 404: room 없음/삭제됨
- 500: AI 서버 오류, DB 오류, 예상치 못한 예외
AI 채팅 내역 조회
GET /api/ai-chatrooms/{roomId}/messages?size=n&lastId=k
목적
- 특정 AI 채팅방의 메시지를 커서 기반으로 조회한다.
파라미터
- size(default 10)
- lastId(Long) : 커서
상세 구현
- 인증 → 실패 401
- 검증
- roomId/size/lastId 파싱 및 범위 검증
- size <= 0 또는 지나치게 큰 값 제한 → 400
- 채팅방 조회 및 인가
aiChatRoomRepository.findByIdAndIsDeletedFalse(roomId)없으면 404- 소유자 불일치면 403
- 메시지 조회(커서 페이징)
- 최신부터 과거로:
id DESC - 첫 페이지: lastId 없으면 최신 size
- 다음 페이지:
id < lastId조건
- 최신부터 과거로:
- hasNext/lastId 계산
- size+1로 조회 후 초과분 있으면 hasNext=true
- 응답 lastId는 “이번 페이지의 마지막 messageId”로 내려줌
- DTO 매핑
- roomId, messageId, interviewId, role, content, type, metadata, createdAt
- 응답(200)
AI 채팅방 삭제
DELETE /api/ai-chatrooms/{roomId}
목적
- AI 채팅방을 soft delete 처리한다.
상세 구현
- 인증 → 401
- roomId 검증 → 400
- room 조회(+isDeleted=false) → 없으면 404
- 소유자 체크 → 불일치 403
- soft delete
room.isDeleted = trueroom.deletedAt = nowaiChatRoomRepository.save(room)(또는 dirty-check)
- 204 반환
면접 세션 생성 트리거
- “모의 면접 시작” 버튼 또는 특정 메시지(type=INTERVIEW) 진입 시
AiInterviewService#createSession(roomId, interviewType)- status=IN_PROGRESS
- currentQuestionCount=0
- createdAt=now
질문 수 증가/완료 처리
- USER 답변이 들어올 때마다
currentQuestionCount++ - 5회에 도달하면 status=COMPLETED로 변경 + updatedAt 기록
메시지와 인터뷰 연결
- 인터뷰 진행 중이면, 해당 세션 id를 메시지(interview_id)에 세팅하여 “특정 세션의 문답 묶음”을 조회/리포트 생성에 활용