요약
db/schema.rb 가 .gitignore 대상(.gitignore:44)이라 각 환경마다 로컬 덤프만 존재한다. Rails 는 마이그레이션 실행 시 이 파일을 자동으로 덮어쓰므로, DB 스키마가 손상되면 그 손상이 조용히 schema.rb 에 기록되고 git status 는 끝까지 깨끗하다.
실제로 발생한 사례
#846 작업 중 마이그레이션 검증을 위해 아래를 실행했다.
RAILS_ENV=test rails db:migrate:redo:primary VERSION=20260502235353
이 마이그레이션은 add_column :sites, :url 을 하는데, down 의 remove_column 이 나중 마이그레이션(20260524104358)이 만든 부분 unique 인덱스 index_sites_unique_url_for_rss 를 CASCADE 로 함께 삭제했다. 재-up 은 컬럼만 되돌리므로 인덱스는 복구되지 않는다.
그 직후 Rails 가 손상된 DB 를 db/schema.rb 에 자동 덤프하면서 인덱스 정의 줄이 사라졌다. gitignore 대상이라 diff 에 아무것도 안 나타났다.
더 나쁜 건 그 다음이다. test 환경의 db:migrate 는 schema.rb 가 있으면 마이그레이션을 실행하지 않고 schema.rb 를 로드한다. 그래서 DB 를 drop/create 하고 다시 마이그레이션해도 손상 상태가 계속 재현됐고, 마치 마이그레이션 자체의 버그처럼 보였다.
schema.rb 를 치우고 마이그레이션 45개를 실제로 실행시켜야만 진상이 드러났다 — 마이그레이션은 정상이며 인덱스를 제대로 만든다.
파생 확인 — 마이그레이션 산출물과 실제 DB 의 드리프트
같은 방식으로 "마이그레이션을 실제로 돌린 결과" 와 프로덕션형 덤프를 비교하니 차이가 있었다.
| 대상 |
프로덕션형 DB |
마이그레이션 산출물 |
posts |
index_posts_on_depth 있음 |
없음 |
users.name |
limit: 50 |
limit 없음 |
users.encrypted_password |
default: "" |
default 없음 |
articles |
index_articles_on_related_and_published_at |
index_articles_on_is_related_and_published_at |
articles |
없음 |
index_articles_on_site_id_and_published_at |
taggings |
idx_on_taggable_id_taggable_type_tagger_id_context_72eb419f98 |
taggings_idy |
프로덕션에 수동 DDL 이 들어간 것인지, 적용 후 마이그레이션 파일이 수정된 것인지는 이번 조사로 구분하지 못했다. schema.rb 가 버전 관리되고 있었다면 어느 커밋에서 갈라졌는지 diff 로 추적 가능했을 문제다.
영향
- 스키마 손상(인덱스·제약 유실)이 리뷰에서 걸러지지 않는다
- 환경 간 스키마 드리프트를 탐지할 방법이 없다
db:migrate 가 실제로 마이그레이션을 돌렸는지 schema 를 로드했는지 구분이 어려워 디버깅이 오래 걸린다
제안
db/schema.rb 를 .gitignore 에서 제거하고 커밋 대상으로 되돌린다. 드리프트가 PR diff 에 드러난다.
- 또는
config.active_record.schema_format = :sql 로 전환해 db/structure.sql 을 커밋한다. ra_news 스키마·부분 인덱스·hairtrigger 트리거까지 정확히 표현되므로 이 프로젝트 구성에는 이쪽이 더 맞을 수 있다.
- 관례 문서화: 컬럼 추가 마이그레이션에
db:migrate:redo 를 쓰면 이후 마이그레이션이 만든 인덱스가 CASCADE 삭제된다. 검증은 db:drop → db:create → 전체 db:migrate 로 한다.
관련: #846, #933
요약
db/schema.rb가.gitignore대상(.gitignore:44)이라 각 환경마다 로컬 덤프만 존재한다. Rails 는 마이그레이션 실행 시 이 파일을 자동으로 덮어쓰므로, DB 스키마가 손상되면 그 손상이 조용히 schema.rb 에 기록되고git status는 끝까지 깨끗하다.실제로 발생한 사례
#846 작업 중 마이그레이션 검증을 위해 아래를 실행했다.
이 마이그레이션은
add_column :sites, :url을 하는데, down 의remove_column이 나중 마이그레이션(20260524104358)이 만든 부분 unique 인덱스index_sites_unique_url_for_rss를 CASCADE 로 함께 삭제했다. 재-up은 컬럼만 되돌리므로 인덱스는 복구되지 않는다.그 직후 Rails 가 손상된 DB 를
db/schema.rb에 자동 덤프하면서 인덱스 정의 줄이 사라졌다. gitignore 대상이라 diff 에 아무것도 안 나타났다.더 나쁜 건 그 다음이다. test 환경의
db:migrate는 schema.rb 가 있으면 마이그레이션을 실행하지 않고 schema.rb 를 로드한다. 그래서 DB 를 drop/create 하고 다시 마이그레이션해도 손상 상태가 계속 재현됐고, 마치 마이그레이션 자체의 버그처럼 보였다.schema.rb 를 치우고 마이그레이션 45개를 실제로 실행시켜야만 진상이 드러났다 — 마이그레이션은 정상이며 인덱스를 제대로 만든다.
파생 확인 — 마이그레이션 산출물과 실제 DB 의 드리프트
같은 방식으로 "마이그레이션을 실제로 돌린 결과" 와 프로덕션형 덤프를 비교하니 차이가 있었다.
postsindex_posts_on_depth있음users.namelimit: 50users.encrypted_passworddefault: ""articlesindex_articles_on_related_and_published_atindex_articles_on_is_related_and_published_atarticlesindex_articles_on_site_id_and_published_attaggingsidx_on_taggable_id_taggable_type_tagger_id_context_72eb419f98taggings_idy프로덕션에 수동 DDL 이 들어간 것인지, 적용 후 마이그레이션 파일이 수정된 것인지는 이번 조사로 구분하지 못했다. schema.rb 가 버전 관리되고 있었다면 어느 커밋에서 갈라졌는지 diff 로 추적 가능했을 문제다.
영향
db:migrate가 실제로 마이그레이션을 돌렸는지 schema 를 로드했는지 구분이 어려워 디버깅이 오래 걸린다제안
db/schema.rb를.gitignore에서 제거하고 커밋 대상으로 되돌린다. 드리프트가 PR diff 에 드러난다.create_table "ra_news.sites") 문제로 덤프가 환경마다 달라질 수 있어 함께 정리 필요.config.active_record.schema_format = :sql로 전환해db/structure.sql을 커밋한다.ra_news스키마·부분 인덱스·hairtrigger 트리거까지 정확히 표현되므로 이 프로젝트 구성에는 이쪽이 더 맞을 수 있다.db:migrate:redo를 쓰면 이후 마이그레이션이 만든 인덱스가 CASCADE 삭제된다. 검증은db:drop→db:create→ 전체db:migrate로 한다.관련: #846, #933