More at rubyonrails.org: Overview | Download | Deploy | Code | Screencasts | Documentation | Ecosystem | Community | Blog

마이그레이션

마이그레이션은 체계적이고 유기적인 수단으로 데이터베이스를 변경할 수 있는 편리한 방법입니다. 여러분은 손수 SQL을 수정할 수 있습니다. 그러나 수정후에 다른 개발자들에게 수정 사항을 알려줘야할 책임을 가지게 될겁니다. 그리고 다음에 제품을 배포 할때도 변경 사항이 실행되는지 이를 지켜봐야만 합니다.

액티브 레코드는 이미 실행된 마이그레이션을 추적합니다. 그래서 여러분이 해야할 일은 소스를 업데이트 하고 rake db:migrate 명령을 실행하는 일 뿐입니다. 액티브 레코드는 실행해야 할 마이그레이션만 작업을 진행합니다. 그리고 여러분의 데이터베이스의 구조에 부합하는 db/schema.rb 파일도 갱신합니다.

마이그레이션은 루비를 이용해서 이러한 변경 사항을 기술합니다. 이것의 굉장한 점은, 대부분의 액티브 레코드의 기능처럼 데이터베이스에 독립적이라는 것 입니다.: SELECT * 의 다양한 변화에 비한다면야, CREATE TABLE의 정확한 문법에 대해서 크게 걱정할 필요가 없습니다. (데이터베이스의 특별한 기능들을 위해 기존 SQL도 사용할 수 있습니다.) 예를 들어, 개발에서는 SQLite3 를 사용하지만, 제품에서는 MySQL을 사용할 수 있죠.

여러분은 다음 내용의 마이그레이션 관한 모든걸 배울 수 있습니다.:

이 가이드의 원본은 영문 버전인 Rails Database Migrations 입니다. 번역 과정에서 원본과 차이가 발생할 수 있습니다. 번역본은 원본의 참고로 생각해 주세요.

1 마이그레이션 분석

마이그레이션의 자세한 부분을 다루기 앞서, 여러분이 수행할 수 있는 몇 가지 예제가 있습니다.:

class CreateProducts < ActiveRecord::Migration
  def self.up
    create_table :products do |t|
      t.string :name
      t.text :description

      t.timestamps
    end
  end

  def self.down
    drop_table :products
  end
end

이 마이그레이션은 문자열 컬럼 name과 텍스트 컬럼 description으로 구성된 products 테이블을 추가합니다. 기본키(primary key) 컬럼 id도 역시 추가됩니다. 그렇지만 기본키에 관한 세팅은 기본 설정입니다. 액티브 레코드가 자동으로 생성하는 타임스탬프 컬럼인 created_atupdated_at도 추가됩니다. 이 마이그레이션은 간단하게 테이블을 삭제하는 것으로 되돌릴 수 있습니다.

마이그레이션은 스키마의 변경에만 그치는게 아닙니다. 마이그레이션을 데이터베이스내의 잘못된 데이터를 고치거나, 새로운 필드를 만드는데도 사용할 수 있습니다.:

class AddReceiveNewsletterToUsers < ActiveRecord::Migration
  def self.up
    change_table :users do |t|
      t.boolean :receive_newsletter, :default => false
    end
    User.update_all ["receive_newsletter = ?", true]
  end

  def self.down
    remove_column :users, :receive_newsletter
  end
end

이 마이그레이션은 receive_newsletter 컬럼을 user 테이블에 추가합니다. 우리는 새로운 사용자에 대해서는 기본 값을 false로 하기를 원하지만, 기존 사용자에 대해서도 옵션을 생각해야 합니다. 그래서 User 모델을 기존 사용자들에 대해 해당 플래그를 true로 세팅하는데 사용합니다.

마이그레이션 중에서 모델을 사용하는데 몇가지 주의사항 을 참고하세요.

1.1 마이그레이션은 클래스이다.

마이그레이션은 up(필요한 변경 사항을 수행합니다.)과 down(변경 사항을 되돌립니다.)을 구현한 ActiveRecord::Migration의 서브 클래스 입니다.

액티브 레코드는 데이터베이스 독립적인 방법으로 일반 데이터 정의 태스크를 수행하는 메소드들을 제공합니다. (여러분은 나중에 이에 관해서 자세히 배울 것입니다.):

  • create_table
  • change_table
  • drop_table
  • add_column
  • change_column
  • rename_column
  • remove_column
  • add_index
  • remove_index

