Rails - CRUD

uglyduck.dev·2022년 3월 26일
0

개념 모아 🗂

목록 보기
36/40

모델 생성하기


CRUD

  • Create, Read, Update, Destroy의 약자
  • 생성, 읽기, 수정, 삭제 기능을 담당

ORM

  • Object Relational Mapping의 약어
  • DB 테이블의 관계형 데이터와 객체형 데이터를 연결
  • DB에 있는 테이블을 클래스화하고 데이터 조작 및 SQL 명령어들을 메서드화

1단계: Model class 생성

$ rails g model Post
  • rails g model [모델명]으로 작성된 명령어
  • 모델명 → 단수형의 CamelCase
  1. db/migrate/xxx_create_posts.rb
    • 데이터베이스 테이블 필드 등의 정보를 정의할 수 있는 마이그레이션 파일
  2. app/model/post.rb
    • Rails에서 사용할 model
    • 1번 마이그레이션을 통해 생성된 posts 테이블과 연결해서 사용

CoC Naming Rules

  1. 모델 클래스의 규칙은 단수형의 CamelCas
    • 첫 글자는 대문자로, 띄어쓰기는 생략하고 그 다음에 오는 문자는 대문자로 설정
    • ex) POST, PostComment
  2. 모델 클래스의 파일 이름은 단수형의 snake__case를 사용
    • 첫 글자는 소문자로, 띄어쓰기 대신 '_'를 사용
    • ex) post.rb, post_comment.rb
  3. 테이블의 복수형은 snake_case임
    • 복수형이라해서 단순하게 모든 단수형에 s만 붙이지 않음
    • Person이라는 모델을 만들면 table명은 people로 변환
    • ex) posts, post_comments

2단계: 마이그레이션 파일에서 테이블 정보 작성

  • Post 모델에 글 제목을 저장할 title 컬럼과 글 내용을 저장할 content 컬럼 생성
db/migrate/&&&&&&&_create_posts.rb
class CreatePosts < ActiveRecord::Migration[5.2]
  def change
    create_table :posts do |t|
			t.string :title
			t.text :content
		
      t.timestamps
    end
  end
end

3단계: 마이그레이션 파일로 테이블 생성

$ rake db:migrate

db/schema.rb

ActiveRecord::Schema.define(version: 2019_09_30_170541) do

  create_table "posts", force: :cascade do |t|
    t.string "title"
    t.text "content"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

end
  • ActiveRecord::Schema.define(version: 2019_09_30_170541) do를 통해 version을 확인하면 timestamp가 보이는데 이 부분은 현재 실행된 마이그레이션 파일의 timestamp임

create, read 하기


  • 생성할 페이지 안내
    1. 모든 글을 보는 페이지 → index
    2. 글 작성 form 페이지 → new
    3. 상세 페이지 → show
  • 생성할 action
    1. index
    2. new
    3. create (글 작성 액션)
    4. show

1단계: CR을 할 controller, action 생성

$ rails g controller posts index new create show
  • 자동으로 routesm controller, view 설정
  • create는 view가 필요하지 않으므로 app/views/posts/create.html.erb 삭제

2단계: index 만들기 (route, action, view 설정)

config/routes.rb

Rails.application.routes.draw do
  # root 주소 추가
  root 'posts#index'
  get 'posts/index'
  get 'posts/new'
  get 'posts/create'
  get 'posts/show'
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
  • root 'posts#index'로 초기 주소 설정

app/controllers/post_controller.rb

class PostsController < ApplicationController
  def index
		@posts = Post.all
  end
	
	[...]
end
  • @posts = Post.all 코드로 모든 Post 모델의 데이터를 @posts로 대입
  • all 메서드 → SELECT * FROM [테이블명]을 실행 후 테이블의 모든 값들을 모델 객체의 배열(ActiveRecord_Replation)로 리턴

app/views/posts/index.html.erb

<a href="/posts/new">새 글 작성</a>
<hr/>
<table border="1">
	<tr>
		<th>id</th>
		<th>제목</th>
		<th>내용</th>
		<th>생성일</th>
		<th>수정일</th>
	</tr>
	<% @posts.each do |post| %>
		<tr>
			<td><%= post.id %></td>
			<td><%= post.title %></td>
			<td><%= post.content %></td>
			<td><%= post.created_at %></td>
			<td><%= post.updated_at %></td>
		</tr>
	<% end %>
