티스토리 뷰

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

 

 

  • Active Record Basics Intro

- Active Record는 Ruby on Rails에서 쓰이는 기법으로서, Active Record의 제공 요소 중 하나인 Object Relational Mapping(ORM) System과도 더불어 알아보고자 합니다.

- Migrate, Validations(유효성 검사), callback에 대해서 알아보겠습니다.

 

 

  • Active Record란?

Active Record는 MVC 패턴 중, M에 해당되는 Rails에서 제공되는 모듈입니다.

주로 데이터베이스 로직을 제어하는데에 있어 사용이 됩니다.

 

저희는 후에 Active Record에서 제공되는 ORM(Object Relational Mapping) 문법을 통해 데이터베이스를 제어를 하는 법에 대해 알아볼 겁니다.

 

Object Relational Mapping (ORM)

ORM(관계형 객체 맵핑)은 데이터베이스를 제어하는 문법입니다.

사실 컴퓨터공학 기초에 있어, 학부생들은 초반에 '데이터베이스는 SQL로 제어한다.' 라고 배웠는데, Ruby on Rails에서는 일단 기본적으로 SQL이 아닌 ORM을 통해 제어를 합니다.

## SQL
SELECT "bulletins".* FROM "bulletins"

## Active Record 지원 기능 : ORM을 통한 표현
Bulletin.all

Ruby on Rails에서는 기본적으로 ORM 문법을 통해 위와같이 데이터베이스를 제어합니다.

어떻게보면 사람에게 친숙한 언어를 활용하는 ORM 만으로도 데이터베이스를 제어할 수 있다는게 Rails의 매력이라고 볼 수 있습니다.

 

그리고 Rails 공식 가이드에서 Active Record 정의 중 'mapping System' 이라고 정의가 내려졌는데 그 이유는 다음과 같습니다 :

Rails에서는 ORM을 활용해서 데이터베이스를 조작한다지만, 실제로는 SQL 언어를 기반으로 제어를 하고 있기 때문입니다.

어떻게보면 Active Record가 ORM ↔ 데이터베이스 간에 있어 언어를 해석해주고 서로를 연결시켜주는 연결고리(맵핑)이라고 보면 됩니다.

 

ORM 에서의 Active Record

Active Record은 아래의 특징이 존재합니다. :

  - Model과 Data를 나타냅니다.

  - Model 간의 연관성(Relational)을 나타냅니다.

  - 모델이 데이터베이스에 쓰여지기 전에 모델의 유효성을 검증합니다.

  - 객체지향 방식으로 작업이 진행됩니다.

 

 

  • Convention over Configuration(COC) in Active Record

Active Record를 타 Application 혹은 타 Framework 사용에 있어 (암묵적인) 규약(configuration)을 지켜가며 코드를 짜야할 수 있습니다.

하지만 Rails는 이미 사전에 규칙(Configuration)이 정해져있고, 특히 명령어를 통해 MVC를 구현 시, 자동으로 CoC 철학을 지켜가며 생성이 이루어지곤 하다보니 사용자가 사전에 구성을 생각하거나 해낼 필요가 없습니다.

 

Naming Conventions

과거 Chapter 1 에 썼던 글에서 블로그 필자는 'Controller 이름은 복수명, Model 이름은 단수명으로 표현한다' 라고 했었는데, 이번 Chapter 2에서는 이 부분에 대해 더 자세히 알아보는 시간을 가져보겠습니다.

 

Model과 Table을 생성함에 있어, Active Record은 이 둘의 관계를 고려하여 맵핑(mapping)이 이루어집니다. Model의 이름에 대해 'pluralize'화 되어 Table 이름이 결정이 됩니다.

기존의 이름이 pluralize 되어 re-naming이 되는 모습

 참고 1  Model 내에서는 ActiveModel::Name 에 의해 네이밍이 이루어집니다.

 

 참고 2  pluralize의 반대말은 singular 입니다.

 