만약, 여러분의 데이터베이스에 특화된 일을 수행할 필요가 있다면 (예컨데 외래키 제약사항 만들기), execute 함수로 임의의 SQL을 실행할 수 있습니다. 단지 마이그레이션은 보통 루비 클래스 입니다. 그래서 이런 함수에 대한 제약 사항은 없습니다. 예를 들어, 컬럼을 추가한 후에 이미 존재하는 레코드를 위해서 추가된 컬럼에 값을 세팅하는 코드를 작성할 수 있습니다. (필요시 여러분의 모델을 사용해야 합니다.)

데이터베이스가 스키마 변경을 위한 트랜젝션을 제공하면(가령 PostgreSQL 혹은 SQLite3), 마이그레이션은 트랜젝션으로 작업을 감쌉니다. 만약 이를 지원하지 않으면 (가령 MySQL), 마이그레이션이 실패 했을때 변경이 적용된 부분들이 되돌아가지(roll back) 못할 것입니다. 이 상황에서 변경된 부분을 손수 되돌려야 할 것입니다.

1.2 이름의 의미

마이그레이션은 각 마이그레이션 클래스당 하나의 파일로 db/migrate에 저장됩니다. 파일의 이름은 YYYYMMDDHHMMSS_create_products.rb한 형태의 포맷입니다. 이 이름은 UTC 타임스탬프 마이그레이션 식별자와 언더스코어(_ 모양의 글자)로 연결된 마이그레이션의 이름으로 구성됩니다. 마이그레이션 클래스의 이름( CamelCase 버전)은 반드시 파일 이름의 글자 부분과 맞아야합니다. 예를들어서 20080906120000_create_products.rb는 반드시 CreateProducts을 정의해야 하고, 20080906120001_add_details_to_products.rb는 반드시 AddDetailsToProducts을 정의해야 합니다. 만약 파일의 이름을 변경하려면, 반드시 파일 내부의 클래스의 이름도 변경해야 합니다. 그렇지 않으면, 레일즈는 클래스를 찾지 못해서 에러를 보고 할 것입니다.

레일즈는 내부적으로 오직 마이그레이션의 번호(타임스탬프를 의미)를 마이그레이션 식별에 사용합니다. 레일즈 2.1 이전에 마이그레이션 번호는 1부터 시작해서 마이그레이션이 생성될때 마다 증가하는 값을 취했습니다. 여러명의 개발자들이 함께 개발할때, 이 규칙은 소규모의 충돌 상황에서도 변경을 되돌리고나서 여러 마이그레이션의 번호를 다시 부여하는 상황을 만들었습니다. 레일즈 2.1에서 마이그레이션의 생성 시각을 식별하는데 사용해서 이러한 문제 상황을 대부분 피할 수 있게 되었습니다. config/application.rb에 다음 라인을 추가시키면 예전 방식의 번호 매기는 방식으로 돌아갈 수 있습니다.

config.active_record.timestamped_migrations = false

타임스탬프와 마이그레이션을 기록하는 조합은 레일즈가 여러 개발자들이 마이그레이션에 관여하면서 생기는 일반적인 상황을 다룰 수 있게 해줍니다.

예를들어서 앨리스가 마이그레이션 2008090612000020080906123000을 추가하고 밥이 20080906124500를 추가시킨 후 이를 실행합니다. 앨리스는 그녀의 마이그레이션에서 변경을 완료하고 점검합니다. 그리고 밥은 자신의 소스에 최근 변경 사항을 내려 받습니다. 레일즈는 앨리스의 두개의 마이그레이션을 아직 실행하지 않았다는걸 알 수있습니다. 그래서 밥이 실행하는 rake db:migrate은 실행되지 않은 앨리스의 마이그레이션을 실행합니다. (더 이후의 타임스템프 값을 가진 밥의 마이그레이션을 실행하는데도 말이죠.) 그리고 이 과정과 비슷하게 마이그레이션 다운(down)시에도 실행되지 않은 앨리스의 down 메소드를 실행하지 않습니다.

물론, 이는 팀간의 대화를 대체하는건 아닙니다. 예를 들어서, 만약 앨리스의 마이그레이션이 밥의 마이그레이션 실행에 필요한 테이블을 삭제하면, 문제는 확실히 발생합니다.

1.3 마이그레이션 변경하기

