Skip to content

[feat] 대화 저장 및 조회 API 구현#11

Merged
kite707 merged 6 commits into
devfrom
feat/3-save-conversation
Jul 20, 2026
Merged

[feat] 대화 저장 및 조회 API 구현#11
kite707 merged 6 commits into
devfrom
feat/3-save-conversation

Conversation

@kite707

@kite707 kite707 commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator

🔗 연관 이슈

📌 개요

사용자의 감정 기록(대화)을 DB에 저장하고 조회하는 기능을 구현했습니다. 채팅방(Conversation)과 말풍선(Message)을 분리해 저장하며, 이후 이슈 #4(LLM 댓글 생성)에서 캐릭터 메시지 또한 Message에 저장할 예정입니다.

🔧 주요 변경사항

도메인 · 스키마 (V2 마이그레이션)

  • Conversation — 채팅방. memberId(ID 참조) · 생성/수정 시각만 보유
  • Message — 말풍선. senderType(USER/CHARACTER) + emotionType(캐릭터일 때만, 6종) 분리로 발신 주체와 감정을 표현
    • 답장은 repliesToMessageId 자기참조로 표현 (nullable)
    • 엔티티 불변식: CHARACTER ⟺ emotionType 존재init에서 강제
  • EmotionType — 감정 캐릭터 6종(기쁨·분노·불안·까칠·다정·엉뚱), emotion 패키지에 정의

저장·조회 서비스

  • 저장: conversationId 없으면 새 채팅방 생성, 있으면 토큰을 통한 사용자 검증 후 이어서 저장(본인 소유 채팅방에만 대화 가능)
  • 답장 검증: 대상 메시지가 같은 채팅방에 존재해야 함 (남의 방 메시지에 답장 불가(400))
  • 날짜별 조회: 서비스상 "하루"는 06:00 KST ~ 익일 06:00
  • 하루 시작 시간은 conversation.day-start-time 설정값으로 조정 가능 (@ConfigurationProperties)

API 3종 (모두 로그인 필요)

  • POST /api/conversations/messages — 저장 (응답에 conversationId 포함 → 이후 클라 요청 시 conversationId 포함하여 요청)
  • GET /api/conversations?date= — 날짜별 채팅방 목록
  • GET /api/conversations/{id}/messages — 채팅방 메시지 전체 (id 오름차순)

공통 개선

  • ErrorCode 추가: CONVERSATION_NOT_FOUND(404) · CONVERSATION_ACCESS_DENIED(403)
  • GlobalExceptionHandler에 쿼리 파라미터 누락·형식 오류 핸들러 추가 — 기존엔 date=abc 같은 요청이 500으로 떨어졌음 → 400 INVALID_INPUT

🌐 API · DB 영향

  • API 변경: 신규 엔드포인트 3종 추가 (기존 API 변경 없음)
  • DB 마이그레이션: V2__conversation.sqlconversations · messages 테이블 생성 (기존 테이블 불변)
  • 하위 호환: 호환

💬 리뷰 포인트

1. 날짜 경계 로직 — 날짜별 조회의 "하루"는 KST 06:00 ~ 익일 06:00입니다. 요청받은 날짜(KST 기준)를 내부에서 UTC Instant 범위로 변환해 조회하며, DB 저장(created_at)도 UTC입니다. 단위 테스트로 경계 변환을(2026-07-1907-18T21:00Z ~ 07-19T21:00Z), 통합 테스트로 새벽 2시(KST)에 만든 방이 전날 목록에만 나오는 것을 확인했습니다.

2. 채팅방/메시지 관련 기능에서 토큰을 이용한 유저 검증AuthPrincipal.memberId와 채팅방 주인을 비교합니다(불일치 시 403).