그래서 레일즈에서 Model과 테이블의 이름을 보면 보통은 다음과 같은 네이밍 규칙을 볼 수 있습니다.

(모델은 단수, 테이블 이름은 복수)

Model/Class Table/Schema
Article articles
LineItem line_items
mouse mice

 

Schema Conventions

Active Record에서는 테이블 컬럼 내 이름을 지을 때에 있어, 컬럼의 목적에 맞게 자동으로 지어집니다.

rails g model image post:references url description:text

위 명령어를 치면 아래와 같은 테이블 구조의 결과가 보여집니다.

images 테이블

위 테이블 중, 기본적으로 생성되는 두 개의 컬럼에 대해 소개해보고자 합니다.

 

1) 기본키(Primary Keys) : 기본적으로 테이블 내 데이터들 간 구분을 짓기 위한 키이며, 레일즈에서는  id  라는 이름으로 네이밍이 이루어집니다.

Active Record 내에서는 기본적으로  id  컬럼을 기반으로 데이터 탐색이 이루어 질 것입니다.

 

2) 외래키 (Forign Keys) : 타 테이블의 데이터를 탐색할 때 활용이 되는 Key로서, 네이밍 규칙은 [singularized_table_name]_id 입니다.

Active Record 내에서는 기본적으로  [singularized_table_name]_id  컬럼을 기반으로 데이터 탐색/참고가 이루어 질 것입니다.

 

이 외에도 몇 개의 optional 네이밍이 존재를 합니다 :

1) created_at (datetime)  Model을 생성 시 Migration 파일 속에 있는 :t.timestemp 에 의해 자동으로 생성되는 컬럼으로, 최초로 데이터가 쓰여진 시간이 기록 됩니다.

2) updated_at (datetime)  Model을 생성 시 Migration 파일 속에 있는 :t.timestemp 에 의해 자동으로 생성되는 컬럼으로, 최초 작성 및 마지막으로 데이터가 수정된 시간이 기록됩니다.

3) [association_name]_type (string)  polymorphic(다형성) 객체의 이름이 기록됩니다.

4) [table_name]_count (string)  has_many의 데이터에 대한 cached count가 기록됩니다.

 

여기서 저는 Polymorphic(다형성)과 cached count에 대해 저는 좀 더 자세히 파봤습니다.

 

 

Polymorphic Associations

데이터 조회에 있어 타 테이블을 참조해서 데이터를 조회해야 하는 경우 보통 Forigen Key를 활용한 방법이 많이 알려져 있는 방법입니다.

 

⚠️ 게시판(CRUD) 역할을 하는 posts와 bulletins 속에 댓글(comments)을 구현해야 하는 상황이 있다고 가정해봅시다.

 

- Foreign Key를 활용한 게시판-댓글 구현

일반적인 Foreign Key 방법을 통해 구현을 한다면 아래와 같은 설계를 해야 합니다.

Foreign Key 방식을 활용한 게시판-댓글 구현

하지만 FK를 활용한 방식은 댓글 기능이 필요한 테이블 N개에 따른 comments 테이블 또한 N개를 만들어야 할 필요가 있습니다.

이렇게 되면 개발이 비효율적이면서도 테이블 낭비도 심합니다.

 

- Polymorphic 방식을 활용한 게시판-댓글 구현

 polymorphic 방식을 활용한 게시판-댓글 구현 

반면에 polymorphic 방식은 타 테이블과 연계가 몇개 되건간에, comments 테이블은 하나만 있으면 됩니다.

그리고 서로다른 Model 이름을 가진 페이지에서 댓글이 작성되면 댓글이 쓰여진 Model(commentable_type)과 commentable_id(해당 Model의 id)이 기록됩니다.


방법에 대해서 간단히 소개해보겠습니다.

 

1) 댓글 기능을 넣을 2개의 CURD을 미리 준비해주세요.

 

2) comments 모델을 생성해주세요.

