Offcanvas

AI / How To / 개발자 / 애플리케이션

챗GPT와 대화하는 자바 앱 만들기

2023.05.31 Matthew Tyson  |  InfoWorld
챗GPT는 재미있고 유용한 대규모 언어 모델(LLM) 대화형 AI다. 워낙 유명해서 다른 소개도 필요 없을 정도다. 여기서는 간단한 자바 프로그램으로 챗GPT API 서비스를 사용하는 방법을 살펴보자. 
 
ⓒ Getty Image Bank
 

챗GPT 시작하기 

가장 먼저 해야 할 일은 API 키를 구하는 것이다. 오픈AI에서 제공하는 챗GPT 무료 버전을 통해 API에 액세스할 수 있다. 3.5 버전은 가입만 하면 무료로 사용할 수 있고, 최신 4 버전으 유료 구독을 해야 한다. 계정을 만들고 로그인하면 새 API 키를 생성할 수 있다. 나중에 이 키를 사용할 것이므로 안전한 곳에 보관한다.
 

새 자바 프로젝트 만들기

이제 새 자바 애플리케이션 만들어 보자. 여기서는 명령줄에서 메이븐(Maven)을 사용한다. <예시 1>의 코드를 사용해 새 프로젝트를 생성한다.

<예시 1> 새 프로젝트 생성 
mvn archetype:generate -DgroupId=com.infoworld -DartifactId=java-gpt -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false -DarchetypeVersion=1.4

/java-gpt 디렉터리에 새 프로젝트의 구조가 만들어진다. 다음 단계로 진행하기 전에 /java-gpt/pom.xml 파일로 이동해 자바 컴파일러와 소스 버전을 11로 설정한다. 이제 <예시 2> 명령을 사용해 테스트할 수 있다. 명령 결과 현재 콘솔에 “Hello, World!”를 출력한다. 이제부터는 이 명령을 사용해 애플리케이션을 실행한다. 

<예시 2> 새 애플리케이션 테스트 실행하기
mvn clean compile exec:java -Dexec.mainClass=com.infoworld.App
 

챗GPT 애플리케이션 설정하기

여기서 만드는 챗GPT 애플리케이션은 데모이므로 기본적인 형태로 유지한다. 사용자에게 입력 문자열을 받은 다음 AI의 응답을 콘솔에 출력한다. 우선 입력을 받는 간단한 App.java 주 클래스를 만든다. URL과 API 키를 정의하면 주 클래스가 이를 ChatBot 클래스에 전달해 실제 작업이 실행된다. <예시 3>에서 App.java 주 클래스를 볼 수 있다. 

<예시 3> App.java 주 클래스 
package com.infoworld;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import org.json.JSONException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class App {
  private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
  public static void main(String[] args) {
    // Set ChatGPT endpoint and API key
    String endpoint = "https://api.openai.com/v1/chat/completions";
    String apiKey = "<YOUR-API-KEY>";
        
    // Prompt user for input string
    try {
      BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
      System.out.print("Enter your message: ");
      String input = reader.readLine();
            
      // Send input to ChatGPT API and display response
      String response = ChatBot.sendQuery(input, endpoint, apiKey);
      LOGGER.info("Response: {}", response);
    } catch (IOException e) {
      LOGGER.error("Error reading input: {}", e.getMessage());
    } catch (JSONException e) {
      LOGGER.error("Error parsing API response: {}", e.getMessage());
    } catch (Exception e) {
      LOGGER.error("Unexpected error: {}", e.getMessage());
    }
  }
}

이 클래스는 간단하다. reader.readLine()으로 사용자의 라인을 읽은 다음 이를 사용해 ChatBot.sendQuery() 정적 메서드를 호출한다. ChatBot은 잠시 후에 살펴보기로 하고, 일단 <YOUR-API-KEY> 토큰을 오픈AI 챗GPT 토큰으로 설정한다. 또한 오픈AI가 오픈AI API를 계속 변경하고 있으므로 엔드포인트 URL https://api.openai.com/v1/chat/completions는 지금은 정확하지만 과거(최근도 포함)의 문서와 다를 수 있다는 점을 유의해야 한다. 오류가 발생한다면 오픈AI API의 엔드포인트 URL이 최신 상태인지 확인하면 된다.
 

ChatBot 클래스 

또한 무슨 일이 일어나는지를 살펴보고 오류를 추적하기 위해 간단한 로거를 구성했다. 이 클래스를 지원하려면 메이븐에 몇 가지 종속 항목을 추가해야 하지만, 우선 <예시 4>의 ChatBot 클래스부터 살펴보자. 편의를 위해 둘 다 같은 패키지 범위에 넣는다. 

<예시 4> 간단한 ChatBot 
package com.infoworld;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.Gson;

public class ChatBot {
  private static final Logger LOGGER = LoggerFactory.getLogger(ChatBot.class);