때로, 마이그레이션을 작성할때 여러분은 실수를 하게 됩니다. 이미 마이그레이션을 실행한 후에는, 단순히 마이그레이션을 수정하고 다시 실행하는 것으로 원하는 변경을 반영할 수는 없습니다.:레일즈는 이미 실행된 마이그레이션이라면, rake db:migrate를 수행할때 아무 것도 실행하지 않습니다. 여러분은 반드시 마이그레이션을 되돌(rollback)리고 (예를 들어, rake db:rollback를 사용), 마이그레이션을 수정하고 정확한 버전에 대하여 rake db:migrate를 실행해야 합니다.

일반적으로 마이그레이션 수정은 좋은 생각이 아닙니다.:여러분은 자신과 동료들을 위해서 추가적인 일을 만들어내고, 만약 제품에 이미 적용된 마이그레이션을 수정하면 골치 아픈 상황을 만들어 낼겁니다. 대신에 필요한 변경을 수행하는 새로운 마이그레이션을 만드세요. 소스 관리 도구에 커밋되지 않은(혹은 좀 더 일반적으로 표현하자면 여러분의 개발 머신 이외에 퍼지지 않은) 새로운 마이그레이션을 수정하는 편이 상대적으로 무해합니다. 이런건 그냥 상식이죠.

2 마이그레이션 만들기

2.1 모델 만들기

모델과 발판(scaffold) 제너레이터는 새로운 모델을 추가를 위한 적합한 마이그레이션을 만들 것입니다. 이 마이그레이션은 이미 관련된 테이블 생성에 대한 코드를 담고 있을 것입니다. 만약 레일즈에게 여러분이 원하는 컬럼들을 알려주면 이 역시 함께 생성됩니다. 예를들어서 다음과 같이 실행하면

rails generate model Product name:string description:text

다음과 같은 마이그레이션이 생성됩니다.

class CreateProducts < ActiveRecord::Migration
  def self.up
    create_table :products do |t|
      t.string :name
      t.text :description

      t.timestamps
    end
  end

  def self.down
    drop_table :products
  end
end

여러분이 원하는 컬럼을 얼마든지 name/type 형태로 추가할 수 있습니다. 기본값으로, t.timestamps가 추가되어 있습니다.(이 구문으로 생성된 updated_atcreated_at 컬럼은 액티브 레코드에 의해서 자동으로 채워집니다.)

2.2 독립적인(Standalone) 마이그레이션 만들기

만약 다른 목적으로 (가령, 이미 존재하는 테이블에 컬럼을 추가하는 경우) 마이그레이션을 만들려면, 마이그레이션 제너레이터를 사용할 수 있습니다.

rails generate migration AddPartNumberToProducts

이 명령은 비어있지만, 정확한 이름을 가진 마이그레이션을 만듭니다.:

class AddPartNumberToProducts < ActiveRecord::Migration
  def self.up
  end

  def self.down
  end
end

만약 마이그레이션 이름이 “AddXXXToYYY” 혹은 “RemoveXXXFromYYY” 이고 이후에 컬럼 이름과 타입을 입력하면 정확한 add_columnremove_column 구문이 생성될 것입니다.

rails generate migration AddPartNumberToProducts part_number:string

이 명령은 다음을 생성하죠.

class AddPartNumberToProducts < ActiveRecord::Migration
  def self.up
    add_column :products, :part_number, :string
  end

  def self.down
    remove_column :products, :part_number
  end
end

비슷하게,

rails generate migration RemovePartNumberFromProducts part_number:string

다음의 내용을 생성합니다.

class RemovePartNumberFromProducts < ActiveRecord::Migration
  def self.up
    remove_column :products, :part_number
  end

  def self.down
    add_column :products, :part_number, :string
  end
end

이런 마술 같은 컬럼 만들기는 하나만 할 수 있는게 아닙니다. 예를들어서

rails generate migration AddDetailsToProducts part_number:string price:decimal

명령은 다음을 생성합니다.

class AddDetailsToProducts < ActiveRecord::Migration
  def self.up
    add_column :products, :part_number, :string
    add_column :products, :price, :decimal
  end

  def self.down
    remove_column :products, :price
    remove_column :products, :part_number
  end
end

늘, 시작점이 될만한 것을 만들 수 있습니다. 여기에서 부터 여러분에 입맛에 맞게 추가하고 삭제하면 됩니다.

3 마이그레이션 작성하기

이제 제너레이터를 이용해서 여러분의 마이그레이션을 만들어 볼때 입니다!

3.1 테이블 만들기

마이그레이션 메소드인 create_table은 여러분의 편리한 도구 중 하나입니다. 보통 이렇게 사용합니다.

create_table :products do |t|
  t.string :name
end

이 코드는 name이란 이름의 컬럼을 가진 products 테이블을 만듭니다. (그리고 아래의 논의에서 처럼, 암시적인 id 컬럼도 생성합니다.)

이 객체는 테이블에 만들 컬럼 정보를 블록 형태로 실행합니다. 이를 수행하는 방법은 두가지가 있죠.:첫번째 (관습적인) 폼은 다음과 같습니다.

create_table :products do |t|
  t.column :name, :string, :null => false
end

두번째 폼은 “섹시(sexy)” 마이그레이션이라 불리며, 중복된 column 메소드를 날려버린 방법이죠. 대신에 string, integer 등의 메소드는 해당 타입의 컬럼을 생성합니다. 이후 파라미터는 동일합니다.

create_table :products do |t|
  t.string :name, :null => false
end

기본적으로, create_tableid라는 기본키(primary key)를 생성합니다. :primary_key 옵션을 이용해서 기본키(primary key)의 이름을 변경하거나, 기본키(primary key)를 원하지 않으면, (예를들어 HABTM 조인 테이블 같이요.) :id => false 를 넘기세요. 만약, 데이터베이스에 특화된 옵션을 넘기려면, :optionsSQL 조각을 위치 시킬 수 있습니다. 예를 들어서

create_table :products, :options => "ENGINE=BLACKHOLE" do |t|
  t.string :name, :null => false
end

이 코드는 ENGINE=BLACKHOLE을 테이블 생성하는 SQL 구문에 추가할 것입니다. (MySQL은 기본 값은 ENGIN=InnoDB 입니다.)

액티브 레코드가 지원하는 타입은 :primary_key, :string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean 입니다.

이들은 각 데이터베이스에 적합한 타입으로 짝지어 집니다. 예를들어서 MySQL을 사용할때 :stringVARCHAR(255)와 짝지어 집니다. 섹시하지 않은(non-sexy) 문법을 사용하면, 액티브 레코드가 지원하지 않는 타입의 컬럼도 만들수 있습니다. 예를들어

create_table :products do |t|
  t.column :name, 'polygon', :null => false
end

그렇지만, 아마 이런 코드는 데이터베이스 이식성이 떨어지게 되겠죠.

3.2 테이블 변경

create_table과 가까운 메소드는 존재하는 테이블을 변경에 사용되는 change_table입니다. 이건 create_table과 비슷한 모습으로 사용되지만 객체가 실행하는 블록에 더 많은 트릭이 있습니다. 예를들어

change_table :products do |t|
  t.remove :description, :name
  t.string :part_number
  t.index :part_number
  t.rename :upccode, :upc_code
end

이 코드는 descriptionname을 삭제, part_number 컬럼을 만들고 인덱스를 부여합니다. 마지막으로 upccode 컬럼을 upc_code로 이름을 변경합니다. 이건 다음의 코드와 동일합니다.

remove_column :products, :description
remove_column :products, :name
add_column :products, :part_number, :string
add_index :products, :part_number
rename_column :products, :upccode, :upc_code

테이블 이름을 반복하지 말고, 수정할 테이블과 관련된 구문을 한곳에 모으세요. 각 변환 코드 역시 줄어듭니다. 예를들어, remove_columnremove로 그리고 add_indexindex가 됩니다.

3.3 특별한 헬퍼

액티브 레코드는 공통의 기능에 대해 몇가지 간단한 방법을 제공합니다. 가장 자주 쓰이는 예가 created_atupdated_at 컬럼을 함께 추가할때, 이를 메소드로 수행 할 수 있습니다.:

create_table :products do |t|
  t.timestamps
end

이 코드는 방금 언급한 두 컬럼(거기에 id컬럼 까지)을 가진 새로운 products 테이블을 만듭니다.

change_table :products do |t|
  t.timestamps
end

이렇게 기존 테이블에 두 컬럼을 추가할 수도 있죠.

references로 불리는 다른 헬퍼도 있습니다. (belongs_to 로도 가능합니다.) 가장 간단한 형태로 예를 들어본다면,

create_table :products do |t|
  t.references :category
end

이 코드는 정확한 타입을 가진 category_id 컬럼을 만듭니다. 컬럼의 이름이 아니라 모델의 이름을 넘기는 부분에 주목하세요. 액티브 레코드는 여러분을 위해 id를 이름에 추가합니다. 만약, 여러 형과 belongs_to 관계를 가지고 있다면, references에 필요한 컬럼 정보도 추가해야 합니다.:

create_table :products do |t|
  t.references :attachment, :polymorphic => {:default => 'Photo'}
end

이 코드는 attachment_id 컬럼과 문자열이면서 기본값이 ’Photo’인 attachment_type 컬럼을 추가할 것입니다.

references 헬퍼는 외래키(foreign key) 제약사항을 추가하지는 않습니다. 이를 위해서 execute를 이용하거나, 외래키(foreign key) 지원 을 추가하는 플러그인을 사용하세요.

액티브 레코드가 제공하는 헬퍼가 만족스럽지 않으면 execute 함수로 원하는 SQL을 실행하세요.

개별 메소드에 대한 더 자세한 내용과 예제는 API 문서를 참고하세요. 상세한 문서들은 다음과 같습니다. ActiveRecord::ConnectionAdapters::SchemaStatements (이 문서는 updown 내에서 사용할수 있는 메소드 정보를 제공합니다.), ActiveRecord::ConnectionAdapters::TableDefinition (이 문서는 creat_table을 사용할때 객체가 실행하는 블록내에서 사용할 수 있는 메소드 정보를 제공합니다.), 그리고 ActiveRecord::ConnectionAdapters::Table (이 문서는 change_table을 사용할때 객체가 실행하는 블록에서 사용할 수 있는 메소드 정보를 제공합니다.).

3.4 down 메소드 작성하기

마이그레이션의 down 메소드는 up 메소드가 수행한 변경을 되돌립니다. 다른 말로하자면, up을 통해서 변경된 데이터베이스 스키마는 down을 통해서 되돌려 집니다. 예를들어, 만약 여러분이 up 메소드에서 테이블을 만들었다면, 여러분은 down 메소드에서 이를 제거해야 합니다. 현명하게 up 메소드의 반대 순서로 만드세요. 예를들면

class ExampleMigration < ActiveRecord::Migration

  def self.up
    create_table :products do |t|
      t.references :category
    end
    #add a foreign key
    execute <<-SQL
      ALTER TABLE products
        ADD CONSTRAINT fk_products_categories
        FOREIGN KEY (category_id)
        REFERENCES categories(id)
    SQL

    add_column :users, :home_page_url, :string

    rename_column :users, :email, :email_address
  end

  def self.down
    rename_column :users, :email_address, :email
    remove_column :users, :home_page_url
    execute "ALTER TABLE products DROP FOREIGN KEY fk_products_categories"
    drop_table :products
  end
end

때로, 마이그레이션을 되돌릴 수 없게 될 수 있습니다. 가령, 데이터를 삭제하는 경우 말이죠. 이렇게 마이그레이션을 되돌릴 수 없을때는 down 메소드에서 IrreversibleMigration을 발생 시키세요. 만약에 다른이가 여러분의 마이그레이션을 되돌리려고 시도하면, 에러 메세지가 불가능 하다는 사실을 안내해 줄 것입니다.

4 마이그레이션 실행하기

레일즈는 마이그레이션 작업을 위한 rake 태스크 조합을 제공합니다. 최초의 마이그레이션은 아마도 여러분이 사용하게될 db:migrate와 관련있습니다. 가장 기본적인 동작은 아직 실행되지 않은 모든 마이그레이션의 up 메소드를 실행하는 것 입니다. 만약 마이그레이션이 없더라도, 일을 무사히 마칩니다.

db:migrate 실행하면 데이터베이스 구조에 부합하는 db/schema.rb 파일을 업데이트하는 db:schema:dump 태스크도 실행하는 걸 기억하세요.

원하는 버전을 지정하면, 액티브 레코드는 필요한 마이그레이션(up 혹은 down)을 정해진 버전까지만 실행합니다. 버전은 마이그레이션 파일 앞 부분의 숫자 접두어 입니다. 예를 들어서 버전 20080906120000을 실행하려 이렇게 합니다.

rake db:migrate VERSION=20080906120000

현재 버전보다 더 크면, 20080906120000를 포함해서 해당 버전까지 모든 마이그레이션을 실행합니다.(다른 말로, ‘마이그레이션 올리기’라고 합니다.) 만약, 마이그레이션 내리기(롤백을 의미)를 수행하면 20080906120000까지 모든 마이그레이션의 down 메소드를 실행하지만 20080906120000을 포함하지는 않습니다.

4.1 롤백 하기

흔한 테스크는 마지막 마이그레이션을 되돌리는 일 입니다. 예컨데, 실수를 한 후 이를 고치기를 원할때죠. 롤백을 위해 관련된 버전 숫자를 따라가는 것보다 이렇게 하는게 더 쉽죠.

