일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- Oracle 초기 사용자
- Oracle 18c HR schema
- ora-01722
- Oracle 테이블 대소문자
- ORA-12899
- 서평단
- Oracle 사용자명
- Oracle 18c 설치
- 윈도우 Oracle
- 오라클 캐릭터셋 조회
- 오라클 캐릭터셋 확인
- Orace 18c
- 무료 오라클 데이터베이스
- 오라클 캐릭터셋 변경
- Oracle 18c HR
- Oracle 윈도우 설치
- Oracle Express Edition
- 비전공자를 위한 데이터베이스 입문
- Oracle 사용자명 입력
- 무료 오라클 설치
- ORA-00922
- oracle
- oracle 18c
- Oracle 테이블 띄어쓰기
- Today
- Total
The Nirsa Way
[Spring AI] #4. ChatController 분석 – 요청 처리와 응답 흐름 정리 본문
[Spring AI] #4. ChatController 분석 – 요청 처리와 응답 흐름 정리
KoreaNirsa 2025. 7. 6. 19:07※ 인프런 강의 Spring AI 실전 가이드: RAG 챗봇 만들기를 실습하는 내용입니다.
※ 해당 강의 코드를 코틀린 → 자바로 언어를 바꿔 진행하기 때문에 일부 코드 및 구현부가 다를 수 있습니다.
※ 실습이지만 코드를 직접 까보는 내용을 기록하는 포스팅이므로 강의 내용과 상이할 수 있습니다.
[Spring AI] #1. LLM 호출 실습 시작 – 구조 먼저 실행해보기
[Spring AI] #2. AiConfig 분석 – OpenAI API 연결 설정 방법
[Spring AI] #3-1. ChatService 분석 – 메시지 생성과 모델 호출 흐름
[Spring AI] #3-2. ChatService 분석 – buildRequestPrompt()로 요청 준비하기
[Spring AI] #3-3. ChatService 분석 – internalCall()로 요청과 응답 재구성 흐름 파악하기
[Spring AI] #4. ChatController 분석 – 요청 처리와 응답 흐름 정리
이전 포스팅에서의 내용
이전 포스팅에서는 최종적으로 서비스 계층에서 chatModel.call(prompt)를 호출하며 내부적으로 buildRequestPrompt()에서 LLM API를 호출하기 위해 필요한 정보들을 담고, internalCall()로 API를 호출하는 내용을 확인 하였습니다.
ChatController
이제 LLM 호출에 대한 마지막으로 컨트롤러에서 호출 과정을 확인해 볼텐데, 전체적인 코드는 아래와 같습니다.
package kr.co.ai.controller;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import kr.co.ai.dto.ChatRequest;
import kr.co.ai.dto.Response;
import kr.co.ai.service.ChatService;
import lombok.RequiredArgsConstructor;
@RestController
@RequestMapping("/api/v1/chat")
@RequiredArgsConstructor
@Tag(name = "Chat API", description = "OpenAI API를 통한 채팅 기능")
public class ChatController {
private final ChatService chatService;
private static final Logger logger = LoggerFactory.getLogger(ChatController.class);
@Operation(
summary = "LLM 채팅 메시지 전송",
description = "사용자의 메시지를 받아 OpenAI API를 통해 응답을 생성합니다.",
responses = {
@ApiResponse(
responseCode = "200",
description = "LLM 응답 성공",
content = @Content(schema = @Schema(implementation = ApiResponse.class))
),
@ApiResponse(responseCode = "400", description = "잘못된 요청"),
@ApiResponse(responseCode = "500", description = "서버 오류")
}
)
@PostMapping("/query")
public ResponseEntity<Response<Map<String, Object>>> sendMessage(
@Parameter(description = "채팅 요청 객체", required = true)
@RequestBody ChatRequest request) {
logger.info("Chat API 요청 받음: model={}", request.getModel());
if (request.getQuery() == null || request.getQuery().isBlank()) {
logger.warn("빈 질의가 요청됨");
return ResponseEntity.badRequest().body(
new Response<>(false, null, "질의가 비어있습니다.")
);
}
try {
String systemMessage = "You are a helpful AI assistant.";
var response = chatService.openAiChat(
request.getQuery(),
systemMessage,
request.getModel()
);
logger.debug("LLM 응답 생성: {}", response);
if (response != null) {
return ResponseEntity.ok(
new Response<>(true, Map.of("answer", response.getResult().getOutput().getText()), null)
);
} else {
logger.error("LLM 응답 생성 실패");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(
new Response<>(false, null, "LLM 응답 생성 중 오류 발생")
);
}
} catch (Exception e) {
logger.error("Chat API 처리 중 오류 발생", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(
new Response<>(false, null, e.getMessage() != null ? e.getMessage() : "알 수 없는 오류 발생")
);
}
}
}
가장 먼저 만나볼 수 있는 코드는 아래와 같은데, 사용자로부터 요청을 받고 query가 비어있는지 확인 후 비어있다면 그에 대한 응답을 하는 간단한 코드입니다.
logger.info("Chat API 요청 받음: model={}", request.getModel());
if (request.getQuery() == null || request.getQuery().isBlank()) {
logger.warn("빈 질의가 요청됨");
return ResponseEntity.badRequest().body(
new Response<>(false, null, "질의가 비어있습니다.")
);
}
이후 try-catch가 시작되는데 systemMessage는 해당 질의를 모델에게 어떠한 메세지를 넘겨주어 답변할지에 대한 메세지 입니다.
※ https://nirsa.tistory.com/442 포스팅 내용에 시스템 메시지에 대한 설명이 작성되어 있습니다.
☞ 즉 지시를 내린다는 것은 모델에게 역할, 대화의 톤 등을 지정할 수 있습니다. (ex. "너는 콜센터 직원이야" 등)
이후 chatService의 openAiChat을 호출하여 여태 포스팅에서 작성되었던 서비스 계층 로직이 실행됩니다. var 대신 ChatResponse을 명시적으로 작성해주는 것이 좋다고 생각합니다.
String systemMessage = "You are a helpful AI assistant.";
var response = chatService.openAiChat(
request.getQuery(),
systemMessage,
request.getModel()
);
※ var 타입?
일반적으로 자바는 타입을 명확하게 지정해주는 것이 기본이기 때문에 처음 보셨을 수 있습니다. var는 타입을 정해두지 않고 어떠한 타입이 오든 컴파일러가 스스로 타입을 추론하여 할당합니다.
개인적으로 자바와 같은 언어에서는 해당 방법을 최대한 지양해야 한다고 생각합니다.
그 다음으로 보이는 코드는 response가 null이 아니라면 정상적인 응답을 반환하고, response가 null 이라면 이에 해당하는 응답을 반환합니다.
logger.debug("LLM 응답 생성: {}", response);
if (response != null) {
return ResponseEntity.ok(
new Response<>(true, Map.of("answer", response.getResult().getOutput().getText()), null)
);
} else {
logger.error("LLM 응답 생성 실패");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(
new Response<>(false, null, "LLM 응답 생성 중 오류 발생")
);
}
마지막으로 catch문에 있는 코드는 위의 과정에서 어떠한 Exception이 발생하더라도 이를 잡아 처리합니다. 메시지가 있다면 메시지를 응답하고, 메시직자 없다면 "알 수 없는 오류 발생"을 반환합니다.
catch (Exception e) {
logger.error("Chat API 처리 중 오류 발생", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(
new Response<>(false, null, e.getMessage() != null ? e.getMessage() : "알 수 없는 오류 발생")
);
}
LLM 호출하기 - 마무리
OpenAI 설정 방법과 서비스와 컨트롤러 계층을 분석해보았습니다. 서비스 계층을 분석하며 느낀건 전체적인 흐름은 파악하였지만, Tool Calling 등 전반적인 AI 지식의 기초가 부족하여 어떠한 방식으로 동작하는지에 따른 정확한 이해는 못한 것 같습니다.
이 다음에서는 RAG 챗봇 만들기를 실습하며 코드를 분석할 예정입니다.
'AI Engineering > Spring AI' 카테고리의 다른 글
[Spring AI] #3-3. ChatService 분석 – internalCall()로 요청과 응답 재구성 흐름 파악하기 (2) | 2025.07.01 |
---|---|
[Spring AI] #3-2. ChatService 분석 – buildRequestPrompt()로 요청 준비하기 (0) | 2025.06.29 |
[Spring AI] #3-1. ChatService 분석 – 메시지 생성과 모델 호출 흐름 (0) | 2025.06.29 |
[Spring AI] #2. AiConfig 분석 – OpenAI API 연결 설정 (2) | 2025.06.24 |
[Spring AI] #1. LLM 호출 실습 시작 – 셋팅 및 전체 구조 먼저 실행해보기 (2) | 2025.06.24 |