From 272cce59e4b35960be334c9155df4064fb51aee3 Mon Sep 17 00:00:00 2001 From: juuuunny <102502542+juuuunny@users.noreply.github.com> Date: Tue, 15 Oct 2024 15:54:17 +0900 Subject: [PATCH 01/23] =?UTF-8?q?#11=20[chore]=20build.gradle=EC=97=90=20r?= =?UTF-8?q?est-docs=20=ED=94=8C=EB=9F=AC=EA=B7=B8=EC=9D=B8=20=EB=B0=8F=20?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=EC=9D=84=20=EC=84=A4=EC=A0=95?= =?UTF-8?q?=ED=95=9C=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/build.gradle b/build.gradle index b8e31c3..bafe643 100644 --- a/build.gradle +++ b/build.gradle @@ -2,6 +2,7 @@ plugins { id 'java' id 'org.springframework.boot' version '3.3.4' id 'io.spring.dependency-management' version '1.1.6' + id 'com.epages.restdocs-api-spec' version '0.18.4' } group = 'kusitms' @@ -52,8 +53,22 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-redis' // Twilio SDK implementation 'com.twilio.sdk:twilio:8.24.0' + + //rest-docs + testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' + testImplementation 'com.epages:restdocs-api-spec-mockmvc:0.18.4' } tasks.named('test') { useJUnitPlatform() +} + +openapi3 { + servers = [ + { url = 'http://localhost:8080' } + ] + title = 'HitZone' + description = '히트존의 API 명세서입니다.' + version = '1.0.0' + format = 'json' } \ No newline at end of file From 064701ad5dc8babb98b26ee7b43d3bfcfe1af8b5 Mon Sep 17 00:00:00 2001 From: juuuunny <102502542+juuuunny@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:18:26 +0900 Subject: [PATCH 02/23] =?UTF-8?q?#11=20[feat]=20rest-docs=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EC=9A=A9=20api=EB=A5=BC=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=ED=95=9C=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kusitms/backend/auth/jwt/JWTFilter.java | 2 +- .../backend/global/config/SecurityConfig.java | 2 +- .../test/dto/request/TestDocsRequestDto.java | 4 ++++ .../test/dto/response/TestDocsResponseDto.java | 7 +++++++ .../test/presentation/TestController.java | 18 +++++++++++++++--- 5 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 src/main/java/kusitms/backend/test/dto/request/TestDocsRequestDto.java create mode 100644 src/main/java/kusitms/backend/test/dto/response/TestDocsResponseDto.java diff --git a/src/main/java/kusitms/backend/auth/jwt/JWTFilter.java b/src/main/java/kusitms/backend/auth/jwt/JWTFilter.java index 2a737f5..e9dd979 100644 --- a/src/main/java/kusitms/backend/auth/jwt/JWTFilter.java +++ b/src/main/java/kusitms/backend/auth/jwt/JWTFilter.java @@ -46,7 +46,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse protected boolean shouldNotFilter(HttpServletRequest request) { String path = request.getServletPath(); return path.equals("/") || path.equals("/login") || path.equals("/login/kakao") || path.equals("/login/naver") || path.equals("/login/google") - || path.startsWith("/public") || path.equals("/api/v1/test-error") || path.equals("/api/v1/health-check") + || path.startsWith("/public") || path.equals("/api/v1/health-check") || path.equals("/api/v1/test-error") || path.equals("/api/v1/test/docs") || path.equals("/api/v1/signup") || path.equals("/api/v1/send-code") || path.equals("/api/v1/verify-code") || path.equals("/api/v1/token/re-issue"); } diff --git a/src/main/java/kusitms/backend/global/config/SecurityConfig.java b/src/main/java/kusitms/backend/global/config/SecurityConfig.java index 7e8e6f5..6b0d02f 100644 --- a/src/main/java/kusitms/backend/global/config/SecurityConfig.java +++ b/src/main/java/kusitms/backend/global/config/SecurityConfig.java @@ -44,7 +44,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .authorizeHttpRequests((auth) -> auth .requestMatchers( "/", "/login", "/login/google", "/login/naver", "/login/kakao", - "/api/v1/health-check", "/api/v1/test-error", + "/api/v1/health-check", "/api/v1/test-error", "/api/v1/test/docs", "/api/v1/signup", "/api/v1/send-code","/api/v1/verify-code", "/api/v1/token/re-issue").permitAll() // 인증이 필요 없는 경로 설정 .anyRequest().authenticated()) diff --git a/src/main/java/kusitms/backend/test/dto/request/TestDocsRequestDto.java b/src/main/java/kusitms/backend/test/dto/request/TestDocsRequestDto.java new file mode 100644 index 0000000..ee1d921 --- /dev/null +++ b/src/main/java/kusitms/backend/test/dto/request/TestDocsRequestDto.java @@ -0,0 +1,4 @@ +package kusitms.backend.test.dto.request; + +public record TestDocsRequestDto(String tip) { +} diff --git a/src/main/java/kusitms/backend/test/dto/response/TestDocsResponseDto.java b/src/main/java/kusitms/backend/test/dto/response/TestDocsResponseDto.java new file mode 100644 index 0000000..687c5bd --- /dev/null +++ b/src/main/java/kusitms/backend/test/dto/response/TestDocsResponseDto.java @@ -0,0 +1,7 @@ +package kusitms.backend.test.dto.response; + +import lombok.Builder; + +@Builder +public record TestDocsResponseDto(String keyword, String tip) { +} diff --git a/src/main/java/kusitms/backend/test/presentation/TestController.java b/src/main/java/kusitms/backend/test/presentation/TestController.java index b87e82f..5d774c4 100644 --- a/src/main/java/kusitms/backend/test/presentation/TestController.java +++ b/src/main/java/kusitms/backend/test/presentation/TestController.java @@ -2,13 +2,14 @@ import kusitms.backend.global.dto.ApiResponse; import kusitms.backend.global.exception.CustomException; +import kusitms.backend.global.status.SuccessStatus; +import kusitms.backend.test.dto.request.TestDocsRequestDto; +import kusitms.backend.test.dto.response.TestDocsResponseDto; import kusitms.backend.test.status.TestErrorStatus; import kusitms.backend.test.status.TestSuccessStatus; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @RestController @RequiredArgsConstructor @@ -27,4 +28,15 @@ public void getError() { throw new CustomException(TestErrorStatus._TEST_ERROR); } + // rest-docs 테스트용 API + @PostMapping("/test/docs") + public ResponseEntity> testDocs(@RequestParam String name, + @RequestParam String keyword, + @RequestBody TestDocsRequestDto request) { + TestDocsResponseDto response = TestDocsResponseDto.builder().keyword(keyword).tip(request.tip()).build(); + + return ApiResponse.onSuccess(SuccessStatus._CREATED, response); + + } + } From 18d0ece7633a03298a376d8823fad31836276184 Mon Sep 17 00:00:00 2001 From: juuuunny <102502542+juuuunny@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:40:35 +0900 Subject: [PATCH 03/23] =?UTF-8?q?#11=20[feat]=20rest-docs=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EC=9A=A9=20api=EC=9D=98=20=EA=B5=AC=EC=A1=B0?= =?UTF-8?q?=EB=A5=BC=20=EC=88=98=EC=A0=95=ED=95=9C=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/test/application/TestService.java | 16 ++++++++++++++++ .../test/presentation/TestController.java | 7 ++++--- 2 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 src/main/java/kusitms/backend/test/application/TestService.java diff --git a/src/main/java/kusitms/backend/test/application/TestService.java b/src/main/java/kusitms/backend/test/application/TestService.java new file mode 100644 index 0000000..2b6bc1f --- /dev/null +++ b/src/main/java/kusitms/backend/test/application/TestService.java @@ -0,0 +1,16 @@ +package kusitms.backend.test.application; + +import kusitms.backend.test.dto.request.TestDocsRequestDto; +import kusitms.backend.test.dto.response.TestDocsResponseDto; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class TestService { + + public TestDocsResponseDto testDocs(String name, String keyword, TestDocsRequestDto request){ + TestDocsResponseDto response = TestDocsResponseDto.builder().keyword(keyword).tip(request.tip()).build(); + return response; + } +} diff --git a/src/main/java/kusitms/backend/test/presentation/TestController.java b/src/main/java/kusitms/backend/test/presentation/TestController.java index 5d774c4..c8c7dd5 100644 --- a/src/main/java/kusitms/backend/test/presentation/TestController.java +++ b/src/main/java/kusitms/backend/test/presentation/TestController.java @@ -3,6 +3,7 @@ import kusitms.backend.global.dto.ApiResponse; import kusitms.backend.global.exception.CustomException; import kusitms.backend.global.status.SuccessStatus; +import kusitms.backend.test.application.TestService; import kusitms.backend.test.dto.request.TestDocsRequestDto; import kusitms.backend.test.dto.response.TestDocsResponseDto; import kusitms.backend.test.status.TestErrorStatus; @@ -16,6 +17,8 @@ @RequestMapping("/api/v1") public class TestController { + private final TestService testService; + // 헬스체크용 API @GetMapping("/health-check") public ResponseEntity> healthCheck() { @@ -33,9 +36,7 @@ public void getError() { public ResponseEntity> testDocs(@RequestParam String name, @RequestParam String keyword, @RequestBody TestDocsRequestDto request) { - TestDocsResponseDto response = TestDocsResponseDto.builder().keyword(keyword).tip(request.tip()).build(); - - return ApiResponse.onSuccess(SuccessStatus._CREATED, response); + return ApiResponse.onSuccess(SuccessStatus._CREATED, testService.testDocs(name, keyword, request)); } From d8bdc715c7f00c9a750c0525f64a217486ffd2b4 Mon Sep 17 00:00:00 2001 From: juuuunny <102502542+juuuunny@users.noreply.github.com> Date: Tue, 15 Oct 2024 17:20:25 +0900 Subject: [PATCH 04/23] =?UTF-8?q?#11=20[feat]=20rest-docs=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=EC=9D=84=20=EC=9C=84=ED=95=9C=20build.gradle=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 67 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 18 deletions(-) diff --git a/build.gradle b/build.gradle index bafe643..5d3d847 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,9 @@ plugins { id 'java' id 'org.springframework.boot' version '3.3.4' id 'io.spring.dependency-management' version '1.1.6' - id 'com.epages.restdocs-api-spec' version '0.18.4' + id "org.asciidoctor.jvm.convert" version "3.3.2" // Asciidoctor 플러그인 + id 'org.hidetake.swagger.generator' version '2.18.2' + id 'com.epages.restdocs-api-spec' version '0.17.1' } group = 'kusitms' @@ -14,16 +16,14 @@ java { } } -configurations { - compileOnly { - extendsFrom annotationProcessor - } -} - repositories { mavenCentral() } +ext { + snippetsDir = file('build/generated-snippets') // 스니펫이 생성되는 디렉터리 경로를 설정 +} + //twilio이 jjwt-jackson 0.11.2를 의존해서 0.12.3으로 강제 의존성 주입 configurations.configureEach { resolutionStrategy { @@ -54,21 +54,52 @@ dependencies { // Twilio SDK implementation 'com.twilio.sdk:twilio:8.24.0' - //rest-docs + // REST Docs & API Spec testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' - testImplementation 'com.epages:restdocs-api-spec-mockmvc:0.18.4' + testImplementation 'com.epages:restdocs-api-spec-mockmvc:0.17.1' + implementation 'org.springframework.restdocs:spring-restdocs-asciidoctor:2.0.6.RELEASE' // Asciidoctor 의존성 + + // MockWebServer + testImplementation 'com.squareup.okhttp3:mockwebserver' +} + +openapi3 { + server = "http://localhost:8080" + title = "히틎본 API 문서" + description = "Spring REST Docs with SwaggerUI." + version = "0.0.1" + outputFileNamePrefix = 'open-api-3.0.1' + format = 'json' + + // /static/docs/open-api-3.0.1.json 생성 → jar 파일만 배포할 예정이기에 build 에 출력 + outputDirectory = 'build/resources/main/static/docs' } +// 테스트 스니펫 출력 위치 정의 tasks.named('test') { useJUnitPlatform() + outputs.dir snippetsDir // 스니펫 디렉터리를 지정 } -openapi3 { - servers = [ - { url = 'http://localhost:8080' } - ] - title = 'HitZone' - description = '히트존의 API 명세서입니다.' - version = '1.0.0' - format = 'json' -} \ No newline at end of file +tasks.named('asciidoctor') { + inputs.dir snippetsDir + dependsOn test +} + +tasks.named("bootJar") { + dependsOn asciidoctor + from("${asciidoctor.outputDir}") { + into 'static/docs' + } + dependsOn openapi3 +} + +tasks.register('copyDocument', Copy) { + dependsOn asciidoctor + from file("$buildDir/docs/asciidoc") + into file("src/main/resources/static/docs") +} + +tasks.named("build") { + dependsOn copyDocument +} From 41ebd4b28a535acf402d33e5524d8f61b7af86bf Mon Sep 17 00:00:00 2001 From: juuuunny <102502542+juuuunny@users.noreply.github.com> Date: Tue, 15 Oct 2024 17:22:26 +0900 Subject: [PATCH 05/23] =?UTF-8?q?#11=20[feat]=20rest-docs=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EA=B3=B5=ED=86=B5=20=EC=BB=A8=ED=8A=B8=EB=A1=A4?= =?UTF-8?q?=EB=9F=AC=20=ED=85=8C=EC=8A=A4=ED=8A=B8=EC=97=90=20=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20ControllerTestConfig=EB=A5=BC=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=ED=95=9C=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../configuration/ControllerTestConfig.java | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/test/java/kusitms/backend/configuration/ControllerTestConfig.java diff --git a/src/test/java/kusitms/backend/configuration/ControllerTestConfig.java b/src/test/java/kusitms/backend/configuration/ControllerTestConfig.java new file mode 100644 index 0000000..3ac2a66 --- /dev/null +++ b/src/test/java/kusitms/backend/configuration/ControllerTestConfig.java @@ -0,0 +1,41 @@ +package kusitms.backend.configuration; + +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.filter.CharacterEncodingFilter; + +@AutoConfigureMockMvc +@AutoConfigureRestDocs +@ExtendWith({RestDocumentationExtension.class}) +public abstract class ControllerTestConfig { + + @Autowired + protected WebApplicationContext ctx; + + @Autowired + protected ObjectMapper objectMapper; + + protected MockMvc mockMvc; + + @BeforeEach + void setUp(final RestDocumentationContextProvider restDocumentation) { + mockMvc = MockMvcBuilders.webAppContextSetup(ctx) + .apply(documentationConfiguration(restDocumentation)) + .addFilters(new CharacterEncodingFilter("UTF-8", true)) + .alwaysDo(print()) + .build(); + } + +} \ No newline at end of file From 796daf874128a49dc0ac4331a00fad77024a3f40 Mon Sep 17 00:00:00 2001 From: juuuunny <102502542+juuuunny@users.noreply.github.com> Date: Tue, 15 Oct 2024 19:18:36 +0900 Subject: [PATCH 06/23] =?UTF-8?q?#11=20[feat]=20rest-docs=20api=EC=97=90?= =?UTF-8?q?=20=EB=8C=80=ED=95=9C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=EC=BD=94?= =?UTF-8?q?=EB=93=9C=EB=A5=BC=20=EC=9E=91=EC=84=B1=ED=95=9C=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 17 ++-- .../backend/test/application/TestService.java | 3 +- .../test/dto/request/TestDocsRequestDto.java | 3 + .../test/presentation/TestController.java | 2 +- .../backend/BackendApplicationTests.java | 26 +++--- .../backend/test/TestControllerTest.java | 90 +++++++++++++++++++ 6 files changed, 119 insertions(+), 22 deletions(-) create mode 100644 src/test/java/kusitms/backend/test/TestControllerTest.java diff --git a/build.gradle b/build.gradle index 5d3d847..5d72d36 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ plugins { id 'java' id 'org.springframework.boot' version '3.3.4' id 'io.spring.dependency-management' version '1.1.6' - id "org.asciidoctor.jvm.convert" version "3.3.2" // Asciidoctor 플러그인 + id "org.asciidoctor.jvm.convert" version "3.3.2" id 'org.hidetake.swagger.generator' version '2.18.2' id 'com.epages.restdocs-api-spec' version '0.17.1' } @@ -21,7 +21,7 @@ repositories { } ext { - snippetsDir = file('build/generated-snippets') // 스니펫이 생성되는 디렉터리 경로를 설정 + snippetsDir = layout.buildDirectory.dir("generated-snippets") // 스니펫이 생성되는 디렉터리 경로 설정 } //twilio이 jjwt-jackson 0.11.2를 의존해서 0.12.3으로 강제 의존성 주입 @@ -65,14 +65,19 @@ dependencies { openapi3 { server = "http://localhost:8080" - title = "히틎본 API 문서" + title = "히트존 API 문서" description = "Spring REST Docs with SwaggerUI." version = "0.0.1" outputFileNamePrefix = 'open-api-3.0.1' format = 'json' // /static/docs/open-api-3.0.1.json 생성 → jar 파일만 배포할 예정이기에 build 에 출력 - outputDirectory = 'build/resources/main/static/docs' + outputDirectory = layout.buildDirectory.dir("resources/main/static/docs") +} + +// Swagger를 생성하는 태스크를 정의 +tasks.register('generateOpenApi', GenerateSwaggerUI) { + dependsOn test // Swagger 문서를 생성하기 전에 테스트를 실행 } // 테스트 스니펫 출력 위치 정의 @@ -88,15 +93,15 @@ tasks.named('asciidoctor') { tasks.named("bootJar") { dependsOn asciidoctor + dependsOn generateOpenApi // Swagger 문서 생성을 bootJar에 포함 from("${asciidoctor.outputDir}") { into 'static/docs' } - dependsOn openapi3 } tasks.register('copyDocument', Copy) { dependsOn asciidoctor - from file("$buildDir/docs/asciidoc") + from layout.buildDirectory.dir("docs/asciidoc") into file("src/main/resources/static/docs") } diff --git a/src/main/java/kusitms/backend/test/application/TestService.java b/src/main/java/kusitms/backend/test/application/TestService.java index 2b6bc1f..e341d63 100644 --- a/src/main/java/kusitms/backend/test/application/TestService.java +++ b/src/main/java/kusitms/backend/test/application/TestService.java @@ -10,7 +10,6 @@ public class TestService { public TestDocsResponseDto testDocs(String name, String keyword, TestDocsRequestDto request){ - TestDocsResponseDto response = TestDocsResponseDto.builder().keyword(keyword).tip(request.tip()).build(); - return response; + return TestDocsResponseDto.builder().keyword(keyword).tip(request.tip()).build(); } } diff --git a/src/main/java/kusitms/backend/test/dto/request/TestDocsRequestDto.java b/src/main/java/kusitms/backend/test/dto/request/TestDocsRequestDto.java index ee1d921..f22bc1c 100644 --- a/src/main/java/kusitms/backend/test/dto/request/TestDocsRequestDto.java +++ b/src/main/java/kusitms/backend/test/dto/request/TestDocsRequestDto.java @@ -1,4 +1,7 @@ package kusitms.backend.test.dto.request; +import lombok.Builder; + +@Builder public record TestDocsRequestDto(String tip) { } diff --git a/src/main/java/kusitms/backend/test/presentation/TestController.java b/src/main/java/kusitms/backend/test/presentation/TestController.java index c8c7dd5..a011a56 100644 --- a/src/main/java/kusitms/backend/test/presentation/TestController.java +++ b/src/main/java/kusitms/backend/test/presentation/TestController.java @@ -36,7 +36,7 @@ public void getError() { public ResponseEntity> testDocs(@RequestParam String name, @RequestParam String keyword, @RequestBody TestDocsRequestDto request) { - return ApiResponse.onSuccess(SuccessStatus._CREATED, testService.testDocs(name, keyword, request)); + return ApiResponse.onSuccess(SuccessStatus._OK, testService.testDocs(name, keyword, request)); } diff --git a/src/test/java/kusitms/backend/BackendApplicationTests.java b/src/test/java/kusitms/backend/BackendApplicationTests.java index 4e84e80..3fec21e 100644 --- a/src/test/java/kusitms/backend/BackendApplicationTests.java +++ b/src/test/java/kusitms/backend/BackendApplicationTests.java @@ -1,13 +1,13 @@ -package kusitms.backend; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest(classes = BackendApplicationTests.class) -class BackendApplicationTests { - - @Test - void contextLoads() { - } - -} +//package kusitms.backend; +// +//import org.junit.jupiter.api.Test; +//import org.springframework.boot.test.context.SpringBootTest; +// +//@SpringBootTest(classes = BackendApplicationTests.class) +//class BackendApplicationTests { +// +// @Test +// void contextLoads() { +// } +// +//} diff --git a/src/test/java/kusitms/backend/test/TestControllerTest.java b/src/test/java/kusitms/backend/test/TestControllerTest.java new file mode 100644 index 0000000..cf3c41b --- /dev/null +++ b/src/test/java/kusitms/backend/test/TestControllerTest.java @@ -0,0 +1,90 @@ +package kusitms.backend.test; + +import com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper; +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.epages.restdocs.apispec.Schema; +import kusitms.backend.configuration.ControllerTestConfig; +import kusitms.backend.test.application.TestService; +import kusitms.backend.test.dto.request.TestDocsRequestDto; +import kusitms.backend.test.dto.response.TestDocsResponseDto; +import kusitms.backend.test.presentation.TestController; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; +import org.springframework.test.web.servlet.ResultActions; + +import static com.epages.restdocs.apispec.ResourceDocumentation.resource; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + + +@WebMvcTest(TestController.class) +public class TestControllerTest extends ControllerTestConfig { + + @MockBean + private TestService testService; + + @Test + @DisplayName("Rest-Docs 테스트 조회") + public void testDocs() throws Exception { + // given + String name = "박준형이"; + String keyword = "테스트키워드"; + String testDocsJsonRequest = """ + { + "tip" : "테스트팁" + } + """; + TestDocsResponseDto testDocsResponseDto = TestDocsResponseDto.builder() + .keyword("테스트키워드") + .tip("테스트팁") + .build(); + Mockito.when(testService.testDocs(anyString(), anyString(), any(TestDocsRequestDto.class))).thenReturn(testDocsResponseDto); + + // when + ResultActions resultActions = this.mockMvc.perform(RestDocumentationRequestBuilders.post("/api/v1/test/docs") + .queryParam("name", name) + .queryParam("keyword", keyword) + .content(testDocsJsonRequest) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)); + + // then + resultActions.andExpect(status().isOk()) + .andDo(MockMvcRestDocumentationWrapper.document("test/docs", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + resource( + ResourceSnippetParameters.builder() + .tag("TEST") + .description("Rest Docs post 테스트용 API") + .queryParameters( + parameterWithName("name").description("테스트 네임"), + parameterWithName("keyword").description("테스트 키워드") + ) + .requestFields( + fieldWithPath("tip").description("테스트 팁") + ) + .responseFields( + fieldWithPath("isSuccess").description("성공 여부"), + fieldWithPath("code").description("응답 코드"), + fieldWithPath("message").description("응답 메시지"), + fieldWithPath("payload").description("응답 데이터").optional(), + fieldWithPath("payload.keyword").description("키워드"), + fieldWithPath("payload.tip").description("팁") + ) + .responseSchema(Schema.schema("TestDocsResponseDto")) // 여기에 추가 + .build() + ) + )); + } +} From 3e458211aa4396f4c81cca39250811fa3d9399d8 Mon Sep 17 00:00:00 2001 From: juuuunny <102502542+juuuunny@users.noreply.github.com> Date: Tue, 15 Oct 2024 23:11:07 +0900 Subject: [PATCH 07/23] =?UTF-8?q?#13=20[feat]=20Rest=20Docs=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20api=EC=97=90=20=EB=8C=80=ED=95=9C=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=EC=BD=94=EB=93=9C=20=EC=99=84?= =?UTF-8?q?=EC=84=B1=20=EB=B0=8F=20Rest=20Docs=EC=99=80=20Swagger=EB=A5=BC?= =?UTF-8?q?=20=EC=97=B0=EA=B2=B0=ED=95=98=EC=97=AC=20=EB=B3=BC=20=EC=88=98?= =?UTF-8?q?=20=EC=9E=88=EA=B2=8C=ED=95=9C=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 96 +++++++++++---- .../kusitms/backend/auth/jwt/JWTFilter.java | 8 +- .../backend/global/config/CorsMvcConfig.java | 6 +- .../backend/global/config/SecurityConfig.java | 3 +- src/main/resources/application.yaml | 13 +- .../resources/static/docs/open-api-3.0.1.json | 115 ++++++++++++++++++ .../backend/BackendApplicationTests.java | 26 ++-- .../backend/test/TestControllerTest.java | 3 +- 8 files changed, 228 insertions(+), 42 deletions(-) create mode 100644 src/main/resources/static/docs/open-api-3.0.1.json diff --git a/build.gradle b/build.gradle index 5d72d36..0c4d1d8 100644 --- a/build.gradle +++ b/build.gradle @@ -2,9 +2,11 @@ plugins { id 'java' id 'org.springframework.boot' version '3.3.4' id 'io.spring.dependency-management' version '1.1.6' + + id "org.sonarqube" version "4.0.0.2929" id "org.asciidoctor.jvm.convert" version "3.3.2" + id 'com.epages.restdocs-api-spec' version '0.17.1'//restdocs -> swagger id 'org.hidetake.swagger.generator' version '2.18.2' - id 'com.epages.restdocs-api-spec' version '0.17.1' } group = 'kusitms' @@ -16,12 +18,15 @@ java { } } -repositories { - mavenCentral() +configurations { + compileOnly { + extendsFrom annotationProcessor + } + asciidoctorExt } -ext { - snippetsDir = layout.buildDirectory.dir("generated-snippets") // 스니펫이 생성되는 디렉터리 경로 설정 +repositories { + mavenCentral() } //twilio이 jjwt-jackson 0.11.2를 의존해서 0.12.3으로 강제 의존성 주입 @@ -54,57 +59,102 @@ dependencies { // Twilio SDK implementation 'com.twilio.sdk:twilio:8.24.0' - // REST Docs & API Spec + //restdocs-api-spec 의존성 추가 + testImplementation 'com.epages:restdocs-api-spec-mockmvc:0.19.2' + asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor' testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' - testImplementation 'com.epages:restdocs-api-spec-mockmvc:0.17.1' - implementation 'org.springframework.restdocs:spring-restdocs-asciidoctor:2.0.6.RELEASE' // Asciidoctor 의존성 - - // MockWebServer testImplementation 'com.squareup.okhttp3:mockwebserver' + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0' +} + +ext { + snippetsDir = file('build/generated-snippets') // 스니펫이 생성되는 디렉터리 경로를 설정 } +sourceSets { + test { + java { + srcDirs = ['src/test/java'] + } + } +} + +def serverUrl = "http://localhost:8080" + +// openapi3 설정 openapi3 { - server = "http://localhost:8080" - title = "히트존 API 문서" + server = serverUrl + title = "위대한레츠비 API 문서" description = "Spring REST Docs with SwaggerUI." version = "0.0.1" outputFileNamePrefix = 'open-api-3.0.1' format = 'json' + outputDirectory = 'build/resources/main/static/docs' +} + +// GenerateSwaggerUI 태스크가 openapi3 태스크에 의존하도록 설정 +tasks.withType(GenerateSwaggerUI).configureEach { + dependsOn 'openapi3' - // /static/docs/open-api-3.0.1.json 생성 → jar 파일만 배포할 예정이기에 build 에 출력 - outputDirectory = layout.buildDirectory.dir("resources/main/static/docs") + // 기존 파일 삭제 후 json 파일 복사 + delete file('src/main/resources/static/docs/') + copy { + from "build/resources/main/static/docs" + into "src/main/resources/static/docs/" + } } -// Swagger를 생성하는 태스크를 정의 -tasks.register('generateOpenApi', GenerateSwaggerUI) { - dependsOn test // Swagger 문서를 생성하기 전에 테스트를 실행 +// 테스트를 통해 생성된 snippets 출력 위치 정의 +tasks.register("ext") { + snippetsDir = file('build/generated-snippets') } -// 테스트 스니펫 출력 위치 정의 tasks.named('test') { useJUnitPlatform() - outputs.dir snippetsDir // 스니펫 디렉터리를 지정 + outputs.dir 'snippetsDir' // test 스니펫 디렉터리를 출력으로 추가하도록 작업을 구성 + + // 파일 생성 로직 추가 + doFirst { + def docsDir = file('build/resources/main/static/docs') + if (!docsDir.exists()) { + docsDir.mkdirs() + } + def apiFile = file('build/resources/main/static/docs/open-api-3.0.1.json') + if (!apiFile.exists()) { + apiFile.createNewFile() + } + } +} + +// 기존에 존재하던 docs 삭제 +asciidoctor.doFirst { + delete file('src/main/resources/static/docs') } -tasks.named('asciidoctor') { - inputs.dir snippetsDir +// asciidoctor 작업을 구성 +tasks.named("asciidoctor") { + inputs.dir 'snippetsDir' + configurations 'asciidoctorExt' dependsOn test } + tasks.named("bootJar") { dependsOn asciidoctor - dependsOn generateOpenApi // Swagger 문서 생성을 bootJar에 포함 from("${asciidoctor.outputDir}") { into 'static/docs' } + dependsOn(':openapi3') } tasks.register('copyDocument', Copy) { dependsOn asciidoctor - from layout.buildDirectory.dir("docs/asciidoc") +// from file("$buildDir/docs/asciidoc") + from file(project.layout.buildDirectory.dir("docs/asciidoc").get().asFile.path) into file("src/main/resources/static/docs") } tasks.named("build") { dependsOn copyDocument } + diff --git a/src/main/java/kusitms/backend/auth/jwt/JWTFilter.java b/src/main/java/kusitms/backend/auth/jwt/JWTFilter.java index e9dd979..1bcde1f 100644 --- a/src/main/java/kusitms/backend/auth/jwt/JWTFilter.java +++ b/src/main/java/kusitms/backend/auth/jwt/JWTFilter.java @@ -46,7 +46,13 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse protected boolean shouldNotFilter(HttpServletRequest request) { String path = request.getServletPath(); return path.equals("/") || path.equals("/login") || path.equals("/login/kakao") || path.equals("/login/naver") || path.equals("/login/google") - || path.startsWith("/public") || path.equals("/api/v1/health-check") || path.equals("/api/v1/test-error") || path.equals("/api/v1/test/docs") + || path.startsWith("/public") || path.equals("/api/v1/health-check") || path.equals("/api/v1/test-error") + + || path.equals("/api/v1/test/docs") || path.equals("/docs/swagger-ui/index.html") || path.equals("/docs/swagger-ui/swagger-ui.css") || path.equals("/docs/swagger-ui/index.css") + || path.equals("/docs/swagger-ui/swagger-ui-bundle.js") || path.equals("/docs/swagger-ui/swagger-ui-standalone-preset.js") || path.equals("/docs/swagger-ui/swagger-initializer.js") + || path.equals("/docs/swagger-ui/favicon-32x32.png") || path.equals("/docs/swagger-ui/favicon-16x16.png") + || path.equals("/v3/api-docs/swagger-config") || path.equals("/docs/open-api-3.0.1.json") + || path.equals("/api/v1/signup") || path.equals("/api/v1/send-code") || path.equals("/api/v1/verify-code") || path.equals("/api/v1/token/re-issue"); } diff --git a/src/main/java/kusitms/backend/global/config/CorsMvcConfig.java b/src/main/java/kusitms/backend/global/config/CorsMvcConfig.java index 0072a52..057cedb 100644 --- a/src/main/java/kusitms/backend/global/config/CorsMvcConfig.java +++ b/src/main/java/kusitms/backend/global/config/CorsMvcConfig.java @@ -2,6 +2,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration @@ -10,6 +11,9 @@ public class CorsMvcConfig implements WebMvcConfigurer { public void addCorsMappings(CorsRegistry corsRegistry) { corsRegistry.addMapping("/**") .exposedHeaders("Set-Cookie", "Authorization") - .allowedOrigins("http://localhost:5173"); + .allowedOrigins("http://localhost:5173", "http://localhost:8080") + .allowedMethods("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS") + .allowCredentials(true); } + } diff --git a/src/main/java/kusitms/backend/global/config/SecurityConfig.java b/src/main/java/kusitms/backend/global/config/SecurityConfig.java index 6b0d02f..a41b6ef 100644 --- a/src/main/java/kusitms/backend/global/config/SecurityConfig.java +++ b/src/main/java/kusitms/backend/global/config/SecurityConfig.java @@ -44,7 +44,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .authorizeHttpRequests((auth) -> auth .requestMatchers( "/", "/login", "/login/google", "/login/naver", "/login/kakao", - "/api/v1/health-check", "/api/v1/test-error", "/api/v1/test/docs", + "/api/v1/health-check", "/api/v1/test-error", + "/api/v1/test/docs","/docs/swagger-ui/**", "/v3/api-docs/**", "/docs/open-api-3.0.1.json", "/api/v1/signup", "/api/v1/send-code","/api/v1/verify-code", "/api/v1/token/re-issue").permitAll() // 인증이 필요 없는 경로 설정 .anyRequest().authenticated()) diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index e3e64e2..e966eab 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,10 +1,18 @@ server: - port: ${SERVER_PORT} + port: 8080 twilio: account_sid: ${TWILIO_ACCOUNT_SID} auth_token: ${TWILIO_AUTH_TOKEN} from_number: ${TWILIO_FROM_NUMBER} +springdoc: + swagger-ui: + url: /docs/open-api-3.0.1.json # OpenAPI JSON 파일 경로 지정 + path: /docs/swagger-ui # Swagger UI의 경로 지정 + api-docs: + path: /v3/api-docs + + spring: jpa: hibernate: @@ -78,4 +86,5 @@ spring: data: redis: host: ${REDIS_HOST} - port: ${REDIS_PORT} \ No newline at end of file + port: ${REDIS_PORT} + diff --git a/src/main/resources/static/docs/open-api-3.0.1.json b/src/main/resources/static/docs/open-api-3.0.1.json new file mode 100644 index 0000000..16607b9 --- /dev/null +++ b/src/main/resources/static/docs/open-api-3.0.1.json @@ -0,0 +1,115 @@ +{ + "openapi" : "3.0.1", + "info" : { + "title" : "위대한레츠비 API 문서", + "description" : "Spring REST Docs with SwaggerUI.", + "version" : "0.0.1" + }, + "servers" : [ { + "url" : "http://localhost:8080" + } ], + "tags" : [ ], + "paths" : { + "/api/v1/test/docs" : { + "post" : { + "tags" : [ "TEST" ], + "summary" : "Rest Docs post 테스트용 API", + "description" : "Rest Docs post 테스트용 API", + "operationId" : "test/docs", + "parameters" : [ { + "name" : "name", + "in" : "query", + "description" : "테스트 네임", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "keyword", + "in" : "query", + "description" : "테스트 키워드", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json;charset=UTF-8" : { + "schema" : { + "$ref" : "#/components/schemas/api-v1-test-docs1498556744" + }, + "examples" : { + "test/docs" : { + "value" : "{\n \"tip\" : \"테스트팁\"\n}" + } + } + } + } + }, + "responses" : { + "200" : { + "description" : "200", + "content" : { + "application/json;charset=UTF-8" : { + "schema" : { + "$ref" : "#/components/schemas/TestDocsResponseDto" + }, + "examples" : { + "test/docs" : { + "value" : "{\n \"isSuccess\" : true,\n \"code\" : \"200\",\n \"message\" : \"성공입니다.\",\n \"payload\" : {\n \"keyword\" : \"테스트키워드\",\n \"tip\" : \"테스트팁\"\n }\n}" + } + } + } + } + } + } + } + } + }, + "components" : { + "schemas" : { + "TestDocsResponseDto" : { + "title" : "TestDocsResponseDto", + "type" : "object", + "properties" : { + "code" : { + "type" : "string", + "description" : "응답 코드" + }, + "payload" : { + "type" : "object", + "properties" : { + "tip" : { + "type" : "string", + "description" : "팁" + }, + "keyword" : { + "type" : "string", + "description" : "키워드" + } + }, + "description" : "응답 데이터" + }, + "message" : { + "type" : "string", + "description" : "응답 메시지" + }, + "isSuccess" : { + "type" : "boolean", + "description" : "성공 여부" + } + } + }, + "api-v1-test-docs1498556744" : { + "type" : "object", + "properties" : { + "tip" : { + "type" : "string", + "description" : "테스트 팁" + } + } + } + } + } +} \ No newline at end of file diff --git a/src/test/java/kusitms/backend/BackendApplicationTests.java b/src/test/java/kusitms/backend/BackendApplicationTests.java index 3fec21e..4e84e80 100644 --- a/src/test/java/kusitms/backend/BackendApplicationTests.java +++ b/src/test/java/kusitms/backend/BackendApplicationTests.java @@ -1,13 +1,13 @@ -//package kusitms.backend; -// -//import org.junit.jupiter.api.Test; -//import org.springframework.boot.test.context.SpringBootTest; -// -//@SpringBootTest(classes = BackendApplicationTests.class) -//class BackendApplicationTests { -// -// @Test -// void contextLoads() { -// } -// -//} +package kusitms.backend; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest(classes = BackendApplicationTests.class) +class BackendApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/src/test/java/kusitms/backend/test/TestControllerTest.java b/src/test/java/kusitms/backend/test/TestControllerTest.java index cf3c41b..db272c4 100644 --- a/src/test/java/kusitms/backend/test/TestControllerTest.java +++ b/src/test/java/kusitms/backend/test/TestControllerTest.java @@ -1,5 +1,7 @@ package kusitms.backend.test; +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; + import com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper; import com.epages.restdocs.apispec.ResourceSnippetParameters; import com.epages.restdocs.apispec.Schema; @@ -11,7 +13,6 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.mockito.Mockito; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.MediaType; From 7c73c63b89fe63b55cd44047ff9e1254e6d56c13 Mon Sep 17 00:00:00 2001 From: juuuunny <102502542+juuuunny@users.noreply.github.com> Date: Tue, 15 Oct 2024 23:14:24 +0900 Subject: [PATCH 08/23] =?UTF-8?q?#13=20[chore]=20OpenApi3=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 0c4d1d8..896ce78 100644 --- a/build.gradle +++ b/build.gradle @@ -84,7 +84,7 @@ def serverUrl = "http://localhost:8080" // openapi3 설정 openapi3 { server = serverUrl - title = "위대한레츠비 API 문서" + title = "히트존 API 문서" description = "Spring REST Docs with SwaggerUI." version = "0.0.1" outputFileNamePrefix = 'open-api-3.0.1' From 67ac6ea6dfd7014ece57546c60fc88e71949b8dc Mon Sep 17 00:00:00 2001 From: juuuunny <102502542+juuuunny@users.noreply.github.com> Date: Wed, 16 Oct 2024 12:48:08 +0900 Subject: [PATCH 09/23] =?UTF-8?q?#13=20[chore]=20openapi-3.0.1=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20gitignore=EC=97=90=20=EC=B6=94=EA=B0=80=ED=95=9C?= =?UTF-8?q?=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 +++- .../java/kusitms/backend/global/config/SecurityConfig.java | 1 - src/main/resources/static/docs/open-api-3.0.1.json | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 2bff8a4..bf6d81f 100644 --- a/.gitignore +++ b/.gitignore @@ -36,4 +36,6 @@ out/ ### VS Code ### .vscode/ -.DS_Store \ No newline at end of file +.DS_Store + +/src/main/resources/static/docs \ No newline at end of file diff --git a/src/main/java/kusitms/backend/global/config/SecurityConfig.java b/src/main/java/kusitms/backend/global/config/SecurityConfig.java index a41b6ef..a8ff473 100644 --- a/src/main/java/kusitms/backend/global/config/SecurityConfig.java +++ b/src/main/java/kusitms/backend/global/config/SecurityConfig.java @@ -69,4 +69,3 @@ public CorsConfigurationSource corsConfigurationSource() { return source; } } - diff --git a/src/main/resources/static/docs/open-api-3.0.1.json b/src/main/resources/static/docs/open-api-3.0.1.json index 16607b9..3565e54 100644 --- a/src/main/resources/static/docs/open-api-3.0.1.json +++ b/src/main/resources/static/docs/open-api-3.0.1.json @@ -1,7 +1,7 @@ { "openapi" : "3.0.1", "info" : { - "title" : "위대한레츠비 API 문서", + "title" : "히트존 API 문서", "description" : "Spring REST Docs with SwaggerUI.", "version" : "0.0.1" }, From 2dcd504bd06000ecc79bbbb1fb0bb8d1294c533d Mon Sep 17 00:00:00 2001 From: juuuunny <102502542+juuuunny@users.noreply.github.com> Date: Thu, 17 Oct 2024 00:36:07 +0900 Subject: [PATCH 10/23] =?UTF-8?q?#13=20[chore]=20=EB=A0=88=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EB=8F=85=EC=8A=A4=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20api?= =?UTF-8?q?=EC=9D=98=20=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=20=EC=98=88=EC=8B=9C=20=EC=B6=94=EA=B0=80=ED=95=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 9 +++++---- .../java/kusitms/backend/test/TestControllerTest.java | 8 ++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/build.gradle b/build.gradle index 896ce78..7458407 100644 --- a/build.gradle +++ b/build.gradle @@ -3,9 +3,9 @@ plugins { id 'org.springframework.boot' version '3.3.4' id 'io.spring.dependency-management' version '1.1.6' - id "org.sonarqube" version "4.0.0.2929" + //rest docs id "org.asciidoctor.jvm.convert" version "3.3.2" - id 'com.epages.restdocs-api-spec' version '0.17.1'//restdocs -> swagger + id 'com.epages.restdocs-api-spec' version '0.17.1' id 'org.hidetake.swagger.generator' version '2.18.2' } @@ -18,6 +18,7 @@ java { } } +//rest-docs configurations { compileOnly { extendsFrom annotationProcessor @@ -67,8 +68,9 @@ dependencies { implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0' } +// 스니펫이 생성되는 디렉터리 경로를 설정 ext { - snippetsDir = file('build/generated-snippets') // 스니펫이 생성되는 디렉터리 경로를 설정 + snippetsDir = file('build/generated-snippets') } sourceSets { @@ -138,7 +140,6 @@ tasks.named("asciidoctor") { dependsOn test } - tasks.named("bootJar") { dependsOn asciidoctor from("${asciidoctor.outputDir}") { diff --git a/src/test/java/kusitms/backend/test/TestControllerTest.java b/src/test/java/kusitms/backend/test/TestControllerTest.java index db272c4..f8678cf 100644 --- a/src/test/java/kusitms/backend/test/TestControllerTest.java +++ b/src/test/java/kusitms/backend/test/TestControllerTest.java @@ -1,7 +1,5 @@ package kusitms.backend.test; -import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; - import com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper; import com.epages.restdocs.apispec.ResourceSnippetParameters; import com.epages.restdocs.apispec.Schema; @@ -20,8 +18,7 @@ import org.springframework.test.web.servlet.ResultActions; import static com.epages.restdocs.apispec.ResourceDocumentation.resource; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.*; import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; @@ -51,6 +48,9 @@ public void testDocs() throws Exception { .build(); Mockito.when(testService.testDocs(anyString(), anyString(), any(TestDocsRequestDto.class))).thenReturn(testDocsResponseDto); + //만약 Response가 없을 경우엔 아래와 같이 실행한다. + //Mockito.doNothing().when(testService).testDocs(anyString(), anyString(), any(TestDocsRequestDto.class)); + // when ResultActions resultActions = this.mockMvc.perform(RestDocumentationRequestBuilders.post("/api/v1/test/docs") .queryParam("name", name) From 08dc1254dfd33134d2a50abb7e780fb442d15176 Mon Sep 17 00:00:00 2001 From: juuuunny <102502542+juuuunny@users.noreply.github.com> Date: Thu, 17 Oct 2024 00:41:04 +0900 Subject: [PATCH 11/23] =?UTF-8?q?#13=20[feat]=20=EC=8A=A4=EC=9B=A8?= =?UTF-8?q?=EA=B1=B0=20=EA=B4=80=EB=A0=A8=20jwt=ED=95=84=ED=84=B0=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC=EB=A5=BC=20.equals=20?= =?UTF-8?q?=EB=8C=80=EC=8B=A0=20.startsWith=EC=9C=BC=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=ED=95=9C=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/kusitms/backend/auth/jwt/JWTFilter.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/main/java/kusitms/backend/auth/jwt/JWTFilter.java b/src/main/java/kusitms/backend/auth/jwt/JWTFilter.java index 1bcde1f..61721bb 100644 --- a/src/main/java/kusitms/backend/auth/jwt/JWTFilter.java +++ b/src/main/java/kusitms/backend/auth/jwt/JWTFilter.java @@ -2,10 +2,8 @@ import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; -import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import kusitms.backend.auth.status.AuthErrorStatus; import kusitms.backend.global.exception.CustomException; import kusitms.backend.global.util.CookieUtil; import lombok.RequiredArgsConstructor; @@ -46,12 +44,9 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse protected boolean shouldNotFilter(HttpServletRequest request) { String path = request.getServletPath(); return path.equals("/") || path.equals("/login") || path.equals("/login/kakao") || path.equals("/login/naver") || path.equals("/login/google") - || path.startsWith("/public") || path.equals("/api/v1/health-check") || path.equals("/api/v1/test-error") - || path.equals("/api/v1/test/docs") || path.equals("/docs/swagger-ui/index.html") || path.equals("/docs/swagger-ui/swagger-ui.css") || path.equals("/docs/swagger-ui/index.css") - || path.equals("/docs/swagger-ui/swagger-ui-bundle.js") || path.equals("/docs/swagger-ui/swagger-ui-standalone-preset.js") || path.equals("/docs/swagger-ui/swagger-initializer.js") - || path.equals("/docs/swagger-ui/favicon-32x32.png") || path.equals("/docs/swagger-ui/favicon-16x16.png") - || path.equals("/v3/api-docs/swagger-config") || path.equals("/docs/open-api-3.0.1.json") + || path.startsWith("/public") || path.equals("/api/v1/health-check") || path.equals("/api/v1/test-error") || path.equals("/api/v1/test/docs") + || path.startsWith("/docs/swagger-ui") || path.equals("/v3/api-docs/swagger-config") || path.equals("/docs/open-api-3.0.1.json") || path.equals("/api/v1/signup") || path.equals("/api/v1/send-code") || path.equals("/api/v1/verify-code") || path.equals("/api/v1/token/re-issue"); From f19434ffbfe057540185630d83b5845566a5bd42 Mon Sep 17 00:00:00 2001 From: bbbang105 <2018111366@dgu.ac.kr> Date: Fri, 18 Oct 2024 15:40:27 +0900 Subject: [PATCH 12/23] =?UTF-8?q?#16=20[refactor]=20:=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=EB=A5=BC=20=EC=9D=B4=EB=8F=99=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kusitms/backend/chatbot/application/ChatbotService.java | 2 +- .../dto/{ => response}/GetGuideChatbotAnswerResponse.java | 2 +- .../kusitms/backend/chatbot/presentation/ChatbotController.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename src/main/java/kusitms/backend/chatbot/dto/{ => response}/GetGuideChatbotAnswerResponse.java (84%) diff --git a/src/main/java/kusitms/backend/chatbot/application/ChatbotService.java b/src/main/java/kusitms/backend/chatbot/application/ChatbotService.java index 3946ff8..df6d142 100644 --- a/src/main/java/kusitms/backend/chatbot/application/ChatbotService.java +++ b/src/main/java/kusitms/backend/chatbot/application/ChatbotService.java @@ -1,7 +1,7 @@ package kusitms.backend.chatbot.application; import kusitms.backend.chatbot.domain.enums.*; -import kusitms.backend.chatbot.dto.GetGuideChatbotAnswerResponse; +import kusitms.backend.chatbot.dto.response.GetGuideChatbotAnswerResponse; import kusitms.backend.chatbot.status.ChatbotErrorStatus; import kusitms.backend.global.exception.CustomException; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/kusitms/backend/chatbot/dto/GetGuideChatbotAnswerResponse.java b/src/main/java/kusitms/backend/chatbot/dto/response/GetGuideChatbotAnswerResponse.java similarity index 84% rename from src/main/java/kusitms/backend/chatbot/dto/GetGuideChatbotAnswerResponse.java rename to src/main/java/kusitms/backend/chatbot/dto/response/GetGuideChatbotAnswerResponse.java index 5ea93dc..7965aaf 100644 --- a/src/main/java/kusitms/backend/chatbot/dto/GetGuideChatbotAnswerResponse.java +++ b/src/main/java/kusitms/backend/chatbot/dto/response/GetGuideChatbotAnswerResponse.java @@ -1,4 +1,4 @@ -package kusitms.backend.chatbot.dto; +package kusitms.backend.chatbot.dto.response; public record GetGuideChatbotAnswerResponse( String[] answers, diff --git a/src/main/java/kusitms/backend/chatbot/presentation/ChatbotController.java b/src/main/java/kusitms/backend/chatbot/presentation/ChatbotController.java index ed2e89e..e95eae4 100644 --- a/src/main/java/kusitms/backend/chatbot/presentation/ChatbotController.java +++ b/src/main/java/kusitms/backend/chatbot/presentation/ChatbotController.java @@ -2,7 +2,7 @@ import jakarta.websocket.server.PathParam; import kusitms.backend.chatbot.application.ChatbotService; -import kusitms.backend.chatbot.dto.GetGuideChatbotAnswerResponse; +import kusitms.backend.chatbot.dto.response.GetGuideChatbotAnswerResponse; import kusitms.backend.chatbot.status.ChatbotSuccessStatus; import kusitms.backend.global.dto.ApiResponse; import lombok.RequiredArgsConstructor; From d19bc167f3ed11691acf9c0aaed03f4db2de6f0b Mon Sep 17 00:00:00 2001 From: bbbang105 <2018111366@dgu.ac.kr> Date: Fri, 18 Oct 2024 15:40:39 +0900 Subject: [PATCH 13/23] =?UTF-8?q?#16=20[refactor]=20:=20=EC=A4=91=EB=B3=B5?= =?UTF-8?q?=EB=90=98=EB=8A=94=20=EA=B2=BD=EB=A1=9C=EB=A5=BC=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/kusitms/backend/auth/jwt/JWTFilter.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/kusitms/backend/auth/jwt/JWTFilter.java b/src/main/java/kusitms/backend/auth/jwt/JWTFilter.java index 16e1a67..3826d21 100644 --- a/src/main/java/kusitms/backend/auth/jwt/JWTFilter.java +++ b/src/main/java/kusitms/backend/auth/jwt/JWTFilter.java @@ -47,12 +47,11 @@ protected boolean shouldNotFilter(HttpServletRequest request) { || path.startsWith("/public") || path.equals("/api/v1/health-check") || path.equals("/api/v1/test-error") || path.equals("/api/v1/test/docs") || path.startsWith("/docs/swagger-ui") || path.equals("/v3/api-docs/swagger-config") || path.equals("/docs/open-api-3.0.1.json") - || path.equals("/api/v1/signup") || path.equals("/api/v1/send-code") || path.equals("/api/v1/verify-code") || path.equals("/api/v1/token/re-issue") || path.equals("/api/v1/zones/recommend") || path.startsWith("/api/v1/chatbot") - || path.equals("/api/v1/zones/recommend"); + ; } // 사용자 인증 설정 From 7ccc4a878404e000337a677afe3e34a60c99cfe6 Mon Sep 17 00:00:00 2001 From: bbbang105 <2018111366@dgu.ac.kr> Date: Sat, 19 Oct 2024 11:40:57 +0900 Subject: [PATCH 14/23] =?UTF-8?q?#16=20[feat]=20:=20=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B2=84=20=ED=81=B4=EB=A1=9C=EB=B0=94=20=ED=99=98=EA=B2=BD?= =?UTF-8?q?=EB=B3=80=EC=88=98=EB=A5=BC=20=EC=B6=94=EA=B0=80=ED=95=9C?= =?UTF-8?q?=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index e966eab..4e0611f 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -88,3 +88,10 @@ spring: host: ${REDIS_HOST} port: ${REDIS_PORT} +clova: + api: + url: ${CLOVA_API_URL} + api-key: ${CLOVA_API_KEY} + api-gateway-key: ${CLOVA_API_GATEWAY_KEY} + prompt: + baseball: ${BASEBALL_PROMPT} \ No newline at end of file From 2f1fa1b50d0e3e6cab7bb2187b0eb812ac4f0907 Mon Sep 17 00:00:00 2001 From: bbbang105 <2018111366@dgu.ac.kr> Date: Sat, 19 Oct 2024 11:41:25 +0900 Subject: [PATCH 15/23] =?UTF-8?q?#16=20[feat]=20:=20=EC=99=B8=EB=B6=80?= =?UTF-8?q?=EC=99=80=20=ED=86=B5=EC=8B=A0=ED=95=98=EA=B8=B0=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20=EC=9B=B9=20=ED=81=B4=EB=9D=BC=EC=9D=B4=EC=96=B8?= =?UTF-8?q?=ED=8A=B8=20=EA=B8=B0=EB=8A=A5=EC=9D=84=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 3 +- .../global/config/WebClientConfig.java | 29 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 src/main/java/kusitms/backend/global/config/WebClientConfig.java diff --git a/build.gradle b/build.gradle index 7458407..b27df96 100644 --- a/build.gradle +++ b/build.gradle @@ -59,13 +59,14 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-redis' // Twilio SDK implementation 'com.twilio.sdk:twilio:8.24.0' - //restdocs-api-spec 의존성 추가 testImplementation 'com.epages:restdocs-api-spec-mockmvc:0.19.2' asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor' testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' testImplementation 'com.squareup.okhttp3:mockwebserver' implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0' + // WebClient + implementation 'org.springframework.boot:spring-boot-starter-webflux' } // 스니펫이 생성되는 디렉터리 경로를 설정 diff --git a/src/main/java/kusitms/backend/global/config/WebClientConfig.java b/src/main/java/kusitms/backend/global/config/WebClientConfig.java new file mode 100644 index 0000000..e041041 --- /dev/null +++ b/src/main/java/kusitms/backend/global/config/WebClientConfig.java @@ -0,0 +1,29 @@ +package kusitms.backend.global.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.reactive.function.client.WebClient; + +@Configuration +public class WebClientConfig { + + @Value("${clova.api.url}") + private String apiUrl; + + @Value("${clova.api.api-key}") + private String apiKey; + + @Value("${clova.api.api-gateway-key}") + private String apiGatewayKey; + + @Bean + public WebClient webClient(WebClient.Builder builder) { + return builder + .baseUrl(apiUrl) + .defaultHeader("X-NCP-CLOVASTUDIO-API-KEY", apiKey) + .defaultHeader("X-NCP-APIGW-API-KEY", apiGatewayKey) + .defaultHeader("Content-Type", "application/json") + .build(); + } +} \ No newline at end of file From c2edea9cea76f7ce0576c0ee2c99db18f1f9d05f Mon Sep 17 00:00:00 2001 From: bbbang105 <2018111366@dgu.ac.kr> Date: Sat, 19 Oct 2024 11:44:45 +0900 Subject: [PATCH 16/23] =?UTF-8?q?#16=20[feat]=20:=20=ED=86=B5=EC=8B=A0?= =?UTF-8?q?=EC=97=90=20=ED=95=84=EC=9A=94=ED=95=9C=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=ED=98=95=ED=83=9C=EB=A5=BC=20=EC=A7=80=EC=A0=95?= =?UTF-8?q?=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/chatbot/domain/enums/Role.java | 7 +++++++ .../chatbot/dto/response/ClovaChatbotAnswer.java | 6 ++++++ .../backend/chatbot/dto/response/Result.java | 15 +++++++++++++++ 3 files changed, 28 insertions(+) create mode 100644 src/main/java/kusitms/backend/chatbot/domain/enums/Role.java create mode 100644 src/main/java/kusitms/backend/chatbot/dto/response/ClovaChatbotAnswer.java create mode 100644 src/main/java/kusitms/backend/chatbot/dto/response/Result.java diff --git a/src/main/java/kusitms/backend/chatbot/domain/enums/Role.java b/src/main/java/kusitms/backend/chatbot/domain/enums/Role.java new file mode 100644 index 0000000..d050aa8 --- /dev/null +++ b/src/main/java/kusitms/backend/chatbot/domain/enums/Role.java @@ -0,0 +1,7 @@ +package kusitms.backend.chatbot.domain.enums; + +public enum Role { + system, // 시스템 + user, // 사용자 + assistant; // 어시스턴트 +} \ No newline at end of file diff --git a/src/main/java/kusitms/backend/chatbot/dto/response/ClovaChatbotAnswer.java b/src/main/java/kusitms/backend/chatbot/dto/response/ClovaChatbotAnswer.java new file mode 100644 index 0000000..46d7bf0 --- /dev/null +++ b/src/main/java/kusitms/backend/chatbot/dto/response/ClovaChatbotAnswer.java @@ -0,0 +1,6 @@ +package kusitms.backend.chatbot.dto.response; + +public record ClovaChatbotAnswer( + Result result +) { +} \ No newline at end of file diff --git a/src/main/java/kusitms/backend/chatbot/dto/response/Result.java b/src/main/java/kusitms/backend/chatbot/dto/response/Result.java new file mode 100644 index 0000000..ce80e2c --- /dev/null +++ b/src/main/java/kusitms/backend/chatbot/dto/response/Result.java @@ -0,0 +1,15 @@ +package kusitms.backend.chatbot.dto.response; + +import kusitms.backend.chatbot.dto.request.Message; + +import java.util.List; + +public record Result( + Message message, // 대화 메시지 + String stopReason, // 결과 중단 이유 + int inputLength, // 입력 토큰 수 + int outputLength, // 응답 토큰 수 + int seed, // 입력 seed 값 + List aiFilter // AI 필터 결과 (여기선 단순히 리스트로 표현) +) { +} \ No newline at end of file From 46948040a963ab2295e1609dfa6a020eca677df3 Mon Sep 17 00:00:00 2001 From: bbbang105 <2018111366@dgu.ac.kr> Date: Sat, 19 Oct 2024 11:45:20 +0900 Subject: [PATCH 17/23] =?UTF-8?q?#16=20[feat]=20:=20=EB=A9=94=EC=84=B8?= =?UTF-8?q?=EC=A7=80=20=EC=83=9D=EC=84=B1=20=ED=8C=A9=ED=86=A0=EB=A6=AC?= =?UTF-8?q?=EB=A5=BC=20=EC=B6=94=EA=B0=80=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chatbot/application/MessageFactory.java | 30 +++++++++++++++++++ .../backend/chatbot/dto/request/Message.java | 9 ++++++ 2 files changed, 39 insertions(+) create mode 100644 src/main/java/kusitms/backend/chatbot/application/MessageFactory.java create mode 100644 src/main/java/kusitms/backend/chatbot/dto/request/Message.java diff --git a/src/main/java/kusitms/backend/chatbot/application/MessageFactory.java b/src/main/java/kusitms/backend/chatbot/application/MessageFactory.java new file mode 100644 index 0000000..488cf15 --- /dev/null +++ b/src/main/java/kusitms/backend/chatbot/application/MessageFactory.java @@ -0,0 +1,30 @@ +package kusitms.backend.chatbot.application; + +import kusitms.backend.chatbot.domain.enums.Role; +import kusitms.backend.chatbot.dto.request.Message; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.util.Base64; + +@Component +public class MessageFactory { + + @Value("${clova.prompt.baseball}") + private String baseballPrompt; + + // 사용자 메시지 생성 + public Message createUserMessage(String content) { + return new Message(Role.user, content); + } + + // 시스템 메시지 생성 + public Message createSystemMessage() { + return new Message(Role.system, new String(Base64.getDecoder().decode(baseballPrompt))); + } + + // 어시스턴트 메시지 생성 + public Message createAssistantMessage(String content) { + return new Message(Role.assistant, content); + } +} \ No newline at end of file diff --git a/src/main/java/kusitms/backend/chatbot/dto/request/Message.java b/src/main/java/kusitms/backend/chatbot/dto/request/Message.java new file mode 100644 index 0000000..9a58301 --- /dev/null +++ b/src/main/java/kusitms/backend/chatbot/dto/request/Message.java @@ -0,0 +1,9 @@ +package kusitms.backend.chatbot.dto.request; + +import kusitms.backend.chatbot.domain.enums.Role; + +public record Message( + Role role, + String content +) { +} \ No newline at end of file From 72cc845b2465e68c52b7b92dd49aadad71aab0b5 Mon Sep 17 00:00:00 2001 From: bbbang105 <2018111366@dgu.ac.kr> Date: Sat, 19 Oct 2024 11:45:34 +0900 Subject: [PATCH 18/23] =?UTF-8?q?#16=20[feat]=20:=20=ED=81=B4=EB=A1=9C?= =?UTF-8?q?=EB=B0=94=20API=20=EC=9A=94=EC=B2=AD=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A6=AC=EB=A5=BC=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/ClovaRequestFactory.java | 22 +++++++++++++++++++ .../chatbot/dto/request/ClovaRequest.java | 12 ++++++++++ 2 files changed, 34 insertions(+) create mode 100644 src/main/java/kusitms/backend/chatbot/application/ClovaRequestFactory.java create mode 100644 src/main/java/kusitms/backend/chatbot/dto/request/ClovaRequest.java diff --git a/src/main/java/kusitms/backend/chatbot/application/ClovaRequestFactory.java b/src/main/java/kusitms/backend/chatbot/application/ClovaRequestFactory.java new file mode 100644 index 0000000..4cbec37 --- /dev/null +++ b/src/main/java/kusitms/backend/chatbot/application/ClovaRequestFactory.java @@ -0,0 +1,22 @@ +package kusitms.backend.chatbot.application; + +import kusitms.backend.chatbot.dto.request.ClovaRequest; +import kusitms.backend.chatbot.dto.request.Message; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; + +@Component +@RequiredArgsConstructor +public class ClovaRequestFactory { + + private final MessageFactory messageFactory; + + public ClovaRequest createClovaRequest() { + ArrayList messages = new ArrayList<>(); + messages.add(messageFactory.createSystemMessage()); + + return new ClovaRequest(messages, 0.8, 0.3, 256, 5.0); + } +}, \ No newline at end of file diff --git a/src/main/java/kusitms/backend/chatbot/dto/request/ClovaRequest.java b/src/main/java/kusitms/backend/chatbot/dto/request/ClovaRequest.java new file mode 100644 index 0000000..37a9d1e --- /dev/null +++ b/src/main/java/kusitms/backend/chatbot/dto/request/ClovaRequest.java @@ -0,0 +1,12 @@ +package kusitms.backend.chatbot.dto.request; + +import java.util.ArrayList; + +public record ClovaRequest( + ArrayList messages, + double topP, + double temperature, + int maxTokens, + double repeatPenalty +) { +} \ No newline at end of file From c8ff3883b9d2702f73d7ff1acad6cc52879892f7 Mon Sep 17 00:00:00 2001 From: bbbang105 <2018111366@dgu.ac.kr> Date: Sat, 19 Oct 2024 11:46:07 +0900 Subject: [PATCH 19/23] =?UTF-8?q?#16=20[feat]=20:=20=EC=99=B8=EB=B6=80=20?= =?UTF-8?q?=ED=81=B4=EB=A1=9C=EB=B0=94=20API=EC=99=80=20=ED=86=B5=EC=8B=A0?= =?UTF-8?q?=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infrastructure/ClovaApiClient.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/main/java/kusitms/backend/chatbot/infrastructure/ClovaApiClient.java diff --git a/src/main/java/kusitms/backend/chatbot/infrastructure/ClovaApiClient.java b/src/main/java/kusitms/backend/chatbot/infrastructure/ClovaApiClient.java new file mode 100644 index 0000000..197ca43 --- /dev/null +++ b/src/main/java/kusitms/backend/chatbot/infrastructure/ClovaApiClient.java @@ -0,0 +1,24 @@ +package kusitms.backend.chatbot.infrastructure; + +import kusitms.backend.chatbot.dto.request.ClovaRequest; +import kusitms.backend.chatbot.dto.response.ClovaChatbotAnswer; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.client.WebClient; + +@Component +@RequiredArgsConstructor +public class ClovaApiClient { + private final WebClient webClient; + + // 외부 CLOVA API와 통신하는 메서드 + public String requestClova(ClovaRequest request) { + ClovaChatbotAnswer clovaChatbotAnswer = webClient.post() + .bodyValue(request) + .retrieve() + .bodyToMono(ClovaChatbotAnswer.class) + .block(); + + return clovaChatbotAnswer.result().message().content(); + } +} \ No newline at end of file From 908112799e5611b268957c0a1c8213a0816fab1b Mon Sep 17 00:00:00 2001 From: bbbang105 <2018111366@dgu.ac.kr> Date: Sat, 19 Oct 2024 11:47:34 +0900 Subject: [PATCH 20/23] =?UTF-8?q?#16=20[feat]=20:=20=ED=81=B4=EB=A1=9C?= =?UTF-8?q?=EB=B0=94=20=EC=B1=97=EB=B4=87=EA=B3=BC=20=EC=9E=90=EC=9C=A0?= =?UTF-8?q?=EB=A1=AD=EA=B2=8C=20=EB=8C=80=ED=99=94=ED=95=A0=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chatbot/application/ClovaService.java | 24 +++++++++++++++++++ .../request/GetClovaChatbotAnswerRequest.java | 9 +++++++ .../GetClovaChatbotAnswerResponse.java | 9 +++++++ .../presentation/ChatbotController.java | 18 +++++++++++--- .../chatbot/status/ChatbotSuccessStatus.java | 3 ++- 5 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 src/main/java/kusitms/backend/chatbot/application/ClovaService.java create mode 100644 src/main/java/kusitms/backend/chatbot/dto/request/GetClovaChatbotAnswerRequest.java create mode 100644 src/main/java/kusitms/backend/chatbot/dto/response/GetClovaChatbotAnswerResponse.java diff --git a/src/main/java/kusitms/backend/chatbot/application/ClovaService.java b/src/main/java/kusitms/backend/chatbot/application/ClovaService.java new file mode 100644 index 0000000..2c52a09 --- /dev/null +++ b/src/main/java/kusitms/backend/chatbot/application/ClovaService.java @@ -0,0 +1,24 @@ +package kusitms.backend.chatbot.application; + +import kusitms.backend.chatbot.dto.request.ClovaRequest; +import kusitms.backend.chatbot.dto.response.GetClovaChatbotAnswerResponse; +import kusitms.backend.chatbot.infrastructure.ClovaApiClient; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class ClovaService { + private final ClovaApiClient clovaApiClient; + private final ClovaRequestFactory clovaRequestFactory; + private final MessageFactory messageFactory; + + // Clova 챗봇 답변을 가져오는 메서드 + public GetClovaChatbotAnswerResponse getClovaChatbotAnswer(String message) { + ClovaRequest request = clovaRequestFactory.createClovaRequest(); + request.messages().add(messageFactory.createUserMessage(message)); + String answer = clovaApiClient.requestClova(request); + + return GetClovaChatbotAnswerResponse.of(answer); + } +} \ No newline at end of file diff --git a/src/main/java/kusitms/backend/chatbot/dto/request/GetClovaChatbotAnswerRequest.java b/src/main/java/kusitms/backend/chatbot/dto/request/GetClovaChatbotAnswerRequest.java new file mode 100644 index 0000000..4078eb7 --- /dev/null +++ b/src/main/java/kusitms/backend/chatbot/dto/request/GetClovaChatbotAnswerRequest.java @@ -0,0 +1,9 @@ +package kusitms.backend.chatbot.dto.request; + + +import jakarta.validation.constraints.NotNull; + +public record GetClovaChatbotAnswerRequest( + @NotNull(message = "사용자 메세지는 필수값입니다.") String message +) { +} \ No newline at end of file diff --git a/src/main/java/kusitms/backend/chatbot/dto/response/GetClovaChatbotAnswerResponse.java b/src/main/java/kusitms/backend/chatbot/dto/response/GetClovaChatbotAnswerResponse.java new file mode 100644 index 0000000..f7504b8 --- /dev/null +++ b/src/main/java/kusitms/backend/chatbot/dto/response/GetClovaChatbotAnswerResponse.java @@ -0,0 +1,9 @@ +package kusitms.backend.chatbot.dto.response; + +public record GetClovaChatbotAnswerResponse( + String answer +) { + public static GetClovaChatbotAnswerResponse of(String answer) { + return new GetClovaChatbotAnswerResponse(answer); + } +} \ No newline at end of file diff --git a/src/main/java/kusitms/backend/chatbot/presentation/ChatbotController.java b/src/main/java/kusitms/backend/chatbot/presentation/ChatbotController.java index e95eae4..6942c45 100644 --- a/src/main/java/kusitms/backend/chatbot/presentation/ChatbotController.java +++ b/src/main/java/kusitms/backend/chatbot/presentation/ChatbotController.java @@ -2,20 +2,22 @@ import jakarta.websocket.server.PathParam; import kusitms.backend.chatbot.application.ChatbotService; +import kusitms.backend.chatbot.application.ClovaService; +import kusitms.backend.chatbot.dto.request.GetClovaChatbotAnswerRequest; +import kusitms.backend.chatbot.dto.response.GetClovaChatbotAnswerResponse; import kusitms.backend.chatbot.dto.response.GetGuideChatbotAnswerResponse; import kusitms.backend.chatbot.status.ChatbotSuccessStatus; import kusitms.backend.global.dto.ApiResponse; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @RestController @RequiredArgsConstructor @RequestMapping("/api/v1/chatbot") public class ChatbotController { private final ChatbotService chatbotService; + private final ClovaService clovaService; // 가이드 챗봇 답변 조회 API @GetMapping("/guide") @@ -28,4 +30,14 @@ public ResponseEntity> getGuideChatbo return ApiResponse.onSuccess(ChatbotSuccessStatus._GET_GUIDE_CHATBOT_ANSWER, response); } + + // Clova 챗봇 답변 조회 API + @PostMapping("/clova") + public ResponseEntity> getClovaChatbotAnswer( + @RequestBody GetClovaChatbotAnswerRequest request) { + + GetClovaChatbotAnswerResponse response = clovaService.getClovaChatbotAnswer(request.message()); + + return ApiResponse.onSuccess(ChatbotSuccessStatus._GET_CLOVA_CHATBOT_ANSWER, response); + } } diff --git a/src/main/java/kusitms/backend/chatbot/status/ChatbotSuccessStatus.java b/src/main/java/kusitms/backend/chatbot/status/ChatbotSuccessStatus.java index 283e11a..5353f1e 100644 --- a/src/main/java/kusitms/backend/chatbot/status/ChatbotSuccessStatus.java +++ b/src/main/java/kusitms/backend/chatbot/status/ChatbotSuccessStatus.java @@ -10,7 +10,8 @@ @AllArgsConstructor public enum ChatbotSuccessStatus implements BaseCode { - _GET_GUIDE_CHATBOT_ANSWER(HttpStatus.OK, "200", "가이드 챗봇 답변을 가져오는 데 성공했습니다.") + _GET_GUIDE_CHATBOT_ANSWER(HttpStatus.OK, "200", "가이드 챗봇 답변을 가져오는 데 성공했습니다."), + _GET_CLOVA_CHATBOT_ANSWER(HttpStatus.OK, "200", "네이버 클로바 챗봇 답변을 가져오는 데 성공했습니다.") ; private final HttpStatus httpStatus; From 5cfba9d9e94090f3ccc7885358196ed5371d4de1 Mon Sep 17 00:00:00 2001 From: bbbang105 <2018111366@dgu.ac.kr> Date: Sat, 19 Oct 2024 11:57:39 +0900 Subject: [PATCH 21/23] =?UTF-8?q?#16=20[fix]=20:=20=EC=BB=B4=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=97=90=EB=9F=AC=EB=A5=BC=20=ED=95=B4=EA=B2=B0?= =?UTF-8?q?=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/chatbot/application/ClovaRequestFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/kusitms/backend/chatbot/application/ClovaRequestFactory.java b/src/main/java/kusitms/backend/chatbot/application/ClovaRequestFactory.java index 4cbec37..c2231b2 100644 --- a/src/main/java/kusitms/backend/chatbot/application/ClovaRequestFactory.java +++ b/src/main/java/kusitms/backend/chatbot/application/ClovaRequestFactory.java @@ -19,4 +19,4 @@ public ClovaRequest createClovaRequest() { return new ClovaRequest(messages, 0.8, 0.3, 256, 5.0); } -}, \ No newline at end of file +} \ No newline at end of file From ff9d2179fb8e82627c21d739bc7118e01ecf752b Mon Sep 17 00:00:00 2001 From: bbbang105 <2018111366@dgu.ac.kr> Date: Sat, 19 Oct 2024 12:27:07 +0900 Subject: [PATCH 22/23] =?UTF-8?q?#16=20[refactor]=20:=20=EB=B9=8C=EB=8D=94?= =?UTF-8?q?=EB=A5=BC=20=EC=83=9D=EC=84=B1=EC=9E=90=EC=97=90=20=EB=B6=99?= =?UTF-8?q?=EC=9D=B4=EB=8A=94=20=EB=B0=A9=EC=8B=9D=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kusitms/backend/user/domain/User.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/main/java/kusitms/backend/user/domain/User.java b/src/main/java/kusitms/backend/user/domain/User.java index 73cbe4d..6787f70 100644 --- a/src/main/java/kusitms/backend/user/domain/User.java +++ b/src/main/java/kusitms/backend/user/domain/User.java @@ -2,18 +2,12 @@ import jakarta.persistence.*; import kusitms.backend.global.domain.BaseTimeEntity; -import kusitms.backend.global.exception.CustomException; -import kusitms.backend.user.status.UserErrorStatus; import lombok.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -@Entity(name="users") +@Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor(access = AccessLevel.PRIVATE) -@Builder +@Table(name = "users") public class User extends BaseTimeEntity { @Id @@ -37,5 +31,12 @@ public class User extends BaseTimeEntity { @Column(nullable = false, unique = true) private String phoneNumber; - -} + @Builder + public User(ProviderStatusType provider, String providerId, String email, String name, String phoneNumber){ + this.provider = provider; + this.providerId = providerId; + this.email = email; + this.name = name; + this.phoneNumber = phoneNumber; + } +} \ No newline at end of file From 9ec6c8a61365f363206251f5c4c25e591b117d2a Mon Sep 17 00:00:00 2001 From: bbbang105 <2018111366@dgu.ac.kr> Date: Sat, 19 Oct 2024 14:25:27 +0900 Subject: [PATCH 23/23] =?UTF-8?q?#16=20[feat]=20:=20=EC=9C=A0=ED=9A=A8?= =?UTF-8?q?=EC=84=B1=20=EA=B2=80=EC=A6=9D=20=EA=B8=B0=EB=8A=A5=EC=9D=84=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chatbot/dto/request/GetClovaChatbotAnswerRequest.java | 4 ++-- .../backend/chatbot/presentation/ChatbotController.java | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/kusitms/backend/chatbot/dto/request/GetClovaChatbotAnswerRequest.java b/src/main/java/kusitms/backend/chatbot/dto/request/GetClovaChatbotAnswerRequest.java index 4078eb7..8e89e69 100644 --- a/src/main/java/kusitms/backend/chatbot/dto/request/GetClovaChatbotAnswerRequest.java +++ b/src/main/java/kusitms/backend/chatbot/dto/request/GetClovaChatbotAnswerRequest.java @@ -1,9 +1,9 @@ package kusitms.backend.chatbot.dto.request; -import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.NotBlank; public record GetClovaChatbotAnswerRequest( - @NotNull(message = "사용자 메세지는 필수값입니다.") String message + @NotBlank(message = "사용자 메세지는 빈 값일 수 없습니다.") String message ) { } \ No newline at end of file diff --git a/src/main/java/kusitms/backend/chatbot/presentation/ChatbotController.java b/src/main/java/kusitms/backend/chatbot/presentation/ChatbotController.java index 6942c45..9bdfed4 100644 --- a/src/main/java/kusitms/backend/chatbot/presentation/ChatbotController.java +++ b/src/main/java/kusitms/backend/chatbot/presentation/ChatbotController.java @@ -1,5 +1,6 @@ package kusitms.backend.chatbot.presentation; +import jakarta.validation.Valid; import jakarta.websocket.server.PathParam; import kusitms.backend.chatbot.application.ChatbotService; import kusitms.backend.chatbot.application.ClovaService; @@ -34,7 +35,7 @@ public ResponseEntity> getGuideChatbo // Clova 챗봇 답변 조회 API @PostMapping("/clova") public ResponseEntity> getClovaChatbotAnswer( - @RequestBody GetClovaChatbotAnswerRequest request) { + @Valid @RequestBody GetClovaChatbotAnswerRequest request) { GetClovaChatbotAnswerResponse response = clovaService.getClovaChatbotAnswer(request.message());