Skip to content

Commit d3f4105

Browse files
committed
feat: OpenAI 챗봇 API 구현
1 parent df8b8ae commit d3f4105

File tree

4 files changed

+192
-0
lines changed

4 files changed

+192
-0
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.example.spring_ai_tutorial.config
2+
3+
import io.swagger.v3.oas.models.OpenAPI
4+
import io.swagger.v3.oas.models.info.Info
5+
import org.springframework.context.annotation.Bean
6+
import org.springframework.context.annotation.Configuration
7+
8+
@Configuration
9+
class OpenApiConfig {
10+
11+
@Bean
12+
fun springOpenAPI(): OpenAPI {
13+
return OpenAPI()
14+
.info(
15+
Info()
16+
.title("Spring AI Tutorial API")
17+
.version("1.0")
18+
.description("Spring AI를 활용한 챗봇 API")
19+
)
20+
}
21+
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package com.example.spring_ai_tutorial.controller
2+
3+
import com.example.spring_ai_tutorial.service.ChatService
4+
import io.github.oshai.kotlinlogging.KotlinLogging
5+
import io.swagger.v3.oas.annotations.Operation
6+
import io.swagger.v3.oas.annotations.Parameter
7+
import io.swagger.v3.oas.annotations.media.Content
8+
import io.swagger.v3.oas.annotations.media.Schema
9+
import io.swagger.v3.oas.annotations.responses.ApiResponse as SwaggerResponse
10+
import io.swagger.v3.oas.annotations.tags.Tag
11+
import org.springframework.http.HttpStatus
12+
import org.springframework.http.ResponseEntity
13+
import org.springframework.web.bind.annotation.*
14+
15+
/**
16+
* Chat API 컨트롤러
17+
*
18+
* LLM API를 통해 채팅 기능을 제공합니다.
19+
*/
20+
@RestController
21+
@RequestMapping("/api/v1/chat")
22+
@Tag(name = "Chat API", description = "OpenAI API를 통한 채팅 기능")
23+
class ChatController(
24+
private val chatService: ChatService
25+
) {
26+
private val logger = KotlinLogging.logger {}
27+
28+
/**
29+
* 사용자의 메시지를 받아 OpenAI API로 응답 생성
30+
*/
31+
@Operation(
32+
summary = "LLM 채팅 메시지 전송",
33+
description = "사용자의 메시지를 받아 OpenAI API를 통해 응답을 생성합니다."
34+
)
35+
@SwaggerResponse(
36+
responseCode = "200",
37+
description = "LLM 응답 성공",
38+
content = [Content(schema = Schema(implementation = ApiResponse::class))]
39+
)
40+
@SwaggerResponse(responseCode = "400", description = "잘못된 요청")
41+
@SwaggerResponse(responseCode = "500", description = "서버 오류")
42+
@PostMapping("/query")
43+
suspend fun sendMessage(
44+
@Parameter(description = "채팅 요청 객체", required = true)
45+
@RequestBody request: ChatRequest
46+
): ResponseEntity<ApiResponse<Map<String, Any>>> {
47+
logger.info { "Chat API 요청 받음: model=${request.model}" }
48+
49+
// 유효성 검사
50+
if (request.query.isBlank()) {
51+
logger.warn { "빈 질의가 요청됨" }
52+
return ResponseEntity.badRequest().body(
53+
ApiResponse(success = false, error = "질의가 비어있습니다.")
54+
)
55+
}
56+
57+
return try {
58+
// 시스템 프롬프트 지정
59+
val systemMessage = "You are a helpful AI assistant."
60+
61+
// AI 응답 생성
62+
val response = chatService.openAiChat(
63+
userInput = request.query,
64+
systemMessage = systemMessage,
65+
model = request.model
66+
)
67+
logger.debug { "LLM 응답 생성: $response" }
68+
69+
response?.let { chatResponse ->
70+
ResponseEntity.ok(
71+
ApiResponse(
72+
success = true,
73+
data = mapOf("answer" to chatResponse.result.output.text)
74+
)
75+
)
76+
} ?: run {
77+
logger.error { "LLM 응답 생성 실패" }
78+
ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(
79+
ApiResponse(
80+
success = false,
81+
error = "LLM 응답 생성 중 오류 발생"
82+
)
83+
)
84+
}
85+
} catch (e: Exception) {
86+
logger.error(e) { "Chat API 처리 중 오류 발생" }
87+
ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(
88+
ApiResponse(
89+
success = false,
90+
error = e.message ?: "알 수 없는 오류 발생"
91+
)
92+
)
93+
}
94+
}
95+
}
96+
97+
@Schema(description = "채팅 요청 데이터 모델")
98+
data class ChatRequest(
99+
@Schema(description = "사용자 질문", example = "안녕하세요")
100+
val query: String,
101+
102+
@Schema(description = "사용할 LLM 모델", example = "gpt-3.5-turbo", defaultValue = "gpt-3.5-turbo")
103+
val model: String = "gpt-3.5-turbo"
104+
)
105+
106+
@Schema(description = "API 응답 포맷")
107+
data class ApiResponse<T>(
108+
@Schema(description = "요청 처리 성공 여부")
109+
val success: Boolean,
110+
111+
@Schema(description = "성공 응답 데이터")
112+
val data: T? = null,
113+
114+
@Schema(description = "실패 오류 메시지")
115+
val error: String? = null
116+
)
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.example.spring_ai_tutorial.service
2+
3+
import io.github.oshai.kotlinlogging.KotlinLogging
4+
import kotlinx.coroutines.Dispatchers
5+
import kotlinx.coroutines.withContext
6+
import org.springframework.ai.chat.model.ChatResponse
7+
import org.springframework.ai.openai.api.OpenAiApi
8+
import org.springframework.stereotype.Service
9+
10+
/**
11+
* OpenAI API를 사용하여 질의응답을 수행하는 서비스
12+
*/
13+
@Service
14+
class ChatService(
15+
private val openAiApi: OpenAiApi
16+
) {
17+
private val logger = KotlinLogging.logger {}
18+
19+
/**
20+
* OpenAI 챗 API를 이용하여 응답을 생성합니다.
21+
*
22+
* @param userInput 사용자 입력 메시지
23+
* @param systemMessage 시스템 프롬프트
24+
* @param model 사용할 LLM 모델명
25+
* @return 챗 응답 객체, 오류 시 null
26+
*/
27+
suspend fun openAiChat(
28+
userInput: String,
29+
systemMessage: String,
30+
model: String = "gpt-3.5-turbo"
31+
): ChatResponse? = withContext(Dispatchers.IO) {
32+
logger.debug { "OpenAI 챗 호출 시작 - 모델: $model" }
33+
try {
34+
// 메시지 구성
35+
36+
// 챗 옵션 설정
37+
38+
// 프롬프트 생성
39+
40+
// 챗 모델 생성 및 호출
41+
42+
return@withContext TODO("응답 생성 로직을 작성하세요")
43+
} catch (e: Exception) {
44+
logger.error(e) { "OpenAI 챗 호출 중 오류 발생: ${e.message}" }
45+
return@withContext null
46+
}
47+
}
48+
}

src/main/resources/application.properties

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,10 @@ server.port=8080
55

66
# OpenAI API Configuration
77
spring.ai.openai.api-key=${OPENAI_API_KEY}
8+
9+
# File Upload Settings
10+
spring.servlet.multipart.max-file-size=20MB
11+
spring.servlet.multipart.max-request-size=20MB
12+
13+
# Swagger/OpenAPI Configuration
14+
springdoc.api-docs.path=/api-docs

0 commit comments

Comments
 (0)