유저 팔로우 기능 만들기
팔로우 구현하기 - 모델 관계 설정
이번 강의에서는 팔로우를 직접 구현하기에 앞서 모델 관계를 설정하는 법을 배워보겠습니다.
- 용어 정의
우리가 구현할 팔로우 기능은 트위터와 유사합니다.
트위터 유저가 아니라면 팔로워, 팔로잉에 대한 개념이 헷갈릴 수 있으니
먼저 용어 정의를 하겠습니다.
팔로우(Follow)는 블로그나 뉴스등을 구독하듯이 누군가가 올린 글을 계속해서 확인하고자 할 때 사용합니다.
팔로잉(Following)은 내가 누군가를 팔로우한 것입니다. 나의 팔로잉이 10명이라면,
내가 팔로우한 유저가 10명 있다는 뜻입니다.
팔로워(Follower)는 나를 팔로우하는 유저입니다. 나의 팔로워가 10명이라면,
나를 팔로우하는 유저가 10명 있다는 뜻입니다.
- Follow 조인 모델
팔로우를 구현하기 위한 모델 관계 설정은 꽤 복잡합니다. 보통 N:N 관계는 이전에 배웠던대로 두 모델 사이의 관계를 나타냅니다.
하지만 이번에는 팔로우를 하는 대상과 팔로우를 당하는 대상이 모두 User이므로
두 모델이 아니라 User 모델만을 가지고 N:N 관계를 구현해야 합니다.
User 모델만으로는 구현할 수 없고, 중간에서 팔로워유저와 팔로잉유저를 이어주는 조인 모델이 필요합니다.
그리고 N:N 관계를 구현하기 위해 조인 모델을 거치는 has_many :through를 사용할 것입니다.
Follow라는 조인 모델을 생성합니다.
rails g model Follow
# db/migrate/xxxxxxxxxx_create_follows.rb
class CreateFollows < ActiveRecord::Migration[5.0]
def change
create_table :follows do |t|
# follower: 팔로우를 신청한 사람
# followed: 팔로우 신청을 받은 사람
t.integer :follower_id
t.integer :followed_id
t.timestamps
end
add_index :follows, :follower_id
add_index :follows, :followed_id
add_index :follows, [:follower_id, :followed_id], unique: true
end
end
Follow 모델은 조인모델이기 때문에 연결해줄 2개의 id를 가지고 있습니다. follower_id와 followed_id는 User 모델의 id가 들어갈 컬럼이며
follower_id에는 팔로우를 신청한 유저의 id가, followed_id에는 팔로우 신청을 받은 유저의 id가 들어가게 됩니다.
작성이 완료되면 rake db:migrate
명령어를 실행시켜줍니다.
- User - Follow 관계 설정
3-1. Follow 모델
Follow 모델은 외래키(foreign_key)를 가지고 있는 입장이기 때문에 모델 관계 설정에 belongs_to를 사용합니다.
관계 이름(follower, followed)과 대상 모델 이름(User)이 다르기 때문에 class_name 옵션을 사용해서 모델명을 별도로 명시해줍니다.
class Follow < ApplicationRecord
# 팔로우 신청한 유저
# 관계 이름 : follower
# 모델 명 : User
belongs_to :follower, class_name: "User"
# 팔로우 신청 받은 유저
# 관계 이름 : followed
# 모델 명 : User
belongs_to :followed, class_name: "User"
end
여기까지 완료하면 Follow 모델 객체에서 팔로우를 신청한 유저와 신청을 받은 유저를 불러올 수 있습니다.
# 팔로우를 신청한 유저는 5번, 팔로우 신청을 받은 유저는 4번 id를 가지고 있음
2.2.3 :022 > follow = Follow.find_by(id: 1)
=> #<Follow id: 1, follower_id: 5, followed_id: 4, created_at: "2016-06-12 12:55:30", updated_at: "2016-06-12 12:55:30">
2.2.3 :024 > follow.follower
=> #<User id: 5, ..., created_at: "2016-06-12 10:55:29", updated_at: "2016-06-12 10:55:29">
2.2.3 :026 > follow.followed
=> #<User id: 4, ..., created_at: "2016-06-12 10:52:46", updated_at: "2016-06-12 10:52:46">
팔로우를 신청한 유저의 id는 Follow 모델의 follower_id에, 팔로우 당하는 입장의 유저의 id는 followed_id에 존재해야 합니다.
3-2. 팔로워
User와 Follow 모델은 1:N 관계를 적용시킬 수 있습니다.
단, 외래키(foreign_key)가 모델 이름과 같은 user_id가 아닌 followed_id이기 때문에 외래키 이름을 별도로 명시해줘야합니다.
그리고 관계 이름(follower_relations)와 모델명(Follow)가 같지 않으므로 class_name으로 모델명도 명시해줍니다.
class User < ApplicationRecord
...
# 관계 이름 : follower_relations(다른 이름으로 변경 가능)
# 외래키 : followed_id
# 모델명 : Follow
has_many :follower_relations, foreign_key: "followed_id", class_name: "Follow"
end
이 상태에서 user.follower_relations 코드를 실행하면 Follow 목록이 반환됩니다. 우리가 원하는 것은 유저의 목록이므로
조금 더 작성해봅시다.
class User < ApplicationRecord
...
has_many :follower_relations, foreign_key: "followed_id", class_name: "Follow"
# 관계 이름 : followers (다른 이름으로 변경 가능)
# follow_relations를 통해 가져올 값 : follower ( follow.follower )
has_many :followers, through: :follower_relations, source: :follower
end
이제 user.followers로 모든 팔로워 유저 목록을 가져올 수 있습니다!
3-3. 팔로잉
팔로잉관계도 팔로워와 완전히 같습니다. 관계 이름과 외래키 정도만 바꿔주면 됩니다.
class User < ApplicationRecord
...
has_many :follower_relations, foreign_key: "followed_id", class_name: "Follow"
has_many :followers, through: :follower_relations, source: :follower
has_many :following_relations, foreign_key: "follower_id", class_name: "Follow"
has_many :followings, through: :following_relations, source: :followed
end
user.followings로 내가 팔로잉하는 유저 목록을 가져올 수 있습니다.
팔로우 기능 구현
팔로우 모델링을 마쳤으니 컨트롤러와 뷰를 작성해보도록 하겠습니다.
팔로우와 팔로우 취소 기능을 구현하고 팔로워와 팔로잉 수를 표시해보겠습니다.
app/views/posts/_post.html.erb 팔로우/팔로우 취소 버튼 구현
<% if user_signed_in? && current_user != post.user %>
<% if current_user.followings.include?(post.user) %>
<button class="right btn">팔로우 취소</button>
<% else %>
<button class="right btn">팔로우</button>
<% end %>
<% end %>
rails g controller follows --no-assets 팔로우 컨트롤러 생성합니다. --no-assets 옵션을 추가하면 css와 js 자동생성을 하지 않습니다.
팔로우 버튼을 form 형태로 수정합니다.
app/views/posts/_post.html.erb
...
<% if user_signed_in? && current_user != post.user %>
<% if current_user.followings.include?(post.user) %>
<%= form_tag follow_path(post.user.id), method: :delete, class: "right" do %>
<button class="btn">팔로우 취소</button>
<% end %>
<% else %>
<%= form_tag follows_path, class: "right" do %>
<input type="hidden" name="followed_id" value="<%= post.user.id %>" />
<button class="btn">팔로우</button>
<% end %>
<% end %>
<% end %>
...
라우트 규칙을 설정합니다.
config/routes.rb
resources :follows, only: [:create, :destroy]
각 버튼과 연결될 액션을 작성합니다.
app/controllers/follows_controller.rb
class FollowsController < ApplicationController
before_action :authenticate_user!
def create
Follow.create(followed_id: params[:followed_id],
follower_id: current_user.id)
redirect_to :back
end
def destroy
Follow.find_by(followed_id: params[:id],
follower_id: current_user.id).destroy
redirect_to :back
end
end
사용자 프로필 정보에 팔로우 정보를 표시해줍니다.
app/views/posts/index.html.erb
<p>팔로워 : <%= current_user.followers.length %></p>
<p>팔로잉 : <%= current_user.followings.length %></p>