Paradox Simulation

728x90
반응형

gpt api를 이용해서 웹페이지 구축 - 백엔드편

Spring Initializr

이 사이트를 들어가서 maven 프로젝트와 dependencies에 Spring WEB, Lombok을 추가한다.

 

프로젝트를 연다.

 

열고나서 간단하게 package는 3개 만든다.

1. config

2. controller

3. service

 

 

우선 config부터 설정한다.

 

파일명에 따라 다르겠지만, 나는 WebConfig.java를 만들었다.

 

WebConfig.java

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("http://localhost:3000")
                .allowedMethods("GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS")
                .allowCredentials(true);
    }
}

이 설정은 추후에 react - spring boot 간에 동작시 포트번호가 달라서 CORS에러가 날건데, 이걸 해결하기 위해서 아주 기초적으로 보안 설정을 없애는 단계라고 생각하면 된다..

allowedOrigins 부분에 localhost:3000을 CORS 허용한다고 보면 된다.

 

CORS에 대한 설명은 다음과 같다.

 

CORS (Cross-Origin Resource Sharing)

웹 브라우저에서 실행되는 애플리케이션에서 다른 도메인의 자원에 접근 할 수 있는 권한을 부여하는 보안 메커니즘임.

 

현재 사용하고있는 브라우저별로 다르겠지만, 보통은 Javascript에서 다른 도메인의 api를 부르는것을 보안상의 이유로 차단하게 된다.

 

하지만 CORS를 허용하게 되면 api를 호스팅하는 서버에서 요청을 허용할 수 있도록 설정하고, 다른 도메인 자원에 접근이 가능하게 해준다.

 

그리고 이제 backend부분에서 controller를 작성해준다.

 

GptController.java

import com.example.chatbotbackend.service.GptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@CrossOrigin
public class GptController {
    private final GptService gptService;

    @Autowired
    public GptController(GptService gptService) {
        this.gptService = gptService;
    }

    @PostMapping("/ask")
    public String ask(@RequestBody String prompt) {
        return gptService.ask(prompt);
    }
}

 

웹페이지를 다뤄보면 알겠지만, 프론트에서 이제 ask로 요청을 보낼거고, 보낸 요청에 대한 답변도 리턴하게 만들었다.

 

다음은 Service다.

 

 

GptService.java

import com.example.chatbotbackend.OpenAIApi;
import org.springframework.stereotype.Service;

@Service
public class GptService {
    private final OpenAIApi openAIApi;

    public GptService() {
        this.openAIApi = new OpenAIApi();
    }

    public String ask(String prompt) {
        return openAIApi.ask(prompt);
    }
}

controller에서 보낸걸 이제 service로 받는 단계이다.

여기서는 OpenAIApi라는 클래스를 만들어서 (유틸 개념으로) 사용하게 될 예정이다.

 

OpenAIApi.java

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import org.json.JSONObject;

public class OpenAIApi {
    private static final String API_KEY = "발급받은 api키";

    public String ask(String prompt) {
        String responseBody = "";
        String formattedPrompt = String.format("다음 질문에 대답해 주세요 또한 답변은 한국어로 해주세요: %s", prompt);

        JSONObject jsonBody = new JSONObject();
        jsonBody.put("prompt", formattedPrompt);
        jsonBody.put("max_tokens", 50);
        jsonBody.put("n", 1);
        jsonBody.put("temperature", 0.7);

        try {
            HttpClient client = HttpClient.newHttpClient();
            HttpRequest request = HttpRequest.newBuilder()
                    .uri(URI.create("https://api.openai.com/v1/engines/text-davinci-002/completions"))
                    .header("Content-Type", "application/json")
                    .header("Authorization", "Bearer " + API_KEY)
                    .POST(HttpRequest.BodyPublishers.ofString(jsonBody.toString()))
                    .build();
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
            responseBody = extractAnswer(response.body());
        } catch (Exception e) {
            e.printStackTrace();
        }

        return responseBody;
    }

    private String extractAnswer(String responseJson) {
        JSONObject jsonObject = new JSONObject(responseJson);

        if (jsonObject.has("choices")) {
            return jsonObject.getJSONArray("choices")
                    .getJSONObject(0)
                    .getString("text")
                    .trim();
        } else {
            System.err.println("Error in API response: " + responseJson);
            return "API 호출 중 오류가 발생했습니다.";
        }
    }
}

여기서 중요한게 openAI사이트에서 키를 하나 발급받아야한다.

 

Account API Keys - OpenAI API

 

OpenAI API

An API for accessing new AI models developed by OpenAI

platform.openai.com

 

해당 사이트를 가게 되면,

 

api keys 탭에 있는 Create new secret key를 발급받으면 된다.

물론 로그인 이후에 사용해야한다.

 

키를 발급 받게 되면 잃어버리지 않게 잘 저장한다.

 

그러면 이제 backend 구성은 끝난것이다.

 

프로젝트를 실행시켜주고,

 

이제 React 구성을 할 시간이다.

 

우선 react가 설치되어있다는 가정하에 진행하겠다.

 

react 프로젝트 생성

npx create-react-app gpt-chat-app
cd gpt-chat-app
npm start

이름을 gpt-chat-app이라고 했지만, 자유롭게 해도 무방하다.

 