rails g model image body:text commentable:references{polymorphic}

 참고 1  과거에 references 옵션은 외래키 옵션을 준다고 언급을 했고, 이번에도 references 옵션이 쓰였긴 했습니다.

하지만 위 예제의 결과에 있어, commentable 모델을 참조하는 FK를 추가되는건 아닙니다.

 참고 2    commentable:references{polymorphic}  옵션을 줄 시, commentable_typecommentable_id 컬럼이 생성됩니다.

 

(옵션, 해당 과정은 필요 시 넣어주세요.) Migrate 파일을 열람 후, 효율적인 데이터 탐색을 위해 색인(index) 기능도 추가합니다.

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

class CreateComments < ActiveRecord::Migration[5.0]
  def change
    create_table :images do |t|
      t.text :body
      - t.references :commentable, polymorphic: true
      + t.references :commentable, polymorphic: true, index: true

      t.timestamps
    end
  end
end

 부록 1  What are indexes? And how to add them to your Rails app?

 부록 2  DB 인덱스를 효과적으로 설정하는 방법 - 고려해야 할 4가지

 참고  색인은 테이블 내 데이터가 너무 많거나, 너무 적은 상태에서 쓰기에는 좋지 않습니다.

 

3) Migrate 파일을 기반으로 Database 서버에 테이블을 생성합니다.

rake db:migrate

 

4) CRUD 역할을 해낼 2개의 Model(저는 post, bulletin 이라고 하겠습니다.)과 댓글 Model(comment)에 다음 내용을 작성합니다.

4-1. comment 모델은 다형성 Model(polymorphic)이다 보니, 다른 테이블과의 연관 관계를 설정 후, 어떤 컬럼 이름을 기준으로 데이터들이 저장할지 및 다형성 여부에 대한 설정을 해줘야 합니다.

저희는 commentable_type, commentable_id 컬럼에 데이터를 저장할 것이므로  belongs_to :commentable  이라고 설정 및  polymorphic: true  옵션을 줍니다.

## app/models/comment.rb

class Comment < ApplicationRecord
  belongs_to :commentable, polymorphic: true
end

⚠️ 이렇게 comments에 polymorphic 옵션을 주고 이유는 comments는 하나의 모델에 대해서만 belongs_to 관계를 가질 수 있지, 2개 이상의 Model에는 belongs_to를 가질 수 없기 때문이라는 점도 명심해야 합니다.

 참고  belongs_to는 테이블의 연관관계를 나타내는 문법으로서, '어떤 모델의 데이터에 종속되어 있다.' 의미를 가지고 있습니다. belongs_to에 지목된 모델 이름은 '단수'명으로 표기합니다.

 

4-2. CURD의 Model 중 하나인 (bulletin) Model에 아래와 같이 comments와 테이블 관계를 정의합니다.

여기서, comments를 정의할 때, as에서 명칭하는 컬럼 이름을 참고해서 polymorphic에 쓰이는 컬럼에 type(출처 Model 이름) 및 id(출처 Model id)가 기록이 됩니다.

class Bulletin < ApplicationRecord
  has_many :comments, as: :commentable
end

 참고  has_many는 테이블의 연관관계를 나타내는 문법으로서, '어떤 모델의 데이터를 많이 가지고있다' 의미를 가지고 있습니다. has_many에 지목된 모델 이름은 '복수'명으로 표기합니다.

 

4-3. CRUD의 Model 중 하나인 (post) Model에 아래와 같이 테이블 관계를 정의합니다.

class Post < ApplicationRecord
  has_many :comments, as: :commentable
end

 

5) CRUD의 show에 해당하는 view 파일에서 Comment 작성 Form 및 Comment 조회 코드, Controller를 작성합니다.

## app/controllers/bulletins_controller.rb

class BulletinsController < ApplicationController
  ... (일부 Action 코드 생략) ...

  def show
    @bulletinId = params[:id]
    @bulletin = Bulletin.find_bulletin(@bulletinId)
    @comment = @bulletin.comments.build
  end