rake db:rollback

이 명령은 최근 마이그레이션의 down을 실행합니다. 만약 여러번의 undo가 필요하면 STEP 파라미터를 이용할 수 있습니다.:

rake db:rollback STEP=3

이 명령은 최근 마이그레이션 3개의 down 메소드를 실행합니다.

db:migrate:redo 태스크는 롤백을 하고 다시 마이그레이션을 수행하는데 사용하는 단축 명령입니다. 만약 더 많은 버전을 한꺼번에 되돌아가고 싶다면, db:rollback 태스크에서 여러분이 STEP 파라미터를 사용한 것 처럼 사용할 수 있습니다. 예를들면 다음과 같습니다.

rake db:migrate:redo STEP=3

이 레이크 태스크들은 db:migrate와 함께하지 않습니다. 그들은 단순히 여러분이 특정한 버전으로 마이그레이그션을 필요 없을때까지 좀더 편한 방법을 제공할 뿐입니다.

마지막으로, db:reset 태스크는 모든 데이터베이스를 삭제하고 현재의 스키마를 재생성합니다.

db:reset은 모든 마이그레이션을 실행하는 것과 동일하지는 않습니다. – schema.rb 을 참고하세요.

4.2 지정 하기

만약, 원하는 버전의 마이그레이션만 올리거나(up) 내릴(down) 필요할때, db:migrate:updb:migrate:down을 이용할 수 있습니다. 적당한 버전을 지정해주면, 관련된 마이그레이션 해당 버전의 up 혹은 down 메소드를 호출 합니다. 예를들어

rake db:migrate:up VERSION=20080906120000

이 명령은 20080906120000 마이그레이션의 up 메소드를 실행합니다. 이 태스크는 마이그레이션이 이미 실행되었는지 여부도 검사합니다. 그래서 예컨데, db:migrate:up VERSION=20080906120000 명령은 액티브 레코드가 이미 20080906120000 버전을 실행했다고 생각되면 아무 것도 수행하지 않습니다.

4.3 결과 출력하기

기본적으로, 마이그레이션은 여러분에게 정확이 어떤 일이 진행되고 있고, 얼마나 시간이 걸렸는지 보고합니다. 테이블을 만들고, 인덱스를 추가하는 마이그레이션 중 출력되는 결과는 다음과 같습니다.:

20080906170109 CreateProducts: migrating
-- create_table(:products)
   -> 0.0021s
-- add_index(:products, :name)
   -> 0.0026s
20080906170109 CreateProducts: migrated (0.0059s)

몇가지 메소드는 이러한 모든 출력을 제어하는 기능을 제공합니다.

  • suppress_messages는 메소드의 블록에서 만들어지는 어떠한 결과도 출력하지 않습니다.
  • say는 텍스트를 출력합니다. (두번째 인자는 들여쓰기를 할 것인지 여부를 결정합니다.)
  • say_with_time는 메소드에 주어진 블록의 수행 시간과 함께 텍스트를 출력합니다. 만약, 그 블록이 정수를 반환하면, 그 숫자를 열(row)로 생각해서 출력 결과에 반영합니다.

예를들어서, 이 마이그레이션은

class CreateProducts < ActiveRecord::Migration
  def self.up
    suppress_messages do
      create_table :products do |t|
        t.string :name
        t.text :description
        t.timestamps
      end
    end
    say "Created a table"
    suppress_messages {add_index :products, :name}
    say "and an index!", true
    say_with_time 'Waiting for a while' do
      sleep 10
      250
    end
  end

  def self.down
    drop_table :products
  end
end

다음과 같은 결과를 출력합니다.

20080906170109 CreateProducts: migrating
  Created a table
   -> and an index!
  Waiting for a while
   -> 10.0001s
   -> 250 rows
20080906170109 CreateProducts: migrated (10.0097s)

만약, 액티브 레코드 실행중에 출력을 원하지 않을때, rake db:migrate VERBOSE=false 명령어로 실행하면 어떠한 출력도 하지 않을 겁니다.

5 여러분의 마이그레이션에서 모델 사용하기

마이그레이션에서 데이터를 만들거나 갱신할때, 자주 모델 중에 하나를 사용하고 싶어지죠. 아무튼, 모델은 관련있는 데이터를 쉽게 접근하는 방법을 제공하니까요. 사용 할 수는 있습니다만, 몇가지 주의 사항에 주목해 주세요.

