Skip to content

20260128/mr6208 채팅방 나가기 기능추가#724

Merged
mr6208 merged 8 commits intotestfrom
20260128/mr6208_채팅방_나가기_기능추가
Feb 2, 2026

Hidden character warning

The head ref may contain hidden characters: "20260128/mr6208_\ucc44\ud305\ubc29_\ub098\uac00\uae30_\uae30\ub2a5\ucd94\uac00"
Merged

20260128/mr6208 채팅방 나가기 기능추가#724
mr6208 merged 8 commits intotestfrom
20260128/mr6208_채팅방_나가기_기능추가

Conversation

@mr6208
Copy link
Contributor

@mr6208 mr6208 commented Jan 28, 2026

✨ 변경 사항


✅ 테스트


  • 수동 테스트 완료
  • 테스트 코드 완료

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 사용자가 채팅방에서 나갈 수 있는 엔드포인트 추가
    • 채팅 활성화 여부(chatEnabled) 및 상대방 퇴장 여부(opponentLeft) 표시 추가
  • 버그 수정

    • 메시지 전송 전 채팅 가능 여부 검증 강화
    • 퇴장한 사용자의 재퇴장/중복 처리 방지 및 채팅 차단 보완
    • 채팅방 목록 필터링 로직 개선
  • Chores

    • 불필요한 멤버 검증 제거 및 정리

@mr6208 mr6208 self-assigned this Jan 28, 2026
@mr6208 mr6208 added the feat 새로운 기능 추가 (Feature) label Jan 28, 2026
@coderabbitai
Copy link

coderabbitai bot commented Jan 28, 2026

Caution

Review failed

The pull request is closed.

Note

.coderabbit.yaml has unrecognized properties

CodeRabbit is using all valid settings from your configuration. Unrecognized properties (listed below) have been ignored and may indicate typos or deprecated fields that can be removed.

⚠️ Parsing warnings (1)
Validation error: Unrecognized key(s) in object: 'ignored_branch'
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Walkthrough

채팅방 나가기 기능을 추가하고 관련 DTO/매퍼/서비스/엔티티/레포지토리/에러코드를 확장합니다. 컨트롤러에 POST /api/chat-room/{chat-room-id}/leave 엔드포인트가 추가되며, 채팅 가능 여부 검증과 퇴장 상태를 추적하도록 변경됩니다.

Changes

Cohort / File(s) Summary
컨트롤러 및 API 문서
ticketmate-api/src/main/java/.../ChatRoomController.java, ticketmate-api/src/main/java/.../ChatRoomControllerDocs.java
POST /api/chat-room/{chat-room-id}/leave 엔드포인트 추가, 인증·로깅, service 위임 및 API 문서 주석 추가.
응답 DTOs
ticketmate-chat/src/main/java/.../ChatRoomResponse.java, ticketmate-chat/src/main/java/.../ChatRoomContextResponse.java
chatEnabled, opponentLeft 불린 필드 추가 및 생성자/빌더 서명 확장.
매퍼
ticketmate-chat/src/main/java/.../ChatMapperImpl.java
ChatRoomResponse/ChatRoomContextResponse 매핑 시 chatEnabled, opponentLeft 계산·설정 추가.
메시지 서비스
ticketmate-chat/src/main/java/.../ChatMessageService.java
handleNewChatMessage에 validateCanChat 사전 검증 추가; 채팅 비활성화 시 예외 발생.
채팅 서비스
ticketmate-chat/src/main/java/.../ChatRoomService.java
leaveChatRoom(Member, String) 공개 메서드 추가, validateActiveParticipant 도입, 퇴장 처리 및 Redis 미읽음 카운터 제거.
엔티티 및 상수
ticketmate-chat/src/main/java/.../ChatRoom.java, ticketmate-chat/src/main/java/.../ChatRoomStatus.java
엔티티에 agentLeftDate, clientLeftDate, roomStatus, closedDate 추가; isLeft, isParticipant, canChat, leave 로직 추가; ChatRoomStatus enum(ACTIVE/CLOSED) 추가.
레포지토리 구현
ticketmate-chat/src/main/java/.../ChatRoomRepositoryImpl.java
검색 조건에서 떠난 참여자 제외 필터 추가(asAgent/asClient), 키워드 분기 수정 및 asAgent/asClient OR 결합.
에러 코드
ticketmate-common/src/main/java/.../ErrorCode.java
CHAT_ROOM_LEFT, CHAT_DISABLED 에러 코드 추가.
기타 서비스 변경
ticketmate-fulfillmentform/src/main/java/.../SuccessHistoryService.java
MemberService 의존성 및 관련 검증 제거.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant ChatRoomController as Controller
    participant ChatRoomService as Service
    participant ChatRoomEntity as Entity
    participant Repository as Repo
    participant Redis as Redis

    User->>Controller: POST /api/chat-room/{id}/leave
    Controller->>Service: leaveChatRoom(member, chatRoomId)
    Service->>Repo: findById(chatRoomId)
    Repo-->>Service: chatRoom
    Service->>Entity: isParticipant(memberId)
    Entity-->>Service: true/false
    Service->>Entity: isLeft(memberId)
    Entity-->>Service: false
    Service->>Entity: leave(memberId, now)
    Note over Entity: agentLeftDate/clientLeftDate 설정<br/>roomStatus → CLOSED, closedDate 설정
    Service->>Repo: save(chatRoom)
    Repo-->>Service: saved
    Service->>Redis: deleteUnreadCounter(memberId, chatRoomId)
    Redis-->>Service: deleted
    Service-->>Controller: success
    Controller-->>User: 200 OK
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45분

