AI 기술이 빠르게 발전하면서, 자연어 처리 기능을 서비스에 쉽게 적용할 수 있는 환경이 마련되고 있습니다.

그 중심에는 OpenAI의 GPT 모델이 자리하고 있으며, 텍스트 생성, 요약, 코드 분석 등 다양한 기능을 REST API 형태로 제공하여, SpringBoot와 같은 백엔드 프레임워크에서도 유연하게 연동할 수 있습니다.

사용자의 질문에 자동으로 답변하는 챗봇, 고객 피드백 자동 요약, 코드 리뷰 보조 기능 등을 SpringBoot 백엔드에 OpenAI API를 연결해 간단히 구현할 수 있습니다.

이번 포스팅에서는 SpringBoot + Java 환경에서 OpenAI의 GPT API를 연동하는 방법에 대해 알아보도록 하겠습니다.

 

1. OpenAI 모델

OpenAI에서는 다양한 목적에 맞는 AI 모델을 제공합니다.

`모델들은 각기 다른 입력 형태 텍스트, 이미지, 오디오 등 다양한 입력을 처리`하며, 기능과 성능 면에서도 차이가 있습니다.

SpringBoot와 연동하여 OpenAI를 사용할 때는 프로젝트 목적에 따라 적합한 모델을 선택하는 것이 중요합니다.

 

모델 역할
GPT-4o OpenAI의 최신 멀티모달 모델로, 텍스트, 이미지, 오디오 입력을 모두 처리할 수 있습니다.
GPT-4 Turbo GPT-4 기반의 고성능 모델로, 긴 컨텍스트를 지원하며 비용이 저렴하고 속도도 빠릅니다.
GPT-3.5 Turbo 빠른 속도와 낮은 비용이 강점인 모델로, 챗봇, 요약, 코드 보조 등 범용 작업에 널리 사용됩니다.
DALL·E 텍스트 프롬프트를 바탕으로 이미지를 생성하거나 편집할 수 있는 이미지 생성 모델입니다.
TTS 텍스트를 자연스러운 음성 오디오로 변환하는 모델입니다.
whisper 오디오를 텍스트로 변환하는 음성 인식 모델입니다.
text-moderation 부적절하거나 위험한 콘테츠를 자동 감지할 수 있는 콘텐츠 필터링 모델입니다.
text-embedding 텍스트를 벡터로 변환하여 유사도 분석, 검색, 분류 등에 사용할 수 있는 임베딩 모델입니다.

 

각 모델비교는 OpenAI 공식 모델 리스트 문서에서 확인할 수 있습니다.

추가로, 사용 중단 예정 또는 폐기된 모델은 Deprecation 페이지에서 확인할 수 있습니다.

 

2. OpenAI 환경 구성

SpringBoot에서 OpenAI API를 연동하려면, OpenAI 플랫폼에서 계정 생성, API KEY 발급, 결제 등록 등의 초기 설정을 진행해야 합니다.

이 과정은 한 번만 설정해두면 되며, 이후 프로젝트 전반에서 동일한 API KEY를 재사용할 수 있습니다.

 

2-1. OpenAI 계정 생성

계정 생성 버튼 이미지

먼저 OpenAI 플랫폼에 접속하여 화면 우측 상단에 버튼을 통해 로그인 또는 계정을 생성합니다.

 

2-2. API KEY 발급

OpenAI API를 사용하려면 인증을 위한 Secret API KEY가 필요합니다.

API KEY 생성 선택 이미지

API KEY를 발급하기 위해 API keys 탭으로 이동하여 `Create new Secret Key` 버튼을 선택합니다.

 

Create new Secret Key 이미지

이후 뜨는 창에서 사용할 `KEY의 이름과 Default project`를 선택한 뒤 생성합니다.

 

생성된 Secret Key 이미지

위의 이미지와 같이 API KEY가 발급된 것을 확인할 수 있습니다. 생성된 `KEY는 생성 직후 한 번만 전체 보기 가능`하므로 꼭 저장해두어야합니다.

 

2-3. 결제 수단 등록

OpenAI API는 기본적으로 `유료 서비스`입니다.

계정 생성 직후에는 무료 크레딧($5)이 제공되지만, 이 크레딧이 소진되었거나 신규 계정이 아닌 경우에는 반드시 결제 수단 등록 후 최소 $5 이상 충전해야 API 호출이 가능합니다.