예를들어, Product 모델을 이용해서 관련 테이블의 한 열을 갱신한다고 생각해 보죠. 앨리스는 Product 모델을 업데이트하고, 새로운 컬럼과 유요성 검증을 추가 했습니다. 밥이 휴가에서 돌아와서, 자신의 소스를 업데이트하고 rake db:migrate를 이용해서 마이그레이션을 실행합니다. 밥이 마이그레이션을 실행하는 시점에서 Product 모델에 앨리스가 추가한 데이터 검증이 추가되어 있는 상황이죠. 그런데 데이터베이스는 아직 옛날 데이터이고 컬럼을 가지고 있지 않아요. 데이터 검증에 필요한 컬럼을 가지고 있지 않아서 에러가 발생합니다.

저는 직접 SQL을 이용하지 않고, 데이터베이스의 자료를 그냥 갱신하기를 원하는 상황이 자주 발생헤요.:어떤 특정한 모델에 국한되지 않습니다. 이를 해결하기 위한 한가지 패턴은 모델의 사본을 마이그레이 내부에 정의하는 겁니다. 예를들면:

class AddPartNumberToProducts < ActiveRecord::Migration
  class Product < ActiveRecord::Base
  end

  def self.up
    ...
  end

  def self.down
    ...
  end
end

일런 패턴의 마이그레이션은 Product 모델의 최소의 사본을 가지며 작업하고, 어플리케이션에 정의된 Product에 대한 걱정을 할 필요가 없습니다.

5.1 모델 변경 처리하기

성능 때문에 컬럼에 관한 정보는 객체에 임시 저장(Cached)됩니다. 예를들어, 여러분이 테이블에 컬럼을 추가하고 나서 연관된 모델을 이용해서 새로운 데이터를 입력하려고 하면 모델은 과거 컬럼 정보를 이용하려 합니다. reset_column_information 메소드를 이용하면 액티브 레코드에게 강제로 컬럼 정보를 다시 읽으라고 명령할 수 있습니다. 예를 들면 다음과 같습니다.:

class AddPartNumberToProducts < ActiveRecord::Migration
  class Product < ActiveRecord::Base
  end

  def self.up
    add_column :product, :part_number, :string
    Product.reset_column_information
    ...
  end

  def self.down
    ...
  end
end

6 스키마 덤프와 여러분

6.1 스키마 파일은 무엇을 위해 존재하는가?

마이그레이션은 아마도 데이터베이스를 위한 믿을만한 소스가 되지는 못합니다. 이 부분은 db/schema.rb 나 액티브 레코드가 데이터베이스 조사해서 생성한 SQL 파일의 역할이죠. 이 파일은 사용자가 손수 수정 하도록 의도된 파일이 아닙니다. 단지 현재 데이터베이스의 상태를 표현합니다.

어플리케이션의 새로운 인스턴스를 배포할때 마이그레이션의 전체 과정을 재현할 필요는 없습니다. (그리고 이건 에러가 발생하기도 쉽죠) 현재 스키마의 기술된 내용을 데이터베이스에 적재하는 편이 훨씬 단순하고 빠릅니다.

예를 들어, 테스트 데이터베이스를 만드는 방법을 생각해보죠.:현재 개발 데이터베이스를 덤프하고(db/schema.rbdb/development.sql 이든) 나서 테스트 데이터베이스에 이것을 적재합니다.

스키마 파일은 액티브 레코드가 어떠한 속성(attribute)를 가지고 있는지 한눈에 보기에도 유용합니다. 이 정보는 모델의 코드에 들어 있지 않고, 흔히 여러 마이그레이션에 흩어 집니다. 그러나, 스키마 파일안에는 모든 정보가 담겨있죠. annotate_models 플러그인은 이런 정보를 각 모델의 상단에 요약해서 주석으로 붙여주는데, 이것도 흥미 있을 겁니다.

6.2 스키마 덤프의 유형

스키마 덤프에는 두가지 방법이 있습니다. config.active_record.schema_format 값으로 config/application.rb안에 세팅됩니다. :sql 이나 :ruby를 가질 수 있습니다.

만약 :ruby를 스키마로 선택했다면, db/schema.rb에 저장됩니다. 이 파일을 보면, 매우 거대한 마이그레이션 하나를 발견할 겁니다.

ActiveRecord::Schema.define(:version => 20080906171750) do
  create_table "authors", :force => true do |t|
    t.string   "name"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "products", :force => true do |t|
    t.string   "name"
    t.text     "description"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string   "part_number"
  end
