코드캐더미로 배우는 레일즈 기초 1부

13 May 2015

코드캐더미 강좌는 볼 때마다 친절하게 잘 짜여져 있다는 생각이 든다. 레일즈 기초 강좌도 앞서 포스팅한 코세라 강좌인 루비 온 레일즈로 블로그 만들기를 들을 때 이해하지 못한(들을 때는 이해한 듯 했지만;;) 부분까지 잘 설명해준다. 이에 필받아서 Learn Ruby on Rails : Codecademy 강좌를 오프라인에서도 따라할 수 있게 좀 더 자세하게 풀어서 설명한다.

루비를 모른다면 Ruby : Codecademy를 먼저 하길 권한다.

코드캐더미에서는 모든 것이 웹브라우저에서 인터렉티브하게 진행되기 때문에 레일즈의 설치와 기본 디렉토리 구조는 루비 온 레일즈로 블로그 만들기의 “루비온 레일즈 설치” 부분을 참조하는 것으로 대치한다.

MySite

rails new MySite
cd MySite
bundle install
rails server

웹브라우저 주소창에 http://localhost:3000을 입력하면 레일즈 앱을 볼 수 있다.

  • rails new 명령은 모든 레일즈 프로젝트의 시작점이다.
  • bundle install 명령은 새로운 레일즈 앱에 필요한 모든 소프트웨어 패키지를 설치한다. 소프트웨어 패키지를 gem이라고 부르며, Gemfile 파일 안에 나열된다.
  • rails server 명령은 레일즈 개발 서버를 시작한다. 이 개발 서버는 WEBrick이다.

request/response cycle

http://localhost:3000을 방문하면 어떠한 일이 발생하는가?

  1. 브라우저가 http://localhost:3000 URL에 요청request한다.
  2. 요청은 config/routes.rb 안의 레일즈 라우터를 hit한다. 라우터는 URL을 인식하고 컨트롤러에 요청을 보낸다.
  3. 컨트롤러가 요청을 받고 처리한다.
  4. 컨트롤러가 뷰view에 요청을 넘긴다.
  5. 뷰는 HTML 페이지를 렌더링한다.
  6. 컨트롤러가 사용자가 보고 있는 브라우저에 HTML을 다시 보낸다.

request/response cycle을 보려면 레일즈앱을 만드는 세 부분이 필요하다: 컨트롤러, 라우트, 뷰

먼저 컨트롤러를 만들자.

Controller

rails generate controller Pages

위의 명령은 Pages라는 새로운 컨트롤러를 제너레이트하며, app/controllers/pages_controller.rb 파일을 만들어 준다.

app/controllers/pages_controller.rb을 열고 PagesController 클래스 안에 home 매서드를 추가한다.

def home
end

레일즈에서 매서드는 컨트롤러 액션action으로 불리기도 한다. Pages 컨트롤러에 home 액션을 추가한 것이다.

Route

이제 컨트롤러를 가졌으니, request/response cycle의 두 번째 부분으로 넘어가서 라우트를 만들자. config/routes.rb 파일을 열고 다음을 추가한다:

get 'welcome' => 'pages#home'

이제 http://localhost:3000/welcome 방문했을 때 위의 라우트가 레일즈에게 Pages 컨트롤러의 home 액션으로 이 요청을 보내라고 말한다.

View

컨트롤러와 라우트를 가졌으니, request/response cycle의 세 번째 부분으로 넘어가서 뷰를 만들자. app/views/pages/home.html.erb을 만들고 다음 HTML을 입력한다. 자신의 이름으로 채워라.

<div class="main">
  <div class="container">
    <h1>Hello my name is Nolboo Kim</h1>
    <p>I make Rails apps.</p>
  </div>
</div>

app/assets/stylesheets/pages.css.scss에 CSS를 입력할 수 있다. 파일명을 pages.scss로 하여도 결과는 같다.

  • 미리 준비된 CSS를 입력하여야 제대로 된 페이지를 볼 수 있다.

