[Bookg-Bookg] AI를 이용한 맞춤형 동화 제작 서비스

2024. 2. 5. 22:03프로젝트/북그-북그(BookgBookg)

소개

스마트폰이 생기고 책보다 재미있는 유튜브가 판을 치는 요즘 유아기의 아이들도 지루한 책 보다는 유튜브에서의 핑크퐁같은 키즈 채널 영상을 자주 본다.

이러한 점이 문제점은 아니지만 책을 아예 읽지 않는 것 보다는 조금이라도 더 흥미를 붙여서 그림책이라도 읽게 하는 것이 어떨까? 라는 생각이 들었다.

그래서 우리 프로젝트 팀이 고민해 본 결과 아이들이 기존 동화책(흥부와 놀부, 신데렐라 등)의 주인공이 되어서 직접 선택을 하며 이야기를 만들어가며 새로운 내용의 동화책을 만들어 보는 것은 어떨까? 라는 결론이 나오게 되었다.

 

우리 서비스의 주요 특징은 다음과 같다.

  1. 맞춤형 이야기 기본 동화책의 배경을 기반으로, 사용자의 선택에 따라 맞춤형 동화책을 제작한다.
  2. 친근한 인터페이스 직관적이고 사용하기 편리한 인터페이스로 아이들이 즐겁게 동화책을 꾸밀 수 있다.

북그북그(bookg-bookg)는 아이들에게 자신의 이야기를 통해 창의적인 표현의 기회를 제공하여 상상력을 키울수 있도록 도울 수 있을것이다.. 라는 취지로 개발하였다.

 

서비스 전체 과정

 

1. 첫 로그인 화면

 

 

 

2. 메인 화면

오른쪽 아래의 책 생성 버튼을 누르면 기본 정보 입력 화면으로 이동한다.

3. 기본 정보 입력 화면

주인공의 이름, 성별, 나이, 언어를 선택 후 배경이 될 동화책을 선택한다.

4. 이야기 선택 화면

두 가지의 서로 다른 흐름의 이야기 중 하나를 선택한다.

이 과정을 6번 반복하여 총 6페이지의 책을 만들어낸다.

마지막에는 동화책의 제목을 짓게 된다.

5. 동화책 제목 생성 화면

이 과정이 완료가 되면 새롭게 만든 책이 생긴다.

6. 책 생성 후 메인 화면

생성 한 책을 조회 해보자

7-1. 책 읽기 화면(한국어)
7-2. 책 읽기 화면(영어)

사용자는 자신의 책을 한글 또는 영어로 볼 수 있고, 각 페이지 오른쪽 아래 스피커 버튼을 누르게 된다면 각 언어에 맞게 책을 읽어줄 수도 있다.

8. 동화책 공유 창

메인 화면에서 각 책의 메뉴에서 쓰레기통 버튼 옆 공유 버튼을 누르면 이메일을 통해 생성한 동화책을 공유를 할 수 있게하는 창이 뜬다.

 

메인 기술 스택

위 시스템 아키텍처에 대한 기술들의 사용 이유는 이와 같다.

Front-end

React

React는 현재 프론트엔드 시장에서 가장 높은 점유율을 가지고 있고, 호환성이 좋으며, 재사용성이 좋다. 그리고 가장 큰 이유는 팀원들이 사용해본 경험이 있는 라이브러리라서 선정을 하게 되었다.

Tailwind

Styled-component와 고민을 하다가, 팀원들 모두 새로운 css의 프레임워크를 사용해보고 싶었고 변수명을 정할 필요가 없는 큰 장점이 있다. 결정적으로 요즘 Tailwind 사용량이 증가함에 따라 경험해보고싶어 사용하게 되었다.

Axios

fetch 라는 비슷한 도구가 있지만 axios를 선정한 이유는 편리한 api 기능을 제공하고, promise 기반으로 비동기 작업을 처리하는데 있어서 더 직관적이고 편리한 방법을 제공하기 때문에 사용하게 되었다.

Recoil

상태 관리 라이브러리 중 하나로 Redux라는 비슷한 라이브러리가 있지만 Recoil은 상태를 단일 원척으로 중앙 집중적으로 관리를 하며 직관적이면서 간단한 구조를 가지기 때문에 사용하게 되었다.

i18n

i18n 이라는 프레임워크는 국제화 된 프레임워크이다. 동화책을 번역하는 과정에서 언어를 감지하고 번역을 보여주기 위해서 사용하게 되었다.

Back-end

Django

Django 프레임 워크는 많은 코딩 없이 프로그램을 완성시킬 수 있어서 빠른 개발이 가능하고, 초보자가 쉽게 익힐 수 있는 프레임 워크이다. 그래서 선택하였다.

