Skip to content

Commit f710cf8

Browse files
committed
docs(be): Update CLAUDE.md and fix dev RabbitMQ config
- Add comprehensive RabbitMQ integration documentation - Add profile-based configuration strategy explanation - Add infrastructure & deployment section - Fix: Exclude RabbitMQ autoconfiguration in dev profile to prevent health check DOWN
1 parent e202b10 commit f710cf8

File tree

2 files changed

+150
-9
lines changed

2 files changed

+150
-9
lines changed

CLAUDE.md

Lines changed: 149 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ Korean Travel Guide Backend - A Spring Boot Kotlin application providing OAuth a
2424

2525
# Run specific test
2626
./gradlew test --tests "WeatherServiceTest"
27+
28+
# Compile Kotlin only (faster than full build)
29+
./gradlew compileKotlin
2730
```
2831

2932
### Code Quality
@@ -54,6 +57,24 @@ open http://localhost:8080/h2-console
5457
curl http://localhost:8080/actuator/health
5558
```
5659

60+
### Local RabbitMQ Setup
61+
```bash
62+
# Start RabbitMQ with docker-compose (for WebSocket testing)
63+
docker network create common
64+
docker-compose up -d
65+
66+
# Check RabbitMQ status
67+
docker logs rabbitmq-1
68+
69+
# Access RabbitMQ Management UI
70+
open http://localhost:15672
71+
# Username: admin
72+
# Password: (from .env PASSWORD_1 or default: qwpokd153098)
73+
74+
# Stop RabbitMQ
75+
docker-compose down
76+
```
77+
5778
### Redis (Optional - for caching)
5879
```bash
5980
# Start Redis with Docker
@@ -73,7 +94,7 @@ The codebase follows Domain-Driven Design with clear separation:
7394
```
7495
com/back/koreaTravelGuide/
7596
├── common/ # Shared infrastructure
76-
│ ├── config/ # App, Security, Redis, AI configs
97+
│ ├── config/ # App, Security, Redis, AI, DevConfig
7798
│ ├── security/ # OAuth2, JWT, filters
7899
│ ├── exception/ # Global exception handler
79100
│ └── ApiResponse.kt # Standard API response wrapper
@@ -86,7 +107,8 @@ com/back/koreaTravelGuide/
86107
│ │ └── tour/ # Tourism API integration
87108
│ ├── userChat/ # WebSocket chat between Guest-Guide
88109
│ │ ├── chatroom/ # Chat room management
89-
│ │ └── chatmessage/ # Message persistence
110+
│ │ ├── chatmessage/ # Message persistence & publishing
111+
│ │ └── stomp/ # WebSocket config (Simple/Rabbit)
90112
│ └── rate/ # Rating system for AI sessions & guides
91113
```
92114

@@ -95,15 +117,43 @@ com/back/koreaTravelGuide/
95117
1. **Each domain is self-contained** with its own entity, repository, service, controller, and DTOs
96118
2. **Common utilities live in `common/`** - never duplicate config or security logic
97119
3. **AI Chat uses Spring AI** with function calling for weather/tour tools
98-
4. **User Chat uses WebSocket** (STOMP) for real-time messaging
99-
5. **Global exception handling** via `GlobalExceptionHandler.kt` - just throw exceptions, they're caught automatically
120+
4. **User Chat uses WebSocket** with profile-based configuration:
121+
- **Dev**: SimpleBroker (in-memory, single server)
122+
- **Prod**: RabbitMQ STOMP Relay (scalable, multi-server)
123+
5. **Port-Adapter pattern** for message publishing (`ChatMessagePublisher` interface with `SimpleChatMessagePublisher` and `RabbitChatMessagePublisher` implementations)
124+
6. **Global exception handling** via `GlobalExceptionHandler.kt` - just throw exceptions, they're caught automatically
100125

101126
### Critical Configuration Files
102127

103128
- **build.gradle.kts**: Contains BuildConfig plugin that generates constants from YAML files (area-codes.yml, prompts.yml, etc.)
104-
- **application.yml**: Dev config with H2, Redis optional, OAuth2 providers
129+
- **application.yml**: Dev config with H2, Redis optional, OAuth2 providers, RabbitMQ for local testing
130+
- **application-prod.yml**: Production config with PostgreSQL, Redis required, RabbitMQ with connection stability settings
105131
- **SecurityConfig.kt**: Currently allows all requests for dev (MUST restrict for production)
106132
- **AiConfig.kt**: Spring AI ChatClient with OpenRouter (uses OPENROUTER_API_KEY env var)
133+
- **DevConfig.kt**: Auto-generates 2 dummy GUIDE users on startup (dev profile only)
134+
135+
### Profile-Based Configuration Strategy
136+
137+
The application uses Spring profiles (`@Profile` annotation) to switch implementations:
138+
139+
**Development Profile (`dev`):**
140+
- H2 in-memory database
141+
- `SimpleChatMessagePublisher` - uses Spring's SimpleBroker (no RabbitMQ needed)
142+
- `UserChatSimpleWebSocketConfig` - basic WebSocket with in-memory broker
143+
- Redis optional (session.store-type: none)
144+
- Dummy guide data auto-generation
145+
146+
**Production Profile (`prod`):**
147+
- PostgreSQL database
148+
- `RabbitChatMessagePublisher` - publishes to RabbitMQ
149+
- `UserChatRabbitWebSocketConfig` - STOMP Broker Relay to RabbitMQ
150+
- Redis required (session.store-type: redis)
151+
- Connection stability settings (timeouts, heartbeats)
152+
153+
When adding new features that differ between dev/prod, follow this pattern:
154+
1. Create an interface in the domain layer
155+
2. Create separate implementations with `@Profile("dev")` and `@Profile("prod")`
156+
3. Inject via the interface, Spring will wire the correct implementation
107157

108158
## Working with Spring AI
109159

@@ -121,6 +171,44 @@ fun getTourSpots(area: String): TourResponse
121171

122172
**Important**: System prompts are managed in `src/main/resources/prompts.yml` and compiled into BuildConfig at build time.
123173

174+
## WebSocket & Real-Time Messaging
175+
176+
### Architecture
177+
User-to-user chat uses WebSocket with STOMP protocol. The implementation switches based on profile:
178+
179+
**Development**: Uses Spring's SimpleBroker (in-memory)
180+
- Suitable for single-server development
181+
- No external dependencies
182+
- Messages stored in memory only
183+
184+
**Production**: Uses RabbitMQ STOMP Relay
185+
- Scales across multiple server instances
186+
- Messages persist in RabbitMQ
187+
- Handles reconnection and failover
188+
189+
### Message Flow
190+
1. Client connects to WebSocket endpoint: `/ws/userchat`
191+
2. Client sends message to: `/pub/chat/send`
192+
3. Server processes and publishes to: `/topic/chat/{roomId}`
193+
4. `ChatMessagePublisher` interface abstracts the publishing mechanism
194+
5. Messages are persisted to database via `ChatMessageService`
195+
196+
### RabbitMQ Configuration
197+
Located in `application.yml` and `application-prod.yml`:
198+
```yaml
199+
spring:
200+
rabbitmq:
201+
host: ${RABBITMQ_HOST}
202+
port: ${RABBITMQ_PORT}
203+
username: ${RABBITMQ_USERNAME}
204+
password: ${RABBITMQ_PASSWORD}
205+
stomp-port: ${RABBITMQ_STOMP_PORT} # Default: 61613
206+
```
207+
208+
RabbitMQ requires two plugins enabled:
209+
- `rabbitmq_management` - Management UI (port 15672)
210+
- `rabbitmq_stomp` - STOMP protocol support (port 61613)
211+
124212
## Testing Strategy
125213

126214
Tests are in `src/test/kotlin` mirroring the main structure:
@@ -152,7 +240,7 @@ Always run `./gradlew ktlintCheck` before committing - it's enforced by git hook
152240

153241
- **Development**: H2 in-memory (jdbc:h2:mem:testdb), resets on restart
154242
- **Production**: PostgreSQL (configured in application-prod.yml)
155-
- **JPA Strategy**: `ddl-auto: create-drop` in dev (wipes DB on restart)
243+
- **JPA Strategy**: `ddl-auto: create-drop` in dev (wipes DB on restart), `update` in prod
156244

157245
Main entities:
158246
- `User` - OAuth users with roles (GUEST/GUIDE/ADMIN)
@@ -166,7 +254,7 @@ Required `.env` file (copy from .env.example):
166254
```bash
167255
# AI (Required)
168256
OPENROUTER_API_KEY=sk-or-v1-...
169-
OPENROUTER_MODEL=anthropic/claude-3.5-sonnet
257+
OPENROUTER_MODEL=z-ai/glm-4.5-air:free
170258
171259
# OAuth (Required for auth)
172260
GOOGLE_CLIENT_ID=...
@@ -183,7 +271,14 @@ TOUR_API_KEY=...
183271
# JWT (Required for production)
184272
CUSTOM__JWT__SECRET_KEY=...
185273
186-
# Redis (Optional - caching)
274+
# RabbitMQ (Required for prod WebSocket)
275+
RABBITMQ_HOST=localhost
276+
RABBITMQ_PORT=5672
277+
RABBITMQ_USERNAME=admin
278+
RABBITMQ_PASSWORD=qwpokd153098
279+
RABBITMQ_STOMP_PORT=61613
280+
281+
# Redis (Optional in dev, required in prod)
187282
REDIS_HOST=localhost
188283
REDIS_PORT=6379
189284
REDIS_PASSWORD=
@@ -217,6 +312,33 @@ Common exceptions mapped to HTTP status:
217312
5. **Commit**: `{type}(scope): summary` (e.g., `feat(be): Add weather caching`)
218313
6. **PR title**: `{type}(scope): summary (#{issue})` (e.g., `feat(be): Add weather caching (#42)`)
219314