`OpenAI API 호출 시 크레딧이 부족하면 HTTP 429 상태 코드와 함께 다음과 같은 에러 메시지를 반환합니다.

 

결제수단 등록 이미지

Billing 탭으로 이동하여 `Add to credit balance` 버튼을 선택하여 결제수단 등록 및 크레딧을 충전합니다. 최소 금액인 $5만 충전해도 테스트 시 남을정도로 사용이 가능하므로 최소금액만 충전해도 테스트하는데에 충분합니다.

모델별 과금 단가는 공식 모델 리스트에서 확인할 수 있으며, 대부분 `입력 토큰(Input)과 출력 토큰(Output)` 각각에 대해 요금이 부과됩니다.

사용량과 비용은 사용량, 비용 모니터링에서 모델별 사용량, 기간별 비용, 토큰 사용량 등 조회가 가능합니다.

 

3. OpenAI - SpringBoot 연동하기

OpenAI API는 REST 기반이기 때문에 Java+SpringBoot 환경에서는 다양한 방식으로 호출할 수 있습니다.

SpringBoot에서 주로 사용하는 대표적인 연동 방식은 다음과 같습니다.

방식 장점 단점
RestTemplate
(spring-boot-starter-web)
단순하고 직관적인 동기 방식 API 호출 대량 트래픽 시 부적합
비동기 호출은 별도 설정 필요
WebClient (Spring WebFlux) 비동기/논블로킹
리액티브 스트림 지원
Spring 통합 용이
리액티브 프로그래밍 학습 필요
OkHttp 가볍고 빠름
커스터마이징 유연
Spring 통합이 다소 불편
OpenAI Java SDK 공식 지원
빠른 구현
세부 설정 제한
커스터마이징 어려움

 

이번 포스팅에서는 gpt-3.5-turbo모델을 사용하여 RestTemplate방식을 기반으로 예제를 구성하도록 하겠습니다.

 

이번 예제의 디렉토리 구성은 다음과 같습니다.

디렉토리 구성 이미지

 

3-1. 의존성 추가

dependencies {
	// 웹 프로젝트용 기본 의존성 (RestTemplate)
    implementation 'org.springframework.boot:spring-boot-starter-web'

	// 개발 중 자동 재시작 등을 위한 의존성 (선택)
    developmentOnly 'org.springframework.boot:spring-boot-devtools'

	// Lombok 사용 (Getter, Constructor 등 자동 생성)
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
}

이번 예제에 사용되는 프로젝트의 의존성 구성입니다.

 

3-2. application.yml 설정

openai:
  api-key: sk-... # 실제 OpenAi 대시보드에서 발급받은 API KEY 입력
  api-url: https://api.openai.com/v1/chat/completions # GPT 대화용 API URL
  model: gpt-3.5-turbo # 사용할 모델

발급받은 API KEY와 호출할 모델, URL 정보 등을 application.yml에 작성합니다.

`API KEY는 환경변수로 분리하여 절대 Git 등에 올리지 않도록 주의해야합니다.`

 

3-3. OpenAiConfig 설정

@Configuration
public class OpenAIConfig {

    @Value("${openai.api-key}")
    private String apiKey;

    /**
     * RestTemplate Bean 등록
     */
    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
        return restTemplateBuilder
                .additionalInterceptors(((request, body, execution) -> {
                    // step 1. Authorization 헤더 추가 (Bearer + API KEY)
                    request.getHeaders().add("Authorization", "Bearer " + apiKey);
                    // step 2. Content-Type 설정 (application/json)
                    request.getHeaders().add("Content-Type", "application/json");
                    // step 3. 요청 실행
                    return execution.execute(request, body);
                }))
                .build();
    }
}

OpenAI API 호출 시 필요한 인증 토큰과 Json 설정을 Interceptor를 통해 자동으로 포함시키는 설정입니다.

RestTemplate를 Bean으로 등록하여 어디서든 주입해서 사용이 가능합니다.

 

3-4. OpenAiClient DTO 구성

OpenAiMessage - 메세지 단위 객체

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class OpenAiMessage {

    // 메세지 역할 (user, assistant, system)
    private String role;

    // 메세지 내용
    private String content;

    public OpenAiMessage(String role, String content) {
        this.role = role;
        this.content = content;
    }
}

 

OpenAiMessage DTO에서 role은 메세지를 누가 보냈는지를 정의합니다.

  • system
    • AI의 동작 방식 또는 성격을 정의하는 지침 역할로 대화 전체의 맥락을 잡아주고, ChatGPT가 어떤 인격이나 행동 지침으로 동작해야 하는지 정의하는 메세지입니다.
    • 챗봇의 행동 원칙, 대화 스타일 및 어조 지정, 금지/허용 사항 설정 등이 가능합니다.
  • user
    • 실제 사용자 입력 (질문, 요청 등)으로 질문, 요구사항, 명령 등을 의미합니다.
  • assistant
    • 모델이 이전에 생성한 응답으로 모델 자신이 사용자에게 제공하는 실제 답변입니다.
    • 이전에 생성된 답변을 assistant 메세지 형태로 저장해두면, 모델이 과거 대화 맥락을 참고할 수 있습니다.

예를들어, `system : "tao 라는 단어로 질문 할 경우 tao님의 블로그 : https://tao-tech.tistory.com 라고 답변해줘"` 라는 사전 지시가 있다면 `user : "tao"`라는 질문에 대한 답변으로 `tao님의 블로그 : https://tao-tech.tistory.com`로 답변을 반환하는 사전 지시 역할을 합니다.