Django Channels

사용자의 초기 정보를 입력한 것을 ChatGPT에게 보내거나 ChatGPT에서 Stream으로 전송되는 글자들을 받아내고, ChatGPT에게서 받은 글을 선택하고 다시 보내는 과정을 위해서 실시간 양방향 데이터 통신을 위해 Django Channels를 사용하였다.

RabbitMQ, Celery

해당 기술들 을 사용한 이유는 ChatGPT 에게서 글을 받아오고 그 글을 토대로 오디오(TTS — Naver)와 그림(DALL-E-3)을 생성하고 DB에 저장하기 위해서는 비동기 적으로 처리를 해서 DB 서버 에서의 직접적인 응답을 기다리지 않고 사용자가 애플리케이션을 계속 사용할 수 있도록한다.

S3 Bucket

S3는 웹 기반 애플리케이션에서 직접적으로 사용될 수 있어서 쉽게 웹 콘텐츠를 호스팅 하거나 사용자 업로드를 처리할 수 있다. Bookg사용자가 선택한 글에 맞는 그림과 오디오를 저장하기 위해 사용하였다.

GPT는 동기 / DALL-E, TTS 비동기

저희 북그-북그의 그림은 DALL-E-3가 그림을 생성하고, TTS는 Naver CLOVA에서 사전 녹음된 목소리를 사용하여 전용 엔진이 텍스트를 음성으로 변환하고 사용자에게 들려주는 방식으로 생성이 된다.

 

북그-북그 서비스의 책 생성 로직에 대해서 설명하자면, Chat-GPT에게 두 가지의 서로 다른 이야기를 받고 사용자가 선택을 하게 된다면, 사용자가 선택한 그림과 음성이 생성 되어야 한다.

 

만약 비동기로 하지 않았다면 이야기를 선택하고 그림과 음성 생성이 될 때 까지 사용자는 기다려야 할 것 이다.

 

그래서 사용자가 기다리지 않고 다음 이야기를 ChatGPT에게 받을 수 있도록 백그라운드에서RabbitMQ에게 그림과 음성을 만드는 메세지를 Celery에게 그 메세지 큐를 전달하여서 생성이 되도록 하였다.

 

북그북그(bookg-bookg)는 GPT는 동기, DALL-E와 TTS는 비동기적으로 사용했다.

시퀀스 다이어그램

 

시퀀스 다이어그램을 보았을 때, GPT는 책의 스토리를 생성하는 과정 중 끊기지 않게 하기위해 동기적으로 사용한 반면, DALL-E와 TTS는 단순히 스토리를 생성하는 것에 비해 생성시간이 오래 걸리기 때문에 그림과 TTS의 확인은 책을 만드는 과정이 아닌 다 만들고 나서 “나의 도서관”에서 확인하기에 비동기적으로 사용했다.

 

북그북그(bookg-bookg)에서는 GPT를 동기적으로 사용하여 책의 스토리를 생성하는 과정에서 끊김 없는 사용자 경험을 제공한다. 이는 사용자가 입력한 텍스트에 대해 실시간으로 응답을 받아 책의 흐름을 자연스럽게 이어갈 수 있게 하였다.

 

반면, DALL-E와 TTS는 이미지 생성 및 음성 생성에 필요한 시간이 상대적으로 오래 걸리기 때문에 비동기적으로 처리했다. 또한 이미지와 음성은 책이 생성된 후 “나의 도서관”에서 확인할 수 있게 되어 사용자의 대기 시간을 최소화했다.

 

이러한 접근은 사용자가 스토리 생성에 집중하면서도 책의 완성된 버전을 확인할 때까지 기다리지 않고 다른 작업을 수행할 수 있도록 하여, 사용자에게 효율적이고 중단 없는 경험을 제공하는 데에 중점을 두었다.

 

Prompt — ChatGPT, DALL-E-3

ChatGPT

북그-북그의 동화책 생성 방식은 초기 정보를 기반으로 두 가지의 서로 다른 방향의 이야기를 사용자에게 제시를 하고, 사용자가 선택을 하면 선택한 내용의 뒤에 이어지는 두 가지의 서로 다른 방향의 이야기를 제시하는 것이었다.

 

저희가 원하는 응답은 이와 같이 그저 정말 아무런 설명 없이 두 가지의 서로 다른 이야기의 문장만 나오게 하는 것이었다.

 

쉬울 것 같다고 생각했지만 역시나 프롬프팅 부분에서 생각보다 많은 고민을 하게 되었다.

 

