노트버디 개발기 3: 그림 한 장에서 성격 있는 친구가 되기까지

TL;DR

  • 참고 그림을 분석하는 단계와 새 초상화를 그리는 단계는 따로 요청합니다. 현재 Anna 앱에서는 그림에서 읽어낸 특징을 글로 전달해 친구를 그리고 있습니다.
  • 처음 성격은 AI가 만들고, 그 뒤의 변화는 실제로 돌보고 모험한 기록을 바탕으로 코드가 계산합니다.
  • 진화 조건을 채우면 세 가지 방향 중 하나를 고를 수 있습니다. AI는 설명과 외형을 제안하고, 성장 조건과 결과는 게임 규칙에서 확인합니다.

들어가며

2편에서는 Python 게임 규칙을 Anna 화면과 저장소에 연결한 과정을 소개했습니다. 이번에는 앱의 주인공인 친구를 어떻게 만들었는지 이야기해보겠습니다.

노트버디에서 꼭 해보고 싶었던 건 제가 그린 그림에서 친구가 태어나는 경험이었습니다. 잘 그린 그림이 아니어도 괜찮고요. 작은 낙서에 있던 특징이 캐릭터에 남아 있고, 말을 걸면 자기만의 말투로 답해주면 좋겠다고 생각했습니다. 😊

노트버디 그림 입력 창에서 새싹 모양 친구를 그린 화면

README에 사용하는 실제 앱의 그림 입력 화면입니다. 직접 그리거나 그림과 사진 파일을 불러올 수 있습니다. 글은 Anna 1.4.13의 흐름을 기준으로 적었고, 이미지는 저장소에 보관된 데모와 진화 테스트 캡처를 사용했습니다.


먼저 그림에 무엇이 있는지 살펴봅니다

파일을 고르자마자 AI를 호출하지는 않습니다. 먼저 미리보기로 확인하고, 사용자가 친구를 만나겠다고 요청하면 그때 분석을 시작합니다.

친구를 만나는 과정을 순서대로 보면 이렇습니다.

  1. 참고 자료 준비: 캔버스 그림이나 업로드한 이미지를 앱에서 사용할 PNG로 정리합니다.
  2. 이미지 분석: AI가 몸의 형태와 색, 무늬, 팔다리처럼 보이는 특징과 종족, 속성 후보를 돌려줍니다.
  3. 친구 생성: 응답 형식과 허용된 값을 확인하고 게임 규칙을 거쳐 친구를 저장합니다. 분류가 불확실하면 사용자에게 확인을 받습니다.
  4. 초상화 생성: 읽어낸 특징을 바탕으로 새 그림을 요청하고, 결과 파일을 친구의 기록에 연결합니다.
  5. 초기 성격 생성: 같은 특징과 이름을 참고해 성격, 말투, 작은 습관을 만들고 저장합니다.

여기서 신경 쓴 건 원래 몸의 형태를 함부로 바꾸지 않는 것이었습니다. 팔이 없는 캐릭터인데 갑자기 팔이 생기면 원래 그림과 느낌이 달라지겠죠. 반대로 사진에서 다리가 잘려 보이지 않는다고, 원래 다리가 없는 캐릭터라고 판단해서도 안 되고요.

이런 차이를 프롬프트에 적어뒀습니다. 다만 지시를 넣었다고 모든 결과가 정확하게 나오는 건 아니라서, 생성 결과는 별도로 확인할 필요가 있습니다.

아래는 실제 데모 친구인 Mori의 참고 그림과 생성 결과입니다.

참고 그림 생성된 Mori의 초상화
둥근 몸과 작은 팔다리, 잎 두 장을 가진 Mori의 참고 그림 Mori의 참고 특징으로 Anna에서 생성한 새싹 캐릭터 초상화

Anna의 이미지 생성 기능을 거쳐 만든 데모입니다. 이번 글을 위해 새로 그린 이미지는 아니고요. 원본과 생성 결과, 사용한 특징은 데모 출처 기록에 남아 있습니다.


그림을 읽는 것과 다시 그리는 건 나눠서 봐야 했습니다

이미지를 넣고 새 캐릭터가 나오면 전부 image-to-image처럼 보일 수 있는데요. 실제로 어떤 API에 무엇을 보내는지 구분해서 확인해야 했습니다.

