Files
web-page-backend/docs/superpowers/specs/2026-04-27-personal-service-migration-design.md
gahusb e3d5eaf6f3 refactor: portfolio → personal 리네이밍 + Blog/Todo 통합
- portfolio/ 디렉토리를 personal/로 리네이밍
- lotto-backend의 Blog/Todo 라우트·CRUD를 personal 서비스로 이전
- lotto-backend에서 Blog/Todo 코드 제거 (DB 테이블 스키마는 유지)
- nginx: /api/todos, /api/blog/ 라우팅을 personal로 추가
- docker-compose: portfolio → personal 서비스 변��
- deploy 스크립트: portfolio → personal 반영

데이터 마이그레이션은 배포 후 NAS에서 별도 수행 필요:
1. cp data/portfolio/portfolio.db data/personal/personal.db
2. sqlite3 data/lotto.db ".dump todos" | sqlite3 data/personal/personal.db
3. sqlite3 data/lotto.db ".dump blog_posts" | sqlite3 data/personal/personal.db

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 16:32:55 +09:00

7.6 KiB

Personal 서비스 마이그레이션 설계

개요

기존 portfolio 서비스를 personal로 리네이밍하고, lotto-backend에 있던 Blog/Todo 기능을 personal 서비스로 통합한다.

목표: 신규 컨테이너 없이, 개인 콘텐츠(포트폴리오 + 블로그 + 투두)를 하나의 서비스로 통합

제약: 기존 데이터 무손실 이전 필수


아키텍처

변경 전

