JSCODE Logo
블로그후기멘토진
회사명 : JSCODE대표 : 박재성사업자 등록번호 : 244-22-01557통신판매업 : 제 2023-인천미추홀-0381 호
학원 명칭 : 제이에스코드(JSCODE)원격학원학원설립ㆍ운영 등록번호 : 제6063호

서울특별시 구로구 경인로 20가길 11(오류동, 아델리아)

Copyright ⓒ 2025 JSCODE - 최상위 현업 개발자들의 프로그래밍 교육 All rights reserved.

이용약관개인정보처리방침
← 블로그 목록으로 돌아가기

[실습] ToolChatConfig, ToolController 구현 - CLI, REST API

JSCODE 시니
JSCODE 시니
2026. 06. 13.
author
JSCODE 시니
category
Spring AI
createdAt
Jun 13, 2026 09:54 AM
isPublic
isPublic
series
실무에 바로 적용하는 Spring AI: Spring 서비스에 챗봇·RAG·MCP 도입하기
slug
practice-tool-chat-config-and-controller-setup
type
post
updatedAt

✅ 1. CLI

ToolChatConfig.java
package com.jscode.tool; @Configuration public class ToolChatConfig { @Bean public SimpleLoggerAdvisor simpleLoggerAdvisor(){ return SimpleLoggerAdvisor.builder().build(); } @Bean public ChatMemory chatMemory(){ return MessageWindowChatMemory.builder().maxMessages(10).build(); } @Bean public MessageChatMemoryAdvisor messageChatMemoryAdvisor(ChatMemory chatMemory){ return MessageChatMemoryAdvisor.builder(chatMemory).build(); } @ConditionalOnProperty(prefix = "app.cli", name = "enabled", havingValue = "true") @Bean // 스프링 부트 서버가 완전히 켜지기 전에 단 한 번 자동으로 실행 public CommandLineRunner cli(@Value("${spring.application.name}") String applicationName, ToolService toolService, @Value("${app.cli.filter-expression:}") String filterExpression) { return args -> { // 1. 스프링 기본 로그 끄기 (채팅에 방해되지 않도록) LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); context.getLogger("ROOT").detachAppender("CONSOLE"); System.out.println("======================================="); System.out.println("🤖 [" + applicationName + "] CLI 챗봇을 시작합니다!"); System.out.println(" (종료하려면 'exit' 또는 'quit' 입력)"); System.out.println("======================================="); try (Scanner scanner = new Scanner(System.in)) { while (true) { System.out.print("\nUSER: "); String userMessage = scanner.nextLine(); // 2. 대화 종료 조건 (무한 루프 탈출) if (userMessage.equalsIgnoreCase("exit") || userMessage.equalsIgnoreCase("quit")) { System.out.println("대화를 종료합니다. 안녕히 계세요!"); break; } System.out.print("ASSISTANT: "); Iterable<String> chatStream = toolService.stream( "cli", new Prompt(userMessage) ).toIterable(); for (String token : chatStream) { System.out.print(token); } System.out.println(); } } }; } }
 
 
 

✅ 2. RestAPI

ToolController.java
package com.jscode.tool.rest; @RestController @RequestMapping("/tool") public class ToolController { private final ToolService toolService; public ToolController(ToolService toolService) { this.toolService = toolService; } public record PromptBody( @NotEmpty @Schema(description = "대화 식별자", example = "jscode-1234") String conversationId, @NotEmpty @Schema(description = "사용자 입력 프롬프트", example = "안녕하세요, 제주도 날씨 어때요?") String userPrompt, @Nullable @Schema(description = "시스템 프롬프트(선택)", example = "You are a helpful assistant.") String systemPrompt, @Nullable @Schema(description = "채팅 옵션(선택)", implementation = DefaultChatOptions.class) DefaultChatOptions chatOptions ) {} @PostMapping(value = "/call", produces = MediaType.APPLICATION_JSON_VALUE) public ChatResponse call(@RequestBody @Valid PromptBody promptBody) { Prompt prompt = createPrompt(promptBody); // 완성된 프롬프트와 conversationID를 가지고 서비스 메소드 호출 return toolService.call(promptBody.conversationId, prompt); } @PostMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux<String> stream(@RequestBody @Valid PromptBody promptBody){ Prompt prompt = createPrompt(promptBody); return toolService.stream(promptBody.conversationId, prompt); } private static Prompt createPrompt(PromptBody promptBody) { List<Message> messages = new ArrayList<>(); if(promptBody.systemPrompt() != null && !promptBody.systemPrompt().isBlank()){ messages.add(new SystemMessage(promptBody.systemPrompt())); } messages.add(new UserMessage(promptBody.userPrompt())); Prompt.Builder promptBuilder = Prompt.builder().messages(messages); if(promptBody.chatOptions() != null){ promptBuilder.chatOptions(promptBody.chatOptions()); } return promptBuilder.build(); } }