소개
Ruby on Rails는 개발자에게 응용 프로그램 개발에 대한 의견이 있는 접근 방식을 제공하는 Ruby로 작성된 웹 애플리케이션 프레임워크입니다. Rails를 사용하면 개발자들은 다음을 얻게 됩니다:
- 라우팅, 상태 데이터 및 자산 관리와 같은 것들을 다루는 규칙.
- A firm grounding in the model-view-controller (MCV) architectural pattern, which separates an application’s logic, located in models, from the presentation and routing of application information.
Rails 애플리케이션에 복잡성을 추가할수록, 여러 모델을 사용하게 될 것입니다. 이 모델들은 응용 프로그램의 비즈니스 로직을 나타내며 데이터베이스와 인터페이스합니다. 관련된 모델을 추가하는 것은 그들 간의 의미 있는 관계를 설정하는 것을 의미하며, 이는 그 후 응용 프로그램의 컨트롤러를 통해 정보가 어떻게 전달되고, 사용자에게 어떻게 제시되는지에 영향을 미칩니다.
이 튜토리얼에서는 상어에 관한 사실을 제공하는 기존의 Rails 애플리케이션을 기반으로 작업할 것입니다. 이 응용 프로그램에는 이미 상어 데이터를 처리하는 모델이 있지만, 개별 상어에 대한 포스트를 위한 중첩된 리소스를 추가할 것입니다. 이를 통해 사용자들은 개별 상어에 대한 더 넓은 생각과 의견을 개발할 수 있습니다.
전제 조건
이 튜토리얼을 따르려면 다음이 필요합니다:
- A local machine or development server running Ubuntu 18.04. Your development machine should have a non-root user with administrative privileges and a firewall configured with
ufw
. For instructions on how to set this up, see our Initial Server Setup with Ubuntu 18.04 tutorial. - 로컬 머신 또는 개발 서버에 Node.js와 npm이 설치되어 있어야 합니다. 이 튜토리얼에서는 Node.js 버전 10.16.3과 npm 버전 6.9.0을 사용합니다. Ubuntu 18.04에 Node.js 및 npm을 설치하는 지침은 “Ubuntu 18.04에 Node.js 설치하기“의 “PPA를 사용하여 설치” 섹션의 지침을 따르십시오.
- 로컬 머신 또는 개발 서버에 Ruby, rbenv, 그리고 Rails가 설치되어 있어야 합니다. “Ubuntu 18.04에 rbenv로 Ruby on Rails 설치하기“의 단계 1-4를 따르십시오. 이 튜토리얼에서는 Ruby 2.5.1, rbenv 1.1.2, 그리고 Rails 5.2.3을 사용합니다.
- SQLite가 설치되어 있어야 하며, 기본 상어 정보 애플리케이션이 생성되어 있어야 합니다. “Ruby on Rails 애플리케이션 만들기“의 지시에 따릅니다.
단계 1 — 중첩 모델 스캐폴딩
우리의 애플리케이션은 Active Record 연결을 활용하여 Shark
와 Post
모델 간의 관계를 구축할 것입니다: 게시물은 특정 상어에 속하며, 각 상어는 여러 게시물을 가질 수 있습니다. 따라서 우리의 Shark
와 Post
모델은 belongs_to
와 has_many
관계를 통해 관련될 것입니다.
이 방식으로 애플리케이션을 구축하기 위한 첫 번째 단계는 Post
모델과 관련된 리소스를 생성하는 것입니다. 이를 위해 우리는 rails generate scaffold
명령을 사용할 수 있습니다. 이 명령은 우리에게 모델, 데이터베이스 스키마를 변경하는 데이터베이스 마이그레이션, 컨트롤러, 표준 생성, 읽기, 수정 및 삭제 (CRUD) 작업을 관리하는 전체 뷰 세트 및 부분, 도우미 및 테스트 템플릿을 제공합니다. 이러한 리소스를 수정해야 하지만, scaffold
명령을 사용하면 시작점으로 사용할 수 있는 구조를 생성해 줍니다.
먼저, 사전 요구 사항에서 생성한 Rails 프로젝트의 sharkapp
디렉토리에 있는지 확인하십시오:
다음 명령을 사용하여 Post
리소스를 생성하십시오:
rails generate scaffold Post body:text shark:references
body:text
를 사용하여 Rails에게 posts
데이터베이스 테이블에 body
필드를 포함하도록 지시합니다. 이 테이블은 Post
모델에 매핑되는 테이블입니다. 또한 :references
키워드를 포함하여 Shark
와 Post
모델 간의 연관성을 설정합니다. 구체적으로, 이는 sharks
데이터베이스의 각 상어 항목을 나타내는 외래 키가 posts
데이터베이스에 추가되도록 보장합니다.
명령을 실행한 후에는 Rails가 애플리케이션을 위해 생성한 리소스를 확인할 수 있습니다. 계속하기 전에 데이터베이스 마이그레이션 파일을 확인하여 이제 모델과 데이터베이스 테이블 간에 존재하는 관계를 살펴볼 수 있습니다. 다음 명령을 사용하여 파일 내용을 확인하고 여기에 표시된 타임스탬프를 자신의 마이그레이션 파일에 대체하십시오:
다음 출력이 표시됩니다:
Outputclass CreatePosts < ActiveRecord::Migration[5.2]
def change
create_table :posts do |t|
t.text :body
t.references :shark, foreign_key: true
t.timestamps
end
end
end
테이블에 상어 외래 키 열이 포함되어 있는 것을 볼 수 있습니다. 이 키는 model_name_id
형식을 취할 것입니다. 여기서는 shark_id
입니다.
Rails는 모델 간의 관계를 다른 곳에서도 설정했습니다. 다음 명령을 사용하여 새로 생성된 Post
모델을 살펴보십시오:
cat app/models/post.rb
Outputclass Post < ApplicationRecord
belongs_to :shark
end
belongs_to
연관은 선언 모델의 단일 인스턴스가 지정된 모델의 단일 인스턴스에 속하는 관계를 설정합니다. 우리 애플리케이션의 경우, 이는 단일 게시물이 단일 상어에 속한다는 것을 의미합니다.
이 관계를 설정하는 것 외에도 rails generate scaffold
명령은 우리가 루비 온 레일 애플리케이션 구축 방법의 3단계에서 상어 리소스에 대해 한 것처럼 게시물에 대한 라우트와 뷰를 생성했습니다.
이것은 유용한 시작이지만 우리 모델과 라우트 간의 관계가 원하는 대로 작동하려면 몇 가지 추가적인 라우팅을 구성하고 Shark
모델의 Active Record 연관성을 확립해야 할 것입니다.
단계 2 — 부모 모델에 대한 중첩된 라우트 및 연관성 지정
레일즈는 이미 rails generate scaffold
명령의 :references
키워드 덕분에 우리의 Post
모델에 belongs_to
연관성을 설정했지만, 이 관계가 올바르게 작동하려면 우리의 Shark
모델에서도 has_many
연관성을 지정해야 합니다. 또한 레일즈가 우리에게 제공한 기본 라우팅을 변경하여 게시물 리소스를 상어 리소스의 자식으로 만들어야 합니다.
has_many
관계를 Shark
모델에 추가하려면 nano
또는 선호하는 편집기를 사용하여 app/models/shark.rb
파일을 엽니다:
다음 줄을 파일에 추가하여 상어와 게시물 간의 관계를 설정합니다:
class Shark < ApplicationRecord
has_many :posts
validates :name, presence: true, uniqueness: true
validates :facts, presence: true
end
여기서 고려해야 할 중요한 점은 특정 상어가 삭제된 후 게시물이 어떻게 처리되는지입니다. 우리는 아마도 삭제된 상어와 관련된 게시물이 데이터베이스에 계속 유지되기를 원하지 않을 것입니다. 주어진 상어와 관련된 게시물이 삭제된 경우 해당 상어가 삭제될 때 해당 상어와 관련된 게시물이 삭제되도록 하려면 관련성과 함께 dependent
옵션을 포함할 수 있습니다.
destroy
액션에서 특정 상어를 삭제하면 관련된 게시물이 삭제되도록하는 코드를 파일에 추가합니다:
class Shark < ApplicationRecord
has_many :posts , dependent: :destroy
validates :name, presence: true, uniqueness: true
validates :facts, presence: true
end
이러한 변경을 완료하면 파일을 저장하고 닫습니다. nano
을 사용하는 경우 CTRL+X
를 누른 다음 Y
를 누르고 ENTER
를 누르면 됩니다.
다음으로, 리소스 라우트 간의 관계를 수정하려면 config/routes.rb
파일을 엽니다:
현재 파일은 다음과 같습니다:
Rails.application.routes.draw do
resources :posts
resources :sharks
root 'sharks#index'
# 이 파일에서 사용 가능한 DSL에 대한 자세한 내용은 http://guides.rubyonrails.org/routing.html을 참조하십시오
end
현재 코드는 라우트 간에 독립적인 관계를 설정하는데, 우리가 표현하고자 하는 것은 상어와 관련된 게시물 간의 종속적인 관계입니다. 종속적인 관계
라우트 선언을 업데이트하여 :sharks
를 :posts
의 상위로 만듭니다. 파일의 코드를 다음과 같이 업데이트하십시오:
Rails.application.routes.draw do
resources :sharks do
resources :posts
end
root 'sharks#index'
# 이 파일에서 사용 가능한 DSL에 대한 자세한 내용은 http://guides.rubyonrails.org/routing.html을(를) 참조하십시오.
end
편집을 완료한 후 파일을 저장하고 닫으십시오.
이러한 변경 사항이 적용되면 posts
컨트롤러를 업데이트할 차례입니다.
단계 3 — 포스트 컨트롤러 업데이트
모델 간의 연관은 특정 상어와 연결된 새로운 포스트 인스턴스를 생성하는 데 사용할 수 있는 메서드를 제공합니다. 이러한 메서드를 사용하려면 포스트 컨트롤러에 추가해야 합니다.
포스트 컨트롤러 파일을 열어보십시오:
현재 파일은 다음과 같습니다:
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
# GET /posts
# GET /posts.json
def index
@posts = Post.all
end
# GET /posts/1
# GET /posts/1.json
def show
end
# GET /posts/new
def new
@post = Post.new
end
# GET /posts/1/edit
def edit
end
# POST /posts
# POST /posts.json
def create
@post = Post.new(post_params)
respond_to do |format|
if @post.save
format.html { redirect_to @post, notice: 'Post was successfully created.' }
format.json { render :show, status: :created, location: @post }
else
format.html { render :new }
format.json { render json: @post.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /posts/1
# PATCH/PUT /posts/1.json
def update
respond_to do |format|
if @post.update(post_params)
format.html { redirect_to @post, notice: 'Post was successfully updated.' }
format.json { render :show, status: :ok, location: @post }
else
format.html { render :edit }
format.json { render json: @post.errors, status: :unprocessable_entity }
end
end
end
# DELETE /posts/1
# DELETE /posts/1.json
def destroy
@post.destroy
respond_to do |format|
format.html { redirect_to posts_url, notice: 'Post was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_post
@post = Post.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def post_params
params.require(:post).permit(:body, :shark_id)
end
end
우리의 상어 컨트롤러와 마찬가지로, 이 컨트롤러의 메서드는 관련된 Post
클래스의 인스턴스와 함께 작동합니다. 예를 들어, new
메서드는 Post
클래스의 새 인스턴스를 생성하고, index
메서드는 클래스의 모든 인스턴스를 가져오며, set_post
메서드는 find
및 params
를 사용하여 특정 게시물을 id
로 선택합니다. 그러나 특정 상어 인스턴스와 연관된 게시물 인스턴스를 사용하려면 현재 Post
클래스가 독립적인 엔티티로 작동하고 있으므로 이 코드를 수정해야 합니다.
우리의 수정은 두 가지를 활용할 것입니다:
- 우리가 모델에
belongs_to
와has_many
연관을 추가했을 때 우리에게 가능해진 방법들. 구체적으로, 우리는 이제Shark
모델에서 정의한has_many
연관 덕분에build
메소드에 접근할 수 있습니다. 이 메소드를 통해 우리는posts
데이터베이스에 있는shark_id
외래 키를 사용하여 특정 상어 객체와 관련된 포스트 객체 컬렉션을 생성할 수 있습니다. - 우리가 중첩된
posts
경로를 생성했을 때 가능해진 라우트와 라우팅 헬퍼들. 리소스간 중첩된 관계를 생성할 때 가능한 예제 라우트의 전체 목록은 Rails 문서를 참조하십시오. 현재로서는 각 특정 상어에 대해 — 예를 들어sharks/1
— 해당 상어와 관련된 포스트를 위한 연관된 라우트가 있을 것입니다:sharks/1/posts
. 이러한 중첩된 경로를 참조하는 라우팅 헬퍼들인shark_posts_path(@shark)
와edit_sharks_posts_path(@shark)
도 있을 것입니다.
파일에서, 우리는 컨트롤러의 각 액션 전에 실행될 get_shark
메소드를 작성할 것입니다. 이 메소드는 shark_id
로 상어 인스턴스를 찾아 로컬 @shark
인스턴스 변수를 생성할 것입니다. 이 변수를 파일에서 사용할 수 있으면 다른 메소드에서 특정 상어에 대한 포스트와 관련시키는 것이 가능해질 것입니다.
파일 아래에 다음 메소드를 추가하십시오:
. . .
private
def get_shark
@shark = Shark.find(params[:shark_id])
end
# 콜백을 사용하여 작업 또는 제약 사항을 액션 간에 공유합니다.
. . .
다음으로, 기존 필터 앞에 파일의 상단에 해당 필터를 추가하십시오:
class PostsController < ApplicationController
before_action :get_shark
이렇게하면 파일에 정의된 각 액션 앞에서 get_shark
가 실행됩니다.
다음으로, 이 @shark
인스턴스를 사용하여 index
메소드를 다시 작성할 수 있습니다. Post
클래스의 모든 인스턴스를 가져 오는 대신이 메소드를 사용하여 특정 상어 인스턴스와 연관된 모든 게시물 인스턴스를 반환하려고합니다.
다음과 같이 index
메소드를 수정하십시오:
. . .
def index
@posts = @shark.posts
end
. . .
new
메소드도 특정 상어와 연관된 새 게시물 인스턴스를 원하므로 이와 유사한 수정이 필요합니다. 이를 위해 build
메소드를 사용하고 로컬 @shark
인스턴스 변수를 사용할 수 있습니다.
new
메소드를 다음과 같이 변경하십시오:
. . .
def new
@post = @shark.posts.build
end
. . .
이 메소드는 get_shark
메소드에서 지정된 특정 상어 인스턴스와 연관된 게시물 객체를 생성합니다.
다음으로, new
에 가장 밀접하게 관련된 메소드인 create
를 다룰 것입니다. create
메소드는 두 가지 작업을 수행합니다. 사용자가 new
양식에 입력 한 매개 변수를 사용하여 새 게시물 인스턴스를 빌드하고 오류가 없는 경우 해당 인스턴스를 저장하고 루트 도우미를 사용하여 사용자가 새 게시물을 볼 수있는 곳으로 리디렉션합니다. 오류가있는 경우 new
템플릿을 다시 렌더링합니다.
create
메소드를 다음과 같이 업데이트하세요:
def create
@post = @shark.posts.build(post_params)
respond_to do |format|
if @post.save
format.html { redirect_to shark_posts_path(@shark), notice: 'Post was successfully created.' }
format.json { render :show, status: :created, location: @post }
else
format.html { render :new }
format.json { render json: @post.errors, status: :unprocessable_entity }
end
end
end
다음으로 update
메소드를 살펴보겠습니다. 이 메소드는 메소드 자체에서 명시적으로 설정되지 않은 @post
인스턴스 변수를 사용합니다. 이 변수는 어디서 온 것입니까?
파일 상단의 필터를 살펴보세요. 두 번째로 자동 생성된 before_action
필터가 답을 제공합니다:
class PostsController < ApplicationController
before_action :get_shark
before_action :set_post, only: [:show, :edit, :update, :destroy]
. . .
update
메소드(show
, edit
, destroy
와 같이)는 set_post
메소드에서 @post
변수를 가져옵니다. 이 메소드는 다음과 같이 다른 private
메소드와 함께 나열된 get_shark
메소드 아래에 있습니다:
. . .
private
. . .
def set_post
@post = Post.find(params[:id])
end
. . .
파일의 다른 곳에서 사용한 메소드와 일치하도록 이 메소드를 수정해야 합니다. 이 메소드는 우리 모델 간의 연관 관계와 그 연관 관계에 따라 제공되는 메소드(build
와 같은) 덕분에, 각 포스트 인스턴스가 특정 상어와 연관된 객체 컬렉션의 일부임을 고려할 때 논리적입니다. 따라서 특정 포스트를 쿼리할 때는 특정 상어와 관련된 포스트 컬렉션을 쿼리해야 합니다.
set_post
를 다음과 같이 업데이트하세요:
. . .
private
. . .
def set_post
@post = @shark.posts.find(params[:id])
end
. . .
전체 Post
클래스의 특정 인스턴스를 id
로 찾는 대신, 특정 상어와 관련된 포스트 컬렉션에서 일치하는 id
를 찾습니다.
그 방법을 업데이트하면 update
및 destroy
메소드를 살펴볼 수 있습니다.
update
메소드는 set_post
에서 @post
인스턴스 변수를 사용하고 사용자가 edit
폼에 입력한 post_params
와 함께 사용합니다. 성공한 경우, 우리는 Rails가 사용자를 특정 상어와 연관된 게시물의 index
뷰로 다시 보내도록 하고 싶습니다. 오류가 발생한 경우, Rails는 edit
템플릿을 다시 렌더링합니다.
이 경우, 우리가 필요로 하는 유일한 변경 사항은 성공적인 업데이트를 처리하기 위해 redirect_to
문에 있습니다. shark_post_path(@shark)
로 리디렉션하도록 업데이트하십시오. 이는 선택한 상어의 게시물의 index
뷰로 리디렉션합니다:
. . .
def update
respond_to do |format|
if @post.update(post_params)
format.html { redirect_to shark_post_path(@shark), notice: 'Post was successfully updated.' }
format.json { render :show, status: :ok, location: @post }
else
format.html { render :edit }
format.json { render json: @post.errors, status: :unprocessable_entity }
end
end
end
. . .
다음으로, destroy
메소드에 유사한 변경을 적용할 것입니다. 성공한 경우 redirect_to
메소드를 shark_posts_path(@shark)
로 요청을 리디렉션하도록 업데이트하십시오:
. . .
def destroy
@post.destroy
respond_to do |format|
format.html { redirect_to shark_posts_path(@shark), notice: 'Post was successfully destroyed.' }
format.json { head :no_content }
end
end
. . .
이것이 우리가 할 마지막 변경입니다. 이제 다음과 같은 게시물 컨트롤러 파일이 있습니다:
class PostsController < ApplicationController
before_action :get_shark
before_action :set_post, only: [:show, :edit, :update, :destroy]
# GET /posts
# GET /posts.json
def index
@posts = @shark.posts
end
# GET /posts/1
# GET /posts/1.json
def show
end
# GET /posts/new
def new
@post = @shark.posts.build
end
# GET /posts/1/edit
def edit
end
# POST /posts
# POST /posts.json
def create
@post = @shark.posts.build(post_params)
respond_to do |format|
if @post.save
format.html { redirect_to shark_posts_path(@shark), notice: 'Post was successfully created.' }
format.json { render :show, status: :created, location: @post }
else
format.html { render :new }
format.json { render json: @post.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /posts/1
# PATCH/PUT /posts/1.json
def update
respond_to do |format|
if @post.update(post_params)
format.html { redirect_to shark_post_path(@shark), notice: 'Post was successfully updated.' }
format.json { render :show, status: :ok, location: @post }
else
format.html { render :edit }
format.json { render json: @post.errors, status: :unprocessable_entity }
end
end
end
# DELETE /posts/1
# DELETE /posts/1.json
def destroy
@post.destroy
respond_to do |format|
format.html { redirect_to shark_posts_path(@shark), notice: 'Post was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def get_shark
@shark = Shark.find(params[:shark_id])
end
# Use callbacks to share common setup or constraints between actions.
def set_post
@post = @shark.posts.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def post_params
params.require(:post).permit(:body, :shark_id)
end
end
컨트롤러는 뷰 템플릿에서 데이터베이스로 및 그 반대로 정보가 전달되는 방식을 관리합니다. 우리의 컨트롤러는 이제 게시물이 특정 상어와 연관되어 있는 Shark
와 Post
모델 간의 관계를 반영합니다. 이제 사용자가 특정 상어에 대한 게시물 정보를 입력하고 수정하는 뷰 템플릿을 수정할 수 있습니다.
단계 4 — 뷰 수정
우리의 뷰 템플릿 수정은 게시물과 관련된 템플릿을 변경하고, 특정 상어와 관련된 게시물을 사용자가 볼 수 있도록 상어 show
뷰를 수정하는 것을 포함합니다.
먼저 게시물에 대한 기본 템플릿인 form
부분을 시작합시다. 이 폼을 열어주세요:
대신에 post
모델만 form_with
폼 헬퍼에 전달하는 대신 shark
와 post
모델을 모두 전달하되, post
를 자식 리소스로 설정합니다.
파일의 첫 줄을 다음과 같이 변경하여 우리의 상어와 포스트 리소스 간의 관계를 반영합니다:
<%= form_with(model: [@shark, post], local: true) do |form| %>
. . .
다음으로, 뷰에서 핵심 정보가 아닌 shark_id
를 나열하는 부분을 삭제합니다.
첫 줄을 편집한 결과와 shark_id
섹션을 삭제한 완성된 폼은 다음과 같이 보입니다:
<%= form_with(model: [@shark, post], local: true) do |form| %>
<% if post.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(post.errors.count, "error") %> prohibited this post from being saved:</h2>
<ul>
<% post.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= form.label :body %>
<%= form.text_area :body %>
</div>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
편집을 마쳤으면 파일을 저장하고 닫으십시오.
다음으로 특정 상어와 관련된 포스트를 보여줄 index
뷰를 엽니다:
rails generate scaffold
명령을 통해 Rails가 템플릿의 대부분을 생성했으므로, 각 포스트의 body
필드와 해당하는 shark
를 보여주는 테이블이 완성되었습니다.
그러나 이미 수정한 다른 코드와 마찬가지로 이 템플릿은 포스트를 독립적인 엔티티로 처리합니다. 우리는 모델 간의 관계와 이러한 관계가 제공하는 컬렉션과 헬퍼 메서드를 활용하려고 합니다.
테이블 본문에서 다음을 업데이트합니다:
먼저, post.shark
를 post.shark.name
으로 업데이트하여 테이블이 상어 객체 자체의 식별 정보 대신에 연관된 상어의 이름 필드를 포함하도록 합니다:
. . .
<tbody>
<% @posts.each do |post| %>
<tr>
<td><%= post.body %></td>
<td><%= post.shark.name %></td>
. . .
다음으로, 사용자가 원래 상어로 돌아갈 수 있는 방법을 원할 것이므로 Show
리디렉션을 연결된 상어에 대한 show
뷰로 변경합니다. 우리는 여기서 컨트롤러에서 설정한 @shark
인스턴스 변수를 활용할 수 있습니다. Rails는 컨트롤러에서 생성된 인스턴스 변수를 모든 뷰에서 사용할 수 있게 합니다. 또한 링크 텍스트를 Show
에서 Show Shark
로 변경하여 사용자가 그 기능을 더 잘 이해할 수 있도록 합니다.
이 줄을 다음과 같이 업데이트합니다:
. . .
<tbody>
<% @posts.each do |post| %>
<tr>
<td><%= post.body %></td>
<td><%= post.shark.name %></td>
<td><%= link_to 'Show Shark', [@shark] %></td>
다음 줄에서는 사용자가 게시물을 편집할 때 올바른 중첩된 경로로 이동되도록합니다. 이것은 사용자가 posts/post_id/edit
로 이동하는 대신 sharks/shark_id/posts/post_id/edit
로 이동되어야 함을 의미합니다. 이를 위해 shark_post_path
라우팅 헬퍼와 모델을 사용하고, Rails는 이를 URL로 처리합니다. 또한 링크 텍스트를 업데이트하여 기능을 더 명확하게 만듭니다.
Edit
줄을 다음과 같이 업데이트합니다:
. . .
<tbody>
<% @posts.each do |post| %>
<tr>
<td><%= post.body %></td>
<td><%= post.shark.name %></td>
<td><%= link_to 'Show Shark', [@shark] %></td>
<td><%= link_to 'Edit Post', edit_shark_post_path(@shark, post) %></td>
다음으로, Destroy
링크에 유사한 변경을 추가하여 문자열의 기능을 업데이트하고 shark
및 post
리소스를 추가합니다:
. . .
<tbody>
<% @posts.each do |post| %>
<tr>
<td><%= post.body %></td>
<td><%= post.shark.name %></td>
<td><%= link_to 'Show Shark', [@shark] %></td>
<td><%= link_to 'Edit Post', edit_shark_post_path(@shark, post) %></td>
<td><%= link_to 'Destroy Post', [@shark, post], method: :delete, data: { confirm: 'Are you sure?' } %></td>
마지막으로, 폼 하단에 있는 New Post
경로를 업데이트하여 사용자가 새로운 게시물을 만들려고 할 때 적절한 중첩된 경로로 이동하도록합니다. 파일의 마지막 줄을 new_shark_post_path(@shark)
라우팅 헬퍼를 사용하도록 업데이트합니다:
. . .
<%= link_to 'New Post', new_shark_post_path(@shark) %>
완성된 파일은 다음과 같습니다:
<p id="notice"><%= notice %></p>
<h1>Posts</h1>
<table>
<thead>
<tr>
<th>Body</th>
<th>Shark</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% @posts.each do |post| %>
<tr>
<td><%= post.body %></td>
<td><%= post.shark.name %></td>
<td><%= link_to 'Show Shark', [@shark] %></td>
<td><%= link_to 'Edit Post', edit_shark_post_path(@shark, post) %></td>
<td><%= link_to 'Destroy Post', [@shark, post], method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'New Post', new_shark_post_path(@shark) %>
파일 편집을 마치면 저장하고 닫으십시오.
다른 뷰에 대한 편집은 이전에 편집한 form
부분을 사용하기 때문에 많지 않을 것입니다. 그러나 다른 포스트 템플릿에서는 form
부분의 변경 사항을 반영하도록 link_to
참조를 업데이트할 것입니다.
app/views/posts/new.html.erb
파일을 엽니다:
파일 하단의 link_to
참조를 shark_posts_path(@shark)
헬퍼를 사용하도록 업데이트합니다:
. . .
<%= link_to 'Back', shark_posts_path(@shark) %>
이 변경 사항을 적용한 후 파일을 저장하고 닫으십시오.
다음으로, edit
템플릿을 엽니다:
Back
경로에 추가로, 중첩된 리소스를 반영하도록 Show
를 업데이트할 것입니다. 파일의 마지막 두 줄을 다음과 같이 변경합니다:
. . .
<%= link_to 'Show', [@shark, @post] %> |
<%= link_to 'Back', shark_posts_path(@shark) %>
파일을 저장하고 닫으십시오.
다음으로, show
템플릿을 엽니다:
nano app/views/posts/show.html.erb
파일 하단의 Edit
및 Back
경로를 다음과 같이 수정합니다:
. . .
<%= link_to 'Edit', edit_shark_post_path(@shark, @post) %> |
<%= link_to 'Back', shark_posts_path(@shark) %>
편집을 마치면 파일을 저장하고 닫으십시오.
마지막 단계로, 상세 정보 페이지를 열어 해당 상어에 대한 포스트가 개별적으로 표시되도록 show
뷰를 업데이트해야 합니다. 지금 해당 파일을 엽니다:
여기서의 편집은 폼에 Posts
섹션을 추가하고 파일 하단에 Add Post
링크를 추가하는 것입니다.
아래는 주어진 상어에 대한 사실입니다. 이 상어와 관련된 포스트 컬렉션의 각 인스턴스를 반복하고, 각 포스트의 본문을 출력하는 새 섹션을 추가할 것입니다.
다음 코드를 폼의 Facts
섹션 아래에 추가하고 파일 아래쪽의 리디렉션 위에 위치시킵니다:
. . .
<p>
<strong>Facts:</strong>
<%= @shark.facts %>
</p>
<h2>Posts</h2>
<% for post in @shark.posts %>
<ul>
<li><%= post.body %></li>
</ul>
<% end %>
<%= link_to 'Edit', edit_shark_path(@shark) %> |
. . .
그다음, 특정 상어에 대한 새로운 포스트를 추가할 수 있는 리디렉트를 추가합니다:
. . .
<%= link_to 'Edit', edit_shark_path(@shark) %> |
<%= link_to 'Add Post', shark_posts_path(@shark) %> |
<%= link_to 'Back', sharks_path %>
편집을 마쳤으면 파일을 저장하고 닫으십시오.
이제 애플리케이션의 모델, 컨트롤러 및 뷰를 변경하여 포스트가 항상 특정 상어와 관련되도록 했습니다. 마지막 단계로, 데이터베이스에 저장되는 데이터의 일관성을 보장하기 위해 Post
모델에 일부 유효성 검사를 추가할 수 있습니다.
단계 5 — 유효성 검사 추가 및 애플리케이션 테스트
단계 5의 루비 온 레일 애플리케이션 만들기에서는 데이터베이스 sharks
에 저장되는 데이터의 일관성과 일관성을 보장하기 위해 Shark
모델에 유효성 검사를 추가했습니다. 이제 posts
데이터베이스에도 유사한 조치를 취하여 보장합니다.
Post
모델이 정의된 파일을 엽니다:
여기서는 게시물이 비어 있지 않고 다른 사용자가 게시한 내용과 중복되지 않도록 보장하려고 합니다. 이를 위해 파일에 다음 줄을 추가하십시오:
class Post < ApplicationRecord
belongs_to :shark
validates :body, presence: true, uniqueness: true
end
편집을 마치면 파일을 저장하고 닫으십시오.
마지막 변경 사항을 적용했으므로 마이그레이션을 실행하고 애플리케이션을 테스트할 준비가 되었습니다.
먼저 마이그레이션을 실행하십시오:
다음으로 서버를 시작하십시오. 로컬에서 작업하는 경우 다음과 같이 실행할 수 있습니다:
개발 서버에서 작업 중이라면 대신 다음 명령을 실행하십시오:
애플리케이션 루트로 이동하십시오. http://localhost:3000
또는 http://your_server_ip:3000
.
필수 레일 프로젝트 자습서에서는 대표적인 대구 상어 항목을 추가하고 편집하는 방법을 안내했습니다. 추가적인 상어를 추가하지 않은 경우, 애플리케이션 랜딩 페이지는 다음과 같이 보일 것입니다:
표시 옆에 Great White의 이름을 클릭하세요. 이렇게 하면 이 상어의 show
뷰로 이동합니다. 상어의 이름과 사실을 볼 수 있으며 내용이 없는 게시물 헤더가 있습니다. 이 양식의 이 부분을 채우기 위해 게시물을 추가해 보겠습니다.
게시물 추가를 클릭하여 게시물 헤더 아래로 이동합니다. 이렇게 하면 게시물 index
뷰로 이동하게 되며 여기서 새 게시물을 선택할 기회가 주어집니다:
Step 6에서 구현한 인증 메커니즘 덕분에 이 단계에서 만든 사용자 이름과 비밀번호로 인증을 요청할 수 있습니다. 새 세션을 생성했는지 여부에 따라서입니다.
새 게시물을 클릭하여 게시물 new
템플릿으로 이동합니다:
내용 필드에 “These sharks are scary!”라고 입력하세요.
게시물 생성을 클릭하세요. 그러면 이 상어에 속하는 모든 게시물의 index
뷰로 리디렉션됩니다:
게시물 자원이 작동하므로 이제 원하는 데이터만 데이터베이스에 저장되도록 데이터 유효성을 테스트할 수 있습니다.
index
뷰에서 새 게시물을 클릭하세요. 새 양식의 내용 필드에 다시 “These sharks are scary!”를 입력해 보세요:
게시물 만들기를 클릭하십시오. 다음 오류가 표시됩니다:
뒤로를 클릭하여 주요 게시물 페이지로 돌아갑니다.
다른 유효성을 테스트하려면 새 게시물을 다시 클릭하십시오. 게시물을 비워두고 게시물 만들기를 클릭하십시오. 다음 오류가 표시됩니다:
중첩된 리소스와 유효성이 올바르게 작동되므로 이제 추가 개발을 위한 출발점으로 사용할 수 있는 작동하는 Rails 애플리케이션이 있습니다.
결론
지금 Rails 애플리케이션이 준비되었으므로 스타일링 및 다른 프론트엔드 구성 요소 개발과 같은 작업을 할 수 있습니다. 라우팅 및 중첩된 리소스에 대해 더 알고 싶다면 Rails 문서를 확인하는 것이 좋습니다.
애플리케이션에 프론트엔드 프레임워크를 통합하는 방법에 대해 자세히 알아보려면 Ruby on Rails 프로젝트에 React 프론트엔드 설정하는 방법을 확인하십시오.