end
## app/views/bulletins/show.html.erb

## 댓글 작성
<%= form_for @bulletin.comments.build, url: bulletin_comments_path(@bulletin.id, :commentable_type => @bulletin.class.name, :commentable_id => @bulletin.id) do |f| %>
  <%= f.text_field :body, placeholder: "댓글 내용" %>
  <%= f.submit('댓글 작성', style: "margin-left: 10px") %>
<% end %>

## 댓글 조회
<div style="margin-top: 40px"></div>
<% Bulletin.preload(:comments).find_by(id: @bulletin.id).comments.each do |comment| %>
<%# Bulletin.find(@bulletin.id).comments.each do |comment| %>
  <%= comment.body %> <%= link_to "삭제", bulletin_comment_path(@bulletin.id, comment.id), method: :delete %><hr/>
<% end %>

 참고  위 코드에서  @bulletin.comments.build  는, Comment 모델이 새 data 작성이 되기 전에 있어, commentable_id와 commentable_type이 미리 작성이 되어있는걸 볼 수 있습니다.

 

6) Comment Controller에서 다음과 같이 코드를 수정합니다.

class CommentsController < ApplicationController
  before_action :find_commentable, :only => [:create]

  def create
    @commentable.comments.create(body: params[:comment][:body])
    redirect_to "/#{@table_name}/#{@commentable_id}"
  end

  def destroy
    data = Comment.find(params[:id])
    table_name = data.commentable_type.tableize
    commentable_id = data.commentable_id
    data.destroy

    redirect_to "/#{table_name}/#{commentable_id}"
  end

  private
  def find_commentable
    model_name = params[:commentable_type].capitalize.constantize
    @commentable = model_name.find(params[:commentable_id])

    @table_name = params[:commentable_type].tableize
    @commentable_id = params[:commentable_id]
  end
end

 

7) polymorphic을 활용한 댓글 시스템 구현의 완성입니다 :D

 

Cached Counter

만약 어떤 특정 조건에 맞는 데이터에 대해 탐색이 필요할 경우, 저희는 ORM 혹은 SQL에서 다음 문법을 통해 알아보곤 했습니다.

## ORM
Comment.where(commentable_type: "Post", commentable_id: 1).count

## SQL
SELECT COUNT(*)
FROM "comments"
WHERE "comments"."commentable_type" = "Post" AND "comments"."commentable_id" = 1

하지만 이는 SQL의 Cost를 생각하면 저렴하지 않을 수 있는 방법입니다. (= 효율이 떨어질 수 있는 방법)

그래서 나온 방법 중 하나는 아예 DB에 Count를 저장시키는 방법입니다.


 실습  CRUD(Model : bulletin) 각 데이터에 댓글이 달릴 때 마다 자동으로 댓글 counter

1) 저는 과거에 이미 CRUD 역할을 하는 bulletin 모델을 생성했다보니 기존 migrate 파일을 수정하는 방식으로 컬럼 추가가 안됩니다.

이럴 경우, 아래 방법을 통해 기존의 테이블에 새로운 컬럼을 추가해줍니다.

# rails g migration [Migrate 파일 이름; 의도있으면서도 누구든 알아볼 수 있게 작성] [count를 하고자 하는 테이블 이름]_count:integer

rails g migration add_comments_count_to_bulletins comments_count:integer

 

그리고 Migrate 작업을 합니다.

rake db:migrate

 

2) Comment 모델파일에 이동 후, 다음과 같이 counter cache 옵션을 추가합니다.

Polymorphic 여부에 따라 코드를 준비했으니 조건에 맞는 코드 하나를 참고하면 될 것 같습니다.

## app/models/comment.rb
# Polymorphic 옵션을 사용하고 있는 모델

class Comment < ApplicationRecord
  belongs_to :commentable, polymorphic: true, counter_cache: true
end
## app/models/comment.rb
# Polymorphic 옵션을 사용하고 있지 않는 모델

class Comment < ApplicationRecord
  belongs_to :bulletin, counter_cache: true
