mongle-hf-package / README.md
Hadimeeee's picture
Add README.md
8aad78d verified
|
Raw
History Blame Contribute Delete
11.5 kB
metadata
license: other
pipeline_tag: text-to-image

Mongle RunPod Test Package

RunPod์—์„œ Mongle ์บ๋ฆญํ„ฐ ์ƒ์„ฑ๊ณผ ํ”ผ๋“œ ์ด๋ฏธ์ง€ ์ƒ์„ฑ์„ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์œ„ํ•œ ํŒจํ‚ค์ง€์ž…๋‹ˆ๋‹ค.

์ด repo๋Š” ํ˜„์žฌ ๋กœ์ง์„ RunPod์—์„œ ๋ฐ”๋กœ ๊ฒ€์ฆํ•˜๊ธฐ ์œ„ํ•œ ํ…Œ์ŠคํŠธ ํŒจํ‚ค์ง€์ž…๋‹ˆ๋‹ค.

์•„๋ž˜ ๋‘ ๊ฐ€์ง€ ๋ฐฉ์‹์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

1. end-to-end ํ…Œ์ŠคํŠธ ํŒŒ์ผ๋กœ ์ „์ฒด ํ๋ฆ„ ๊ฒ€์ฆ
   - test_text2feed_pipeline.py
   - test_image2feed_pipeline.py

2. RunPod serverless handler๋กœ ์บ๋ฆญํ„ฐ/ํ”ผ๋“œ ๋ถ„๋ฆฌ ์‹คํ–‰
   - handler_character.py
   - handler_feed.py

์ „์ฒด ๊ตฌ์กฐ

ํ˜„์žฌ ๋กœ์ง์€ ํฌ๊ฒŒ ๋‘ ๋‹จ๊ณ„๋กœ ๋‚˜๋‰ฉ๋‹ˆ๋‹ค.

1. ์บ๋ฆญํ„ฐ ์ƒ์„ฑ
   - ํ…์ŠคํŠธ๋กœ ์บ๋ฆญํ„ฐ๋ฅผ ๋งŒ๋“ค๊ฑฐ๋‚˜
   - ์ด๋ฏธ์ง€๋กœ ์บ๋ฆญํ„ฐ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Œ
   - ๊ฒฐ๊ณผ๋กœ character.png์™€ appearance.json์„ ๋งŒ๋“ฆ

2. ํ”ผ๋“œ ์ƒ์„ฑ
   - 1๋‹จ๊ณ„์—์„œ ๋‚˜์˜จ appearance.json
   - ์‚ฌ์šฉ์ž์˜ quest text
   - ์œ„ ๋‘ ๊ฐœ๋ฅผ ์‚ฌ์šฉํ•ด์„œ feed.png๋ฅผ ๋งŒ๋“ฆ

์„œ๋น„์Šค ๊ด€์ ์—์„œ๋Š” ์•„๋ž˜์ฒ˜๋Ÿผ ๋ถ„๋ฆฌํ•ด์„œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

handler_character
  ์ž…๋ ฅ:
    - character_ko ๋˜๋Š” character_image
  ์ถœ๋ ฅ:
    - character.png
    - appearance.json

handler_feed
  ์ž…๋ ฅ:
    - appearance.json
    - quest_ko
  ์ถœ๋ ฅ:
    - feed.png
    - quest_en

์ด repo์—๋Š” ์œ„ ๊ตฌ์กฐ๋ฅผ ์‹ค์ œ๋กœ ๋‚˜๋ˆˆ handler ํŒŒ์ผ๋„ ๋“ค์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

handler_character.py
  ํ…์ŠคํŠธ branch์™€ ์ด๋ฏธ์ง€ branch๋ฅผ ๋ชจ๋‘ ์ฒ˜๋ฆฌ
  character.png์™€ appearance.json์„ ๋ฐ˜ํ™˜

handler_feed.py
  appearance.json๊ณผ quest_ko๋ฅผ ๋ฐ›์•„ feed.png๋ฅผ ๋ฐ˜ํ™˜

