-
Notifications
You must be signed in to change notification settings - Fork 0
Description
[feat] 롱 폴링 기반 상담 채팅 시스템 구현
📌 이슈 유형
- ✨ 새 기능 추가 (feat)
- 🐛 버그 수정 (fix)
- 🔧 기능 개선 (refactor)
- 📚 문서 작업 (docs)
- 🧪 테스트 (test)
- 🏗️ 빌드/배포 (ci/build)
- 🔥 긴급 수정 (hotfix)
- 🧹 기타 작업 (chore)
🎯 배경 / 목적
회원과 기관 간 실시간 상담을 위한 채팅 기능이 필요합니다. WebSocket 대신 구현이 간단한 롱 폴링 방식을 채택하여 기관 소속 여러 관리자(기관장, 직원)가 동시에 참여할 수 있는 다대일 채팅을 구축합니다.
상담 요청 생성 이후, 회원이 상담 요청 상세 화면에서 ‘상담 시작’ 버튼을 누르는 순간,
ConsultRequest + ChatRoom 이 동시에 생성되며 상담이 활성화됩니다.
App/Web 공통으로 사용할 수 있는 RESTful API를 제공하며, 기관 측 메시지는 화면에 기관명으로만 표기되도록 합니다.
✅ 완료 조건 (AC: Acceptance Criteria)
-
ConsultRequest상태를 ACTIVE / CLOSED로 간소화- ACTIVE: 상담 진행 가능
- CLOSED: 상담 완전 종료
-
같은 회원 + 같은 상담 서비스에 대해 ACTIVE 상태의
ConsultRequest는 하나만 존재 -
회원이 ‘상담 시작’ 클릭 시
→ ConsultRequest + ChatRoom 동시 생성 -
CLOSED 상태에서 재시작 시
→ 새로운 ConsultRequest + 새로운 ChatRoom 생성 -
ConsultRequest 1건은 최대 1개의 ChatRoom을 가진다 (1:1)
-
회원 ↔ 기관 관리자 다대일 채팅 지원
-
롱 폴링 방식으로 신규 메시지 실시간 수신 (30초 타임아웃)
-
기관 관리자 메시지는 DTO에서 기관명만 노출
-
메시지 전송 / 조회 / 삭제 API 구현
-
Pagination 및 Soft Delete 지원
-
Swagger 문서화
-
권한 검증 (회원: 자신의 상담만, 관리자: 소속 기관 상담만 처리 가능)
-
읽음 처리 기능(
isRead,readAt)은 후속 작업(Phase 2)에서 구현
🛠️ 해결 방안 / 구현 상세
1. 엔티티 설계
ConsultRequest (상담 요청 — 기존 수정)
- status: ACTIVE / CLOSED
- ACTIVE: 상담 가능 상태
- CLOSED: 상담 종료
- 중복 방지: 같은 회원 + 같은 상담 서비스는 ACTIVE 1개만 허용
- CLOSED → 재시작 시 새로운 ConsultRequest 생성ChatRoom (채팅방 — 신규)
- id
- consultRequest (ConsultRequest 1:1, unique)
- lastMessageContent
- lastMessageAt
- isActive (ACTIVE/CLOSED와 연동)
- createdAt (BaseEntity)ChatMessage (메시지 — 신규)
- id
- chatRoom
- senderType (MEMBER, INSTITUTION_ADMIN)
- senderId (회원 ID 또는 관리자 ID)
- content
- deleted (Soft Delete)
- createdAt (BaseEntity)※ ChatParticipant / 읽음 처리 필드는 Phase 2에서 추가 예정
2. API 설계 (ConsultRequest Id 기반 유지)
▶ 상담 시작 (ConsultRequest + ChatRoom 동시 생성)
POST /api/v1/consult-requests
Body 예:
{
"institutionId": 1,
"counselId": 3
}Response:
{
"consultRequestId": 12,
"chatRoomId": 8
}▶ 메시지 API
POST /api/v1/consult-requests/{requestId}/chat/messages
GET /api/v1/consult-requests/{requestId}/chat/messages
GET /api/v1/consult-requests/{requestId}/chat/messages/poll
DELETE /api/v1/consult-requests/{requestId}/chat/messages/{messageId}
GET /api/v1/consult-requests/{requestId}/chat
※ 채팅방 목록 조회는 Phase 2 (현재 스코프에서 제외)
3. 롱 폴링 구현 전략
- 타임아웃: 30초
- 폴링 간격: 0.5초
- 종료 조건: 신규 메시지 발생 즉시 반환
- 요청 파라미터:
lastMessageId - 응답: 신규 메시지 리스트 (없으면 빈 배열)
4. 권한 검증
회원
consultRequest.member.id == currentUser.id
기관 관리자
consultRequest.institution.id == admin.institution.id
🧩 작업 범위
✔ 기존 수정
ConsultRequestStatus.java— ACTIVE/CLOSED로 축소ConsultRequest.java— 상태 전이 메서드 정리ConsultRequestRepository.java— ACTIVE 중복 방지 쿼리 추가SecurityConfig.java— 채팅 API 허용 추가
✔ 신규 생성
ChatRoom,ChatMessage엔티티- Repository (ChatRoomRepository, ChatMessageRepository)
- ChatService (startChat, sendMessage, getMessages, pollMessages, deleteMessage, getChatRoomInfo)
- ChatController
- DTO 생성
✔ DB 변경
consult_request테이블 상태값 변경chat_room,chat_message테이블 추가
🧪 테스트 계획
단위 테스트
- ChatRoom 생성 (상담 시작 시)
- ACTIVE 중복 방지 로직
- Soft Delete
- 권한 검증
- Pagination
통합 테스트
- 상담 요청 생성 → 상담 시작 → ChatRoom 자동 생성
- 메시지 전송 → 롱 폴링 수신
- 기관 관리자 다대일 채팅
수동 테스트
- 회원이 ‘상담 시작’ 버튼 클릭 → ChatRoom 생성
- 기관명 표시 정상 동작
- 롱 폴링 정상 동작
- Soft Delete 후 재조회 제외
- 권한 체크 정상 동작
🔐 보안/성능 고려사항
- ConsultRequest 기반 권한 검증 철저
- 인덱스:
(chat_room_id, id),(chat_room_id, created_at) - Soft Delete 적용
- 기관 관리자 실명 노출 금지 → 기관명 표시
🚀 배포 체크리스트
- 환경변수 변경 없음
- JPA 신규 테이블/컬럼 정상 생성
- 기존 API 영향 없음
- Swagger 동기화
- 롱 폴링 타임아웃 및 응답 지연 여부 확인
🙋♂️ 담당자
- Backend: @clainyun
- Frontend: TBD
- Reviewer: @SongTaeKwon @Uechann @jimini0823
- 우선순위: P0
- 예상 소요: 2일
📎 참고
- 11/23 회의록
- 엔티티:
ConsultRequest,InstitutionAdmin,Member,Institution - MDN Long Polling Docs
- 원칙: 상담요청 1건 = ChatRoom 1개