처음은 ChatGPT 웹사이트에서 프롬프팅을 진행 하였는데요! 역시 원하는 결과가 나오지 않고, 쓸데 없는 설명 후 문장을 출력한다던가 갑자기 어떤 흐름으로 이야기를 진행할지 역으로 질문을 하는 그러한 응답이 있었다.

# 첫 프롬프팅

이야기를 써 주시고, 다음 이야기를 이어나갈 서로 다른 이야기의 내용 2개의 선택지를 제안해 주세요.
답변에 따라 이야기가 바뀝니다. 제가 선택을 하기 전까지 기다려 주세요. 선택을 하면, 이야기를 계속 이어나가 주세요.
이 단계를 반복하며, 7번의 전환 후에 해피엔딩으로 이야기를 마무리합니다.

[주요 이야기]

주제: 피노키오

주인공: 김진용

[요구 사항]

이것은 한국과 미국의 5-7세 아이를 위한 한국어&영어 교육용입니다. 한국어로 반환 해주셨으면, 그 다음에 영어로도 반환 해주세요.

각 이야기는 약 100단어 길이입니다.

기본 300 단어만 사용합니다."

## "-----" 아래의 형식으로만 반환해주세요

@ 내용1(한국어로)
@ 내용2(한국어로)
# content(영어로)
# content(영어로)

위 프롬프트를 조금씩 고쳐가며 프롬프팅 테스트를 진행하고 고쳐가면서 점점 내가 원하는 응답이 나오게 되었다.

 

이 과정에서 제일 어려웠던 부분이 내가 응답 받을 형식인데, 역시 내가 원하는 결과가 나오기 쉽지 않았다. 쓸데 없는 설명이 나오는 결과가 너무 많이 나오게 되었다.

 

심지어 요청으로 “쓸데없는 설명을 하지마”라고 했는데도 오히려 더 해버리는 상황이었다.

여러 블로그를 참고하며 프롬프팅에 대해서 공부하면서 알아낸 것이 “하지마”라고 하면 할 수록 즉, “do not” 형태를 사용할수록 하지말라고 한 것을 더더욱 해버린다는 것을 알아 내었다.

 

예를 들면 “쓸데없는 설명을 하지마”라고 하기보단 “너는 그저 문장만 출력해줘” 이런 식으로 프롬프팅을 해야하는 것이었다.

 

수많은 수정과 더 많은 자세한 묘사를 하였더니 웹 사이트 ChatGPT에서는 위와 같은 대답이 10번 중 7정도 내가 원하는 응답이 나오게 되었다.

# Example
해당 문장을 '@'나 '#' 바로 뒤에 오도록 형식을 따라 주세요.
# 내가 원하던 응답

@한 번은 김진용이 학교에서 친구들과 함께 피노키오 이야기를 읽었습니다. 이야기 속에서 피노키오는 거짓말을 하면 코가 점점 길어진다는 것을 알게 되었습니다. 김진용이는 우연히 학교에서 자신의 친구와의 약속을 어긴 후 거짓말을 하게 됩니다. 그 순간, 김진용의 코도 점점 길어지기 시작합니다. 김진용은 스스로 거짓말을 반성하고, 마음을 바로잡아야만 피노키오처럼 정직한 사람이 될 수 있다는 것을 깨닫고 학문에 집중하게 됩니다.
@One day, Kim Jinyong read the story of Pinocchio with his friends at school. In the story, he learned that Pinocchio's nose would grow longer if he told a lie. One day, Kim Jinyong accidentally broke his promise with his friend and told a lie. At that moment, his own nose started growing longer just like Pinocchio's. Kim Jinyong realized that he needed to reflect on his lies and correct his behavior in order to become an honest person like Pinocchio. He then focused on his studies and became a better person.
#김진용이는 피노키오의 이야기를 듣고 자신도 마법사에게 부탁해 나무로 만들어진 인형이 살아나게 되었습니다. 하지만 이 인형은 말을 할 수 없었습니다. 김진용은 인형인 피노키오와 함께 한국어와 영어를 배우기 시작했습니다. 김진용은 피노키오와의 대화를 통해 언어 실력을 향상시키고, 친구들과 함께 즐겁게 대화할 수 있게 되었습니다. 피노키오와 김진용은 서로에게 도움을 주고 받으며 더욱 더 친구가 되어갔습니다.
#After hearing the story of Pinocchio, Kim Jinyong asked a magician to bring a wooden doll to life, just like Pinocchio. However, the doll couldn't speak. Kim Jinyong started learning Korean and English with Pinocchio so they could communicate with each other. Through their conversations, Kim Jinyong improved his language skills and became able to have enjoyable conversations with his friends. Pinocchio and Kim Jinyong helped each other and became even closer friends.