๊ทธ๋ฆฌ๊ณ  ์•„๋ž˜ ๋‘ ํ…Œ์ŠคํŠธ ํŒŒ์ผ์€ ๋‘ ๋‹จ๊ณ„๋ฅผ ํ•œ ๋ฒˆ์— ์ด์–ด์„œ ๋Œ๋ ค๋ณด๊ธฐ ์œ„ํ•œ end-to-end ํ…Œ์ŠคํŠธ ํŒŒ์ผ์ž…๋‹ˆ๋‹ค.

test_text2feed_pipeline.py
  ํ…์ŠคํŠธ ์บ๋ฆญํ„ฐ ์„ค๋ช…์—์„œ ์‹œ์ž‘ํ•ด์„œ character.png, appearance.json, feed.png๊นŒ์ง€ ์ƒ์„ฑ

test_image2feed_pipeline.py
  ์ž…๋ ฅ ์บ๋ฆญํ„ฐ ์ด๋ฏธ์ง€์—์„œ ์‹œ์ž‘ํ•ด์„œ character.png, appearance.json, feed.png๊นŒ์ง€ ์ƒ์„ฑ

๋ถ„๊ธฐ ๊ธฐ์ค€

์บ๋ฆญํ„ฐ ์ƒ์„ฑ ์ž…๋ ฅ์ด ํ…์ŠคํŠธ์ธ์ง€ ์ด๋ฏธ์ง€์ธ์ง€์— ๋”ฐ๋ผ ํŒŒ์ผ์„ ๋‹ค๋ฅด๊ฒŒ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

ํ…์ŠคํŠธ๋กœ ์บ๋ฆญํ„ฐ ์ƒ์„ฑ ํ…Œ์ŠคํŠธ
  -> test_text2feed_pipeline.py

์ด๋ฏธ์ง€๋กœ ์บ๋ฆญํ„ฐ ์ƒ์„ฑ ํ…Œ์ŠคํŠธ
  -> test_image2feed_pipeline.py

๋‘ ํŒŒ์ผ ๋ชจ๋‘ ๋งˆ์ง€๋ง‰์—๋Š” ํ”ผ๋“œ ์ƒ์„ฑ๊นŒ์ง€ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

appearance.json + quest_ko
-> Qwen2.5-7B๋กœ quest_en ์ƒ์„ฑ
-> SDXL + lora_v3_32bit + lora_bg_v1๋กœ feed.png ์ƒ์„ฑ

ํฌํ•จ๋œ LoRA

์•„๋ž˜ ๋‘ LoRA๋Š” ์ด repo์— ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋กœ ์—…๋กœ๋“œํ•˜๊ฑฐ๋‚˜ ๋ณ„๋„๋กœ ๋ฐ›์„ ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

models/lora_v3_32bit/pytorch_lora_weights.safetensors
models/lora_bg_v1/pytorch_lora_weights.safetensors

์ฝ”๋“œ๋Š” ์œ„ ๊ฒฝ๋กœ๋ฅผ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์บ๋ฆญํ„ฐ ์ƒ์„ฑ:
  models/lora_v3_32bit

ํ”ผ๋“œ ์ƒ์„ฑ:
  models/lora_v3_32bit/pytorch_lora_weights.safetensors
  models/lora_bg_v1/pytorch_lora_weights.safetensors

์‹คํ–‰ ์ค‘ ์ž๋™ ๋‹ค์šด๋กœ๋“œ๋˜๋Š” ๋ฒ ์ด์Šค ๋ชจ๋ธ

์•„๋ž˜ ๋ชจ๋ธ๋“ค์€ ์ด repo์— ํฌํ•จํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. RunPod์—์„œ ์ฒ˜์Œ ์‹คํ–‰ํ•  ๋•Œ Hugging Face์—์„œ ์ž๋™์œผ๋กœ ๋‹ค์šด๋กœ๋“œ๋ฉ๋‹ˆ๋‹ค.

stabilityai/stable-diffusion-xl-base-1.0
Qwen/Qwen2.5-7B-Instruct
Qwen/Qwen2.5-VL-7B-Instruct

์šฉ๋„๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

stabilityai/stable-diffusion-xl-base-1.0
  - character.png ์ƒ์„ฑ
  - feed.png ์ƒ์„ฑ