end

 

3) 구현 끝입니다!

이제부터 댓글이 작성될 때 마다 bulletins 테이블에도 게시글이 가진 댓글 개수가 표기됩니다.

 Tip  Ruby on Rails에서는 n개의 데이터 결과값에 대해 갯수를 세는 메소드가 여러 존재합니다 : length, count, size

이 중, size 메소드는 counter cache 컬럼이 존재할 경우, 이를 우선순위로 고려하여 카운트 결과 값을 보여줍니다.

count VS size, 어느게 더 효율적인 SQL 방법으로 결과를 보여줄까?

 이슈  데이터 동기화 이슈

 부록  Caching counters with ActiveRecord's counter caches : callback 부분 내용 참고

사실 counter cached는 후에 언급하겠지만, Model 내 Callback의 성질을 이용해서 돌아가는 원리입니다.

그렇다보니 만약 직접적으로 데이터를 제어할 경우(예를들어 callback을 무시하고 삭제를 하는 delete 메소드) counter에 대해 동기화가 되지 않을 수 있습니다.

 

부록글에 의하면, 이를 해결하기위해서는 때때로 Background Job(Active Job)을 통해 scheduler 시간에 따라 자동으로 동기화를 이루는 작업을 따로 해낼 것을 권유하고 있습니다.

 

 

 개인적인 이슈  과연 counter cache가 SQL 퍼포먼스에 있어 효율적일까?..

 

counter cache는 테이블에 SUM 값을 저장하는 성격 상, 카운팅이 될 때 마다 데이터베이스에서는 UPDATE가 계속해서 발생합니다.

이는 SQL의 퍼포먼스에 영향을 미친다는 것인데, 과연 이 방법이 효율적일지는.. 저도 모르겠습니다.

 

📣 시니어 개발자님께 여쭤본 결과, 해당 기능은 목적에 따라 사용해야 한다고 합니다. (쓰면 오히려 퍼포먼스가 떨어지는 상황이 있음.)

counter cached는 주어진 상황에 따라 사용하는걸로..

 

 

  • Creating Active Record Models

Active Record Model을 만드는 법은 아주 간단합니다.

우리는 주로 Model을 만들 때 터미널에서 아래 명령어를 통해 만들어냅니다.

rails g model bulletins title content:text

위 명령어를 통해 만들어진 파일 중,  app/models/bulletin.rb  모델을 보면

class Bulletin < ApplicationRecord
end

기본적으로 class가  ApplicationRecord  에서 상속받은걸 볼 수 있습니다.

 

 ApplicationRecord  을 상속한 것 만으로 Active Record 사용의 준비가 끝났고, ORM 문법으로 데이터 제어를 할 시, SQL 문법과 맵핑되어 데이터 조작이 이루어집니다.

 

 ★ 개념  Model에서 지원되는 Active Record의 실세는 따로 있다?

사실 Active Record는 ApplicationRecord 에서 비롯되는게 아닙니다.

(엥.. 아까 ApplicationRecord에서 상속받아서 뭐 Active Record를 쓴다면서요....?)

 

저도 처음에는 그런 줄 알았는데, 더 알아보니 사실 진짜 Active Record의 부모는 ActiveRecord::Base 였습니다.

실제로 옛날 버전 때에는 모든 Model에서 Active Record를 사용하기 위해 ApplicationRecord가 아닌 ActiveRecord::Base을 상속받았었고, 옛날 코드들을 보면 그 흔적이 남아있었습니다.

 부록  Ruby on Rails - User 모델 만들기 (글 내용 중, Model 부분을 봐보세요!)

 

또한, 공식 rails gem Github를 보면 ApplicationRecord가 또 ActiveRecord::Base을 상속하고 있는 모습 또한 포착되었습니다.

 부록  Github : rails Gem (app/models/application_record.rb 부분)

 