Possibly related PRs

  • 20251201 #651 deploy 서버 이전 #652: ChatRoomController 변경 관련 — 동일 컨트롤러에 엔드포인트 추가/수정이 있음.
  • Test #718: ChatMessageService 변경 관련 — handleNewChatMessage 관련 검증·로직 수정이 겹침.
  • Test #723: SuccessHistoryService 변경 관련 — MemberService 제거 및 관련 로직 변경이 동일함.

Suggested reviewers

  • Yooonjeong

채팅방을 나가네, 토끼가 말하네 🐇
ACTIVE였던 방은 조용히 CLOSED로,
agentLeft와 clientLeft가 발자국 남기고,
chatEnabled와 opponentLeft는 진실을 말하네,
로그 한 줄에 안녕을 담아 떠나리 ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 47.83% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목이 주요 변경 사항(채팅방 나가기 기능)을 명확하게 설명하고 있으며, 변경 집합의 핵심 내용과 일치합니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 20260128/mr6208_채팅방_나가기_기능추가

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.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

이 PR의 목적은 1:1 채팅방에 “채팅방 나가기” 기능을 도입하고, 누가 방을 나갔는지에 따라 채팅 가능 여부와 목록/내부 조회 동작을 분리해서 처리하는 것입니다.

Changes:

  • ChatRoom 도큐먼트에 agentLeftAt, clientLeftAt, roomStatus, closedAt 필드를 추가하고, 나가기/채팅 가능 여부를 판단하는 비즈니스 메서드(isLeft, canChat, leave)를 도입했습니다.
  • 채팅방 검색/목록/메시지 전송/컨텍스트 조회 플로우를 canChat()isLeft()를 기준으로 동작하도록 확장하고, 나가기 API(POST /api/chat-room/{chat-room-id}/leave) 및 관련 ErrorCode, 응답 DTO 필드(chatEnabled, opponentLeft)와 문서를 추가했습니다.
  • 성공내역 조회 서비스에서 불필요했던 대리인 타입 검증 의존성을 제거해, 보다 자유로운 성공내역 조회 요구사항에 맞추었습니다.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
