Rails에서 DB 데이터 다루기

2020-10-02 hit count image

Ruby on Rails를 사용해서 DB에 데이터를 CRUD(Create Read Update Delete)하는 방법에 대해서 알아봅니다.

개요

이번 블로그 포스트에서는 Rails를 사용하여 본격적으로 데이터를 다루어 보려고 합니다. 본격적으로 데이터를 다루기 위해 DB를 설정하고, DB에 테이블을 생성할 예정입니다. 이렇게 생성된 DB에 Rails를 사용하여 CRUD(Create Read Update Delete)를 해 봄으로써, Rails에서 데이터를 다루는 방법에 대해서 이해해 보려고 합니다.

이 블로그 포스트는 시리즈로 제작되었습니다. 자세한 내용은 아래에 링크를 참고하시기 바랍니다.

여기서 사용한 소스코드는 Github에서 확인할 수 있습니다.

DB 설정

우선 DB를 다루기 위해서는 Rails에 DB에 대한 설정을 해 주어야합니다. 여기서는 mysql을 설정하는 방법에 대해서만 다룹니다. mysql은 이미 로컬에 설치되었다는 가정하에 진행합니다.

Rails에서 DB 설정은 config/database.yml 파일이 담당하고 있습니다. config/database.yml 파일을 열면 아래와 같은 내용을 확인할 수 있습니다.

default: &default
  adapter: sqlite3
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  timeout: 5000

development:
  <<: *default
  database: db/development.sqlite3

test:
  <<: *default
  database: db/test.sqlite3

production:
  <<: *default
  database: db/production.sqlite3

Rails는 기본적으로 sqlite3를 설정하고 있습니다. 우리는 mysql을 사용할 예정이므로 위에 설정을 아래와 같이 수정합니다.

default: &default
  adapter: mysql2
  encoding: utf8
  database: study_rails
  pool: 5
  username: root
  password:
  socket: /tmp/mysql.sock

development:
  <<: *default

test:
  <<: *default

production:
  <<: *default

위에 설정에서 database, username, password를 자신의 로컬 환경에 맞춰서 수정해 줍니다.

위에 설정은 모든 설정을 default에 작성하였으며, 다른 환경에서는 default에 모든 내용을 참조하도록 하였습니다. 특정 환경에서 특정 정보를 수정하고 싶다면 아래와 같이 변경할 수 있습니다.

production:
  <<: *default
  username: root
  password: XXXX

위와 같이 작성하면 production 환경인 경우는 주어진 username과 password를 사용하게 됩니다.

DB 설정에 사용할 수 있는 매개 변수는 다음과 같습니다.

이름설명
adapter접속할 데이터베이스 종류(sqlite3, mysql2, postgresql 등)
database데이터베이스 이름(sqlite는 데이터베이스 파일 경로)
host호스트 이름 또는 IP 주소
port포트 번호
pool확보할 접속 풀
timeout접속 타임아웃(밀리초 단위)
encoding사용할 문자 코드
username사용자 이름
password비밀 번호
socket소켓(/tmp/mysql.sock)

mysql2

Rails에서 Mysql에 연결하기 위해서는 mysql2라는 gem이 필요합니다. 아래에 명령어로 mysql2를 설치합니다.

bundle add mysql2

설치가 완료되었다면, 아래에 명령어를 사용하여 데이터베이스를 생성합니다.

bundle exec rake db:create

만약 아래와 같은 메세지가 나오면서 데이터베이스가 생성되지 않는다면,

warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
The called method `initialize' is defined here
[BUG] Segmentation fault at 0x0000000000000000
...

아래에 명령어를 사용하여 mysql2를 설치합니다.

gem install mysql2 -- --with-ldflags=-L/usr/local/opt/openssl/lib --with-cppflags=-I/usr/local/opt/openssl/include

그리고 다시 아래에 명령어를 사용하여 데이터베이스를 생성합니다.

bundle exec rake db:create

무사히 데이터베이스가 생성되었다면 아래와 같은 메세지를 확인할 수 있습니다.

Created database 'study_rails'
Database 'study_rails' already exists

Model 만들기

데이터베이스를 만들었으니, 이제 데이터를 저장할 테이블을 만들어 봅시다. Rails를 사용하여 테이블을 만들기 위해서는 우선 Model을 생성할 필요가 있습니다.

아래에 명령어를 사용하여 Model을 생성합니다.

# bundle exec rails generate model post
bundle exec rails g model post

이렇게 Model을 생성하면 아래와 같은 파일들이 생성되는 것을 확인할 수 있습니다.

├── app
│   ├── models
│   │   ├── post.rb
├── db
│   ├── migrate
│   │   ├── 20200315053129_create_posts.rb
├── test
│   ├── fixtures
│   │   ├── posts.yml
│   ├── models
│   │   ├── post_test.rb
  • app/models/post.rb: 실제 테이블과 연결되는 Model
  • db/migrate/20200315053129_create_posts.rb: 테이블을 생성하기 위한 migratoin 파일
  • test/fixtures/posts.yml: 테스트를 위한 Dummy 데이터
  • test/models/post_test.rb: Model의 유닛 테스트를 위한 파일

테이블 생성

이제 실제 데이터를 데이터베이스에 저장하기 위해 테이블을 생성해 봅시다. 데이터베이스에 테이블을 생성하기 위해서는 Migration 파일을 수정할 필요가 있습니다.

posts 테이블을 생성하기 위해 db/migrate/20200315053129_create_posts.rb 파일을 열고 아래와 같이 수정합니다.

class CreatePosts < ActiveRecord::Migration[6.0]
  def change
    create_table :posts do |t|
      t.string :title
      t.text :content
      t.timestamps
    end
  end
end

이 posts 테이블은 기본적으로 String 타입의 title과 긴 문자열을 저장하기 위해 Text 타입의 content를 가지고 있습니다. 또한

bundle exec rake db:migrate

명령어를 실행하여 테이블을 생성하면 db/schema.rb 파일이 생성되는 것을 확인할 수 있습니다. db/schema.rb 파일을 열면 아래와 같은 내용을 확인할 수 있습니다.

ActiveRecord::Schema.define(version: 2020_03_15_053129) do

  create_table "posts", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
    t.string "title"
    t.text "content"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end

end

또한 데이터베이스 툴을 사용하여 실제 테이블이 생성되었는지 확인하면, 아래와 같이 잘 생성된 것을 확인할 수 있습니다.

Ruby on Rails 테이블 생성 결과

Migration을 사용하여 테이블을 생성하였다면, 아래에 명령어를 사용하여 생성전으로 돌아갈 수 있습니다.

bundle exec rake db:rollback

CRUD

지금부터는 위에서 생성한 테이블에 Rails를 사용하여 CRUD(Create Read Update Delete)을 해보도록 하겠습니다.

Create

데이터를 생성하기 위해 우선 app/controllers/home_controller.rb 파일을 열고 아래와 같이 수정합니다.

class HomeController < ApplicationController
    ...
    def form

    end
end

사용자로부터 입력을 받을 화면을 표시할 form를 생성하였습니다. 해당 form Action에 관한 View를 작성하기 위해, app/views/home/form.erb 파일을 생성하고 아래와 같이 수정합니다.

<a href="/list">Go back</a><br/>
<form action="/create" method="POST">
  <input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>" />
  <label for="title">title:</label>
  <input type="text" name="title" />
  <label for="content">content:</label>
  <input type="text" name="content" />
  <input type="submit" />
</form>

사용자가 submit 버튼을 누르면 POST 방식으로 /create라는 URL에 데이터를 보내도록 만들었습니다.

그리고 Route에 해당 URL을 등록하기 위해, config/routes.rb 파일을 열고 아래와 같이 수정합니다.

Rails.application.routes.draw do
  ...
  get '/form', to: 'home#form'
end

그리고 확인을 위해 아래에 명령어로 Rails 서버를 실행합니다.

bundle exec rails s

그리고 http://127.0.0.1:3000/form에 이동하면 아래와 같은 화면을 볼 수 있습니다.

Ruby on Rails, CRUD 데이터 생성 form

이제 실제로 데이터를 받고 처리하는 부분을 만들어 봅시다. app/controllers/home_controller.rb 파일을 열고 아래와 같이 수정합니다.

class HomeController < ApplicationController
    ...
    def create
        post = Post.new
        post.title = params[:title]
        post.content = params[:content]
        post.save

        redirect_to '/list'
    end

    def list

    end
end

위에 내용을 자세히 살펴보면,

  • post = Post.new: 우리가 만든 POST 모델을 사용하여 새로운 데이터를 생성할 준비를 합니다.
  • post.title = params[:title]: 새롭게 생성할 POST 데이터의 title에 사용자가 입력한 title 데이터를 대입합니다
  • post.content = params[:content]: 새롭게 생성할 POST 데이터의 content에 사용자가 입력한 content 데이터를 대입합니다
  • post.save: 마지막으로 해당 데이터를 저장함으로써, 데이터베이스에 데이터를 생성(create)합니다.
  • redirect_to '/list': 생성을 완료한 후, /list라는 URL로 Redirect 시킵니다.
  • def list: 일단 Redirect할 때, 에러가 나오지 않도록 빈 Action을 추가하였습니다. 추후 이 Action에 데이터를 표시하는 처리를 하도록 할 예정입니다.

위에 내용을 보면 알 수 있듯이, create 액션은 redirect를 시키므로, View가 필요하지 않습니다. 하지만 추후 데이터를 표시할 list 액션은 View가 필요하므로, app/views/home/list.erb 파일을 생성해 둡니다.

이제 이렇게 추가한 Action들을 사용하기 위해 config/routes.rb 파일을 열고 아래와 같이 수정합니다.

Rails.application.routes.draw do
  ...
  post '/create', to: 'home#create'
  get '/list', to: 'home#list'
end

그리고 http://127.0.0.1:3000/form에 데이터를 넣고 Submit 버튼을 누르면 http://127.0.0.1:3000/list로 이동하는 것을 확인할 수 있습니다. 또한 데이터베이스 툴을 사용하면 아래와 같이 데이터가 잘 추가된 것을 확인할 수 있습니다.

Ruby on Rails, CRUD 데이터 생성 확인

Read

이제 위에서 생성한 데이터를 읽어와서(Read), 화면에 표시해 보도록 합시다. app/controllers/home_controller.rb 파일을 열고 아래와 같이 수정합니다.

class HomeController < ApplicationController
    ...
    def list
        @posts = Post.all
    end
end

위에서 Post.all을 사용하여 Post 테이블에 저장된 모든 데이터를 가져왔습니다. 이렇게 가져온 데이터를 View에 전달하기 위해 @posts 라는 인스턴스 변수에 저장하였습니다.

이렇게 저장한 데이터를 화면에 표시하기 위해, app/views/home/list.erb 파일을 열고 아래와 같이 수정합니다.

<style>
table, th, td {
  border: 1px solid black;
}
</style>
<a href="/form">Create New Post</a>
<table>
    <thead>
        <tr>
            <th>Title</th>
            <th>Content</th>
            <th>Action</th>
        </tr>
    </thead>
    <tbody>
        <% @posts.each do |post| %>
        <tr>
            <td><%= post.title %></td>
            <td><%= post.content %></td>
            <td></td>
        </tr>
        <% end %>
    </tbody>
</table>

여기서 중요한 부분은 @posts 인스턴스 변수를 통해 가져온 데이터를 아래에 코드를 사용하여 루프를 돌면서 하나씩 post 변수에 할당합니다.

<% @posts.each do |post| %>
...
<% end %>

이렇게 할당한 post 변수에서 title과 content를 가져와서 화면에 표시합니다.

<td><%= post.title %></td>
<td><%= post.content %></td>

이렇게 작성하고 다시 http://127.0.0.1:3000/list에 접속해 보면 아래와 같이 데이터를 잘 표시하는 것을 확인할 수 있습니다.

Ruby on Rails 데이터베이스로부터 가져온 데이터를 화면에 표시

Update

이제 위에서 생성한 데이터를 수정(Update)하는 방법에 대해서 알아보자. app/controllers/home_controller.rb 파일을 열고 아래와 같이 수정합니다.

class HomeController < ApplicationController
    ...
    def modify
        @post = Post.find(params[:id])
    end
end

위에 modify는 사용자에게 수정할 수 있는 Form을 제공하기 위한 액션이다. 사용자로부터 수정하고 싶은 id를 전달받고, 전달 받은 id로 필요한 데이터를 찾고(Post.find), 찾은 데이터를 View에 전달하기 위해 인스턴스 변수(@post)에 할당하였다.

이제 추가되어 있는 데이터를 수정하기 위한 Form을 사용자에게 제공하기 위해 app/views/home/modify.erb 파일을 생성하고 아래와 같이 수정한다.

<a href="/list">Go back</a><br/>
<form action="/update/<%= @post.id %>" method="POST">
  <input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>" />
  <label for="title">title:</label>
  <input type="text" name="title" value="<%= @post.title %>"/>
  <label for="content">content:</label>
  <input type="text" name="content" value="<%= @post.content %>" />
  <input type="submit" />
</form>

데이터 생성을 위해 만든 app/views/home/form.erb과 거의 동일하다. 다른 부분은 Input 태그의 value에 Controller로부터 전달 받은 데이터를 표시하고 있다.

<input type="text" name="title" value="<%= @post.title %>%"/>
<input type="text" name="content" value="<%= @post.content %>%" />

이렇게 생성한 페이지를 표시하기 위해 Route를 설정해 봅시다. config/routes.rb 파일을 열고 아래와 같이 수정합니다.

Rails.application.routes.draw do
  ...
  get '/modify/:id', to: 'home#modify'
end

데이터를 수정하기 위한 View인 modify는 URL에 파라메터를 전달하는 방식을 사용하였습니다. 이렇게 URL로 id를 전달받고, Controller는 전달받은 id로 데이터를 조회한 후, 화면에 표시할 예정입니다.

이제 modify 페이지를 열기 위한 링크를 추가하기 위해 app/views/home/list.erb 파일을 열고 아래와 같이 수정합니다.

...
<table>
    <thead>
        ...
    </thead>
    <tbody>
        <% @posts.each do |post| %>
        <tr>
            <td><%= post.title %></td>
            <td><%= post.content %></td>
            <!-- add this line -->
            <td><a href="/modify/<%= post.id %>">modify</a></td>
        </tr>
        <% end %>
    </tbody>
</table>

이렇게 작성하고 다시 http://127.0.0.1:3000/list에 접속해 보면 아래와 같이 데이터를 잘 표시하는 것을 확인할 수 있습니다.

Ruby on Rails 데이터를 수정하기 위한 링크 추가

여기서 modify 링크를 클릭하면 아래와 같은 화면을 확인할 수 있습니다.

Ruby on Rails 데이터 수정 페이지

이제 데이터를 실제로 업데이트할 액션을 만들어 봅시다. 위의 modify 페이지에서 submit 버튼을 누르면 /update/:id로 이동하도록 설정하였습니다.

<a href="/list">Go back</a><br/>
<form action="/update/<%= @post.id %>" method="POST">
  ...
</form>

그럼 이 링크에 해당하는 액션을 생성하기 위해, app/controllers/home_controller.rb를 열고 아래와 같이 수정합니다.

class HomeController < ApplicationController
    ...
    def update
        post = Post.find(params[:id])
        post.title = params[:title]
        post.content = params[:content]
        post.save

        redirect_to '/list'
    end
end

파라메터로 전달받은 id로 저장한 데이터를 가져오고, 사용자가 입력한 titlecontent 데이터로 저장된 데이터를 갱신한 후, post.save를 사용하여 데이터를 업데이트했습니다. 마지막으로 데이터를 업데이트하면 /list 페이지로 Redirect되도록 설정하였습니다.

그럼 이 액션을 사용할 수 있도록 Route에 URL을 추가해 봅시다. config/routes.rb 파일을 열고 아래와 같이 수정합니다.

Rails.application.routes.draw do
  ...
  post '/update/:id', to: 'home#update'
end

URL을 통해 id 파라메터를 전달받을 예정이고, 사용자가 입력한 데이터는 POST 방식으로 전달받을 예정입니다.

이제 데이터 수정 페이지로 이동한 후,

Ruby on Rails 데이터를 수정하기 위한 링크 추가

아래와 같이 기존 데이터와 다른 내용을 입력해 봅니다.

Ruby on Rails 데이터 수정 페이지 - 데이터 수정

그리고 Submit 버튼을 누르면 아래와 같이, 데이터가 잘 수정된 것을 확인할 수 있습니다.

Ruby on Rails 데이터 리스트 페이지 - 데이터 수정 결과

Delete

이제 CRUD의 마지막인 데이터 삭제(Delete)에 관해서 알아봅시다. 데이터를 삭제하는 액션을 추가하기 위해 app/controllers/home_controller.rb 파일을 열고 아래와 같이 수정합니다.

class HomeController < ApplicationController
    ...
    def delete
        Post.destroy(params[:id])

        redirect_to '/list'
    end
end

전달받은 id를 가지고 Post 모델의 데이터를 삭제(Post.destroy)하고 /list 페이지로 Redirect하도록 하였습니다.

이제 이 액션을 URL에 추가하기 위해 config/routes.rb 파일을 열고 아래와 같이 수정합니다.

Rails.application.routes.draw do
  ...
  get '/delete/:id', to: 'home#delete'
end

그리고 이 페이지를 호출할 수 있도록 list 페이지를 수정해 봅시다. app/views/home/list.erb 파일을 열고 아래와 같이 수정합니다.

...
<table>
    <thead>
        ...
    </thead>
    <tbody>
        <% @posts.each do |post| %>
        <tr>
            <td><%= post.title %></td>
            <td><%= post.content %></td>
            <td>
                <a href="/modify/<%= post.id %>">modify</a><br/>
                <a href="/delete/<%= post.id %>">delete</a><br/>
            </td>
        </tr>
        <% end %>
    </tbody>
</table>

다시 http://127.0.0.1:3000/list에 접속하면 아래와 같이 delete 링크가 추가된 것을 확인할 수 있습니다.

Ruby on Rails 데이터 리스트 페이지 - 삭제 링크

그럼 delete 링크를 눌러봅니다. delete 링크를 누르면 저장된 데이터가 잘 삭제되고, 아래와 같이 리스트 페이지에도 데이터가 표시되지 않는 것을 확인할 수 있습니다.

Ruby on Rails 데이터 리스트 페이지 - 삭제 성공

완료

이것으로 Ruby on Rails에서 데이터베이스를 생성하고 데이터를 CRUD(Create Read Update Delete)하는 방법에 대해서 알아보았습니다. 이제 여러분은 Ruby on Rails로 기본적인 웹 서비스를 개발할 준비가 되었습니다.

이제 웹 서비스를 만들어보면서 Rails를 깊게 공부해 보시기 바랍니다.

참고

이 블로그 포스트는 시리즈로 제작되었습니다. 자세한 내용은 아래에 링크를 참고하시기 바랍니다.

여기서 사용한 소스코드는 Github에서 확인할 수 있습니다.

제 블로그가 도움이 되셨나요? 하단의 댓글을 달아주시면 저에게 큰 힘이 됩니다!

책 홍보

저도 블로그를 시작한지 1년만에...책을 다 써봅니다...인생에서 이런 날도 오는군요...타국에서 책 출판도 할 수 있고, 참 좋은 세상입니다.

이번에 쓴 책은 스무디 한 잔 마시며 끝내는 React Native입니다. 다양한 예제를 통해 리액트 네이티브를 공부할 수 있도록 구성해 보았습니다. 또한 설치부터 배포까지 실전에서도 사용할 수 있는 내용들을 담고 있습니다.

아래 링크를 통해 제가 쓴 책을 구매하실 수 있습니다.
많은 분들에게 도움이 되면 좋겠네요.

스무디 한 잔 마시며 끝내는 React Native React Native로 실전 스마트폰 앱 만들기
Posts