Skip to content

Commit c2f44a9

Browse files
authored
feat/#56 -> staging merge commit
* docs: CLAUDE.md 추가 * docs: TESTING.md 추가 * chore: 사용하지 않는 문서 제거
1 parent c3e484f commit c2f44a9

File tree

6 files changed

+1034
-0
lines changed

6 files changed

+1034
-0
lines changed

.claude/CLAUDE.md

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# CLAUDE.md
2+
3+
Instructions for Claude Code when working with this repository.
4+
5+
## Quick Reference
6+
7+
```bash
8+
# Build & Run
9+
./gradlew build # Build project
10+
./gradlew bootRun # Run locally (requires Docker)
11+
./gradlew test # Run all tests
12+
./gradlew spotlessApply # Format code (ktlint)
13+
14+
# Docker (local development)
15+
docker compose up -d # Start PostgreSQL + Redis
16+
docker compose down # Stop containers
17+
```
18+
19+
---
20+
21+
## Project Overview
22+
23+
**Yapp** is a photo booth platform API server providing:
24+
- **Photo Poses**: Users share photo booth pose recommendations
25+
- **Photo Archiving**: Store and organize photos in folders
26+
- **Booth Location Search**: Map-based search across multiple photo booth brands
27+
28+
| Profile | Infrastructure |
29+
|---------|---------------|
30+
| `local` | Docker Compose |
31+
| `staging` | k3s (Linux) |
32+
| `prod` | k3s (Linux) |
33+
34+
---
35+
36+
## Technology Stack
37+
38+
| Category | Technology |
39+
|----------|------------|
40+
| Language | Kotlin 2.0, Java 21 |
41+
| Framework | Spring Boot 3.5 |
42+
| Database | PostgreSQL (main), Redis (cache) |
43+
| ORM | JPA + QueryDSL |
44+
| Auth | JWT, OAuth (Kakao, Apple OIDC) |
45+
| Storage | AWS S3 |
46+
| Docs | SpringDoc OpenAPI (Swagger) |
47+
| Testing | Kotest, MockK |
48+
| Code Style | ktlint via Spotless |
49+
50+
---
51+
52+
## Critical Constraints
53+
54+
### ❌ NEVER DO
55+
56+
| Constraint | Reason |
57+
|------------|--------|
58+
| Import from other domains | Breaks module isolation |
59+
| Bypass ports to access infra directly | Violates Clean Architecture (exception: auth/user) |
60+
| Remove observability code | Critical for production debugging |
61+
62+
### ✅ ALWAYS DO
63+
64+
| Practice | Reason |
65+
|----------|--------|
66+
| Wrap responses in `BaseResponse` | Consistent API format |
67+
| Use `BusinessException` for errors | Centralized error handling |
68+
| Write E2E tests for new endpoints | Quality assurance |
69+
| Follow existing package structure | Maintainability |
70+
| Run `spotlessApply` before commit | Code style consistency |
71+
72+
---
73+
74+
## Code Modification Guidelines
75+
76+
1. **Minimal changes**: Prefer targeted fixes over large refactors
77+
2. **Follow conventions**: Match existing naming and package patterns
78+
3. **No new dependencies**: Unless explicitly requested
79+
4. **No reformatting**: Only modify relevant files
80+
5. **Test coverage**: Add E2E tests for new endpoints
81+
82+
---
83+
84+
## Context Loading Guide
85+
86+
Load additional context as needed using `@` references:
87+
88+
| Task | Load Command |
89+
|------|--------------|
90+
| Writing tests | `@.claude/docs/TESTING.md` |
91+
| API development | `@.claude/docs/API_PATTERNS.md` |
92+
| Architecture/Design | `@.claude/docs/ARCHITECTURE.md` |
93+
| Configuration | `@.claude/docs/CONFIGURATION.md` |
94+
| Logging/Metrics | `@.claude/docs/OBSERVABILITY.md` |
95+
96+
### Quick File Reference
97+
98+
| Component | Location |
99+
|-----------|----------|
100+
| UseCase annotation | `src/main/kotlin/com/yapp2app/common/annotation/UseCase.kt` |
101+
| Base response | `src/main/kotlin/com/yapp2app/common/api/dto/BaseResponse.kt` |
102+
| Result codes | `src/main/kotlin/com/yapp2app/common/api/dto/ResultCode.kt` |
103+
| Business exception | `src/main/kotlin/com/yapp2app/common/exception/BusinessException.kt` |
104+
| E2E test base | `src/test/kotlin/com/yapp2app/e2e/E2ETestBase.kt` |