</table>
  • @posts 객체 배열의 내용을 순서대로 출력하기 위해 → each 메서드 사용
  • <%= %> erb 파일에서 Ruby 코드를 실행하지만 출력하지 않을 때 사용 (조건문, 반복문, 변수 할당 등)

3단계: new 만들기 (route, action, view 설정)

app/views/posts/new.html.erb

<h4>
	새 글 작성
</h4>
<form action="/posts/create" method="POST">
	<input name="authenticity_token" value="<%= form_authenticity_token %>" type="hidden">
	<input type="text" name="title">
	<br/>
	<textarea name="content"></textarea>
	<button>
		제출
	</button>
</form>
  • <input name="authenticity_token" value="<%= form_authenticity_token %>" type="hidden"> 코드는 CSRF 공격을 대응하는 코드
    • CSRF
      • 크로스 사이트 요청 위초, 웹사이트 취약점 공격
      • 사용자의 권한 이용, 사용자의 의지와 무관하게 공격자가 의도한 행위를 특정 웹사이트에 요청하게 하는 공격
  • POST, PUT, DELETE 등의 HTTP 메서드로 요청보낼 때 자동으로 난수화된 토큰을 생성하여 함께 전송
  • 토큰 일치여부에 따른 요청 처리 방식

config/routes.rb

Rails.application.routes.draw do
  get 'posts/index'
  get 'posts/new'
  post 'posts/create'
  get 'posts/show'
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
  • get 'posts/create' 코드를 post 'posts/create'로 변경
  • 데이터 저장 요청은 HTTP post 메서드 사용에 더 적합

4단계: create 만들기

app/controllers/posts_controller.rb

class PostsController < ApplicationController
  [...]

  def create
	  Post.create(title: params[:title], content: params[:content])
	  redirect_to "/posts/index"
  end

  [...]
end
  • Post.create(title: params[:title], content: params[:content]) 코드를 통해서 입력 받은 title과 content의 게시글(Post 모델의 데이터) 생성
  • redirect_to "/posts/index"로 데이터 생성 후 페이지 이동

5단계: show 만들기

app/views/posts/index.html.erb

<a href="/posts/new">새 글 작성</a>
<hr/>
<table border="1">
	<tr>
		<th>id</th>
		<th>제목</th>
		<th>내용</th>
		<th>생성일</th>
		<th>수정일</th>
		<!-- 추가 -->
		<th>링크</th>
		<!-- 추가 끝 -->
	</tr>
	<% @posts.each do |post| %>
		<tr>
			<td><%= post.id %></td>
			<td><%= post.title %></td>
			<td><%= post.content %></td>
			<td><%= post.created_at %></td>
			<td><%= post.updated_at %></td>
			<!-- 추가 -->
			<td><a href="/posts/show/<%= post.id %>">보기</a></td>
			<!-- 추가 끝 -->
		</tr>
	<% end %>
</table>
  • <a href="/posts/show/<%= [post.id](http://post.id) %>">보기</a>주소를 /posts/show/<%= post.id %>로 설정한 이유
    • 상세페이지는 라우트 파라미터 방식으로 특정 데이터의 id를 보내기 때문에 이와 같이 작성함

config/routes.rb

Rails.application.routes.draw do
  get 'posts/index'
  get 'posts/new'
  post 'posts/create'
	# 추가
  get 'posts/show/:id' => "posts#show"
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
  • get 'posts/show/:id' => "posts#show" 코드로 라우트 파리미터 정의
  • posts/show/ 뒤에 따라오는 문자는 id에 대입된 뒤 컨트롤러에 전달
  • 라우트 파라미터 정의하는 방식
    • routes.rb에서 주소 정의 부분에 ':변수명' 규칙을 지켜 작성
    • posts/:post_id/comments/:comment_id 방식처럼 라우트 파라미터 변수를 여러개 추가하는 것이 가능함

app/controllers/posts_controller.rb

class PostsController < ApplicationController
	[...]

  def show
	  @post = Post.find(params[:id])
  end
end
  • @post = Post.find(param[:id]) 라이트 파라미터 방식으로 넘어온 :id 값을 param[:id]로 추출
  • ex) "/posts/show/1" → params[:id] 는 1이됨
  • :id 값을 추출한 뒤 모델의 find 메서드로 넘어온 id값을 Post 모델에서 검색, 결과를 @post에 저장
    • find(val) 메서드
      • SELECT * FROM [테이블명] WHERE id=val;을 실행

app/views/posts/show.html.erb

<p>
	title : <%= @post.title %>
</p>

<p>
	content : <%= @post.content %>
