티스토리 뷰

이 글은 Rails 5.0 Guide 기준으로 작성됩니다.
https://guides.rubyonrails.org/v5.0/active_record_migrations.html

 

 

  • Active Record Migrations Intro

Active Record를 대표하는 Migration 기능은 데이터베이스 스키마의 기능을 확대해 나가도록 도와줍니다.

순수 SQL 방식으로 스키마를 수정/표현하는 방식 대신, *Ruby DSL 방식을 통해 Ruby on Rails 내에 있는 테이블 관리를 더욱 쉽게 해줄 것입니다.

 

DSL (Domain Specific Language)

특정 도메인(산업, 분야등 특정 영역)에 특화된 언어를 말한다.

특정 영역의 문제 해결에는 그 영역에 맞는 특화된 도구를 사용하자라는 것이다. 어찌보면 과도로 끝내도 될 일을 맥가이버칼을 들이대는 격이다. 그리고 표현 방식은 해당 도메인의 전문가가 이해할 수 있는 형태(고급 언어; 자연어)여야 한다.

실제로 Ruby처럼 그 코드가 일반 자연어를 읽는 것과 같이 쉽게 이해되기 때문에 도메인 전문가와 프로그래머가 아이디어를 공유하기 좋고 거기에 Lisp 계열 이어서 DSL에 많이 활용된다.

 부록  Lisp 언어 : 후대의 프로그래밍 언어에 많은 영향을 끼친 구 함수형 프로그래밍 언어

 

이번 chapter 3 과정을 통해 여러분들은 다음 내용을 배워나갈 수 있습니다 :

  1. 터미널에서 generate 명령어를 통해 Migration 파일을 생성해낼 수 있습니다.

  2. Database를 제어함에 있어, Active Record에서 제공하는 메소드를 알아갈 수 있습니다.

  3. Migration 및 스키마를 제어하는 rake 명령어

  4. Migration 기능과  db/schema.rb  파일간의 관계

 

 

  • Migration Overview

Migration 기능은 정말 쉬운방법으로 스키마(데이터베이스의 구조를 정의)를 제어할 수 있습니다.

데이터베이스를 제어하는 SQL 지식이 없더라도 Rails 내 Migration 관리를 통해 스키마를 관리해낼 수 있고, Rails 내 Migration/스키마와 실제 데이터베이스와 독립적으로 관리됩니다.

 

Ruby on Rails 내에 있는 Migration 파일에는 '버전'이 명시되어 있습니다.

위에 명시된 버전을 통해 Ruby on Rails 내 데이터베이스를 제어하는 Active Record 모듈은 데이터베이스 동기화(관리)가 이루어집니다.

그리고 Rails에서 데이터베이스 구조 및 Migration 버전이 명시된 내용은  db/schema.rb  파일을 통해 확인할 수 있습니다.

## db/schema.rb

# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# Note that this schema.rb definition is the authoritative source for your
# database schema. If you need to create the application database on another
# system, you should be using db:schema:load, not running all the migrations
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
# you'll amass, the slower it'll run and the greater likelihood for issues).
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 20200701111152) do

  create_table "bulletins", force: :cascade do |t|
    t.string   "title",          null: false
    t.text     "content"
    t.datetime "created_at",     null: false
    t.datetime "updated_at",     null: false
    t.integer  "comments_count"
  end

  create_table "comments", force: :cascade do |t|
    t.string   "body"
    t.string   "commentable_type"
    t.integer  "commentable_id"
    t.datetime "created_at",       null: false
    t.datetime "updated_at",       null: false
    t.index ["commentable_type", "commentable_id"], name: "index_comments_on_commentable_type_and_commentable_id"
  end

  create_table "posts", force: :cascade do |t|
    t.string   "title"
    t.text     "content"
    t.integer  "comments_count", default: 0
    t.datetime "created_at",                 null: false
    t.datetime "updated_at",                 null: false
  end

end

 

마이그레이션 파일의 예는 다음과 같습니다.

## db/migrate/[버전]_create_bulletins.rb