ticketmate-fulfillmentform/.../SuccessHistoryService.java 성공내역 조회 시 MemberService/MemberType 의존성과 대리인 타입 검증을 제거해, 단순히 전달받은 agentId 기준으로 성공내역을 조회하도록 단순화했습니다.
ticketmate-common/.../ErrorCode.java 채팅방 나가기/차단 시나리오용 CHAT_ROOM_LEFT, CHAT_DISABLED 에러 코드를 추가했습니다.
ticketmate-chat/.../ChatRoomRepositoryImpl.java 채팅방 검색 시 현재 사용자가 이미 나간 방(agentLeftAt/clientLeftAt이 설정된 방)을 제외하도록 Criteria를 확장했습니다.
ticketmate-chat/.../ChatRoom.java 에이전트/클라이언트의 퇴장 시점, 방 상태(ChatRoomStatus), 종료 시점 필드를 추가하고, 참가자/퇴장/채팅 가능 여부를 판단하는 도메인 메서드를 도입했습니다.
ticketmate-chat/.../ChatRoomStatus.java 채팅방 상태를 나타내는 ACTIVE/CLOSED enum을 추가했습니다.
ticketmate-chat/.../ChatRoomService.java 채팅 메시지 조회 시 “나간 사용자”를 거르기 위한 validateActiveParticipant를 도입하고, leaveChatRoom 서비스 로직을 추가해 퇴장 처리 및 unread 카운터 정리를 수행합니다.
ticketmate-chat/.../ChatMessageService.java 새 메시지 처리 시 validateCanChat을 호출해 누군가 나간 방에서는 메시지 전송을 막고, 이를 위해 CHAT_DISABLED 에러를 사용합니다.
ticketmate-chat/.../ChatMapperImpl.java 채팅방 목록/컨텍스트 응답에 chatEnabledopponentLeft 플래그를 채우도록 매핑 로직을 확장했습니다.
ticketmate-chat/.../ChatRoomResponse.java 채팅방 리스트용 응답 DTO에 chatEnabled, opponentLeft 필드를 추가하고 빌더 시그니처를 확장했습니다.
ticketmate-chat/.../ChatRoomContextResponse.java 채팅방 컨텍스트 응답 DTO에 chatEnabled, opponentLeft 필드를 추가하고 빌더 시그니처를 확장했습니다.
ticketmate-api/.../ChatRoomControllerDocs.java 채팅방 나가기 API 스펙과, 나간/남은 사용자별로 목록/내부 조회 및 채팅 가능 여부가 어떻게 달라지는지에 대한 문서를 추가했습니다.
ticketmate-api/.../ChatRoomController.java POST /api/chat-room/{chat-room-id}/leave 엔드포인트를 추가해 인증된 사용자가 현재 채팅방에서 나갈 수 있도록 했습니다.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

};
}

// 현재 채팅을 진행 수 있는 상태인지(한명이라도 나가면 채팅 불가)
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

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

주석 "현재 채팅을 진행 수 있는 상태인지(한명이라도 나가면 채팅 불가)"에서 "진행 수"는 "진행할 수"로, "한명"은 "한 명"으로 표기하는 것이 자연스럽습니다. "현재 채팅을 진행할 수 있는 상태인지(한 명이라도 나가면 채팅 불가)"로 주석을 정리해 주세요.

Suggested change
// 현재 채팅을 진행 수 있는 상태인지(한명이라도 나가면 채팅 불가)
// 현재 채팅을 진행할 수 있는 상태인지(한 명이라도 나가면 채팅 불가)

Copilot uses AI. Check for mistakes.

