Spring AI 1.0.0, 어떻게 적용할까?
Spring Boot에서 Spring AI 1.0.0을 활용해 ChatClient를 구성하고 프롬프트를 설정해, 서비스에 넣을 챗봇을 구현하는 방법을 소개합니다.
2025.05.28
들어가기 전에...
왜 Spring AI였나?
- Spring 기반 프로젝트에서 OpenAI API를 활용해 챗봇 기능을 구현하려다 발견한 Spring AI.
- 프로젝트를 실제 진행할 때는 정식 버전이 아니었기에(기억은 안나지만 1.0.0-SNAPSHOT, 0.8.6? 뭐 이런게 있었던 것 같은데, 기억의 왜곡일 수도...) 정보가 부족해 어려움을 겪었다. (GPT도 잘 모르고 유튜브와 공식 문서가 유일한 My friends)
- 그때 봤던 유튜브들
도입 당시: snapshot 의존성과 수동 구성 방식 (정식 출시 전)
- Spring AI Snapshot 버전은 Maven Central에 등록되지 않아 별도 저장소 등록이 필요했고, 의존성도 추가했다.
- 공식 문서를 참고해 의존성과 저장소를 추가했다.
repositories {
mavenCentral()
maven { url 'https://repo.spring.io/milestone' }
maven { url 'https://repo.spring.io/snapshot' }
maven {
name = 'Central Portal Snapshots'
url = 'https://central.sonatype.com/repository/maven-snapshots/'
}
}
dependencies {
implementation platform("org.springframework.ai:spring-ai-bom:1.0.0-SNAPSHOT")
implementation 'org.springframework.ai:spring-ai-openai'
}
- 나머지 코드들도 있지만... 어차피 release 되었으니 생략하고 바로 잘 동작하는 1.0.0 버전의 코드를 정리한다.
👉 Spring AI 1.0.0 Released
Spring AI 1.0.0 정식 버전의 핵심 변화
ChatClient
를 직접 생성하지 않고, Spring Boot가 자동으로 Builder를 구성해줌OpenAiChatModel
은 @Bean으로 등록하여 구성ChatClient.Builder
는 자동 주입되고.build()
로 클라이언트 생성- 프롬프트 스타일을
defaultSystem()
으로 설정 가능
프로젝트에 적용해 보자!
▪️ 의존성
- Maven Central에 등록되어 레포들은 이제 없어도 된다.
repositories {
mavenCentral()
}
dependencies {
implementation platform("org.springframework.ai:spring-ai-bom:1.0.0")
implementation 'org.springframework.ai:spring-ai-starter-model-openai'
implementation "org.springframework.ai:spring-ai-client-chat:1.0.0"
}
spring-ai-bom
: 버전 관리를 위한 BOM 선언spring-ai-starter-model-openai
: OpenAI 연동을 위한 스타터- (
spring-ai-client-chat
: starter에 이미 포함되어 있어 생략 가능)
🔗 참고 | spring-ai/getting-started - 근데 아직 SNAPSHOT 버전이 작성되어 있다.
▪️ ChatModel & ChatClient 구성
- 정식 버전 이후에는 Spring Boot의 Auto Configuration 기능을 통해
ChatClient.Builder
를 자동 주입받을 수 있다.
@Configuration
public class AiConfig {
@Value("${spring.ai.openai.api-key}")
private String apiKey;
@Bean
public ChatModel chatModel() {
OpenAiApi openAiApi = OpenAiApi.builder()
.apiKey(apiKey)
.build();
OpenAiChatOptions options = OpenAiChatOptions.builder()
.model("gpt-4o-mini")
.build();
return OpenAiChatModel.builder()
.openAiApi(openAiApi)
.defaultOptions(options)
.build();
}
@Bean
public ChatClient chatClient(ChatClient.Builder builder) {
return builder
.defaultSystem("넌 친근하고 재밌는 튜터야. 항상 친구에게 설명하듯 말해줘. 쓸모 없는 말은 삼가고 대답은 최대한 간결하고 짧게 부탁할게.")
.build();
}
}
ChatClient
를 수동으로 등록해 기본 지침을 작성했다. 지침이 필요 없다면 없어도 된다.
🔗 참고 | Default System Text
▪️ AI 프롬프트 요청 흐름
- Web IDE 프로젝트에 사용을 했기에 code-based, hint, refactor 같은
모드
를 나눠서 구현하려 했고, 사용자가 질문(prompt)을 입력하면 AI가 응답하는 형태로 구성된다.
RequestDto
@Getter
@Setter
@NoArgsConstructor
public class AiRequest {
private String mode;
private String prompt;
private String code;
}
ResponseDto
@Getter
@Setter
@AllArgsConstructor
public class AiResponse {
private String answer;
}
Service
@Service
@RequiredArgsConstructor
public class AiService {
private final ChatClient chatClient;
public String ask(AiRequest request) {
String mode = Optional.ofNullable(request.getMode()).orElse("");
String prompt = request.getPrompt();
String code = Optional.ofNullable(request.getCode()).orElse("");
String finalPrompt = switch (mode) {
case "code-based" -> prompt + "\n\n[코드]\n" + code;
case "hint" -> "아래 질문에 대해 문제를 푸는 데 도움이 되는 '힌트'를 알려줘. "
+ "정답 코드나 직접적인 풀이 코드는 절대 제공하지 마! "
+ "대신 개념 설명, 풀이 아이디어, 비슷한 예제 코드 등은 자유롭게 제공해줘. "
+ "질문: " + prompt;
default -> prompt;
};
return chatClient.prompt()
.system("넌 친근하고 재밌는 튜터야. 항상 친구에게 설명하듯 말해줘. 쓸모 없는 말은 삼가고 대답은 최대한 간결하고 짧게 부탁할게.")
.user(finalPrompt)
.call()
.content();
}
}
- 기획을 할 때, 버튼으로 모드를 설정해서 프롬프트를 입력하면 간편하게 다양한 모드를 사용할 수 있도록 했다.
- code-based: 코드와 함께 질문을 보냄 (코드 에디터에 입력한 코드를 요청 데이터에 포함)
- hint: 문제 풀이를 위한 힌트를 요청 (알고리즘 문제 등 학습 도움을 위한 코드 제외, 힌트 응답 기능)
- default: 어떤 모드도 선택하지 않으면, 챗봇으로 사용
▪️ REST API 구성
- 컨트롤러를 통해
/ai/ask
API 구현
@RestController
@RequiredArgsConstructor
@RequestMapping("/ai")
public class AiController {
private final AiService aiService;
@PostMapping("/ask")
public ResponseEntity<AiResponse> ask(
@RequestBody AiRequest aiRequest,
@AuthenticationPrincipal JwtUserInfoDto userInfoDto
) {
String result = aiService.ask(aiRequest);
return ResponseEntity.ok(new AiResponse(result));
}
}
▪️ 기능 확인
- 잘 동작하는 것을 확인할 수 있다.
- mode, code가 있다면, request body에 추가하면 된다.
정식 버전이 나온 만큼, 이제는 더 간단하게 적용할 수 있기에 프로젝트 할 때 부담 없이 활용할 수 있을 것 같다. 🫠