Skip to content

Commit

Permalink
#19 [feat] 전체.
Browse files Browse the repository at this point in the history
  • Loading branch information
juuuunny committed Oct 19, 2024
2 parents 317ce34 + 2f5db3d commit f3c7775
Show file tree
Hide file tree
Showing 29 changed files with 534 additions and 18 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,6 @@ out/
### VS Code ###
.vscode/

.DS_Store
.DS_Store

/src/main/resources/static/docs
105 changes: 104 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ plugins {
id 'java'
id 'org.springframework.boot' version '3.3.4'
id 'io.spring.dependency-management' version '1.1.6'

//rest docs
id "org.asciidoctor.jvm.convert" version "3.3.2"
id 'com.epages.restdocs-api-spec' version '0.17.1'
id 'org.hidetake.swagger.generator' version '2.18.2'
}

group = 'kusitms'
Expand All @@ -13,10 +18,12 @@ java {
}
}

//rest-docs
configurations {
compileOnly {
extendsFrom annotationProcessor
}
asciidoctorExt
}

repositories {
Expand Down Expand Up @@ -52,8 +59,104 @@ 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'
}

// 스니펫이 생성되는 디렉터리 경로를 설정
ext {
snippetsDir = file('build/generated-snippets')
}

sourceSets {
test {
java {
srcDirs = ['src/test/java']
}
}
}

def serverUrl = "http://localhost:8080"

// openapi3 설정
openapi3 {
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'

// 기존 파일 삭제 후 json 파일 복사
delete file('src/main/resources/static/docs/')
copy {
from "build/resources/main/static/docs"
into "src/main/resources/static/docs/"
}
}

// 테스트를 통해 생성된 snippets 출력 위치 정의
tasks.register("ext") {
snippetsDir = file('build/generated-snippets')
}

tasks.named('test') {
useJUnitPlatform()
}
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')
}

// asciidoctor 작업을 구성
tasks.named("asciidoctor") {
inputs.dir 'snippetsDir'
configurations 'asciidoctorExt'
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")
from file(project.layout.buildDirectory.dir("docs/asciidoc").get().asFile.path)
into file("src/main/resources/static/docs")
}

tasks.named("build") {
dependsOn copyDocument
}

3 changes: 2 additions & 1 deletion src/main/java/kusitms/backend/auth/jwt/JWTFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ protected boolean shouldNotFilter(HttpServletRequest request) {
|| path.equals("/onboarding") || path.equals("/base")
|| 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/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/zones/recommend") || path.equals("/api/v1/profiles")
|| path.startsWith("/api/v1/chatbot");
}
Expand All @@ -69,7 +70,7 @@ private void handleCustomException(HttpServletResponse response, CustomException
response.getWriter().write(jsonResponse);
}

// 공통 예외 처리 메서드
// 토큰 예외 처리 메서드
private void handleException(HttpServletResponse response, Exception e) throws IOException {
log.error("서버 예외 발생: {}", e.getMessage());
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Message> messages = new ArrayList<>();
messages.add(messageFactory.createSystemMessage());

return new ClovaRequest(messages, 0.8, 0.3, 256, 5.0);
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
7 changes: 7 additions & 0 deletions src/main/java/kusitms/backend/chatbot/domain/enums/Role.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package kusitms.backend.chatbot.domain.enums;

public enum Role {
system, // 시스템
user, // 사용자
assistant; // 어시스턴트
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package kusitms.backend.chatbot.dto.request;

import java.util.ArrayList;

public record ClovaRequest(
ArrayList<Message> messages,
double topP,
double temperature,
int maxTokens,
double repeatPenalty
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package kusitms.backend.chatbot.dto.request;


import jakarta.validation.constraints.NotBlank;

public record GetClovaChatbotAnswerRequest(
@NotBlank(message = "사용자 메세지는 빈 값일 수 없습니다.") String message
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package kusitms.backend.chatbot.dto.request;

import kusitms.backend.chatbot.domain.enums.Role;

public record Message(
Role role,
String content
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package kusitms.backend.chatbot.dto.response;

public record ClovaChatbotAnswer(
Result result
) {
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package kusitms.backend.chatbot.dto;
package kusitms.backend.chatbot.dto.response;

public record GetGuideChatbotAnswerResponse(
String[] answers,
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/kusitms/backend/chatbot/dto/response/Result.java
Original file line number Diff line number Diff line change
@@ -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<String> aiFilter // AI 필터 결과 (여기선 단순히 리스트로 표현)
) {
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
Loading

0 comments on commit f3c7775

Please sign in to comment.