public boolean canChat() {
// 상대가 나갔을시 남아있는 사람도 채팅 불가
return roomStatus == ChatRoomStatus.ACTIVE && agentLeftAt == null && clientLeftAt == null;
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

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

ChatRoomStatus roomStatus is defaulted to ACTIVE only at build time, but for existing Mongo documents the roomStatus field will be null when loaded, so canChat() will return false for all legacy chat rooms and block sending new messages after this change. To avoid unintentionally disabling chat in existing rooms, treat null as ACTIVE (or base canChat() solely on agentLeftAt/clientLeftAt) so that pre-existing rooms without roomStatus populated remain chat-enabled until someone actually leaves.

Suggested change
return roomStatus == ChatRoomStatus.ACTIVE && agentLeftAt == null && clientLeftAt == null;
// roomStatus 가 null 인 기존 문서는 ACTIVE 로 간주한다.
return (roomStatus == null || roomStatus == ChatRoomStatus.ACTIVE)
&& agentLeftAt == null
&& clientLeftAt == null;

Copilot uses AI. Check for mistakes.
Comment on lines +313 to +323
- 1:1 채팅도중 상대방이 나갔을 시 내 기준에서는 과거 채팅목록 조회가 가능하지만 채팅은 불가합니다.
- 상대방 기준에서는(나간사람) 채팅방 목록에 나간 채팅방이 보이지 않고 채팅방 내부 조회 자체가 불가능합니다.
- 이런 요구사항을 반영하기 위해 기존 응답데이터에 대한 변경이 이루어졌습니다.

### 아래는 채팅방 리스트업 및 채팅방 내부 조회시 추가된 데이터입니다.
```json
{
"chatEnabled(현재 채팅이 가능한지 알려주는 플래그)": "true/false",
"opponentLeft(현재 채팅방에서 상대방이 나갔는지를 알려주는 플래그)": true/false
}
```
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

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

여기 JSON 예시에서 chatEnabledopponentLeft 설명은 "true/false"와 true/false처럼 혼용되어 있고, 특히 아래 설명(상대방 기준에서는 나간 채팅방 내부 조회가 불가능)과 실제 구현이 완전히 일치하지 않습니다. 현재 구현에서는 채팅방을 나간 사용자가 /context 엔드포인트를 직접 호출하면 여전히 채팅방 컨텍스트를 조회할 수 있으므로, 문서의 요구사항에 맞게 해당 사용자도 내부 조회를 막거나(예: CHAT_ROOM_LEFT 에러 재사용) 문서 설명을 실제 동작에 맞게 조정하는 것이 필요합니다.

Suggested change
- 1:1 채팅도중 상대방이 나갔을 기준에서는 과거 채팅목록 조회가 가능하지만 채팅은 불가합니다.
- 상대방 기준에서는(나간사람) 채팅방 목록에 나간 채팅방이 보이지 않고 채팅방 내부 조회 자체가 불가능합니다.
- 이런 요구사항을 반영하기 위해 기존 응답데이터에 대한 변경이 이루어졌습니다.
### 아래는 채팅방 리스트업 채팅방 내부 조회시 추가된 데이터입니다.
```json
{
"chatEnabled(현재 채팅이 가능한지 알려주는 플래그)": "true/false",
"opponentLeft(현재 채팅방에서 상대방이 나갔는지를 알려주는 플래그)": true/false
}
```
- 1:1 채팅 도중 상대방이 나갔을 , 기준에서는 과거 채팅 목록 조회가 가능하지만 채팅은 불가합니다.
- 상대방 기준에서는(나간 사람) 채팅방 목록에 나간 채팅방이 보이지 않으며, 현재 구현에서는 `/context` 엔드포인트를 직접 호출하는 경우에 한해 과거 채팅방 내부 조회는 가능하지만 추가 채팅은 불가합니다.
- 이런 요구사항 현재 구현을 반영하기 위해 기존 응답 데이터에 대한 변경이 이루어졌습니다.
### 아래는 채팅방 리스트업 채팅방 내부 조회 추가된 데이터입니다.
```json
{
"chatEnabled": true,
"opponentLeft": false
}
```
- `chatEnabled`: 현재 채팅이 가능한지 알려주는 **boolean** 플래그입니다.
- `opponentLeft`: 현재 채팅방에서 상대방이 나갔는지를 알려주는 **boolean** 플래그입니다.

Copilot uses AI. Check for mistakes.
Criteria asAgent = Criteria.where("agentMemberId").is(memberId);
// 1. 내가 ‘대리인’으로 들어가 있는 방 조건 + 나가지 않은 채팅방(나간시점의 기록이 없는)
Criteria asAgent = Criteria.where("agentMemberId").is(memberId).and("agentLeftAt").is(null);
;
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

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

Criteria asAgent = Criteria.where("agentMemberId").is(memberId).and("agentLeftAt").is(null); 다음 줄의 단독 세미콜론(;)은 아무 동작을 하지 않는 잔여 코드로 보입니다. 의미 없는 문장은 제거해 가독성을 높이고 잠재적인 오타로 오해되지 않도록 정리하는 것이 좋습니다.

Suggested change
;

Copilot uses AI. Check for mistakes.

CHAT_MESSAGE_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "채팅 메시지 전송중 오류가 발생했습니다."),