class CreateBulletins < ActiveRecord::Migration[5.0]
  def change
    create_table :bulletins do |t|
      t.string :title, null: false
      t.text :content

      t.timestamps
    end
  end
end

 

과거에도 언급했었지만 Migration 파일은 Table 생성 전, 테이블 구조를 나타내는 명세서라고 보면 됩니다.

 

위 구조를 간단히 리뷰해 보자면 다음과 같습니다 :

1) bulletins 라는 이름의 테이블을 새로 생성한다.

create_table(:bulletins)

 

2) 아래 명시된 2개의 컬럼(Column)들을 추가한다.

단, title 컬럼(Column)은 NULL에 대해서는 허용하지 않는다.

add_column(:bulletins, :title, :string, {null: false})
add_column(:bulletins, :content, :text)

 

3) 개발자가 명시해낸 컬럼(Column) 외, Rails에서 기본으로 3개의 컬럼(Column)을 추가한다 :

## SUDO 코드

:id (:integer), :timestamps => { :created_at (:datetime), :updated_at (:datetime) }

 참고  컬럼 추가를 원치 않을 경우, 아래와 같은 방법으로 기본 column에 대한 생성을 막을 수 있습니다.

## db/migrate/[버전]_create_bulletins.rb

class CreateBulletins < ActiveRecord::Migration[5.0]
  def change
    create_table :bulletins do |t|
      t.string :title, null: false
      t.text :content

      t.timestamps
    end
    
    remove_column :images, :id, :integer
  end
end

 

Rails에서 Migration 파일을 기반으로 진행되는 관리를 통해 테이블을 추가를 하거나, Rollback을 하는 등 데이터베이스를 쉬우면서도 체계적으로 관리를 해나아갈 수 있습니다.

 

reversible

reversible 은 Migration 파일에 대해 테이블 up(migrate)/down(revert) 여부에 따라 테이블을 어떻게 재설정 할지 결정하는 기능(함수) 입니다.

 

1) up : Migration 파일에 적인 DB 명세기록을 토대로 데이터베이스 서버에 적용

2) down : Migration 파일에 적인 DB 명세기록을 토대로 데이터베이스 서버에서 제거(롤백)

 

이를테면, Migration up/down에 따라 어떤 컬럼에 대해 데이터베이스에 변화를 줘야 할 경우를 생각할 수 있는데, 이를 위해 다음과 같이 내용을 작성할 수 있습니다.

class CreateImages < ActiveRecord::Migration[5.0]
  def change
    create_table :images do |t|
      t.string :url
      t.string :description
      t.string :uid

      t.timestamps
    end

    reversible do |dir|
      change_table :bulletins do |t|
        dir.up   { t.change :content, :text }
        dir.down { t.change :content, :string }
      end
    end
  end
end

위 내용은 images 테이블이 생성되면서 버전검사를 진행 후, Migration up/down 여부에 따라 bulletins 테이블 내 content column의 데이터 타입을 바꾸는 기능입니다.

 

또한 위 표현에 대해선 다음과 같이도 표현해볼 수 있습니다.

class CreateImages < ActiveRecord::Migration[5.0]
  def change
    create_table :images do |t|
      t.string :url
      t.string :description
      t.string :uid

      t.timestamps
    end
  end

  def up
    change_table :posts do |t|
      t.change :content, :text
    end
  end

  def down
    change_table :posts do |t|
      t.change :content, :string
    end
  end
end

 

 

  • Creating a Migration

Migration 파일 생성

과거에 Model 파일을 생성할 때를 보면  db/migrate  폴더 속에 table 명세서 같은 무슨 파일이 생성됐던 때를 기억할겁니다. 사실 이 때 생성나는 파일 역시 Migration 파일의 일부분인데, 이 파일은 테이블이 기본적으로 설정된 채로 내용이 작성되어 있습니다.

 

Migration 파일 역시 모델 방식과 같이  db/migrate  폴더 속에 생성이 되는 파일입니다.

Migration 파일 이름 앞에 붙는 버전 숫자에 대한 명시 기준은 주로 UTC Time 기준으로 표기가 되고, Time format 형식은 YYYYMMDDHHMMSS 입니다.

 