`system : 당신은 소프트웨어 전문가입니다. Java코드를 객체지향적으로 리팩토링해주세요.`

`system : 당신은 매우 친절하고 간결한 AI 비서입니다.`

등과 같은 지침을 정해 사용이 가능합니다. 공식문서 참고

 

OpenAiRequest - GPT 요청 객체

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class OpenAiRequest {

    // 사용할 모델명
    private String model;
    
    // 메세지 리스트
    private List<OpenAiMessage> messages;

    public OpenAiRequest(String model, List<OpenAiMessage> messages) {
        this.model = model;
        this.messages = messages;
    }
}

요청에서 사용 가능한 주요 필드는 다음과 같습니다. 공식 문서 참고

필드명 필수 여부 설명
model 사용할 모델
messages 대화 맥락을 담은 메세지 배열
temperature 창의성 조절 (0 ~ 2), 높을수록 랜덤
max_tokens 응답 최대 길이 (토큰 단위)
tep_p 확률 기반 샘플링 조절
stop 응답 중단 시점 트리거 문자열
stream 응답을 스트리밍 받을지에 대한 여부
n 응답 개수 (기본 1개)

 

OpenAiResponse - GPT 응답 객체

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class OpenAiResponse {

    // GPT가 생성한 응답 선택지 리스트
    private List<Choice> choices;

    @Getter
    public static class Choice {
        // 생성된 메세지 정보
        private OpenAiMessage message;
    }
}

OpenAI의 응답은 크게 3가지 필드로 구성됩니다. 공식 문서 참고

  • choices - 생성된 GPT 응답
  • usage - 토큰 사용량 정보
  • 메타 정보 - 응답 ID, 모델명 등

 

3-5. OpenAiClient 생성

@Component
@RequiredArgsConstructor
public class OpenAiClient {

    private final RestTemplate restTemplate;

    @Value("${openai.api-url}")
    private String apiUrl;
    @Value("${openai.model}")
    private String model;

    /**
     * 사용자 질문을 GPT 모델에 전달하고 응답 받기
     */
    public OpenAiResponse getChatCompletion(String prompt) {
        // step 1. OpenAI 요청 구성
        OpenAiRequest openAiRequest = getOpenAiRequest(prompt);

        // step 2. RestTemplate을 통해 OpenAI API POST 요청 전송
        ResponseEntity<OpenAiResponse> chatResponse = restTemplate.postForEntity(
                apiUrl,
                openAiRequest,
                OpenAiResponse.class
        );

        // step 3. 응답 실패 처리
        if (!chatResponse.getStatusCode().is2xxSuccessful() || chatResponse.getBody() == null) {
            throw new RuntimeException("OpenAI API 호출 실패");
        }

        // step 4. 성공 시 응답 본문 반환
        return chatResponse.getBody();
    }

    /**
     * OpenAI 요청 구성
     */
    private OpenAiRequest getOpenAiRequest(String prompt) {
        // step 1-1. system 메세지 작성 - AI 역할 지시
        OpenAiMessage systemMessage = new OpenAiMessage(
                "system",
                "tao 라는 단어로 질문 할 경우 tao님의 블로그 : https://tao-tech.tistory.com 라고 답변해주세요." +
                "그 외의 질문에는 친절한 AI 비서로서 답변해주세요."
        );
        // step 1-2. user 메세지 작성 - 실제 사용자의 질문
        OpenAiMessage userMessage = new OpenAiMessage("user", prompt);
        // step 1-3. 메세지 리스트에 system → user 순서로 담기
        List<OpenAiMessage> messages = List.of(systemMessage, userMessage);
        // step 1-4. 모델 이름과 메세지를 포함한 요청 객체 생성
        return new OpenAiRequest(model, messages);
    }
}