워크플로우 정리

지금까지가 Request-Response Cycle를 가이드로 한, 레일즈 앱을 만들때 일반적인 워크플로우이다.

  1. 새 레일즈 앱을 제너레이트한다.
  2. 컨트롤러를 제너레이트하고 액션을 추가한다.
  3. 라우트를 만들고 컨트롤러 액션에 URL을 매핑한다.
  4. HTML, CSS로 뷰를 만든다.
  5. 로컬 웹서버를 실행하고 브라우저에서 앱을 미리보기한다.

정적static 페이지를 가진 레일즈앱을 만들었으며, 컨트롤러, 라우트, 뷰를 사용하였다. 정적 페이지를 가진 레이즈 앱은 모든 사용자가 같은 화면을 보게 된다.

메신저 앱

이제 데이타베이스와 함께 정보를 저장하는 앱을 만들어 보자. 데이타베이스에 맞는 Request-Response Cycle은 다음과 같다. 다이어그램은 Request-Response Cycle II을 참조한다.

  1. 브라우저에 http://localhost:3000/welcome를 입력하면 브라우저가 /welcome URL을 위한 요청을 만든다.
  2. 요청은 레일즈 라우터를 hit한다.
  3. 라우터는 요청을 핸들링하기위해 그 URL을 컨트롤러 액션에 매핑한다.
  4. 컨트롤러 액션은 요청을 받고, 데이터베이스에서 데이터를 불러오도록 모델model에게 요청한다.
  5. 모델은 컨트롤러 액션에게 데이터를 리턴한다.
  6. 컨트롤러 액션은 뷰에 데이터를 넘긴다.
  7. 뷰가 HTML 페이지를 렌더링한다.
  8. 컨트롤러서 HTML을 브라우저에게 보낸다.
rails new MessengerApp
cd MessengerApp
bundle install
rails server

MessengerApp이라는 레일즈 앱을 만들었다.

Request-Response Cycle II를 보라. 레일즈을 만드는 네 부분이 필요하다 - 모델, 라우트, 컨트롤러, 뷰

Model

모델을 만들자. 터미널에서 다음 명령어로 Message라는 새로운 모델을 제너레이트한다.

rails generate model Message

이러면 레일즈는 두 개의 파일을 만든다.

  1. app/models/message.rb라는 모델 파일. 데이터베이스의 테이블을 기술한다.
  2. db/migrate/안의 마이그레이션 파일. 마이그레이션은 데이터베이스를 업데이트하는 방법이다.

마이그레이션

메시지 테이블을 위해 db/migrate/ 안의 마이그레이션 파일을 연다. 마이그레이션 파일명은 생성일시 timestamp로 시작된다. change 매서드 안의 line4에 다음을 추가한다:

    t.text :content

change 메서드는 데이터베스에 무엇을 변경할 것인가를 레일즈에게 말해준다. 여기서는 데이터베이스에 메시지를 저장할 새로운 테이블을 만들기 위해 create_table을 이용한다.

create_table 안에 t.text :content추가한 것은 메시지 테이블 안에 content라는 텍스트 컬럼을 만든 것이다.

마지막 줄 t.timestamps는 메시지 테이블에 create_atupdate_at이라는 두개의 컬럼을 만드는 레일즈 명령어이다. 이 컬럼들은 메시지가 만들어지고 업데이트될 때 자동으로 설정된다.

rake db:migrate

rake db:migrate 명령어는 새로운 메시지 데이터 모델을 데이터베이스에 업데이트한다.

rake db:seed 명령어는 db/seeds.rb로부터 샘플 데이터를 공급seed해준다. 먼저 db/seeds.rb에 샘플 데이터를 다음과 같이 데이터를 추가한다:

m1 = Message.create(content: "We're at the beach so you should meet us here! I make a mean sandcastle. :)")