.claude/docs/API_PATTERNS.md

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
# API Development Patterns
2+
3+
Load this context when creating or modifying API endpoints.
4+
5+
---
6+
7+
## Response Wrapper
8+
9+
ALL API responses MUST use `BaseResponse<T>`:
10+
11+
```kotlin
12+
data class BaseResponse<T>(
13+
val resultCode: String = ResultCode.SUCCESS.code,
14+
val message: String = ResultCode.SUCCESS.message,
15+
val success: Boolean = true,
16+
val data: T? = null,
17+
)
18+
```
19+
20+
**Usage in Controller:**
21+
```kotlin
22+
@GetMapping
23+
fun getAllFolders(): BaseResponse<GetAllFolderResponse> {
24+
val result = getFoldersUseCase.execute(command)
25+
val response = resultConverter.toResponse(result)
26+
return BaseResponse(data = response)
27+
}
28+
```
29+
30+
Reference: `src/main/kotlin/com/yapp2app/common/api/dto/BaseResponse.kt`
31+
32+
---
33+
34+
## Data Flow Pattern
35+
36+
```
37+
Request → Controller → Converter → Command → UseCase → Result → Converter → Response
38+
```
39+
40+
### Complete Example
41+
42+
```kotlin
43+
@RestController
44+
@RequestMapping("/api/folders")
45+
class FolderController(
46+
private val createFolderUseCase: CreateFolderUseCase,
47+
private val commandConverter: FolderCommandConverter,
48+
private val resultConverter: FolderResultConverter,
49+
) {
50+
@PostMapping
51+
fun createFolder(
52+
@AuthenticationPrincipal(expression = "id") userId: Long,
53+
@Valid @RequestBody request: CreateFolderRequest,
54+
): BaseResponse<CreateFolderResponse> {
55+
// 1. Convert request to command
56+
val command = commandConverter.toCreateFolderCommand(request, userId)
57+
58+
// 2. Execute use case
59+
val result = createFolderUseCase.execute(command)
60+
61+
// 3. Convert result to response
62+
val response = resultConverter.toCreateFolderResponse(result)
63+
64+
// 4. Wrap in BaseResponse
65+
return BaseResponse(data = response)
66+
}
67+
}
68+
```
69+
70+
---
71+
72+
## Exception Handling
73+
74+
Use `BusinessException` with `ResultCode`:
75+
76+
```kotlin
77+
// Throwing exceptions
78+
throw BusinessException(ResultCode.CONFLICT_FOLDER)
79+
throw BusinessException(ResultCode.NOT_FOUND)
80+
81+
// Available result codes
82+
enum class ResultCode(val code: String, val message: String) {
83+
SUCCESS("D-0", "OK"),
84+
ERROR("D-99", "ERROR"),
85+
INVALID_PARAMETER("D-01", "Invalid input"),
86+
ALREADY_SIGNUP("D-02", "Already registered"),
87+
NOT_FOUND_USER("D-03", "User not found"),
88+
NOT_FOUND("D-04", "Data not found"),
89+
ALREADY_REQUEST("D-05", "Already processed"),
90+
CONFLICT_FOLDER("D-06", "Folder already exists"),
91+
// Token errors
92+
EXPIRED_TOKEN_ERROR("D-997", "Token expired"),
93+
INVALID_TOKEN_ERROR("D-998", "Invalid token"),
94+
SECURITY_ERROR("D-999", "Authentication failed"),
95+
}
96+
```
97+
98+
Key files:
99+
- `src/main/kotlin/com/yapp2app/common/exception/BusinessException.kt`
100+
- `src/main/kotlin/com/yapp2app/common/api/dto/ResultCode.kt`
101+
- `src/main/kotlin/com/yapp2app/common/exception/handler/ExceptionHandler.kt`
102+
103+
---
104+
105+
## Swagger Documentation
106+
107+
Every endpoint MUST include `@Operation`:
108+
109+
```kotlin
110+
@Operation(
111+
summary = "Create folder", // Short description
112+
description = "Creates a new photo folder for the user." // Detailed for frontend devs
113+
)
114+
@PostMapping
115+
fun createFolder(...): BaseResponse<CreateFolderResponse>
116+
```
117+
118+
### Multi-Step Workflow Documentation
119+
120+
For endpoints that are part of a workflow, document the full flow:
121+
122+
```kotlin
123+
@Operation(
124+
summary = "Register photo image",
125+
description = """
126+
Photo upload workflow:
127+
1. Call GET /api/media/presigned-url → Get S3 upload URL
128+
2. Client uploads file directly to S3
129+
3. Call this API to register the photo metadata
130+
131+
The imageKey from step 1 must be passed to this endpoint.
132+
"""
133+
)
134+
```
135+
136+
### Security Annotation
137+
138+
Protected endpoints use `@RequiresSecurity`:
139+
140+
```kotlin
141+
@RequiresSecurity // Marks endpoint as requiring authentication in Swagger
142+
@Tag(name = "folder", description = "Folder APIs")
143+
@RestController
144+
@RequestMapping("/api/folders")
145+
class FolderController
146+
```
147+
148+
Reference: `src/main/kotlin/com/yapp2app/common/api/document/SwaggerConfig.kt`
149+
150+
---
151+
152+
## Request Validation
153+
154+
Use Jakarta validation annotations:
155+
156+
```kotlin
157+
data class CreateFolderRequest(
158+
@field:NotBlank(message = "Folder name is required")
159+
@field:Size(max = 50, message = "Folder name must be 50 characters or less")
160+
val name: String,
161+
)
162+
```
163+
164+
Validation errors are automatically handled and return:
165+
```json
166+
{
167+
"resultCode": "D-01",
168+
"message": "Folder name is required",
169+
"success": false,
170+
"errors": [
171+
{ "field": "name", "message": "Folder name is required" }
172+
]
173+
}
174+
```
175+
176+
---
177+
178+
## Authentication
179+
180+
Get current user ID from JWT:
181+
182+
```kotlin
183+
@PostMapping
184+
fun createFolder(
185+
@AuthenticationPrincipal(expression = "id") userId: Long, // Extracts user ID from token
186+
@RequestBody request: CreateFolderRequest,
187+
): BaseResponse<CreateFolderResponse>
188+
```
189+
190+
---
191+
192+
## Checklist for New Endpoints
193+
194+
- [ ] Use `BaseResponse<T>` wrapper
195+
- [ ] Create Request/Response DTOs in `api/dto/`
196+
- [ ] Create Command in `application/command/`
197+
- [ ] Create Result in `application/result/`
198+
- [ ] Create Converters in `api/converter/`
199+
- [ ] Add `@Operation` with summary and description
200+
- [ ] Add `@RequiresSecurity` if authentication required
201+
- [ ] Add validation annotations to request DTOs
202+
- [ ] Write E2E tests (see `@.claude/docs/TESTING.md`)

0 commit comments

Comments
 (0)