CHAT_ROOM_LEFT(HttpStatus.NOT_FOUND, "채팅방을 나가서 존재하지 않은 채팅방입니다."),
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

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

에러 메시지 "채팅방을 나가서 존재하지 않은 채팅방입니다."는 어색한 표현입니다. "채팅방을 나가 존재하지 않는 채팅방입니다."처럼 자연스러운 문장으로 수정하는 것이 좋습니다.

Suggested change
CHAT_ROOM_LEFT(HttpStatus.NOT_FOUND, "채팅방을 나가서 존재하지 않은 채팅방입니다."),
CHAT_ROOM_LEFT(HttpStatus.NOT_FOUND, "채팅방을 나가 존재하지 않는 채팅방입니다."),

Copilot uses AI. Check for mistakes.

CHAT_ROOM_LEFT(HttpStatus.NOT_FOUND, "채팅방을 나가서 존재하지 않은 채팅방입니다."),

CHAT_DISABLED(HttpStatus.BAD_REQUEST, "해당 채팅방은 상대방이 채팅방을 나가 채팅을 진행 할 수 없습니다."),
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

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

에러 메시지 "해당 채팅방은 상대방이 채팅방을 나가 채팅을 진행 할 수 없습니다."에서 "진행 할"은 붙여 쓰는 것이 맞습니다. "해당 채팅방은 상대방이 채팅방을 나가 채팅을 진행할 수 없습니다."처럼 띄어쓰기를 수정해 주세요.

Suggested change
CHAT_DISABLED(HttpStatus.BAD_REQUEST, "해당 채팅방은 상대방이 채팅방을 나가 채팅을 진행 할 수 없습니다."),
CHAT_DISABLED(HttpStatus.BAD_REQUEST, "해당 채팅방은 상대방이 채팅방을 나가 채팅을 진행할 수 없습니다."),

Copilot uses AI. Check for mistakes.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In
`@ticketmate-chat/src/main/java/com/ticketmate/backend/chat/infrastructure/entity/ChatRoom.java`:
- Around line 109-143: The leave(UUID memberId, Instant now) method currently
overwrites agentLeftAt/clientLeftAt on duplicate calls which can desync with
closedAt; change leave so it only assigns agentLeftAt or clientLeftAt if that
field is null (preserve first leave time), and when transitioning roomStatus to
ChatRoomStatus.CLOSED set closedAt only if closedAt is null (or rely on the
status check but add the null guard for safety); update references to
agentLeftAt, clientLeftAt, leave(...), closedAt, and roomStatus accordingly.

In
`@ticketmate-common/src/main/java/com/ticketmate/backend/common/application/exception/ErrorCode.java`:
- Around line 402-405: Fix Korean spelling/spacing in the ErrorCode enum
messages for CHAT_ROOM_LEFT and CHAT_DISABLED: update the message for
CHAT_ROOM_LEFT (enum constant CHAT_ROOM_LEFT in ErrorCode) to a grammatically
correct string such as "채팅방을 나가 존재하지 않는 채팅방입니다." and update CHAT_DISABLED (enum
constant CHAT_DISABLED in ErrorCode) to remove the extra spacing and improve
phrasing, e.g. "해당 채팅방은 상대방이 나가 채팅을 진행할 수 없습니다." so user-facing messages have
correct spacing and wording.
🧹 Nitpick comments (2)
ticketmate-api/src/main/java/com/ticketmate/backend/api/application/controller/chat/ChatRoomControllerDocs.java (1)

291-298: issueUrl 필드가 비어 있습니다.

다른 @ApiChangeLog 항목들과 일관성을 위해 관련 이슈 URL을 추가하는 것이 좋습니다.

ticketmate-chat/src/main/java/com/ticketmate/backend/chat/application/mapper/ChatMapperImpl.java (1)

80-88: getOpponentId 중복 호출을 제거하세요.

Line 80에서 opponentId를 이미 계산했으나, line 88에서 getOpponentId를 다시 호출하고 있습니다. 이미 계산된 변수를 재사용하세요.

♻️ 수정 제안
     return ChatRoomContextResponse.builder()
       .concertName(response.concertName())
       .concertType(response.concertType())
       .fulfillmentFormId(fulfillmentFormId)
