From c673bee0ae14289669c1b728f9208a1b25c6d4e2 Mon Sep 17 00:00:00 2001 From: seokjin8678 Date: Sun, 18 Feb 2024 18:09:27 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20API=20=EC=9D=91=EB=8B=B5=EC=97=90?= =?UTF-8?q?=20status=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kr/galaxyhub/sc/api/common/ApiResponse.kt | 19 ++++++++++++++----- .../sc/api/common/ExceptionHandler.kt | 9 +++++---- .../api/v1/auth/oauth2/OAuth2ControllerV1.kt | 2 +- .../sc/api/v1/crawler/CrawlerControllerV1.kt | 2 +- .../sc/api/v1/news/NewsControllerV1.kt | 8 ++++---- .../v1/translation/TranslationControllerV1.kt | 4 ++-- .../kr/galaxyhub/sc/api/support/RestDocDsl.kt | 6 ++++++ .../v1/auth/oauth2/OAuth2ControllerV1Test.kt | 2 +- .../api/v1/crawler/CrawlerControllerV1Test.kt | 2 +- .../sc/api/v1/news/NewsControllerV1Test.kt | 6 +++--- .../TranslationControllerV1Test.kt | 6 +++--- 11 files changed, 41 insertions(+), 25 deletions(-) diff --git a/src/main/kotlin/kr/galaxyhub/sc/api/common/ApiResponse.kt b/src/main/kotlin/kr/galaxyhub/sc/api/common/ApiResponse.kt index 4353baa..de4b3b3 100644 --- a/src/main/kotlin/kr/galaxyhub/sc/api/common/ApiResponse.kt +++ b/src/main/kotlin/kr/galaxyhub/sc/api/common/ApiResponse.kt @@ -5,18 +5,27 @@ import com.fasterxml.jackson.annotation.JsonInclude.Include @JsonInclude(Include.NON_NULL) class ApiResponse( + val status: Int, val message: String? = null, - val data: T, + val data: T?, ) { companion object { - fun error(message: String): ApiResponse { - return ApiResponse(message, Unit) + fun error(statusCode: Int, message: String): ApiResponse { + return ApiResponse(status = statusCode, message = message, data = Unit) } - fun success(data: T): ApiResponse { - return ApiResponse(data = data) + fun ok(data: T): ApiResponse { + return ApiResponse(status = 200, message = null, data = data) + } + + fun created(data: T): ApiResponse { + return ApiResponse(status = 201, message = null, data = data) + } + + fun noContent(data: T): ApiResponse { + return ApiResponse(status = 204, message = null, data = data) } } } diff --git a/src/main/kotlin/kr/galaxyhub/sc/api/common/ExceptionHandler.kt b/src/main/kotlin/kr/galaxyhub/sc/api/common/ExceptionHandler.kt index d08c55f..bebf372 100644 --- a/src/main/kotlin/kr/galaxyhub/sc/api/common/ExceptionHandler.kt +++ b/src/main/kotlin/kr/galaxyhub/sc/api/common/ExceptionHandler.kt @@ -25,8 +25,9 @@ class ExceptionHandler : ResponseEntityExceptionHandler() { status: HttpStatusCode, request: WebRequest, ): ResponseEntity { + // 누락된 쿼리 파라미터 값 때문에 기본 생성자 사용 return ResponseEntity( - ApiResponse("쿼리 파라미터에 누락된 값이 있습니다.", ex.missingParameters()), + ApiResponse(400, "쿼리 파라미터에 누락된 값이 있습니다.", ex.missingParameters()), HttpStatus.BAD_REQUEST ) } @@ -42,7 +43,7 @@ class ExceptionHandler : ResponseEntityExceptionHandler() { request: WebRequest, ): ResponseEntity { return ResponseEntity( - ApiResponse("${ex.propertyName}에 잘못된 값이 입력 되었습니다.", "${ex.propertyName},${ex.value}"), + ApiResponse(400, "${ex.propertyName}에 잘못된 값이 입력 되었습니다.", "${ex.propertyName},${ex.value}"), HttpStatus.BAD_REQUEST ) } @@ -53,7 +54,7 @@ class ExceptionHandler : ResponseEntityExceptionHandler() { request: HttpServletRequest, ): ResponseEntity> { log.debug(e) { "[🟢DEBUG] - (${request.method} ${request.requestURI})" } - return ResponseEntity(ApiResponse.error(e.message!!), e.httpStatus) + return ResponseEntity(ApiResponse.error(e.httpStatus.value(), e.message!!), e.httpStatus) } @ExceptionHandler(Exception::class) @@ -64,7 +65,7 @@ class ExceptionHandler : ResponseEntityExceptionHandler() { companion object { - private val DEFAULT_ERROR = ApiResponse.error("서버 내부에 알 수 없는 문제가 발생했습니다.") + private val DEFAULT_ERROR = ApiResponse.error(500, "서버 내부에 알 수 없는 문제가 발생했습니다.") } } diff --git a/src/main/kotlin/kr/galaxyhub/sc/api/v1/auth/oauth2/OAuth2ControllerV1.kt b/src/main/kotlin/kr/galaxyhub/sc/api/v1/auth/oauth2/OAuth2ControllerV1.kt index 5182149..b1f0ee7 100644 --- a/src/main/kotlin/kr/galaxyhub/sc/api/v1/auth/oauth2/OAuth2ControllerV1.kt +++ b/src/main/kotlin/kr/galaxyhub/sc/api/v1/auth/oauth2/OAuth2ControllerV1.kt @@ -23,6 +23,6 @@ class OAuth2ControllerV1( ): ResponseEntity> { val response = oAuth2FacadeService.login(code, socialType) return ResponseEntity.ok() - .body(ApiResponse.success(response)) + .body(ApiResponse.ok(response)) } } diff --git a/src/main/kotlin/kr/galaxyhub/sc/api/v1/crawler/CrawlerControllerV1.kt b/src/main/kotlin/kr/galaxyhub/sc/api/v1/crawler/CrawlerControllerV1.kt index 42a2eda..733a905 100644 --- a/src/main/kotlin/kr/galaxyhub/sc/api/v1/crawler/CrawlerControllerV1.kt +++ b/src/main/kotlin/kr/galaxyhub/sc/api/v1/crawler/CrawlerControllerV1.kt @@ -26,6 +26,6 @@ class CrawlerControllerV1( ): ResponseEntity> { val newsId = crawlerCommandService.crawling(request.url) return ResponseEntity.created("/api/v1/news/${newsId}".toUri()) - .body(ApiResponse.success(newsId)) + .body(ApiResponse.created(newsId)) } } diff --git a/src/main/kotlin/kr/galaxyhub/sc/api/v1/news/NewsControllerV1.kt b/src/main/kotlin/kr/galaxyhub/sc/api/v1/news/NewsControllerV1.kt index b635426..5d95a87 100644 --- a/src/main/kotlin/kr/galaxyhub/sc/api/v1/news/NewsControllerV1.kt +++ b/src/main/kotlin/kr/galaxyhub/sc/api/v1/news/NewsControllerV1.kt @@ -30,7 +30,7 @@ class NewsControllerV1( fun findAll(): ResponseEntity>> { val response = newsQueryService.findAll() return ResponseEntity.ok() - .body(ApiResponse.success(response)) + .body(ApiResponse.ok(response)) } @GetMapping("/{newsId}") @@ -40,7 +40,7 @@ class NewsControllerV1( ): ResponseEntity> { val response = newsQueryService.getDetailByIdAndLanguage(newsId, language) return ResponseEntity.ok() - .body(ApiResponse.success(response)) + .body(ApiResponse.ok(response)) } @PostMapping @@ -49,7 +49,7 @@ class NewsControllerV1( ): ResponseEntity> { val newsId = newsCommandService.create(request.toCommand()) return ResponseEntity.created("/api/v1/news/${newsId}".toUri()) - .body(ApiResponse.success(newsId)) + .body(ApiResponse.created(newsId)) } @PostMapping("/{newsId}/content") @@ -59,6 +59,6 @@ class NewsControllerV1( ): ResponseEntity> { newsCommandService.updateContent(newsId, request.toCommand()) return ResponseEntity.ok() - .body(ApiResponse.success(Unit)) + .body(ApiResponse.ok(Unit)) } } diff --git a/src/main/kotlin/kr/galaxyhub/sc/api/v1/translation/TranslationControllerV1.kt b/src/main/kotlin/kr/galaxyhub/sc/api/v1/translation/TranslationControllerV1.kt index 212e099..a0c9625 100644 --- a/src/main/kotlin/kr/galaxyhub/sc/api/v1/translation/TranslationControllerV1.kt +++ b/src/main/kotlin/kr/galaxyhub/sc/api/v1/translation/TranslationControllerV1.kt @@ -30,7 +30,7 @@ class TranslationControllerV1( val command = request.toCommand(newsId) val translateProgressionId = translationCommandService.translate(command) return ResponseEntity.created("/api/v1/translation/${translateProgressionId}".toUri()) - .body(ApiResponse.success(translateProgressionId)) + .body(ApiResponse.created(translateProgressionId)) } @GetMapping("/{translateProgressionId}") @@ -39,6 +39,6 @@ class TranslationControllerV1( ): ResponseEntity> { val response = translationQueryService.findById(translateProgressionId) return ResponseEntity.ok() - .body(ApiResponse.success(response)) + .body(ApiResponse.ok(response)) } } diff --git a/src/test/kotlin/kr/galaxyhub/sc/api/support/RestDocDsl.kt b/src/test/kotlin/kr/galaxyhub/sc/api/support/RestDocDsl.kt index 218974f..2289d84 100644 --- a/src/test/kotlin/kr/galaxyhub/sc/api/support/RestDocDsl.kt +++ b/src/test/kotlin/kr/galaxyhub/sc/api/support/RestDocDsl.kt @@ -34,6 +34,12 @@ class RestDocDsl { responseFieldsSnippet = PayloadDocumentation.responseFields(fields.map { it.descriptor }) } + fun responseBodyWithStatus(vararg fields: DocField) { + val docField = "status" type NUMBER means "HTTP 상태 코드" + responseFieldsSnippet = + PayloadDocumentation.responseFields(docField.descriptor).and(fields.map { it.descriptor }) + } + fun queryParameters(vararg params: DocParam) { queryParametersSnippet = RequestDocumentation.queryParameters(params.map { it.descriptor }) } diff --git a/src/test/kotlin/kr/galaxyhub/sc/api/v1/auth/oauth2/OAuth2ControllerV1Test.kt b/src/test/kotlin/kr/galaxyhub/sc/api/v1/auth/oauth2/OAuth2ControllerV1Test.kt index 48e7d89..f238bfd 100644 --- a/src/test/kotlin/kr/galaxyhub/sc/api/v1/auth/oauth2/OAuth2ControllerV1Test.kt +++ b/src/test/kotlin/kr/galaxyhub/sc/api/v1/auth/oauth2/OAuth2ControllerV1Test.kt @@ -37,7 +37,7 @@ class OAuth2ControllerV1Test( "code" pathMeans "Authorization 코드", "socialType" pathMeans "OAuth2 소셜 타입" formattedAs ENUM(SocialType.entries.filter { it != SocialType.LOCAL }), ) - responseBody( + responseBodyWithStatus( "data.accessToken" type STRING means "Bearer JWT Access Token", "data.nickname" type STRING means "사용자의 닉네임", "data.imageUrl" type STRING means "사용자의 프로필 사진 링크" isOptional true, diff --git a/src/test/kotlin/kr/galaxyhub/sc/api/v1/crawler/CrawlerControllerV1Test.kt b/src/test/kotlin/kr/galaxyhub/sc/api/v1/crawler/CrawlerControllerV1Test.kt index 6ea0587..4e199e9 100644 --- a/src/test/kotlin/kr/galaxyhub/sc/api/v1/crawler/CrawlerControllerV1Test.kt +++ b/src/test/kotlin/kr/galaxyhub/sc/api/v1/crawler/CrawlerControllerV1Test.kt @@ -35,7 +35,7 @@ class CrawlerControllerV1Test( requestBody( "url" type STRING means "크롤링할 뉴스의 URL", ) - responseBody( + responseBodyWithStatus( "data" type STRING means "크롤링되서 생성된 뉴스의 식별자" ) } diff --git a/src/test/kotlin/kr/galaxyhub/sc/api/v1/news/NewsControllerV1Test.kt b/src/test/kotlin/kr/galaxyhub/sc/api/v1/news/NewsControllerV1Test.kt index e8fc283..7e25de9 100644 --- a/src/test/kotlin/kr/galaxyhub/sc/api/v1/news/NewsControllerV1Test.kt +++ b/src/test/kotlin/kr/galaxyhub/sc/api/v1/news/NewsControllerV1Test.kt @@ -57,7 +57,7 @@ class NewsControllerV1Test( queryParameters( "language" pathMeans "뉴스 언어" formattedAs ENUM(Language::class) ) - responseBody( + responseBodyWithStatus( "data" type OBJECT means "뉴스 상세 정보", "data.id" type STRING means "뉴스 식별자", "data.newsType" type ENUM(NewsType::class) means "뉴스 타입", @@ -86,7 +86,7 @@ class NewsControllerV1Test( }.andExpect { status { isOk() } }.andDocument("news/find-all") { - responseBody( + responseBodyWithStatus( "data" type ARRAY means "뉴스 목록", "data[0].id" type STRING means "뉴스 식별자", "data[0].newsType" type ENUM(NewsType::class) means "뉴스 타입", @@ -124,7 +124,7 @@ class NewsControllerV1Test( "language" type ENUM(Language::class) means "뉴스 언어", "content" type STRING means "뉴스 내용" ) - responseBody( + responseBodyWithStatus( "data" type STRING means "생성한 뉴스의 식별자" ) } diff --git a/src/test/kotlin/kr/galaxyhub/sc/api/v1/translation/TranslationControllerV1Test.kt b/src/test/kotlin/kr/galaxyhub/sc/api/v1/translation/TranslationControllerV1Test.kt index 8abbf7e..3d0d146 100644 --- a/src/test/kotlin/kr/galaxyhub/sc/api/v1/translation/TranslationControllerV1Test.kt +++ b/src/test/kotlin/kr/galaxyhub/sc/api/v1/translation/TranslationControllerV1Test.kt @@ -21,7 +21,7 @@ import kr.galaxyhub.sc.translation.domain.TranslatorProvider import org.springframework.http.MediaType import org.springframework.test.web.servlet.MockMvc -class TranslationControllerV1Test( +class TranslationControllerV1Test( private val mockMvc: MockMvc, private val objectMapper: ObjectMapper, private val translationCommandService: TranslationCommandService, @@ -50,7 +50,7 @@ class TranslationControllerV1Test( "translatorProvider" type ENUM(TranslatorProvider.entries .filter { it != TranslatorProvider.LOCAL }) means "번역 서비스 제공자" ) - responseBody( + responseBodyWithStatus( "data" type STRING means "번역 진행 상황의 식별자" ) } @@ -73,7 +73,7 @@ class TranslationControllerV1Test( pathParameters( "translateProgressionId" pathMeans "번역 진행 상황의 식별자" ) - responseBody( + responseBodyWithStatus( "data.translateProgressionId" type STRING means "번역 진행 상황의 식별자", "data.targetNewsId" type STRING means "번역할 뉴스의 식별자", "data.translationStatus" type ENUM(TranslationStatus::class) means "번역 상태",