이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
- Spring AI 첫 실습에 필요한 의존성과 설정은 무엇인가?
ChatClient로 첫 응답을 받는 최소 코드는 어떻게 생겼는가?- 첫 실습에서 OpenAI와 Ollama를 어떻게 나눠 써야 하는가?
- 첫 호출 뒤 바로 운영 코드로 가지 않기 위해 무엇을 분리해야 하는가?
개요
첫 실습의 목표는 멋진 챗봇을 만드는 것이 아니다. Spring Boot 애플리케이션에서 모델 호출이 어떤 bean과 설정으로 연결되는지 확인하는 것이다. 첫 호출이 성공하면 그 다음부터는 호출 경계를 어떻게 감쌀지 생각해야 한다.
Spring AI의 ChatClient는 AI model과 통신하는 fluent API다. 동기 호출과 streaming 호출을 모두 지원하지만, 입문 단계에서는 동기 호출로 시작한다.
원리
기본 흐름은 단순하다.
- Spring AI BOM을 dependency management에 넣는다.
- provider starter를 추가한다.
- provider property를 설정한다.
ChatClient.Builder를 주입받는다.ChatClient로 prompt를 보내고 응답을 받는다.
이때 provider starter가 model bean과 builder를 자동 구성한다. 따라서 처음에는 코드보다 버전과 property가 더 중요하다.
실습
Maven 예시는 다음처럼 시작한다.
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
</dependencies>OpenAI 설정은 다음처럼 둔다.
spring:
ai:
model:
chat: openai
openai:
api-key: ${OPENAI_API_KEY}
chat:
model: gpt-4o-mini
temperature: 0.2첫 호출은 service에서 확인한다.
@Service
class FirstChatService {
private final ChatClient chatClient;
FirstChatService(ChatClient.Builder builder) {
this.chatClient = builder
.defaultSystem("You answer briefly in Korean.")
.build();
}
String ask(String question) {
return chatClient.prompt()
.user(question)
.call()
.content();
}
}간단한 Controller를 붙이면 브라우저나 curl로 확인할 수 있다.
@RestController
@RequestMapping("/api/ai")
class FirstChatController {
private final FirstChatService firstChatService;
FirstChatController(FirstChatService firstChatService) {
this.firstChatService = firstChatService;
}
@PostMapping("/ask")
Map<String, String> ask(@RequestBody Map<String, String> body) {
return Map.of("answer", firstChatService.ask(body.get("question")));
}
}성공 경로는 “질문을 보내면 한국어 짧은 답변이 온다”이다.
자주 나는 오류는 API key 누락, model 이름 오타, BOM 버전 불일치, provider quota 초과, network timeout이다. OpenAI GPT-5 계열을 선택하면 temperature가 지원되지 않을 수 있으므로, 해당 모델을 쓸 때는 temperature 설정을 제거하고 공식 문서의 reasoning model option을 확인한다.
운영에서는 이 Controller를 그대로 키우지 않는다. 다음 장에서 AI Gateway로 분리한다.
실전 판단
첫 실습부터 다음 기준을 메모한다.
- 이 요청은 사용자가 기다릴 수 있는가?
- 실패하면 어떤 메시지를 보여줄 것인가?
- prompt 원문을 로그에 남겨도 되는가?
- 누가 이 기능을 얼마나 호출할 수 있는가?
- 모델 비용을 feature별로 볼 수 있는가?
입문 단계에서 이 질문을 같이 보아야 나중에 운영 장에서 갑자기 낯설지 않다.
실전 팁
- 첫 코드는 Controller보다 service에 둔다.
OPENAI_API_KEY는 shell history나 노트에 적지 않는다.- Ollama로 실습할 때는 먼저
ollama list로 모델명을 확인한다. - 응답 원문을 로그에 찍고 싶다면 local profile에서만 제한적으로 찍는다.
- 첫 성공 뒤에는 streaming보다 timeout, 예외 처리, gateway 분리를 먼저 배운다.
위험 신호!
- API key를
application.yml에 직접 커밋한다. - Controller가 prompt, model, provider 예외를 모두 안다.
- OpenAI 호출 실패가 사용자에게 stack trace로 노출된다.
- 첫 데모가 성공했다는 이유로 비용과 quota를 보지 않는다.
- Ollama 응답 품질을 기준으로 운영 모델 선택을 단정한다.
확인 질문
확인 질문
- 첫 ChatClient 실습의 목표는 무엇인가?
- Spring Boot 설정과 bean으로 모델 호출이 연결되는 흐름을 확인하는 것이다.
- 첫 실습 코드를 Controller에만 두면 왜 위험한가?
- HTTP 계약과 AI provider 호출 정책이 섞여 테스트와 변경이 어려워지기 때문이다.
- OpenAI 설정에서 secret은 어디에 둬야 하는가?
- 환경 변수나 secret manager에 두고 코드와 노트에는 남기지 않는다.
- 첫 호출 뒤 다음으로 배워야 할 것은 무엇인가?
- AI Gateway 계층 분리, 예외 변환, timeout, 사용량 기록이다.
학습 연결
- 이전 문서: 01. Spring AI 학습 로드맵과 버전 선택.md
- 다음 문서: 03. Spring Boot 계층 설계와 AI Gateway.md
- 함께 보면 좋은 문서: 01. LLM Client Adapter와 Provider 경계.md
- 함께 보면 좋은 문서: 02. Timeout Retry Rate Limit Circuit Breaker.md