-      .opponentMemberId(chatRoom.getOpponentId(currentMemberId))
+      .opponentMemberId(opponentId)
       .opponentMemberNickName(opponentMemberNickname)

Comment on lines +109 to +143
public boolean isLeft(UUID memberId) {
if (memberId.equals(agentMemberId)) {
return agentLeftAt != null;
}
if (memberId.equals(clientMemberId)) {
return clientLeftAt != null;
}
return false;
}

public boolean isParticipant(UUID memberId) {
return memberId.equals(agentMemberId) || memberId.equals(clientMemberId);
}

public boolean canChat() {
// 상대가 나갔을시 남아있는 사람도 채팅 불가
return roomStatus == ChatRoomStatus.ACTIVE && agentLeftAt == null && clientLeftAt == null;
}

// 채팅방을 나갈 시 동작하는 메서드
public void leave(UUID memberId, Instant now) {
if (memberId.equals(agentMemberId)) {
agentLeftAt = now;
} else if (memberId.equals(clientMemberId)) {
clientLeftAt = now;
} else {
throw new CustomException(ErrorCode.NO_AUTH_TO_ROOM);
}

// 한 명이라도 나가면 방은 CLOSED
if (roomStatus != ChatRoomStatus.CLOSED) {
roomStatus = ChatRoomStatus.CLOSED;
closedAt = now;
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

leave 중복 호출 시 leftAt 덮어쓰기 방지 필요
재요청/중복 호출 시 leftAt이 갱신되면 closedAt과 시간 불일치가 생길 수 있어 최초 값 유지가 안전합니다.

🛠️ 제안 수정안
-  public void leave(UUID memberId, Instant now) {
-    if (memberId.equals(agentMemberId)) {
-      agentLeftAt = now;
-    } else if (memberId.equals(clientMemberId)) {
-      clientLeftAt = now;
-    } else {
-      throw new CustomException(ErrorCode.NO_AUTH_TO_ROOM);
-    }
+  public void leave(UUID memberId, Instant now) {
+    if (memberId.equals(agentMemberId)) {
+      if (agentLeftAt == null) {
+        agentLeftAt = now;
+      }
+    } else if (memberId.equals(clientMemberId)) {
+      if (clientLeftAt == null) {
+        clientLeftAt = now;
+      }
+    } else {
+      throw new CustomException(ErrorCode.NO_AUTH_TO_ROOM);
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public boolean isLeft(UUID memberId) {
if (memberId.equals(agentMemberId)) {
return agentLeftAt != null;
}
if (memberId.equals(clientMemberId)) {
return clientLeftAt != null;
}
return false;
}
public boolean isParticipant(UUID memberId) {
return memberId.equals(agentMemberId) || memberId.equals(clientMemberId);
}
public boolean canChat() {
// 상대가 나갔을시 남아있는 사람도 채팅 불가
return roomStatus == ChatRoomStatus.ACTIVE && agentLeftAt == null && clientLeftAt == null;
}
// 채팅방을 나갈 시 동작하는 메서드
public void leave(UUID memberId, Instant now) {
if (memberId.equals(agentMemberId)) {
agentLeftAt = now;
} else if (memberId.equals(clientMemberId)) {
clientLeftAt = now;
} else {
throw new CustomException(ErrorCode.NO_AUTH_TO_ROOM);
}
// 한 명이라도 나가면 방은 CLOSED
if (roomStatus != ChatRoomStatus.CLOSED) {
roomStatus = ChatRoomStatus.CLOSED;
closedAt = now;
}
}
public void leave(UUID memberId, Instant now) {
if (memberId.equals(agentMemberId)) {
if (agentLeftAt == null) {
agentLeftAt = now;
}
} else if (memberId.equals(clientMemberId)) {
if (clientLeftAt == null) {
clientLeftAt = now;
}
} else {
throw new CustomException(ErrorCode.NO_AUTH_TO_ROOM);
}
// 한 명이라도 나가면 방은 CLOSED
if (roomStatus != ChatRoomStatus.CLOSED) {
roomStatus = ChatRoomStatus.CLOSED;
closedAt = now;
}
}
🤖 Prompt for AI Agents
In
`@ticketmate-chat/src/main/java/com/ticketmate/backend/chat/infrastructure/entity/ChatRoom.java`
around lines 109 - 143, The leave(UUID memberId, Instant now) method currently
overwrites agentLeftAt/clientLeftAt on duplicate calls which can desync with
closedAt; change leave so it only assigns agentLeftAt or clientLeftAt if that
field is null (preserve first leave time), and when transitioning roomStatus to
ChatRoomStatus.CLOSED set closedAt only if closedAt is null (or rely on the
status check but add the null guard for safety); update references to
agentLeftAt, clientLeftAt, leave(...), closedAt, and roomStatus accordingly.

Copy link
Contributor

@Yooonjeong Yooonjeong left a comment

Choose a reason for hiding this comment

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

고생하셨습니당

private ChatRoomStatus roomStatus = ChatRoomStatus.ACTIVE; // 한명이라도 나가면 CLOSED 상태

@Indexed
private Instant closedAt;
Copy link
Contributor

Choose a reason for hiding this comment

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

저희 모든 시간 관련 필드가 ~Date로 끝나서 이 필드들도 At 대신 Date로 변경해도 좋을 것 같아요!

Copy link
Contributor

Choose a reason for hiding this comment

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

필드 추가하셨는데 flyway 스크립트가 누락된 것 같아서 확인 부탁드려요!
인덱스도 추가한다면 @Indexed보다 flyway로 관리하면 더 좋을 것 같아요 ~~

Copy link
Member

Choose a reason for hiding this comment

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

@Yooonjeong 몽고 컬렉션이라 flyway 영향을 안받습니다 ㅠㅠㅠ

Copy link
Contributor

Choose a reason for hiding this comment

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

@Chuseok22 아!!!! 맞네요 깜빡했서요 ㅋㅋㅋ감사합니당

Copy link
Member

@Chuseok22 Chuseok22 left a comment

Choose a reason for hiding this comment

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

고생하셨습니다!!

Comment on lines +261 to +263
if (room.isLeft(member.getMemberId())) {
throw new CustomException(ErrorCode.CHAT_ROOM_LEFT);
}
Copy link
Member

Choose a reason for hiding this comment

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

로깅 한 줄 있으면 좋을 것 같습니다

private ChatRoomStatus roomStatus = ChatRoomStatus.ACTIVE; // 한명이라도 나가면 CLOSED 상태

@Indexed
private Instant closedAt;
Copy link
Member

Choose a reason for hiding this comment

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

@Yooonjeong 몽고 컬렉션이라 flyway 영향을 안받습니다 ㅠㅠㅠ

@mr6208 mr6208 merged commit 7686294 into test Feb 2, 2026
1 of 2 checks passed
mr6208 added a commit that referenced this pull request Feb 2, 2026
* hotfix : 성공내역 조회 시도시 조회객체 의뢰인/대리인 모두 가능하게 수정

* hotfix : 성공내역 조회 시도시 조회객체 의뢰인/대리인 모두 가능하게 수정

* 20260128/mr6208 채팅방 나가기 기능추가 (#724)

* Test (#723)

* hotfix : 성공내역 조회 시도시 조회객체 의뢰인/대리인 모두 가능하게 수정

* hotfix : 성공내역 조회 시도시 조회객체 의뢰인/대리인 모두 가능하게 수정

* 채팅방_나가기_기능구현 : feat : 채팅방 나가기 API 설계 및 검증 메서드 추가

* 채팅방_나가기_기능구현 : feat : 채팅방 도메인 및 응답 데이터 필드 추가

* 채팅방_나가기_기능구현 : feat : 체팅방 필드 추가로 인한 쿼리 수정 및 메퍼쪽 필드 추가

* 채팅방_나가기_기능구현 : feat : 채팅방 시간 관련 필드명 변경

* 채팅방_나가기_기능구현 : feat : 오탈자 수정

* 채팅방_나가기_기능구현 : feat : 디버깅용 로깅 추가

---------

Co-authored-by: Yooonjeong <115490096+Yooonjeong@users.noreply.github.com>

---------

Co-authored-by: Yooonjeong <115490096+Yooonjeong@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat 새로운 기능 추가 (Feature)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants