🗄️ TypeORM6. Migration & Schema📖 개요

06 · Migration & Schema — 스키마 진화의 5층

이 챕터가 답하는 질문: TypeORM의 스키마는 어떻게 진화하나, 그리고 프로덕션에서 절대 깨지지 않게 하려면 무엇을 알아야 하나? 한 줄 답 (Pyramid Top): synchronize: true개발용 자동 폭탄이고, 프로덕션의 진실은 generate된 SQL을 사람이 읽고 보정한 migration이다 — 그 위에 expand → migrate → contract의 3단계가 zero-downtime을 만든다.”

이전 챕터(05 — Transaction & DataSource)가 한 시점의 일관성을 다뤘다면, 이번 챕터는 시간을 가로지르는 일관성을 다룬다. 스키마는 코드와 함께 진화해야 하는데, TypeORM은 그것을 두 가지 모순된 방법으로 제공한다 — synchronize(개발용)와 migration(프로덕션용). 이 챕터는 두 길이 갈라지는 지점왜 한쪽이 거의 항상 잘못된 선택인지를 보여준다.


챕터 지도


읽는 순서

#문서읽는 데누구에게
0101-synchronize-is-evil8분”synchronize 켜놓고 잘 살고 있는데?”라는 사람
0202-migration-anatomy10분migration 파일을 읽고 쓰는 사람
0303-migration-generate12분generate왜 가끔 이상한 SQL을 뱉는가를 알고 싶은 사람
0404-migration-run-and-revert10분운영에서 runrevert실제로 돌리는 사람
0505-production-strategy15분다운타임 없는 배포가 왜 한 PR로 안 되는가가 궁금한 사람
0606-cli-and-typeorm-config10분typeorm 명령어가 왜 ESM에서 안 도는지 막혀본 사람

추천 동선: 01 → 02 → 03 → 04. 여기까지 개념과 메커니즘. 05는 조직에 배포 절차가 있는 사람 대상. 06은 세팅이 안 돌 때 디버깅 모음.


두 모드 비교 — synchronize vs migration

TypeORM은 스키마를 두 가지 완전히 다른 방식으로 관리한다. 둘은 절대로 같이 쓰지 않는다.

synchronize: truemigration
트리거DataSource.initialize()typeorm migration:run
비교 대상entity 메타 ↔ 실제 DB파일 시스템의 마이그레이션 파일
실행 SQL자동 생성·자동 실행코드로 작성된 SQL을 순서대로
사람의 검토없음git diff·PR 리뷰
다운/롤백불가능migration:revert로 한 칸씩
가역성DROP COLUMN이 데이터까지 날린다down() 메서드가 명시적으로 복구
권장 환경로컬 개발 + 일회용 테스트 DB그 외 모든 환경
깨질 때 신호”어 데이터가 사라졌네”migration이 실패하면 transaction이 rollback

이 챕터의 단 하나의 문장이 있다면: synchronize: true는 production에서 rm -rf 만큼 위험하다.”


Why — 왜 스키마 관리가 따로 챕터인가

세 가지 잘못된 직관이 거의 모든 마이그레이션 사고의 출처다.

잘못된 직관실제어디서 다루나
”synchronize는 개발 편의 기능이다”데이터 보존 보장 없는 자동 DDL이다. 한 번 실수하면 복구 불가.01
”migration:generate가 다 알아서 해준다”entity ↔ DB의 diff 추측이다. 모호한 경우 TypeORM은 자기 멋대로 결정한다.03
”마이그레이션은 한 PR로 끝난다”큰 테이블의 컬럼 변경은 3개 PR 이상으로 expand-contract을 거쳐야 한다.05

이 챕터는 세 직관이 어디서 깨지는지, 그리고 어떻게 보정하는지를 6개 문서로 나눠 보여준다.


How — 어떻게 읽나

  • 로컬에서 처음 TypeORM을 켜는 사람: 01만 봐도 오늘 밤 잠을 잘 수 있다. synchronize: false가 첫 번째 PR이 되어야 한다.
  • 이미 migration을 쓰지만 가끔 깨지는 사람: 02 → 03 → 04. 왜 generate가 이상한 SQL을 만들었는지 03에서 찾을 수 있다.
  • 프로덕션에 큰 컬럼 변경을 배포해야 하는 사람: 05 필독. 한 PR로 끝낼 수 없는 일이다.
  • NestJS + TypeORM + ESM 조합으로 CLI가 안 도는 사람: 06.

What — 6개 문서 한 줄 결론

#문서한 줄 답
01synchronize is evil데이터 보존 보장 없는 자동 DDL이라 — 컬럼 rename은 DROP + ADD로 해석되어 모든 데이터가 사라진다.
02Migration anatomymigration 파일은 up·down 두 함수 + QueryRunner 인자가 전부다 — 타임스탬프 prefix가 실행 순서를 결정한다.
03migration:generateTypeORM이 entity 메타와 현재 DB 스키마를 비교해 diff SQL을 뱉는다 — generate된 SQL은 초안이고, 진실은 사람이 보정한 commit이다.
04run & revertmigrations 테이블이 어디까지 적용됐는지를 기록하고, 각 migration은 기본적으로 transaction 안에서 돈다 — DDL이 트랜잭션을 깨면 반쯤 적용된 상태가 남는다.
05Production strategyexpand → migrate → contract — 새 컬럼을 추가하고 코드를 양쪽 호환으로 배포한 뒤, 데이터 복사, 마지막에 옛 컬럼 제거. 한 PR로 끝낼 수 없다.
06CLI & datasourceTypeORM CLI는 ts-node 위에서 datasource.ts를 평가한다 — ESM·"type": "module"·alias path에서 반복적으로 깨진다.

What-if — 이 챕터를 건너뛰면

  • synchronize를 그대로 켜고 배포: 컬럼 이름만 바꿔도 모든 데이터가 사라진다 — git revert로도 복구 불가능.
  • migration 파일을 git에 안 올림: 다른 개발자의 DB와 영원히 어긋난다 — “내 컴에선 되는데” 무한반복.
  • generate된 SQL을 검토 없이 실행: enum 변경·컬럼 type 변경·FK 변경에서 데이터 손실. TypeORM은 모르고 사람이 알아야 한다.
  • migration이 transaction이라고 믿음: PostgreSQL의 일부 DDL(예: CREATE INDEX CONCURRENTLY)은 트랜잭션 안에서 못 돈다transaction: false 옵션을 모르면 막힌다.
  • production에서 큰 테이블에 ALTER: PostgreSQL이 table-level lock을 잡아 수십 분간 서비스 중단. expand-contract 없이는 못 푼다.
  • CLI 설정을 안 잡음: 코드 한 줄도 못 짜고 세팅에서 하루를 날린다 — TypeORM의 가장 흔한 좌절 지점.

Insight — 한 단락 이야기

“마이그레이션은 코드가 아니다 — 시간의 증인이다”

일반 코드는 현재의 정답만 갖고 있어도 된다. 어제의 잘못된 코드는 git history에 박제되지만, 실행되는 건 오늘의 코드다. 마이그레이션은 다르다 — 어제의 SQL을 그대로 보존해야 새 환경에서 같은 스키마가 재생된다. 그래서 마이그레이션 파일은 한 번 commit되면 영원히 수정 금지다 — 다른 환경의 history와 어긋나는 순간, 그 환경은 복구 불가능해진다. TypeORM의 synchronize는 그 시간 축을 지워버리는 도구다. 현재의 entity만 보고 DB를 거기에 맞춘다. 그래서 개발용으로는 빠르지만, 시간을 가로지르는 일관성이 필요한 프로덕션에서는 데이터 살인 무기가 된다. 그리고 zero-downtime은 — 새 코드와 옛 코드가 동시에 살아있는 시간명시적으로 다루는 일이다. expand-contract은 한 시점의 스냅샷이 아니라 몇 분~며칠에 걸친 과정이다. 마이그레이션이 코드보다 어려운 이유는 — 시간이 자기 자리를 차지하기 때문.


Mermaid 4색 규약

이 챕터의 모든 다이어그램은 도메인 공통 규약을 따른다.


한 단락 요약

TypeORM의 스키마 관리는 두 모드가 있다 — synchronize(개발용 자동 폭탄)와 migration(프로덕션의 유일한 진실). 01이 왜 synchronize가 위험한지, 02~04가 migration 파일의 해부·생성·실행을 다룬다. 05는 프로덕션에서 zero-downtime을 만드는 expand-contract 패턴, 06은 CLI가 안 돌 때 디버깅이다. 이 챕터를 끝내면 “마이그레이션은 코드가 아니라 시간의 증인이다” 라는 한 문장이 남는다 — 그리고 *“한 PR로 못 끝낸다”*는 결심이 따라온다. 다음 챕터(07-performance-n-plus-1)는 완벽한 스키마조차 런타임에 N+1로 죽을 수 있다를 다룬다.