완료가 되면 이제 프론트엔드 수정을 위해서 다음과같이 App.js를 수정한다.

 

App.js

import React from "react";
import "./App.css";
import ChatWindow from "./components/ChatWindow";

function App() {
  return (
    <div className="App">
      <ChatWindow />
    </div>
  );
}

export default App;

 

그다음은 App.css를 수정한다.

 

App.css

.App {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  background-color: #f5f5f5;
}

.chat-window {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  height: 80vh;
  width: 60%;
  max-width: 800px;
  background-color: #fff;
  border-radius: 10px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  padding: 16px;
}

.chat-messages {
  display: flex;
  flex-direction: column;
  flex: 1;
  overflow-y: auto;
  padding: 8px;
}

.chat-message {
  margin-bottom: 8px;
}

.user-message {
  align-self: flex-end;
  background-color: #d2ebff;
  border-radius: 10px;
  padding: 8px;
  max-width: 60%;
}

.bot-message {
  align-self: flex-start;
  background-color: #f1f1f1;
  border-radius: 10px;
  padding: 8px;
  max-width: 60%;
}

.chat-input {
  display: flex;
  width: 100%;
  margin-top: 16px;
}

.chat-input input {
  flex: 1;
  padding: 8px;
  border: 1px solid #ccc;
  border-radius: 5px;
  outline: none;
}

.chat-input button {
  background-color: #007bff;
  color: white;
  border: none;
  border-radius: 5px;
  padding: 8px 16px;
  margin-left: 8px;
  cursor: pointer;
  transition: background-color 0.2s;
}

.chat-input button:hover {
  background-color: #0056b3;
}

.clearfix::after {
  content: "";
  display: table;
  clear: both;
}

 

다음은 src폴더 내에 components 폴더를 하나 만들고, 

3가지의 js파일을 만든다.

1. ChatInput.js

2. ChatMessage.js

3. ChatWindow.js

 

ChatInput.js

import React, { useState } from "react";

const ChatInput = ({ onSubmit }) => {
  const [inputValue, setInputValue] = useState("");

  const handleSubmit = (event) => {
    event.preventDefault();
    onSubmit(inputValue);
    setInputValue("");
  };

  return (
    <form onSubmit={handleSubmit} className="chat-input">
      <input
        type="text"
        value={inputValue}
        onChange={(event) => setInputValue(event.target.value)}
        placeholder="Type your message..."
      />
      <button type="submit">Send</button>
    </form>
  );
};

export default ChatInput;

이부분은 입력했을때 텍스트를 전송해주는 부분을 담당한다.

 

ChatMessage.js

import React from "react";

const ChatMessage = ({ message, isUser }) => {
  const messageStyle = {
    marginBottom: isUser ? "16px" : "0",
  };

  return (
    <div
      className={`chat-message ${isUser ? "user-message" : "bot-message"}`}
      style={messageStyle}
    >
      <p>{message}</p>
    </div>
  );
};

export default ChatMessage;

이부분은 이제 Message를 입력하고, 답변을 받았을때 어떤형식으로 보여줄지 스타일 및 div설정을 보여주는 부분이다.

 

제일중요한

ChatWindow.js

import React, { useState } from "react";
import axios from "axios";
import ChatMessage from "./ChatMessage";
import ChatInput from "./ChatInput";

const ChatWindow = () => {
  const [messages, setMessages] = useState([]);

  const addMessage = (message, isUser) => {
    setMessages((prevMessages) => [...prevMessages, { text: message, isUser }]);
  };

  const handleSubmit = async (message) => {
    // 사용자 메시지를 추가합니다.
    addMessage(message, true);

    try {
      // 백엔드 서버와 통신하여 GPT의 응답을 받습니다.
      const response = await axios.post("http://localhost:8080/ask", {
        prompt: message,
      });

      // GPT로부터 받은 응답을 채팅창에 추가합니다.
      if (response.data) {
        addMessage(response.data, false);
      }
    } catch (error) {
      console.error("Error fetching GPT response:", error);
    }
  };

  return (
    <div className="chat-window">
      <div className="chat-messages">
        {messages.map((message, index) => (
          <ChatMessage
            key={index}
            message={message.text}
            isUser={message.isUser}
          />
        ))}
      </div>

      <ChatInput onSubmit={handleSubmit} />
    </div>
  );
};

export default ChatWindow;

이제 설정은 끝났다.

 

spring boot 어플리케이션을 실행하고,

터미널이나 사용한 ide 터미널에서 npm start를 입력해서 프론트엔드를 실행시키면 끝이다.

 

웹페이지에서 localhost:3000을 들어가게되면 리액트로 구현한 내 페이지가 나오게된다.

 

아직 백엔드 부분에서 답변을 제대로 받아서 처리해야하니까, 학습이 덜된건지 아니면 백엔드 기능을 좀 더 손을 봐야하는지는 모르겠지만, 아주 간단한 답변은 제대로 동작한다.

 

청개구리같은녀석...

 

이로써 chat gpt api를 이용해서 react - springboot를 활용한 gpt 웹사이트 구현을 마치겠다.

728x90
반응형
250x250
반응형

공유하기

facebook twitter kakaoTalk kakaostory naver band