diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000000000000000000000000000000000..12298a4624d371e485b73d1b73f62b22a5c018b0 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +# Git LFS로 관리할 대용량 파일 확장자 목록 +*.bin filter=lfs diff=lfs merge=lfs -text +*.pt filter=lfs diff=lfs merge=lfs -text +*.pth filter=lfs diff=lfs merge=lfs -text +*.safetensors filter=lfs diff=lfs merge=lfs -text +*.jpg filter=lfs diff=lfs merge=lfs -text \ No newline at end of file diff --git a/.github/workflows/sync-to-hub.yml b/.github/workflows/sync-to-hub.yml new file mode 100644 index 0000000000000000000000000000000000000000..4ba97a16ec247f5e8db4d87f6ef1f6478dbcc311 --- /dev/null +++ b/.github/workflows/sync-to-hub.yml @@ -0,0 +1,38 @@ +name: Sync to Hugging Face hub + +on: + push: + branches: [main] + +jobs: + sync-to-hub: + runs-on: ubuntu-latest + steps: + # 1. GitHub 저장소의 코드를 가져옵니다. (LFS 파일 포함) + - uses: actions/checkout@v2 # 안정성을 위해 v2를 사용 + with: + fetch-depth: 0 + lfs: true + + # 2. 깨끗한 버전의 내역을 만들어서 Hugging Face Hub에 푸시합니다. + - name: Push clean history to Hugging Face Hub + env: + HF_TOKEN: ${{ secrets.TOKEN }} # GitHub Secret 이름은 TOKEN으로 유지 + run: | + # Git 사용자 정보 및 원격 저장소 설정 + git config --global user.email "hf@example.com" + git config --global user.name "Hugging Face" + git remote add hf "https://huggingface.co/spaces/taehoon222/emotion-chatbot-app" + git config --global credential.helper store + + # 올바른 변수 이름인 HF_TOKEN을 사용하도록 수정 + echo "https://user:${HF_TOKEN}@huggingface.co" > $HOME/.git-credentials + + # .env 파일이 존재할 경우 강제 삭제 + rm -f .env + + # 새로운 브랜치를 만들어 과거 기록을 모두 제거하고 푸시 + git checkout --orphan clean-history + git add -A + git commit -m "Deploy clean snapshot of the repository" + git push --force hf clean-history:main \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..6a0b93fcfa844b4992efd0bf5e7b75fe1c6b562a --- /dev/null +++ b/.gitignore @@ -0,0 +1,114 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# IDEs and editors +.vscode/ +.idea/ +.project +.pydevproject +.settings/ +*.swp +*~ +*.sublime-project +*.sublime-workspace + +# Cursor +cursor/ + +# Supabase +supabase/.temp/ + +# Node.js +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# Database files +*.db +*.sqlite3 +/src/database.db + +# Model files +*.pt +*.bin +korean-emotion-final/ + +# Logs and results +logs/ +results/ +results1/ + +# OS-generated files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# Data files +# 주의: data/ 디렉토리의 모든 파일이 무시됩니다. +# 만약 프로젝트 실행에 필요한 데이터 파일이 있다면, +# 해당 파일은 버전 관리에 포함시켜야 합니다. +# 예: !data/required_data.csv +data/ +src/static/images/ diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md new file mode 100644 index 0000000000000000000000000000000000000000..3e2e7647329aca2562c13d56d25ccf9bc44c78bc --- /dev/null +++ b/DEVELOPMENT.md @@ -0,0 +1,42 @@ +# 🧗‍♂️ 개발 과정 및 문제 해결 (Development Journey) + +이 문서에서는 Emotion Diary 프로젝트를 진행하며 겪었던 주요 기술적 도전 과제와 이를 해결하기 위한 과정을 상세히 기록합니다. + +--- + +### 1. 대용량 AI 모델 관리 및 배포 전략 수립 +- **문제점**: 1GB가 넘는 AI 모델 파일을 Git LFS로 관리했으나, Hugging Face Spaces의 1GB 저장 공간 한계(Storage limit reached)와 LFS 파일-포인터 불일치(LFS pointer does not exist) 등 배포 과정에서 지속적인 오류가 발생했습니다. +- **해결 과정**: 모델과 앱 코드의 완전한 분리 전략을 채택했습니다. + - 대용량 모델은 Hugging Face Hub에 별도로 업로드하여 버전 관리합니다. + - GitHub 저장소에서는 Git LFS 추적을 완전히 제거하고 순수 앱 코드만 관리하도록 변경했습니다. + - GitHub Actions 워크플로우(`sync-to-hub.yml`)의 `lfs` 옵션을 `false`로 설정하여, 배포 시에는 앱 코드만 Spaces로 푸시하도록 수정했습니다. +- **결론**: Spaces 앱 실행 시점에서 `emotion_engine.py`가 Hub로부터 모델을 다운로드하도록 구현했습니다. 이를 통해 저장 공간 문제를 근본적으로 해결하고, 코드 변경 시 모델을 다시 업로드할 필요가 없는 효율적인 배포 파이프라인을 완성했습니다. + +--- + +### 2. CI/CD 파이프라인의 분산 환경 인증 문제 해결 +- **문제점**: 로컬에서 `git push`로 트리거된 GitHub Actions가 Hugging Face Spaces에 접근할 때 `Invalid credentials` 인증 오류가 발생했습니다. 이는 Spaces의 Secret과 GitHub Actions의 Secret 역할에 대한 혼동 때문이었습니다. +- **해결 과정**: '배포 로봇(GitHub Actions)'과 '빌드 로봇(Spaces)'의 개념으로 역할을 명확히 분리하여 접근했습니다. + - **GitHub Actions Secret (`HF_TOKEN`)**: '배포 로봇'이 Hugging Face 저장소(Repository)에 코드를 푸시할 때 필요한 `write` 권한 토큰을 등록했습니다. + - **Hugging Face Spaces Secret (`HF_TOKEN`)**: '빌드 로봇'이 내부적으로 LFS 파일 처리나 다른 private 저장소에 접근할 때 필요한 토큰을 등록했습니다. (이 프로젝트에서는 모델을 분리하면서 Spaces Secret의 필요성은 낮아졌습니다.) +- **결론**: 각기 다른 실행 환경에서 필요한 인증 정보를 명확히 분리하고 올바른 권한의 토큰을 제공함으로써 CI/CD 파이프라인의 인증 문제를 해결했습니다. + +--- + +### 3. Flask 애플리케이션 구조 설계 및 런타임 오류 디버깅 +- **문제점**: 개발 초기, 모든 로직이 담긴 단일 파일 구조(`app.py`)로 인해 순환 참조(Circular Import) 및 `ModuleNotFoundError`가 발생했습니다. 또한, API가 호출될 때마다 AI 모델을 로딩하여 응답 속도가 매우 느렸습니다. +- **해결 과정**: Flask의 **Application Factory 패턴**을 도입하여 프로젝트 구조를 체계적으로 재설계했습니다. + - `src/__init__.py`의 `create_app` 함수를 통해 앱의 모든 구성요소(DB, 블루프린트, 설정)를 조립하도록 변경했습니다. + - 앱이 시작되는 시점(`create_app` 내부)에서 AI 모델을 단 한 번만 로드하여 `app` 객체에 저장(`app.emotion_classifier`)했습니다. +- **결론**: 각 API 요청에서는 `current_app` 프록시를 통해 미리 로드된 모델을 참조하게 하여, 메모리 효율성과 응답 속도를 극대화했습니다. 이를 통해 확장 가능하고 안정적인 백엔드 구조를 완성할 수 있었습니다. + +--- + +### 4. 모델 성능 하락 및 정확도 개선 과정 +- **문제점**: 개발 과정에서 원본 학습 데이터가 유실되어 모델을 재학습하자, 정확도가 초기 모델보다 현저히 낮아지는 문제에 직면했습니다. +- **해결 과정**: 성능을 복원하고 개선하기 위해 다음과 같은 다양한 방법을 체계적으로 실험했습니다. + - **혼동 행렬(Confusion Matrix) 분석**: 모델이 어떤 감정들을 서로 혼동하는지 파악하여 문제의 원인을 진단했습니다. + - **레이블 재구성 (Label Remapping)**: 세분화된 감정 레이블을 6개 또는 4개의 주요 감정으로 그룹화하여 분류 문제의 복잡도를 조절했습니다. + - **데이터 불균형 해소**: 소수 클래스의 데이터를 증강하는 오버샘플링(Oversampling) 및 각 클래스에 다른 중요도를 부여하는 수동 클래스 가중치(Manual Class Weights)를 적용했습니다. + - **전이 학습(Transfer Learning) 강화**: 감성 분석에 특화된 NSMC(Naver Movie Corpus) 데이터셋으로 1차 사전 학습한 모델을 기반으로, 최종 감정 데이터에 2차 미세조정(Fine-tuning)을 수행했습니다. +- **결론**: 여러 실험 결과, '한국어 감성대화 말뭉치' 데이터의 레이블을 6개의 주요 감정으로 매핑하고, 데이터 불균형 문제를 해결하기 위해 **수동으로 클래스 가중치를 정교하게 조정**하여 학습하는 방식이 약 80%의 정확도를 회복하며 가장 안정적이고 효과적임을 확인했습니다. diff --git a/DEVLOG.md b/DEVLOG.md new file mode 100644 index 0000000000000000000000000000000000000000..ee75971ebcec2fb7b0d4ae5ba7441167670685ac --- /dev/null +++ b/DEVLOG.md @@ -0,0 +1,138 @@ + +--- +## 🚀 새로운 아이디어 및 개발 예정 (Upcoming Tasks) + +1. **달력 디자인 개선:** 투명 스타일에서 가독성 높은 '모던 카드(Modern Card)' 스타일로 변경. +2. **감정 이모지 톤 매칭:** 저장 애니메이션 구슬(Orb) 색상을 감정별 분위기에 맞게 세부 조정. +3. **용어 직관화:** 추천 카테고리 명칭 변경 ('수용/전환' → **'공감/환기'**) 및 관련 로직 동기화. +4. **저장 애니메이션 고도화:** 구슬이 통통 튀기다(Bouncing) '나의 일기' 메뉴로 강력하게 날아가는(Super Jump) 효과 구현. +5. **마이페이지 '기억의 유리병' 시각화 구현.** + +--- + +## 📅 2025년 11월 27일 +### 주제: UI 디테일 완성도 향상 & 애니메이션 고도화 (Polishing) + +사용자 경험의 질을 높이기 위해 내비게이션, 달력 등 핵심 UI 요소를 현대적인 스타일로 리디자인하고, 저장 애니메이션에 물리적인 생동감을 더했습니다. + +#### 1. 매직 내비게이션 바 (Magic Capsule Navbar) +- **디자인:** 기존 텍스트 나열 방식을 탈피하여, 공중에 떠 있는 **캡슐 형태의 글래스모피즘(Glassmorphism)** 디자인 적용. +- **인터랙션:** 마우스의 움직임에 따라 하얀색 하이라이트 바(`nav-marker`)가 메뉴 사이를 물 흐르듯 따라다니는 **Sliding Magic Line** 효과 구현. + +#### 2. 대시보드형 달력 (Modern Card Calendar) +- **스타일:** 투명한 배경 대신 가독성이 뛰어난 **'모던 카드(Modern Card)'** 스타일로 변경. 헤더에 테마 색상을 적용하여 집중도 향상. +- **레이아웃 최적화:** 요일(Header)과 날짜(Grid)의 정렬이 어긋나는 문제를 CSS `flex-basis: 14.28%` 강제 적용을 통해 1:1로 완벽하게 맞춤. +- **시각화:** 날짜 아래에 해당 일기의 감정을 나타내는 **색상 점(Dot)**이 자동으로 표시되도록 구현. + +#### 3. 저장 애니메이션 고도화 (Bouncing & Super Jump) +- **물리 효과:** 단순히 날아가는 애니메이션을 **"바닥에 통통 튀기다가(Bouncing) 에너지를 모아 목표지점으로 슈웅 날아가는(Super Jump)"** 역동적인 시퀀스로 업그레이드. +- **데이터 동기화:** CSS 애니메이션 시간(5.1초)과 서버 저장 요청(`fetch`)을 `Promise.all`로 동기화하여, 애니메이션과 데이터 저장이 안전하게 완료된 후 페이지가 이동되도록 로직 강화. + +#### 4. 감성 디테일 및 용어 개선 +- **용어 직관화:** 추천 카테고리 명칭을 '수용/전환'에서 더 감성적인 **'공감/환기'**로 변경하고, 프론트엔드 파싱 로직을 이에 맞춰 수정. +- **추천 요약:** AI의 긴 추천 이유를 제거하고, 핵심 정보(종류, 제목)만 깔끔하게 보여주도록 텍스트 정제 함수(`extractSummary`) 적용. +- **톤 매칭:** 저장 애니메이션 시 생성되는 '기억 구슬'의 색상을 각 감정(기쁨=Gold, 슬픔=SteelBlue 등)의 고유 분위기에 맞춰 세밀하게 조정. + +--- + +## 📅 2025년 11월 25일 +### 주제: 애니메이션 통합 및 버그 수정 + +`test_animation.html`에서 개발된 책 접기 및 구슬 바운스 애니메이션을 `main.html`에 통합하고 관련 버그를 수정했습니다. + +#### 1. 애니메이션 기능 개선 및 통합 +- **애니메이션 시작 문제 해결**: `playFullAnimation` 함수에 전달되던 정의되지 않은 변수(`rect`) 오류를 수정하여 애니메이션이 정상적으로 시작되도록 했습니다. +- **구슬 바운스 정교화**: 중앙에서 시작하여 화면 오른쪽 가장자리에서 정확히 마무리되는 6회 바운스 애니메이션을 구현했습니다. 각 바운스의 수평 이동 거리를 조정하여 자연스러운 움직임을 만들었습니다. +- **책 → 구슬 변신 인트로 애니메이션 구현**: + - 책이 구겨지듯 중앙으로 모여 구슬로 변하는 1.5초(책 응축) + 5초(구슬 바운스) = 총 6.5초 길이의 인트로 애니메이션을 구현했습니다. + - 책이 모이는 동안 구슬의 감정 색상으로 배경색이 변하도록 하여 시각적 연결성을 강화했습니다. + - 책이 모이는 동작은 '접히는' 대신 회전 없이 '중앙으로 응축되는' 느낌을 주도록 `transform` 속성을 조정했습니다. + - 응축되는 책이 최종적으로 구슬 모양과 잘 연결되도록 `borderRadius` 애니메이션을 다시 도입했습니다. + - 책이 사라지는 느낌 없이 구슬로 전환되도록, 책이 사라지는 동안 구슬이 동시에 나타나는 크로스페이드(cross-fade) 기법을 사용하여 부드러운 변신을 구현했습니다. +- **`main.html` 통합**: 개발된 애니메이션 로직을 `src/templates/static/js/main_logic.js` 파일로 이전했습니다. 이 애니메이션은 이제 '일기장에 저장하기' 버튼을 클릭할 때 실행됩니다. + +#### 2. 리디렉션 버그 수정 +- **문제**: 애니메이션 완료 후 `main_logic.js`에서 `/diary` 경로로 리디렉션할 때 'Not Found' 오류가 발생했습니다. +- **원인**: 백엔드에 `/diary` 라우트가 정의되어 있지 않고, 실제 일기 목록 페이지는 `/my_diary` 라우트를 사용하고 있었습니다. +- **해결**: `main_logic.js` 파일 내 `window.location.href`의 경로를 `/diary`에서 올바른 경로인 `/my_diary`로 수정하여 리디렉션 오류를 해결했습니다. + +--- +## 📅 2025년 11월 23일 +### 주제: 메인 화면 UI/UX 대규모 개편 & 인터랙션 강화 + +사용자가 일기를 쓰는 행위에 더 깊이 몰입할 수 있도록, 메인 화면의 디자인 언어를 '디지털'에서 '아날로그 감성'으로 전면 교체하고, 저장 과정에 스토리텔링을 담았습니다. + +#### 1. 펼쳐진 다이어리 (Book Spread Layout) +- **문제:** 기존의 분리된 입력창(Left)과 결과창(Right)은 시선이 분산되고, '일기장'이라는 앱의 정체성을 시각적으로 전달하기 부족함. +- **해결:** 화면 전체를 아우르는 **'펼쳐진 다이어리'** 형태의 레이아웃 도입. +- **구현 디테일:** + - **Wide Layout:** `max-width: 1600px`, `min-width: 1200px`를 적용하여 실제 책상 위에 노트를 펼친 듯한 시원한 개방감 확보. + - **Clean UI:** 추천 콘텐츠가 길어질 경우 스크롤은 되지만 스크롤바(Bar)는 숨겨(`scrollbar-width: none`) 깔끔함 유지. + - **Line-clamp:** 추천 텍스트가 길어지면 `line-clamp`로 말줄임 처리하고, Hover 시 전체 내용을 보여주는 인터랙션 추가. + +#### 2. '기억의 구슬' 저장 애니메이션 (Memory Orb & Bouncing) +- **기획 의도:** 단순히 데이터를 서버로 전송(Submit)하는 것을 넘어, **"나의 소중한 하루가 기억의 구슬이 되어 보관된다"**는 메타포(영화 *인사이드 아웃* 모티브)를 전달하고자 함. +- **애니메이션 시나리오:** + 1. **응축 (Morph):** 저장 버튼 클릭 시, 거대했던 다이어리가 순식간에 작아지며 동그란 구슬로 변신. + 2. **감정의 색:** AI가 분석한 감정(기쁨=노랑, 슬픔=파랑 등)에 맞춰 구슬의 색상이 실시간으로 변화. + 3. **역동적 이동 (Bouncing):** 구슬이 바닥에 통통 튀기며(Squash & Stretch) 에너지를 모으다, 마지막에 메뉴(보관함) 쪽으로 슈웅 날아감. +- **기술적 구현:** + - **Clone Node:** 원본 DOM을 건드리면 레이아웃이 깨지므로, `cloneNode`로 다이어리의 복제본을 생성하여 `fixed` 포지션으로 띄운 뒤 애니메이션 적용. + - **Keyframes:** `0%`~`100%` 구간을 세밀하게 나누어 위치 이동(`translate`)과 형태 변형(`scale`, `border-radius`)을 동시에 제어. + +#### 🔜 Next Step +- 상단 내비게이션 바(Navbar)에 'Sliding Magic Line' 인터랙션 적용 예정. +- 마이페이지 '기억의 유리병' 시각화 구현. + +--- + + +## 📅 2025년 11월 21일 (UI/UX Refinement) + +### Feature: 새로운 하단 내비게이션 바 +- 기존 상단 헤더를 대체하는 새로운 SVG 기반 하단 내비게이션 바를 구현했습니다. +- 주요 기능인 '홈', '나의 일기', '마이페이지' 및 '로그아웃' 버튼을 포함합니다. +- 내비게이션 바는 사용자가 로그인했을 때만 표시되도록 설정했습니다. + +### Refactor: 내비게이션 바 인터랙션 개선 +- **UI 스타일:** 흰색 배경, 어두운 색상의 아이콘 및 활성 페이지를 나타내는 '빛 효과(tubelight)'를 적용했습니다. 4개의 아이콘에 맞게 너비를 조정했습니다. +- **위치 문제 해결 (CSS 기반 로직):** 페이지 로드 시 '빛 효과'의 위치가 어긋나는 문제를 해결하기 위해, 불안정한 JavaScript 타이밍 대신 CSS만으로 위치를 제어하는 방식으로 로직을 전면 수정했습니다. + - 서버에서 현재 페이지 주소(`request.endpoint`)에 따라 `