실무에 바로 적용하는 Spring AI: Spring 서비스에 챗봇·RAG·MCP 도입하기
practice-chat-controller-call-vs-stream
✅ 1. call() 메서드 생성
ChatService.java
public @Nullable ChatResponse call(Prompt prompt, String conversationId){
return prepareRequest(prompt, conversationId)
.call()
.chatResponse();
}
✅ 2. record 생성
💁♀️
record는 데이터를 담고 전달하는 목적에 최적화된 최신 자바 문법입니다.
소괄호 안에 필요한 데이터(필드)를 나열해주면, 자바가 알아서 생성자와 Getter를 모두 만들어줍니다!
주로 REST API에서 프론트엔드가 서버로 보내는 데이터를 JSON 형태로 받아내기 위해 DTO를 제작할 때 사용합니다.
record 문법이 생소하신 분들은 한 번 학습하고 넘어가시길 바랍니다.
ChatController.java
public record PromptBody(@NotEmpty String conversationId,
@NotEmpty String userPrompt,
@Nullable String systemPrompt,
DefaultChatOptions chatOptions){}
✅ 3. call 방식 생성
ChatController.java
@PostMapping(value = "/call", produces = MediaType.APPLICATION_JSON_VALUE)
public ChatResponse call(@RequestBody @Valid PromptBody promptBody) {
// 1. 메시지들을 차곡차곡 담을 빈 리스트를 생성
List<Message> messages = new ArrayList<>();
// 2. systemPrompt가 입력으로 들어왔다면 리스트에 넣자
if(promptBody.systemPrompt() != null && !promptBody.systemPrompt().isBlank()){
messages.add(new SystemMessage(promptBody.systemPrompt()));
}
// 3. userPrompt는 필수 값이니 무조건 리스트에 넣자
messages.add(new UserMessage(promptBody.userPrompt()));
// 4. 리스트에 담긴 메시지들로 프롬프트 조립하기
Prompt.Builder promptBuilder = Prompt.builder().messages(messages);
// 5. 프론트엔드에서 보낸 chatOptions가 있다면 적용하기
if(promptBody.chatOptions() != null){
promptBuilder.chatOptions(promptBody.chatOptions());
}
// 완성된 프롬프트와 conversationID를 가지고 서비스 메서드 호출
return chatService.call(promptBuilder.build(), promptBody.conversationId());
}
✅ 4. 코드 리팩토링
ChatController.java
@PostMapping(value = "/call", produces = MediaType.APPLICATION_JSON_VALUE)
public ChatResponse call(@RequestBody @Valid PromptBody promptBody) {
Prompt prompt = createPrompt(promptBody);
// 완성된 프롬프트와 conversationID를 가지고 서비스 메서드 호출
return chatService.call(prompt, promptBody.conversationId());
}
private static Prompt createPrompt(PromptBody promptBody) {
// 1. 메시지들을 차곡차곡 담을 빈 List를 생성
List<Message> messages = new ArrayList<>();
// 2. systemPrompt가 입력으로 들어왔다면 리스트에 넣자
if(promptBody.systemPrompt() != null && !promptBody.systemPrompt().isBlank()){
messages.add(new SystemMessage(promptBody.systemPrompt()));
}
// 3. userPrompt는 필수 값이니 무조건 리스트에 넣자
messages.add(new UserMessage(promptBody.userPrompt()));
// 4. 리스트에 담긴 메시지들로 프롬프트 조립하기
Prompt.Builder promptBuilder = Prompt.builder().messages(messages);
// 5. 프론트엔드에서 보낸 chatOptions가 있다면 적용하기
if(promptBody.chatOptions() != null){
promptBuilder.chatOptions(promptBody.chatOptions());
}
return promptBuilder.build();
}
✅ 5. stream 방식 생성하기
ChatController.java
@PostMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> stream(@RequestBody @Valid PromptBody promptBody){
Prompt prompt = createPrompt(promptBody);
return chatService.stream(prompt, promptBody.conversationId());
}
✅ 6. 로깅 설정
spring:
application:
name: chat
# cli: true
ai:
#[1] openAI 설정
openai:
# 환경변수 값
api-key: ${OPENAI_API_KEY}
chat:
# 실제 사용할 대화형 AI의 모델명
model: openai/gpt-4.1-nano
# GitHub Models 서버로 요청을 보냄
base-url: https://models.github.ai/inference
# 세부 API 경로
completions-path: /chat/completions
#[2] Ollama 설정
ollama:
init:
pull-model-strategy: when_missing
chat:
model: hf.co/Qwen/Qwen2.5-1.5B-Instruct-GGUF
logging:
level:
org.springframework.ai.chat.client.advisor: DEBUG
✅ 7. 테스트