Migration 파일이 쓰이는 대표적인 예시는 이미 Migrate 된 테이블 양식에 대해 수정을 해줘야 할 때 입니다. (기존에 이미 Migrate 된 파일에 있어선 해당 파일에서 수정을 해봤자 적용이 안됩니다.)

그래가지고 저희는

1) 추가적으로 Migration 파일을 만들고,

2) 해당 파일 속에 테이블을 제어하는 코드를 넣고

3) 또 다시  rake db:migrate 

를 함으로서 스키마를 최신화 시킬 수 있고, 테이블 속성을 변경할 수 있습니다.

 

바로 한번 해보겠습니다.

 

1. 본격적인 Migrate 실습 전에 앞서, image 모델을 생성 및 Migrate를 합니다.

rails g model image bulletin:references url description
rake db:migrate

 

2. 저희는 images 테이블의 description 컬럼을 현재의 string 타입에서 text 타입으로 변경 및 NULL을 비허용(false) 하는 작업을 해볼겁니다.

'이미 Migration이 진행된  [버전]_create_images.rb  파일에서 단순히 type을 수정하면 되지않을까?' 라고 생각하겠지만, 해당 방법이 안됩니다.

 

이미 Migrate 된 테이블의 속성을 변경하기 위해 migration 파일을 추가합니다.

# rails g migration change_[컬럼 이름]_to_[테이블 이름]

rails g migration change_description_to_images

 

4.  db/migrate  파일에 새로 생긴 파일을 열람 후, 다음과 같이 코드를 작성해냅니다.

change_column(:images, :description, :text, :null => false)
## db/migrate/[버전]_change_description_to_images.rb

class ChangeDescriptionToImages < ActiveRecord::Migration[5.0]
  def change
    change_column(:images, :description, :text, :null => false)
  end
end

 

5. 이어서 images 테이블에 uid (integer), order (integer) 컬럼을 추가해보겠습니다.

아래 명령어를 통해 Migration 파일을 추가합니다.

# rails g migration add_detail_to_[테이블 이름] [column1:type1 column2:type2 ...]

rails g migration change_detail_to_images uid:integer order_num:integer

 

그리고 Migrate를 진행해주세요.

rake db:migrate

그럼 기존의 테이블에 새로운 컬럼이 추가되어있는 것을 볼 수 있습니다.

 

6. 또한, M:N 테이블 설계에 있어 테이블 Join 또한 해낼 수 있습니다.

# rails g migration CreateJoinTable[모델1][모델2] [모델1(소문자)] [모델2(소문자)]

rails g migration CreateJoinTableBulletinImage bulletin image
## db/migrate/[버전]_create_join_table_bulletin_image.rb

class CreateJoinTableBulletinImage < ActiveRecord::Migration[5.0]
  def change
    create_join_table :bulletins, :images do |t|
      # t.index [:bulletin_id, :image_id]
      # t.index [:image_id, :bulletin_id]
    end
  end
end

 참고  Migration 파일을 만들게 되면 위 코드와 같이 2개의 색인 코드가 주석이 된 채로 나오는데, 위 두 줄의 색인 코드는 완전히 똑같은 코드는 아니고, 목적에 따라 선택을 해야 함(아니면 아예 색인 옵션을 안넣어도 됨.)을 알아주세요.

 부록  Multi-column index order in Rails

 

색인 코드에 대한 주석을 풀고, Migrate 작업을 진행하면 아래와 같은 테이블 결과를 볼 수 있습니다.

 

7. 또한 Migration 생성에 있어, 다음과 같은 옵션 추가가 가능합니다.

rails g migration add_details_to_images imageable:references{polymorphic}

 

 

  • Writing a Migration

Migration 파일을 편집해봅시다.

 

Creating a Table

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

위와같은 유형같은 경우에는 products 라는 테이블의 속성을 t 라는 변수(= 상속변수)가 이어받게 되어, do와 end 사이의 블록 내에서 컬럼을 설정해주면 됩니다.

 