315+
## Infrastructure & Deployment
316+
317+
### Terraform Infrastructure (`infra/main.tf`)
318+
EC2 instance setup with:
319+
1. Docker & docker-compose installation
320+
2. Container network (`common`)
321+
3. Nginx Proxy Manager (ports 80, 443, 81)
322+
4. Redis (port 6379)
323+
5. PostgreSQL 16 (port 5432)
324+
6. RabbitMQ with management & STOMP plugins (ports 5672, 15672, 61613)
325+
326+
**Order matters**: Docker → docker-compose → network → containers
327+
328+
### CI/CD Pipeline (`.github/workflows/deploy.yml`)
329+
Blue-Green deployment strategy:
330+
1. Build on GitHub Actions runner
331+
2. Transfer JAR to EC2
332+
3. Deploy to blue/green container based on availability
333+
4. Health check before switching traffic
334+
5. Environment variables injected via `docker run -e`
335+
336+
### Docker Compose Files
337+
- `docker-compose.yml` (root) - Local development RabbitMQ
338+
- `infra/rabbitmq-docker-compose.yml` - EC2 production RabbitMQ template
339+
340+
Both require `common` network to be pre-created: `docker network create common`
341+
220342
## Common Issues & Solutions
221343

222344
### Build fails with "BuildConfig not found"
@@ -228,20 +350,33 @@ Common exceptions mapped to HTTP status:
228350
- Start Redis: `docker run -d -p 6379:6379 --name redis redis:alpine`
229351
- Check: `docker logs redis`
230352