과거에 클론코딩을 통해 공부하면서 가끔 옛날 글을 보면 Model에서 상속하는 상위클래스가 좀 다르단거에 의문을 품긴 했었는데, 그 이유를 찾아보니 '아~!' 했습니다.

 Q&A  왜 Rails 5는 ActiveRecord::Base 대신 ApplicationRecord를 사용합니까?
 Story  ApplicationRecord in Rails 5

 

이유는 아주 간단했습니다, '메소드 오버라이딩'의 영향 때문이었습니다.

 

## 문제의 여지가 될 코드
# 출처 : https://blog.bigbinary.com/2015/12/28/application-record-in-rails-5

module MyAwesomeFeature
  def do_something_great
    puts "Doing something complex stuff!!"
  end
end

ActiveRecord::Base.include(MyAwesomeFeature)

Model에서 Active Record를 사용함에 있어, 과거에는 모든 Model에서 직접 ActiveRecord::Base을 참고를 해오곤 했었는데, 개인적으로 Active Record를 활용하여 개발함에 있어 그 주체가 되는 ActiveRecord::Base과 혼합하며 개발하다 보니 나중에 내가 만들어냈던 코드가 ActiveRecord::Base와 결합되고, 모든 Model에서 상속이 되는 사태가 벌여지게 됩니다. (아마 이렇게되면 다른 Model에서도 코드/메소드가 이름이 똑같았다면 서로 충돌되지 않았나 싶기도..)

 

Rails 5.0 버전으로 넘어온 후에는 모든 Model에 대해 Active Record의 능력을 이어받은 ApplicationRecord을 상속함으로서 Active Record의 독립성을 보장시켜나가는 방향으로 개발이 진행됐다고 합니다.

 

 

  • Overriding the Naming Conventions

⚠️ 일단 메소드 오버라이딩에 대한 정의를 알아야 할 필요가 있습니다 :

메소드 오버라이딩은 상위 객체의 속성을 받아와서 재정의 하는 것을 의미합니다.

 

Table 이름과 모델 이름과 다르게 해야하는 상황이거나, legacy 코드를 유지보수를 해야함에 있어 때론 메소드 오버라이딩을 해야할 필요가 있습니다.

 

1) 테이블과 모델의 이름을 달리 할 경우

만약, bulletin Model과 bulletins 테이블이 있는 상태에서, 만약 bulletin 모델이 new_bulletins 테이블을 가리켜야 할 경우 아래와 같이 정의를 내려주면 됩니다.

## app/models/bulletin.rb

class Bulletin < ApplicationRecord
  self.table_name = "new_bulletins"
end

 

테스트코드를 사용할 경우, 테스트 코드 내에서도 이름을 바꿔줘야 할 필요가 있습니다.

## test/models/bulletin_test.rb

class BulletinTest < ActiveSupport::TestCase
  set_fixture_class new_bulletins: Bulletin
  fixtures :new_bulletins
  ...
end

 

기본키에 대해서도 오버라이딩이 가능합니다.

 참고  Ruby on Rails에서는 Default로 기본키를 가리키는 컬럼은  id  입니다.

## app/models/bulletin.rb

class Bulletin < ApplicationRecord
  self.primary_key = "bulletin_id"
end

 

 

  • CRUD: Reading and Writing Data

Create

데이터 저장을 돕는 메소드는 크게 2가지 입니다 : new, create

위 두 메소드를 활용해서 여러 방법으로 데이터를 저장할 수 있습니다.

 

 참고  Model 유효성검사, 데이터베이스 컬럼에 명시했던 제약조건 위반(NULL) 등 Validation Error 발생 시 Rollback 처리가 되면서 데이터 저장이 안됩니다.

 

1) 바로 생성하기

# [Model].create(Column1: value, Column2: value2, ...)

Bulletin.create(title: "안녕", content: "하세요!")

 

2) 모델 정의(초기화)

# data = [Model].new
# data.column1 = value1
# data.column2 = value2
# data.save

data = Bulletin.new
data.title = "안녕"
data.content = "하세요!"
data.save

 