이렇게 프롬프팅 부분에서 해결이 되었고, 다음 스텝으로 Django 내에서 ChatGPT API를 호출해서 프롬프팅을 할 때 문제가 일어났다.

 

완성된 프롬프트를 가지고 본격적으로 클라이언트 서버와 연결을 해서 테스트를 해보았는데 자꾸만 처음부터 시작하는 일이 일어났다....

그래서 또 ChatGPT API 와 ChatGPT 웹사이트의 차이점을 알아보니 ChatGPT API 는 이전 대화 기록을 저장하지 않는다는 사실을 알게 되었다.

 

그래서 저희는 이전 대화 정보를 저장할 수 있도록 book_content 리스트를 생성해서 저희가 ChatGPT에게서 받은 두 가지의 이야기 중 하나를 선택하면 그 선택한 이야기를 book_content 리스트에 저장하여서 다음 페이지의 이야기로 넘어갈 때 프롬프트에 book_content 리스트를 건네주며 “book_content의 이야기에 이어지는 서로 다른 두 가지의 이야기를 줘” 라는 식으로 프롬프팅을 진행해서 ChatGPT가 아닌 저희 자체적으로 대신 이전 대화 기록을 저장할 수 있게 하였다.

 

하지만 ChatGPT API 또한 완벽하지 않아서인지 10개 중 2~3개의 응답은 저희가 원하는 응답이 나오지 않았다.

 

저희의 최선으로써는 원하지 않는 응답이 나올 확률을 조금이나마 줄이는 방법으로 프롬프팅을 계속해서 마지막까지 진행하였다.

 

북그-북그(Bookg-Bookg) 프로젝트와 다른 프로젝트를 개발 하더라도 이와 같은 응답 받은 것을 저장하고 싶다면 리스트 생성 후 저장하는 것도 생각해보길 바란다.

DALL-E-3

다음으로 DALL-E 프롬프팅에서도 ChatGPT만큼은 아니지만 조금 고민을 해야하는 모델이었다.

 

초기에는 사용자가 선택한 글만 프롬프트에 적어서 그림 생성 요청을 하였더니 그림이 이쁘긴 한데 가끔 무서움을 느끼게 하는 그림체로 나오게 되었습니다. 그리고 등장 인물들이 다음 페이지에서도 같은 등장인물이 나와야하는데 모습이 모두 달라졌다.

 

이러한 점들은 어떻게 극복하였는지 서술하자면, 그림체 부분은 맨 처음에는 “귀엽게 그려줘”라는 간단하게 프롬프팅을 했는데 귀여운 그림체가 나오는 확률이 많아졌지만 여전히 어색한 그림체가 많이 나오게 되었다.

 

검색을 해서 알아보니 생각보다 많이 자세하게 묘사를 해야한다는 것을 알아내었다.

그래서 저희는 더더욱 자세하게 “귀엽게, 2D 만화같이, 아이들이 좋아하는 그림체, 메이플 스토리 그림체” 등으로 프롬프팅을 진행하였더니 전체적으로 그림이 초반 프롬프팅보다 훨씬 많이 저희가 원하는 그림체로 개선이 되었다.

 

그리고 아쉽게도 등장 인물들이 다음 생성되는 그림에도 같은 등장 인물들이 그려져야 했는데 DALL-E-3에서는 DALL-E-2 버전과 달리 현재에서는 기존 그림을 첨부해서 편집할 수 있는 기능이 없어서 그러한 점은 극복하지 못하였다.

 

그래서 최대한 그림체라도 일관성이 있도록 프롬프팅을 진행하게 되었다.

페이지 1
페이지 2

역시 DALL-E-3 프롬프팅에서도 저희가 원하는 그림체인 그림이 나오지 않는 경우가 있거나, 글 내용 중에 동화 제목 예를 들면 “신데렐라”와 같은 키워드가 들어간 글의 그림은 정책으로 인해 생성되지 않는 경우도 있었다.

 

이러한 점도 저희가 계속해서 잘 못 나오는 경우의 확률을 줄이는 방향으로 프롬프팅 개선을 진행하였다.

 

 

이렇게 6주동안의 프로젝트 과정을 거치면서 먼저 전체 개발 프로젝트 프로세스를 알게 되었고, 동기/비동기 개념에 대해 조금 알게 되었고, 프롬프트 작성 법을 터득하게 되면서 많은 것을 얻게 되었다..

이렇게 전체 개발 과정을 경험하고 나니까 더더욱 개발에 대한 흥미가 붙게 되는 것 같다.

앞으로도 이러한 프로젝트 경험 기회가 있다면 용기를 내어서 도전하고 싶다.