개발 slecs

블로그 포스트를 파일에서 DB로 마이그레이션한 과정

목차

블로그 포스트를 파일 기반으로 관리하다가 DB로 옮기는 작업, 생각보다 품이 꽤 든다.

왜 마이그레이션 유틸을 따로 만들었나

파일 기반 콘텐츠 관리에서 DB 기반으로 전환할 때 가장 흔하게 겪는 문제 중 하나가 "일회성이라 대충 하면 되지 않나?"라는 판단이다. 실제로는 그 대충이 나중에 데이터 누락이나 인코딩 깨짐, 날짜 파싱 오류 같은 사고로 이어진다. 이번에 scripts/db/ 아래에 dump-posts.jsmigrate-posts-to-db.js를 분리해서 만든 이유가 거기 있다.

dump와 migrate를 하나의 스크립트로 합칠 수도 있었다. 그런데 합치면 디버깅할 때 어느 단계에서 터진 건지 구분이 안 된다. dump는 "기존 포스트 데이터를 읽어서 중간 표현으로 뽑아낸다", migrate는 "그 중간 표현을 DB에 적재한다" — 이렇게 역할을 쪼개면 중간 결과물을 눈으로 확인할 수 있고, migrate만 재실행하거나 dump만 재실행하는 것도 가능해진다. 파이프라인을 단계별로 분리하는 건 ETL이든 마이그레이션 스크립트든 기본기에 해당하는 부분인데, 급하게 만들다 보면 놓치기 쉽다.

스크립트 구성 훑어보기

이번에 추가된 파일은 세 개다.

파일 역할
scripts/db/dump-posts.js 기존 포스트 소스(파일 등)를 읽어 중간 데이터로 추출
scripts/db/migrate-posts-to-db.js 추출된 데이터를 DB에 적재
scripts/db/README.md 실행 순서, 환경변수, 주의사항 문서화

README를 함께 커밋한 게 작지만 중요한 포인트다. 마이그레이션 스크립트는 한 번 실행하면 잊히는 코드처럼 보이지만, 실제로는 팀원이 로컬에서 재현하거나, 스테이징 환경 세팅하거나, 이슈 생겼을 때 다시 돌려야 하는 상황이 꼭 온다. 그때 문서가 없으면 "이거 어떻게 써요?"가 슬랙으로 날아온다. 팀장 입장에서는 스크립트 작성보다 README 챙기는 게 더 중요하다고 본다.

마이그레이션 스크립트를 짤 때 일반적으로 신경 써야 하는 것들을 정리하면 이렇다.

  • 멱등성(idempotency): 같은 스크립트를 두 번 돌렸을 때 데이터가 중복되면 안 됨. upsert 또는 실행 전 중복 체크 로직 필요
  • dry-run 옵션: 실제로 DB에 쓰기 전에 어떤 데이터가 들어갈지 미리 확인할 수 있어야 함
  • 오류 처리와 롤백: 적재 도중 실패했을 때 부분 적재 상태로 남지 않도록 트랜잭션 혹은 실패 로그 관리
  • 진행 상황 출력: 포스트가 수십 개면 모르겠지만 수백 개 이상이면 진행 바나 카운터 없이는 스크립트가 멈춘 건지 살아있는 건지 알 수가 없음
// 예: 멱등성을 위한 upsert 패턴 (의사코드)
for (const post of posts) {
  await db.posts.upsert({
    where: { slug: post.slug },
    update: { ...post },
    create: { ...post },
  });
  console.log(`[${++count}/${total}] migrated: ${post.slug}`);
}

실제 구현이 어떻게 되어 있는지는 코드를 봐야 알지만, 이런 패턴을 기본으로 깔고 가는 게 안전하다.

회고

chore로 분류했지만 체감 난이도는 꽤 있었다. 파일 기반 데이터를 파싱할 때 frontmatter 처리나 날짜 포맷, 카테고리 정규화 같은 엣지 케이스가 생각보다 많이 나왔다. 이런 마이그레이션 유틸은 "완성되면 버리는 코드"가 아니라, 환경이 바뀌거나 스키마가 수정될 때 또 손대야 할 코드다. 그러니까 대충 만들면 나중에 내가 다시 고통받는다.

README까지 포함해서 커밋한 건 잘한 판단이라 생각한다. 팀에서 운영 스크립트 문서화 습관을 만들어가는 것, 결국 이런 작은 선택들이 쌓이는 거다.

끝.


🛒 이 글과 어울리는 추천 상품

*위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.

댓글 0

첫 댓글 달아줘.