Qwen/Qwen2.5-7B-Instruct
  - ํ•œ๊ตญ์–ด ์บ๋ฆญํ„ฐ ์„ค๋ช… -> ์˜์–ด ์บ๋ฆญํ„ฐ ํ”„๋กฌํ”„ํŠธ
  - ํ•œ๊ตญ์–ด ํ€˜์ŠคํŠธ -> ์˜์–ด ์žฅ๋ฉด ํ”„๋กฌํ”„ํŠธ

Qwen/Qwen2.5-VL-7B-Instruct
  - ์บ๋ฆญํ„ฐ ์ด๋ฏธ์ง€ -> appearance.json ์ถ”์ถœ

rembg๋„ ์ฒซ ์‹คํ–‰ ์‹œ ๋ฐฐ๊ฒฝ ์ œ๊ฑฐ์šฉ ๋ชจ๋ธ์„ ์บ์‹œ์— ๋‹ค์šด๋กœ๋“œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํฌํ•จ๋œ ์ฝ”๋“œ

test_text2feed_pipeline.py
test_image2feed_pipeline.py
handler_character.py
handler_feed.py
test_qwen25_vl_extract.py
src/pipeline/persona2prompt.py
src/feed/feed_generator_1.py
src/feed/feed_generator_2.py
requirements.txt

ํฌํ•จํ•˜์ง€ ์•Š์€ ๊ฒƒ

.env
HF_TOKEN
outputs/
data/
image/
grid/
logs
caches

RunPod์—์„œ ๋ฐ›๊ธฐ

LoRA๊ฐ€ Git LFS๋กœ ์˜ฌ๋ผ๊ฐ€ ์žˆ์œผ๋ฏ€๋กœ ๋ฐ˜๋“œ์‹œ git lfs๋ฅผ ๋จผ์ € ์ผญ๋‹ˆ๋‹ค.

git lfs install
git clone https://huggingface.co/Hadimeeee/mongle-hf-package
cd mongle-hf-package
git lfs pull

LoRA๊ฐ€ ์‹ค์ œ ํŒŒ์ผ๋กœ ๋ฐ›์•„์กŒ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

du -sh models/lora_v3_32bit/pytorch_lora_weights.safetensors
du -sh models/lora_bg_v1/pytorch_lora_weights.safetensors

๊ฐ๊ฐ ์•ฝ 177M ์ •๋„๋กœ ๋ณด์—ฌ์•ผ ์ •์ƒ์ž…๋‹ˆ๋‹ค. ๋ช‡ KB๋กœ ๋ณด์ด๋ฉด LFS pointer๋งŒ ๋ฐ›์€ ์ƒํƒœ์ž…๋‹ˆ๋‹ค.

ํŒจํ‚ค์ง€ ์„ค์น˜

pip install -r requirements.txt

RunPod CUDA ์ด๋ฏธ์ง€์— ๋”ฐ๋ผ torch, torchvision, xformers, bitsandbytes ๋ฒ„์ „ ์ถฉ๋Œ์ด ๋‚  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ๊ฒฝ์šฐ RunPod ์ด๋ฏธ์ง€์˜ CUDA/PyTorch ๋ฒ„์ „์— ๋งž์ถฐ ๋จผ์ € ์„ค์น˜ํ•œ ๋’ค requirements.txt๋ฅผ ์กฐ์ •ํ•˜์„ธ์š”.

ํ…Œ์ŠคํŠธ 1: ํ…์ŠคํŠธ์—์„œ ์บ๋ฆญํ„ฐ์™€ ํ”ผ๋“œ ์ƒ์„ฑ

ํ…์ŠคํŠธ ์บ๋ฆญํ„ฐ ์„ค๋ช…๋งŒ์œผ๋กœ ์บ๋ฆญํ„ฐ๋ฅผ ๋งŒ๋“ค๊ณ , ์ด์–ด์„œ ํ”ผ๋“œ ์ด๋ฏธ์ง€๊นŒ์ง€ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