</p>

<a href="/posts/index">Back</a>

update, destroy 하기


  • 생성할 action 안내
    1. edit
    2. update (글 수정 액션)
    3. destroy (글 삭제 액션)

1단계: edit 만들기

app/views/posts/index.html.erb

<a href="/posts/new">새 글 작성</a>
<hr/>
<table border="1">
	<tr>
		<th>id</th>
		<th>제목</th>
		<th>내용</th>
		<th>생성일</th>
		<th>수정일</th>
		<th>링크</th>
	</tr>
	<% @posts.each do |post| %>
		<tr>
			<td><%= post.id %></td>
			<td><%= post.title %></td>
			<td><%= post.content %></td>
			<td><%= post.created_at %></td>
			<td><%= post.updated_at %></td>
			<td>
				<a href="/posts/show/<%= post.id %>">보기</a> 
				<a href="/posts/edit/<%= post.id %>">수정</a>
			</td>
		</tr>
	<% end %>
</table>
  • 글을 수정하는 form을 만들기 위해 글의 정보가 필요하니 edit의 route 주소에 id 값이 필요함

config/routes.rb

Rails.application.routes.draw do
  get 'posts/index'
  get 'posts/new'
  post 'posts/create'
  get 'posts/show/:id' => "posts#show"
	# 추가
  get 'posts/edit/:id' => "posts#edit"
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end

app/controllers/posts_controller.rb

class PostsController < ApplicationController
  [...]
	
  def edit
	  @post = Post.find(params[:id])
  end
	
end
  • @post = Post.find(params[:id])를 통해 특정 게시글을 가져옴

app/views/posts/edit.html.rb

<h4>
	기존 글 수정
</h4>
<form action="/posts/update/<%= @post.id %>" method="POST">
	<input name="authenticity_token" value="<%= form_authenticity_token %>" type="hidden">
	<input type="text" name="title" value="<%= @post.title %>">
	<br/>
	<textarea name="content"><%= @post.content %></textarea>
	<button>
		수정
	</button>
</form>
  • <input type="text" name="title" value="<%= @post.title %>"> 기존 title 값 추가
  • <textarea name="content"><%= @post.content %></textarea> 기존 content 글 → input과 textarea에 추가

2단계: update 하기

config/routes.rb

Rails.application.routes.draw do
  [...]
  post 'posts/update/:id' => "posts#update"
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end

app/controllers/post_controller.rb

class PostsController < ApplicationController
  [...]
  def update
	  @post = Post.find(params[:id]) #기존 글 검색		
	  @post.update(title: params[:title], content: params[:content]) #글 수정
	  redirect_to "/posts/show/#{@post.id}" #수정된 게시글 확인을 위해 이동
  end
end
  • 큰 따옴표("")로 감싼 문자열 안에 변수값을 포함할 수 있음
  • 코드 중간에 등장한 #{} 안에 변수명을 입력하면 문자열 안에 값이 포함됨

3단계: destroy 만들기

config/routes.rb

Rails.application.routes.draw do
	[...]
  get 'posts/destroy/:id' => "posts#destroy"
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end

app/views/index.html.erb

<a href="/posts/new">새 글 작성</a>
<hr/>
<table border="1">
	<tr>
		<th>id</th>
		<th>제목</th>
		<th>내용</th>
		<th>생성일</th>
		<th>수정일</th>
		<th>링크</th>
	</tr>
	<% @posts.each do |post| %>
		<tr>
			<td><%= post.id %></td>
			<td><%= post.title %></td>
			<td><%= post.content %></td>
			<td><%= post.created_at %></td>
			<td><%= post.updated_at %></td>
			<td>
				<a href="/posts/show/<%= post.id %>">보기</a> 
				<a href="/posts/edit/<%= post.id %>">수정</a>
				<!-- 코드 추가 -->
				<a href="/posts/destroy/<%= post.id %>">삭제</a>
			</td>
		</tr>
	<% end %>
</table>
  • destroy 메서드를 실행하면 id로 검색했던 @post의 데이터를 Post 모델에서 삭제

app/controllers/post_controller.rb

class PostsController < ApplicationController
  [...]
  def destroy
	  @post = Post.find(params[:id])
	  @post.destroy
	  redirect_to "/posts/index"
  end
end
  • destroy는 SQL에서 DELETE FROM [테이블명] WHERE 조건; 쿼리를 실행하는 것과 같음

Reference

profile
시행착오, 문제해결 그 어디 즈음에.

0개의 댓글