3) 상속변수를 활용한 생성

data = Bulletin.new do |t|
  t.title = "안녕"
  t.content = "하세요!"
end

data.save

 

Read

데이터를 읽는데에 있어 정말 많은 메소드 들이 존재합니다.

 

1) 모든 데이터 조회

## 모든 데이터를 읽어들인다.
## Console에서는 최대 10개 까지의 데이터만 보여진다.

Bulletin.all

 

2) id 순서 기준, 첫 번째 및 마지막 데이터 조회

## 첫 번째 데이터
Bulletin.first

## 마지막 데이터
Bulletin.last

 

3) 조건에 맞는 데이터 탐색

## 오직 기본키를 기본키를 기반으로 한 데이터 탐색
# Model에서 기본키에 대해 메소드 오버라이딩을 통해 컬럼 이름을 변경할 시, 해당 메소드에도 영향을 받습니다.
Comment.find(32)

## 탐색 결과값 중 ASC 정렬 기준 첫 번째 데이터만 return
Comment.find_by(commentable_type: "Bulletin", commentable_id: 3)

## 탐색 결과값 중 여러개의 데이터 return
Comment.find_by(commentable_type: "Bulletin")

 

Update

데이터 수정 및 저장을 합니다.

 

1) 수정 (특정 데이터 하나)

Bulletin.find(24).update(title: "싱글벙글")

 

2) 수정 (모든 데이터)

 주의  해당 수정방법은 Model에 명시했던 유효성 제약조건을 무시합니다.

 참고  단, Database(Migration 파일에 쓰여진 데이터베이스 명세조건)에 명시된 제약조건은 지킵니다. (예시 -> null: false)

Bulletin.where(title: "안녕").update_all(title: "Hello")

 

Delete

특정 데이터를 삭제합니다.

 

1) 삭제

 참고  callback 내 조건을 무시하고 삭제합니다.

Bulletin.find(42).delete

 

2) 삭제 (자식 데이터까지)

 참고 1  외래키(Foreign Key)와 엮여진 데이터와도 함께 삭제됩니다.

  * 개발 응용 예시 : 댓글 삭제 시 대댓글 또한 삭제

 참고 2  Polymorphic으로 연계된 데이터는 삭제가 안됩니다.

Comment.find(42).destroy

 

3) 삭제 (모든 데이터)

 참고  delete 메소드와 동일한 역할입니다. (외래키 및 Polymorphic과 연계된 데이터는 삭제가 안됩니다.)

Comment.where(commentable_type: "Bulletin").delete_all

 

 

  • Validations

Validations는 데이터가 작성(Write) 전에 있어, Model에서 설정한 조건을 만족하는지 확인하는 과정입니다.

조건이라 함은 다음과 같은 조건을 나타냅니다 :

  1) 중복 내용인가?

  2) 최소 n글자 이상 작성 되었는가?

  3) 글자가 쓰여졌는가? (NULL 혹은 Empty 상태가 아닌지?)

  4) boolean형 데이터에 있어 true 상태인가?

  5) 동일하게 내용을 작성했는가? (예시 : 비밀번호, 비밀번호 재확인 입력 form)

  6) 유일한 데이터인가?

 

만약에 댓글(comments) 작성에 있어, 내용이 쓰여졌는지 + 최소 3글자 이상인지 검사를 하고자 할 때에는 다음과 같이 작성하면 됩니다.

## app/models/comment.rb

class Comment < ApplicationRecord
  ... (belongs_to) ...
  
  validates :body, presence: true,
            length: { minimum: 3 }
end

 

Model 파일에 위와같이 작성 후, 댓글 작성 때에 있어 아예 빈 내용을 보내거나 혹은 3글자 미만의 글을 작성 시, 데이터 작성이 Rollback 처리 되며 댓글이 등록되지 않습니다.

validates를 통해 제약조건을 명시 후, 제약조건에 걸리는 text를 작성할 경우의 상황 [좌측 : 3글자 미만, 우측 : 내용을 아무것도 안 쓴 상태(empty)]

 