python test_text2feed_pipeline.py \
  --name runpod_text_test \
  --character-ko "์ด ์นœ๊ตฌ๋Š” ๋ถ€๋“œ๋Ÿฌ์šด ์ฒด๋ฆฌํ•‘ํฌ์ƒ‰ ๊ณฐ ์ธํ˜•์ด์—์š”. ํฌ๋ฆผ์ƒ‰ ๋ฐฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๊ณ  ์ž‘์€ ๋‘ฅ๊ทผ ๊ท€์™€ ๋ถ„ํ™์ƒ‰ ์ฝ”๊ฐ€ ์žˆ์–ด์š”." \
  --quest-ko "๊ณต์›์—์„œ 30๋ถ„ ๋‹ฌ๋ฆฌ๊ธฐ๋ฅผ ์™„๋ฃŒํ–ˆ์–ด์š”!" \
  --force

์‹คํ–‰ ํ๋ฆ„:

character_ko
-> Qwen2.5-7B
-> English character prompt
-> SDXL + lora_v3_32bit
-> character.png
-> Qwen2.5-VL
-> appearance.json
-> quest_ko๋ฅผ Qwen2.5-7B๋กœ quest_en ๋ณ€ํ™˜
-> SDXL + lora_v3_32bit + lora_bg_v1
-> feed.png

์ถœ๋ ฅ:

outputs/text2feed/runpod_text_test/
  character.png
  appearance_raw.txt
  appearance.json
  character_prompt.json
  quest.json
  feed.png
  results.json

์ด๋ฏธ ์˜์–ด ์บ๋ฆญํ„ฐ ํ”„๋กฌํ”„ํŠธ๊ฐ€ ์žˆ์œผ๋ฉด Qwen ์บ๋ฆญํ„ฐ ๋ฒˆ์—ญ ๋‹จ๊ณ„๋ฅผ ๊ฑด๋„ˆ๋›ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

python test_text2feed_pipeline.py \
  --name runpod_text_fast \
  --character-text "soft cherry pink bear plush mascot with a cream belly, tiny pink nose, rounded ears" \
  --quest-ko "๊ณต์›์—์„œ 30๋ถ„ ๋‹ฌ๋ฆฌ๊ธฐ๋ฅผ ์™„๋ฃŒํ–ˆ์–ด์š”!" \
  --force

์ด๋ฏธ ์˜์–ด ํ€˜์ŠคํŠธ ์žฅ๋ฉด์ด ์žˆ์œผ๋ฉด Qwen ํ€˜์ŠคํŠธ ๋ฒˆ์—ญ ๋‹จ๊ณ„๋„ ๊ฑด๋„ˆ๋›ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

python test_text2feed_pipeline.py \
  --name runpod_text_fastest \
  --character-text "soft cherry pink bear plush mascot with a cream belly, tiny pink nose, rounded ears" \
  --quest-en "running with arms swinging along a fluffy cloud meadow path" \
  --force

Handler 1: ์บ๋ฆญํ„ฐ ์ƒ์„ฑ

RunPod serverless์—์„œ ์บ๋ฆญํ„ฐ ์ƒ์„ฑ ์ „์šฉ worker๋กœ ์“ธ ํŒŒ์ผ์ž…๋‹ˆ๋‹ค.

handler_character.py

์ด handler๋Š” ์ž…๋ ฅ์— ๋”ฐ๋ผ ๋‚ด๋ถ€์—์„œ ๋‘ branch๋กœ ๋‚˜๋‰ฉ๋‹ˆ๋‹ค.

mode = "text"
  character_ko ๋˜๋Š” character_text ์‚ฌ์šฉ
  -> character.png
  -> appearance.json

mode = "image"
  character_image base64 ์‚ฌ์šฉ
  -> rembg
  -> appearance.json
  -> character.png

ํ…์ŠคํŠธ branch ์ž…๋ ฅ ์˜ˆ์‹œ:

{
  "input": {
    "mode": "text",
    "name": "char_text_01",
    "character_ko": "์ด ์นœ๊ตฌ๋Š” ๋ถ€๋“œ๋Ÿฌ์šด ์ฒด๋ฆฌํ•‘ํฌ์ƒ‰ ๊ณฐ ์ธํ˜•์ด์—์š”. ํฌ๋ฆผ์ƒ‰ ๋ฐฐ์™€ ์ž‘์€ ๋‘ฅ๊ทผ ๊ท€๊ฐ€ ์žˆ์–ด์š”.",
    "seed": 42,
    "character_steps": 30
  }
}

์ด๋ฏธ์ง€ branch ์ž…๋ ฅ ์˜ˆ์‹œ:

{
  "input": {
    "mode": "image",
    "name": "char_image_01",
    "character_image": "<base64 png or jpg>",
    "seed": 42,
    "character_steps": 30
  }
}

์ถœ๋ ฅ ์˜ˆ์‹œ:

{
  "name": "char_text_01",
  "mode": "text",
  "character_image": "<base64 png>",
  "appearance": {
    "animal_type": "bear",
    "body_color": "soft cherry pink"
  },
  "appearance_raw": "..."
}

RunPod serverless entrypoint ์˜ˆ์‹œ:

python handler_character.py

ํ…Œ์ŠคํŠธ 2: ์ด๋ฏธ์ง€์—์„œ ์บ๋ฆญํ„ฐ์™€ ํ”ผ๋“œ ์ƒ์„ฑ

์ž…๋ ฅ ์บ๋ฆญํ„ฐ ์ด๋ฏธ์ง€๋ฅผ ์‚ฌ์šฉํ•ด์„œ appearance๋ฅผ ์ถ”์ถœํ•˜๊ณ , ํ”ฝ์…€ ์บ๋ฆญํ„ฐ์™€ ํ”ผ๋“œ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

๋จผ์ € ํ…Œ์ŠคํŠธ ์ด๋ฏธ์ง€๋ฅผ ์ค€๋น„ํ•ฉ๋‹ˆ๋‹ค.

mkdir -p image
# image/01.jpg ์œ„์น˜์— ์บ๋ฆญํ„ฐ ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œ

์‹คํ–‰:

python test_image2feed_pipeline.py \
  --name runpod_image_test \
  --character-image image/01.jpg \
  --quest-ko "๊ณต์›์—์„œ 30๋ถ„ ๋‹ฌ๋ฆฌ๊ธฐ๋ฅผ ์™„๋ฃŒํ–ˆ์–ด์š”!" \
  --force

์‹คํ–‰ ํ๋ฆ„:

character_image
-> rembg
-> input_nobg.png
-> Qwen2.5-VL
-> appearance.json
-> SDXL + lora_v3_32bit
-> character.png
-> quest_ko๋ฅผ Qwen2.5-7B๋กœ quest_en ๋ณ€ํ™˜
-> SDXL + lora_v3_32bit + lora_bg_v1
-> feed.png

์ถœ๋ ฅ:

outputs/image2feed/runpod_image_test/
  input_nobg.png
  appearance_raw.txt
  appearance.json
  character.png
  quest.json
  feed.png
  results.json

์ด๋ฏธ ์˜์–ด ํ€˜์ŠคํŠธ ์žฅ๋ฉด์ด ์žˆ์œผ๋ฉด ๋ฒˆ์—ญ ๋‹จ๊ณ„๋ฅผ ๊ฑด๋„ˆ๋›ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

python test_image2feed_pipeline.py \
  --name runpod_image_fast \
  --character-image image/01.jpg \
  --quest-en "running with arms swinging along a fluffy cloud meadow path" \
  --force

Handler 2: ํ”ผ๋“œ ์ƒ์„ฑ

RunPod serverless์—์„œ ํ”ผ๋“œ ์ƒ์„ฑ ์ „์šฉ worker๋กœ ์“ธ ํŒŒ์ผ์ž…๋‹ˆ๋‹ค.

handler_feed.py

์ด handler๋Š” ์บ๋ฆญํ„ฐ ์ƒ์„ฑ handler์—์„œ ๋‚˜์˜จ appearance์™€ ํ€˜์ŠคํŠธ ํ…์ŠคํŠธ๋ฅผ ๋ฐ›์•„์„œ ํ”ผ๋“œ ์ด๋ฏธ์ง€๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

์ž…๋ ฅ ์˜ˆ์‹œ:

{
  "input": {
    "name": "feed_01",
    "appearance": {
      "animal_type": "bear",
      "body_color": "soft cherry pink",
      "body_shape": "round plush body",
      "eye_style": "small black bead eyes"
    },
    "quest_ko": "๊ณต์›์—์„œ 30๋ถ„ ๋‹ฌ๋ฆฌ๊ธฐ๋ฅผ ์™„๋ฃŒํ–ˆ์–ด์š”!",
    "feed_seed": 123
  }
}

์ด๋ฏธ ์˜์–ด ์žฅ๋ฉด ํ”„๋กฌํ”„ํŠธ๊ฐ€ ์žˆ์œผ๋ฉด quest_en์„ ๋„ฃ์–ด์„œ Qwen ๋ฒˆ์—ญ ๋‹จ๊ณ„๋ฅผ ๊ฑด๋„ˆ๋›ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

{
  "input": {
    "name": "feed_fast_01",
    "appearance": {
      "animal_type": "bear",
      "body_color": "soft cherry pink"
    },
    "quest_en": "running with arms swinging along a fluffy cloud meadow path",
    "feed_seed": 123
  }
}

์ถœ๋ ฅ ์˜ˆ์‹œ:

{
  "name": "feed_01",
  "quest": {
    "quest_ko": "๊ณต์›์—์„œ 30๋ถ„ ๋‹ฌ๋ฆฌ๊ธฐ๋ฅผ ์™„๋ฃŒํ–ˆ์–ด์š”!",
    "quest_en": "running with arms swinging along a fluffy cloud meadow path"
  },
  "feed_image": "<base64 png>"
}

RunPod serverless entrypoint ์˜ˆ์‹œ:

python handler_feed.py

์„œ๋น„์Šค ์ฝ”๋“œ๋กœ ๋ถ„๋ฆฌํ•  ๋•Œ

ํ˜„์žฌ ํ…Œ์ŠคํŠธ ํŒŒ์ผ์€ end-to-end ๊ฒ€์ฆ์šฉ์ด๊ณ , ์‹ค์ œ ์„œ๋น„์Šค์—์„œ๋Š” handler_character.py์™€ handler_feed.py๋ฅผ ๋‚˜๋ˆ„์–ด ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ž์—ฐ์Šค๋Ÿฝ์Šต๋‹ˆ๋‹ค.

handler_character.py
  - ์•ฑ์—์„œ ์บ๋ฆญํ„ฐ ์ƒ์„ฑ ์š”์ฒญ์„ ๋ฐ›์Œ
  - ํ…์ŠคํŠธ ์ž…๋ ฅ์ด๋ฉด text branch ์‚ฌ์šฉ
  - ์ด๋ฏธ์ง€ ์ž…๋ ฅ์ด๋ฉด image branch ์‚ฌ์šฉ
  - character.png์™€ appearance.json ๋ฐ˜ํ™˜
  - ์„œ๋ฒ„๋Š” character.png๋ฅผ S3์— ์—…๋กœ๋“œํ•˜๊ณ  appearance๋ฅผ DB์— ์ €์žฅ

handler_feed.py
  - ์•ฑ์—์„œ quest ์™„๋ฃŒ ์š”์ฒญ์„ ๋ฐ›์Œ
  - ์„œ๋ฒ„๊ฐ€ DB์—์„œ character_id๋กœ appearance๋ฅผ ์กฐํšŒ
  - appearance + quest_ko๋ฅผ RunPod์— ์ „๋‹ฌ
  - feed.png์™€ quest_en ๋ฐ˜ํ™˜
  - ์„œ๋ฒ„๋Š” feed.png๋ฅผ S3์— ์—…๋กœ๋“œํ•˜๊ณ  feed record๋ฅผ DB์— ์ €์žฅ

์„œ๋น„์Šค ํ๋ฆ„:

์บ๋ฆญํ„ฐ ์ƒ์„ฑ ์š”์ฒญ
-> handler_character
-> character.png + appearance.json
-> S3 + DB ์ €์žฅ

ํ€˜์ŠคํŠธ ์™„๋ฃŒ ์š”์ฒญ
-> DB์—์„œ appearance ์กฐํšŒ
-> handler_feed
-> feed.png + quest_en
-> S3 + DB ์ €์žฅ

์ฆ‰, appearance.json์ด ์บ๋ฆญํ„ฐ ์ƒ์„ฑ๊ณผ ํ”ผ๋“œ ์ƒ์„ฑ์„ ์ด์–ด์ฃผ๋Š” ์ค‘๊ฐ„ ์‚ฐ์ถœ๋ฌผ์ž…๋‹ˆ๋‹ค.