위 코드를 기반으로 테이블이 생성되면  products  라는 이름의 테이블이 생성되고, 기본적으로  id  라는 이름을 가진 기본키 컬럼이 생성됩니다. 만약 기본키 및 컬럼 생성을 원치 않을 경우, 아래와 같이 설정을 해주면 됩니다.

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

 

또한 다음과 같이 컬럼별로 옵션을 줄 수 있습니다.

create_table :products, :id => false do |t|
  t.integer :category_id, :null => false, :foreign_key => true, :comment => "카테고리 ID값 (외래키)"
  t.string :name, :null => false, :comment => "제품 이름"
  t.string :ucode, :null => false, :comment => "제품 코드번호"
end

 Tip  Rails 공식 문서에 따르면, comment 옵션을 통해 해당 컬럼의 역할에 대해 집필을 하는 것을 권유하고 있습니다.

해당 주석(comment)은  db/schema.rb  에 기록이 되며, 데이터베이스 서버 상에서는 MySQLPostgreSQL 에서만 주석(comment) 표기 지원이 됩니다.

 

Creating a Join Table

Join 테이블은 M:N 관계의 기능을 만듬에 있어 쓰일 수 있는 기법입니다.

Join 테이블은 다음과 같은 형태로 Join 테이블을 생성할 수 있습니다.

create_join_table :bulletins, :images

Join 테이블의 컬럼에 있어, 기본적으로 null을 허용하지 않는 상태입니다.

만약 테이블 컬럼에 null을 허용(true)하고 싶으면 다음과 같이 설정해주세요.

create_join_table :bulletins, :images, column_options: { null: true }

 

Join되는 테이블의 기본적인 이름은  [테이블A]_[테이블B]  입니다.

만약 테이블 이름을 재정의 하고싶으면 다음 옵션을 주면 됩니다.

create_join_table :bulletins, :images, table_name: :gallery

위 코드로 생성 시,  gallery  테이블이 생성됩니다.

 

다음과 같이 다양한 방법으로 색인에 옵션도 줄 수 있습니다.

## db/migrate/[버전]_create_join_table_bulletin_image.rb

class CreateJoinTableBulletinImage < ActiveRecord::Migration[5.0]
  def change
    create_join_table :bulletins, :images do |t|
      # t.index :bulletin_id
      # t.index :image_id
      # t.index [:bulletin_id, :image_id]
      # t.index [:image_id, :bulletin_id]
    end
  end
end

 

Changing Tables

기존의 테이블에 대한 변경을 다양하게 해볼 수 있습니다.

change_table :images do |t|
  t.remove :description, :url		## 컬럼 제거
  t.string :part_number			## 컬럼 추가 (:string)
  t.index :part_number			## 색인 추가
  t.rename :unique_id, :uid		## 기존의 unique_id → uid 로 컬럼이름 변경
end

 

다음과 같은 방법을 통해서도 NULL 여부 변경 및 default boolean 설정을 할 수 있습니다.

change_column_null :images, :url, false
change_column_default :images, :national, from: false, to: true

 

Foreign Key

add_foreign_key :images, :bulletins

위 옵션은 images 테이블 속의 bulletin_id 컬럼에 외래키 옵션을 주는 것을 의미합니다.

 

 

  • Running Migrations

기본적으로  db/migrate  에 있는 파일들에 있어, 데이터베이스 서버에 적용되지 않은 migration 파일을 서버에 적용을 할 때 아래 명령어를 통해 migrate를 진행합니다.

rake db:migrate

 

하지만 만약 특정 버전에 대해서만 Migrate를 진행하고 싶을 경우, 아래와 같이 입력해주면 특정 버전의 migrate 파일만 서버에 적용이 됩니다.

# rake db:migrate VERSION=[버전]

rake db:migrate VERSION=20200702071609

 

Rollback

이미 Migrate 된 상태에서 undo를 시키고 싶을 경우, 아래와 같이 입력해주면 됩니다.

# rake db:rollback STEP=n
# 현재 버전 기준, 바로 이전단계로 rollback