end

많은 방법 중 이것이 가장 정확합니다. 데이터베이스를 조사하고 create_table, add_index 등을 이용하여 구조화된 표현으로 이 파일이 만들어 집니다. 이 파일은 데이터베이스 독립적이기 때문에 액티브 레코드를 지원하는 어떤 데이터베이스에도 적재할 수 있습니다. 만약 여러분이 어플리케이션을 배포할때 다양한 데이터베이스를 대상으로 한다면 이는 매우 유용합니다.

그렇지만, 교환 조건(trade-off)가 있죠.:db/schema.rb는 데이터베이스에 특화된 표현을 할수 없습니다. 가령 외래키(foreign key) 제약 사항, 트리거(trigger)나 저장 프로시저(stored procedure) 같은거 말이죠. 마이그레이션 중에 여러분은 맞춤형 SQL 구문을 실행수 있지만, 스키마 덤프 엔진은 데이터베이스에서 그러한 구문을 만들어 낼 수 없습니다. 만약 이런 기능을 만드시 사용해야한다면 스키마 포맷의 세팅을 :sql로 하세요.

액티브 레코드의 스키마 덤프 엔진 대신에, 해당 데이터베이스에 특화된 도구(db:structure:dump Rake 태스크를 통해서)를 사용해서 db/#{Rails.env}_structure.sql에 데이터베이스의 구조를 덤프 할 수 있습니다. 예를들어, PostgresSQL을 위해서는 pg_dump 유틸리티를 사용하고, MySQL을 위해서는 다양한 테이블에 대한 SHOW CREATE TABLE을 결과를 파일에 저장하면 됩니다. 스키마를 데이터베이스에 적재하는 방법은 내부에 포함된 SQL 구문 실행으로 간단하게 수행할 수 있습니다.

당연히, 이는 데이터베이스의 구조를 완벽하게 복제할 수 있는 방법이지만, 이 방법은 데이터베이스 이외의 저장 수단에는 스키마 정보를 로딩할 수 없게됩니다.

By definition this will be a perfect copy of the database’s structure but this will usually prevent loading the schema into a database other than the one used to create it.

6.3 스키마 덤프와 소스 컨트롤

스키마 덤프는 여러분의 데이터베이스의 스키마를 위한 믿을 만한 소스입니다. 그래서 이를 소스 컨트롤에 포함 시키는걸 강력히 추천합니다.

7 액티브 레코드와 참조 무결성

액티브 레코드의 방법은 데이터베이스보다 여러분의 모델을 더 영리하게 만들기를 주장합니다. 가령, 트리거(trigger)나 외래키 제약 사항(constraint) 같은 요소 같이 영리함(intelligence)을 데이터베이스로 맡기는 요소를 과도하게 사용하는 방법을 권장하지 않습니다.

validates :foreign_key, :uniqueness => true과 같은 데이터 검증(validation)은 모델에서 데이터의 무결성을 지킬수 있는 방법입니다. 관계(association)에서 :dependent 옵션은 부모 모델이 삭제되면 자동으로 자식 모델도 삭제합니다. 어플리케이션 레벨의 이러한 동작만으로 완전히 참조 무결성을 보장할 수는 없습니다. 그래서 몇몇 분들은 외래키 제약 사항을 더 많이 사용합니다.

액티브 레코드는 그러한 요소에 대해서 직접적인 어떤 도구도 지원하지는 않지만, execute 메소드로 SQL을 실행할 수 있습니다. 이와 관련한 다양한 플러그도 있습니다. 가령 foreign_key_migrations 플러그인은 외래키 추가 기능을 액티브 레코드에 제공합니다. (db/schema.rb에 외래키 정보를 덤프하는 것도 포함합니다.)

8 Changelog

9 Changelog for Korean Translation

  • 2011년 2월 15일 초벌 번역 완료 by Sangmin Ryu

Feedback

You're encouraged to help in keeping the quality of this guide.

If you see any typos or factual errors you are confident to patch, please clone docrails and push the change yourself. That branch of Rails has public write access. Commits are still reviewed, but that happens after you've submitted your contribution. docrails is cross-merged with master periodically.

You may also find incomplete content, or stuff that is not up to date. Please do add any missing documentation for master. Check the Ruby on Rails Guides Guidelines for style and conventions.

Issues may also be reported in Github.

And last but not least, any kind of discussion regarding Ruby on Rails documentation is very welcome in the rubyonrails-docs mailing list.