lotto-backend (lotto.db)
├── 로또 API (/api/lotto/*)
├── 블로그 API (/api/blog/posts)  ← 이전 대상
└── 투두 API (/api/todos)         ← 이전 대상

portfolio (portfolio.db)
└── 포트폴리오 API (/api/profile/*)

변경 후

lotto-backend (lotto.db)
└── 로또 API (/api/lotto/*)       ← Blog/Todo 라우트 제거

personal (personal.db)
├── 포트폴리오 API (/api/profile/*)
├── 블로그 API (/api/blog/posts)  ← 통합
└── 투두 API (/api/todos)         ← 통합

서비스 속성

항목 현재 (portfolio) 변경 후 (personal)
디렉토리 portfolio/ personal/
컨테이너명 portfolio personal
포트 18850 18850 (유지)
DB 파일 data/portfolio/portfolio.db data/personal/personal.db
API prefix /api/profile/ /api/profile/ + /api/todos + /api/blog/

DB 스키마

personal.db에 기존 5테이블 + 신규 2테이블:

기존 테이블 (portfolio에서 이관)

  • profile — 프로필 (id=1 싱글턴)
  • careers — 경력
  • projects — 프로젝트
  • skills — 기술스택
  • introductions — 자기소개

신규 추가 테이블 (lotto-backend에서 이관)

CREATE TABLE IF NOT EXISTS todos (
    id          TEXT PRIMARY KEY
                DEFAULT (lower(hex(randomblob(4))) || '-' || lower(hex(randomblob(2)))),
    title       TEXT NOT NULL,
    description TEXT,
    status      TEXT NOT NULL DEFAULT 'todo'
                CHECK(status IN ('todo','in_progress','done')),
    created_at  TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
    updated_at  TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
);
CREATE INDEX IF NOT EXISTS idx_todos_created ON todos(created_at DESC);

CREATE TABLE IF NOT EXISTS blog_posts (
    id         INTEGER PRIMARY KEY AUTOINCREMENT,
    title      TEXT NOT NULL,
    body       TEXT NOT NULL DEFAULT '',
    excerpt    TEXT NOT NULL DEFAULT '',
    tags       TEXT NOT NULL DEFAULT '[]',
    date       TEXT NOT NULL DEFAULT (date('now','localtime')),
    created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
    updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
);
CREATE INDEX IF NOT EXISTS idx_blog_date ON blog_posts(date DESC);

API 엔드포인트 (personal 서비스 전체)

포트폴리오 (기존 유지)

메서드 경로 인증 설명
GET /api/profile/public - 공개 데이터 일괄 조회
POST /api/profile/auth - 비밀번호 인증 → 토큰
GET/PUT /api/profile/profile Bearer 프로필 조회/수정
GET/POST /api/profile/careers Bearer 경력 목록/추가
PUT/DELETE /api/profile/careers/{id} Bearer 경력 수정/삭제
GET/POST /api/profile/projects Bearer 프로젝트 목록/추가
PUT/DELETE /api/profile/projects/{id} Bearer 프로젝트 수정/삭제
GET/POST /api/profile/skills Bearer 기술 목록/추가
PUT/DELETE /api/profile/skills/{id} Bearer 기술 수정/삭제
GET/POST /api/profile/introductions Bearer 자기소개 목록/추가
PUT/DELETE /api/profile/introductions/{id} Bearer 자기소개 수정/삭제
PATCH /api/profile/introductions/{id}/main Bearer 메인 자기소개 지정

투두 (lotto-backend에서 이전, 인증 없음)

메서드 경로 설명
GET /api/todos 전체 목록
POST /api/todos 생성
DELETE /api/todos/done 완료 일괄 삭제
PUT /api/todos/{id} 수정
DELETE /api/todos/{id} 삭제

블로그 (lotto-backend에서 이전, 인증 없음)

메서드 경로 설명
GET /api/blog/posts 목록 ({"posts": [...]})
POST /api/blog/posts 생성
PUT /api/blog/posts/{id} 수정
DELETE /api/blog/posts/{id} 삭제

Nginx 라우팅 변경

# 추가: /api/todos → personal
location /api/todos {
    resolver 127.0.0.11 valid=10s;
    set $personal_backend personal:8000;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_pass http://$personal_backend$request_uri;
}

# 추가: /api/blog/ → personal
location /api/blog/ {
    resolver 127.0.0.11 valid=10s;
    set $personal_backend personal:8000;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_pass http://$personal_backend$request_uri;
}

# 변경: portfolio → personal
location /api/profile/ {
    resolver 127.0.0.11 valid=10s;
    set $personal_backend personal:8000;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_pass http://$personal_backend$request_uri;
}

기존 /api/ catch-all은 lotto-backend로 유지 (todos/blog 요청은 위의 더 구체적인 location에서 먼저 매칭).


인프라 변경

docker-compose.yml

  • portfolio 서비스 → personal로 리네이밍
  • 볼륨: ${RUNTIME_PATH}/data/personal:/app/data
  • 환경변수 동일 (PORTFOLIO_EDIT_PASSWORD 등)

deploy.sh / deploy-nas.sh

  • SERVICES, BUILD_TARGETS, CONTAINER_NAMES 등에서 portfoliopersonal 변경
  • DATA_DIRS에서 portfoliopersonal 변경

lotto-backend 정리

  • main.py에서 Blog/Todo 라우트 + Pydantic 모델 제거 (약 100줄)
  • db.py에서 Blog/Todo CRUD 함수 제거 (약 130줄)
  • db.pyinit_db()에서 todos/blog_posts 테이블 생성 코드는 유지 (기존 DB 호환)

배포 순서 (안전 우선)

  1. 코드 개발 — personal 서비스 + lotto-backend 정리 + 인프라 변경
  2. git push — 자동 배포 트리거
  3. NAS에서 데이터 디렉토리 준비mkdir -p data/personal
  4. 기존 portfolio.db 이동cp data/portfolio/portfolio.db data/personal/personal.db
  5. lotto.db에서 Blog/Todo 데이터 복사:
    sqlite3 data/lotto.db ".dump todos" | sqlite3 data/personal/personal.db
    sqlite3 data/lotto.db ".dump blog_posts" | sqlite3 data/personal/personal.db
    
  6. 컨테이너 재시작docker compose restart personal
  7. 검증 — API 호출로 데이터 건수 대조
  8. lotto.db 원본 테이블 — 삭제하지 않고 당분간 유지

프론트엔드

변경 없음. 모든 API 호출이 상대경로(/api/todos, /api/blog/posts, /api/profile/)이므로 nginx 라우팅 변경만으로 자동 적용.


리스크

  • 낮음: Blog/Todo는 lotto 테이블과 FK/공유 쿼리 없음
  • 롤백: lotto.db 원본 테이블 유지 + nginx 라우팅 원복으로 즉시 롤백 가능
  • 다운타임: nginx reload 순간 (~1초)