P3_GPT API로 프로그래밍 AI 웹서비스 만들어보기(spring Boot+React)_5 기능 명세 정의
* 이 포스트는 학습 과정에서 그 내용을 기록한 글이기에 부정확한 정보가 포함될 수 있습니다.
따라서 해당 글은 참고용으로만 봐주시고 틀린 부분이 있다면 알려주시면 감사하겠습니다.
완료된 프로젝트의 url은 http://bit.ly/3J6dkQ1입니다.
❗ 이 포스트에서 진행되는 프로젝트는 Spring boot와 React로 진행됩니다. 따라서 이 글은 기본적으로 두 stack의 기초를 안다는 전제 하에 진행됩니다, 따라서 이에 대해 아예 모르신다면 이 포스트를 보기 힘들 수 있음을 알려드립니다.
* 잘 모르시는 기술은 로그인 필요 없이 이 곳에서 AI에게 물어보세요!
https://github.com/kth1017/project_GptApi_1 이 링크에서 프로젝트 코드를 보실 수 있습니다. 프로젝트 진행 환경은 다음과 같습니다.
IDE : IntelliJ community(이상의 버전 가능), VSCode
Back-end : Java 11, Spring 2.7.9, (build tools: groovy gradle)
Front-end : Chrome 110.0, npm 0.1.0, react 18.2.0
DB : h2 1.4.2
Server : NginX 1.21.4
Deployment : Docker 23.0.1, Docker-Compose 2.10.0
다시 한 번 말씀드리면 이번 포스트는 설명을 위한게 아닌 프로젝트 개요를 적으므로 선행 지식이 없다면 보기 어려우실 수 없습니다. 학습을 위해 보실거라면 다음 지식들에 대한 기초 학습이 이뤄져야 합니다.
1. API, ERD 설계, entity, dto, JPA
2. Spring Boot, Spring MVC(도메인, 리포지토리, 서비스, 컨트롤러)
3. React, MUI 라이브러리
이번 포스트부턴 본격적으로 프로젝트를 진행할 예정입니다. 기본적으로 기초 사항은 모두 숙지하신다는 가정하에 진행합니다. 간단한 설명이 필요하시면 이번 포스트가 아닌 다음 포스트들부터 보시면 됩니다.
Index
1 구현 기능 설계
2 기능 설계시 고려사항
3 client 코드
4 server 코드
5 ERD(DB table - entity) 설계
6 기타 추가사항
구현 기능 설계
이번 프로젝트에서 구현할 기본 기능은 다음과 같습니다.
1. 파파고 api를 이용한 번역 기능
2. Gpt api를 이용한 질문 기능
3. 질문 추천 기능
4. JPA를 이용한 질문&답변 저장 기능
1,2의 경우 기본적으로 처음부터 생각한 기능이므로 설명은 생략하겠습니다. 3의 경우 단순 질문의 경우 추천 카테고리를 받아서 사용자가 질문을 자동 완성할 수 있도록 구현합니다. 질문 추천의 경우 Category를 정하고 Details를 정하면 문장이 완성되도록 만들겠습니다.
4의 경우 서버에 저장될 정보를 ai에게 하는 질문과 받은 답변 두 개로 한정합니다. 파파고로 번역 요청한 문장이나 번역된 문장은 DB만 차지할 뿐 서버에 저장할 큰 이유가 없으므로 저장하지 않고 서버에 로그만 남도록 도메인을 작성할 예정입니다.
예상되는 flow는 다음과 같습니다.
기능 설계시 고려사항
우선 고려사항은 다음과 같습니다.
1. Gpt Api에게 질문을 요청하면 답변이 오는데 평균 3~8초가 소요
2. 서버에 저장할 질문과 답변이 가질 field
3. 이후 로그인 추가시 기능 변경
#1
현재 api 테스트를 통해 파파고 번역의 경우 요청 이후 응답까지 소요되는 시간은 문장 길이가 정말 길지 않은 이상 1초도 되지 않으니 이는 고려 사항이 아닙니다. 그러나 Gpt api에 단순 질문의 경우 영어로 질문할 때 평균 3~8초, 한글로 질문할 때 평균 5~10초가 소요되며 긴 코드를 첨부할 경우 소요 시간은 단순 질문의 2배까지 증가합니다. 따라서 이 점을 고려하여 코드를 설계해야 합니다.
#2
이는 뒤에 entity 설계에서 자세히 언급하겠습니다.
#3
향후 로그인 기능 추가시 즐겨찾기, 질문&답변 내역 등을 추가하는 것이 좋으며, 로그인 기능 추가시 User entity가 추가 되기 때문에 코드 변경시 이를 고려해야합니다.
client 코드
client는 현재 리액트로 작성할 예정이고 코드에 포함될 사항은 다음과 같습니다.
1-1. server에게 papago api 스펙에 맞는 번역할 문장을 post 요청으로 전송
1-2. server로부터 1-1에서 전송한 요청에 대한 응답
2-1. server로부터 추천 질문 Category를 받는 get 요청 전송과 그 응답
2-2. server에게 사용자가 정한 Category를 알려주는 post 요청을 전송
2-3. server로부터 2-2에서 전송한 요청에 대한 응답
3-1. server에게 gpt api 스펙에 맞는 질문할 문장을 post 요청으로 전송
3-2. server로부터 3-1에서 전송한 요청에 대한 응답
4-1. server에게 gpt api 스펙에 맞는 번역할 답변을 post 요청으로 전송
4-2. server로부터 4-1에서 전송한 요청에 대한 응답
* 질문 예시와 도움말에 대한 Dial 기능 구현
여기서 이전 과정에 대한 요청으로 받은 응답은 모두 다음 과정 입력 form에 자동으로 채워지므로(예시/번역 요청이 이뤄지면 응답 내용이 다음 과정의 input의 value가 됨) 받은 응답에서 일부분이 마음에 안 들 경우 수정할 수 있게 모든 과정에서 문장 수정이 가능하도록 구현합니다.
client의 디자인은 MUI(Material-UI) 라이브러리를 사용해서 하겠습니다.
server 코드
서버는 spring boot를 통해 mvc 패턴으로 구현하겠습니다. 전체 골격은 다음과 같습니다,
1. View : Clent로 대신함
* dto : 번역 요청, 카테고리 요청, 질문 요청 3가지에 대응하는 requestDto가 있고 responseDto는 작성하지 않습니다.
2. Controller : 파파고 요청&응답/카테고리 요청&응답/Gpt 요청&응답 세 가지를 처리
3. Service: Controller의 각 메서드에 일대일 대응
4. Repository : Service에서 처리할 메서드 구현
5. domain : entity인 question, answer과 일반 도메인인 ForTrans(번역), RecomQ(질문추천), questionToAnswer(ai질문)
서버 화면은 따로 구현하지 않을 예정이므로 서버의 컨트롤러는 http 통신을 위한 restController 하나만 두겠습니다.
dto의 경우 domain, 특히 entity를 서비스단에서 직접 생성하거나 set하지 않기 위해 requestDto를 도입하며 get을 열어두고 프로젝트 규모가 작으므로 responseDto는 따로 두지 않겠습니다.
Service의 경우 도메인 모델 패턴을 도입하여 직접적인 비즈니스 로직을 처리하지 않도록 구성하고 thread-safe을 위해서 요청~응답 사이를 한 transaction안에서 해결되도록 구성합니다. 프로젝트 규모상 서비스는 interface를 생략하고 구현체만 직접 작성하겠습니다.
Repository의 경우 entity는 JPARepository를 상속하는 interface만 존재하도록 작성하고 일반 도메인의 경우 inteface를 사용하고 구현체에 메서드가 작성되도록 구현하며 일반 도메인도 기능 추가시 entity 승격 가능성이 있으므로 간단한 map store를 사용하겠습니다. 단, map은 thread-safe를 위해 concurrentHashmap을 사용하며 저장되는 id는 null방지를 위해 Long타입을 사용합니다.
Domain은 entity인 question과 answer과 일반 도메인인 ForTrans, RecomQ, questionToAnswer로 작성됩니다. 질문과 답변 사이 시간이 소요되므로 DB에 불필요한 갱신 쿼리를 날리지 않기 위해 entity 사이에서 비즈니스 로직을 가진 questionToAnswer 중계 도메인을 하나 작성했습니다.
ERD(DB table - entity) 설계
Entity인 Question과 Answer은 일대일 관계로 객체간 관계 및 프로퍼티는 아래와 같습니다.
이를 매핑한 테이블은 다음과 같습니다.
이런 설계는 질문 이후 답변을 받기까지 소요되는 시간을 고려한 것으로 question 생성 이후 answer이 생성될 때 question_id를 FK로 갖게끔하면 필요없는 갱신 쿼리를 없앨 수 있습니다. 객체는 JPA를 통해 DB로 영속화되도록 코드를 작성할 것이며 이후 포스트에서 다시 언급하겠습니다.
기타 추가사항
우선 이번 프로젝트에선 1차 완성시에 로그인 기능을 넣지 않을 예정입니다. 로그인 기능은 제가 리액트에 대해 더 공부한 다음 추가할 예정이므로 로그인 기능이 추가될 경우 user entity가 반드시 추가되어야 하며 그에 따른 다른 기능들도 스펙이 변경되어야하므로 이를 고려하여 클래스간 연결을 느슨하게 설계했습니다.
이후 빌드 및 배포는 Docker+AWS EB를 이용할 생각인데 배포 자동화 여부도 결정하지 않았기에 docker에 대한 학습이 마무리되면 이 부분은 수정해서 작성하겠습니다. 아무래도 다음 포스트는 지금 학습 중인 docker 관련 내용일 것 같습니다.
+ 추가) 빌드는 Docker, 배포는 AWS EC2+RDS, 웹 서버로는 NginX를 사용해서 EC2에 docker compose를 통해 배포하였습니다. 배포 DB는 RDS의 mariaDB로 변경했습니다. 배포 자동화는 이후 필요하다면 진행할 예정입니다. 원래 빌드, 배포를 EB로 한 번에 하려했으나...
* 배포 방식을 바꾼 이유는 다음 링크 참조 https://fadet-coding.tistory.com/83