이 외, 더 자세한 내용은 Rails Guide : Active Record Validations 에서 참고해주세요.

 

 

  • Callbacks

콜백은 Model 내에서 이벤트가 처리가 되기 전에 있어, 내부적으로 어떤 이벤트 발생 전 혹은 발생 후에 검증하는 단계라고 볼 수 있습니다.

콜백은 SQL 트랜잭션 작동 전(before) 및 후(after)에 자유롭게 접근이 가능합니다.

 

예를들어 댓글이 쓰여지기 전, callback을 적용한다 하면 다음과 같습니다.

class Comment < ApplicationRecord
  ... (belongs_to 코드 등 생략) ...

  before_create do
    self.body = body.titleize
  end
end

위 예시는 comments 테이블이 데이터가 쓰여지기 전(create), 첫 번째 글자에 대해선 대문자로 전환하는 작업입니다.

더 자세한 내용은 Rails Guide : Active Record Callbacks 에서 참고해주세요.

 

 

  • Migrations

Ruby on Rails에서는 데이터베이스에 접근 및 스키마를 관리하는 Migration 파일이 존재합니다.

과거에 Model 파일을 생성하기 위해 아래 코드를 입력해내왔던 순간을 여러분들은 기억할 것입니다.

# rails generate model [모델이름] [Attributes(Default: String)]
rails generate model bulletin title content:text

위 명령어를 입력하면 여러 파일들이 생성되는데, 그 중 Migrate 폴더속에 뭔가 파일이 생겨나는게 확인이 되고 이 파일을 열람하면 아래와 같이 내용이 쓰여져있는걸 확인해 볼 수 있습니다.

## app/db/migrate/버전_create_bulletins.rb

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

      t.timestamps
    end
  end
end

본 서버에 반영되기 전의 Table 명세서로서 Table 내 Column의 규칙 등을 해당 Migrate 파일을 통해 설정해낼 수 있습니다.

## app/db/migrate/버전_create_bulletins.rb

create_table :bulletins do |t|
  t.string :title, null: false, comment: "bulletin 제목"
  t.text :content, null: false, comment: "bulletin 내용"

  t.timestamps
end

 

하지만 단순히 Model 파일을 Generate를 했다 해서 바로 쓸 수 있는 것은 아닙니다.

rake db:migrate

위 명령어를 통해 Rails 프로젝트→Database 에게 '명세서' 라는 것을 넘김으로서, Database Table을 관리하는 스키마를 최신화를 시켜야 비로소 Migrate 파일의 내용이 Database 서버에 적용이 됩니다.

 

Migrate 파일을 만들고  rake db:migrate  까지 해냈으나, 중간에 오타 혹은 오류가 생길 때를 대비해 Rails에서는 Rollback 기능을 제공합니다.

# rake db:rollback STEP=[롤백을 하고자 하는 단계]

## 바로 이 전 단계에 대해 롤백처리
rake db:rollback STEP=1

STEP=1 롤백 : images 테이블을 생성 후, 롤백 처리하는 모습

 참고  Rollback이 작동되는 기준은 DB에 Migrate 까지 끝마친 작업 기준입니다.

 

또한 Migrate 파일은 어떠한 Database 환경(PostgreSQL, MySQL, Oracle 등) 이든지 적용이 가능하다보니 호환성에 대해선 걱정하지 않아도 됩니다.

 

이 외, 더 자세한 내용은 Rails Guide : Active Record Migrations 에서 참고해주세요.

 

 

  • 자료참고

1. Wiki : Active record pattern

2. how to generate migration to make references polymorphic

3. [Ruby on Rails] Polymorphic Associations (다중 연관성)

4. [번역] 레일즈의 Polymorphic Association 이해하기

5. 왜 Rails 5는 ActiveRecord::Base 대신 ApplicationRecord를 사용합니까?

 

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
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
글 보관함