353+
### RabbitMQ connection issues
354+
- In dev: RabbitMQ is optional (SimpleBroker used instead)
355+
- In prod: RabbitMQ is required for WebSocket
356+
- Start local RabbitMQ: `docker-compose up -d`
357+
- Check logs: `docker logs rabbitmq-1`
358+
- Verify plugins: `rabbitmq_management` and `rabbitmq_stomp` must be enabled
359+
231360
### ktlint failures
232361
- Auto-fix: `./gradlew ktlintFormat`
233362
- Pre-commit hook enforces this - setup via `./setup-git-hooks.sh`
234363

235364
### Spring AI errors
236365
- Verify `OPENROUTER_API_KEY` in .env
237-
- Check model name matches OpenRouter API (currently: anthropic/claude-3.5-sonnet)
366+
- Check model name matches OpenRouter API (currently: z-ai/glm-4.5-air:free)
238367
- Logs show AI requests: `logging.level.org.springframework.ai: DEBUG`
239368

240369
### OAuth login fails
241370
- Ensure all OAuth credentials in .env
242371
- Check redirect URIs match OAuth provider settings
243372
- Dev: `http://localhost:8080/login/oauth2/code/{provider}`
244373

374+
### WebSocket connection fails
375+
- Check profile: dev uses SimpleBroker, prod uses RabbitMQ
376+
- In prod: Ensure RabbitMQ is running and accessible
377+
- Verify STOMP port (61613) is open and reachable
378+
- Check `UserChatStompAuthChannelInterceptor` for authentication issues
379+
245380
## Important Notes
246381

247382
- **Never commit .env** - it's gitignored, contains secrets
@@ -250,3 +385,8 @@ Common exceptions mapped to HTTP status:
250385
- **Global config in common/** - don't duplicate security/config in domains
251386
- **BuildConfig is generated** - don't edit manually, modify YAML sources
252387
- **Redis is optional in dev** - required for production caching/sessions
388+
- **RabbitMQ is optional in dev** - required for production WebSocket scaling
389+
- **Profile-based beans** - use `@Profile` to switch implementations between dev/prod
390+
- **Dummy data in dev** - 2 guide users auto-generated on startup (DevConfig.kt)
391+
- **Docker network required** - `common` network must exist before running docker-compose
392+
- **Terraform order matters** - Docker installation before docker-compose, containers after both

src/main/resources/application.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ spring:
1111
exclude:
1212
- org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration # Redis 없어도 실행 가능하도록 변경
1313
- org.springframework.boot.autoconfigure.session.SessionAutoConfiguration # Redis 없어도 실행 가능하도록 변경
14+
- org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration # RabbitMQ 없어도 실행 가능하도록 변경 (dev용)
1415
config:
1516
import:
1617
- "optional:file:.env[.properties]"

0 commit comments

Comments
 (0)