rake db:rollback STEP=1

 

Rollback + Migrate

 rollback 후, 바로 migrate를 진행하고 싶은 경우는 아래 명령어로 입력해주세요.

# rake db:rollback STEP=n
# 현재 버전 기준, 바로 이전단계로 rollback 후 migrate

rake db:migrate:redo STEP=1

 

Setup

rake db:setup

다음 과정이 시행됩니다 : rake db:create → rake db:schema:load → rake db:seed

 

Reset 

DB Drop 후 Setup이 이루어집니다.

rake db:reset

 

Migrate up/down

(과거에 설명했던 reversible의 부연설명)

특정 테이블에 대해 데이터베이스 서버에서 up/down을 합니다.

 

1) up : Migration 파일에 적인 DB 명세기록을 토대로 데이터베이스 서버에 적용

2) down : Migration 파일에 적인 DB 명세기록을 토대로 데이터베이스 서버에서 제거(롤백)

#rake db:migrate:[up/down] VERSION=[버전]

rake db:migrate:up VERSION=20200702044935
rake db:migrate:down VERSION=20200702044935

 

Environment

Ruby on Rails에는 기본으로 3개의 Environment가 설정되어 있습니다 : development, test, production

 * Environment는 개발자가 원한다면 더 추가를 해줄 수 있습니다.

 RAILS_ENV  옵션을 주는 것 없이 명령어 입력 및 작업을 시킨다면, development 환경으로 작업이 진행됩니다.

 

각 환경에 대한 설정은 다르며,  config/environments  에서 확인해볼 수 있습니다.

Environments 설정파일

 

서버의 환경에 따라 데이터베이스 역시 연동되는게 다른데, Development 환경 같은 경우에는 특별히 환경 설정을 안해도 되나, 만약 production 등과 같은 환경일 경우, 많은 명령어 입력에 있어 환경을 언급해야 할 필요가 있습니다.

 

Migrate 작업 같은 경우, production 환경에서 작업이 진행될 경우, 아래와 같이 기입을 해야합니다.

rake db:migrate RAILS_ENV=production

이 외 특정 Environment에 대한 옵션을 붙여야 하는 명령어는 다양합니다 : rake db:create, rake db:drop, rake db:seed, rails s, ...

 

 

Migrate Status

Migrate의 up/down 상태 확인은 아래 명령어를 통해 확인 가능합니다.

rake db:migrate:status

posts 테이블을 reverting 후, Migrate 상태를 확인해본 결과

 

 

  • Changing Existing Migrations

본래 마이그레이션을 이미 실행 한 경우 마이그레이션을 편집하고 마이그레이션을 다시 실행할 수 없습니다. Rails는 마이그레이션을 이미 실행 한 것으로 간주하여 실행할 때 아무 작업도 수행하지 않습니다.

 

하지만 본 서버에 올려진 Migration 파일에 있어 수정을 해야할 상황이 올 경우, 다양한 방법을 통해 할 수 있습니다.

그 중, 기존의 Migration 파일을 수정하는 방법으로서 진행할 경우, revert(rollback, migrate:down) 기능을 활용하는걸 추천합니다.

 

하지만 Rails 공식 문서 에서는 실 서버에 Load된 Migration 파일을 임의로 건드는 것에 대해서는 권장되지 않습니다.

 

 

  • Schema Dumping and You

 db  폴더 안에는 데이터베이스에 쓰여져있는 테이블 및 컬럼에 대한 정보, Migration 버전 정보가 담겨져있는  schema.rb  파일이 존재합니다. 

 

스키마 파일은 기본적으로 루비 형식으로 쓰여져있고, 만약 SQL 언어로 된 덤프파일을 받고싶다면  config/application.rb  파일에 다음 내용을 정의해주면 됩니다.

## Default
# config.active_record.schema_format = :ruby

config.active_record.schema_format = :sql

 

SQL로 전환 후,  rake db:migrate  시 SQL 언어로 된 덤프파일이 생성됩니다.

## db/structure.sql

CREATE TABLE IF NOT EXISTS "schema_migrations" ("version" varchar NOT NULL PRIMARY KEY);
CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL);
CREATE TABLE sqlite_sequence(name,seq);
CREATE TABLE IF NOT EXISTS "comments" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "body" varchar, "commentable_type" varchar, "commentable_id" integer, "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL);
CREATE INDEX "index_comments_on_commentable_type_and_commentable_id" ON "comments" ("commentable_type", "commentable_id");
CREATE TABLE IF NOT EXISTS "posts" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "title" varchar, "content" text, "comments_count" integer DEFAULT 0, "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL);
CREATE TABLE IF NOT EXISTS "bulletins" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "title" varchar NOT NULL, "content" varchar, "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL, "comments_count" integer);
INSERT INTO "schema_migrations" (version) VALUES
('20200624064400'),
('20200629162230'),
('20200630034856'),
('20200701111152');

 

또한, 스키마 언어 기본설정이 ruby로 되어있어도 아래 명령어를 통해 SQL 언어로 된 덤프파일을 받아낼 수 있습니다.

rake db:structure:dump

 

 

  • Active Record and Referential Integrity

Database가 아닌 Model에 사용되는 Active Record는 후에 *참조 무결성에 있어서도 많은 보완을 해낼 수 있습니다.

참조 무결성

데이터베이스의 한곳에서 어떤 특정한 값으로 참조를 하는 정보가 데이터베이스의 다른 곳에 반드시 존재하여야 하는 원칙.

 

이를테면, 후에 배우게 될 Validation(유효성 검사)을 기반으로 아래와 같이 활용해내어 참조 무결성을 강하게 다져나갈 수 있습니다.

validates :foreign_key, uniqueness: true

# 테이블 속 데이터에 있어, 외래키 ID 값은 오직 1개여야 한다.

 

또한 다음과 같은 옵션으로 외래키로 연결된 데이터들에 대해서도 삭제를 함으로서 참조 무결성 조건을 지켜낼 수 있습니다.

class Bulletin < ApplicationRecord
  has_many :comments, dependent: :destroy
end

# bulletins 테이블 내 데이터가 삭제되면, 그에 종속되는 comment 데이터들이 삭제된다.

 

 

 

  • Migrations and Seed Data

Seed 같은 경우는, 데이터베이스 생성 후 혹은 특정 트리거 명령어를 줌으로서 더미데이터들을 빠르게 만들어나갈 수 있는 기능입니다.

 

1) Migration 파일을 통해 Seed코드 실행

아주 간단하게, Migration 코드 속에 다음과같이 입력해낸 후,  rake db:migrate  를 해내면 됩니다.

class CreateBulletins < ActiveRecord::Migration[5.0]
  def change
    create_table :bulletins do |t|
      t.string :title, null: false
      t.text :content

      t.timestamps
    end

    for i in 0..10
      Bulletin.create(title: "Hello! #{SecureRandom.hex(5)}", content: "World! #{SecureRandom.hex(5)}")
    end
  end
end

 

2) 명령어를 통해 Seed코드 실행

 db  폴더속을 보면  seeds.rb  파일이 존재합니다. 이 파일을 이용해서 더미데이터를 생성해내보겠습니다.

 

1)  seeds.rb  파일에 다음과 같은 코드를 작성해낸 후

## db/seeds.rb
## Bulletin 모델을 참고하여 10개의 데이터를 생성

for i in 0..10
  Bulletin.create(title: "안녕하세요! #{SecureRandom.hex(5)}", content: "반갑습니다! #{SecureRandom.hex(5)}")
end

 

2) 터미널에서 아래 명령어를 입력해내면 더미데이터가 바로 생성됩니다.

rake db:seed

 

 참고  development Environment 외 특정 Environment 서버에 더미데이터를 생성할 경우, 아래와 같이 입력해야 합니다.

RAILS_ENV=production rake db:seed

 

 

  • 자료 참고

1. DSL(Domain Specific Language) 이해하기

2. Is there a way to remove the id column in a subsequent Rails 3 ActiveRecord migration?

 

 

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/03   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
글 보관함