3. messages.content가 VARCHAR(500)인 이유 — 사용자 입력은 140자 제한(DTO 검증)이지만, 같은 테이블에 이후 LLM 캐릭터 메시지(#4)가 들어오므로 컬럼은 여유 있게 잡았습니다.

4. 날짜별 목록은 방 "생성일" 기준 — 자동 종료(새벽 6시)가 아직 없어 어제 만든 방에 오늘 이어 쓸 수 있는데, 이 방은 어제 목록에만 나옵니다. 종료 기능이 들어올 때 자연스럽게 정리될 부분이라 이번 범위에선 그대로 뒀습니다.

5. 이슈와 관련없는 작업 분리 — 추후 채팅방 종료 후 카드 생성기능이나, 채팅방 삭제와 같은 기능에 필요한 컬럼들이 있겠지만, 관련 이슈에서 작업하는게 맞다고 생각해 우선 작업하지 않았습니다. 테이블 생성 시 부터 미리 작업해두는게 좋다고 생각되면 말씀해주세요!

@kite707
kite707 requested a review from theminjunchoi July 19, 2026 13:25
@kite707 kite707 self-assigned this Jul 19, 2026
@github-actions

github-actions Bot commented Jul 19, 2026

Copy link
Copy Markdown

Test Results

132 tests  +30   132 ✅ +30   53s ⏱️ -6s
 27 suites + 4     0 💤 ± 0 
 27 files   + 4     0 ❌ ± 0 

Results for commit 3635042. ± Comparison against base commit 55dadf5.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Jul 19, 2026

Copy link
Copy Markdown

Test Coverage

Overall Project 93.23% -0.44% 🍏
Files changed 98.18% 🍏

File Coverage
ErrorCode.kt 100% 🍏
ConversationResponse.kt 100% 🍏
SaveMessageRequest.kt 100% 🍏
ConversationController.kt 100% 🍏
ConversationProperties.kt 100% 🍏
SenderType.kt 100% 🍏
ConversationService.kt 100% 🍏
GlobalExceptionHandler.kt 97.89% 🍏
MessageResponse.kt 97.14% -2.86% 🍏
Message.kt 96% -4% 🍏
EmotionType.kt 95% -5% 🍏
Conversation.kt 93.94% -6.06% 🍏

@theminjunchoi theminjunchoi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

좋은데요?
아래 질문하나 남겼습니다!
approve는 해둘게요!

Comment on lines +35 to +38
@LastModifiedDate
@Column(name = "updated_at", nullable = false)
var updatedAt: Instant = Instant.now()
protected set

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

궁금한 게 있습니다!

사실 Conversation 엔티티는 생성 후 절대 수정하지 않아서 udpatedAt이 죽은 필드/컬럼이 될 것 같은데, 만든 이유가 있을까요??

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Conversation 은 대화방이고 사용자가 남기는 메시지나 감정봇들이 다는 댓글은 모두 Message에 저장됩니다.

추후 해당 방의 입력이 몇 분 전에 입력된 것인지 보여줄 것을 생각하여 updatedAt을 넣어봤습니다!

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

해당 방의 마지막 입력이 몇 분 전인지는 마지막 메시지의 created_at을 보면 되지 않을까요...?

LastModifiedDate는 conversation 행 자체가 수정될 때만 갱신되는데, 사용자든 감정봇이든 메시지는 전부 Message에 INSERT라 conversation 행은 안 건드리니까, updatedAt이 갱신될 일이 없는 것 같아서요!

혹시 제가 잘못 이해한거라면 알려주세요!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

말씀하신대로네요! 지금대로라면 conversation의 updatedAt은 죽은 컬럼입니다...

해결을 위해 방안 두 가지를 검토봤는데요,

  1. 메시지 저장 시마다 conversation.updatedAt을 함께 갱신
  2. 목록 조회 시 방마다 마지막 메시지를 조회해서 시간 표시

2안은 조회할 때마다 window function 이나 서브쿼리로 방마다 최신 메시지를 골라야 해서, 상대적으로 자주 호출되는 조회 쿼리가 무거워집니다. 반면 1안은 메시지 저장 시 UPDATE 한 번 추가되는 정도라 비용이 훨씬 작아서 1안으로 작업해봤습니다.

다만 저장 지점이 늘어날 때마다 updatedAt 갱신을 빠뜨릴 위험이 있어서, Conversation.createMessage()로 메시지 생성 시 conversation의 updatedAt을 갱신하도록 했습니다. 이렇게 연관 엔티티의 정합성을 루트 엔티티가 책임지는 걸 DDD에서 Aggregate Root라고 부르더라구요. 이번 상황에 맞는 것 같아 적용해봤습니다!

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

굿굿 좋아요~

메시지 저장 지점마다 갱신을 빠뜨릴 위험이 있어, Conversation.createMessage()로
메시지 생성 통로를 일원화해 생성과 동시에 updatedAt이 갱신되도록 구현
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Caution

Review failed

An error occurred during the review process. Please try again later.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/3-save-conversation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@kite707
kite707 merged commit 895feff into dev Jul 20, 2026
4 checks passed
@kite707
kite707 deleted the feat/3-save-conversation branch July 20, 2026 03:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feat] 대화 저장

2 participants