♠︎ 2025. 7. 29 ~ 2025. 8. 27 ♠︎
- DungeonTalk는 AI와 함께하는 혁신적인 TRPG(테이블탑 롤플레잉 게임) 플랫폼입니다.
- 실시간 채팅을 통해 AI 게임마스터와 함께 특정 세계관에서의 모험을 즐길 수 있습니다.
- 플레이어는 직접 게임 속의 캐릭터가 되어 직접 스토리를 만들어 나가면서 다양한 상황을 체험할 수 있습니다.
- 🤖 AI 게임마스터: 역동적이고 상호작용적인 TRPG Dungeontalk 게임의 진행을 담당하는 게임마스터
- ⚡ 실시간 통신: WebSocket STOMP 기반 실시간 멀티플레이어 지원
- 🎮 자유형 게임플레이: 제약 없는 창의적인 롤플레잉 경험
- 🌍 다양한 세계관: 판타지, 좀비 아포칼립스 등의 테마로 구성
- 👥 매칭 시스템: 세계관별 자동 매칭 및 대기열 관리
🧭 ERD - 설명 링크
erDiagram
%% =========================
%% RDB (PostgreSQL)
%% =========================
MEMBER {
string id PK
datetime created_at
datetime updated_at
string name
string nick_name
string password
}
AUTH {
string id PK
datetime created_at
datetime updated_at
string access_token
string token_type
string refresh_token
string email
string member_id FK
}
%% 캐릭터(신규 스키마)
CHARACTER {
string id PK
datetime created_at
datetime updated_at
string member_id FK
int player_level
int total_exp
int unspent_points
int str
int dex
int int
int wil
int wis
int luk
int race_type_id FK
}
%% 캐릭터(기존/대안 스키마)
GAME_CHARACTER {
string id PK
datetime created_at
datetime updated_at
string member_id FK
int player_level
int total_exp
int unspent_points
int str
int dex
int int
int wil
int wis
int luk
int strength
int dexterity
int intelligence
int willpower
int wisdom
int luck
int race_id FK
}
RACE_STATS {
string id PK
datetime created_at
datetime updated_at
string race
int hp
int mp
int health_points
int mana_points
int accuracy
int dice_odds
int evasion_rate
int physical_attack
int magic_attack
}
WORLD_TYPES {
int id PK
string code
string display_name
string description
string game_settings
boolean is_active
int sort_order
datetime created_at
datetime updated_at
}
WORLD {
int world_id PK
string world_name
int clear_exp
}
REQUEST_EXP {
int level PK
int request_next_level_exp
int request_total_exp
}
%% RDB 관계
MEMBER ||--o{ AUTH : has
MEMBER ||--o{ CHARACTER : owns
MEMBER ||--o{ GAME_CHARACTER : owns
GAME_CHARACTER }o--|| RACE_STATS : race_id_ref
CHARACTER }o--|| WORLD_TYPES : race_type_ref
%% (선택) 캐릭터가 특정 월드 진행도를 가진다면 연결 가능
%% CHARACTER }o--o{ WORLD : progress_in
%% =========================
%% MongoDB (채팅/AI 게임)
%% =========================
CHAT_ROOM {
string id PK
string name
string room_type
int max_participants
string[] participants
datetime created_at
datetime updated_at
}
CHAT_MESSAGE {
string id PK
string room_id FK
string sender_id
string receiver_Id
string content
string message_type
datetime created_at
datetime updated_at
}
CHAT_ROOM_MEMBER {
string id PK
string room_id FK
string member_id
string status
datetime joinedAt
datetime updateAt
datetime leftAt
}
AI_GAME_ROOM {
string id PK
string base_room_id
string world_type
string prompt
string state
string[] participants
datetime created_at
datetime updated_at
}
AI_GAME_MESSAGE {
string id PK
string ai_game_room_id FK
string content
string message_type
int turn_number
int message_order
datetime created_at
}
CHAT_ROOM ||--o{ CHAT_MESSAGE : contains
AI_GAME_ROOM ||--o{ AI_GAME_MESSAGE : contains
%% =========================
%% Cross-DB (논리 연결)
%% =========================
MEMBER ||--o{ CHAT_ROOM : participants_ref
MEMBER ||--o{ CHAT_MESSAGE : sender_ref
MEMBER ||--o{ AI_GAME_ROOM : participants_ref
| 계층(Layer) | 주요 구성요소 | 역할 요약 |
|---|---|---|
| Client Layer | WebSocket/STOMP Client |
클라이언트가 WebSocket(STOMP) 프로토콜로 서버와 실시간 통신 수행. REST API 및 STOMP 메시지 전송 역할. |
| Presentation Layer | ChatRoomMemberController, ChatRoomController, ChatStompController |
REST API와 STOMP 엔드포인트를 담당하는 진입 계층. HTTP 요청과 WebSocket 요청을 분리 처리. |
| Service Layer | ChatMessageService, ChatRoomService, ChatRoomMemberService, ChatSessionService |
비즈니스 로직 계층. 메시지 처리, 입장/퇴장 관리, 멤버 상태 관리, 세션 TTL 유지 등 핵심 도메인 로직을 수행. |
| Infrastructure Layer | KafkaSubscriber, KafkaPublisher, WebSocketDisconnectHandler, ChatPresenceSystemMessageListener, ChatRoomMemberManager |
외부 시스템(메시지 브로커·WebSocket·Redis 등)과 통신하는 인프라 계층. 비동기 이벤트 처리 및 연결 관리 담당. |
| Persistence Layer | MongoDB, Valkey/Redis, Kafka |
데이터 저장 및 메시징 계층. MongoDB는 채팅 로그, Redis는 세션 및 접속자 상태, Kafka는 메시지 브로드캐스트 담당. |
| 단계 | 주체 | 메서드 / 동작 | 설명 |
|---|---|---|---|
| 1 | Client | STOMP /pub/chat/send (type=TALK) |
클라이언트가 STOMP 프로토콜을 통해 채팅 메시지를 서버로 발행 |
| 2 | ChatStompController | processMessage(dto) |
STOMP 메시지를 수신하여 DTO 변환 후 ChatMessageService에 전달 |
| 3 | ChatMessageService | switch(type=TALK) |
메시지 타입이 TALK일 경우 handleTalkMessage() 실행 |
| 4 | ChatSessionService | extendSession(roomId, memberId) |
세션 TTL(1800초) 연장 — heartbeat 기반 세션 유지 로직 |
| 5 | ChatMessageService | validateMessage(content) |
ProfanityFilterService 호출 전 메시지 내용 검증 |
| 6 | ProfanityFilterService | filter(content) |
비속어 감지 및 치환(필터링 처리) |
| 7 | MongoDB | save(ChatMessage) |
MongoDB에 메시지 영구 저장 (비속어 필터링 이후 저장) |
| 8 | ChatMessageService | publishChatAsync(roomId, dto) |
KafkaPublisher를 통해 비동기 브로드캐스트 요청 |
| 9 | KafkaPublisher | @Async send(topic, roomId, message) |
Kafka 토픽으로 메시지 발행(비동기 처리) |
| 10 | Kafka | ack |
메시지 발행 성공 ACK 반환 |
| 11 | KafkaSubscriber | consume(message) |
Kafka 토픽 메시지를 수신하고 WebSocket 브로드캐스트로 전달 |
| 12 | WebSocket | convertAndSend(/sub/chat/room/{roomId}) |
구독 중인 모든 클라이언트에게 메시지 전송 |
| 13 | Client | 200 OK |
클라이언트는 서버로부터 메시지 전송 완료 응답 수신 |
| 단계 | 주체 | 메서드 / 동작 | 설명 |
|---|---|---|---|
| 1 | Client | STOMP /pub/chat/send (type=LEAVE) 또는 SessionDisconnectEvent |
클라이언트가 퇴장 메시지를 전송하거나, WebSocket 연결이 끊어짐 |
| 2 | WebSocketDisconnectHandler | SessionDisconnectEvent 수신 |
비정상 연결 종료 감지 후 ChatRoomService.leaveRoom() 호출 |
| 3 | ChatRoomService | leaveRoom(roomId, memberId) |
퇴장 로직 진입 — 세션 종료 및 멤버 제거 처리 |
| 4 | ChatSessionService | endSession(roomId, memberId) |
Redis(Valkey)에 저장된 세션 key(chat:session:{roomId}:{memberId}) 삭제 |
| 5 | Valkey | DEL chat:session:{roomId}:{memberId} |
세션 데이터 제거 (1 deleted) |
| 6 | ChatRoomMemberManager | removeUser(roomId, memberId) |
Redis에서 사용자 목록에서 제거 |
| 7 | Valkey | SREM chat:room:{roomId}:users [memberId] |
참여자 목록(Set)에서 멤버 제거 |
| 8 | Valkey | HDEL chat:room:{roomId}:nicks [memberId] |
닉네임 매핑(Hash)에서도 제거 |
| 9 | ChatRoomService | markOffline(roomId, memberId) |
해당 사용자를 오프라인 상태로 표시 |
| 10 | ChatRoomMemberService | update ChatRoomMember(status=OFFLINE) |
MongoDB에 저장된 멤버 상태를 “OFFLINE”으로 변경 |
| 11 | ChatRoomMemberService | publishEvent(ChatPresenceEvent type=LEAVE) |
퇴장 이벤트 발행 (Spring EventPublisher 사용) |
| 12 | EventPublisher | publishEvent() → 대기열 등록 |
@TransactionalEventListener(AFTER_COMMIT)로 전달됨 |
| 13 | ChatPresenceSystemMessageListener | onPresence(ChatPresenceEvent) |
트랜잭션 커밋 이후 이벤트 수신 |
| 14 | MongoDB | save("퇴장했습니다.") |
시스템 메시지(퇴장 알림) 저장 |
| 15 | Valkey | Redis Pub/Sub 발행(메시지) |
“퇴장 알림” 이벤트를 Redis Pub/Sub을 통해 모든 서버에 전파 |
| 16 | ChatRoomService | broadcastPresence(roomId, "LEAVE") |
WebSocket을 통해 현재 채팅방에 접속한 모든 사용자에게 브로드캐스트 |
| 17 | Client | WebSocket으로 Presence 브로드캐스트 수신 |
클라이언트가 퇴장 알림 메시지를 수신하고 UI 갱신 |
| 이벤트 | 상태 전이 | 설명 |
|---|---|---|
startSession() |
NotExist → Active | 사용자가 WebSocket 연결을 시작할 때 호출. 세션 생성 (SET key EX 1800) |
heartbeat() / extendSession() |
Active → Active | 일정 주기로 TTL 갱신. 메시지 전송 시에도 호출되어 EXPIRE key 1800 |
| TTL 만료 (30분) | Active → Expired | 비정상 종료나 heartbeat 중단 시 자동 만료 처리 |
endSession() |
Active → NotExist | 사용자가 명시적으로 퇴장 시 key 삭제 (DEL key) |
Valkey 자동 삭제 |
Expired → NotExist | TTL 종료 후 key 자동 제거. 메모리 누수 방지 |
| TTL 만료 + 퇴장 정리 | NotExist → Expired | 비정상 종료 후 자동 청소 프로세스(30분 내) |
| 계층 | 구성 요소 | 주요 역할 및 책임 |
|---|---|---|
| Controllers | ChatRoomController, ChatStompController, ChatRoomMemberController |
|
| Services | ChatMessageService, ChatRoomService, ChatRoomMemberService, ChatSessionService |
|
| Infrastructure | WebSocketDisconnectHandler, ChatRoomMemberManager, KafkaPublisher, KafkaSubscriber, ChatPresenceSystemMessageListener |
|
| External Systems | Valkey, Kafka, MongoDB |
|
| 구분 | 기술/도구 |
|---|---|
| Frontend | HTML, CSS, JavaScript, Thymeleaf, SockJS |
| Backend | Java 21 (LTS), Spring Boot 3.4.0, Spring Security, JWT |
| DataBase | PostgreSQL 17 (pgvector 지원), MongoDB 7.0, Redis(Valkey) - (캐시/세션) |
| Main Tech Stack | WebSocket, STOMP, Redis (Valkey) Pub/Sub, RAG |
| Dev Tools | Swagger/OpenAPI (API 문서화), JUnit 5, Mockito, Docker (컨테이너화), Discord |
src/
├── main/
│ ├── java/org/com/dungeontalk/
│ │ ├── domain/ # 도메인별 패키지
│ │ │ ├── aichat/ # AI 채팅 시스템
│ │ │ ├── auth/ # 인증/인가
│ │ │ ├── chat/ # 일반 채팅
│ │ │ ├── matching/ # 매칭 시스템
│ │ │ ├── gamecharacter/ # 게임 캐릭터
│ │ │ └── ...
│ │ ├── global/ # 전역 설정
│ │ │ ├── config/ # 설정 클래스
│ │ │ ├── security/ # 보안 설정
│ │ │ ├── websocket/ # WebSocket 설정
│ │ │ └── exception/ # 예외 처리
│ │ └── web/ # 웹 컨트롤러
│ └── resources/
│ ├── application*.properties # 환경별 설정
│ ├── static/ # 정적 파일
│ └── templates/ # 템플릿 파일
└── test/ # 테스트 코드
더 궁금한 내용이 있으시면 아래를 참고해주세요.
- 협업 방식을 알고 싶다. 👉 협업 가이드 바로가기
- Moodbook의 Github Project. 👉Github Project 바로가기
- 도메인별 API 상세정보가 궁금하다. 👉 DungeonTalk API 문서 바로가기
- 사용한 기술 스택이 궁금하다. 👉 기술 스택 바로가기
- 주요 기능 개발 과정이 궁금하다. 👉 기술 상세 정보 바로가기
- 활용한 디자인 패턴에 대해 알고 싶다. 👉 DungeonTalk에서 활용한 디자인 패턴 바로가기
- CI/CD 구성 절차가 궁금하다. 👉 CI-CD 구성 바로가기
- ERD와 DB 정보에 대해 궁금하다. 👉 ERD 정보 바로가기
- 트러블 슈팅에 대한 내용이 궁금하다. 👉 트러블 슈팅 바로가기
- 프로젝트 진행 과정에 대한 내용이 궁금하다. 👉 DungeonTalk 기술 블로그
- 도메인별 테스트 코드 내용이 궁금하다. 👉 DungeonTalk 도메인별 테스트 코드 작성
기타 다른 내용들은 📝 팀 위키 에서 확인할 수 있습니다.






