From 0896e3bf30dfd5f16e06129fa792c6ac83b46df1 Mon Sep 17 00:00:00 2001 From: pricelees Date: Thu, 17 Jul 2025 18:18:26 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20ThemeController=20=EC=BD=94?= =?UTF-8?q?=ED=8B=80=EB=A6=B0=20=EB=A7=88=EC=9D=B4=EA=B7=B8=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20=EB=B0=8F=20Swagger=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/roomescape/theme/docs/ThemeAPI.kt | 51 +++++++ .../roomescape/theme/web/ThemeController.kt | 125 ++++++------------ .../java/roomescape/theme/web/ThemeDTO.kt | 2 + 3 files changed, 92 insertions(+), 86 deletions(-) create mode 100644 src/main/java/roomescape/theme/docs/ThemeAPI.kt diff --git a/src/main/java/roomescape/theme/docs/ThemeAPI.kt b/src/main/java/roomescape/theme/docs/ThemeAPI.kt new file mode 100644 index 00000000..624d830a --- /dev/null +++ b/src/main/java/roomescape/theme/docs/ThemeAPI.kt @@ -0,0 +1,51 @@ +package roomescape.theme.docs + +import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.Parameter +import io.swagger.v3.oas.annotations.responses.ApiResponse +import io.swagger.v3.oas.annotations.responses.ApiResponses +import io.swagger.v3.oas.annotations.tags.Tag +import jakarta.validation.Valid +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestParam +import roomescape.auth.web.support.Admin +import roomescape.auth.web.support.LoginRequired +import roomescape.common.dto.response.CommonApiResponse +import roomescape.theme.web.ThemeRequest +import roomescape.theme.web.ThemeResponse +import roomescape.theme.web.ThemesResponse + +@Tag(name = "5. 테마 API", description = "테마를 조회 / 추가 / 삭제할 때 사용합니다.") +interface ThemeAPI { + + @LoginRequired + @Operation(summary = "모든 테마 조회", description = "모든 테마를 조회합니다.", tags = ["로그인이 필요한 API"]) + @ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true)) + fun getAllThemes(): ResponseEntity> + + @Operation(summary = "가장 많이 예약된 테마 조회") + @ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true)) + fun getMostReservedThemes( + @RequestParam(defaultValue = "10") @Parameter(description = "최대로 조회할 테마 갯수") count: Int + ): ResponseEntity> + + @Admin + @Operation(summary = "테마 추가", tags = ["관리자 로그인이 필요한 API"]) + @ApiResponses( + ApiResponse(responseCode = "201", description = "성공", useReturnTypeSchema = true), + ) + fun saveTheme( + @Valid @RequestBody request: ThemeRequest, + ): ResponseEntity> + + @Admin + @Operation(summary = "테마 삭제", tags = ["관리자 로그인이 필요한 API"]) + @ApiResponses( + ApiResponse(responseCode = "204", description = "성공", useReturnTypeSchema = true), + ) + fun removeTheme( + @PathVariable id: Long + ): ResponseEntity> +} diff --git a/src/main/java/roomescape/theme/web/ThemeController.kt b/src/main/java/roomescape/theme/web/ThemeController.kt index b75758d1..c7182b83 100644 --- a/src/main/java/roomescape/theme/web/ThemeController.kt +++ b/src/main/java/roomescape/theme/web/ThemeController.kt @@ -1,98 +1,51 @@ -package roomescape.theme.web; +package roomescape.theme.web -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.bind.annotation.RestController; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.responses.ApiResponses; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; -import jakarta.validation.constraints.NotNull; -import roomescape.auth.web.support.Admin; -import roomescape.auth.web.support.LoginRequired; -import roomescape.common.dto.response.RoomescapeApiResponse; -import roomescape.common.dto.response.RoomescapeErrorResponse; -import roomescape.theme.business.ThemeService; +import io.swagger.v3.oas.annotations.Parameter +import jakarta.validation.Valid +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.* +import roomescape.common.dto.response.CommonApiResponse +import roomescape.theme.business.ThemeService +import roomescape.theme.docs.ThemeAPI +import java.net.URI @RestController -@Tag(name = "5. 테마 API", description = "테마를 조회 / 추가 / 삭제할 때 사용합니다.") -public class ThemeController { +class ThemeController( + private val themeService: ThemeService +) : ThemeAPI { - private final ThemeService themeService; + @GetMapping("/themes") + override fun getAllThemes(): ResponseEntity> { + val response: ThemesResponse = themeService.findAllThemes() - public ThemeController(ThemeService themeService) { - this.themeService = themeService; - } + return ResponseEntity.ok(CommonApiResponse(response)) + } - @LoginRequired - @GetMapping("/themes") - @ResponseStatus(HttpStatus.OK) - @Operation(summary = "모든 테마 조회", description = "모든 테마를 조회합니다.", tags = "로그인이 필요한 API") - @ApiResponses({ - @ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true) - }) - public RoomescapeApiResponse getAllThemes() { - return RoomescapeApiResponse.success(themeService.findAllThemes()); - } + @GetMapping("/themes/most-reserved-last-week") + override fun getMostReservedThemes( + @RequestParam(defaultValue = "10") @Parameter(description = "최대로 조회할 테마 갯수") count: Int + ): ResponseEntity> { + val response: ThemesResponse = themeService.getMostReservedThemesByCount(count) - @GetMapping("/themes/most-reserved-last-week") - @ResponseStatus(HttpStatus.OK) - @Operation(summary = "가장 많이 예약된 테마 조회") - @ApiResponses({ - @ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true) - }) - public RoomescapeApiResponse getMostReservedThemes( - @RequestParam(defaultValue = "10") @Parameter(description = "최대로 조회할 테마 갯수") int count - ) { - return RoomescapeApiResponse.success(themeService.getMostReservedThemesByCount(count)); - } + return ResponseEntity.ok(CommonApiResponse(response)) + } - @Admin - @PostMapping("/themes") - @ResponseStatus(HttpStatus.CREATED) - @Operation(summary = "테마 추가", tags = "관리자 로그인이 필요한 API") - @ApiResponses({ - @ApiResponse(responseCode = "201", description = "성공", useReturnTypeSchema = true), - @ApiResponse(responseCode = "409", description = "같은 이름의 테마를 추가할 수 없습니다.", - content = @Content(schema = @Schema(implementation = RoomescapeErrorResponse.class))) - }) - public RoomescapeApiResponse saveTheme( - @Valid @RequestBody ThemeRequest request, - HttpServletResponse response - ) { - ThemeResponse themeResponse = themeService.addTheme(request); - response.setHeader(HttpHeaders.LOCATION, "/themes/" + themeResponse.id()); + @PostMapping("/themes") + override fun saveTheme( + @RequestBody @Valid request: ThemeRequest + ): ResponseEntity> { + val themeResponse: ThemeResponse = themeService.addTheme(request) - return RoomescapeApiResponse.success(themeResponse); - } + return ResponseEntity.created(URI.create("/themes/${themeResponse.id}")) + .body(CommonApiResponse(themeResponse)) + } - @Admin - @DeleteMapping("/themes/{id}") - @ResponseStatus(HttpStatus.NO_CONTENT) - @Operation(summary = "테마 삭제", tags = "관리자 로그인이 필요한 API") - @ApiResponses({ - @ApiResponse(responseCode = "204", description = "성공", useReturnTypeSchema = true), - @ApiResponse(responseCode = "409", description = "예약된 테마는 삭제할 수 없습니다.", - content = @Content(schema = @Schema(implementation = RoomescapeErrorResponse.class))) - }) - public RoomescapeApiResponse removeTheme( - @NotNull(message = "themeId는 null일 수 없습니다.") @PathVariable Long id - ) { - themeService.removeThemeById(id); + @DeleteMapping("/themes/{id}") + override fun removeTheme( + @PathVariable id: Long + ): ResponseEntity> { + themeService.removeThemeById(id) - return RoomescapeApiResponse.success(); - } + return ResponseEntity.noContent().build() + } } diff --git a/src/main/java/roomescape/theme/web/ThemeDTO.kt b/src/main/java/roomescape/theme/web/ThemeDTO.kt index 14291da9..7f9a4ad8 100644 --- a/src/main/java/roomescape/theme/web/ThemeDTO.kt +++ b/src/main/java/roomescape/theme/web/ThemeDTO.kt @@ -3,6 +3,7 @@ package roomescape.theme.web import io.swagger.v3.oas.annotations.media.Schema import jakarta.validation.constraints.NotBlank import jakarta.validation.constraints.Size +import org.hibernate.validator.constraints.URL import roomescape.theme.infrastructure.persistence.Theme @Schema(name = "테마 저장 요청", description = "테마 정보를 저장할 때 사용합니다.") @@ -20,6 +21,7 @@ data class ThemeRequest( @field:Schema(description = "필수 값이며, 썸네일 이미지 URL 을 입력해주세요.") @NotBlank + @URL val thumbnail: String )