Skip to content

[REFACTOR] 불필요한 트랜잭션 제거 - #39

Merged
kseysh merged 3 commits into
mainfrom
fix/#38-delete-transaction
Dec 10, 2025
Merged

kseysh merged 3 commits into
mainfrom
fix/#38-delete-transaction

Conversation

@kseysh

@kseysh kseysh commented Dec 10, 2025

Copy link
Copy Markdown
Owner

Related issue 🛠

Work Description ✏️

  • LLM 호출시에도 커넥션 풀을 붙잡고 있는 트랜잭션을 제거했습니다.
  • 사용하지 않는 Deprecated API를 제거했습니다.

@kseysh kseysh self-assigned this Dec 10, 2025
@kseysh kseysh changed the title [FIX] 불필요한 트랜잭션 제거 [REFACTOR] 불필요한 트랜잭션 제거 Dec 10, 2025
@coderabbitai

coderabbitai Bot commented Dec 10, 2025 •

Copy link
Copy Markdown

Summary by CodeRabbit

릴리스 노트

  • Revert

    • 더 이상 사용되지 않는 API 엔드포인트 제거 (채팅 및 채팅방 생성 관련 구 버전 API)
  • Bug Fixes

    • 채팅 서비스의 오류 처리 및 유효성 검사 개선
    • Null 값에 대한 안전성 강화

✏️ Tip: You can customize this high-level summary in your review settings.

Walkthrough

더 이상 사용되지 않는 채팅 API 엔드포인트 및 응답 타입을 제거하고, 불필요한 @Transactional 어노테이션을 ChatFacade 및 LLMService에서 제거하여 커넥션 풀 점유 시간을 단축합니다.

Changes

Cohort / File(s) 변경 요약
더 이상 사용되지 않는 응답 클래스 제거
chat/src/main/java/core/chat/controller/response/ChatResponseDeprecated.java, chat/src/main/java/core/chat/controller/response/CreateChatRoomResponseDeprecated.java
더 이상 사용되지 않는 DTO 클래스 두 개(ChatResponseDeprecated, CreateChatRoomResponseDeprecated)를 완전히 삭제하고 관련 정적 팩토리 메서드 제거
더 이상 사용되지 않는 API 엔드포인트 제거
chat/src/main/java/core/chat/controller/ChatController.java
더 이상 사용되지 않는 엔드포인트 핸들러(POST /v1/chat, POST /v1/chat/room/create)와 관련 메서드(chatDeprecated, createChatRoomDeprecated) 제거 및 임포트 정리
더 이상 사용되지 않는 서비스 메서드 및 트랜잭션 제거
chat/src/main/java/core/chat/service/ChatFacade.java
더 이상 사용되지 않는 메서드(chatDeprecated, startNewChatDeprecated) 제거 및 여러 공개 메서드에서 불필요한 @Transactional 어노테이션 제거
트랜잭션 어노테이션 제거 및 오류 처리 강화
mcp-client/src/main/java/core/mcpclient/service/LLMService.java
네 개의 공개 메서드에서 @Transactional 어노테이션 제거, org.springframework.transaction.annotation.Transactional 임포트 삭제, startNewChat에서 응답 콘텐츠에 대한 null 체크 추가 및 오류 처리 로직 개선

추정 코드 리뷰 소요 시간

🎯 2 (Simple) | ⏱️ ~10분

  • ChatFacade: 더 이상 사용되지 않는 메서드 및 @Transactional 어노테이션 제거 시 다른 메서드들이 영향을 받지 않는지 확인 필요
  • LLMService: startNewChat 메서드의 null 체크 로직 추가로 인한 오류 처리 경로 검증 필요

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 변경사항의 주요 내용을 명확하게 나타냅니다. '불필요한 트랜잭션 제거'는 PR에서 수행된 핵심 작업을 정확하게 반영하고 있습니다.
Description check ✅ Passed PR 설명이 변경사항과 관련이 있으며, LLM 호출 중 트랜잭션 제거와 사용하지 않는 Deprecated API 제거라는 두 가지 작업을 언급하고 있습니다.
Linked Issues check ✅ Passed PR이 연결된 이슈 #38의 요구사항을 충족합니다. 불필요하게 커넥션 풀을 잡고 있는 @transactional 어노테이션이 4개 메서드에서 제거되었습니다.
Out of Scope Changes check ✅ Passed PR의 모든 변경사항이 이슈 #38의 범위 내에 있습니다. Deprecated API 제거도 PR 설명에 명시되어 있으며, 불필요한 코드 정리라는 범주에 속합니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/#38-delete-transaction

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 and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
chat/src/main/java/core/chat/service/ChatFacade.java (1)

93-112: 다중 엔티티 저장 시 트랜잭션 범위 검토 필요

이 메서드는 chatRoomService.saveChatRoom(chatRoom)과 chatHistoryService.saveChatHistory(userChat, llmChat)를 순차적으로 호출합니다. @Transactional 제거 후 chatRoom 저장 성공 → chatHistory 저장 실패 시 고아 데이터가 발생할 수 있습니다.

LLM 호출(line 97) 후에 DB 저장을 분리하는 방식으로 리팩토링하면, LLM 호출 중 커넥션 점유 문제와 데이터 정합성을 모두 해결할 수 있습니다.

// 예시: LLM 호출과 DB 저장 분리
public CreateChatRoomResponse startNewChat(String userId, CreateChatRoomRequest request) {
    Long roomId = Snowflake.getInstance().nextId();
    String question = request.getQuestion();

    // LLM 호출 (트랜잭션 외부)
    NewChatRoomInfo newChatRoomInfo = llmService.startNewChat(roomId, question);

    // DB 저장 (별도 트랜잭션 메서드로 분리)
    return saveNewChatData(roomId, userId, question, newChatRoomInfo);
}

@Transactional
protected CreateChatRoomResponse saveNewChatData(Long roomId, String userId, String question, NewChatRoomInfo info) {
    // chatRoom, userChat, llmChat 저장 로직
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 59a3edd and 613b381.

📒 Files selected for processing (5)
  • chat/src/main/java/core/chat/controller/ChatController.java (0 hunks)
  • chat/src/main/java/core/chat/controller/response/ChatResponseDeprecated.java (0 hunks)
  • chat/src/main/java/core/chat/controller/response/CreateChatRoomResponseDeprecated.java (0 hunks)
  • chat/src/main/java/core/chat/service/ChatFacade.java (1 hunks)
  • mcp-client/src/main/java/core/mcpclient/service/LLMService.java (2 hunks)
💤 Files with no reviewable changes (3)
  • chat/src/main/java/core/chat/controller/response/ChatResponseDeprecated.java
  • chat/src/main/java/core/chat/controller/response/CreateChatRoomResponseDeprecated.java
  • chat/src/main/java/core/chat/controller/ChatController.java
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-20T07:10:22.381Z
Learnt from: kseysh
Repo: kseysh/factory-chatbot PR: 24
File: chat/src/main/java/core/chat/service/ChatFacade.java:96-100
Timestamp: 2025-11-20T07:10:22.381Z
Learning: In chat/src/main/java/core/chat/service/ChatFacade.java, the `checkUserCanDeleteRoom` method returns `orElse(true)` when a chat room doesn't exist. This is intentional to ensure idempotency of the DELETE operation - allowing deletion of non-existent rooms to succeed silently, treating DELETE as a declaration of desired end state rather than an action.

Applied to files:

  • chat/src/main/java/core/chat/service/ChatFacade.java
🔇 Additional comments (5)
chat/src/main/java/core/chat/service/ChatFacade.java (3)

54-54: boundedElastic 스케줄러 사용 적절함

블로킹 호출인 canUserAccessRoom을 boundedElastic 스케줄러에서 실행하여 리액티브 스트림의 논블로킹 특성을 유지한 좋은 패턴입니다.


197-205: deleteRoom에 @Transactional 유지 적절함

삭제 작업은 LLM 호출이 없고, chatRoom과 chatHistory를 함께 삭제해야 하므로 트랜잭션 유지가 올바른 선택입니다.


29-47: No issue—ChatHistoryService.saveChatHistory() has @Transactional annotation on the method itself (line 17), ensuring both user and LLM chat histories are saved atomically regardless of the @Transactional removal from ChatFacade.chat().

mcp-client/src/main/java/core/mcpclient/service/LLMService.java (2)

42-49: @Transactional 제거 적절함

LLMService는 외부 LLM API를 호출하며 DB 작업을 수행하지 않습니다. 트랜잭션 제거로 LLM 응답 대기 중 커넥션 풀 점유 문제가 해결됩니다. PR 목표에 부합하는 변경입니다.


72-98: 스트리밍 응답에서 구분자 누락 시 동작 확인 필요

startNewChatStream에서는 startNewChat과 달리 구분자(§)가 없을 때 예외를 던지지 않고 전체 chunk를 roomName으로 처리합니다 (line 87). 스트리밍 특성상 의도된 동작일 수 있으나, LLM이 구분자 없이 응답을 완료하면 answer가 빈 문자열이 될 수 있습니다.

호출부(ChatFacade.startNewChatStream)에서 빈 answer에 대한 처리가 있는지 확인이 필요합니다.

Comment on lines +60 to +66
if (responseContent == null) {
throw new RuntimeException("LLM 응답이 존재하지 않습니다.");
}
int separatorIndex = responseContent.indexOf(TITLE_SEPARATOR);
if (separatorIndex == -1) throw new RuntimeException("LLM의 답변에 제목과 본문을 구분하는 구분자가 존재하지 않습니다.");
if (separatorIndex == -1) {
throw new RuntimeException("LLM의 답변에 제목과 본문을 구분하는 구분자가 존재하지 않습니다.");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Null 체크 및 에러 처리 개선 승인

LLM 응답이 null이거나 구분자가 없는 경우에 대한 방어 로직 추가가 좋습니다. 다만, RuntimeException 대신 커스텀 예외나 보다 구체적인 예외 타입을 사용하면 호출부에서 에러 핸들링이 용이해집니다.

+// 예: LLMResponseException 커스텀 예외 사용
 if (responseContent == null) {
-    throw new RuntimeException("LLM 응답이 존재하지 않습니다.");
+    throw new LLMResponseException("LLM 응답이 존재하지 않습니다.");
 }
 int separatorIndex = responseContent.indexOf(TITLE_SEPARATOR);
 if (separatorIndex == -1) {
-    throw new RuntimeException("LLM의 답변에 제목과 본문을 구분하는 구분자가 존재하지 않습니다.");
+    throw new LLMResponseException("LLM의 답변에 제목과 본문을 구분하는 구분자가 존재하지 않습니다.");
 }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In mcp-client/src/main/java/core/mcpclient/service/LLMService.java around lines
60 to 66, the code throws generic RuntimeException when responseContent is null
or missing the TITLE_SEPARATOR; replace these with a more specific, descriptive
exception type (e.g., create and throw a custom unchecked LLMResponseException
or use an appropriate existing exception like IllegalStateException) so callers
can handle errors explicitly; implement the new exception class in the same
package with constructors that accept a message and an optional cause, throw
that exception with clear messages (include relevant context such as the
response or separator value), and update any callers/tests that expect
RuntimeException to handle the new exception type.

@kseysh
kseysh merged commit 4b53488 into main Dec 10, 2025
2 checks 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.

[REFACTOR] 불필요한 트랜잭션 제거

1 participant