m2 = Message.create(content: "Let's meet there!")

입력된 후의 모습은 db/seeds.rb에서 확인할 수 있다.

다음 명령어로 샘플 데이터를 seed한다:

rake db:seed

Controller

이제 모델을 가졌으니 request/response cycle의 두번째와 세번째 부분으로 넘어가서 컨트롤러와 라우트를 만들자.

rails generate controller Messages

config/routes.rb 파일을 열고 다음을 추가하여 /messages URL을 Messages 컨트롤러의 index 액션에 매핑하는 라우트를 만든다:

get '/messages' => 'messages#index'

app/controllers/messages_controller.rbindex 액션을 추가한다:

def index
    @messages = Message.all
end

Messages 컨트롤러의 액션명을 index로 한 이유는? Standard Controller Actions를 참조하라. 레일즈는 데이터를 일반적으로 조작하기 위한 7가지의 표준 컨트롤러 액션을 제공한다.(index, show, new, create, edit, update, destroy) 여기서는 모든 메시지의 목록을 보여주길 원해서 index 액션을 사용했다.

이제 사용자가 http://localhost:3000/messages를 방문하면 라우트 파일은 이 요청을 Messages 컨트롤러의 index 액션으로 매핑한다. index 액션은 데이터베이스의 모든 메시지를 가져와 @messages 변수에 저장한다.

View

@messages 변수는 뷰로 넘겨지고 뷰는 각각의 메시지를 보여줘야 한다.

app/views/messages/index.html.erb를 만들고, 다음을 입력한다:

<div class="header">
  <div class="container">
    <img src="http://s3.amazonaws.com/codecademy-content/courses/learn-rails/img/logo-1m.svg">
    <h1>Messenger</h1>
  </div>
</div>

<div class="messages">
  <div class="container">

    <% @messages.each do |message| %> 
        <div class="message"> 
          <p class="content"><%= message.content %></p> 
          <p class="time"><%= message.created_at %></p> 
        </div> 
    <% end %>
    
  </div>
</div>

index.html.erb은 일종의 웹 템플릿template이다. 웹 템플릿은 변수와 컨트롤 플로우 선언을 포함하는 HTML 파일이다. 각 메시지를 반복해서 같은 HTML을 쓰는 대신에 데이터베이스에서 데이터를 루핑하여 보여주기 위해 웹 템플릿을 사용할 수 있다.

  1. <% @messages.each do |message| %>@messages 배열의 각각의 메시지를 반복하여 실행한다. 우린 Messages 컨트롤러의 index 액션에서 @messages를 만들었었다.
  2. 각각의 메시지에 대하여, 메시지 내용와 만들어진 시간을 보여주기 위해 <%= message.content %><%= message.created_at %>를 사용했다.

스타일링을 위해서 /app/assets/stylesheets/messages.scss를 보고 CSS 코드를 입력한다.

이제 rails server를 실행하고 http://localhost:3000/messages를 방문하면 메시지앱을 볼 수 있다.

메시지앱 실행화면

Route

지금까지 메시지를 데이터베이스에서 로드하고 뷰에서 보여주었다. 그럼 어떻게 새로운 메시지를 만들고 그것을 데이터베이스에 저장할까? Standard Controller Actions를 참조하면, newcreate 액션을 사용할 필요가 있다.

라우트 파일 config/routes.rbmessage/new 요청을 Message 컨트롤러의 new 액션과 매핑하는 라우트를 만들어 준다:

get '/messages/new' => 'messages#new'

Messages 컨트롤러 app/controllers/messages_controller.rbindex 액션 밑에 new 액션을 추가한다:

def new
    @message = Message.new
end

라우트 파일 config/routes.rb에 다음 라우트를 추가하여 Messages 컨트롤러의 create 액션에 요청을 매핑한다.

post 'messages' => 'messages#create'

Messages 컨트롤러 app/controllers/messages_controller.rbnew 액션 밑에 message_params라는 개인private 매서드를 추가한다:

private 
  def message_params 
    params.require(:message).permit(:content) 
  end

new 액션과 개인 매서드 사이에 create 액션을 추가한다:

def create
  @message = Message.new(message_params) 
  if @message.save 
    redirect_to '/messages' 
  else 
    render 'new' 
  end 
end

Form

다음에 app/views/messages/new.html.erb를 만들고 다음을 입력한다:

<div class="header">
  <div class="container">
    <img src="http://s3.amazonaws.com/codecademy-content/courses/learn-rails/img/logo-1m.svg">
    <h1>Messenger</h1>
  </div>
</div>

<div class="create">
  <div class="container">
    
    <%= form_for(@message) do |f| %>  
      <div class="field"> 
        <%= f.label :message %><br> 
        <%= f.text_area :content %> 
      </div> 
      <div class="actions"> 
        <%= f.submit "Create" %> 
      </div> 
    <% end %>

  </div>
</div>

마지막으로 app/views/messages/index.html.erb<% @messages.each do |message| %>...<% end %> 블록 밑에 다음을 추가한다:

<%= link_to 'New Message', "messages/new" %>

http://localhost:3000/messages를 방문하여 New Message를 클릭하여 자신만의 메시지를 만들어 보자.

Workflow

Request-Response Cycle III를 가이드 삼아 사용자 요청이 앱 전체에 걸쳐 어떻게 흐르는지 살펴보라.

새 메시지를 만들기위해서 http://localhost:3000/messages/new를 방문했을 때 request/response cycle의 첫번째 turn이 트리거trigger 된다.

  1. 브라우저가 /messages/new URL을 위한 HTTP GET 요청을 만든다.
  2. 레일즈 라우터가 이 URL을 Messages 컨트롤러의 new 액션으로 매핑한다. new 액션은 새로운 Message 객체 @message를 만들고 app/views/messages/new.html.erb의 뷰로 넘긴다.
  3. 뷰에서 form_for@message 객체의 필드들로 폼을 만든다.

폼을 다 채우고 Create를 누르면 request/response cycle의 두번째 turn이 트리거trigger 된다.

  1. 브라우저가 /messages URL에 대한 HTTP POST 요청을 통해 레일즈 앱에 데이터를 보낸다.
  2. 이번에는 레일즈 라우터가 이 URL을 create 액션에 매핑한다.
  3. create 액션은 폼에서 데이터를 안전하게 모으고 데이터베이스를 업데이트하기 위해 message_param을 사용한다.

여기서 /messages/new로의 링크를 만들기 위해 link_to를 사용했다. <a> 엘리먼트를 하드코딩하는 대신 링크를 제너레이트하기위해 link_to를 사용할 수 있다:

  • 첫번째 패러미터는 링크 텍스트이다.
  • 두번째 패어미터는 URL이다.

축하한다! 메시지를 저장하기위해 데이터베이스를 사용하는 메시징앱을 만들었다. 여기까지 일반화할 수 것은 무엇인가?

  • 모델은 데이터베이스 안의 테이블을 기술한다.
  • 마이그레이션은 새로운 테이블로 데이터베이스를 업데이트하거나 기존의 테이블을 변경하는 방법이다.
  • 레일즈는 데이터를 보여주거나 만드는 등의 그런 일반적인 일을 하기위해 7가지 표준 컨트롤러 액션을 제공한다.
  • 데이터는 ERB 웹 템플레이팅을 사용하여 뷰에서 보여질 수 있다.
  • 데이터는 웹 폼을 이용하여 데이터베이스 안에 저장될 수 있다.

  • MessageApp 전체 소스는 깃허브 저장소에 올려놓았다.

맺음말

일단 포스트가 길어져서 50% 진도 부분만을 먼저 포스팅한다. 뒷부분은 배우는 대로 포스팅할 예정이다.

Share this:

comments powered by Disqus