| name | api-patterns |
| description | Use when writing or modifying Spring WebMVC controllers and REST endpoints. Covers the `DpmApiResponse<T>` response wrapper, `DpmException` + `ErrorCode` for errors, `@Operation`/`@Tag` Swagger docs (mandatory on every endpoint), request DTO validation, and HTTP status conventions. Trigger on any file ending in `Controller.kt` or when adding a new API route. |
API Patterns
Response Wrapper
๋ชจ๋ ์๋ํฌ์ธํธ๋ DpmApiResponse<T>๋ก ์๋ต์ ๊ฐ์ธ์ผ ํ๋ค.
data class DpmApiResponse<T>(
val data: T? = null,
val error: ErrorResponse? = null,
)
fun someEndpoint(): DpmApiResponse<SomeResponse> {
val result = someService(params)
return DpmApiResponse.ok(result)
}
fun deleteEndpoint(): DpmApiResponse<Unit> {
someService.delete(id)
return DpmApiResponse.ok()
}
์ปจํธ๋กค๋ฌ ๊ตฌ์กฐ
@RestController
@RequestMapping("${ContextConstants.API_VERSION_V1}/meetings")
@Tag(name = "Meeting", description = "๋ฏธํ
๊ด๋ จ API")
class MeetingController(
private val createMeetingService: CreateMeetingService,
private val getMeetingService: GetMeetingService,
) {
@PostMapping
@Operation(summary = "๋ฏธํ
์์ฑ", description = "์๋ก์ด ๋ฏธํ
์ ์์ฑํฉ๋๋ค.")
@ApiResponses(
ApiResponse(responseCode = "200", description = "์ฑ๊ณต"),
ApiResponse(responseCode = "404", description = "์ฌ์ฉ์ ์์ - USER_NOT_FOUND"),
)
suspend fun createMeeting(
@UserId userId: Long,
@Valid @RequestBody request: CreateMeetingRequest,
): DpmApiResponse<MeetingResponse> {
return DpmApiResponse.ok(createMeetingService(userId, request))
}
}
์ฌ์ฉ์ ์ธ์ฆ ์ถ์ถ
@AuthenticationPrincipal์ ์ง์ ์ฌ์ฉํ์ง ๋ง ๊ฒ. ํญ์ ์ปค์คํ
@UserId ์ด๋
ธํ
์ด์
์ ์ฌ์ฉํ๋ค.
suspend fun someEndpoint(@UserId userId: Long): DpmApiResponse<SomeResponse>
suspend fun someEndpoint(@UserId userId: Long?): DpmApiResponse<SomeResponse>
suspend fun someEndpoint(@MeetingId meetingId: Long): DpmApiResponse<SomeResponse>
์์ธ ์ฒ๋ฆฌ
RuntimeException์ด๋ ์ผ๋ฐ ์์ธ๋ฅผ ๋์ง์ง ๋ง ๊ฒ. ๋ฐ๋์ ๋๋ฉ์ธ๋ณ DpmException ์๋ธํด๋์ค๋ฅผ ์ฌ์ฉํ๋ค.
class MeetingException(
errorCode: ErrorCode,
detail: Map<String, Any?>? = null,
) : DpmException(errorCode, detail)
throw MeetingException(ErrorCode.MEETING_NOT_FOUND)
throw MeetingException(ErrorCode.INVALID_REQUEST, mapOf("field" to "name", "value" to name))
์์ฒญ ์ ํจ์ฑ ๊ฒ์ฌ
data class CreateMeetingRequest(
@field:NotBlank(message = "๋ฏธํ
์ด๋ฆ์ ํ์์
๋๋ค")
val name: String,
@field:Size(max = 50)
val description: String?,
)
suspend fun create(@Valid @RequestBody request: CreateMeetingRequest)
Command ๊ฐ์ฒด
์ปจํธ๋กค๋ฌ โ ์๋น์ค ๊ฐ ํ๋ผ๋ฏธํฐ๊ฐ ๋ง์ ๊ฒฝ์ฐ Command ๊ฐ์ฒด๋ก ๋ฌถ๋๋ค.
data class CreateMeetingCommand(
val userId: Long,
val name: String,
val locationId: Long,
)
val command = CreateMeetingCommand(userId, request.name, request.locationId)
createMeetingService(command)
Swagger ๋ฌธ์ํ ์ฒดํฌ๋ฆฌ์คํธ
์ ์๋ํฌ์ธํธ ์ถ๊ฐ ์: