You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
selected Set과 drawing_ids 구조 유지 필수 — 동화 생성 API 입력값
동화 생성 (API)
app.py, services/
scene_data 구조 변경 시 storybook.js도 반드시 같이 수정
동화 뷰어 (/story/<id>)
storybook.html, storybook.js
JS 전체가 <script id="story-data"> JSON 구조에 의존. DB 스키마 변경 시 같이 수정
도서관 (/library)
library.html
React 기반 북셀프 UI — story.keywords 사용
🚨 절대 건드리면 안 되는 것들
1. canvas.js — 그림 그리기 캔버스
이 파일을 수정하면 저장되는 image_data(base64) 형식이 달라질 수 있음
저장 시 JS Canvas에서 흰 배경 제거(removeWhiteBg) 후 전송 → DB → storybook.js 누끼 처리까지 연결됨
수정이 필요하면 팀 전체 합의 필요
2. app.py 페이지 라우터 (/, /main, /draw, /collection, /library, /story/<id>)
URL 경로 바꾸면 모든 페이지 내 링크(href, window.location.href) 전부 깨짐
추가는 괜찮지만 기존 경로 수정/삭제 금지
3. database.py — save_story() / get_story() 반환 키 이름
title, content, moral, keywords, drawing_ids, scene_data, audio_path 키 이름을 바꾸면
storybook.html (Jinja2 변수), storybook.js (SD.title 등), library.html 전부 깨짐
컬럼 추가는 괜찮음. 기존 키 이름 변경/삭제는 금지
4. scene_data JSON 구조 (text, bg, bg_image, layout_hints)
ai_service.py가 생성 → image_service.py가 bg_image 추가 → DB 저장 → storybook.js가 읽음
키 이름 하나라도 바꾸면 배경 이미지, 그림 배치, 텍스트 표시가 전부 깨짐
필드 추가는 괜찮음
5. static/js/storybook.js — spreads 배열 구조
spreads[i] = { leftHTML, rightHTML } 구조가 고정됨
renderSpread(), goNext(), goPrev() 모두 이 구조에 의존
leftHTML / rightHTML 키 이름 변경 금지
⚡ 작업 시 자주 발생하는 충돌 패턴
config.py 머지 충돌
여러 브랜치에서 config.py에 새 변수 추가 시 충돌 발생
해결법: 충돌 마커(<<<<<<, =======, >>>>>>>) 모두 제거하고 양쪽 변수를 모두 유지
충돌 해결 후 반드시 git add config.py 실행
Flask debug=True + GPU 모델 로드 → segfault
debug=True는 Flask가 프로세스 2개를 띄움 (reloader + worker)
양쪽에서 LCM-DreamShaper 모델 로드 시도 → VRAM 초과 → segfault
현재 debug=False로 고정. 절대 debug=True로 바꾸지 말 것
코드 수정 후 서버 재시작은 직접 Ctrl+C → python app.py
requirements.txt에 torch 직접 설치 명령 쓰지 말 것
torch는 CUDA 버전을 특수 URL로 설치해야 함
pip install torch만 쓰면 CPU 버전 설치됨 → GPU 이미지 생성 안 됨
앱 실행 시 _ensure_dependencies()가 자동으로 올바른 버전 설치함
📦 브랜치 작업 전 체크리스트
□ git pull 후 작업 시작
□ .env 파일 직접 만들기 (git에 없음 — .env.example 참고)
□ python app.py 첫 실행 시 torch 자동 설치 대기 (약 5~10분)
□ LCM-DreamShaper v7 첫 다운로드 대기 (~4GB, 첫 밑그림/배경 생성 시)
□ 수정 후 /collection → 동화 만들기 → /story 흐름 전체 테스트
🔗 페이지 간 데이터 흐름 (수정 시 영향 범위)
[drawing.html + canvas.js]
└─ removeWhiteBg (JS Canvas 흰배경 제거)
└─ POST /api/drawing/save
└─ DB drawings 테이블 (image_data, file_path)
└─ [collection.html] drawing 카드 표시
└─ POST /api/story/generate (drawing_ids)
└─ [ai_service.py] keywords + layout_hints 추출
└─ [image_service.py] bg_image 병렬 생성
└─ DB stories 테이블 (scene_data + layout_data 포함)
└─ [storybook.js] 누끼 + 배경 + 배치
cd"AI Story/AI Story"# 또는 프로젝트 루트
pip install -r requirements.txt
# torch는 첫 실행 시 자동 설치됨 (app.py _ensure_dependencies 참고)
cp .env.example .env # API 키 입력
python app.py
# → http://localhost:5000
환경변수 (.env):
GEMINI_API_KEY=your_key # 동화 생성 (필수)
ANTHROPIC_API_KEY=your_key # Claude 폴백 (선택)
CLOVA_CLIENT_ID=your_id # CLOVA Voice TTS (권장)
CLOVA_CLIENT_SECRET=your_secret
FLASK_SECRET_KEY=any_secret
로컬 이미지 생성: 첫 실행 시 LCM-DreamShaper v7 모델이 ~/.cache/huggingface/에 자동 다운로드됨 (~4GB). GPU(VRAM 3GB+) 필요.
파일 구조 및 역할
AI Story/AI Story/
├── app.py ← Flask 진입점. 라우터 + API 엔드포인트
├── config.py ← 환경변수 로드, 경로 상수 정의
├── database.py ← SQLite CRUD 함수 모음
├── requirements.txt ← 패키지 목록 (torch는 자동 설치)
├── fairy_tale.db ← SQLite DB (자동 생성, .gitignore)
├── .env ← API 키 (절대 git에 올리지 않음)
│
├── services/
│ ├── ai_service.py ← Gemini/Claude로 동화 텍스트 + layout_hints 생성
│ ├── image_service.py ← LCM-DreamShaper v7로 배경/참고 이미지 생성
│ └── tts_service.py ← CLOVA Voice / gTTS로 MP3 생성
│
├── templates/
│ ├── base.html ← 공통 레이아웃 (네비, 폰트 등)
│ ├── landing.html ← 시작 화면
│ ├── main.html ← 메인 메뉴
│ ├── drawing.html ← 2열 레이아웃 캔버스 화면 (좌: 단어/예시/버튼, 우: 캔버스)
│ ├── collection.html ← 그린 그림 목록 + 선택 UI
│ ├── library.html ← 만들어진 동화 목록 (React 북셀프)
│ └── storybook.html ← 3D 책 뷰어 (CSS + JS)
│
└── static/
├── css/style.css ← 전체 스타일
├── js/
│ ├── canvas.js ← 그림 그리기 캔버스 (손대지 말 것)
│ ├── collection.js ← 그림 선택 + 동화 생성 요청
│ └── storybook.js ← 3D 책 넘기기, 누끼, 배경, 그림 배치
└── generated/
├── drawings/ ← 아이가 그린 그림 PNG (.gitignore)
├── bgs/ ← LCM 장면 배경 이미지 (.gitignore)
└── merged/ ← 교훈 콜라주 이미지 (.gitignore)
└── audio/ ← TTS MP3 파일 (.gitignore)
파일 간 연결 관계
브라우저 요청
GET /collection → app.py → database.get_all_drawings()
→ collection.html + collection.js
POST /api/story/generate
app.py
├─ database.get_drawings_by_ids(drawing_ids)
├─ ai_service.generate_fairy_tale(keywords, drawings)
│ └─ Gemini API → JSON {title, scenes[], moral, layout_hints}
├─ [병렬] image_service.generate_scene_bgs_parallel(scene_data)
│ └─ LCM-DreamShaper v7 → bg_xxx.jpg × 4장
├─ [병렬] image_service.generate_moral_collage(moral, drawings)
│ └─ 교훈 콜라주 이미지
├─ [병렬] tts_service.generate_tts(full_text)
│ └─ CLOVA/gTTS → story_xxx.mp3
└─ database.save_story(...)
└─ stories 테이블에 저장
GET /story/<id> → app.py → database.get_story(id)
→ storybook.html + storybook.js
├─ 누끼 처리 (Canvas API)
├─ 배경 이미지 표시 (scene.bg_image)
├─ layoutHints로 그림 pos/size 결정
└─ 3D 책 넘기기 애니메이션
DB 스키마
-- 단어 목록 (시드 35개)CREATETABLEwords (
id INTEGERPRIMARY KEY AUTOINCREMENT,
korean TEXTNOT NULL, -- "사자"
english TEXTNOT NULL, -- "lion"
emoji TEXTNOT NULL, -- "🦁"
category TEXT DEFAULT 'general'
);
-- 아이가 그린 그림CREATETABLEdrawings (
id INTEGERPRIMARY KEY AUTOINCREMENT,
word_id INTEGERREFERENCES words(id),
image_data TEXTNOT NULL, -- base64 data URI (흰배경 제거된 PNG)
file_path TEXT, -- "static/generated/drawings/xxx.png"
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 생성된 동화CREATETABLEstories (
id INTEGERPRIMARY KEY AUTOINCREMENT,
title TEXTNOT NULL,
content TEXTNOT NULL, -- 전체 텍스트 (4단락 합친 것)
moral TEXT, -- 교훈 문장
keywords TEXTNOT NULL, -- JSON: ["사자", "토끼"]
drawing_ids TEXTNOT NULL, -- JSON: [1, 3, 2]
illustration_path TEXT, -- 미사용 (NULL로 저장)
audio_path TEXT, -- "static/audio/xxx.mp3"
scene_data TEXT, -- JSON: [{text, bg, bg_image, layout_hints}, ...]
layout_data TEXT, -- JSON: spreads 배열 (그림 배치 사전 계산)
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
1. drawing_ids 받음 (1~5개)
↓
2. DB에서 그림 정보 조회
drawings = get_drawings_by_ids(drawing_ids)
keywords = [d['korean'] for d in drawings]
↓
3. [Gemini] 동화 텍스트 생성
gemini-2.5-flash-lite 호출
→ {title, scene_data: [{text, bg, layout_hints}, ...], moral}
↓
4. present 처리 + layout_spreads 계산
↓
5. [병렬 ThreadPoolExecutor(3)] 동시 실행
├─ [LCM] 장면 배경 이미지 4장 생성
├─ [CLOVA/gTTS] MP3 음성 생성
└─ 교훈 콜라주 이미지 생성
↓
6. DB 저장
save_story(title, content, moral, keywords, drawing_ids,
audio_path, scene_data, layout_data)
→ story_id
↓
7. { success: true, story_id } 반환
→ 브라우저 /story/<story_id> 이동
네비게이션 흐름
/ (landing)
└─→ /main
├─→ /draw → 그림 저장 → /collection
├─→ /collection → 동화 생성 → /story/<id>
└─→ /library → /story/<id>