From 821f379e78aa71f2988ad8f0f6eba92c5246ff85 Mon Sep 17 00:00:00 2001 From: pricelees Date: Wed, 23 Jul 2025 16:43:31 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20theme=20=EB=82=B4=EC=9D=98=20DTO=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=20=EC=BB=A8=EB=B2=A4=EC=85=98=20=ED=86=B5?= =?UTF-8?q?=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reservation/web/ReservationResponse.kt | 4 +- .../roomescape/theme/business/ThemeService.kt | 6 +-- .../kotlin/roomescape/theme/docs/ThemeAPI.kt | 14 +++---- .../roomescape/theme/web/ThemeController.kt | 14 +++---- .../kotlin/roomescape/theme/web/ThemeDTO.kt | 39 +++++++------------ .../theme/business/ThemeServiceTest.kt | 4 +- .../theme/web/ThemeControllerTest.kt | 12 +++--- 7 files changed, 42 insertions(+), 51 deletions(-) diff --git a/src/main/kotlin/roomescape/reservation/web/ReservationResponse.kt b/src/main/kotlin/roomescape/reservation/web/ReservationResponse.kt index 8cd2d26a..711eabd6 100644 --- a/src/main/kotlin/roomescape/reservation/web/ReservationResponse.kt +++ b/src/main/kotlin/roomescape/reservation/web/ReservationResponse.kt @@ -6,7 +6,7 @@ import roomescape.member.web.MemberRetrieveResponse import roomescape.member.web.toRetrieveResponse import roomescape.reservation.infrastructure.persistence.ReservationEntity import roomescape.reservation.infrastructure.persistence.ReservationStatus -import roomescape.theme.web.ThemeResponse +import roomescape.theme.web.ThemeRetrieveResponse import roomescape.theme.web.toResponse import java.time.LocalDate import java.time.LocalTime @@ -41,7 +41,7 @@ data class ReservationRetrieveResponse( val time: TimeCreateResponse, @field:JsonProperty("theme") - val theme: ThemeResponse, + val theme: ThemeRetrieveResponse, val status: ReservationStatus ) diff --git a/src/main/kotlin/roomescape/theme/business/ThemeService.kt b/src/main/kotlin/roomescape/theme/business/ThemeService.kt index 91708c13..8a31b170 100644 --- a/src/main/kotlin/roomescape/theme/business/ThemeService.kt +++ b/src/main/kotlin/roomescape/theme/business/ThemeService.kt @@ -27,11 +27,11 @@ class ThemeService( ) @Transactional(readOnly = true) - fun findThemes(): ThemesResponse = themeRepository.findAll() + fun findThemes(): ThemeRetrieveListResponse = themeRepository.findAll() .toResponse() @Transactional(readOnly = true) - fun findMostReservedThemes(count: Int): ThemesResponse { + fun findMostReservedThemes(count: Int): ThemeRetrieveListResponse { val today = LocalDate.now() val startDate = today.minusDays(7) val endDate = today.minusDays(1) @@ -41,7 +41,7 @@ class ThemeService( } @Transactional - fun createTheme(request: ThemeRequest): ThemeResponse { + fun createTheme(request: ThemeCreateRequest): ThemeRetrieveResponse { if (themeRepository.existsByName(request.name)) { throw RoomescapeException( ErrorType.THEME_DUPLICATED, diff --git a/src/main/kotlin/roomescape/theme/docs/ThemeAPI.kt b/src/main/kotlin/roomescape/theme/docs/ThemeAPI.kt index d971884b..3012aaa7 100644 --- a/src/main/kotlin/roomescape/theme/docs/ThemeAPI.kt +++ b/src/main/kotlin/roomescape/theme/docs/ThemeAPI.kt @@ -13,9 +13,9 @@ 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 +import roomescape.theme.web.ThemeCreateRequest +import roomescape.theme.web.ThemeRetrieveResponse +import roomescape.theme.web.ThemeRetrieveListResponse @Tag(name = "5. 테마 API", description = "테마를 조회 / 추가 / 삭제할 때 사용합니다.") interface ThemeAPI { @@ -23,13 +23,13 @@ interface ThemeAPI { @LoginRequired @Operation(summary = "모든 테마 조회", description = "모든 테마를 조회합니다.", tags = ["로그인이 필요한 API"]) @ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true)) - fun findThemes(): ResponseEntity> + fun findThemes(): ResponseEntity> @Operation(summary = "가장 많이 예약된 테마 조회") @ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true)) fun findMostReservedThemes( @RequestParam(defaultValue = "10") @Parameter(description = "최대로 조회할 테마 갯수") count: Int - ): ResponseEntity> + ): ResponseEntity> @Admin @Operation(summary = "테마 추가", tags = ["관리자 로그인이 필요한 API"]) @@ -37,8 +37,8 @@ interface ThemeAPI { ApiResponse(responseCode = "201", description = "성공", useReturnTypeSchema = true), ) fun createTheme( - @Valid @RequestBody request: ThemeRequest, - ): ResponseEntity> + @Valid @RequestBody request: ThemeCreateRequest, + ): ResponseEntity> @Admin @Operation(summary = "테마 삭제", tags = ["관리자 로그인이 필요한 API"]) diff --git a/src/main/kotlin/roomescape/theme/web/ThemeController.kt b/src/main/kotlin/roomescape/theme/web/ThemeController.kt index 05820cb2..c0be7fc1 100644 --- a/src/main/kotlin/roomescape/theme/web/ThemeController.kt +++ b/src/main/kotlin/roomescape/theme/web/ThemeController.kt @@ -15,8 +15,8 @@ class ThemeController( ) : ThemeAPI { @GetMapping("/themes") - override fun findThemes(): ResponseEntity> { - val response: ThemesResponse = themeService.findThemes() + override fun findThemes(): ResponseEntity> { + val response: ThemeRetrieveListResponse = themeService.findThemes() return ResponseEntity.ok(CommonApiResponse(response)) } @@ -24,17 +24,17 @@ class ThemeController( @GetMapping("/themes/most-reserved-last-week") override fun findMostReservedThemes( @RequestParam(defaultValue = "10") @Parameter(description = "최대로 조회할 테마 갯수") count: Int - ): ResponseEntity> { - val response: ThemesResponse = themeService.findMostReservedThemes(count) + ): ResponseEntity> { + val response: ThemeRetrieveListResponse = themeService.findMostReservedThemes(count) return ResponseEntity.ok(CommonApiResponse(response)) } @PostMapping("/themes") override fun createTheme( - @RequestBody @Valid request: ThemeRequest - ): ResponseEntity> { - val themeResponse: ThemeResponse = themeService.createTheme(request) + @RequestBody @Valid request: ThemeCreateRequest + ): ResponseEntity> { + val themeResponse: ThemeRetrieveResponse = themeService.createTheme(request) return ResponseEntity.created(URI.create("/themes/${themeResponse.id}")) .body(CommonApiResponse(themeResponse)) diff --git a/src/main/kotlin/roomescape/theme/web/ThemeDTO.kt b/src/main/kotlin/roomescape/theme/web/ThemeDTO.kt index 303ab9e5..d183e56f 100644 --- a/src/main/kotlin/roomescape/theme/web/ThemeDTO.kt +++ b/src/main/kotlin/roomescape/theme/web/ThemeDTO.kt @@ -1,57 +1,48 @@ 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.ThemeEntity -@Schema(name = "테마 저장 요청", description = "테마 정보를 저장할 때 사용합니다.") -data class ThemeRequest( - @Schema(description = "필수 값이며, 최대 20글자까지 입력 가능합니다.") +data class ThemeCreateRequest( @NotBlank - @Size(max = 20, message = "테마의 이름은 1~20글자 사이여야 합니다.") + @Size(max = 20) val name: String, - @Schema(description = "필수 값이며, 최대 100글자까지 입력 가능합니다.") @NotBlank - @Size(max = 100, message = "테마의 설명은 1~100글자 사이여야 합니다.") + @Size(max = 100) val description: String, - @Schema(description = "필수 값이며, 썸네일 이미지 URL 을 입력해주세요.") - @NotBlank @URL + @NotBlank val thumbnail: String ) -@Schema(name = "테마 정보", description = "테마 추가 및 조회 응답에 사용됩니다.") -data class ThemeResponse( - @Schema(description = "테마 번호. 테마를 식별할 때 사용합니다.") +fun ThemeCreateRequest.toEntity(): ThemeEntity = ThemeEntity( + name = this.name, + description = this.description, + thumbnail = this.thumbnail +) + +data class ThemeRetrieveResponse( val id: Long, - - @Schema(description = "테마 이름. 중복을 허용하지 않습니다.") val name: String, - - @Schema(description = "테마 설명") val description: String, - - @Schema(description = "테마 썸네일 이미지 URL") val thumbnail: String ) -fun ThemeEntity.toResponse(): ThemeResponse = ThemeResponse( +fun ThemeEntity.toResponse(): ThemeRetrieveResponse = ThemeRetrieveResponse( id = this.id!!, name = this.name, description = this.description, thumbnail = this.thumbnail ) -@Schema(name = "테마 목록 조회 응답", description = "모든 테마 목록 조회 응답시 사용됩니다.") -data class ThemesResponse( - @Schema(description = "모든 테마 목록") - val themes: List +data class ThemeRetrieveListResponse( + val themes: List ) -fun List.toResponse(): ThemesResponse = ThemesResponse( +fun List.toResponse(): ThemeRetrieveListResponse = ThemeRetrieveListResponse( themes = this.map { it.toResponse() } ) diff --git a/src/test/kotlin/roomescape/theme/business/ThemeServiceTest.kt b/src/test/kotlin/roomescape/theme/business/ThemeServiceTest.kt index c823c351..d22108b1 100644 --- a/src/test/kotlin/roomescape/theme/business/ThemeServiceTest.kt +++ b/src/test/kotlin/roomescape/theme/business/ThemeServiceTest.kt @@ -12,7 +12,7 @@ import roomescape.common.exception.ErrorType import roomescape.common.exception.RoomescapeException import roomescape.theme.infrastructure.persistence.ThemeEntity import roomescape.theme.infrastructure.persistence.ThemeRepository -import roomescape.theme.web.ThemeRequest +import roomescape.theme.web.ThemeCreateRequest import roomescape.util.ThemeFixture class ThemeServiceTest : FunSpec({ @@ -68,7 +68,7 @@ class ThemeServiceTest : FunSpec({ } returns true val exception = shouldThrow { - themeService.createTheme(ThemeRequest( + themeService.createTheme(ThemeCreateRequest( name = name, description = "Description", thumbnail = "http://example.com/thumbnail.jpg" diff --git a/src/test/kotlin/roomescape/theme/web/ThemeControllerTest.kt b/src/test/kotlin/roomescape/theme/web/ThemeControllerTest.kt index f4963788..c151a9f8 100644 --- a/src/test/kotlin/roomescape/theme/web/ThemeControllerTest.kt +++ b/src/test/kotlin/roomescape/theme/web/ThemeControllerTest.kt @@ -58,7 +58,7 @@ class ThemeControllerTest(mockMvc: MockMvc) : RoomescapeApiTest() { ThemeFixture.create(id = 3, name = "theme3") ) - val response: ThemesResponse = runGetTest( + val response: ThemeRetrieveListResponse = runGetTest( mockMvc = mockMvc, endpoint = endpoint, ) { @@ -66,7 +66,7 @@ class ThemeControllerTest(mockMvc: MockMvc) : RoomescapeApiTest() { content { contentType(MediaType.APPLICATION_JSON) } - }.andReturn().readValue(ThemesResponse::class.java) + }.andReturn().readValue(ThemeRetrieveListResponse::class.java) assertSoftly(response.themes) { it.size shouldBe 3 @@ -78,7 +78,7 @@ class ThemeControllerTest(mockMvc: MockMvc) : RoomescapeApiTest() { Given("테마를 추가할 때") { val endpoint = "/themes" - val request = ThemeRequest( + val request = ThemeCreateRequest( name = "theme1", description = "description1", thumbnail = "http://example.com/thumbnail1.jpg" @@ -138,13 +138,13 @@ class ThemeControllerTest(mockMvc: MockMvc) : RoomescapeApiTest() { loginAsAdmin() } - val request = ThemeRequest( + val request = ThemeCreateRequest( name = "theme1", description = "description1", thumbnail = "http://example.com/thumbnail1.jpg" ) - fun runTest(request: ThemeRequest) { + fun runTest(request: ThemeCreateRequest) { runPostTest( mockMvc = mockMvc, endpoint = endpoint, @@ -197,7 +197,7 @@ class ThemeControllerTest(mockMvc: MockMvc) : RoomescapeApiTest() { every { themeService.createTheme(request) - } returns ThemeResponse( + } returns ThemeRetrieveResponse( id = theme.id!!, name = theme.name, description = theme.description,