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에 추가하면 된다.

정식 버전이 나온 만큼, 이제는 더 간단하게 적용할 수 있기에 프로젝트 할 때 부담 없이 활용할 수 있을 것 같다. 🫠


Copyright © 2025 NahyunKim

Mag.dev