기능 하는 일 현재 Anna 노트버디에서의 사용
이미지 이해 그림을 보고 특징을 글로 설명합니다. llm.complete에 이미지와 질문을 함께 전달합니다.
이미지 생성 요청한 내용을 새 그림으로 그립니다. 분석한 특징을 텍스트로 image.generate에 전달합니다.
참고 이미지 기반 생성과 편집 생성 모델에 원본 이미지도 직접 전달합니다. 현재 이 앱의 초상화 생성 경로에서는 사용하지 않습니다.

지금 앱에서 사용하는 흐름은 원본 이미지 → 관찰한 특징 → 새 그림입니다. 생성 모델이 원본 픽셀을 직접 받아 고치는 방식은 아니라서, 생김새나 구도가 완전히 같다고 보장하기는 어렵습니다. 실제 연결 방식이 궁금하시면 그림 분석과 생성 모듈을 보셔도 좋겠습니다.

이 부분을 확인하면서 Anna 포럼의 공식 답변도 참고했습니다. 2026년 9월 20일 답변에서는 당시 llm.complete의 이미지 블록이 모델에 도달하기 전에 누락되는 버그를 인정하고, 호스트 1.1.0-beta.169에서 수정 배포를 안내했습니다.

이건 이미지가 분석 모델에 제대로 전달되는지에 관한 답변입니다. 이미지 생성 API에서 참고 이미지를 직접 받아 편집하는 기능까지 같은 이야기로 받아들이면 안 되겠더라고요.

사진도 입력할 수 있지만, 여기서 만드는 성격은 가상 캐릭터를 위한 설정입니다. 사진 속 실제 사람이 누구인지, 어떤 성격인지 판별하는 기능은 아닙니다.


친구마다 말투와 작은 습관도 만들어봤습니다

그림이 생긴 뒤에는 초기 성격을 저장합니다. 앱을 열 때마다 성격이 달라지면 같은 친구와 지내는 느낌이 약해질 것 같아서, 처음 만든 설정을 남기고 계속 참고하도록 했습니다.

성격에는 짧은 소개와 말투, 작은 습관이 들어갑니다. Mori의 데모 기록에는 자연을 조심스럽게 관찰하는 성격과 부드러운 말투가 담겨 있는데요. 생각할 때 머리의 잎이 균형을 잡도록 고개를 기울이는 습관도 있습니다. 실제 행동을 관찰한 기록은 아니고, 캐릭터를 위해 생성한 설정입니다.

성향은 아래 네 가지로 나눠봤습니다.

성향 표현하려는 차이
호기심 새로운 곳과 일을 얼마나 궁금해하는지
친화성 상대에게 얼마나 다정하게 다가가는지
활동성 얼마나 활발하게 움직이려 하는지
신중함 낯선 상황에서 얼마나 조심스럽게 행동하는지

이후에는 실제로 돌보고 모험한 기록을 반영합니다. 산책과 탐험은 호기심에, 먹이를 주거나 함께 노는 경험은 친화성에 반영하는 식입니다. 성격 계산 코드에서 초기 성향과 누적 행동을 바탕으로 변화를 계산하고, 변화 폭에도 한도를 뒀습니다. 한 번 놀아줬다고 갑자기 전혀 다른 성격이 되지는 않도록요.

여기서 대화로 “이제 용감해져”라고 말했다고 신중함 수치가 바로 바뀌는 건 아닙니다. 대화는 대화의 맥락으로 사용하고, 성향 수치는 게임에 기록된 행동을 따라 바뀝니다. 초기 성격을 만들 때도 전투 능력치를 임의로 올리지는 않습니다.


대화할 때는 어떤 기억을 참고할까요?

채팅에는 현재 친구의 상태와 저장된 성격, 최근 대화를 함께 전달합니다. 현재 Anna 구현에서는 응답을 만들 때 참고할 최근 메시지를 최대 12개로 제한하고 있습니다. 예전에 나눈 모든 말을 영구적으로 기억하는 방식은 아닙니다.

말투와 현재 성향은 참고하되, 기록에 없는 과거를 꾸며내거나 게임 행동을 실행했다고 말하지 않도록 지시했습니다. “밥 먹자”라는 말에 답하는 것과 먹이 버튼으로 상태를 바꾸는 것도 구분했고요. 실제 대화 요청과 대화 지시문에 이 내용을 넣어뒀습니다.

물론 프롬프트만으로 답변을 항상 정확하게 만들기는 어렵습니다. 그래서 친구가 어떤 말을 하더라도 그 문장이 코인이나 경험치를 직접 바꾸지는 못하도록 했습니다.


함께한 경험을 진화에도 담아보고 싶었습니다

처음에는 레벨이 오르고 새로운 그림을 얻는 것에 관심이 많았습니다. 그런데 계속 만들다 보니 어떻게 돌봤고 어떤 모험을 했는지도 진화에 드러나면 좋겠다는 생각이 들었습니다.

현재 첫 진화에는 레벨 10, 돌봄 10회, 산책 3회라는 조건이 있습니다. 조건을 채운 뒤 진화 방향을 요청하면, AI가 현재 외형과 성격, 돌봄과 모험 기록, 이전 진화 기억과 최근 대화를 참고해서 세 가지 선택지를 설명해 줍니다.

모찌의 첫 진화에서 기존 모습을 이어 가거나 바람·자연 속성 방향으로 성장하는 세 가지 선택지를 보여 주는 한국어 화면

README에 수록된 실제 진화 테스트 화면입니다. 앞에서 본 Mori와는 다른 친구인 ‘모찌’의 모습인데요. 레벨, 돌봄, 산책 조건과 세 가지 진화 설명, 새로운 외형 생성 선택을 볼 수 있습니다.

선택지는 게임 규칙에서 허용하는 경로를 먼저 준비합니다. AI는 그 안에서 이름과 이유, 외형 설명을 작성하고, 반환된 경로 ID도 코드에서 확인합니다. 첫 번째 방향은 현재 속성을 유지하고, 나머지는 허용된 다른 속성으로 이어지도록 했습니다.

사용자가 하나를 고르면 진화 결과를 먼저 게임 상태에 저장합니다. 새 외형 생성을 선택했다면 그다음에 그림을 요청하고요. 그림 생성에 실패했다고 이미 저장한 성장을 잃거나 다른 친구로 바뀌지는 않습니다. 그때는 이전 그림을 유지합니다. 이미지를 받았는데 저장만 실패한 경우라면 새로 그리지 않고 저장만 다시 시도합니다. 관련 처리는 진화 모듈에 정리해 뒀습니다.


다시 들어왔을 때도 같은 친구가 있어야 하니까요

이 흐름은 실제 Anna AI와 저장소를 사용해서 확인했습니다. 10월 18일 검사 기록에는 별도로 분리한 개발 앱에서 새싹 그림 한 장으로 분석, 초상화, 성격, 첫 홈의 픽셀 모습까지 확인한 내용을 남겼습니다. 초상화가 완성됐다고 이동용 그림까지 자동 생성하지 않는지, 페이지를 다시 열어도 저장한 그림이 나타나는지도 봤고요.

당시 분석과 성격에 텍스트/비전 요청 두 번, 초상화와 사용자가 선택한 홈 모습에 이미지 요청 두 번을 사용했습니다. 다만 중간에 개발 연결과 업로드 문제를 복구하는 과정이 있었습니다. 첫 시도부터 끊김 없이 전부 성공한 테스트는 아니었습니다. 새싹 그림 한 장으로 확인한 결과라서, 실제 사진이나 모든 체형의 생성 품질까지 확인했다고 보기도 어렵고요.

마이홈에서 사용할 이동 모습은 따로 선택할 수 있습니다. 해당 외형으로 처음 홈에 들어갈 때 AI로 방향별 모습을 만들거나, 현재 초상화를 무료로 픽셀 변환한 기본 모습을 고르면 됩니다. 저장한 뒤 홈을 다시 열거나 움직일 때마다 그림을 새로 만들지는 않습니다.

저는 처음 만든 모습과 성격을 남겨두고, 돌봄과 모험이 쌓이면서 그 기록이 다음 대화와 성장에 조금씩 드러나게 하고 싶었습니다. 한 번 그림을 보고 끝내기보다, 다음 날에도 다시 만나고 싶은 친구가 됐으면 좋겠네요.

다음 4편에서는 이 친구를 화면 안에서 움직이게 만든 이야기를 해보려 합니다. 초상화 한 장을 움직이던 모습에서 방향별 스프라이트와 집, 마당의 픽셀 공간으로 바꿔간 과정을 이어서 소개하겠습니다.


Comments