sample ์ดํ๋ฆฌ์ผ์ด์
์ ์ฝ์ด ๋ถ๋ถ์ ๊ฐ๋ฐํ๊ธฐ ์ํด, ์ง๊ธ๊น์ง ์ ์ , ์ธ์
, Account ์ ํจํ, ํจ์ค์๋ ๋ฆฌ์
๋ฑ 4๊ฐ์ ๋ฆฌ์์ค์ ๋ํด ํ์ตํ์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ด๊ฒ ๋ค ์ค, "์ ์ " ๋ผ๊ณ ํ๋ ๋ฆฌ์์ค๋ง์ด Active Record์ ์ํด ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์ ํ
์ด๋ธ๊ณผ ์ด์ด์ ธ์์ต๋๋ค. ๋ชจ๋ ์ค๋น๋ฅผ ๋ง์น ์ง๊ธ, ์ ์ ๊ฐ ์งง์ ๋ฉ์ธ์ง๋ฅผ ํฌ๊ณ ํ ์ ์๋๋ก ํ๊ธฐ ์ํด ๋ฆฌ์์ค "Micropost" ๋ฅผ ์ถ๊ฐํด๋ณด๊ฒ ์ต๋๋ค. ์ 2์ฅ ์์ ๊ฐ์ด์ ์ธ micropost ๋ฑ๋ก form์ ํธ๋ค๋งํด๋ณด์์ต๋๋ค๋ง, ์ด๋ฒ ์ฑํฐ์์๋ 2.3 ์์ ํ์ตํ Micropost ๋ฐ์ดํฐ ๋ชจ๋ธ์ ์์ฑํ๊ณ , User ๋ชจ๋ธ๊ณผ has_many , belongs_to ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ๊ด๋ จ์ง๊ธฐ๋ฅผ ํด๋ณผ ๊ฒ ์
๋๋ค. ๊ฒ๋ค๊ฐ ๊ฒฐ๊ณผ๋ฅผ ์ฒ๋ฆฌํ๊ณ ํ์ํ๊ธฐ ์ํด ํ์ํ Form๊ณผ ํด๋น ๋ถํ์ ์์ฑํด๋ณด๊ฒ ์ต๋๋ค. (13.4์์ ์ด๋ฏธ์ง์ ์
๋ก๋๋ ๊ตฌํํด๋ณผ ๊ฒ ์
๋๋ค.) ์ 14์ฅ์์๋ micropost์ ํผ๋๋ฅผ ๋ฐ๊ธฐ ์ํด ์ ์ ๋ฅผ ํ๋ก์ฐํ๋ ๊ฐ๋
์ ๋์
ํ๊ณ , Twitter์ ๋ฏธ๋ํด๋ก ๋ฒ์ ์ ์์ฑ์์ผ๋ณด๊ฒ ์ต๋๋ค.
์ฐ์ Micropost ๋ฆฌ์์ค์ ์ ์ผ ๋ณธ์ง์ ์ธ ๋ถ๋ถ์ ํํํ๋ Micropost ๋ชจ๋ธ์ ์์ฑํ๋ ๋ถ๋ถ๋ถํฐ ํด๋ณด๊ฒ ์ต๋๋ค. 2.3 ์์ ์์ฑํ ๋ชจ๋ธ๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก, ์ด ์๋ก์ด Micropost ๋ชจ๋ธ๋ ๋ฐ์ดํฐ ๊ฒ์ฆ๊ณผ User ๋ชจ๋ธ์ ๊ด๋ จ์ง๊ธฐ๋ฅผ ํฌํจํ๊ณ ์์ต๋๋ค. ์ด์ ๋ชจ๋ธ๊ณผ๋ ๋ค๋ฅด๊ฒ ์ด๋ฒ micropost ๋ชจ๋ธ์ ์์ ํ ํ ์คํธ๋์ด์ง๊ณ ๋ํดํธ์ ์์๋ฅผ ๊ฐ์ง๋ฉฐ, ์์ ๋ฐ์ดํฐ์ธ ์ ์ ๊ฐ ์ญ์ ๋๋ ๊ฒฝ์ฐ ์๋์ ์ผ๋ก ํ๊ธฐ๋๋๋ก ํฉ๋๋ค.
Git์ผ๋ก ๋ฒ์ ๊ด๋ฆฌ์ ์ฌ์ฉํ๊ณ ์๋ ๊ฒฝ์ฐ๋, ์ธ์ ๋์ฒ๋ผ ํ ํฝ๋ธ๋์น๋ฅผ ์์ฑํด๋ด ์๋ค.
$ git checkout -b user-microposts
Micropost ๋ชจ๋ธ์ micropost์ ๋ด์ฉ์ ์ ์ฅํ content ์์ฑ๊ณผ ํน์ ์ ์ ์ micropost๋ฅผ ๊ด๋ จ์ง๋ user_id ์์ฑ์ 2๊ฐ ๋ง์ ๊ฐ์ง๋๋ค. ์คํํ ๊ฒฐ๊ณผ์ Micropost์ ๊ตฌ์กฐ๋ ์๋์ ๊ฐ์ต๋๋ค.
์ ๋ชจ๋ธ์์๋ micropost์ ๋ฉ์ธ์ง ํํ๋ฅผ String์ด ์๋ Text ํํ๋ก ์ฌ์ฉํ๊ณ ์๋ ์ ์ ์ฃผ๋ชฉํด์ฃผ์ธ์. ์ด๊ฒ์ ์ด๋์ ๋์ ์์ ํ
์คํธ๋ฅผ ์ ์ฅํ ๋ ์ฌ์ฉํ๋ ํํ์
๋๋ค. String ํํ๋ก๋ 255๋ฌธ์๊น์ง๋ ์ ์ฅํ ์ ์๊ธฐ ๋๋ฌธ์, String ํ์ผ๋ก๋ 13.1.2์์ ๊ตฌํํ๋ 140๋ฌธ์ ์ ํ์ ์ถฉ์กฑ์ํฌ ์๋ ์์ผ๋, Text ํ์ด ์ข ๋ ๋ง์ ๋ด์ฉ์ micropost๋ฅผ ์์ฑํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด 13.3.2 ์์๋ ์์ฑ form์์ String ์ฉ ํ
์คํธ ํ๋๊ฐ ์๋, Text์ฉ ํ
์คํธ ์๋ฆฌ์ด๋ฅผ ์ฌ์ฉํ๊ณ ์๊ธฐ ๋๋ฌธ์, ๋ณด๋ค ๋ ์์ฐ์ค๋ฌ์ด ์์ฑ Form์ ํํํ ์ ์์ต๋๋ค. ๋ํ Text ํ์ด ์ ์ฐ์ฑ์ ๊ฐ์ง๊ณ ์์ต๋๋ค. ์๋ฅผ๋ค์ด ๋์ค์ ๊ตญ์ ํ๋ก ์ธํ ๋ฒ์ญ์ ํ ๋, ์ธ์ด๋ณ๋ก ์์ฑ๋ด์ฉ์ ๊ธธ์ด๋ฅผ ์กฐ์ ํ ์๋ ์์ต๋๋ค. ๊ฒ๋ค๊ฐ Text ํ์ ์ฌ์ฉํ์ฌ๋ ์ค์ ๋ฐฐํฌํ๊ฒฝ์์ ํผํฌ๋จผ์ค์ ์ฐจ์ด๋ ์์ต๋๋ค. ์ด๋ฌํ ์ด์ ๋ค๋ก ๋จ์ ๋ณด๋ค๋ ์ฅ์ ์ด ๋ง๊ธฐ ๋๋ฌธ์, ์ด๋ฒ์๋ Text ํ์ ์ฑ์ฉํ๊ฒ ์ต๋๋ค.
์ 6์ฅ์์ User ๋ชจ๋ธ์ ์์ฑํ๋ ๊ฒ๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก, Rails์ generate model ์ปค๋งจ๋๋ฅผ ์ฌ์ฉํ์ฌ Micropost ๋ชจ๋ธ์ ์์ฑํด๋ณด๊ฒ ์ต๋๋ค.
$ rails generate model Micropost content:text user:references
์ ์ปค๋งจ๋๋ฅผ ์คํํ๋ฉด, ์๋์ ๊ฐ์ ๋ชจ๋ธ ํ์ผ์ด ์์ฑ๋ฉ๋๋ค. ์ฆ 6.1.2 ๋์ ๋ง์ฐฌ๊ฐ์ง๋ก ApplicationRecord ๋ฅผ ๊ณ์นํ ๋ชจ๋ธ์ด ์์ฑ๋ฉ๋๋ค. ๋จ, ์ด๋ฒ์ ์์ฑ๋ ๋ชจ๋ธ ๋ด๋ถ์๋ ์ ์ ์ 1๋1์ ๊ด๊ณ๋ฅผ ๋ํ๋ด๋ belongs_to ์ ์ฝ๋๋ ์ถ๊ฐ๋ฉ๋๋ค. ์ด๊ฒ์ ์๊น ์ ์ปค๋งจ๋๋ฅผ ์คํํ์ ๋์ user:reference ๋ผ๊ณ ํ๋ ํ๋ผ๋ฏธํฐ๋ฅผ ํฌํจํ๊ธฐ ๋๋ฌธ์
๋๋ค. ์์ธํ ์ค๋ช
์ ๋ํด์๋ 13.1.3์์ ํ๋๋ก ํ๊ฒ ์ต๋๋ค.
# app/models/micropost.rb
class Micropost < ApplicationRecord
belongs_to :user
end์ด์ 6์ฅ์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ users ํ
์ด๋ธ์ ์์ฑํ ๋ ๋ง์ด๊ทธ๋ ์ด์
์ ์์ฑํ ๊ฒ ๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก, ์ด generate ์ปค๋งจ๋๋ microposts ํ
์ด๋ธ์ ์์ฑํ๊ธฐ ์ํด ๋ง์ด๊ทธ๋ ์ด์
ํ์ผ์ ์์ฑํฉ๋๋ค.
User๋ชจ๋ธ๊ณผ์ ์ ์ผ ํฐ ์ฐจ์ด์ ์ references ํ์ ์ด์ฉํ๊ณ ์๋ค๋ ์ ์
๋๋ค. ์ด๊ฒ์ ์ด์ฉํ๋ฉด, ์๋์ ์ผ๋ก ์ธ๋ฑ์ค์ ์ธ๋ถ Key ์ฐธ์กฐ๋ฅผ ํ๋ user_id ์ปฌ๋ผ์ด ์์ฑ๋์ด User์ Microepost์ ๊ด๋ จ์ง๋ ๋ฐ์์
์ ์๋์ผ๋ก ํด์ค๋๋ค. User ๋ชจ๋ธ๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก, Micropost ๋ชจ๋ธ์ ๋ง์ด๊ทธ๋ ์ด์
ํ์ผ์๋ t.timestamp ๋ผ๊ณ ํ๋ ํ (๋งค์ง์ปฌ๋ผ)์ด ์๋์ ์ผ๋ก ์์ฑ๋ฉ๋๋ค. ์ด๊ฒ์ผ๋ก 6.1.1 ์์ ์ค๋ช
ํ๋ฏ์ด created_at ๊ณผ updated_at ์ด๋ผ๊ณ ํ๋ ์ปฌ๋ผ์ด ์ถ๊ฐ๋ฉ๋๋ค. ๋ํ created_at ์ปฌ๋ผ์ 13.1.4์ ๊ตฌํ์ ์งํํด๋๊ฐ๋ฉด์ ํ์ํ ์ปฌ๋ผ์ด๊ธฐ๋ ํฉ๋๋ค.
# db/migrate/[timestamp]_create_microposts.rb
class CreateMicroposts < ActiveRecord::Migration[5.0]
def change
create_table :microposts do |t|
t.text :content
t.references :user, foreign_key: true
t.timestamps
end
add_index :microposts, [:user_id, :created_at]
end
end์ฌ๊ธฐ์ ์ ์ฝ๋์์ user_id ์ created_at ์ปฌ๋ผ์ ์ธ๋ฑ์ค๋ฅผ ์ถ๊ฐํ๊ณ ์๋ ๊ฒ์ ์ฃผ๋ชฉํด์ฃผ์ธ์. ์ด๋ ๊ฒ ํจ์ผ๋ก ์ธํ์ฌ user_id ์ ๊ด๋ จ๋ ๋ชจ๋ ๋ง์ดํฌ๋ก ํฌ์คํธ๋ฅผ ์์ฑ์๊ฐ์ ์ญ์์ผ๋ก ์ป๊ธฐ ์ฌ์์ง๊ฒ ๋์์ต๋๋ค.
add_index :micropost, [:user_id, :created_at]
๋ํ user_id ์ created_id ๋ ๋ค ํ๋์ ๋ฐฐ์ด๋ก ๋ฌถ๊ณ ์๋ ์ ์ ์ฃผ๋ชฉํด์ฃผ์ธ์. ์ด๋ ๊ฒ ํ๋ ๊ฒ์ผ๋ก Active Record๋ ์์ชฝ์ key๋ฅผ ๋์์ ๋ค๋ฃจ๋ ๋ณตํฉ key index๋ฅผ ์์ฑํฉ๋๋ค. What is a multiple key index?
๊ทธ๋ฌ๋ฉด ์ ๋ง์ด๊ทธ๋ ์ด์ ํ์ผ์ ์ด์ฉํ์ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ๊ฐฑ์ ํด๋ด ์๋ค.
$ rails db:migrate
-
Rails ์ฝ์์์
Micropost.new๋ฅผ ์คํํ์ฌ ์ธ์คํด์ค๋ฅผ ๋ณ์micropost์ ๋์ ํด๋ณด์ธ์. ๊ทธ ๋ค์,user_id์ ์ ์ผ ์ฒซ ๋ฒ์งธ ์ ์ id๋ฅผ,content์ "Lorem ipsum" ์ ๊ฐ๊ฐ ๋์ ํด๋ณด์ธ์. ์ด ์์ ์์micropost์ค๋ธ์ ํธ์ ๋งค์ง์ปฌ๋ผ (created_at๊ณผupdated_at) ์๋ ๋ฌด์์ด ๋ค์ด์์ต๋๊น? -
์์ ๋ง๋ ์ค๋ธ์ ํธ๋ฅผ ์ฌ์ฉํ์ฌ
micropost.user์คํ์์ผ๋ด ์๋ค. ์ด๋ ํ ๊ฒฐ๊ณผ๊ฐ ์ถ๋ ฅ๋๋์? ๋ํmicropost.user.name์ ์คํํ์ ๊ฒฝ์ฐ์๋ ์ด๋ ํ ๊ฒฐ๊ณผ๊ฐ ๋ฉ๋๊น? -
์์ ๋ง๋
micropost์ค๋ธ์ ํธ๋ฅผ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅํด๋ด ์๋ค. ์ด ์์ ์์ ํ ๋ฒ ๋ ๋งค์ง์ปฌ๋ผ์ ๋ด์ฉ์ ํ์ธํด๋ด ์๋ค. ์ด๋ฒ์๋ ์ด๋ ํ ๊ฐ์ด ๋ค์ด์๋์?
๊ธฐ๋ณธ์ ์ธ ๋ชจ๋ธ์ ์์ฑํ์ต๋๋ค. ๋ค์์ผ๋ก๋ ์๊ตฌ๋๋ ์ ํ์ฌํญ์ ๊ตฌํํ๊ธฐ ์ํด์, Validation์ ์ถ๊ฐํด๋ด
์๋ค. Micropost ๋ชจ๋ธ์ ์์ฑํ์ ๋, micropost๋ ์์ฑํ ์ ์ ์ id(user_id) ๋ฅผ ๋ฐ์ดํฐ๋ก ๊ฐ์ง๊ณ ์๊ฒ๋ ํ์์ต๋๋ค. ์ด๊ฒ์ ์ฌ์ฉํ์ฌ ๊ด์ต์ ์ผ๋ก ์ฌ๋ฐ๋ฅธ Active Record์์ ๊ด๊ณ๋งบ๊ธฐ ๋ฅผ ๊ตฌํํด๋ณผ ๊ฒ ์
๋๋ค. ์ฐ์ Micropost ๋ชจ๋ธ ๋ง์ (ํ
์คํธ ์ฃผ๋ ๊ฐ๋ฐ๋ก) ๋์ํด๋ณด๋๋ก ํฉ์๋ค.
Micropost ์ ์ด๊ธฐ ํ
์คํธ๋ User๋ชจ๋ธ์ ์ด๊ธฐ ํ
์คํธ์ ๋น์ทํฉ๋๋ค. ์ฐ์ ์ setup ๋จ๊ณ์์, fixture์ sample ์ ์ ์ ๊ด๋ จ์๋ ์๋ก์ด micropost๋ฅผ ์์ฑํ๊ณ ์์ต๋๋ค. ๋ค์์ผ๋ก ์์ฑํ ๋ง์ดํฌ๋กํฌ์คํธ๊ฐ ์ ํจํ์ง ์๋์ง๋ฅผ ์ฒดํฌํฉ๋๋ค. ๋ง์ง๋ง์ผ๋ก ๋ชจ๋ micropost๋ ์ ์ ์ id๋ฅผ ๊ฐ์ง๊ณ ์์ด์ผ๋ง ํ๊ธฐ ๋๋ฌธ์ user_id ์ ์กด์ฌ์ฑ์ validation์ ๋ํ ํ
์คํธ๋ฅผ ์ถ๊ฐํฉ๋๋ค. ์ด๊ฒ๋ค์ ํ๋๋ก ๋ฌถ์ผ๋ฉด ์๋์ ๊ฐ์ ์ฝ๋๊ฐ ๋ ๊ฒ ์
๋๋ค.
# test/models/micropost_test.rb
require 'test_helper'
class MicropostTest < ActiveSupport::TestCase
def setup
@user = users(:michael)
# ์ด ์ฝ๋๋ ๊ด์ต์ ์ผ๋ก ์ฌ๋ฐ๋ฅธ ์ฝ๋๊ฐ ์๋๋๋ค.
@micropost = Micropost.new(content: "Lorem ipsum", user_id: @user.id)
end
test "should be valid" do
assert @micropost.valid?
end
test "user id should be present" do
@micropost.user_id = nil
assert_not @micropost.valid?
end
endsetup ๋ฉ์๋์ ๋ด๋ถ์ ์ฝ๋ฉํธ์ฒ๋ผ, micropost๋ฅผ ์์ฑํ๋ ์ฝ๋๋ ๋์ํฉ๋๋ค๋ง ๊ด์ต์ ์ผ๋ก๋ ์ฌ๋ฐ๋ฅด์ง ์์ ์ฝ๋์
๋๋ค. (13.1.3์์ ์์ ํฉ๋๋ค.)
์๋๋ถํฐ ์กด์ฌํ๋ User ๋ชจ๋ธ์ ํ
์คํธ์ ๋ง์ฐฌ๊ฐ์ง๋ก, ์ ์ฒซ ๋ฒ์งธ ํ
์คํธ์์๋ ์ ์์ ์ธ ์ํ์ธ์ง ์๋์ง๋ฅผ ํ
์คํธ (Sanity Test) ๋ฅผ ํ๊ณ ์์ต๋๋ค. ๋ ๋ฒ์งธ ํ
์คํธ์์๋ user_id ๊ฐ ์กด์ฌํ๋์ง๋ฅผ (nil ์ ์๋์ง) ๋ฅผ ํ
์คํธํ๊ณ ์์ต๋๋ค. ์ด ํ
์คํธ๋ฅผ ํต๊ณผ์ํค๊ธฐ ์ํด์๋ ์๋์ ๊ฐ์ด, ์กด์ฌ์ฑ์ validation์ ์ถ๊ฐํด๋ด
์๋ค.
# app/models/micropost.rb
class Micropost < ActiveRecord::Base
belongs_to :user
validates :user_id, presence: true
end์ฌ๋ด์ผ๋ก, Rails 5์์๋ ์ validation์ ์ถ๊ฐํ์ง ์์๋ ํ
์คํธ๋ ํต๊ณผํ ๊ฒ ์
๋๋ค. ๊ทธ๋ฌ๋ ์ด๊ฒ์ ๊ด์ต์ ์ธ ์๋ฏธ์์ ์ฌ๋ฐ๋ฅด์ง ์์ ์ฝ๋๋ฅผ ์์ฑํ ๊ฒฝ์ฐ์๋ง ๋ฐ์ํฉ๋๋ค. ์ด ๋ถ๋ถ์ "๊ด์ต์ ์ผ๋ก ์ฌ๋ฐ๋ฅธ" ์ฝ๋๋ก ์์ฑํ๋ฉด, user_id ์ ๋ํ ์กด์ฌ์ฑ์ validation์ด ๊ธฐ๋ํ๋ ๋๋ก ๋์ํ ๊ฒ ์
๋๋ค. ์ด ๋ถ๋ถ์ ์ค๋ช
ํ๊ณ ์ถ์๊ธฐ ๋๋ฌธ์ ์์ ์ฝ๋์ ์ฝ๋ฉํธ๋ฅผ ๋จ๊ฒจ๋์ ๊ฒ ์
๋๋ค.
์ด์์ผ๋ก ์ ํ ์คํธ๋ ํต๊ณผํ ๊ฒ ์ ๋๋ค. ํ์ธํด๋ด ์๋ค.
$ rails test:models
๋ค์์ผ๋ก micropost์ content ์์ฑ์ ๋ํ validation์ ์ถ๊ฐํด๋ด
์๋ค. (2.3.2์์ ์๊ฐํด๋๋ฆฐ ๋ฐฉ๋ฒ๊ณผ ๋น์ทํฉ๋๋ค.) user_id ์์ฑ๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก, content ์์ฑ๋ ์กด์ฌํ ํ์๊ฐ ์์ต๋๋ค. ๊ฒ๋ค๊ฐ micropost๊ฐ 140๋ฌธ์๋ณด๋ค ๊ธด ๋ฌธ์๊ฐ ๋์ง ์๋๋ก ์ ํ์ ๊ฑธ์ด๋ณด๊ฒ ์ต๋๋ค. (์ด๊ฒ์ด micropost๊ฐ micro๋ผ๊ณ ์ด๋ฆ์ด ๋ถ์ ์ด์ ์
๋๋ค.)
6.2 ์์ User๋ชจ๋ธ์ Validation์ ์ถ๊ฐํ์ ๋์ ๋ง์ฐฌ๊ฐ์ง๋ก, ํ ์คํธ ์ฃผ๋ ๊ฐ๋ฐ์์ micropost ๋ชจ๋ธ์ validation์ ์ถ๊ฐํด๋ณด๊ฒ ์ต๋๋ค. ๊ธฐ๋ณธ์ ์ผ๋ก๋ User๋ชจ๋ธ ๋์ ๋น์ทํ validation์ ์ถ๊ฐํด๋ณผ ๊ฒ ์ ๋๋ค.
# test/models/micropost_test.rb
require 'test_helper'
class MicropostTest < ActiveSupport::TestCase
def setup
@user = users(:michael)
@micropost = Micropost.new(content: "Lorem ipsum", user_id: @user.id)
end
test "should be valid" do
assert @micropost.valid?
end
test "user id should be present" do
@micropost.user_id = nil
assert_not @micropost.valid?
end
#์ถ๊ฐ
test "content should be present" do
@micropost.content = " "
assert_not @micropost.valid?
end
test "content should be at most 140 characters" do
@micropost.content = "a" * 141
assert_not @micropost.valid?
end
#์ถ๊ฐ
end6.2 ์ ๋ง์ฐฌ๊ฐ์ง๋ก, ์ ์ฝ๋๋ micropost์ ๊ธธ์ด๋ฅผ ํ ์คํธํ๊ธฐ ์ํด ๋ฌธ์์ด์ ๊ณ์ฐ์ ์ด์ฉํฉ๋๋ค.
$ rails console
>> "a" * 10
=> "aaaaaaaaaa"
>> "a" * 141
=> "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"์ด๊ฒ์ ๋์ํ๋ ์ดํ๋ฆฌ์ผ์ด์
์์์ ๊ตฌํ์, User์ name ์ฉ validation๋์ (6์ฅ) ๋๊ฐ์ต๋๋ค. ์๋๋ ๊ฒฐ๊ณผ์ ๊ฐ์ต๋๋ค.
app/models/micropost.rb
class Micropost < ApplicationRecord
belongs_to :user
validates :user_id, presence: true
validates :content, presence: true, length: { maximum: 140 }
end์ด ์์ ์์, ๋ชจ๋ ํ ์คํธ๋ GREEN์ด ๋ ๊ฒ ์ ๋๋ค.
$ rails test
- Rails ์ฝ์์ ์คํ์ํค๊ณ ,
user_id์content๊ฐ ๋น์ด์๋ micropost ์ค๋ธ์ ํธ๋ฅผ ๋ง๋ค์ด๋ณด์ธ์. ์ด ์ค๋ธ์ ํธ์ ๋ํดvalid?๋ฅผ ์คํ์ํค๋ฉด ์คํจํ๋ ๊ฒ์ ํ์ธํด๋ณด์ธ์. ๋ํ ์์ฑ๋ ์๋ฌ๋ฉ์ธ์ง์ ์ด๋ ํ ๋ด์ฉ์ด ๊ธฐ์ฌ๋์ด ์๋์? - ์ฝ์์ ์คํ์ํค๊ณ ์ด๋ฒ์๋
user_id๊ฐ ๋น์ด์๊ณ ,content๊ฐ 141๋ฌธ์ ์ด์์ micropost ์ค๋ธ์ ํธ๋ฅผ ์์ฑํด๋ณด์ธ์. ์ด ์ค๋ธ์ ํธ์ ๋ํด์valid?๋ฅผ ์คํ์ํค๋ฉด ์คํจํ๋ ๊ฒ์ ํ์ธํด๋ณด์ธ์. ๋ํ ์์ฑ๋ ์๋ฌ๋ฉ์ธ์ง์๋ ์ด๋ ํ ๋ด์ฉ์ด ์๋์ง ํ์ธํด๋ณด์ธ์.
Web ์ดํ๋ฆฌ์ผ์ด์ ์ฉ์ ๋ฐ์ดํฐ๋ชจ๋ธ์ ๊ตฌ์ถํ ๋๋, ๊ฐ๊ฐ์ ๋ชจ๋ธ ๊ฐ์ ๊ด๊ณ์ ๋ํด์ ์ถฉ๋ถํ ์๊ฐํ ํ์๊ฐ ์์ต๋๋ค. ์ด๋ฒ ๊ฒฝ์ฐ์๋ 2.3.3 ์์๋ ํด๋ณธ ์ ์ด ์๋ฏ, ๊ฐ๊ฐ์ micropost๋ 1๋ช ์ ์ ์ ์ ๊ด๊ณ๋ฅผ ๋งบ๊ฒ๋๊ณ , ๊ฐ๊ฐ์ ์ ์ ๋ (์ ์ฌ์ ์ผ๋ก) ์ฌ๋ฌ๊ฐ์ micropost์ ๊ด๊ณ ๋งบ๊ฒ๋ฉ๋๋ค. ์ด ๊ด๊ณ๋ ์๋์ ๊ฐ์ต๋๋ค. ์ด๋ฌํ ๊ด๊ณ๋ฅผ ๊ตฌํํ๊ธฐ ์ํ ์ผํ์ผ๋ก, micropost ๋ชจ๋ธ์ ๋ํ ํ ์คํธ์ฝ๋๋ฅผ ์์ฑํ๊ณ , ๊ฒ๋ค๊ฐ User๋ชจ๋ธ์ ๋ช๊ฐ์ง ํ ์คํธ๋ฅผ ์ถ๊ฐํด๋ณผ ๊ฒ ์ ๋๋ค.
์ด๋ฒ ์น์
์์ ์ ์ํ๋ belongs_to / has_many ๊ด๊ณ๋ฅผ ์ฌ์ฉํฉ๋๋ค. ์ ๊ทธ๋ฆผ์์ ์ฌ์ฉํ๋ ๋ฉ์๋๋ฅผ Rails์์ ์ฌ์ฉํ ์ ์๊ฒ ๋ฉ๋๋ค. ์์ฑ๋ค์ ์๋์ ๋ฉ์๋๊ฐ ์๋
Micropost.create
Micropost.create!
Micropost.new์๋ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๊ณ ์๋ ์ ์ ์ฃผ์ํด์ฃผ์ธ์.
user.microposts.create
user.microposts.create!
user.microposts.build์ด ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉด, ๊ด๊ณ๋ฅผ ๋งบ๊ณ ์๋ ์ ์ ๋ฅผ ํตํด micropost๋ฅผ ์์ฑํ ์ ์์ต๋๋ค. (๊ด์ต์ ์ผ๋ก ์ฌ๋ฐ๋ฅธ ๋ฐฉ๋ฒ์
๋๋ค.) ์ ๊ท micropost๊ฐ ์ด ๋ฐฉ๋ฒ์ผ๋ก ์์ฑ๋๋ ๊ฒฝ์ฐ, user_id ๋ ์๋์ ์ผ๋ก ์ฌ๋ฐ๋ฅธ ๊ฐ์ผ๋ก ์ค์ ๋ฉ๋๋ค. ์ด ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๋ฉด, ์๋ฅผ ๋ค์ด ์๋์ ๊ฐ์
@user = users(:michael)
# ์๋ ์ฝ๋๋ ๊ด์ต์ ์ผ๋ก ์ฌ๋ฐ๋ฅด์ง ์์ต๋๋ค.
@micropost = Micropost.new(content: "Lorem ipsum", user_id: @user.id)์์ ๊ฐ์ ์์ฑ๋ฐฉ๋ฒ์ด ์๋์ ๊ฐ์ด ๋ค์ ์ธ ์ ์์ต๋๋ค.
@user = users(:michael)
@micropost = @user.microposts.build(content: "Lorem ipsum")(new ๋ฉ์๋์ ๋ง์ฐฌ๊ฐ์ง๋ก, build ๋ฉ์๋๋ ์ค๋ธ์ ํธ๋ฅผ ๋ฆฌํดํ๋๋ฐ, ๋ฐ์ดํฐ๋ฒ ์ด์ค์๋ ๋ฐ์ํ์ง ์์ต๋๋ค.) ํ ๋ฒ์ ์ฌ๋ฐ๋ฅธ ๊ด๊ณ๋ฅผ ์ ์ํด ๋๋๋ค๋ฉด, @micropost ๋ณ์์ user_id ๋ ๊ด๊ณ๋ฅผ ๋งบ๊ณ ์๋ ์ ์ ์ id๊ฐ ์๋์ ์ผ๋ก ์ค์ ๋ฉ๋๋ค.
| ๋ฉ์๋ | ์ฉ๋ |
|---|---|
micropost.user |
Micropost์ ๊ด๊ณ๋ฅผ ๋งบ๊ณ ์๋ User ์ค๋ธ์ ํธ๋ฅผ ๋ฆฌํดํ๋ค. |
user.microposts |
User์ Micropost ์งํฉ์ ๋ฆฌํดํ๋ค. |
user.microposts.create(arg) |
user ๊ด๊ณ๋งบ์ micropost๋ฅผ ์์ฑํ๋ค. |
user.microposts.create!(arg) |
user์ ๊ด๊ณ๋งบ์ micropost๋ฅผ ์์ฑํ๋ค. (์คํจ ์ ์์ธ ๋ฐ์) |
user.microposts.build(arg) |
user ์ ๊ด๊ณ๋งบ์ ์๋ก์ด Micropost ์ค๋ธ์ ํธ๋ฅผ ๋ฆฌํดํ๋ค. |
user.microposts.find_by(id: 1) |
user์ ๊ด๊ณ๋ฅผ ๋งบ๊ณ ์๊ณ id๊ฐ 1์ธ micropost๋ฅผ ๊ฒ์ํฉ๋๋ค. |
@user.micropost.build ์ ๊ฐ์ ์ฝ๋๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด, User๋ชจ๋ธ๊ณผ Micropost ๋ชจ๋ธ์ ๊ฐ๊ฐ ์์ ํ๊ณ , ๊ด๊ณ๋ฅผ ๋งบ์ ํ์๊ฐ ์์ต๋๋ค. Micropost ๋ชจ๋ธ์์๋ belongs_to :user ๋ผ๊ณ ํ๋ ์ฝ๋๊ฐ ํ์ํฉ๋๋ค๋ง, ์ด๊ฒ์ ์๋ ์ฝ๋์ migration์ ์ํด ์๋์ ์ผ๋ก ์์ฑ๋ ๊ฒ ์
๋๋ค. ํ ํธ, User๋ชจ๋ธ์์๋ has_many :microposts ๋ผ๊ณ ์ถ๊ฐํ ํ์๊ฐ ์์ต๋๋ค. ์ฌ๊ธฐ๋ ์๋์ ์ผ๋ก ์์ฑ๋์ง ์๊ธฐ ๋๋ฌธ์, ์๋์ผ๋ก ์ถ๊ฐํด์ผํ ํ์๊ฐ ์์ต๋๋ค.
# app/models/micropost.rb
class Micropost < ApplicationRecord
belongs_to :user # ์์
validates :user_id, presence: true
validates :content, presence: true, length: { maximum: 140 }
end# app/models/user.rb
class User < ApplicationRecord
has_many :microposts
.
.
.
end์ฌ๋ฐ๋ฅด๊ฒ ๊ด๊ณ๊ฐ ๋งบ์ด์ก๋ค๋ฉด, setup ๋ฉ์๋๋ฅผ ์์ ํ๊ณ ๊ด์ต์ ์ผ๋ก ์ฌ๋ฐ๋ฅธ micropost๋ฅผ ์์ฑํด๋ด
์๋ค.
# test/models/micropost_test.rb
require 'test_helper'
class MicropostTest < ActiveSupport::TestCase
def setup
@user = users(:michael)
@micropost = @user.microposts.build(content: "Lorem ipsum") #Update
end
test "should be valid" do
assert @micropost.valid?
end
test "user id should be present" do
@micropost.user_id = nil
assert_not @micropost.valid?
end
.
.
.
end๋ฌผ๋ก ์ฌ์ธํ ๋ฆฌํฉํ ๋ง์ด๊ธฐ ๋๋ฌธ์, ํ ์คํธ๋ ์์ง ํต๊ณผํ ๊ฒ ์ ๋๋ค.
$ rails test
- ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์๋ ์ ์ผ ์ฒซ ๋ฒ์งธ ์ ์ ๋ฅผ ๋ณ์
user์ ๋์ ํด์ฃผ์ธ์. ํด๋น user ์ค๋ธ์ ํธ๋ฅผ ์ฌ์ฉํ์ฌmicropost = user.microposts.create(content: "Lorem ipsum")์ ์คํํ๋ฉด ์ด๋ ํ ๊ฒฐ๊ณผ๋ฅผ ์ป์ ์ ์์ต๋๊น? - ์์ ์ฐ์ต๋ฌธ์ ์์, ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์ ์๋ก์ด micropost๊ฐ ์ถ๊ฐ๋์์ ๊ฒ ์
๋๋ค.
user.microposts.find(micropost.id)๋ฅผ ์คํํ์ฌ, ์ ๋ง๋ก ์ถ๊ฐ๋์๋์ง๋ฅผ ํ์ธํด๋ด ์๋ค. ๋ํ ์์ ์คํํmicropost.id์ ๋ถ๋ถ์micropost๋ก ๋ฐ๊พธ๋ฉด ์ด๋ ํ ๊ฒฐ๊ณผ๊ฐ ์๊ธฐ๋์? user == micropost.user๋ฅผ ์คํํ ๊ฒฐ๊ณผ๋ ์ด๋ป๊ฒ ๋๋์? ๋ํuser.microposts.first == micropost๋ฅผ ์คํํ ๊ฒฐ๊ณผ๋ ์ด๋ป๊ฒ ๋๋์? ๊ฐ๊ฐ ํ์ธํด๋ด ์๋ค.
์ด๋ฒ ์น์ ์์๋ User์ Micropost์ ๊ด๊ณ๋งบ๊ธฐ๋ฅผ ๊ฐ์ ํด๋ณด๊ฒ ์ต๋๋ค. ๊ตฌ์ฒด์ ์ผ๋ก๋ ์ ์ ์ micropost๋ฅผ ํน์ ์์๋ก ์กฐํํ ์ ์๋ค๊ฑฐ๋, micropost๋ฅผ ์ ์ ์๊ฒ ์์กด์ํ๋ก ๋ง๋ค๊ณ ์ ์ ๊ฐ ์ญ์ ๋๋ฉด micropost๋ ์๋์ ์ผ๋ก ์ญ์ ๋๋๋ก ํด๋ณด๊ฒ ์ต๋๋ค.
user.microposts ๋ฉ์๋๋ ๊ธฐ๋ณธ์ผ๋ก ์กฐํ์์์ ๋ํด์ ์๋ฌด๊ฒ๋ ๋ณด์ฆํ์ง ์์ต๋๋ค๋ง ๋ธ๋ก๊ทธ๋ Twitter์ ๊ด์ต์ ๋ฐ๋ผ ์์ฑ์๊ฐ์ ์ญ์, ์ฆ ์ ์ผ ์ต์ ์ micropost๋ฅผ ์ ์ผ ์ฒ์์ผ๋ก ํ์ํ ์ ์๊ฒ ํด๋ด
์๋ค. ์ด๊ฒ์ ๊ตฌํํ๊ธฐ ์ํด์๋ default scope ๋ผ๊ณ ํ๋ ํ
ํฌ๋์ ์ฌ์ฉํด๋ณด๊ฒ ์ต๋๋ค.
์ด ๊ธฐ๋ฅ์ ํ
์คํธ๋ ๊ฒ์์ ๋ณด์ด๊ธฐ๋งํ๋ ๊ฒฐ๊ณผ๋ก ์ฑ๊ณตํ๋ค๊ณ ์๋งํ ์ ์์ต๋๋ค. "์ดํ๋ฆฌ์ผ์ด์
์์์ ๊ตฌํ์ ์ฌ์ค ์๋ชป๋์ด์์ง๋ง ํ
์คํธ๊ฐ ์ฑ๊ณตํด๋ฒ๋ฆฌ๋" ๊ฒฝ์ฐ๊ฐ ์์ต๋๋ค. ์ฌ๋ฐ๋ฅธ ํ
์คํธ๋ฅผ ์์ฑํ๊ธฐ ์ํด์, ์ฌ๊ธฐ์๋ ํ
์คํธ ์ฃผ๋ ๊ฐ๋ฐ๋ก ์งํํด๊ฐ๊ฒ ์ต๋๋ค. ๊ตฌ์ฒด์ ์ผ๋ก๋ ์ฐ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์ ์ ์ผ ์ฒซ ๋ฒ์งธ micropost๊ฐ fixture ๋ด์ micropost(most_recent) ์ ๊ฐ์ ๊ฒ์ ๊ฒ์ฆํ๋ ํ
์คํธ๋ฅผ ์์ฑํด๋ด
์๋ค.
# test/models/micropost_test.rb
require 'test_helper'
class MicropostTest < ActiveSupport::TestCase
.
.
.
test "order should be most recent first" do
assert_equal microposts(:most_recent), Micropost.first
end
end์ ์ฝ๋๋ micropost์ฉ์ fixture ํ์ผ๋ก๋ถํฐ sample ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด์ค๊ณ ์์ต๋๋ค. ๋ค์์ผ๋ก fixture ํ์ผ๋ ํ์ํฉ๋๋ค.
# test/fixtures/microposts.yml
orange:
content: "I just ate an orange!"
created_at: <%= 10.minutes.ago %>
tau_manifesto:
content: "Check out the @tauday site by @mhartl: http://tauday.com"
created_at: <%= 3.years.ago %>
cat_video:
content: "Sad cats are sad: http://youtu.be/PKffm2uI4dk"
created_at: <%= 2.hours.ago %>
most_recent:
content: "Writing a short test"
created_at: <%= Time.zone.now %>์ฌ๊ธฐ์ Embeded Ruby๋ฅผ ์ฌ์ฉํ์ฌ created_at ์ปฌ๋ผ์ ๊ฐ์ ์
ํ
ํ๊ณ ์๋ ์ ์ ์ฃผ๋ชฉํด์ฃผ์ธ์. ์ด ์ปฌ๋ผ์ Rails์ ์ํด ์๋์ ์ผ๋ก ๊ฐฑ์ ๋๊ธฐ ๋๋ฌธ์ ๊ธฐ๋ณธ์ ์ผ๋ก ์๋์ผ๋ก ๊ฐฑ์ ํ ํ์๋ ์์ต๋๋ค๋ง, fixture ํ์ผ ์์์๋ ๊ฐฑ์ ์ด ๊ฐ๋ฅํฉ๋๋ค. ์ด ๊ตฌ์กฐ๋ฅผ ์ฌ์ฉํ์ฌ ์๋์ ์ผ๋ก ์์๋ฅผ ๋ณ๊ฒฝํ๊ณ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด ํ์ผ ๋ด์ ์ ์ผ ์๋ sample ๋ฐ์ดํฐ๋ ์ ์ผ ๋์ค์ ์์ฑ๋์๊ธฐ ๋๋ฌธ์ ์ต์ ์ post ๊ฐ ๋๋๋ก ์์ ํ๋ ๋๋์
๋๋ค. ๋ง์ ์์คํ
์์ fixture๋ ์์์๋ถํฐ ์์๋๋ก ์คํ๋๊ธฐ ๋๋ฌธ์, ํ์ผ ๋ด์ ์ ์ผ ์๋์ ์๋ sample ๋ฐ์ดํฐ๊ฐ ์ ์ผ ๋ง์ง๋ง์ ์์ฑ๋๋, ์ด๋ฌํ ๋์์ ์์กดํ ํ
์คํธ๋ ์์ฑํ ํ์๋ ์์ ๊ฒ์
๋๋ค.
์ฌ๊ธฐ์ ์คํ์์๋ฅผ ํ ์คํธํ๋ ์ ๋๊ฐ์ ์ฝ๋๋ฅผ ์ถ๊ฐํฉ๋๋ค. ์ด ํ ์คํธ๋ฅผ ์คํํ๋ฉด ์ผ๋จ ์คํจํ๊ฒ ๋ ๊ฒ ์ ๋๋ค.
$ rails test test/models/micropost_test.rb
๋ค์์ผ๋ก Rails ์ default_scope ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ์ด ํ
์คํธ๋ฅผ ์ฑ๊ณต์์ผ๋ณด๊ฒ ์ต๋๋ค. ์ด ๋ฉ์๋๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ก๋ถํฐ ๋ฐ์ดํฐ๋ฅผ ์ป์์ ๋, ๊ธฐ๋ณธ ์์๋ฅผ ์ง์ ํ๋ ๋ฉ์๋์
๋๋ค. ํน์ ์์๋ก ํ๊ณ ์ถ์ ๊ฒฝ์ฐ์๋ default_scope ์ ํ๋ผ๋ฏธํฐ์ order ๋ฅผ ๋ถ์ฌํฉ๋๋ค. ์๋ฅผ ๋ค์ด, created_at ์ปฌ๋ผ์ ์์๋ก ํ๊ณ ์ถ์ ๊ฒฝ์ฐ์๋ ๋ค์๊ณผ ๊ฐ์ด ํฉ๋๋ค.
order(:created_at)
๋จ, ์์ฝ๊ฒ๋ ๊ธฐ๋ณธ ์์๊ฐ ์ค๋ฆ์ฐจ์ (ascending) ์ผ๋ก ๋์ด์๊ธฐ ๋๋ฌธ์, ์ด๋๋ก๋ ์์ ์๋ถํฐ ํฐ ์์ ์์ผ๋ก ์ ๋ ฌ๋์ด๋ฒ๋ฆฝ๋๋ค. (์ ์ผ ์ค๋๋ ๋ด์ฉ์ด ์ ์ผ ์ฒ์์ ํ์๋์ด๋ฒ๋ฆฝ๋๋ค.) ์์๋ฅผ ๋ฐ๊พธ๊ธฐ ์ํด์๋, ์กฐ๊ธ ๋ฎ์ ๋ ๋ฒจ์ ๊ธฐ์ ์ด๊ธด ํฉ๋๋ค๋ง ๋ค์๊ณผ ๊ฐ์ด SQL์ ํ๋ผ๋ฏธํฐ๋ก ๋๊ธธ ํ์๊ฐ ์์ต๋๋ค.
order(created_at: :desc)
์ด ์ฝ๋๋ฅผ ์ฌ์ฉํ์ฌ Micropost ๋ชจ๋ธ์ ์์ ํ ๊ฒฐ๊ณผ๋ ์๋์ ๊ฐ์ต๋๋ค.
# app/models/micropost.rb
class Micropost < ApplicationRecord
belongs_to :user
default_scope -> { order(created_at: :desc) } #์ถ๊ฐ
validates :user_id, presence: true
validates :content, presence: true, length: { maximum: 140 }
end์ ์ฝ๋์์๋ ์๋กญ๊ฒ ๋๋ค์ (Stabby lambda) ๋ผ๊ณ ํ๋ ๋ฐฉ์์ด ์ฐ์ด๊ณ ์์ต๋๋ค. ์ด๊ฒ์ Proc ์ด๋ lambda (ํน์ ๋ฌด๋ช
ํจ์) ๋ผ๊ณ ๋ถ๋ฆฌ๋ ์ค๋ธ์ ํธ๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ์
๋๋ค. -> ๋ฅผ ์ฌ์ฉํ๋ ๋๋ค์์, ๋ธ๋ก(4.3.2) ์ ํ๋ผ๋ฏธํฐ๋ก ํ๋ฉฐ Proc ์ค๋ธ์ ํธ๋ฅผ ๋ฆฌํดํฉ๋๋ค. ์ด ์ค๋ธ์ ํธ๋ call ๋ฉ์๋๊ฐ ํธ์ถ๋์์ ๋, ๋ธ๋ก ๋ด์ ์ฒ๋ฆฌ๋ฅผ ํ๊ฐํฉ๋๋ค. ์ด ๋ฌธ๋ฒ์ ์ฝ์์์๋ ํ์ธํ ์ ์์ต๋๋ค.
>> -> { puts "foo" }
=> #<Proc:0x007fab938d0108@(irb):1 (lambda)>
>> -> { puts "foo" }.call
foo
=> nil(Proc์ Ruby์์๋ ์กฐ๊ธ ๋์ด๋๊ฐ ๋์ ์ฃผ์ ์ด๊ธฐ ๋๋ฌธ์, ์ง๊ธ ๋น์ฅ ์ดํดํ ํ์๋ ์์ต๋๋ค.)
์ ๋ชจ๋ธ์ ์ฝ๋๋ฅผ ์ถ๊ฐํ ๊ฒ์ผ๋ก, ํ ์คํธ์ฝ๋๋ ์ฑ๊ณตํ ๊ฒ ์ ๋๋ค.
$ rails test
์์์ ๋ํด์๋ ์ผ๋จ ์ฌ๊ธฐ์ ๋ง์น๋๋กํ๊ฒ ์ต๋๋ค. ์ด๋ฒ์๋ micropost์ ์ 2์ ์์๋ฅผ ์ถ๊ฐํด๋ณด๊ฒ ์ต๋๋ค. 10.4 ์์ ์์ฑํ๋ฏ์ด, ์ฌ์ดํธ ๊ด๋ฆฌ์๋ ์ ์ ๋ฅผ ํ๊ธฐ ํ ๊ถํ์ ๊ฐ์ง๋๋ค. ์ ์ ๊ฐ ํ๊ธฐ๋์์ ๊ฒฝ์ฐ, ์ ์ ์ micropost๋ ๊ฐ์ด ํ๊ธฐ๋์ด์ผํ ๊ฒ ์ ๋๋ค.
์ด๋ฌํ ๋์์ has_many ๋ฉ์๋์ ์ต์
์ ์
๋ ฅํ๋ ๊ฒ์ผ๋ก ๊ตฌํํ ์ ์์ต๋๋ค.
app/models/user.rb
class User < ApplicationRecord
has_many :microposts, dependent: :destroy #Update
.
.
.
enddependent: :destory ๋ผ๊ณ ํ๋ ์ต์
์ ์ฌ์ฉํ๋ฉด, ์ ์ ๊ฐ ์ญ์ ๋์์ ๋ ํด๋น ์ ์ ์ ๊ด๊ณ๋ฅผ ๋งบ์ (ํด๋น ์ ์ ๊ฐ ์์ฑํ) micropost๋ ๊ฐ์ด ์ญ์ ๋๊ฒ ๋ ๊ฒ ์
๋๋ค. ์ด๊ฒ์ ๊ด๋ฆฌ์๊ฐ ์์คํ
์ผ๋ก๋ถํฐ ์ ์ ๋ฅผ ์ญ์ ํ์ ๋, ์์ฑ์๊ฐ ์กด์ฌํ์ง ์์ micropost๊ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋จ๊ฒจ์ง๋ ๋ฌธ์ ๋ฅผ ๋ฐฉ์งํฉ๋๋ค.
๋ค์์ผ๋ก ์ ์ฝ๋๊ฐ ์ ๋๋ก ๋์ํ๋์ง๋ฅผ ํ ์คํธ๋ฅผ ํตํด User๋ชจ๋ธ์ ๊ฒ์ฆํด๋ด ์๋ค. ์ด ํ ์คํธ์์๋ (id๋ฅผ ๊ด๊ณ์ง๊ฒ ํ๊ธฐ ์ํด) ์ ์ ๋ฅผ ์์ฑํ๋ ๊ฒ๊ณผ, ํด๋น ์ ์ ์ ๊ด๊ณ๋ฅผ ๋งบ๊ณ ์๋ micropost๋ฅผ ์์ฑํ ํ์๊ฐ ์์ต๋๋ค. ๊ทธ ํ, ์ ์ ๋ฅผ ์ญ์ ํ์ฌ micropost์ ๊ฐฏ์๊ฐ 1๊ฐ ์ค์ด๋ ๊ฒ์ ํ์ธํฉ๋๋ค. ์์ฑํ ์ฝ๋๋ ์๋์ ๊ฐ์ต๋๋ค. ๋ํ ์ด์ ์ ์์ฑํ [delete] ๋งํฌ์ ํตํฉํ ์คํธ์ ๋น๊ตํด๋ณด๋ ๊ฒ๋ ์ดํดํ๊ธฐ์ ๋์์ด ๋ ๊ฒ ์ ๋๋ค.
# test/models/user_test.rb
require 'test_helper'
class UserTest < ActiveSupport::TestCase
def setup
@user = User.new(name: "Example User", email: "user@example.com",
password: "foobar", password_confirmation: "foobar")
end
.
.
.
test "associated microposts should be destroyed" do
@user.save
@user.microposts.create!(content: "Lorem ipsum")
assert_difference 'Micropost.count', -1 do
@user.destroy
end
end
end์ ์ฝ๋๊ฐ ์ ๋๋ก ๋์ํ๋ค๋ฉด, ํ ์คํธ๋ ํต๊ณผํ ๊ฒ ์ ๋๋ค.
$ rails test
Micropost.first.created_at์ ์คํ๊ฒฐ๊ณผ์Micropost.last.created_at์ ์คํ๊ฒฐ๊ณผ๋ฅผ ๋น๊ตํด๋ด ์๋ค.Micropost.first๋ฅผ ์คํํ์ ๋ ์์ฑ๋๋ SQL์ ์ด๋ ํ ํํ์ธ๊ฐ์? ๋ง์ฐฌ๊ฐ์ง๋กMicropost.last์ ๊ฒฝ์ฐ๋ ์ด๋ค ํํ์ธ๊ฐ์? Hint : ๊ฐ๊ฐ์ ์ฝ๋๋ฅผ ์ฝ์์์ ์คํํด ๋ณด์์ ๋ ํ์๋๋ ๋ฌธ์์ด์ด SQL๋ฌธ์ด ๋ ๊ฒ ์ ๋๋ค.- ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์ ์ ์ผ ์ฒซ ๋ฒ์งธ ์ ์ ๋ฅผ ๋ณ์
user์ ๋์ ํ์ธ์. ํด๋น user ์ค๋ธ์ ํธ๊ฐ ์ ์ผ ์ฒ์์ผ๋ก ์์ฑํ micropost์ id๋ ๋ช์ธ๊ฐ์? ๋ค์์ผ๋กdestory๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ํด๋น user ์ค๋ธ์ ํธ๋ฅผ ์ญ์ ํด๋ณด์ธ์. ์ญ์ ํ๋ฉด, ํด๋น user ์ ๊ด๊ณ๋งบ๊ณ ์๋ micropost๋ ์ญ์ ๋๋ ๊ฒ์,Micropost.find๋ก ํ์ธํด๋ณด์ธ์.
Web ๊ฒฝ์ ๋ก Micropost๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ์ ํ ์์ ์์๋ ์์ต๋๋ค๋ง, (13.3.2์์๋ถํฐ ์์ฑํด๋ด
๋๋ค.) Micropost๋ฅผ ํ์ํ๋ ๊ฒ๊ณผ, ํ
์คํธํ๋ ๊ฒ์ ๊ฐ๋ฅํฉ๋๋ค. ์ฌ๊ธฐ์๋ Twitter์ ๊ฐ์ ๋
๋ฆฝ์ ์ธ micropost์ index ํ์ด์ง๋ฅผ ๋ง๋ค์ง ์๊ณ , ์๋์ ๊ฐ์ด ์ ์ ์ show ํ์ด์ง์ ์ง์ micropost๋ฅผ ํ์์์ผ๋ณด๊ฒ ์ต๋๋ค. ์ ์ ์ ํ๋กํ์ micropost๋ฅผ ํ์์ํค๊ธฐ ์ํด, ๋งค์ฐ ๊ฐ๋จํ ERB ํ
ํ๋ ์ ์ฒ์์ผ๋ก ์์ฑํฉ๋๋ค. ๊ทธ ๋ค์์ผ๋ก๋ 10.3.2 ์ sample ๋ฐ์ดํฐ ์์ฑ ํ์คํฌ์ micropost์ sample์ ์ถ๊ฐํ์ฌ ํ๋ฉด์ sample ๋ฐ์ดํฐ๊ฐ ํ์๋๋์ง๋ฅผ ํ์ธํด๋ณด๊ฒ ์ต๋๋ค.
์ด๋ฒ ์น์
์์๋ ์ ์ ์ ํ๋กํ ํ๋ฉด (show.html.erb) ์์ ํด๋น ์ ์ ์ micropost๋ฅผ ํ์ํ๊ฑฐ๋, ์ง๊ธ๊น์ง ์์ฑํ micropost์ ์ด ๊ฐฏ์๋ฅผ ํ์ํ๋ ์ฒ๋ฆฌ๋ฅผ ๊ตฌํํฉ๋๋ค. ๊ทธ๋ ๋ค๊ณ ํด๋, ์ด๋ฒ์ ํ์ํ ์์ด๋์ด์ ๋๋ถ๋ถ์ 10.3 ์์ ๊ตฌํํ, ์ ์ ๋ฅผ ํ์ํ๋ ๋ถ๋ถ๊ณผ ๋ฎ์ ์์ต๋๋ค.
์ฐ์ต์์ ์ด๋ฏธ micropost๋ฅผ ๋ช ๊ฐ ์ ๋ ์์ฑํ ๊ฒฝ์ฐ์๋, ํ ๋ฒ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ๋ฆฌ์ ํ๊ณ sample ๋ฐ์ดํฐ๋ฅผ ์ฌ์์ฑํด๋์ผ์ธ์.
$ rails db:migrate:reset
$ rails db:seed์ฐ์ , Micropost์ ์ปจํธ๋กค๋ฌ์ ๋ทฐ๋ฅผ ์์ฑํ๊ธฐ ์ํด์ ์ปจํธ๋กค๋ฌ๋ฅผ ์์ฑํด๋ด ์๋ค. ๋ํ ์ด๋ฒ์ ์ฌ์ฉํ๋ ๊ฒ์ ๋ทฐ ๋ฟ๋ง์ด๋ฉฐ, Micropost ์ปจํธ๋กค๋ฌ๋ 13.3๋ถํฐ ์ฌ์ฉํฉ๋๋ค.
$ rails generate controller Microposts
์ด๋ฒ ๋ชฉ์ ์, ์ ์ ๋ณ๋ก ๋ชจ๋ Micropost๋ฅผ ํ์ํ ์ ์๋๋ก ํ๋ ๊ฒ์ ๋๋ค. 10.3.5 ์์ ๋ณธ ์ฝ๋๋,
<ul class="users">
<%= render @users %>
</ul>_user.html.erb ํ์
์ ์ฌ์ฉํ์ฌ ์๋์ ์ผ๋ก @users ๋ณ์ ๋ด์ ๊ฐ๊ฐ์ ์ ์ ๋ฅผ ์ถ๋ ฅํ์์ต๋๋ค. ๊ทธ๊ฒ์ ์ฐธ๊ณ ํ์ฌ _micropost.html.erb ํ์
์ ์ฌ์ฉํ์ฌ micropost์ ์ปฌ๋ ์
์ ํ์ํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ์ด ๋ฉ๋๋ค.
<ol class="microposts">
<%= render @microposts %>
</ol>์ฐ์ ์ ์์๊ฐ ์๋ ๋ฆฌ์คํธ์ธ ul ํ๊ทธ๊ฐ ์๋, ์์๊ฐ ์๋ ๋ฆฌ์คํธ์ ol ํ๊ทธ๋ฅผ ์ฌ์ฉํ๊ณ ์๋ ์ ์ ์ฃผ๋ชฉํด์ฃผ์ธ์. ์ด๊ฒ์ micropost ๊ฐ ํน์ ํ ์์ (์ต์ ์ > ์ค๋๋ ์) ์ผ๋ก ์์กดํ๊ณ ์๊ธฐ ๋๋ฌธ์
๋๋ค. ๋ค์์ผ๋ก ๋์ํ๋ ํ์
์ ์๋์ ์ฝ๋์ ๊ฐ์ต๋๋ค.
<!-- app/views/microposts/_micropost.html.erb -->
<li id="micropost-<%= micropost.id %>">
<%= link_to gravatar_for(micropost.user, size: 50), micropost.user %>
<span class="user"><%= link_to micropost.user.name, micropost.user %></span>
<span class="content"><%= micropost.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
</span>
</li>์ฌ๊ธฐ์๋ time_ago_in_words ๋ผ๊ณ ํ๋ ํฌํผ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๊ณ ์์ต๋๋ค. ์ด๊ฒ์ ๋ฉ์๋ ์ด๋ฆ์ด ๋ํ๋ด๋ ๋๋ก์
๋๋ค๋ง, "3๋ถ ์ ์ ์์ฑ" ์ด๋ผ๋ ๋ฌธ์์ด์ ์ถ๋ ฅํฉ๋๋ค. ๊ตฌ์ฒด์ ์ธ ํจ๊ณผ์ ๋ํด์๋ 13.2.2์์ ์ค๋ช
ํฉ๋๋ค. ๋ํ ์ ์ฝ๋์์๋ ๊ฐ Micropost์ ๋ํ์ฌ CSS์ id๋ฅผ ํ ๋นํ๊ณ ์์ต๋๋ค.
<li id="micropost-<%= micropost.id %>">
์ด ๊ฒ์ ์ผ๋ฐ์ ์ผ๋ก ์ข๋ค๊ณ ํ๊ฐ๋๋ ๊ด์ต์ผ๋ก, ์๋ฅผ ๋ค์ด ์์ผ๋ก Javascript๋ฅผ ์ฌ์ฉํ์ฌ ๊ฐ micropost๋ฅผ ์กฐ์ํ๋ ค๊ณ ํ ๋ ๋์์ด ๋ฉ๋๋ค.
๋ค์์ผ๋ก ํ ๋ฒ์ ๋ชจ๋ micropost๊ฐ ํ์๋์ด๋ฒ๋ฆฌ๋ ์ ์ฌ์ ์ธ ๋ฌธ์ ์ ๋ํด ๋์ฒํด๋ด
๋๋ค. 10.3.3 ์์๋ Pagination์ ์ฌ์ฉํ์์ต๋๋ค๋ง, ์ด๋ฒ์๋ ๊ฐ์ ๋ฐฉ๋ฒ์ผ๋ก ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํด๋ด
๋๋ค. ์ ๋ฒ๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก will_paginate ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉด ๋ค์๊ณผ ๊ฐ์ด ๋ฉ๋๋ค.
<%= will_paginate @microposts %>
์ด์ 10์ฅ์ ์ ์ ๋ฆฌ์คํธ ํ๋ฉด์ ์ฝ๋์ ๋น๊ตํด๋ณด๋ฉด ์กฐ๊ธ์ ๋ค๋ฆ ๋๋ค. ์ด์ ์๋ ๋ค์๊ณผ ๊ฐ์ ๋จ์ํ ์ฝ๋์์ต๋๋ค.
<%= will_paginate %>
์ฌ์ค์ ์ ์ฝ๋๋ ํ๋ผ๋ฏธํฐ๊ฐ ์์ด๋ ๋์ํ๊ณ ์์์ต๋๋ค. ์ด๊ฒ์ will_paginate ๊ฐ Users ์ปจํธ๋กค๋ฌ์ ๋ฌธ๋งฅ์์, @users ์ธ์คํด์ค ๋ณ์๊ฐ ์กด์ฌํ๊ณ ์๋ ๊ฒ์ ์ ์ ๋ก ํ๊ณ ์๊ธฐ ๋๋ฌธ์
๋๋ค. ์ด ์ธ์คํด์ค ๋ณ์๋ 10.3.3 ์ ์์ ํ๋ ๊ฒ ์ฒ๋ผ, ActiveRecord::Relation ํด๋์ค์ ์ธ์คํด์ค์
๋๋ค. ์ด๋ฒ ๊ฒฝ์ฐ์๋ Users ์ปจํธ๋กค๋ฌ์ ๋ฌธ๋งฅ์ผ๋ก๋ถํฐ, micropost๋ฅผ paginationํ๊ณ ์ถ๊ธฐ ๋๋ฌธ์, (์ฆ ๋ฌธ๋งฅ์ด ๋ค๋ฅด๊ธฐ ๋๋ฌธ์), ๋ช
์์ ์ผ๋ก @microposts ๋ณ์๋ฅผ will_paginate ์ ๋๊ธธ ํ์๊ฐ ์์ต๋๋ค. ๋ฐ๋ผ์ ์ด์ ๊ฐ์ด ์ธ์คํด์ค ๋ณ์๋ฅผ Users ์ปจํธ๋กค๋ฌ์ show ์ก์
์์ ์ ์ํด์ฃผ์ด์ผ๋ง ํฉ๋๋ค.
# app/controllers/users_controller.rb
class UsersController < ApplicationController
.
.
.
def show
@user = User.find(params[:id])
@microposts = @user.microposts.paginate(page: params[:page]) #์ถ๊ฐ
end
.
.
.
endpaginate ๋ฉ์๋์ ๋๋จํจ์ ์ฃผ๋ชฉํด์ฃผ์ธ์. micropost ์ ๊ด๊ณ๋ฅผ ๊ฒฝ์ ํ์ฌ micropost ํ
์ด๋ธ์ ๋๋ฌํ๊ณ , ํ์ํ micropost์ ํ์ด์ง๋ฅผ ๋์ด๋ด๊ณ ์์ต๋๋ค.
๋ง์ง๋ง ๊ณผ์ ๋ก๋, micropost์ ์์ฑ ์๋ฅผ ํ์ํ๋ ๊ฒ์
๋๋ค๋ง, ์ด๊ฒ์ count ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ฌ ํด๊ฒฐํ ์ ์์ต๋๋ค.
user.microposts.count
paginate ์ ๋ง์ฐฌ๊ฐ์ง๋ก, ๊ด๊ณ๋ฅผ ํตํ์ฌ count ๋ฉ์๋๋ฅผ ํธ์ถํ๋ ๊ฒ์ด ๊ฐ๋ฅํฉ๋๋ค. ์ค์ํ ์ ์, count ๋ฉ์๋์์๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์ micropost๋ฅผ ์ ๋ถ ์ฝ์ด๋ค์ธ ํ, ๊ฒฐ๊ณผ์ ๋ฐฐ์ด์ ๋ํด length ๋ฅผ ํธ์ถ, ํ๋ ์ด๋ฌํ ๋ถํ์ํ ์ฒ๋ฆฌ๋ ํ์ง ์๋๋ค๋ ์ ์
๋๋ค. ๊ทธ๋ฌํ ํ์๋ฅผ ํ๋ค๋ฉด, micropost์ ์๊ฐ ์ฆ๊ฐํ๋ฉด ๊ทธ์ ๋ฐ๋ผ ํจ์จ์ด ๋จ์ด์ง๊ฒ ๋ ๊ฒ์
๋๋ค. ๊ทธ๋ ์ง ์๊ณ , (๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ด์ ๊ณ์ฐ์ ๊ณ ๋๋ก ์ต์ ํ๋์ด ์๊ธฐ ๋๋ฌธ์) ๋ฐ์ดํฐ๋ฒ ์ด์ค์๊ฒ ๋์ ๊ณ์ฐ์ ํ๊ฒ ํ๊ณ , ํน์ user_id ์ ๊ด๊ณ๋งบ์ด์ ธ ์๋ micropost ์ ์๋ฅผ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์กฐํํฉ๋๋ค. (๊ทธ๋ผ์๋ count ๋ฉ์๋๊ฐ ์ดํ๋ฆฌ์ผ์ด์
์ bottle neck์ด ๋๋ ๊ฒฝ์ฐ๊ฐ ์๋ค๋ฉด, ์ข ๋ ๋น ๋ฅธ counter cache ์ ์ฌ์ฉํ๋ ๊ฒ๋ ๊ฐ๋ฅํฉ๋๋ค.)
์ด๊ฒ์ผ๋ก ๋ชจ๋ ์์๋ ์ ๋ฆฌ๋์์ต๋๋ค. ํ๋กํ ํ๋ฉด์ micropost๋ฅผ ํ์ํด๋ด
์๋ค. (์ด ๋, 7์ฅ์์์ ๋ง์ฐฌ๊ฐ์ง๋ก if @user.microposts.any? ๋ฅผ ์ฌ์ฉํ์ฌ ์ ์ ์ micropost๊ฐ ํ๋๋ ์๋ ๊ฒฝ์ฐ์๋ ๋น ๋ฆฌ์คํธ๋ฅผ ํ์ํ์ง ์๊ฒํ๋ ์ ๋ ์ฃผ๋ชฉํด์ฃผ์ธ์.)
<!-- app/views/users/show.html.erb -->
<% provide(:title, @user.name) %>
<div class="row">
<aside class="col-md-4">
<section class="user_info">
<h1>
<%= gravatar_for @user %>
<%= @user.name %>
</h1>
</section>
</aside>
<div class="col-md-8">
<% if @user.microposts.any? %>
<h3>Microposts (<%= @user.microposts.count %>)</h3>
<ol class="microposts">
<%= render @microposts %>
</ol>
<%= will_paginate @microposts %>
<% end %>
</div>
</div>์ฌ๊ธฐ์ ๊ฐ์ ํ ์๋ก์ด ํ๋กํ ํ๋ฉด์ ๋ธ๋ผ์ฐ์ ์์ ํ์ธํด๋ด ์๋ค. ...์๋ฌด๊ฒ๋ ํ์๋์ง ์๋ ์ธ๋ก์ด ํ์ด์ง๋ค์. micropost๊ฐ ํ๋๋ ์๋ ๊ฑด ์๋๊น์? ๊ทธ๋ผ micropost๋ฅผ ์ถ๊ฐํด๋ด ์๋ค.
- 7.3.3 ์ ๊ฐ๋ณ๊ฒ ์ค๋ช
๋๋ ธ๋ฏ, ์ด๋ฒ ํฌํผ ๋ฉ์๋๋ก ์ฌ์ฉํ
time_ago_in_words๋ฉ์๋๋ Rails ์ฝ์์helper์ค๋ธ์ ํธ๋ก๋ถํฐ ํธ์ถํ ์ ์์ต๋๋ค. ์ดhelper์ค๋ธ์ ํธ์time_ago_in_words๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ3.weeks.ago๋6.months.ago๋ฅผ ์คํํด๋ด ์๋ค. helper.time_ago_in_words(1.year.ago)๋ฅผ ์คํํ๋ฉด, ์ด๋ ํ ๊ฒฐ๊ณผ๊ฐ ๋ณด์ฌ์ง๋์?- microposts ์ค๋ธ์ ํธ์ ํด๋์ค๋ ๋ฌด์์ธ๊ฐ์? Hint : ์ ๋ ๋ฒ์งธ ์ฝ๋์ ์๋ฏ, ์ฐ์
paginate๋ฉ์๋ (ํ๋ผ๋ฏธํฐ๋page: nil) ๋ก ์ค๋ธ์ ํธ๋ฅผ ์ป๊ณ , ๊ทธ ๋ค์class๋ฉ์๋๋ฅผ ํธ์ถํด๋ด ์๋ค.
13.2.1 ์ ์ ์ micropost์ ํ ํ๋ฆฟ ์์ฑ์์ ์ ๊ฒฐ๊ณผ๋ ์กฐ๊ธ์ ๋จ์ํ ๊ฒฐ๊ณผ์ ๋๋ค. 10.3.2 ์ Sample data ์์ฑ Task์ micropost๋ ์ถ๊ฐํ์ฌ ์ด๋ฌํ ์กฐ๊ธ ํ๋น ์ง๋ ์ํฉ์ ์์ ํด๋ด ์๋ค.
๋ชจ๋ ์ ์ ์ micropost๋ฅผ ์ถ๊ฐํ๋ ค๊ณ ํ๋ฉด ์๊ฐ์ด ๊ฑธ๋ฆฌ๊ธฐ ๋๋ฌธ์, take ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ์ ์ผ ์ฒ์์ 6๋ช
๋ง ์ถ๊ฐํด๋ด
์๋ค.
User.order(:created_at).take(6)
(์ด ๋, order ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ผ๋ก, ์ ์ผ ์ด๋ฐ์ ์์ฑ๋ 6๋ช
์ ์ ์ ๋ฅผ ๋ช
์์ ์ผ๋ก ํธ์ถํด๋ผ ์ ์์ต๋๋ค.)
์ด 6๋ช ์ ์ ์ ์ ๋ํด์๋ 1ํ์ด์ง์ ํ์ํ๊ณ์ (30) ๋ฅผ ๋๊ธฐ๊ธฐ ์ํด ๊ฐ๊ฐ 50์ฌ๊ฐ์ micropost๋ฅผ ์ถ๊ฐํด๋ณด๋๋ก ํฉ๋๋ค. ๋ํ ๊ฐ ํฌ๊ณ ๋ด์ฉ์ ๋ํด์๋ Faker gem์ Lorem.sentence๋ผ๊ณ ํ๋ ํธ๋ฆฌํ ๋ฉ์๋๊ฐ ์๊ธฐ ๋๋ฌธ์, ์ด ๋ฉ์๋๋ฅผ ์ฌ์ฉํด๋ณด๊ฒ ์ต๋๋ค. ๋ณ๊ฒฝํ ๊ฒฐ๊ณผ๋ ์๋์ ๊ฐ์ต๋๋ค. (์๋ ์ฝ๋์ ๋ฃจํ ์์๋ ์ํ๊ฐ์ด ์์์ง๋ ๋ชจ๋ฅด๊ฒ ์ต๋๋ค๋ง, ์ด๊ฒ์ 14.3์์ ์คํ ์ดํฐ์ค ํผ๋(ํ์๋ผ์ธ) ์ ๊ตฌํํ๊ธฐ ์ํด ๋์์ด ๋ ๊ฒ ์ ๋๋ค. ์ ์ ๋ณ๋ก 50๊ฐ๋ถ์ micropost๋ฅผ ์ ๋ฆฌํ์ฌ ์์ฑํด๋ฒ๋ฆฌ๋ฉด, ์คํ ์ดํฐ์ค ํผ๋์ ํ์๋๋ ํฌ๊ณ ๊ฐ ๋ชจ๋ ๊ฐ์ ์ ์ ๊ฐ ๋์ด๋ฒ๋ฆฌ๊ณ ๋ง๋, ์๊ฐ์ ์ผ๋ก๋ ์ข์ง ์์ ๊ฒ ์ ๋๋ค.)
# db/seeds.rb
.
.
.
users = User.order(:created_at).take(6)
50.times do
content = Faker::Lorem.sentence(5)
users.each { |user| user.microposts.create!(content: content) }
end์ฌ๊ธฐ์ ์ธ์ ๋์ฒ๋ผ ๊ฐ๋ฐํ๊ฒฝ์ฉ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ค์ ํ ๋ฒ sample data๋ฅผ ์์ฑํฉ๋๋ค.
$ rails db:migrate:reset
$ rails db:seed
์์ฑ์ด ๋๋ฌ๋ค๋ฉด, Rails ์๋ฒ๋ฅผ ์ฌ๋ถํ ํด๋ณด์ธ์.
๊ฐ๊ฐ์ micropost๊ฐ ํ์๋๋ ๊ฒ์ ์ํด, 13.2.1 ์ ๊น๋น ์ง๋ ์์ ์ด ๋๋์ด ๋๋ฌ์ต๋๋ค. ์คํ ๊ฒฐ๊ณผ๋ ์๋์ ๊ฐ์ต๋๋ค.
์ ํ์ด์ง์๋ micropost ๊ณ ์ ์ ์คํ์ผ์ด ์ ์ฉ๋์ด์์ง ์๊ธฐ ๋๋ฌธ์, ์๋์ ์ฝ๋๋ฅผ ์ถ๊ฐํ์ฌ ๊ฒฐ๊ณผ ํ์ด์ง๋ฅผ ์๋ก๊ณ ์นจํด๋ด ์๋ค.
/* app/assets/stylesheets/custom.scss */
.
.
.
/* microposts */
.microposts {
list-style: none;
padding: 0;
li {
padding: 10px 0;
border-top: 1px solid #e8e8e8;
}
.user {
margin-top: 5em;
padding-top: 0;
}
.content {
display: block;
margin-left: 60px;
img {
display: block;
padding: 5px 0;
}
}
.timestamp {
color: $gray-light;
display: block;
margin-left: 60px;
}
.gravatar {
float: left;
margin-right: 10px;
margin-top: 5px;
}
}
aside {
textarea {
height: 100px;
margin-bottom: 5px;
}
}
span.picture {
margin-top: 10px;
input {
border: 0;
}
}์๋ ์ฒซ ๋ฒ์งธ ์ ์ ์ ํ๋กํ ํ๋ฉด์, ์๋ ๋ ๋ฒ์งธ ํ๋ฉด์์๋, 2๋ฒ์งธ ์ ์ ์ ํ๋กํ ํ๋ฉด์ ํ์ํ๊ณ ์์ต๋๋ค. ๋ง์ง๋ง ์คํฌ๋ฆฐ์ท์์๋ ์ ์ผ ์ฒซ ๋ฒ์งธ ์ ์ ์ 2๋ฒ์งธ ํ์ด์ง์ ์ ์ผ ๋ฐ์ ์๋ pagination์ ๋งํฌ๋ฅผ ํ์ํ๊ณ ์์ต๋๋ค. ๊ฐ micropost์ ํ์์๋ 3๊ฐ์ ์ด๋ ํ ๊ฒฝ์ฐ๋ผ๋, ์์ฑ๋ ํ๋ก์ ๊ฒฝ๊ณผ์๊ฐ ("1๋ถ์ ์ ํฌ๊ณ " ๋ฑ)์ด ํ์๋๋ ๊ฒ์ ์ฃผ๋ชฉํด์ฃผ์ธ์. ์ด๊ฒ์ ์ด์ ์ ์์ฑํ time_ago_in_words ๋ฉ์๋์ ์ํ ๊ฒ ์
๋๋ค. ์ ๋ถ์ ๊ธฐ๋ค๋ฆฌ๊ณ ๋์ ํจ์ด์ง๋ฅผ ์๊ณ ๋ก์นจํ๋ฉด, ์ด ํ
์คํธ๋ ์๋์ ์ผ๋ก ์๋ก์ด ์๊ฐ๋๋ก ๊ฐฑ์ ๋ ๊ฒ ์
๋๋ค.
(1..10).to_a.take(6)๋ผ๊ณ ํ๋ ์ฝ๋์ ์คํ๊ฒฐ๊ณผ๋ฅผ ์ถ์ธกํด๋ณผ ์ ์์ต๋๊น? ์ถ์ธกํ ๊ฒฐ๊ณผ๊ฐ์ด ์ ๋๋ก ๋ง๋์ง ์ค์ ๋ก ์ฝ์์์ ํ์ธํด๋ด ์๋ค.- ๋ฐฉ๊ธ ์ ์ฐ์ต์์ ์ฌ์ฉํ
to_a๋ฉ์๋์ ๋ถ๋ถ์ ์ ๋ง๋ก ํ์ํ ๋ถ๋ถ์ธ๊ฐ์? ํ์ธํด๋ด ์๋ค. - Faker ๋ Lorem ipsum ์ด์ธ์๋ ๋งค์ฐ ๋ค์ํ ์ฌ๋ก์ ๋์ํ๊ณ ์์ต๋๋ค. Faker์ ๋ฌธ์ ๋ฅผ ํ์ธํ๋ฉด์ ํ๋ฉด์ ์ถ๋ ฅํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์ฐ๊ณ , ์ค์ ๋ก ๋ํ์ด๋ฆ์ด๋ ์ ํ๋ฒํธ ๋ฑ์ ํ๋ฉด์ ์ถ๋ ฅํด๋ด ์๋ค.
์ด์ ๋ง account๋ฅผ ์ ํจํํ ์ ์ ๋ ํ๋กํํ๋ฉด์ ๋ฆฌ๋ค์ด๋ ํธ๋๊ธฐ์, ํด๋น ํ๋กํํ๋ฉด์ด ์ ๋๋ก ํ์๋๋์ง๋ ์ ๋ ํ ์คํธ๋ฅผ ํตํด ํ์ธํด๋ณด์์ต๋๋ค. ์ด๋ฒ ์น์ ์์๋ ํ๋กํ ํ๋ฉด์ ํ์๋๋ micropost์ ๋ํด ํตํฉ ํ ์คํธ๋ฅผ ์์ฑํด๋ณด๊ฒ ์ต๋๋ค. ์ฐ์ ํ๋กํ ํ๋ฉด์ฉ์ ํตํฉ ํ ์คํธ๋ฅผ ์์ฑํด๋ด ์๋ค.
$ rails generate integration_test users_profile
invoke test_unit
create test/integration/users_profile_test.rbํ๋กํํ๋ฉด์ ์๋ micropost๋ฅผ ํ ์คํธํ๊ธฐ ์ํด์๋ ์ ์ ์ ๊ด๊ณ๊ฐ ๋งบ์ด์ ธ ์๋ micropost์ ํ ์คํธ์ฉ ๋ฐ์ดํฐ๊ฐ ํ์ํฉ๋๋ค. Rails์ ๊ด์ต์ ๋ฐ๋ผ, ๊ด๋ จ ํ ์คํธ ๋ฐ์ดํฐ๋ฅผ fixture ํ์ผ์ ์ถ๊ฐํ๋ฉด, ๋ค์๊ณผ ๊ฐ์ด ๋ ๊ฒ ์ ๋๋ค.
orange:
content: "I just ate an orange!"
created_at: <%= 10.minutes.ago %>
user: michael
user ์ michael ์ด๋ผ๊ณ ํ๋ ๊ฐ์ ์
๋ ฅํ๋ฉด, Rails๋ fixture ํ์ผ ๋ด์ ๋์ํ๋ ์ ์ ๋ฅผ ์ฐพ์๋ด์ด (ํน์ ๋ฐ๊ฒฌํ๋ค๋ฉด,) micropost์ ๊ด๊ณ๋ฅผ ๋งบ์ด์ค ๊ฒ ์
๋๋ค.
name: Michael Example
email: michael@example.com
.
.
.
๋ํ micropost์ pagination์ ํ ์คํธํ๊ธฐ ์ํด, micropost์ฉ์ fixture์ ๋ช๊ฐ์ง ํ ์คํธ ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐํ ํ์๊ฐ ์์ต๋๋ค๋ง, ์ด๊ฒ์ ์ด์ 10์ฅ์์ ์ ์ ๋ฅผ ์ถ๊ฐํ์ ๋์ ๋ง์ฐฌ๊ฐ์ง๋ก, Embeded Ruby์ฝ๋๋ฅผ ์ฌ์ฉํ๋ฉด ๊ฐ๋จํ ๊ฒ ์ ๋๋ค.
<% 30.times do |n| %>
micropost_<%= n %>:
content: <%= Faker::Lorem.sentence(5) %>
created_at: <%= 42.days.ago %>
user: michael
<% end %>
์ ์ฝ๋๋ฅผ 1๊ฐ๋ก ์ ๋ฆฌํ๋ฉด, micropost ์ฉ์ fixture ํ์ผ์ ์๋์ ๊ฐ์ด ๋ฉ๋๋ค.
# test/fixtures/microposts.yml
orange:
content: "I just ate an orange!"
created_at: <%= 10.minutes.ago %>
user: michael
tau_manifesto:
content: "Check out the @tauday site by @mhartl: http://tauday.com"
created_at: <%= 3.years.ago %>
user: michael
cat_video:
content: "Sad cats are sad: http://youtu.be/PKffm2uI4dk"
created_at: <%= 2.hours.ago %>
user: michael
most_recent:
content: "Writing a short test"
created_at: <%= Time.zone.now %>
user: michael
<% 30.times do |n| %>
micropost_<%= n %>:
content: <%= Faker::Lorem.sentence(5) %>
created_at: <%= 42.days.ago %>
user: michael
<% end %>ํ
์คํธ๋ฐ์ดํฐ๋ ์ค๋น๊ฐ ๋์์ต๋๋ค. ์ง๊ธ๋ถํฐ๋ ํ
์คํธ ์ฝ๋๋ฅผ ์์ฑํด๋ณด๊ฒ ์ต๋๋ค๋ง, ์ด๋ฒ ํ
์คํธ๋ ์กฐ๊ธ์ ๋จ์ํฉ๋๋ค. ์ด๋ฒ ํ
์คํธ์์๋ ํ๋กํ ํ๋ฉด์ ์ก์ธ์ค ํ ๋ค์, ํ์ด์ง ํ์ดํ๊ณผ ์ ์ ์ด๋ฆ, Gravatar, micropost์ ํฌ๊ณ ๊ฐ์, ๊ทธ๋ฆฌ๊ณ ํ์ด์ง ๋ถํ ๋ micropost ๋ฑ์ ์์ผ๋ก ํ
์คํธํด๋ณผ ๊ฒ ์
๋๋ค. ์์ฑํ ์ฝ๋๋ ์๋์ ๊ฐ์ต๋๋ค. (Application ํฌํผ๋ฅผ ์ฝ์ด๋ค์ธ ๊ฒ์ผ๋ก, full_title ํฌํผ๋ฅผ ์ด์ฉํ ์ ์๋ ์ ์ ์ฃผ๋ชฉํด์ฃผ์ธ์.)
# test/integration/users_profile_test.rb
require 'test_helper'
class UsersProfileTest < ActionDispatch::IntegrationTest
include ApplicationHelper
def setup
@user = users(:michael)
end
test "profile display" do
get user_path(@user)
assert_template 'users/show'
assert_select 'title', full_title(@user.name)
assert_select 'h1', text: @user.name
assert_select 'h1>img.gravatar'
assert_match @user.microposts.count.to_s, response.body
assert_select 'div.pagination'
@user.microposts.paginate(page: 1).each do |micropost|
assert_match micropost.content, response.body
end
end
end์ ์ฝ๋์์๋ micropost์ ํฌ๊ณ ๊ฐ์๋ฅผ ์ฒดํฌํ๊ธฐ ์ํด์, ์ 12์ฅ ์ ์ฐ์ต (12.3.3 ์ ์ฐ์ต๋ฌธ์ ) ์์ ์๊ฐํ response.body ๋ฅผ ์ฌ์ฉํ๊ณ ์์ต๋๋ค. ์ด๋ฆ์ ๋ณด๋ฉด ์คํดํ ์ง๋ ๋ชจ๋ฅด๊ฒ ์ต๋๋ค๋ง, response.body ์๋ ํด๋น ํ์ด์ง์ ์์ ํ HTML์ด ํฌํจ๋์ด ์์ต๋๋ค. (HTML์ body ํ๊ทธ ๋ฟ๋ง ์๋ ๊ฒ์ด ์๋๋๋ค.) ๋ฐ๋ผ์ ํด๋น ํ์ด์ง์ ์ด๋๊ฐ์๋ micropost์ ํฌ๊ณ ๊ฐ์๊ฐ ์กด์ฌํ๋ค๋ฉด, ๋ค์๊ณผ ๊ฐ์ด ์ฐพ์๋ด์ด ๋งค์นํ ์ ์์ ๊ฒ ์
๋๋ค.
assert_match @user.microposts.count.to_s, response.body
์ด ๊ฒ์ assert_select ๋ณด๋ค๋ ์ข ๋ ์ถ์์ ์ธ ๋ฉ์๋์
๋๋ค. ํนํ assert_select ์์๋ ์ด๋ ํ HTML ํ๊ทธ๋ฅผ ์ฐพ๋์ง ์๋ ค์ฃผ๋ ๊ฒ์ด ํ์ํฉ๋๋ค๋ง, assert_match ๋ฉ์๋์์๋ ๊ทธ๋ด ํ์๊ฐ ์๋ค๋ ์ ์ด ๋ค๋ฆ
๋๋ค.
๋ํ assert_select ์ ํ๋ผ๋ฏธํฐ์๋ ์ข ๋ ์์ธํ ๋ฌธ๋ฒ์ ์ฌ์ฉํ๊ณ ์๋ค๋ ์ ์ ์ฃผ๋ชฉํด์ฃผ์ธ์.
assert_select 'h1>img.gravatar'
์ด๋ ๊ฒ ์์ฑํ๋ ๊ฒ์ผ๋ก h1 ํ๊ทธ (์ ์ผ ํฐ ํ์ ๊ธ์จ) ์ ์์ชฝ ์ ์๋ gravatar ํด๋์ค๋ฅผ ๊ฐ์ง img ํ๊ทธ๊ฐ ์กด์ฌํ๋์ง๋ฅผ ์ฒดํฌํฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ ์ดํ๋ฆฌ์ผ์ด์ ์ธก์ ์ฝ๋๋ ๊ตฌํํด๋์์ผ๋ฏ๋ก, ์ด ํ ์คํธ๋ ํต๊ณผํ ๊ฒ ์ ๋๋ค.
$ rails test
- ํ
์คํธ ์ฝ๋์ ์๋ 2๊ฐ์
h1์ ํ ์คํธ๊ฐ ์ฌ๋ฐ๋ฅธ์ง ํ์ธํ๊ธฐ ์ํด, ํด๋น ์ดํ๋ฆฌ์ผ์ด์ ์ธก์ ์ฝ๋๋ฅผ ์ฝ๋ฉํธ์์ํด๋ด ์๋ค. ํ ์คํธ๊ฐ ํต๊ณผํ์ง ์๋ ๊ฒ์ ํ์ธํด๋ด ์๋ค. - ํ
์คํธ ์ฝ๋๋ฅผ ๋ณ๊ฒฝํ์ฌ,
will_paginate๊ฐ 1๋ฒ๋ง ํ์๋๋ ๊ฒ์ ํ ์คํธํด๋ด ์๋ค. Hint : ์ด์ 5์ฅ์ 2๋ฒ์งธํ๋ฅผ ํ์ธํด์ฃผ์ธ์.
๋ฐ์ดํฐ ๋ชจ๋ธ๋ง๊ณผ micropost ํ์ ํ ํ๋ฆฟ์ ์์ชฝ์ ์์ฑ์์ผฐ์ต๋๋ค. ๋ค์์ Web ๊ฒฝ์ ๋ก micropost๋ฅผ ์์ฑํ๊ธฐ ์ํ Interface๋ฅผ ์์ฑํด๋ด ์๋ค. ์ด๋ฒ ์น์ ์์๋ Status feed (์ 14์ฅ์์ ์์ฑ์ํต๋๋ค.) ์ ์ ์ผ ์ฒซ ๋ฒ์งธ ํํธ๋ฅผ ๋ณด์ฌ๋๋ฆฌ๊ฒ ์ต๋๋ค. ๋ง์ง๋ง์ผ๋ก๋ ์ ์ ๊ฐ micropost๋ฅผ Web ๊ฒฝ์ ๋ก ์ญ์ ํ ์ ์๋๋ก ํด๋ด ์๋ค.
์ง๊ธ๊น์ง์ Rails๊ฐ๋ฐ์ ์ฐ์ต๊ณผ ๋ค๋ฅธ ๋ถ๋ถ์ด 1๊ตฐ๋ฐ ์์ต๋๋ค. Micropost ๋ฆฌ์์ค๋ก์ Interface๋ ์ฃผ๋ก ํ๋กํ ํ์ด์ง์ Home ํ์ด์ง์ ์ปจํธ๋กค๋ฌ๋ฅผ ๊ฒฝ์ ํ์ฌ ์คํ๋์๊ธฐ ๋๋ฌธ์, Micropost ์ปจํธ๋กค๋ฌ๋ new ๋ edit ๊ณผ ๊ฐ์ ์ก์
์ ํ์ํ์ง ์์ต๋๋ค. ์ฆ, create ๋ destroy ๋ง ์์ผ๋ฉด ์ถฉ๋ถํฉ๋๋ค. ๋ฐ๋ผ์ Microposts์ ๋ฆฌ์์ค๋ ์๋์ ๊ฐ์ด ๋ฉ๋๋ค. ๊ทธ ๊ฒฐ๊ณผ, ์๋ ์ฝ๋๋ RESTfulํ ๋ผ์ฐํ
์ ๋ถ๋ถ์งํฉ์ด ๋ฉ๋๋ค. ๋ฌผ๋ก ์ฌํํ๊ฒ ๋์๋ค๋ ๊ฒ์ ์์ฑ๋๊ฐ ๋์ฑ ๋์์ก๋ค๋ ์ฆ๊ฑฐ์ด๋ฉฐ, ํดํ๋์ง ์์์ต๋๋ค. ์ 2์ฅ ์์ scaffold์ ๋งก๊ธฐ๊ธฐ๋งํ๋ ์์ ๋ถํฐ ์ง๊ธ์ ์ด๋ฅด๊ธฐ๊น์ง ๋จธ๋๋จผ ๊ธธ์ด์์ต๋๋ค๋ง, ์ง๊ธ์ scaffold๊ฐ ์์ฑํด์ฃผ๋ ๋ณต์กํ ์ฝ๋๋ ๊ฑฐ์ ํ์ํ์ง ์๊ฒ ๋์์ต๋๋ค.
# config/routes.rb
Rails.application.routes.draw do
root 'static_pages#home'
get '/help', to: 'static_pages#help'
get '/about', to: 'static_pages#about'
get '/contact', to: 'static_pages#contact'
get '/signup', to: 'users#new'
get '/login', to: 'sessions#new'
post '/login', to: 'sessions#create'
delete '/logout', to: 'sessions#destroy'
resources :users
resources :account_activations, only: [:edit]
resources :password_resets, only: [:new, :create, :edit, :update]
resources :microposts, only: [:create, :destroy] #new
end| HTTP Request | URL | Action | Named root |
|---|---|---|---|
POST |
/microposts | create |
microposts_path |
DELETE |
/microposts/1 | destroy |
micropost_path(micropost) |
Microposts ๋ฆฌ์์ค์ ๊ฐ๋ฐ์์๋, Microposts ์ปจํธ๋กค๋ฌ ๋ด๋ถ์ ์ ๊ทผ์ ์ด๋ถํฐ ์์ํด๋ด
์๋ค. ๊ด๋ จ๋ ์ ์ ๋ค์ ํตํ์ฌ micropost๋ก ์ ๊ทผํ๊ธฐ ๋๋ฌธ์, create ์ก์
์ด๋ destory ์ก์
์ ์ด์ฉํ๋ ์ ์ ๋ ์ด๋ฏธ ๋ก๊ทธ์ธ๋์ด์๋ ์ํ์ด์ง ์์ผ๋ฉด ์๋ฉ๋๋ค.
๋ก๊ทธ์ธ๋์ด์๋์ง ์๋์ง ํ์ธํ๋ ํ ์คํธ์์๋ Users ์ปจํธ๋กค๋ฌ์ฉ์ ํ ์คํธ๋ฅผ ๊ทธ๋๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค. ์ฆ, ์ฌ๋ฐ๋ฅธ Request๋ฅผ ๊ฐ ์ก์ ์ผ๋ก ์คํ์์ผ, Micropost์ ์์ ๋ณํ๊ฐ ์๋์ง ์๋์ง๋ฅผ ํ์ธํ๊ณ , Redirect๋๋์ง๋ฅผ ํ์ธํ๋ฉด ๋ ๊ฒ ๊ฐ์ต๋๋ค.
# test/controllers/microposts_controller_test.rb
require 'test_helper'
class MicropostsControllerTest < ActionDispatch::IntegrationTest
def setup
@micropost = microposts(:orange)
end
test "should redirect create when not logged in" do
assert_no_difference 'Micropost.count' do
post micropost_path, params: { micropost: { content: "Lorem ipsum" } }
end
assert_redirected_to login_url
end
test "should redirect destroy when not logged in" do
assert_no_difference 'Micropost.count' do
delete micropost_path(@micropost)
end
assert_redirected_to login_url
end
end์ ํ
์คํธ ์ฝ๋์์ ํ
์คํธ์ ํต๊ณผํ๊ธฐ ์ํ ์ฝ๋๋ฅผ ์์ฑํ๊ธฐ ์ํด์๋, ์ฝ๊ฐ์ application์์์ ์ฝ๋๋ฅผ Refactoringํด์ผํ ํ์๊ฐ ์์ต๋๋ค. 10.2.1 ์์๋ before ํํฐ์ logged_in_user ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ, ๋ก๊ทธ์ธ์ ์์ฒญํ๋ ๊ฒ์ ๋ ์ฌ๋ ค์ฃผ์ธ์. ๊ทธ ๋์๋ Users ์ปจํธ๋กค๋ฌ ๋ด๋ถ์ ์ด ๋ฉ์๋๊ฐ ์กด์ฌํ์๊ธฐ ๋๋ฌธ์, before ํํฐ๋ก ์ง์ ํ์์ต๋๋ค๋ง, ์ด ๋ฉ์๋๋ microposts ์ปจํธ๋กค๋ฌ์์๋ ํ์ํฉ๋๋ค. ๋๋ฌธ์ ๊ฐ ์ปจํธ๋กค๋ฌ๊ฐ ๊ณ์นํ๋ Application ์ปจํธ๋กค๋ฌ์ ๋ด๋ถ์ ์ด ๋ฉ์๋๋ฅผ ์ฎ๊ฒจ๋ด
์๋ค. ์์ฑํ ์ฝ๋๋ ์๋์ ๊ฐ์ต๋๋ค.
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
include SessionsHelper
private
# ์ ์ ์ ๋ก๊ทธ์ธ์ ํ์ธํฉ๋๋ค.
def logged_in_user
unless logged_in?
store_location
flash[:danger] = "Please log in."
redirect_to login_url
end
end
end์ฝ๋๊ฐ ์ค๋ณตํ์ง ์๋๋ก, Users์ปจํธ๋กค๋ฌ๋ถํฐ logged_in_user ๋ฅผ ์ญ์ ํฉ์๋ค.
# app/controllers/users_controller.rb
class UsersController < ApplicationController
before_action :logged_in_user, only: [:index, :edit, :update, :destroy]
.
.
.
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
# before filter
# ์ฌ๋ฐ๋ฅธ ์ ์ ์ธ์ง ํ์ธํฉ๋๋ค.
def correct_user
@user = User.find(params[:id])
redirect_to(root_url) unless current_user?(@user)
end
# ๊ด๋ฆฌ์์ธ์ง ํ์ธํฉ๋๋ค.
def admin_user
redirect_to(root_url) unless current_user.admin?
end
endapplication_controller.rb ์ ์ฝ๋์ ๋ฐ๋ผ์๋, Microposts ์ปจํธ๋กค๋ฌ๋ก๋ถํฐ๋ logged_in_user ๋ฉ์๋๋ฅผ ํธ์ถํ ์ ์๊ฒ ๋์์ต๋๋ค. ์ด๊ฒ์ผ๋ก create ์ก์
์ด๋ destroy ์ก์
์ ๋ํ ์ ๊ทผ์ ์ด๋ฅผ, before ํํฐ๋ก ๊ฐ๋จํ๊ฒ ๊ตฌํํ ์ ์๊ฒ ๋์์ต๋๋ค.
# app/controllers/microposts_controller.rb
class MicropostsController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy]
def create
end
def destroy
end
end์ด ๊ฒ์ผ๋ก ํ ์คํธ๋ ํต๊ณผํ ๊ฒ ์ ๋๋ค.
$ rails test
- ์ด์งธ์ Users ์ปจํธ๋กค๋ฌ ๋ด๋ถ์ ์๋
logged_in_userํํฐ๋ฅผ ๋จ๊ฒจ๋๋ค๋ฉด ์ข์ง ์์ ๊ฒ์ผ๊น์? ์๊ฐํด๋ด ์๋ค.
์ 7์ฅ ์์๋ HTTP POST Request๋ฅผ Users ์ปจํธ๋กค๋ฌ์ create ์ก์
์ผ๋ก ๋ฐํํ๋ HTML form์ ์์ฑํ์ฌ ์ ์ ์ sign up ์ ๊ตฌํํด๋ณด์์ต๋๋ค. Micropost ์์ฑ์ ๊ตฌํ๋ ๋น์ทํฉ๋๋ค. ์ฃผ๋ ์ฐจ์ด์ ์ ๋ค๋ฅธ micropost / new ํ์ด์ง๋ฅผ ์ฌ์ฉํ๋ ๋์ ์, Home ํ๋ฉด (์ฆ, Root path) ์ Form์ ์ค์นํ๋ค๋ ๊ฒ์
๋๋ค. ์๋์ ๋ชฉ์
์ ํ์ธํด์ฃผ์ธ์.
๋ง์ง๋ง์ผ๋ก Home ํ๋ฉด์ ๊ตฌํํ์์ ๋, (5์ฅ) [Sign up now !] ๋ฒํผ์ด ์ค์์ ์์์ต๋๋ค. Micropost ์์ฑ form์, ๋ก๊ทธ์ธ๋์ด์๋ ํน์ ์ ์ ๋ง์ด ์ฌ์ฉํ ์ ์๋ ๊ธฐ๋ฅ์ด๊ธฐ ๋๋ฌธ์, ์ด ์น์ ์์์ ๋ชฉํ์ค ํ๋๋ก๋, ์ ์ ์ ๋ก๊ทธ์ธ ๊ธฐ๋ฅ์ ๋ํ์ฌ Home ํ๋ฉด์ ํ์๋ฅผ ๋ณ๊ฒฝํ๋ ๊ฒ์ ๋๋ค. ์ด๊ฒ์ ๋ํด์๋ ์๋ ๋ ๋ฒ์งธ ์ฝ๋์์ ๊ตฌํํด๋ด ๋๋ค.
๋ค์์ผ๋ก Micropost์ create ์ก์
์ ์์ฑํด๋ด
์๋ค. ์ด ์ก์
๋ ์ด์ 7์ฅ์์ ๊ตฌํํ ์ ์ ์ฉ์ ์ก์
๊ณผ ๋น์ทํฉ๋๋ค. ์ฐจ์ด์ ์ผ๋ก๋ ์๋ก์ด micropost๋ฅผ build ํ๊ธฐ ์ํด User/Micropost ๊ด๊ณ๋ฅผ ์ฌ์ฉํ๊ณ ์๋ ๊ฒ์
๋๋ค. micropost_params ์์ Strong Parameters ๋ฅผ ์ฌ์ฉํจ์ผ๋ก ์ธํ์ฌ, micropost์ content ์์ฑ๋ง์ด Web ๊ฒฝ์ ๋ก ๋ณ๊ฒฝ๊ฐ๋ฅํ๊ฒ ๋ ์ ์ ์ฃผ๋ชฉํด์ฃผ์ธ์.
# app/controllers/microposts_controller.rb
class MicropostsController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy]
def create
@micropost = current_user.microposts.build(micropost_params)
if @micropost.save
flash[:success] = "Micropost created!"
redirect_to root_url
else
render 'static_pages/home'
end
end
def destroy
end
private
def micropost_params
params.require(:micropost).permit(:content)
end
endMicropost ์์ฑ Form์ ๊ตฌ์ถํ๊ธฐ ์ํด, ์ฌ์ดํธ ๋ฐฉ๋ฌธ์๊ฐ ๋ก๊ทธ์ธํ๊ณ ์๋์ง์ ๋ฐ๋ผ ๋ณํ๋ HTML์ ์ ๊ณตํ๋ ์ฝ๋๋ฅผ ์ฌ์ฉํด๋ด ์๋ค.
<!-- app/views/static_pages/home.html.erb -->
<!-- new -->
<% if logged_in? %>
<div class="row">
<aside class="col-md-4">
<section class="user_info">
<%= render 'shared/user_info' %>
</section>
<section class="micropost_form">
<%= render 'shared/micropost_form' %>
</section>
</aside>
</div>
<% else %>
<!-- new -->
<div class="center jumbotron">
<h1>Welcome to the Sample App</h1>
<h2>
This is the home page for the
<a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a>
sample application.
</h2>
<%= link_to "Sign up now!", signup_path, class: "btn btn-lg btn-primary" %>
</div>
<%= link_to image_tag("rails.png", alt: "Rails logo"),
'http://rubyonrails.org/' %>
<% end %> <!-- new -->if-else ๋ฌธ์ ๋ถ๊ธฐ์์ ์ฝ๋๋ฅผ ๋๋๊ณ ์๋ ์ ์ด ์กฐ๊ธ์ ๋ฒ๊ฑฐ๋กญ๊ธด ํ์ง๋ง, ์ด ์ฝ๋์ Refactoring์ ์ฐ์ต๋ฌธ์ ๋ก ๋ด๊ธฐ๋ก ํด๋ด
์๋ค.
์ ์ฝ๋๋ฅผ ๋์ํ๊ฒ ํ๊ธฐ ์ํด์๋ ๋ช๊ฐ์ง์ Partial์ ์์ฑํ ํ์๊ฐ ์์ต๋๋ค. ์ฐ์ Home ํ์ด์ง์ ์๋ก์ด ์ฌ์ด๋๋ฐ๋ถํฐ์ ๋๋ค. ๋ค์ ์ฝ๋์ ๊ฐ์ต๋๋ค.
<!-- app/views/shared/_user_info.html.erb -->
<%= link_to gravatar_for(current_user, size: 50), current_user %>
<h1><%= current_user.name %></h1>
<span><%= link_to "view my profile", current_user %></span>
<span><%= pluralize(current_user.microposts.count, "micropost") %></span>ํ๋กํ ์ฌ์ด๋๋ฐ์ ๋ง์ฐฌ๊ฐ์ง๋ก, ์ ์ฝ๋์ ์ ์ ์ ๋ณด์๋ ํด๋น ์ ์ ๊ฐ ํฌ๊ณ ํ micropost์ ์ด ๊ฐฏ์๋ฅผ ํ์ํ๊ณ ์๋ ๊ฒ์ ๋ํด ์ฃผ๋ชฉํด์ฃผ์ธ์. ๋จ, ์กฐ๊ธ ํ์ํ๋ ๋ฐฉ๋ฒ์ด ๋ฌ๋ผ์ง๋๋ค. ํ๋กํ ์ฌ์ด๋๋ฐ์์๋ "Micropost" ์ ๋ผ๋ฒจ๋ก ํ์ํ๊ณ , "Micropost(1)" ๋ก ํ์ํ๋ ๊ฒ์ ๋ฌธ์ ๊ฐ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ์ด๋ฒ ์ฒ๋ผ "1 micropost" ๋ผ๊ณ ํ์ํด๋ฒ๋ฆฌ๋ฉด ์์ด์ ๋ฌธ๋ฒ์ ์ค๋ฅ๊ฐ ๋ฐ์ํฉ๋๋ค. ์ฌ๊ธฐ์ 7.3.3 ์๊ฐํ pluralize ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ "1 micropost" ๋ "2 micrposts" ๋ผ๊ณ ํ์ํ ์ ์๋๋ก ์กฐ์ ํฉ๋๋ค.
๋ค์์ผ๋ก๋ micropost์์ฑ form์ ์ ์ํด๋ณด๊ฒ ์ต๋๋ค. ์ด๊ฒ์ ์ ์ ๋ฑ๋ก form๊ณผ ๋น์ทํฉ๋๋ค. (7์ฅ)
<!-- app/views/shared/_micropost_form.html.erb -->
<%= form_for(@micropost) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "Compose new micropost..." %>
</div>
<%= f.submit "Post", class: "btn btn-primary" %>
<% end %>์ ์ฝ๋์์์ form์ด ๋์ํ๊ฒ ํ๊ธฐ ์ํด์๋, 2๊ฐ์ง์ ๋ณ๊ฒฝ์ด ํ์ํฉ๋๋ค. ํ๋๋ (์ด์ ๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก) ๊ด๊ณ๋งบ๊ธฐ๋ฅผ ์ด์ฉํ์ฌ ๋ค์๊ณผ ๊ฐ์ด @micropost ๋ฅผ ์ ์ํฉ๋๋ค.
@micropost = current_user.microposts.build
์์ฑํ ์ฝ๋๋ ์๋์ ๊ฐ์ต๋๋ค.
# app/controllers/static_pages_controller.rb
# home ์ก์
์ micropost๋ฅผ ์ธ์คํด์ค๋ณ์๋ฅผ ์ถ๊ฐํ๋ค.
class StaticPagesController < ApplicationController
def home
@micropost = current_user.microposts.build if logged_in?
end
def help
end
def about
end
def contact
end
end๋ฌผ๋ก current_user ๋ฉ์๋๋ ์ ์ ๊ฐ ๋ก๊ทธ์ธํ๊ณ ์์๋๋ง ์ธ ์ ์์ต๋๋ค. ๋ฐ๋ผ์ @micropost ๋ณ์๋ ๋ก๊ทธ์ธํ๊ณ ์์๋๋ง ์ ์๋ ์ ์๋๋ก ํฉ๋๋ค.
์ ๋๋ฒ์งธ ์ฝ๋๋ฅผ ๋์ํ๊ฒ ํ๊ธฐ ์ํ ๋ค๋ฅธ ํ๋์ ๋ณ๊ฒฝ์ ์, ์๋ฌ ๋ฉ์ธ์ง์ partial์ ์ฌ์ ์ํ๋ ๊ฒ์ ๋๋ค. ๊ทธ๋ ์ง ์๋๋ค๋ฉด ์ ๋๋ฒ์งธ ์ฝ๋๋ ๋์ํ์ง ์์ ๊ฒ ์ ๋๋ค.
<%= render 'shared/error_messages, object: f.object %>
7์ฅ์์๋ ์๋ฌ ๋ฉ์ธ์ง partial์ด @user ๋ณ์๋ฅผ ์ง์ ์ฐธ์กฐํ๊ณ ์๋ ๊ฒ์ ๋ ์ฌ๋ ค์ฃผ์ธ์. ์ด๋ฒ์๋ ๊ทธ ๋์ ์, @micropost ๋ณ์๋ฅผ ์ฌ์ฉํ ํ์๊ฐ ์์ต๋๋ค. ๊ทธ๋ฌํ ์ผ์ด์ค๋ค์ ์ ๋ฆฌํด๋ณด๋ฉด, form ๋ณ์ f ๋ฅผ, f.object ๋ก ์ ์ธํ๋ ๊ฒ์ผ๋ก, ๊ด๊ณ๋งบ์ด์ ธ์๋ ์ค๋ธ์ ํธ์ ์ก์ธ์คํ ์ ์์ต๋๋ค. ๋ฐ๋ผ์,
form_for(@user) do |f|
์์ ๊ฐ์ด f.object ๊ฐ @user ๊ฐ ๋๋ ๊ฒฝ์ฐ์,
form_for(@micropost) do |f|
์์ ๊ฐ์ด f.object ๊ฐ @micropost ๊ฐ ๋๋ ๊ฒฝ์ฐ๊ฐ ์์ต๋๋ค.
Partial์ ์ค๋ธ์ ํธ๋ฅผ ๋๊ธฐ๊ธฐ ์ํด์, ๊ฐ์ด Object์ด๋ฉด์ Key๋ฅผ Partial์์์ ๋ณ์๋ช
๊ณผ ๊ฐ์ ํด์๋ฅผ ์ด์ฉํฉ๋๋ค. ์ด๊ฒ์ผ๋ก ์ ๋ ๋ฒ์งธ ์ฝ๋์ 2๋ฒ์งธ์ค์ ์ฝ๋๊ฐ ์์ฑ๋ฉ๋๋ค. ๋ฐ๊ฟ๋งํ์๋ฉด, object: f.object ์ error_message partial ์์์ object ๋ผ๊ณ ํ๋ ๋ณ์๋ช
์ ์์ฑํด๋๋๋ค๋ฉด, ์ด ๋ณ์๋ฅผ ์ฌ์ฉํ์ฌ ์๋ฌ ๋ฉ์ธ์ง๋ฅผ ๊ฐฑ์ ํ๋ฉด ๋๋ ๊ฒ ์
๋๋ค.
<!-- app/views/shared/_error_messages.html.erb -->
<% if object.errors.any? %>
<div id="error_explanation">
<div class="alert alert-danger">
The form contains <%= pluralize(object.errors.count, "error") %>.
</div>
<ul>
<% object.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>์ด ์์ ์์ ํ ์คํธ๋ฅผ ์คํ์ํค๋ฉด ํ ์คํธ๋ ์์ง ์คํจ์ผ ๊ฒ ์ ๋๋ค.
$ rails test
์ด์งธ์ ์คํจํ๊ณ ์๋ ๊ฒ์ผ๊น์? ํํธ๋ error_message Partial์ ๋ค๋ฅธ ์ฌ์ฉ์ฅ์๊ฐ ๋ฌธ์ ์
๋๋ค. ์ด partial์ ๋ค๋ฅธ ์ฝ๋์์๋ ํธ์ถ๋๊ณ ์๊ธฐ ๋๋ฌธ์, ์ ์ ๋ฑ๋ก, ํจ์ค์๋ ์ฌ์ค์ , ๊ทธ๋ฆฌ๊ณ ์ ์ ํธ์ง์ ๊ฐ๊ฐ์ view์์ ์์ ์ ํด์ค ํ์๊ฐ ์์ต๋๋ค. ๊ฐ view์์ ์์ ํ ๊ฒฐ๊ณผ๋ ์๋์ ๊ฐ์ต๋๋ค.
<!-- app/views/users/new.html.erb -->
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(@user) do |f| %>
<%= render 'shared/error_messages', object: f.object %> <!-- update -->
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
<%= f.submit "Create my account", class: "btn btn-primary" %>
<% end %>
</div>
</div><!-- app/views/users/edit.html.erb -->
<% provide(:title, "Edit user") %>
<h1>Update your profile</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(@user) do |f| %>
<%= render 'shared/error_messages', object: f.object %><!-- update -->
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
<%= f.submit "Save changes", class: "btn btn-primary" %>
<% end %>
<div class="gravatar_edit">
<%= gravatar_for @user %>
<a href="http://gravatar.com/emails">change</a>
</div>
</div>
</div><!-- app/views/password_resets/edit.html.erb -->
<% provide(:title, 'Reset password') %>
<h1>Password reset</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(@user, url: password_reset_path(params[:id])) do |f| %>
<%= render 'shared/error_messages', object: f.object %> <!-- update -->
<%= hidden_field_tag :email, @user.email %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
<%= f.submit "Update password", class: "btn btn-primary" %>
<% end %>
</div>
</div>์ด๊ฒ์ผ๋ก ๋ชจ๋ ํ ์คํธ๊ฐ ํต๊ณผํ ๊ฒ ์ ๋๋ค.
$ rails test
๊ฒ๋ค๊ฐ ์ด๋ฒ ์ฑํฐ์์ ์์ฑํ ๋ชจ๋ HTML์ด ์ ์ ํ๊ฒ ํ์๋ ๊ฒ ์ ๋๋ค. ์ต์ข ์ form์ ์๋์ ์ฒซ ๋ฒ์งธ ์คํฌ๋ฆฐ์ท ์ฒ๋ผ, ์์ฑ ์๋ฌ๊ฐ ํ์๋ form์ ์๋ ๋ ๋ฒ์งธ ์คํฌ๋ฆฐ์ท์ฒ๋ผ ๋ ๊ฒ ์ ๋๋ค.
- Homeํ์ด์ง๋ฅผ Refactoring ํ์ฌ
if-else๋ฌธ์ ๋ถ๊ธฐ๋ฅผ ๊ฐ๊ฐ Partial๋ก ์์ฑํด๋ด ์๋ค.
micropost ์์ฑ form์ด ๋์ํ ์ ์๋๋ก ๋์์ต๋๋ค๋ง, ์ง๊ธ ๋จ๊ณ์์๋ ์์ฑํ ๋ด์ฉ์ ๋ฐ๋ก ๋ณผ ์๋ ์์ต๋๋ค. ์์ง Home ํ์ด์ง์ micropost ๋ฅผ ํ์ํ๋ ๋ถ๋ถ์ด ์์ง ๊ตฌํ๋์ง ์์๊ธฐ ๋๋ฌธ์ ๋๋ค.
micropost ์์ฑ form์ด ์ ๋๋ก ๋์ํ๋์ง ํ์ธํ๊ณ ์ถ์ ๊ฒฝ์ฐ, ์ฌ๋ฐ๋ฅธ ํํ๋ก ์์ฑํ ํ, ํ๋กํ ํ์ด์ง๋ก ์ด๋ํ์ฌ post๊ฐ ์ ๋๋ก ํ์๋๋ฉด ๊ทธ๊ฑธ๋ก ๋คํ์ ๋๋ค๋ง, ์ด ๊ฒ์ ์กฐ๊ธ ๊ท์ฐฎ์ ์์ ์ด๊ธฐ๋ ํฉ๋๋ค. ์๋ ๋ชฉ์ ์ฒ๋ผ, ์ ์ ์์ ์์ post๋ฅผ ํฌํจํ, micropost์ feed๊ฐ ์์ผ๋ฉด ๋ถํธํฉ๋๋ค. (์ 14์ฅ์์๋ ํผ๋๋ฅผ ๋ฒ์ฉํํ์ฌ์ฌ follow ํ๊ณ ์๋ ์ ์ ๋ค์ micropost๋ feed์ ํ์ํ ์์ ์ ๋๋ค.)
๋ชจ๋ ์ ์ ๊ฐ feed๋ฅผ ๊ฐ์ง๊ณ ์๊ธฐ์, feed ๋ฉ์๋๋ User ๋ชจ๋ธ์์ ์์ฑํ๋ ๊ฒ์ด ์์ฐ์ค๋ฝ์ต๋๋ค. Feed์ ์ํ์, ์ฐ์ ํ์ฌ ๋ก๊ทธ์ธํ๊ณ ์๋ ์ ์ ์ micropost๋ฅผ ์ ๋ถ ์กฐํํฉ๋๋ค. ๋ํ ๋ค์ ์ฑํฐ์์ ์์ ํ feed๋ฅผ ๊ตฌํํ๊ธฐ ๋๋ฌธ์, ์ด๋ฒ์๋ 11.3.3 ์์ ์๊ฐํ where ๋ฉ์๋์์ ์ด๊ฒ์ ๊ตฌํํด๋ณผ ๊ฒ ์
๋๋ค. Micropost ๋ชจ๋ธ์ ๋ณ๊ฒฝ์ ์ ๋ํ ๊ฒฐ๊ณผ๋ ์๋์ ๊ฐ์ต๋๋ค.
# app/models/user.rb
class User < ApplicationRecord
.
.
.
#
# ์์ ํ ๊ตฌํ์ ๋ค์์ฅ์ "์ ์ ๋ฅผ followํด๋ณด์" ๋ฅผ ์ฐธ์กฐํด์ฃผ์ธ์.
def feed
Micropost.where("user_id = ?", id)
end
private
.
.
.
end๋ค์ ์ฝ๋์์ ์ฌ์ฉ๋๊ณ ์๋ ๋ฌผ์ํ๋, ๋ณด์์ ์ค์ํ ์ญํ ์ ํด์ค๋๋ค.
Microposts.where("user_id = ?", id)
์ ๋ฌผ์ํ๊ฐ ์๋ ๊ฒ์ผ๋ก ์ธํ์ฌ, SQL Query์ ๋์
ํ๊ธฐ ์ ์ id ๊ฐ Escape๋์ด์ง๊ธฐ ๋๋ฌธ์, SQL์ธ์ ์
(SQL Injection) ์ด๋ผ ๋ถ๋ฆฌ๋ ์ฌ๊ฐํ ๋ณด์์ ๋ฌธ์ ๋ฅผ ํํผํ ์ ์์ต๋๋ค. ์ด ๊ฒฝ์ฐ์ id ์์ฑ์ ๋จ์ํ ์ ์ (์ฆ, self.id ๋ ์ ์ ์ id) ์ด ๋๋ฌธ์ ์ํํ์ง ์์ต๋๋ค๋ง, SQL ๋ฌธ์ ๋ณ์๋ฅผ ๋์
ํ๋ ๊ฒฝ์ฐ์๋ ํญ์ Escapeํ๋ ์ต๊ด์ ๋ชธ์ ์ตํ์ฃผ์๊ธธ ๋ฐ๋๋๋ค.
์ธ์ฌํ ๋ ์๋ค์ ์ ์ฝ๋์์ ๋ณธ์ง์ ์ผ๋ก ๋ค์ ์ฝ๋์ ๊ฐ์ ๊ฒ์ ์์์ฑ๋ ธ์ ์๋ ์์ ๊ฒ ๊ฐ์ต๋๋ค.
def feed
microposts
end์ ์ฝ๋๋ฅผ ์ฌ์ฉํ์ง ์๊ณ , ์ผ๋ถ๋ฌ ์ 2๋ฒ์งธ ์ฝ๋๋ฅผ ์ฌ์ฉํ ๊ฒ์, ์ 14์ฅ์์ ํ์ํ๊ฒ๋๋ ์์ ํ Status feed์์ ์์ฉ์ ํด๋ณผ ์ ์๊ธฐ ๋๋ฌธ์ ๋๋ค.
Sample application์ feed๊ธฐ๋ฅ์ ๋์
ํ๊ธฐ ์ํด, ๋ก๊ทธ์ธ ์ ์ ์ feed์ฉ์ผ๋ก ์ธ์คํด์ค ๋ณ์ @feed_items ๋ฅผ ์ถ๊ฐํ๊ณ , Home ํ์ด์ง์์๋ feed์ฉ์ Partial์ ์ถ๊ฐํฉ๋๋ค. Home ํ์ด์ง์ ๋ณ๊ฒฝ์ ๋ํ ๊ฒฐ๊ณผ๋ ์๋ ์ธ ๋ฒ์งธ app/views/static_pages/home.html.erb ์ฝ๋๋ฅผ ํ์ธํด์ฃผ์ธ์. ์ด ๋, ์ ์ ๊ฐ ๋ก๊ทธ์ธํด์๋์ง ์ด๋ค์ง ํ์ธํ ํ if๋ฌธ์ด ๋ณํํ๊ณ ์๋ ์ ์ ์ฃผ๋ชฉํด์ฃผ์ธ์. ์ฆ, ์๋ ์ฝ๋
@micropost = current_user.microposts.build if logged_in?
๊ฐ ๋ค์๊ณผ ๊ฐ์ด ๋ณํฉ๋๋ค.
if logged_in?
@micropost = current_user.microposts.build
@feed_items = current_user.feed.paginate(page: params[:page])
end# app/controllers/static_pages_controller.rb
# home ์ก์
์ feed์ ์ธ์คํด์ค๋ณ์๋ฅผ ์ถ๊ฐํ๋ค.
class StaticPagesController < ApplicationController
def home
if logged_in?
@micropost = current_user.microposts.build
@feed_items = current_user.feed.paginate(page: params[:page])
end
end
def help
end
def about
end
def contact
end
end<!-- app/views/shared/_feed.html.erb -->
<!-- statusfeed์ partial -->
<% if @feed_items.any? %>
<ol class="microposts">
<%= render @feed_items %>
</ol>
<%= will_paginate @feed_items %>
<% end %>Status Feed์ Partial์ Micropost์ partial๊ณผ๋ ๋ค๋ฅธ ์ ์ ์ฃผ๋ชฉํด์ฃผ์ธ์.
<%= render @feed_items %>
์ด ๋, @feed_item ์ ๊ฐ ์์๊ฐ Micropost ํด๋์ค๋ฅผ ๊ฐ์ง๊ณ ์๊ธฐ ๋๋ฌธ์, Rails๋ Micropost์ Partial์ ํธ์ถํ ์ ์์ต๋๋ค. ์ด๋ ๊ฒ Rails๋ ๋์ํ๋ ์ด๋ฆ์ Partial์ ๋๊ฒจ์ง ๋ฆฌ์์ค์ ๋๋ ํ ๋ฆฌ ๋ด๋ถ๋ก๋ถํฐ ์ฐพ์๋ผ ์ ์์ต๋๋ค.
app/views/microposts/_micropost.html.erb
๋จ์ ๊ฒ์, ์ธ์ ๋์ฒ๋ผ feed partial์ ํ์ํ๋ฉด Homeํ์ด์ง์ feed๋ฅผ ์ถ๊ฐํ ์ ์์ต๋๋ค. ์ด ๊ฒฐ๊ณผ๋ Homeํ์ด์ง์ Feed๋ก์จ ํ์๋ฉ๋๋ค.
<!-- app/views/static_pages/home.html.erb -->
<% if logged_in? %>
<div class="row">
<aside class="col-md-4">
<section class="user_info">
<%= render 'shared/user_info' %>
</section>
<section class="micropost_form">
<%= render 'shared/micropost_form' %>
</section>
</aside>
<!-- new -->
<div class="col-md-8">
<h3>Micropost Feed</h3>
<%= render 'shared/feed' %>
</div>
<!-- new -->
</div>
<% else %>
.
.
.
<% end %>ํ ์์ ์์๋ ์๋ก์ด micropost์ ์์ฑ์ ๊ธฐ๋ํ๋ ๋๋ก ๋์ํฉ๋๋ค. ๊ทธ๋ฌ๋ ์กฐ๊ธ ๋ณ๊ฑฐ ์๋ ์๋ ์์ต๋๋ค๋ง, micropost์ ํฌ๊ณ ๊ฐ ์คํจ ํ๋ฉด, Home ํ์ด์ง๋ @feed_items ์ธ์คํด์ค ๋ณ์๋ฅผ ๊ธฐ๋ค๋ฆฌ๊ณ ์๊ธฐ ๋๋ฌธ์ ํ์ฌ ๋จ๊ณ์์๋ Exception์ ์ผ์ผํค๊ฒ ๋๋ฒ๋ฆฌ๊ณ ๋ง ๊ฒ ์
๋๋ค. ์ ์ผ ๊ฐ๋จํ ํด๊ฒฐ๋ฐฉ๋ฒ์ ์๋ ์ฝ๋์ ๊ฐ์ด ๋น ๋ฐฐ์ด์ ๋๊ฒจ๋๋ ๊ฒ ์
๋๋ค. ์์ฝ๊ฒ๋ ์ด ๋ฐฉ๋ฒ์ผ๋ก๋ Page๊ฐ ๋ถํ ๋ feed๋ฅผ ๋ค์ ๋ณด๊ณ ์ถ์ด๋ ์ ๋๋ก ๋์ํ์ง ์์ต๋๋ค. ์์ง์ด์ง ์๋ ์ด์ ๋ฅผ ์์๋ณด๊ณ ์ถ์ผ์ ๋ถ์, ์ค์ ๋ก ๊ตฌํํ์ฌ Pagination์ ๋งํฌ๋ฅผ ํด๋ฆญํด๋ณด์ธ์.
# app/controllers/microposts_controller.rb
class MicropostsController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy]
def create
@micropost = current_user.microposts.build(micropost_params)
if @micropost.save
flash[:success] = "Micropost created!"
redirect_to root_url
else
@feed_items = [] #๋น ๋ฐฐ์ด
render 'static_pages/home'
end
end
def destroy
end
private
def micropost_params
params.require(:micropost).permit(:content)
end
end- ์๋กญ๊ฒ ๊ตฌํํ micropost์ ์์ฑ form์ ์ฌ์ฉํ์ฌ ์ค์ ๋ก micropost๋ฅผ ํฌ๊ณ ํด๋ด
์๋ค. Rails ์๋ฒ์ ๋ก๊ทธ ๋ด๋ถ์ ์๋
INSERT๋ฌธ์ ์ด๋ ํ ๋ด์ฉ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ก ๋ณด๋ด๊ณ ์์ต๋๊น? ํ์ธํด๋ด ์๋ค. - ์ฝ์์ ์คํ์ํค๊ณ ,
user๋ณ์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์ ์ ์ผ ์ฒซ ๋ฒ์งธ ์ ์ ๋ฅผ ๋์ ์์ผ๋ด ์๋ค. ๊ทธ ๋ค์,Micropost.where("user_id = ?", user.id)์user.microposts๊ทธ๋ฆฌ๊ณuser.feed๋ฅผ ๊ฐ๊ฐ ์คํ์์ผ๋ณด๊ณ , ์คํ๊ฒฐ๊ณผ๊ฐ ๋ชจ๋ ๊ฐ์ ๊ฒ์ ํ์ธํด๋ด ์๋ค. Hint :==๋ก ๋น๊ตํ๋ฉด ๊ฒฐ๊ณผ๊ฐ ๊ฐ์์ง ์๋์ง๋ฅผ ๊ฐ๋จํ๊ฒ ํ๋จํ ์ ์์ต๋๋ค.
๋ง์ง๋ง ๊ธฐ๋ฅ์ผ๋ก๋ micropost resource์ post๋ฅผ ์ญ์ ํ๋ ๊ธฐ๋ฅ์ ์ถ๊ฐํด๋ด ๋๋ค. ์ด๊ฒ์ ์ ์ ์ญ์ ์ ๋ง์ฐฌ๊ฐ์ง๋ก (10.4.2) "delete" ๋งํฌ๋ก ์คํํด๋ด ์๋ค. ์ ์ ์ ์ญ์ ๋ ๊ด๋ฆฌ์ ์ ์ ๋ง ๊ฐ๋ฅํ ์ ์๋๋ก ์ ํํ ๊ฒ์ ๋นํด, ์ด๋ฒ์๋ ์์ ์ด ์์ฑํ micropost์ ๋ํด์๋ง ์ญ์ ๋งํฌ๋ฅผ ๋์ํ๋๋ก ํด๋ด ์๋ค.
์ ์ผ ์ฒ์์ผ๋ก๋ micropost์ partial ์ ์ญ์ ๋งํฌ๋ฅผ ์ถ๊ฐํด๋ด ๋๋ค. ์์ฑํ ์ฝ๋๋ ์๋์ ๊ฐ์ต๋๋ค.
<!-- app/views/microposts/_micropost.html.erb -->
<li id="micropost-<%= micropost.id %>">
<%= link_to gravatar_for(micropost.user, size: 50), micropost.user %>
<span class="user"><%= link_to micropost.user.name, micropost.user %></span>
<span class="content"><%= micropost.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
<!-- add -->
<% if current_user?(micropost.user) %>
<%= link_to "delete", micropost, method: :delete,
data: { confirm: "You sure?" } %>
<% end %>
<!-- add -->
</span>
</li>๋ค์์ผ๋ก๋, Microposts ์ปจํธ๋กค๋ฌ์ destrory ์ก์
์ ์ ์ํด๋ด
์๋ค. ์ด๊ฒ ๋ํ ์ ์ ์ ์ญ์ ์ ๋๊ฐ ๋น์ทํฉ๋๋ค. ํฐ ์ฐจ์ด๋ก๋ admin_user ํํฐ์์ @user ๋ณ์๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์๋, ๊ด๊ณ๋ฅผ ์ฌ์ฉํ์ฌ micropost๋ฅผ ๊ฒ์ํ ์ ์๋ค๋ ์ ์
๋๋ค. ์ด๊ฒ์ผ๋ก ์ด๋ค ์ ์ ๊ฐ ๋ค๋ฅธ ์ ์ ์ micropost๋ฅผ ์ญ์ ํ๋ ค๊ณ ํด๋ ์๋์ ์ผ๋ก ์คํจํด๋ฒ๋ฆด ๊ฒ์
๋๋ค. ๊ตฌ์ฒด์ ์ผ๋ก๋ correct_user ํํฐ ๋ด์์ find ๋ฉ์๋๋ฅผ ํธ์ถํ๋ ๊ฒ์ผ๋ก, ํ์ฌ ์ ์ ๊ฐ ์ญ์ ๋์์ micropost๋ฅผ ๋ณด์ ํ๊ณ ์๋์ง๋ฅผ ํ์ธํฉ๋๋ค. ์์ฑ๋ ์ฝ๋๋ ์๋์ ๊ฐ์ต๋๋ค.
# app/controllers/microposts_controller.rb
class MicropostsController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy]
before_action :correct_user, only: :destroy #new
.
.
.
#new
def destroy
@micropost.destroy
flash[:success] = "Micropost deleted"
redirect_to request.referrer || root_url
end
private
def micropost_params
params.require(:micropost).permit(:content)
end
#new
def correct_user
@micropost = current_user.microposts.find_by(id: params[:id])
redirect_to root_url if @micropost.nil?
end
end์ด ๋ ์ ์ฝ๋์ destory ๋ฉ์๋์์ redirect๋ฅผ ์ฌ์ฉํ๊ณ ์๋ ์ ์ ์ฃผ๋ชฉํด์ฃผ์ธ์.
request.referrer || root_url
์ฌ๊ธฐ์๋ request.referrer ๋ผ๊ณ ํ๋ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๊ณ ์์ต๋๋ค. ์ด ๋ฉ์๋๋ Friendly forwarding์ request.url ๋ณ์ (10.2.3) ์ ๋น์ทํ๊ฒ, ๋ฐ๋ก ์ด์ URL์ ๋ฆฌํดํฉ๋๋ค. (์ด ๊ฒฝ์ฐ, Homeํ์ด์ง๊ฐ ๋ ๊ฒ ์
๋๋ค.) ๋๋ฌธ์ micropost๊ฐ Homeํ์ด์ง์์ ์ญ์ ๋ ๊ฒฝ์ฐ์๋ ํ๋กํ ํ์ด์ง๋ก๋ถํฐ ์ญ์ ๋ ๊ฒฝ์ฐ์๋, request.referrer ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ผ๋ก DELETE Request๊ฐ ์คํ๋ ํ์ด์ง๋ก ๋๋์๊ฐ๊ธฐ ๋๋ฌธ์ ๋งค์ฐ ํธ๋ฆฌํฉ๋๋ค. ์ฌ๋ด์ผ๋ก, ์ด์ ์ผ๋ก ๋์๊ฐ๋ URL์ด ์๋ค๊ณ ํ๋๋ผ๋, (์๋ฅผ๋ค์ด ํ
์คํธ์์๋ nil ์ด ๋ฆฌํด๋๋ ๊ฒฝ์ฐ๋ ์์ต๋๋ค.) ์ ์ฝ๋์์ || ์ฐ์ฐ์์ ์ํ์ฌ root_url ์ ๋ํดํธ๋ก ์ค์ ํ๊ณ ์๊ธฐ ๋๋ฌธ์ ๊ด์ฐฎ์ต๋๋ค.
์ ์ฝ๋๋ก ์ธํ์ฌ ์์์๋ถํฐ 2๋ฒ์ฌ micropost ๋ฅผ ์ญ์ ํ๋ฉด ์๋์ ๊ฐ์ด ๋์ํ ๊ฒ ์ ๋๋ค.
- micropost๋ฅผ ์์ฑํ๊ณ , ๊ทธ ๋ค์ ์์ฑํ micropost๋ฅผ ์ญ์ ํด๋ด
์๋ค. ๋ค์์ผ๋ก Rails ์๋ฒ์ ๋ก๊ทธ๋ฅผ ํ์ธํ์ฌ
DELETE๋ฌธ์ ๋ด์ฉ์ ํ์ธํด๋ด ์๋ค. redirect_to request.referrer || root_url์ ํ์redirect_back(fallback_location: root_url)์ผ๋ก ๋ฐ๊พธ์ด๋ ์ ๋๋ก ๋์ํ๋์ง, ๋ธ๋ผ์ฐ์ ๋ฅผ ํตํด ํ์ธํด๋ด ์๋ค. (์ด ๋ฉ์๋๋ Rails 5๋ถํฐ ์๋กญ๊ฒ ์ถ๊ฐ๋์์ต๋๋ค.)
13.3.4 ์ ์ฝ๋์์, MIcropost ๋ชจ๋ธ๊ณผ ํด๋น ์ธํฐํ์ด์ค๋ฅผ ์์ฑ์์ผฐ์ต๋๋ค. ๋จ์ ๋ด์ฉ์ผ๋ก๋ Micropost ์ปจํธ๋กค๋ฌ์ ํ๊ฐ๋ฅผ ์ฒดํฌํ๋ ์งง์ ํ ์คํธ์ ๊ทธ๊ฒ๋ค์ ์ ๋ฆฌํ๋ ํตํฉํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํด๋ณผ ๊ฒ ์ ๋๋ค.
์ฐ์ ์ micropost์ฉ์ fixture์ ์ ๊ฐ๊ฐ์ ์ ์ ์ ์ฐ๊ฒฐ๋์ด์ง micropost๋ฅผ ์ถ๊ฐํฉ๋๋ค. (์ง๊ธ์ ์ด ์ค 1๊ฐ๋ง ์ฌ์ฉํฉ๋๋ค๋ง ๋์ค์๋ ๋ค๋ฅธ micropost๋ ์ฌ์ฉํ๊ฒ ๋ฉ๋๋ค.)
# test/fixtures/microposts.yml
.
.
.
ants:
content: "Oh, is that what you want? Because that's how you get ants!"
created_at: <%= 2.years.ago %>
user: archer
zone:
content: "Danger zone!"
created_at: <%= 3.days.ago %>
user: archer
tone:
content: "I'm sorry. Your words made sense, but your sarcastic tone did not."
created_at: <%= 10.minutes.ago %>
user: lana
van:
content: "Dude, this van's, like, rolling probable cause."
created_at: <%= 4.hours.ago %>
user: lana๋ค์์ผ๋ก ์์ ์ด์ธ์ ์ ์ ์ micropost ๋ ์ญ์ ๋ฅผ ํ๋ คํ๋ฉด ์ ์ ํ๊ฒ redirectํ๋ ์ฒ๋ฆฌ๋ฅผ ํ ์คํธ์์ ํ์ธํด๋ด ์๋ค.
# test/controllers/microposts_controller_test.rb
require 'test_helper'
class MicropostsControllerTest < ActionDispatch::IntegrationTest
def setup
@micropost = microposts(:orange)
end
test "should redirect create when not logged in" do
assert_no_difference 'Micropost.count' do
post microposts_path, params: { micropost: { content: "Lorem ipsum" } }
end
assert_redirected_to login_url
end
test "should redirect destroy when not logged in" do
assert_no_difference 'Micropost.count' do
delete micropost_path(@micropost)
end
assert_redirected_to login_url
end
# new
test "should redirect destroy for wrong micropost" do
log_in_as(users(:michael))
micropost = microposts(:ants)
assert_no_difference 'Micropost.count' do
delete micropost_path(micropost)
end
assert_redirected_to root_url
end
end๋ง์ง๋ง์ผ๋ก ํตํฉ ํ ์คํธ๋ฅผ ์์ฑํด๋ด ๋๋ค. ์ด๋ฒ ํตํฉํ ์คํธ์์๋ ๋ก๊ทธ์ธ, micropost์ ํ์ด์ง ๋ถํ ์ ํ์ธ, ๋ฌดํจํ micropost์ ์์ฑ, ์ ํจํ micropost์ ์์ฑ, micropost ์ญ์ , ๊ทธ๋ฆฌ๊ณ ๋ค๋ฅธ ์ ์ ์ micropost๋ "Delete" ๋งํฌ๊ฐ ์๋ ๊ฒ์ ํ์ธ ํ๋ ์์๋ก ํด๋ด ์๋ค. ์ธ์ ๋ ์ฒ๋ผ ํตํฉ ํ ์คํธ๋ฅผ ์์ฑํด๋ด ์๋ค.
$ rails generate integration_test microposts_interface
invoke test_unit
create test/integration/microposts_interface_test.rb
์์ ์์๋๋ก ์์ฑํ ํตํฉํ ์คํธ๋ ์๋์ ๊ฐ์ต๋๋ค. ์ด์ ์ ์์ฑํ ์ฝ๋๊ฐ ์ด์ง ์์ฌ์๋ ์ ์ ์ฃผ์ํด์ฃผ์ธ์.
# test/integration/microposts_interface_test.rb
require 'test_helper'
class MicropostsInterfaceTest < ActionDispatch::IntegrationTest
def setup
@user = users(:michael)
end
test "micropost interface" do
log_in_as(@user)
get root_path
assert_select 'div.pagination'
# ๋ฌดํจํ ์์ฑ
assert_no_difference 'Micropost.count' do
post microposts_path, params: { micropost: { content: "" } }
end
assert_select 'div#error_explanation'
# ์ ํจํ ์์ฑ
content = "This micropost really ties the room together"
assert_difference 'Micropost.count', 1 do
post microposts_path, params: { micropost: { content: content } }
end
assert_redirected_to root_url
follow_redirect!
assert_match content, response.body
# micropost๋ฅผ ์ญ์
assert_select 'a', text: 'delete'
first_micropost = @user.microposts.paginate(page: 1).first
assert_difference 'Micropost.count', -1 do
delete micropost_path(first_micropost)
end
# ๋ค๋ฅธ ์ ์ ์ ํ๋กํ๋ก์ ์ก์ธ์ค (์ญ์ ๋งํฌ๊ฐ ์๋ ๊ฒ์ ํ์ธ)
get user_path(users(:archer))
assert_select 'a', text: 'delete', count: 0
end
end์ด๋ฏธ ์ดํ๋ฆฌ์ผ์ด์ ์ ์ฝ๋๋ ์์ฑํด๋์๊ธฐ ๋๋ฌธ์, ์ด ํ ์คํธ๋ ํต๊ณผ๋ ๊ฒ ์ ๋๋ค.
$ rails test
-
์ ์ฝ๋์ 4๊ฐ์ ์ฝ๋ฉํธ์ ๊ฐ๊ฐ์ ๋ํด ํ ์คํธ๊ฐ ์ ๋๋ก ๋์ํ๋์ง๋ฅผ ํ์ธํด๋ด ์๋ค. ๊ตฌ์ฒด์ ์ผ๋ก๋ ๋์ํ๋ application ์ ์ฝ๋๋ฅผ ์ฝ๋ฉํธ์์ํ๊ณ ํ ์คํธ๊ฐ ์คํจํ๋ ๊ฒ์ ํ์ธํ๊ณ , ์๋๋๋ก ๋๋๋ฆฐ ํ ํ ์คํธ๊ฐ ๋ค์ ํต๊ณผ๋๋์ง๋ฅผ ํ์ธํด๋ด ์๋ค.
-
์ฌ์ด๋ ๋ฐ์ ์๋ micropost์ ํฉ๊ณ ์์ฑ ๊ฐ์๋ฅผ ํ ์คํธํด๋ด ์๋ค. ์ด ๋ ๋จ์ํ(micropost) ์ ๋ณต์ํ (microposts) ๊ฐ ์ ๋๋ก ํ์๋๋์ง๋ ํ ์คํธํด๋ด ์๋ค. Hint : ์ ํ ์คํธ ์ฝ๋๋ฅผ ํ์ธํด๋ด ์๋ค
# test/integration/microposts_interface_test.rb require 'test_helper' class MicropostInterfaceTest < ActionDispatch::IntegrationTest def setup @user = users(:michael) end . . . # new test "micropost sidebar count" do log_in_as(@user) get root_path assert_match "#{FILL_IN} microposts", response.body # ์์ง micropost๋ฅผ ์์ฑํ์ง ์์ ์ ์ other_user = users(:malory) log_in_as(other_user) get root_path assert_match "0 microposts", response.body other_user.microposts.create!(content: "A micropost") get root_path assert_match FILL_IN, response.body end end
์ฌ๊ธฐ๊น์ง micropost์ ๊ดํ ๊ธฐ๋ณธ์ ์ธ ์กฐ์์ ๋ชจ๋ ๊ตฌํํด๋ณด์์ต๋๋ค. ์ด๋ฒ ์น์ ์์๋ ์์ฉํธ์ผ๋ก์จ, ์ด๋ฏธ์ง๋ฅผ ์ฒจ๋ถํ micropost๋ฅผ ์์ฑํ ์ ์๋๋ก ํด๋ณด๊ฒ ์ต๋๋ค. ์์๋ก๋ ์ผ๋จ ๊ฐ๋ฐํ๊ฒฝ์ฉ์ ๋ฒ ํํ์ ๊ตฌํํ๊ณ , ๊ทธ ํ ๋ช๊ฐ์ง์ ๊ฐ์ ์ ๊ฑฐ์ณ ์ค์ ๋ฐฐํฌํ๊ฒฝ์ฉ์ ์์ฑํ์ ๊ตฌํํด๋ณด๊ฒ ์ต๋๋ค.
์ด๋ฏธ์ง ์ ๋ก๋ ๊ธฐ๋ฅ์ ์ถ๊ฐํ๊ธฐ ์ํด์๋ 2๊ฐ์ ์๊ฐ์ ์ธ ์์๊ฐ ํ์ํฉ๋๋ค. ํ๋๋ ์ด๋ฏธ์ง๋ฅผ ์ ๋ก๋ํ๊ธฐ ์ํ form, ๋ ๋ค๋ฅธ ํ๋๋ ์์ฑํ ์ด๋ฏธ์ง ๊ทธ ์์ฒด์ ๋๋ค. [Upload image] ๋ฒํผ๊ณผ ์ด๋ฏธ์ง๊ฐ ์ฒจ๋ถ๋์ด์๋ micropost์ ๋ชฉ์ ์ ์๋์ ๊ฐ์ต๋๋ค.
์
๋ก๋ํ ์ด๋ฏธ์ง๋ฅผ ๋ค๋ฃจ๊ฑฐ๋ ํด๋น ์ด๋ฏธ์ง๋ฅผ micropost ๋ชจ๋ธ๊ณผ ๊ด๊ณ๋งบ๊ธฐ๋ฅผ ํ๊ธฐ ์ํด์, ์ด๋ฒ์๋ CarrierWave ๋ผ๊ณ ํ๋ Image Uploader๋ฅผ ์ฌ์ฉํด๋ณด๊ฒ ์ต๋๋ค. ์ฐ์ carrierwave gem ์ Gemfile ์ ์ถ๊ฐํด๋ด
์๋ค. ์ด ๋, ์๋ ์ฝ๋์์๋ mini_magick gem ๊ณผ fog gems ๋ ๊ฐ์ด ํฌํจ๋์ด์๋ ์ ์ ์ฃผ๋ชฉํด์ฃผ์ธ์. ์ด gem๋ค์ Resizeํ๊ฑฐ๋(13.4.3) ์ค์ ๋ฐฐํฌํ๊ฒฝ์์ ์ด๋ฏธ์ง๋ฅผ ์
๋ก๋ํ๊ธฐ ์ํด ์ฌ์ฉ๋ ๊ฒ ์
๋๋ค. (13.4.4)
gem 'rails', '5.1.6'
gem 'bcrypt', '3.1.12'
gem 'faker', '1.7.3'
gem 'carrierwave', '1.2.2' #new
gem 'mini_magick', '4.7.0' #new
gem 'will_paginate', '3.1.5'
gem 'bootstrap-will_paginate', '1.0.0'
.
.
.
group :production do
gem 'pg', '0.20.0'
gem 'fog', '1.42' #new
end
.
.
.๋ค์์ผ๋ก๋ ์ธ์ ๋์ฒ๋ผ bundle install ์ ์คํํด๋ด
๋๋ค.
$ bundle install
CarrierWave๋ฅผ ๋์
ํ๋ฉด, Rails์ ์ ๋ค๋ ์ดํฐ๋ก ์ด๋ฏธ์ง ์
๋ก๋๋ฅผ ์์ฑํ ์ ์๊ฒ ๋ฉ๋๋ค. ๋ค์ ์ปค๋งจ๋๋ฅผ ์คํํด๋ด
์๋ค. (์ด๋ฏธ์ง๋ฅผ image๋ผ๊ณ ํ๋ ๊ฒ์ ๋๋ฌด ํ๋ฒํ๊ธฐ ๋๋ฌธ์, picture ๋ผ๊ณ ํ๊ฒ ์ต๋๋ค.)
$ rails generate uploader Picture
CarrierWave๋ก ์ ๋ก๋๋ ์ด๋ฏธ์ง๋, Active Record ๋ชจ๋ธ์ ์์ฑ๊ณผ ๊ด๊ณ๋ฅผ ๋งบ์ด์ผํ ๊ฒ ์ ๋๋ค. ๊ด๊ณ๋งบ์ด์ง ์์ฑ์๋ ์ด๋ฏธ์ง์ ํ์ผ๋ช ์ด ์ ์ฅ๋๊ธฐ ๋๋ฌธ์ Stringํ์ผ๋ก ํด๋์ต๋๋ค. ํ์ฅ๋ micropost์ ๋ฐ์ดํฐ ๋ชจ๋ธ์ ์๋์ ๊ฐ์ต๋๋ค.
ํ์ํ picture ์์ฑ์ Micropost๋ชจ๋ธ์ ์ถ๊ฐํ๊ธฐ ์ํด, migration ํ์ผ์ ์์ฑํ๊ณ , ๊ฐ๋ฐํ๊ฒฝ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฉํฉ๋๋ค.
$ rails generate migration add_picture_to_microposts picture:string
$ rails db:migrate
CarrierWave์ ์ด๋ฏธ์ง์ ๊ด๊ณ์ง์ด๋์ ๋ชจ๋ธ์ ์
๋ ฅํ๊ธฐ ์ํด์ , mount_uploader ๋ผ๊ณ ํ๋ ๋ฉ์๋๋ฅผ ์ฌ์ฉํฉ๋๋ค. ์ด ๋ฉ์๋๋ ํ๋ผ๋ฏธํฐ์ ์์ฑ๋ช
์ ์ฌ๋ณผ๊ณผ ์์ฑ๋์ด์ง ์
๋ก๋์ ํด๋์ค์ด๋ฆ์ ์ทจํฉ๋๋ค.
mount_uploader :picture, PictureUploader
(picture_uploader.rb ๋ผ๊ณ ํ๋ ํ์ผ์ PictureUploader ํด๋์ค๊ฐ ์ ์๋์ด ์์ต๋๋ค. 13.4.2์์ ์์ ํฉ๋๋ค๋ง, ์ง๊ธ์ ๋ํดํธ์ธ ์ํ๋ก๋ ์๊ด์์ต๋๋ค. ) Micropost ๋ชจ๋ธ์ ์
๋ก๋๋ฅผ ์ถ๊ฐํ ๊ฒฐ๊ณผ๋ ์๋์ ๊ฐ์ต๋๋ค.
# app/models/micropost.rb
# micropost๋ชจ๋ธ์ ์ด๋ฏธ์ง๋ฅผ ์ถ๊ฐํ๋ค.
class Micropost < ApplicationRecord
belongs_to :user
default_scope -> { order(created_at: :desc) }
mount_uploader :picture, PictureUploader #new
validates :user_id, presence: true
validates :content, presence: true, length: { maximum: 140 }
end์์คํ ์ ๋ฐ๋ผ์๋ ์ฌ๊ธฐ์ ์ผ๋จ Rails ์๋ฒ๋ฅผ ์ฌ๊ธฐ๋ํ ํ์๊ฐ ์์ต๋๋ค. ์ฌ๊ธฐ๋๋๋ฉด ํ ์คํธ์ฝ๋๋ฅผ ์คํ์์ผ์ฃผ์ธ์. ์ผ๋จ ํต๊ณผํ ๊ฒ ์ ๋๋ค. (๋จ, 3.6.2 ์์ ์ค๋ช ํ Guard๋ฅผ ์ฌ์ฉํ ๊ฒฝ์ฐ๋, ์ฌ๊ธฐ๋ํ๋ ๊ฒ๋ง์ผ๋ก๋ ์ ๋๋ก ๋์ํ์ง ์์ ์๋ ์์ต๋๋ค. ์ผ๋จ ๊ทธ ๊ฒฝ์ฐ๋ผ๋ฉด Terminal์์ ์ผ๋จ ์ข ๋ฃํ๊ณ , ์๋ก์ด Terminal์์ Guard๋ฅผ ์ฌ์คํํด์ฃผ์ธ์.)
Home ํ์ด์ง์ ์
๋ก๋๋ฅผ ์ถ๊ฐํ๊ธฐ ์ํด์๋ micropost์ form์ file_field ํ๊ทธ๋ฅผ ํฌํจํ ํ์๊ฐ ์์ต๋๋ค.
<!-- app/views/shared/_micropost_form.html.erb -->
<%= form_for(@micropost) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "Compose new micropost..." %>
</div>
<%= f.submit "Post", class: "btn btn-primary" %>
<span class="picture">
<%= f.file_field :picture %> <!-- new -->
</span>
<% end %>๋ง์ง๋ง์ผ๋ก๋ Web์ผ๋ก๋ถํฐ ์ถ๊ฐํ ์ ์๋ ํ๊ฐ ๋ฆฌ์คํธ์ picture ์์ฑ์ ์ถ๊ฐํด๋์ต๋๋ค. ์ถ๊ฐํด๋์ผ๋ฉด micropost_params ๋ฉ์๋๋ ์๋์ ๊ฐ์ด ๋ฉ๋๋ค.
# app/controllers/microposts_controller.rb
class MicropostsController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy]
before_action :correct_user, only: :destroy
.
.
.
private
def micropost_params
params.require(:micropost).permit(:content, :picture) # new
end
def correct_user
@micropost = current_user.microposts.find_by(id: params[:id])
redirect_to root_url if @micropost.nil?
end
endํ ๋ฒ ์ด๋ฏธ์ง๋ฅผ ์
๋ก๋ํด๋์ผ๋ฉด, Micropost partial์ image_tag ํฌํผ๋ก ํด๋น ์ด๋ฏธ์ง๋ฅผ ํ์ํ ์ ์๊ฒ ๋ฉ๋๋ค. ๋ํ ์ด๋ฏธ์ง๊ฐ ์๋ (ํ
์คํธ๋ง ์๋) micropost์์๋ ์ด๋ฏธ์ง๋ฅผ ํ์ํ์ง์๊ฒ ํ๊ธฐ ์ํด, picture? ๋ผ๊ณ ํ๋ ๋
ผ๋ฆฌ๊ฐ์ ๋ฆฌํดํ๋ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ ์ ์ ์ฃผ๋ชฉํด์ฃผ์ธ์. ์ด ๋ฉ์๋๋ ์ด๋ฏธ์ง์ฉ์ ์์ฑ๋ช
์ ๋ฐ๋ผ, CarrierWave๊ฐ ์๋์ ์ผ๋ก ์์ฑํด์ฃผ๋ ๋ฉ์๋์
๋๋ค. ์๋์ผ๋ก ์ด๋ฏธ์ง๊ฐ ํฌํจ๋ micropost๋ฅผ ์์ฑํด๋ณด๋ฉด ์๋ ์บก์ณ์ ๊ฐ์ด ๋ฉ๋๋ค. ์
๋ก๋์ ๋ํ ํ
์คํธ๋ ์ฐ์ต๋ฌธ์ ๋ก ๋ด๋ณด๊ฒ ์ต๋๋ค.
<!-- app/views/microposts/_micropost.html.erb -->
<!-- micropost์ ์ด๋ฏธ์งํ์๋ฅผ ์ถ๊ฐํ๋ค. -->
<li id="micropost-<%= micropost.id %>">
<%= link_to gravatar_for(micropost.user, size: 50), micropost.user %>
<span class="user"><%= link_to micropost.user.name, micropost.user %></span>
<span class="content">
<%= micropost.content %>
<%= image_tag micropost.picture.url if micropost.picture? %>
</span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
<% if current_user?(micropost.user) %>
<%= link_to "delete", micropost, method: :delete,
data: { confirm: "You sure?" } %>
<% end %>
</span>
</li>- ์ด๋ฏธ์ง๊ฐ ์๋ micropost๋ฅผ ์์ฑํด๋ด ์๋ค. ํน์๋ ๋๋ฌด๋๋ ํฐ ์ด๋ฏธ์ง๋ฅผ ์ฒจ๋ถํด๋ฒ๋ฆฌ์ง ์์ผ์ จ๋์? (๊ฑฑ์ ํ์ง๋ง์ธ์. ์ด ๋ฌธ์ ๋ ๋ค์ [13.4.3](#1343-image์- resize)์์ ๋์ํด๋ด ๋๋ค.)
- ์๋ ์ฝ๋์ ํ
ํ๋ฆฟ์ ์ฐธ๊ณ ํ์ฌ, 13.4 ์์ ๊ตฌํํ ์ด๋ฏธ์ง ์
๋ก๋๋ฅผ ํ
์คํธํด๋ณด์ธ์. ํ
์คํธ์ ์ค๋น๋ก์, ๋จผ์ sample ์ด๋ฏธ์ง๋ฅผ fixture๋ฅผ ์ถ๊ฐํด๋ณด์ธ์. (์ปค๋งจ๋ :
cp app/assets/images/rails.png test/fixtures/) ์๋ ์ฝ๋์์ ์ถ๊ฐํ ํ ์คํธ์์๋, Home ํ์ด์ง์ ์๋ ํ์ผ ์ ๋ก๋์, ์์ฑ์ ์ฑ๊ณตํ์ ๋ ์ด๋ฏธ์ง๊ฐ ํ์๋๋์ง๋ฅผ ์ฒดํฌํฉ๋๋ค. ๋ํ ํ ์คํธ ๋ด๋ถ์ ์๋fixture_file_upload๋ผ๊ณ ํ๋ ๋ฉ์๋๋, fixture์์ ์ ์๋ ํ์ผ์ ์ ๋ก๋ํ๋ ํน๋ณํ ๋ฉ์๋์ ๋๋ค. Hint :picture์์ฑ์ด ์ ํจํ์ง ์๋์ง๋ฅผ ํ์ธํ๊ธฐ ์ํด์๋, 11.3.3 ์์ ์๊ฐํassigns๋ฉ์๋๋ฅผ ์ฌ์ฉํด์ฃผ์ธ์. ์ด ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉด ์์ฑ ์ฑ๊ณตํ ํ,create์ก์ ๋ด์ micropost์ ์ก์ธ์คํ๊ฒ ๋ฉ๋๋ค.
# test/integration/microposts_interface_test.rb
# ์์์
๋ก๋๋ฅผ ํ
์คํธํ๊ธฐ ์ํ ํ
ํ๋ฆฟ
require 'test_helper'
class MicropostInterfaceTest < ActionDispatch::IntegrationTest
def setup
@user = users(:michael)
end
test "micropost interface" do
log_in_as(@user)
get root_path
assert_select 'div.pagination'
assert_select 'input[type=FILL_IN]'
# ๋ฌดํจํ ์ก์
post microposts_path, params: { micropost: { content: "" } }
assert_select 'div#error_explanation'
# ์ ํจํ ์ก์
content = "This micropost really ties the room together"
picture = fixture_file_upload('test/fixtures/rails.png', 'image/png')
assert_difference 'Micropost.count', 1 do
post microposts_path, params: { micropost:
{ content: content,
picture: FILL_IN } }
end
assert FILL_IN.picture?
follow_redirect!
assert_match content, response.body
# ์์ฑํ ๊ธ์ ์ญ์ ํ๋ค.
assert_select 'a', 'delete'
first_micropost = @user.microposts.paginate(page: 1).first
assert_difference 'Micropost.count', -1 do
delete micropost_path(first_micropost)
end
# ๋ค๋ฅธ ์ ์ ์ ํ๋กํ์ ์ก์ธ์คํด๋ณธ๋ค.
get user_path(users(:archer))
assert_select 'a', { text: 'delete', count: 0 }
end
.
.
.
end13.4.1 ์ ์ ๋ก๋๋ ๋์์ง๋ ์์ต๋๋ค๋ง, ๋ช๊ฐ์ง ๋์ ๋๋ ๊ฒฐ์ ์ด ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, ์ ๋ก๋๋ ์ด๋ฏธ์ง์ ๋ํ ์ ํ์ด ์๊ธฐ ๋๋ฌธ์, ๋ง์ฝ ์ ์ ๊ฐ ์์ฒญ ํฐ ํ์ผ์ ์ ๋ก๋ํ๋ค๊ฑฐ๋ ๋ฌดํจํ ํ์ผ์ ์ ๋ก๋ํ๋ฉด ๋ฌธ์ ๊ฐ ๋ฐ์ํ๊ฒ ๋ฉ๋๋ค. ์ด๋ฐ ๊ฒฐ์ ์ ๋ณด์ํ๊ธฐ ์ํด ์ด๋ฏธ์ง ์ฉ๋์ด๋ ํฌ๋งท์ ๋ํ validation์ ๊ตฌํํ๊ณ ์๋ฒ์ฉ๊ณผ ํด๋ผ์ด์ธํธ (๋ธ๋ผ์ฐ์ ) ์ฉ์ ์์ชฝ์ ์ถ๊ฐํด๋ด ์๋ค.
์ ์ผ ์ฒซ validation์์๋ ์ ํจํ ์ด๋ฏธ์ง์ ์ข ๋ฅ๋ฅผ ์ ํํด๋ด ์๋ค. ์ด๊ฒ์ CarrierWave์ ์ ๋ก๋ ์์ ํํธ๊ฐ ์กด์ฌํฉ๋๋ค. ์์ฑ๋ ์ ๋ก๋์ ์์ ์ฃผ์์ฒ๋ฆฌ๋ ์ฝ๋๊ฐ ์์ต๋๋ค. ์ด ์ฃผ์์ฒ๋ฆฌ๋ ์ฝ๋๋ฅผ ์๋๋๋ก ๋๋๋ฆฌ๋ ๊ฒ์ผ๋ก, ์ด๋ฏธ์ง์ ํ์ผ๋ช ์ผ๋ก๋ถํฐ ์ ํจํ ํ์ฅ์ (PNG/GIF/JPEG ๋ฑ)์ ๊ฒ์ฆํ ์ ์๊ฒ ๋ฉ๋๋ค.
# app/uploaders/picture_uploader.rb
# Image format validation
class PictureUploader < CarrierWave::Uploader::Base
storage :file
# Uploadํ์ผ์ ์ ์ฅํด๋๋ ๋ฎ์ด์ฐ๊ธฐ ๊ฐ๋ฅ
# ์๋๋ ๊ธฐ๋ณธ ์ ์ฅ ํด๋
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
# ์
๋ก๋๊ฐ ๊ฐ๋ฅํ ํ์ฅ์์ ๋ฆฌ์คํธ
def extension_whitelist
%w(jpg jpeg gif png)
end
end2๋ฒ์งธ valiadtion์์๋ ์ด๋ฏธ์ง์ ์ฉ๋์ ์ ํํฉ๋๋ค. ์ด๊ฒ์ Micropost ๋ชจ๋ธ์ ์ถ๊ฐํด๋ณด๊ฒ ์ต๋๋ค. ๋ฐฉ๊ธ ์ validation๊ณผ๋ ๋ค๋ฅด๊ฒ, ํ์ผ ์ฉ๋์ ๋ํ validation์ Rails์ ๊ธฐ์กด ์ต์
(presence๋ length ๋ฑ) ์๋ ์์ต๋๋ค. ๋ฐ๋ผ์ ์ด๋ฒ์๋ ์๋์ผ๋ก picture_size ๋ผ๊ณ ํ๋ ๋
์์ ์ธ validation์ ์ ์ํด๋ด
์๋ค. ๊ฒฐ๊ณผ๋ ์๋์ ๊ฐ์ต๋๋ค. ๋
์์ ์ธ validation์ ์ฌ์ฉํ๊ธฐ ์ํด ์ง๊ธ๊น์ง ์ฌ์ฉํ๊ณ ์๋ validates ๋ฉ์๋๊ฐ ์๋ validate ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๊ณ ์๋ ์ ์ ์ฃผ๋ชฉํด์ฃผ์ธ์.
# app/models/micropost.rb
# Add imageSize Validation
class Micropost < ApplicationRecord
belongs_to :user
default_scope -> { order(created_at: :desc) }
mount_uploader :picture, PictureUploader
validates :user_id, presence: true
validates :content, presence: true, length: { maximum: 140 }
validate :picture_size #new
private
# ์
๋ก๋๋ ์ด๋ฏธ์ง ์ฉ๋์ validationํ๋ค.
def picture_size
if picture.size > 5.megabytes
errors.add(:picture, "should be less than 5MB")
end
end์ด validate ๋ฉ์๋๋ ํ๋ผ๋ฏธํฐ๋ก ์ฌ๋ณผ (:picture_size) ๋ฅผ ์ ๋ฌ๋ฐ๋๋ค. ์ด ์ฌ๋ณผ๋ช
์ ๋์ํ๋ ๋ฉ์๋๋ฅผ ํธ์ถํฉ๋๋ค. ๋ํ ํธ์ถ๋ picture_size ๋ฉ์๋์์๋ 5MB๋ฅผ ์ํ์ผ๋ก ํ์ฌ (๋ฌธ๋ฒ์ ์ปฌ๋ผ 9.1 ์ ์ฐธ์กฐ) ํด๋น ์ฉ๋์ ๋์ ๊ฒฝ์ฐ์๋ ์ปค์คํฐ๋ง์ด์ฆํ ์๋ฌ๋ฉ์ธ์ง๋ฅผ errors ์ปฌ๋ ์
์ ์ถ๊ฐํฉ๋๋ค. (errors ์ ๋ํด์๋ 6.2.2 ์์ ์ค๋ช
ํ์ต๋๋ค.)
์ ๋ ๊ฐ์ ์ฝ๋์์ ์ ์ํ ์ด๋ฏธ์ง์ validation์ view์์ ๊ตฌํํ๊ธฐ ์ํด, ํด๋ผ์ด์ธํธ ์ธก์ 2๊ฐ์ ์ฒ๋ฆฌ๋ฅผ ์ถ๊ฐํด๋ด
์๋ค. ์ผ๋จ์ format์ validation์ ๋ฐ์ํ๊ธฐ ์ํด์๋ file_field ํ๊ทธ์ accept ํ๋ผ๋ฏธํฐ๋ฅผ ๋ถ์ฌํ์ฌ ์ฌ์ฉํฉ๋๋ค.
<%= f.file_field :picture, accept: 'image/jpeg,image/gif,image/png' %>
์ด ๋ acceptํ๋ผ๋ฏธํฐ์์๋ ์ ์ฒซ ๋ฒ์งธ ์ฝ๋์์ ํ๊ฐํ ํ์ผ ํ์์, MIME ํ์ ์ผ๋ก ์ง์ ํ ์ ์์ต๋๋ค.
๋ค์์ผ๋ก๋ ๋งค์ฐ ํฐ ํ์ผ ์ฌ์ด์ฆ์ ๋ํด ๊ฒฝ๊ณ ๋ฅผ ์ถ๋ ฅํ๊ธฐ ์ํด, ๊ฐ๋จํ JavaScript (์ ํํ๋ jQuery) ๋ฅผ ์ถ๊ฐํด๋ด ๋๋ค. ์ด๋ ๊ฒํ๋ ๊ฒ์ผ๋ก ๋๋ฌด ๊ธด ์ ๋ก๋ ์๊ฐ์ ๋ง์ ์ ์์ผ๋ฉฐ ์๋ฒ์ ๋ถ๋ด์ ์ค์ผ ์ ์์ต๋๋ค.
$('#micropost_picture').bind('change', function() {
var size_in_megabytes = this.files[0].size/1024/1024;
if (size_in_megabytes > 5) {
alert('Maximum file size is 5MB. Please choose a smaller file.');
}
});jQuery๋ ๋ณธ ํํ ๋ฆฌ์ผ์ ์ฃผ์ ๊ฐ ์๋๊ธฐ ๋๋ฌธ์ ์์ธํ ์ค๋ช
์ ํ์ง ์์ต๋๋ค๋ง, ์ ์ฝ๋์์๋ (ํด์๋งํฌ # ์ผ๋ก๋ถํฐ ์ ์ ์๋ฏ) CSS id micropost_picture ๋ฅผ ํฌํจํ ์์๋ฅผ ์ฐพ์๋ด์ด, ํด๋น ์์๋ฅผ ๊ฐ์ํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ด id๋ฅผ ๊ฐ์ง ์์๋ micropost form์ ์ง์นญํฉ๋๋ค. (๋ํ ๋ธ๋ผ์ฐ์ ์์์๋ ํ๋ฉด์ ์ค๋ฅธ์ชฝ ํด๋ฆญํ์ฌ inspector๋ก ์์๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.) ์ฆ, ์ด CSS id๋ฅผ ๊ฐ์ง ์์๊ฐ ๋ณํํ์์๋, ์ด jQuery ํจ์๊ฐ ๋์ํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋ง์ฝ ํ์ผ ์ฌ์ด์ฆ๊ฐ ๋๋ฌด ํฐ ๊ฒฝ์ฐ์๋ alert ๋ฉ์๋๋ก ๊ฒฝ๊ณ ๋ฅผ ๋ด๋ณด๋
๋๋ค.
์ด๋ฌํ ์ถ๊ฐ์ ์ธ ์ฒดํฌ๊ธฐ๋ฅ์ ์ ๋ฆฌํ์๋ฉด ์๋์ ๊ฐ์ด ๋ฉ๋๋ค.
<!-- app/views/shared/_micropost_form.html.erb -->
<%= form_for(@micropost) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "Compose new micropost..." %>
</div>
<%= f.submit "Post", class: "btn btn-primary" %>
<span class="picture">
<%= f.file_field :picture, accept: 'image/jpeg,image/gif,image/png' %>
</span>
<% end %>
<script type="text/javascript">
$('#micropost_picture').bind('change', function() {
var size_in_megabytes = this.files[0].size/1024/1024;
if (size_in_megabytes > 5) {
alert('Maximum file size is 5MB. Please choose a smaller file.');
}
});
</script>์ด๋ฏธ ๋์น์ฑ ๋
์๋ถ๋ค๋ ์์ ์๋ ์๊ฒ ์ต๋๋ค๋ง, ์์ ๊ฐ์ ์ฝ๋์์๋ ํฐ ํ์ผ์ ์
๋ก๋๋ฅผ ์์ ํ๊ฒ ๋ง์ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, ์ ์ ๋ alert๋ฅผ ๋ฌด์ํ๊ณ ์
๋ก๋๋ฅผ ๊ฐํํ ์๋ ์์ ๊ฒ์
๋๋ค. ๋ง์ฝ ๋ณธ ํํ ๋ฆฌ์ผ์ด jQuery ํํ ๋ฆฌ์ผ์ด์๋ค๋ฉด ์ด ๋ฌธ์ ์ ๊ณผ ํด๊ฒฐ์ฑ
์ ์๋ฅํ๊ฒ ์ค๋ช
ํ์ ๊ฒ์
๋๋ค๋ง, ๋ณธ ํํ ๋ฆฌ์ผ์ Rails ํํ ๋ฆฌ์ผ์ด๊ธฐ ๋๋ฌธ์ ์ด๋ฒ์๋ "์์ ๊ฐ์ ์ฝ๋์ผ๋ก๋ ์์ง ๊ตฌํ์ด ๋ถ์์ ํ๋ค" ๋ผ๋ ์ ์ ์์๋๋๋ค๋ฉด ์ถฉ๋ถํฉ๋๋ค. ๋ํ ๋ง์ฝ ์ก์ form์ ์ฌ์ฉํ์ฌ ์์ฑ์ ์ ํํ ์ ์๋คํ์ฌ๋, ๋ธ๋ผ์ฐ์ ์ inspector ๊ธฐ๋ฅ์ผ๋ก JavaScript๋ฅผ ๋ฏ์ด๊ณ ์น๋ค๊ฑฐ๋ curl ์ ์ฌ์ฉํ์ฌ ์ง์ POST ๋ฆฌํ์คํธ๋ฅผ ์ก์ ํ๋ ๊ฒฝ์ฐ์๋ ๋์ํ ์ ์์ต๋๋ค. ์ด๋ฌํ ๊ฒฝ์ฐ์๋ ๋์ํ ์ ์๋๋ก ํ๊ธฐ ์ํด, ์๋ฒ์ธก validation์ ์ค์ํ ๊ฒ ์
๋๋ค.
- 5MB์ด์์ ์ด๋ฏธ์ง ํ์ผ์ ๋ฑ๋กํ๊ณ ์ ํ๋ ๊ฒฝ์ฐ์๋ ์ด๋ป๊ฒ ๋ฉ๋๊น?
- ๋ฌดํจํ ํ์ฅ์์ ํ์ผ์ ๋ฑ๋กํ๊ณ ์ํ๋ ๊ฒฝ์ฐ์๋ ์ด๋ป๊ฒ ๋ฉ๋๊น?
ํ์ผ์ฌ์ด์ฆ์ ๋ํ Validation (13.4.2) ์ ์ ๋๋ก ๊ตฌํ๋์์ต๋๋ค. ๊ทธ๋ฌ๋ ์ด๋ฏธ์ง ์ฌ์ด์ฆ (๊ฐ๋ก์ธ๋ก์ ๊ธธ์ด)์ ๋ํ ์ ํ์ ์๋๊ธฐ ๋๋ฌธ์, ํฐ ์ด๋ฏธ์ง ์ฌ์ด์ฆ๊ฐ ์ ๋ก๋ ๋์ด์ง๋ฉด ์๋ ์บก์ณ์๊ฐ์ด ๋ ์ด์์์ด ๊นจ์ ธ๋ฒ๋ฆฌ๊ฒ ๋ฉ๋๋ค. ๊ทธ๋ ๋ค๊ณ ๋ ํด๋, ์ ์ ์๊ฒ ์ ์ ๊ฐ ๊ฐ์ง ์ด๋ฏธ์ง์ ์ฌ์ด์ฆ๋ฅผ ์์ ํ๋๋ก ํ๋ ๊ฒ์ ๋ถํธํฉ๋๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์ด๋ฏธ์ง๋ฅผ ํ์ํ๊ธฐ ์ ์ ์ฌ์ด์ฆ๋ฅผ ๋ณ๊ฒฝํ๋ (Resize) ์ฒ๋ฆฌ๋ฅผ ๊ตฌํํด๋ด ์๋ค.
์ด๋ฏธ์ง๋ฅผ ๋ฆฌ์ฌ์ด์ฆํ๊ธฐ ์ํด์๋ ์ด๋ฏธ์ง๋ฅผ ์กฐ์ํ๋ ํ๋ก๊ทธ๋จ์ด ํ์ํฉ๋๋ค. ์ด๋ฒ์๋ ImageMagick ๋ผ๊ณ ํ๋ ํ๋ก๊ทธ๋จ์ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์, ์ด๊ฒ์ ๊ฐ๋ฐํ๊ฒฝ์ ์ค์นํด๋ณด๊ฒ ์ต๋๋ค. (13.4.4์์๋ ์ค๋ช ํฉ๋๋ค๋ง, ์ค์ ๋ฐฐํฌํ๊ฒฝ์ด Heroku๋ผ๋ฉด, ์ด๋ฏธ ๋ฐฐํฌํ๊ฒฝ์ ImageMagick์ ์ธ ์ ์๋๋ก ์ค๋น๋์ด์ ธ ์์ต๋๋ค.) Cloud IDE์ ๊ฒฝ์ฐ, ๋ค์ ์ปค๋งจ๋๋ก ์ด ํ๋ก๊ทธ๋จ์ ์ค์นํ ์ ์์ต๋๋ค.
sudo yum install -y ImageMagick
(ํน์ฌ๋ ๋ก์ปฌํ๊ฒฝ์์ ๊ฐ๋ฐํ๊ณ ์๋ ๊ฒฝ์ฐ์๋, ๊ฐ๊ฐ์ ํ๊ฒฝ์ ๋ง์ถ์ด ImageMagick๋ฅผ ์ค์นํ๋ ์์๊ฐ ๋ค๋ฆ
๋๋ค. ์๋ฅผ ๋ค์ด Mac์ด๋ผ๋ฉด, Homebrew๋ฅผ ๋์
ํ์ฌ brew install imagemagick ์ปค๋งจ๋๋ฅผ ์ฌ์ฉํ์ฌ ์ค์นํ ์ ์์ต๋๋ค. ๋ง์ฝ ์ค์น๊ฐ ์ ๋๋ก ์๋๋ค๋ฉด, ์ปฌ๋ผ1.1๋ฅผ ์ฐธ๊ณ ํด์ฃผ์ธ์.)
๋ค์์ผ๋ก, MiniMagick ์ด๋ผ๊ณ ํ๋, ImageMagick๊ณผ Ruby๋ฅผ ์ด์ด์ฃผ๋ gem์ ์ฌ์ฉํ์ฌ ์ด๋ฏธ์ง๋ฅผ ๋ฆฌ์ฌ์ด์ฆํด๋ด
์๋ค. MiniMagick์ Document ๋ฅผ ํ์ธํ๋ฉด, ์ฌ๋ฌ๊ฐ์ง ๋ฐฉ๋ฒ์ผ๋ก ๋ฆฌ์ฌ์ด์ฆํ ์ ์๋ ๊ฒ์ ์ ์ ์์ต๋๋ค๋ง, ์ด๋ฒ์๋ resize_to_limit: [400, 400] ๋ผ๊ณ ํ๋ ๋ฐฉ๋ฒ์ ์ฌ์ฉํด๋ด
๋๋ค. ์ด ๊ฒ์ ๊ฐ๋ก์ธ๋ก ์ด๋์ชฝ์ด๋ 400px๋ฅผ ์ด๊ณผํ ๊ฒฝ์ฐ, ์ ์ ํ ์ฌ์ด์ฆ๋ก ์ถ์ํ๋ ์ต์
์
๋๋ค. (๋จ, ์์ ์ด๋ฏธ์ง๋ผ๊ณ ํด๋ ํ๋ํ์ง ์์ต๋๋ค.) ์ฌ๋ด์ผ๋ก, CarrierWave์ MiniMagick ํญ๋ชฉ ์ ํ์ธํด๋ณด๋ฉด, ๋๋ฌด๋๋ ์์ ์ด๋ฏธ์ง๋ฅผ ์ต์ง๋ก ๋๋ฆฌ๋ ๋ฐฉ๋ฒ๋ ์๋ ๊ฒ ๊ฐ์ต๋๋ค๋ง, ์ด๋ฒ์๋ ์ฌ์ฉํ์ง ์๊ฒ ์ต๋๋ค. ๋ฐ๋ผ์ ์ต์ข
์ ์ธ ์ฝ๋๋ ์๋์ ๊ฐ์ด ๋ฉ๋๋ค. ์ด๊ฒ์ผ๋ก ์ปค๋ค๋ ์ด๋ฏธ์ง ์ฌ์ด์ฆ๋ผ๋ ์ ์ ํ๊ฒ ๋ฆฌ์ฌ์ด์ฆ๋ ๊ฒ ์
๋๋ค.
# app/uploaders/picture_uploader.rb
class PictureUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick #new
process resize_to_limit: [400, 400] #new
storage :file
# ์
๋ก๋ํ์ผ์ ์ ์ฅํด๋๋ ๋ฎ์ด์ฐ๊ธฐ ๊ฐ๋ฅ
# ์๋๋ ๊ธฐ๋ณธ ์ ์ฅ ํด๋
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
# ์
๋ก๋๊ฐ๋ฅํ ํ์ฅ์ ๋ฆฌ์คํธ
def extension_whitelist
%w(jpg jpeg gif png)
end
end- ํด์๋๊ฐ ๋์ ์ด๋ฏธ์ง๋ฅผ ์ ๋ก๋ํ๊ณ , ๋ฆฌ์ฌ์ด์ฆ ๋์ด์ง๋์ง๋ฅผ ํ์ธํด๋ด ์๋ค. ์ด๋ฏธ์ง๊ฐ ์ง์ฌ๊ฐํ ์ผ ๊ฒฝ์ฐ, ๋ฆฌ์ฌ์ด์ฆ๋ ์ ๋๋ก ์ ๋ฉ๋๊น?
- ์ด๋ฏธ ์ด์ ์ ์ด๋ฏธ์ง ์ฌ์ด์ฆ์ ๋ํด ํ ์คํธ๋ฅผ ์ถ๊ฐํ ๊ฒฝ์ฐ, ์ด ์์ ์์ ํ ์คํธ๋ฅผ ์คํํ๋ฉด ์๋ฌ๋ฉ์ธ์ง๊ฐ ํ์๋ ๊ฒ ์ ๋๋ค. ์ด ์๋ฌ๋ฅผ ์์ ๋ด ์๋ค. Hint : ์๋ ์ค์ ํ์ผ์ ์์ ํ์ฌ ํ ์คํธ ์์๋ CarrierWave์ ์ด๋ฏธ์ง์ ๋ฆฌ์ฌ์ด์ฆ๋ฅผ ์คํ์ํค์ง ์๋๋ก ํด๋ด ์๋ค.
# config/initializers/skip_image_resizing.rb
if Rails.env.test?
CarrierWave.configure do |config|
config.enable_processing = false
end
end(์ด ์น์ ์ Skip ํ์ฌ๋ ๋ฉ๋๋ค. ์ ๋๋ก ๋์ง์๋๋ค๋ฉด Skipํด๋ ๋ฌธ์ ์์ต๋๋ค.)
13.4.3 ์์ ๊ตฌํํ ์ด๋ฏธ์ง ์
๋ก๋๋ ๊ฐ๋ฐํ๊ฒฝ์์ ๋์์ํค๊ธฐ์๋ ๋ฌธ์ ์์ต๋๋ค๋ง, ์ค์ ๋ฐฐํฌํ๊ฒฝ์๋ ์ ์ ํ์ง ์์ต๋๋ค. ์ด ๊ฒ์ picture_uploader.rb์ storage :file ์ด๋ผ๊ณ ํ๋ ์ฝ๋์ ์ํด์, ๋ก์ปฌ ํ์ผ์์คํ
์ ์ด๋ฏธ์ง๋ฅผ ์ ์ฅํ๋๋ก ๋์ด์๊ธฐ ๋๋ฌธ์
๋๋ค. ์ค์ ๋ฐฐํฌํ๊ฒฝ์์๋ ํ์ผ ์์คํ
์ด ์๋ Cloud Storage ์๋น์ค์ ์ด๋ฏธ์ง๋ฅผ ์ ์ฅํ๋๋ก ํด๋ด
์๋ค.
์ค์ ๋ฐฐํฌํ๊ฒฝ์์ cloud storage์ ์ ์ฅํ๊ฒ ํ๊ธฐ ์ํด์๋, ์๋ ์ฝ๋์ฒ๋ผ fog gem ์ ์ฌ์ฉํ๋ฉด ๊ฐ๋จํ๊ฒ ํ ์ ์์ต๋๋ค.
# app/uploaders/picture_uploader.rb
class PictureUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
process resize_to_limit: [400, 400]
# new
if Rails.env.production?
storage :fog
else
storage :file
end
# ์
๋ก๋ ํ์ผ์ ์ ์ฅ ๋๋ ํ ๋ฆฌ๋ ๋ฎ์ด์ฐ๊ธฐ ๊ฐ๋ฅ
# ์๋๋ ๊ธฐ๋ณธ ์ ์ฅ ๋๋ ํ ๋ฆฌ
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
# ์
๋ก๋ ๊ฐ๋ฅํ ํ์ฅ์์ ๋ฆฌ์คํธ
def extension_whitelist
%w(jpg jpeg gif png)
end
end์ ์ฝ๋์์ production? ์ด๋ผ๊ณ ํ๋ ๋
ผ๋ฆฌ๊ฐ์ ๋ฆฌํดํ๋ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๊ณ ์์ต๋๋ค. ์ด ๋ฉ์๋๋ ์ปฌ๋ผ 7.1์์๋ ์๊ฐํ์ต๋๋ค๋ง, ์ด๊ฒ์ ์ฌ์ฉํ๋ฉด ํ๊ฒฝ๋ณ๋ก ์ ์ฅ ๋๋ ํ ๋ฆฌ๋ฅผ ๋ฐ๊ฟ ์ ์์ต๋๋ค.
if Rails.env.production?
storage :fog
else
storage :file
end์์ฆ ์๋์๋ ๋ง์ cloud storage ์๋น์ค๊ฐ ์์ต๋๋ค๋ง, ์ด๋ฒ์๋ ์ ๋ช ํ๊ณ ์ ๋ขฐ์ฑ์ด ๋์ Amazon์ Simple Storage Service (S3) ๋ฅผ ์ฌ์ฉํด๋ณด๊ฒ ์ต๋๋ค. ์ค์น ์์๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- Amazon Web Services ์ ๊ณ์ ์ ์์ฑํ๋ค.
- WS Identity and Access Management (IAM) ์์ ์ ์ ๋ฅผ ์์ฑํ๊ณ , Access key ์ Secret key๋ฅผ ์์ฑํ๊ณ ๋ฉ๋ชจํด๋๋๋ค.
- AWS Console ์์ S3 bucket์ ์์ฑํ๊ณ , ( bucket ์ด๋ฆ์ ์๋ฌด๊ฑฐ๋ ์๊ด ์์ต๋๋ค. ) 2์์ ์์ฑํ ์ ์ ์๊ฒ Read๊ถํ๊ณผ Write ๊ถํ์ ๋ถ์ฌํ๋ค.
(S3์ ์ค์น๋ ๊ฝค๋ ์ด๋ ต์ต๋๋ค. ์ปฌ๋ผ1.1๋ฅผ ์ฐธ๊ณ ํ์ฌ ์์๋ด ์๋ค.)
์ข ๋ ์์ธํ๊ฒ ์๊ณ ์ถ์ ๊ฒฝ์ฐ์๋ S3์ Document ๋ฅผ ์ฝ์ด๋ณด๊ฑฐ๋, Google์ด๋ Stack Overflow์์ ๊ฒ์ํด๋ด ์๋ค.
S3 Account์ ์์ฑ๊ณผ ์ค์ ์ด ๋๋ฌ๋ค๋ฉด, CarrierWave์ ์ค์ ํ์ผ์ ๋ค์ ์๋ ์ฝ๋์ ๊ฐ์ด ์์ ํด๋ด ์๋ค.
# config/initializers/carrier_wave.rb
if Rails.env.production?
CarrierWave.configure do |config|
config.fog_credentials = {
# Amazon S3์ ์ค์
:provider => 'AWS',
:region => ENV['S3_REGION'], # ไพ: 'ap-northeast-1'
:aws_access_key_id => ENV['S3_ACCESS_KEY'],
:aws_secret_access_key => ENV['S3_SECRET_KEY']
}
config.fog_directory = ENV['S3_BUCKET']
end
end์ค์ ๋ฐฐํฌํ๊ฒฝ์ ๋ฉ์ผ์ค์ ๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก, ์ ์ฝ๋์์๋ Heroku์ ํ๊ฒฝ๋ณ์ ENV ๋ฅผ ์ฌ์ฉํ์ฌ ๊ธฐ๋ฐ์ ๋ณด๊ฐ ๋์ค๋์ง ์๋๋ก ํ๊ณ ์์ต๋๋ค. 11.4๋ 12.4์์๋ SendGrid์ AddOn์ด ์ด๋ฌํ ํ๊ฒฝ๋ณ์๋ฅผ ์๋์ ์ผ๋ก ์ค์ ํด์ฃผ์์ผ๋, ์ด๋ฒ์๋ ์๋์ผ๋ก ์ค์ ํ ํ์๊ฐ ์์ต๋๋ค. heroku config:set ์ปค๋งจ๋๋ฅผ ์ฌ์ฉํ์ฌ ๋ค์๊ณผ ๊ฐ์ด Heroku ์์ ํ๊ฒฝ๋ณ์๋ฅผ ์ค์ ํด์ฃผ์ธ์.
$ heroku config:set S3_ACCESS_KEY="ใณใณใซๅ
ใปใฉใกใขใใAccessใญใผใๅ
ฅๅ"
$ heroku config:set S3_SECRET_KEY="ๅๆงใซใSecretใญใผใๅ
ฅๅ"
$ heroku config:set S3_BUCKET="Bucketใฎๅๅใๅ
ฅๅ"
$ heroku config:set S3_REGION="Regionใฎๅๅใๅ
ฅๅ"
์ค์ ์ด ๋ฌด์ฌํ ๋๋ฌ๋ค๋ฉด, ์ง๊ธ๊น์ง์ ๋ณ๊ฒฝ์ commitํ๊ณ Deployํ ์ค๋น๊ฐ ๋์์ต๋๋ค. ๋จ ๊ทธ์ ์, .gitignore ํ์ผ์ ์๋์ ๊ฐ์ด ๋ด์ฉ์ ์ถ๊ฐํด๋ด
์๋ค. ์ด๊ฒ์ผ๋ก ์ด๋ฏธ์ง๋ฅผ ์ ์ฅํ๋ ๋๋ ํ ๋ฆฌ๊ฐ Git์ ๋์์ผ๋ก๋ถํฐ ์ ์ธ๋๊ธฐ ๋๋ฌธ์, application๊ณผ ๊ด๊ณ์๋ ์ด๋ฏธ์ง ํ์ผ ๋ฑ์ ๋ฌด์ํ ์์๋๋ก ๋ฉ๋๋ค.
// .gitignore
# ์
๋ก๋๋ ํ
์คํธ ์ด๋ฏธ์ง๋ฅผ ๋ฌด์ํ๋ค.
/public/uploads
๊ทธ๋ ๋ค๋ฉด ์ง๊ธ๊น์ง์ ๋ณ๊ฒฝ์ Topic branch์ commit ํ๊ณ , master branch์ mergeํด๋ด ์๋ค.
$ rails test
$ git add -A
$ git commit -m "Add user microposts"
$ git checkout master
$ git merge user-microposts
$ git push
๋ค์์ผ๋ก, Heroku์ Deploy, Database์ Reset, sample data์ ์์ฑ์ ์ฐจ๋ก๋๋ก ์คํํด๋ด ์๋ค.
$ git push heroku
$ heroku pg:reset DATABASE
$ heroku run rails db:migrate
$ heroku run rails db:seed
Heroku์๋ ์ด๋ฏธ ImageMagick ์ด ์ค์น๋์ด์๊ธฐ ๋๋ฌธ์, (์ค์ ์ด ์ ๋๋ก ๋์ด์๋ค๋ฉด,) ์ด๋ฏธ์ง ๋ฆฌ์ฌ์ด์ฆ๋ ์ค์ ๋ฐฐํฌํ๊ฒฝ์์์ ์ด๋ฏธ์ง ์ ๋ก๋๋ ์ฑ๊ณตํ ๊ฒ ์ ๋๋ค.
- ์ค์ ๋ฐฐํฌํ๊ฒฝ์์ ํด์๋๊ฐ ๋์ ์์์ ์ ๋ก๋ํ๊ณ , ์ ์ ํ๊ฒ ๋ฆฌ์ฌ์ด์ฆ๋๋์ง๋ฅผ ํ์ธํด๋ด ์๋ค. ์ง์ฌ๊ฐํ์ ์ด๋ฏธ์ง์ฌ๋ ์ ์ ํ๊ฒ ๋ฆฌ์ฌ์ด์ฆ๋ฉ๋๊น?
Micropost ๋ฆฌ์์ค์ ์ถ๊ฐ๋ก ์ธํ์ฌ, Sample Application์ ๊ฑฐ์ ์์ฑ๋์์ต๋๋ค. ๋จ์ ๋ด์ฉ์ผ๋ก๋ ์ ์ ๋ฅผ ์๋ก ํ๋ก์ฐํ ์ ์๋ Social๊ธฐ๋ฅ์ ์ถ๊ฐ์ ๋๋ค. ์ 14์ฅ์์๋ ๊ทธ๋ฌํ ์ ์ ๊ฐ์ ๊ด๊ณ (Relationship) ์ ๋ชจ๋ธ๋งํ๋ ๋ฐฉ๋ฒ์ ๋ํด ๋ฐฐ์ฐ๊ณ , Micropost์ ํผ๋์ ์ด๋ป๊ฒ ๊ด๊ณ๋๋์ง ์์๋ด ๋๋ค.
๋ง์ฝ 13.4.4 ๋ฅผ ์คํตํ์๋ค๋ฉด, ์ฌ๊ธฐ์ ์ง๊ธ๊น์ง์ ๋ณ๊ฒฝ์ฌํญ์ ์ปค๋ฐํ๊ณ mergeํด๋ด ์๋ค.
$ rails test
$ git add -A
$ git commit -m "Add user microposts"
$ git checkout master
$ git merge user-microposts
$ git push
์ค๋น๋์๋ค๋ฉด ์ค์ ๋ฐฐํฌํ๊ฒฝ์ผ๋ก ๋ฐฐํฌํด๋ด ์๋ค.
$ git push heroku
$ heroku pg:reset DATABASE
$ heroku run rails db:migrate
$ heroku run rails db:seed
๋ํ, ํ์ํ gem์ ์ค์น๋ ์ด๊ฒ์ผ๋ก ๋๋ฌ์ต๋๋ค. ์ดํ ์๋ก์ด gem์ ์ถ๊ฐํ ์ผ์ ์์ต๋๋ค. ์ ์ผ ๋ง์ง๋ง์ํ์ Gemfile ์ ์ฐธ๊ณ ํด์ฃผ์ธ์.
source 'https://rubygems.org'
gem 'rails', '5.1.6'
gem 'bcrypt', '3.1.12'
gem 'faker', '1.7.3'
gem 'carrierwave', '1.2.2'
gem 'mini_magick', '4.7.0'
gem 'will_paginate', '3.1.6'
gem 'bootstrap-will_paginate', '1.0.0'
gem 'bootstrap-sass', '3.3.7'
gem 'puma', '3.9.1'
gem 'sass-rails', '5.0.6'
gem 'uglifier', '3.2.0'
gem 'coffee-rails', '4.2.2'
gem 'jquery-rails', '4.3.1'
gem 'turbolinks', '5.0.1'
gem 'jbuilder', '2.7.0'
group :development, :test do
gem 'sqlite3', '1.3.13'
gem 'byebug', '9.0.6', platform: :mri
end
group :development do
gem 'web-console', '3.5.1'
gem 'listen', '3.1.5'
gem 'spring', '2.0.2'
gem 'spring-watcher-listen', '2.0.1'
end
group :test do
gem 'rails-controller-testing', '1.0.2'
gem 'minitest', '5.10.3'
gem 'minitest-reporters', '1.1.14'
gem 'guard', '2.14.1'
gem 'guard-minitest', '2.4.6'
end
group :production do
gem 'pg', '0.20.0'
gem 'fog', '1.42'
end
# Windowsํ๊ฒฝ์์๋ ์๋ gem์ ํฌํจ์ํฌ ํ์๊ฐ ์์ต๋๋ค.
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]- Active Record ๋ชจ๋ธ๋๋ถ์, Micropost๋ ์ ์ ์ ๋ง์ฐฌ๊ฐ์ง๋ก Resource๋ก์จ ๋ค๋ฃฐ ์ ์๋ค.
- Rails๋ ์ฌ๋ฌ๊ฐ์ Key Index๋ฅผ ์ง์ํ๋ค.
- User๋ ์ฌ๋ฌ๊ฐ์ Micropost๋ฅผ ๊ฐ์ง๊ณ ์์ผ๋ฉฐ (
has_many) Micropost๋ 1๋ช ์ User์๊ฒ ์์กดํ๋ (belongs_to) ๊ด๊ณ๋ฅผ ๋ชจ๋ธํํ๋ค. has_many๋belongs_to๋ฅผ ์ด์ฉํ๋ ๊ฒ์ผ๋ก, ๊ด๊ณ๋งบ๊ธฐ๋ฅผ ํตํ ๋ง์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ ์ ์๋ค.user.microposts.build(...)๋ผ๊ณ ํ๋ ์ฝ๋๋, ํ๋ผ๋ฏธํฐ๋ก ์ฃผ์ด์ง ์ ์ ์๊ฒ ๊ด๊ณ๋ micropost๋ฅผ ๋ฆฌํดํ๋ค.default_scope๋ฅผ ์ฌ์ฉํ๋ฉด ๊ธฐ๋ณธ์ ์์๋ฅผ ๋ณ๊ฒฝํ ์ ์๋ค.default_scope๋ ํ๋ผ๋ฏธํฐ์ ๋ฌด๋ช ํจ์(->) ๋ฅผ ์ ๋ ฅํ ์ ์๋ค.dependent: :destroy์ต์ ์ ์ฌ์ฉํ๋ฉด, ๊ด๊ณ๋ ์ค๋ธ์ ํธ์ ์๊ธฐ์์ ์ ๋์์ ์ญ์ ํ ์ ์๋ค.- paginate ๋ฉ์๋๋ count ๋ฉ์๋๋, ์ด๋์ชฝ์ด๋ ๊ด๊ณ๋งบ๊ธฐ ํ์๋ฅผ ํตํด ์คํ๋๊ณ , ํจ์จ์ ์ธ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ก์ ์กฐํ๋ฅผ ์คํํ๋.
- fixture๋ ๊ด๊ณ๋งบ๊ธฐ๋ฅผ ์ฌ์ฉํ Object์ ์์ฑ๋ ์ง์ํ๋ค.
- Partial์ ํธ์ถํ ๋, ๊ฐ์ด ํ๋ผ๋ฏธํฐ๋ฅผ ๋๊ธธ ์ ์๋ค.
where๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉด, Active Record๋ฅผ ํตํ์ฌ ์ ํ (๋ถ๋ถ์งํฉ์ ์ทจํ๋ ๊ฒ)ํ ์ ์๋ค.- ์์กดํ๊ณ ์๋ Object๋ฅผ ์์ฑ/์ญ์ ํ ๋๋, ํญ์ ๊ด๊ณ๋ฅผ ํตํ์ฌ ์คํํ๋ ๊ฒ์ผ๋ก, ๋ณด๋ค ๋ ์์ ํ ์กฐ์์ ๊ตฌํํ ์ ์๋ค.
- CarrierWave๋ฅผ ์ฌ์ฉํ๋ฉด ์์ ์ ๋ก๋๋ ์์ ๋ฆฌ์ฌ์ด์ฆ๋ฅผ ํ ์ ์๋ค.






