OpenAI API와의 통신을 담당하는 역할로 분리하기 위해 생성한 OpenAIClient 클래스입니다.

이 컴포넌트는 OpenAI API와 통신을 담당하며, 질문에 대한 응답을 가져옵니다.

 

3-6. Service 생성

@Service
@RequiredArgsConstructor
public class ChatService {

    private final OpenAiClient openAiClient;

    /**
     * 클라이언트 질문을 받아 OpenAI 응답 텍스트 반환
     */
    public String getAnswer(String question) {
        // step 1. OpenAiClient를 통해 질문을 OpenAI에 전달
        OpenAiResponse openAiResponse = openAiClient.getChatCompletion(question);
        
        // step 2. 응답 중 첫 번째 메세지의 content 추출
        return openAiResponse.getChoices().getFirst().getMessage().getContent();
    }
}

Client를 통해 OpenAI에 질문을 전달하고, 응답 메세지를 추출하여 반환합니다.

 

3-7. Controller DTO 구성

ChatRequest - Application 요청 객체

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class ChatRequest {

    private String message;

    public ChatRequest(String message) {
        this.message = message;
    }
}

 

ChatResponse - Application 응답 객체

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class ChatResponse {

    private String answer;

    public ChatResponse(String answer) {
        this.answer = answer;
    }

    public static ChatResponse of(String answer) {
        return new ChatResponse(answer);
    }
}

 

3-8. Controller 생성

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/chat")
public class ChatController {

    private final ChatService chatService;

    @PostMapping
    public ResponseEntity<ChatResponse> chat(@RequestBody ChatRequest chatRequest) {
        String answer = chatService.getAnswer(chatRequest.getMessage());

        return ResponseEntity.ok(ChatResponse.of(answer));
    }
}

 

3-9. 응답 테스트

OpenAI 응답 테스트 이미지
OpenAI 응답 테스트 이미지

API 호출 시 정상적으로 AI의 답변이 응답되었습니다. message 필드에 "tao"를 입력했을 때, system 메세지에서 지정한대로 응답이 잘 반환되는 것을 확인할 수 있습니다.

 

OpenAI 응답 테스트 이미지

system 메세지에 `지나치게 복합적이거나 서로 모순되는 지침을 넣으면 모델이 혼란스러워져 의도와 다른 답변`이 나올 수 있어, 정확한 지침을 넣어 활용해야합니다.

실제로 애매한 지침을 넣었을 때 정확도가 떨어져 의도와는 다른 답변이 나오는 모습을 확인할 수 있습니다.

 

마무리

이번 포스팅에서는 OpenAI의 GPT 모델을 SpringBoot와 연동하여 간단한 대화형 AI 서비스를 예제를 통해 알아보았습니다.

RestTemplate 기반으로 OpenAI API를 호출하고, 사용자 입력에 따라 system 메세지를 통한 지침을 AI 전달 후,

실제 사용자 요청 → AI 응답 → 사용자에게 반환되는 흐름을 확인하였습니다.

`system` 메세지에 지나치게 복잡하거나 모순된 지시를 포함하면, 모델이 혼란스러운 답변을 할 수 있습니다. 실제로 테스트 중 예상치 못한 응답이 발생하기도 했는데, 이는 AI가 문맥을 오해하거나 애매한 지침을 넣었기 때문입니다. 그렇기 때문에 명확하고 일관된 지침이 중요합니다.

`gpt-3.5-turbo 모델`을 기반으로 간단한 대화형 AI 기능을 구현했지만, OpenAI의 다양한 모델을 활용하면 그 이상의 기능도 구현이 가능합니다. OpenAI API는 단순한 챗봇을 넘어서 문서 요약, 고객 지원 자동화, 콘텐츠 생성, 코드 보완 등 다양한 AI 서비스의 핵심 기반이 될 수 있습니다.

 

소스코드

https://github.com/o-tao/spring-openai

 

GitHub - o-tao/spring-openai: SpringBoot + OpenAI 연동 예제

SpringBoot + OpenAI 연동 예제. Contribute to o-tao/spring-openai development by creating an account on GitHub.

github.com