  public static String sendQuery(String input, String endpoint, String apiKey) {
    // Build input and API key params
    JSONObject payload = new JSONObject();
    JSONObject message = new JSONObject();
    JSONArray messageList = new JSONArray();

    message.put("role", "user");
    message.put("content", input);
    messageList.put(message);

    payload.put("model", "gpt-3.5-turbo"); // model is important
    payload.put("messages", messageList);
    payload.put("temperature", 0.7);

    StringEntity inputEntity = new StringEntity(payload.toString(), ContentType.APPLICATION_JSON);

    // Build POST request
    HttpPost post = new HttpPost(endpoint);
    post.setEntity(inputEntity);
    post.setHeader("Authorization", "Bearer " + apiKey);
    post.setHeader("Content-Type", "application/json");

    // Send POST request and parse response
    try (CloseableHttpClient httpClient = HttpClients.createDefault();
      CloseableHttpResponse response = httpClient.execute(post)) {
        HttpEntity resEntity = response.getEntity();
        String resJsonString = new String(resEntity.getContent().readAllBytes(), StandardCharsets.UTF_8);
        JSONObject resJson = new JSONObject(resJsonString);

        if (resJson.has("error")) {
          String errorMsg = resJson.getString("error");
          LOGGER.error("Chatbot API error: {}", errorMsg);
          return "Error: " + errorMsg;
        }

        // Parse JSON response
        JSONArray responseArray = resJson.getJSONArray("choices");
        List<String> responseList = new ArrayList<>();

        for (int i = 0; i < responseArray.length(); i++) {
          JSONObject responseObj = responseArray.getJSONObject(i);
          String responseString = responseObj.getJSONObject("message").getString("content");
          responseList.add(responseString);
        }

        // Convert response list to JSON and return it
        Gson gson = new Gson();
        String jsonResponse = gson.toJson(responseList);
        return jsonResponse;
      } catch (IOException | JSONException e) {
        LOGGER.error("Error sending request: {}", e.getMessage());
        return "Error: " + e.getMessage();
      }
  }
}

ChatBot은 얼핏 복잡해 보이지만 사실은 간단하다. 가장 중요한 것은 JSON 처리다. 먼저 sendQuery()로 전달된 문자열 입력을 챗GPT가 인식하는 JSON 형식으로 마샬링해야 한다(이 부분도 최근 변화가 있었음). 챗GPT는 지속적인 대화를 허용하므로 사용자 또는 비서 역할이 태그된 메시지 모음을 찾아 이를 대화에서 오가는 응답으로 본다. 전체 대화의 “어조”를 설정하는 데 사용되는 기능도 있지만 이 속성은 일관성 없이 적용된다. 챗GPT가 기대하는 JSON 형식은 <예시 5>와 같다(오픈AI 문서에서 발췌). 

<예시 5> 샘플 챗GPT 제출 형식 JSON
messages:[
  {"role": "system", "content": "You are a helpful assistant."},
  {"role": "user", "content": "Who won the world series in 2020?"},
  {"role": "assistant", "content": "The Los Angeles Dodgers won the World Series in 2020."},
  {"role": "user", "content": "Where was it played?"}
]
 

JSON 응답 설정 

여기서는 하나의 role:user와 입력 문자열로 설정된 콘텐츠가 있는 간단한 메시지 모음을 사용한다. 또한 model과 temperature 필드도 설정했다. model 필드는 사용되는 챗GPT 버전을 정의하는 데 중요하며 새로운(유료) GPT-4를 포함한 여러 옵션이 있다. temperature는 AI의 “창의성(단어 재사용 방지 측면에서)”을 설정하는 GPT 기능이다. 

적절한 JSON을 확보하면 아파치 HTTP 라이브러리를 사용해 POST 요청을 작성하고 본문을 JSON으로 설정한다(post.setEntity() 사용). 예시에서 볼 수 있듯 이 라인으로 auth 헤더에 API 키를 설정했다. 

다음으로, try-with-resource 블록을 사용해 요청한다. 오류가 없다면 응답을 완료한 후 JSON 응답 구조를 탐색해 가장 최근의 message:content를 반환한다. 이제 테스트할 준비가 거의 다 됐다. 우선 <예시 6>과 같이 pom.xml에 필요한 종속 항목을 추가한다. 

<예시 6> pom.xml의 메이븐 종속 항목 
post.setHeader("Authorization", "Bearer " + apiKey);

이제 코드를 실행하면 <예시 7>과 같이 작동하는 모습을 볼 수 있다.

<예시 7> 자바 명령줄 애플리케이션으로 챗GPT에 말하기 
<dependencies>
   <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
   </dependency>
   <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.7</version>
   </dependency>
   <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>2.8.6</version>
   </dependency>
   <dependency>
      <groupId>org.apache.httpcomponents</groupId>
      <artifactId>httpclient</artifactId>
      <version>4.5.13</version>
   </dependency>
</dependencies>
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>1.7.30</version>
</dependency>
<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>1.2.3</version>
</dependency>

전체 애플리케이션 코드는 필자의 java-chatgpt GitHub 리포지토리에서 볼 수 있다. 
 

결론 

여기서 살펴본 것처럼 자바로 챗GPT 클라이언트를 구축하는 방법은 매우 간단하다. JSON 처리가 약간 번거롭지만 그 외에는 여기서 소개한 데모 코드를 쉽게 확장할 수 있다. 첫 연습으로 이 챗GPT 프로그램을 지속적인 대화가 가능한 REPL(Read-Eval-Print-Loop)로 확장해 보는 것도 좋다. 
editor@itworld.co.kr
CIO Korea 뉴스레터 및 IT 트랜드 보고서 무료 구독하기
추천 테크라이브러리

회사명:한국IDG 제호: CIO Korea 주소 : 서울시 중구 세종대로 23, 4층 우)04512
등록번호 : 서울 아01641 등록발행일자 : 2011년 05월 27일

발행인 : 박형미 편집인 : 천신응 청소년보호책임자 : 한정규
사업자 등록번호 : 214-87-22467 Tel : 02-558-6950

Copyright © 2024 International Data Group. All rights reserved.