[이론] 모델 관계 이론
모델 관계 설정(Model Relation) - 기초
모델은 서로 관계라는 것으로 연결될 수 있습니다.
모델 관계의 이해를 돕기 위해 한 가지 예를 들어보겠습니다. 게시판에는 글을 작성하는 유저(User)가 있고, 유저가 작성하는 글(Post)이 있습니다. 이 때 글(Post)을 작성한 작성자(User)를 식별하기 위해 User가 다수의 Post를 소유하고 있다는 관계를 만들 수 있습니다.
모델이 가질 수 있는 관계는 1:1, 1:N, N:N 3가지로 분류됩니다.
이번 강의에서는 1:1, 1:N 관계에 대해 알아봅니다.
1:1
오직 하나의 데이터와 관계를 맺는 것을 1대1 관계라고 합니다.
User 모델과 유저의 계좌 정보를 저장하는 Account 모델이 있을 때 User와 Account는 1:1 관계입니다. 하나의 유저는 한 개의 Account만을 가질 수 있고, 하나의 Account도 마찬가지로 한 개의 유저에만 속할 수 있습니다.
1:1 관계를 설정할 땐 두 개의 테이블 중 한 곳에만 관계를 맺을 상대의 id(외래키라고 합니다)를 저장합니다. 여기서는 Account에 User의 id를 저장했습니다. 각 테이블이 가지는 컬럼은 다음과 같습니다.
이것은 다음과 같이 마이그레이션 파일로 작성할 수 있습니다.
# db/migrate/xxxxxxxx_create_users.rb
class CreateUsers < ActiveRecord::Migration[5.0]
def change
create_table :users do |t|
t.string :name
t.string :email
t.string :password_digest
t.timestamps
end
end
end
# db/migrate/xxxxxxxx_create_accounts.rb
class CreateAccounts < ActiveRecord::Migration[5.0]
def change
create_table :accounts do |t|
# t.belongs_to :user는
# 레일즈에서 외래키를 직관적으로 명시할 수 있도록 제공하는 기능입니다.
# t.integer :user_id 와 같습니다.
t.belongs_to :user
...
t.timestamps
end
end
end
그리고 각 모델 파일에 다음과 같이 명시합니다. 이것은 관계를 가지고 있는 상대 모델의 객체를 손쉽게 불러오기 위해 작성합니다.
# app/models/user.rb
class User < ApplicationRecord
has_one :account
end
# app/models/account.rb
class Account < ApplicationRecord
belongs_to :user
end
위와 같이 모델에 명시함으로써 관계를 맺은 모델을 다음과 같이 쉽게 불러올 수 있습니다.
# id가 1번인 유저의 계좌 불러오기
user = User.find_by(id: 1)
user_account = user.account # <- has_one :account를 명시해서 사용 가능!
# id가 1번인 계좌의 주인 유저를 불러오기
account = Account.find_by(id: 1)
user = account.user # <- belongs_to :user를 명시해서 사용 가능!
어떤 테이블에 외래키를 넣을지 정하는 것은 여러분의 선택이지만 두 모델이 부모관계가 확실할 땐 자식 모델에게 외래키를 주는 것이 일반적입니다. 그리고 외래키를 가진 모델이 belongs_to를 사용해야 합니다.
1:N
1:N 관계는 하나의 데이터가 다수의 데이터와 관계를 가집니다.
User 모델과 블로그 글을 나타내는 Post 모델이 있을 때, 하나의 유저가 여러개의 글을 쓸 수 있고, 하나의 글은 하나의 유저밖에 가질 수 없기 때문에 1:N 관계입니다.
User모델과 Post모델의 관계는 다음과 같습니다.
마이그레이션 파일은 1:1 관계일 때와 같습니다.
# db/migrate/xxxxxxxx_create_users.rb
class CreateUsers < ActiveRecord::Migration[5.0]
def change
create_table :users do |t|
t.string :name
t.string :email
t.string :password_digest
t.timestamps
end
end
end
# db/migrate/xxxxxxxx_create_posts.rb
class CreatePosts < ActiveRecord::Migration[5.0]
def change
create_table :posts do |t|
# t.belongs_to :user는
# 레일즈에서 외래키를 직관적으로 명시할 수 있도록 제공하는 기능입니다.
# t.integer :user_id 와 같습니다.
t.belongs_to :user
...
t.timestamps
end
end
end
각 모델 파일에 다음과 같이 명시합니다.
# app/models/user.rb
class User < ApplicationRecord
has_many :posts
end
# app/models/Post.rb
class Post < ApplicationRecord
belongs_to :user
end
has_many에 명시된 posts는 복수형이라는 것에 주의하세요.
위와 같이 모델에 명시함으로써 관계를 맺은 모델을 다음과 같이 쉽게 불러올 수 있습니다.
# id가 1번인 User가 작성한 모든 글 가져오기
user = User.find_by(id: 1)
all_posts = user.posts # <- has_many :posts를 명시해서 사용 가능!
# id가 1번인 Post를 작성한 유저를 가져오기
post = Post.find_by(id: 1)
author = post.user # <- belongs_to :user를 명시해서 사용 가능!
[SNS 실습] 글(Post) 모델 만들고 유저와 관계 설정
이번 강의에서는 타임라인에 올릴 글(Post) 모델을 만들고, 이전에 만들었던 유저(User)모델과 1:N 관계를 설정해보겠습니다.
5-3-1. Post 모델 생성
터미널에 모델 생성 명령어를 입력합니다.
$ rails g model Post
5-3-2
다음과 같이 마이그레이션 파일을 작성합니다.
# db/migrate/xxxxxxxx_create_posts.rb
class CreatePosts < ActiveRecord::Migration[5.0]
def change
create_table :posts do |t|
# user의 id를 값을 저장하기 위해 integer 타입을 사용
t.integer :user_id
# 글 내용을 저장할 content
t.text :content
t.timestamps
end
end
end
rails db:migrate
명령어를 실행하면 Post 테이블이 생성됩니다.
5-3-4. User 모델과 관계 설정
User(1) : Post(N) 관계이므로 User 모델에 has_many를, Post 모델에는 belongs_to를 명시합니다.
# app/models/user.rb
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :posts
end
5-3-5
# app/models/post.rb
class Post < ApplicationRecord
belongs_to :user
end
콘솔으로 관계 확인하기
터미널에서 콘솔에 접속하여 관계 설정이 제대로 이루어졌는지 확인해봅시다.
1번 id의 유저가 작성한 첫 번째 글을 생성합니다.
2.2.3 :014 > Post.create(user_id: 1, content: "첫 글입니다.")
=> #<Post id: 3, user_id: 1, content: "첫 글입니다.", created_at: "2016-06-05 14:30:45", updated_at: "2016-06-05 14:30:45">
1번 id의 유저가 작성한 두 번째 글을 생성합니다.
2.2.3 :015 > Post.create(user_id: 1, content: "두 번째 글입니다.")
=> #<Post id: 4, user_id: 1, content: "두 번째 글입니다.", created_at: "2016-06-05 14:30:50", updated_at: "2016-06-05 14:30:50">
1번 id의 유저가 작성한 모든 글을 가져옵니다.
2.2.3 :020 > User.first.posts
=> #<ActiveRecord::Associations::CollectionProxy [#<Post id: 3, user_id: 1, content: "첫 글입니다.", created_at: "2016-06-05 14:30:45", updated_at: "2016-06-05 14:30:45">, #<Post id: 4, user_id: 1, content: "두 번째 글입니다.", created_at: "2016-06-05 14:30:50", updated_at: "2016-06-05 14:30:50">]>
사용 예
- 작성자 이름 가져오기
first_post = Post.find_by(id: 1)
author = first_post.user.name
puts author # => 유저 이름
- 작성자인지 확인하기
first_post = Post.find_by(id: 1)
is_author = (first_post.user == current_user)
puts is_author # => true or false
- 유저가 작성한 글 개수 가져오기
user = User.find_by(id: 1)
posts_count = user.posts.size
Post 작성 기능 만들기
수많은 인터넷 서비스들은 게시판을 기본적인 기능으로 제공하고 있습니다.
약간 변형된 모습이 페이스북처럼 타임라인을 구성하는 소셜네트워크 서비스입니다.
사용자 가입 / 로그인 기능이 있고 사용자별로 자신의 담벼락에 포스트를 작성할 수 있고 사진도 올릴 수 있습니다.
사용자들이 서로 포스트에 좋아요를 표시할 수 있고 댓글을 달 수 있습니다.
페이스북의 친구맺기 대신 트위터의 팔로우 기능을 구현해보겠습니다. (친구맺기 보다 팔로우 기능의 구현이 좀 더 복잡합니다)
가장 먼저 사용자가 작성할 포스트를 관리할 컨트롤러를 생성해 보겠습니다.
5-3-6
posts 컨트롤러와 index 액션 생성합니다.
$ rails g controller Posts
config/routes.rb
수정합니다. 포스트는 리스트보기, 작성 폼 뷰, 작성 기능, 수정 폼 뷰, 수정 기능, 삭제기능을 구현할 예정입니다.
:index, :create, :edit, :update, :show, :destroy
중 상세보기로 많이 쓰이는 show
액션만 제외하고 route 규칙을 생성합니다.
5-3-7. routes 설
홈페이지는 포스트의 리스트를 볼 수 있는 posts
컨트롤러의 index
액션으로 지정하겠습니다.
Rails.application.routes.draw do
root 'posts#index'
resources :posts, except:[:show]
end
포스트 작성과 리스트 기능 구현
app/views/posts/new.html.erb 에 새로운 포스트 작성 뷰를 구성해보겠습니다. 코드를 일일히 따라치시기 보다는 아래 노트의 내용을 복사 붙여넣기 하시면 편합니다. 루비 코드만 눈여겨 봐주세요. 앞 강의에서 다루었던 내용입니다.
5-3-8.
TODO :: new 뷰 샘플 코드 넣기
5-3-9. create 액션 작
app/controllers/posts_controller.rb 파일에서 create 액션을 작성해줍니다.
TODO :: 컨트롤러 샘플 코드 넣기
그리고 상단에 로그인한 유저만 포스트 컨트롤러를 사용할 수 있도록 before_action
을 추가해줍니다.
before_action :authenticate_user!
이렇게 완성된 posts 컨트롤러 코드의 모습이 아래와 같습니다.
TODO :: 전체 컨트롤러 코드 넣기
이제 글을 쓸 수 있는 뷰와 기능은 다 구현되었습니다. 작성된 글을 출력하는 기능을 구성해보겠습니다.
5-3-10. posts#index 액션 구현
app/controllers/posts_controller.rb
에서 index 액션을 수정하여 Post 모델의 모든 데이터를 최신순으로 뷰에 전달합니다. 그리고 작성한 글의 수도 @posts_count에 담아 보냅니다.
def index
@posts = Post.all.order('created_at desc')
@posts_count = current_user.posts.length
end
5-3-11. posts#index 뷰 구현
이제 사용자의 프로필과 작성된 글들을 출력하는 뷰를 구성해보겠습니다.
app/views/posts/index.html.erb
에 기본적인 레이아웃을 구성합니다. 1:2로 구역을 나눠 좌측에는 프로필 정보를 쓰고 우측에는 포스트의 리스트를 구성하겠습니다.
Bootstrap은 기본적으로 12 개의 컬럼으로 레이아웃을 구성합니다. col 이라는 class는 컬럼을 의미하고 s12 는 작은 화면(모바일 폰)에서 12 컬럼을 모두 차지하는 것, m4는 중간 사이즈(테블릿) 화면에서 4/12인 1/3 만큼의 영역을 차지하는 사이즈가 됩니다. l8의 경우는 큰 사이즈(PC) 화면에서 8/12인 2/3 만큼의 영역을 차지합니다.
이러한 column을 바깥에 row 클래스를 가진 div 로 묶어줄 수 있습니다.
TODO :: posts#index 뷰 파일 샘플 넣기