mm Home

Rails 기본 본문

기타

Rails 기본

jess_m 2017. 9. 12. 17:04

루비온레일즈 가이드 홈페이지 내용을 공부하면서 정리한 내용이다. (http://rubykr.github.io/rails_guides/)


Rails 철학

레일즈란? 루비로 쓰여진 웹 어플리케이션 프레임워크.

같은 것을 반복하지 말 것(Don't Repeat Yourself: DRY)
설정보다 규정을 우선한다(Convention Over Configuration) : 기존의 경험이나 관습에 기초해, 각 설정들의 기본값을 정해두고 있다. 모든 의견을 수용하기 위해 자유롭게 설정할 수 있는 스프링과 달리 독단적으로 결정된 기본값이 있다. (설정이 쉬움)




 

Rails 기본 구조


IDE에서 rails 프로젝트를 신규 생성하면 아래와 같은 구조를 가지는 프로젝트가 생성된다.  (rails new 명령어를 이용해 생성됨)

gem : 루비에서 지원하는 패키지 시스템 (루비 라이브러리)
bundler : 번들러는 필요한 정확한 gem과 버전을 추적하고 설치하여 루비 프로젝트를 위한 일관된 환경을 제공. 번들러는 의존성 지옥에서 벗어나게 하고, 필요한 gem이 개발, 스테이징, 프로덕션에 있는지 확인해 준다.

Scaffold : 특정 Model을 생성하면서 CRUD 기반의 View, Controller를 한 번에 만들어주는 명령어

Rake : 레일즈가 여러 목적으로 사용하는 범용 명령 실행 도구
rails 폴더 구조

 

파일/폴더
목적
app/애플리케이션 컴포넌트를 담고 있다. 뷰, 모델, 컨트롤러
config/rails 어플리케이션의 설정(라우팅, 데이터베이스 등)
db/RDB를 접근하는 모델 & RDB 관리 스크립트 & 마이그레이션 내용
Gemfile
Gemfile.lock
gem의 의존성을 관리하는 파일 (bundler 에서 사용)
lib/애플리케이션에서 사용하는 확장 모듈
log/애플리케이션의 로그
public/이 폴더의 밑에 있는 파일들은 외부(인터넷)에서 직접 참조할 수 있다. 정적인 파일이나 컴파일된 애셋들이 위치한다.
RakefileMakefile과 비슷함. rails 코드를 빌드, 패키징, 테스트하는데 쓰인다.
test/Unit 테스트 관련
tmp/임시 파일 폴더
vendor/3rd party 벤더들이 제공하는 외부 라이브러리 디렉토리

 

 

Scaffold : 특정 Model을 생성하면서 CRUD 기반의 View, Controller를 한 번에 만들어주는 명령어

  • 이 명령어 한 번에 소규모 게시판은 바로 만들어진다  (물론 view의 레이아웃이나 CSS, Controller의 API 호출이나 세부적인 사항은 개발자가 직접 해야하긴 함)


$ rails generate scaffold Post title:string author:string contents:text


scaffold generate 는 아래와 각 디렉토리에 15개의 파일을 생성한다

 

파일 
목적
db/migrate/20100207214725_create_posts.rb 데이터베이스에 ‘posts’ 테이블 생성하는 마이그레이션 (여러분의 파일 이름은, 다른 타임 스템프 값을 가지고 있습니다.)
app/models/post.rb Post 모델
test/fixtures/posts.yml 테스트를 위한 더미(Dummy) posts
app/controllers/posts_controller.rb Posts 컨트롤러
app/views/posts/index.html.erb 모든 posts 를 출력하는 index 뷰
app/views/posts/edit.html.erb 존재하는 post 를 수정하는 edit 뷰
app/views/posts/show.html.erb 단일 post를 보여주는 show 뷰
app/views/posts/new.html.erb 새로운 post 를 만들기 위한 new 뷰
app/views/posts/_form.html.erb post 를 수정하거나 새로 만드는데 사용되는 폼(form)을 저장하는 조각(partial) 파일
app/helpers/posts_helper.rb post 뷰를 위한 헬퍼(Helper) 함수를 위한 파일
test/unit/post_test.rb posts 모델을 위한 유닛 테스트 파일
test/functional/posts_controller_test.rb posts 컨트롤러를 위한 기능 테스트 파일
test/unit/helpers/posts_helper_test.rb posts 헬퍼(Helper)를 위한 유닛 테스트 파일
config/routes.rb posts 를 위한 라우팅 정보를 담은 수정된 라우팅 파일
public/stylesheets/scaffold.css 발판(Scaffold) 뷰를 좀 더 미려하게 만드는 CSS 파일



 



Routing

rails 의 라우터는 요청받은 URL을 인식하여 적절한 컨트롤러의 액션에 매핑한다. 라우터는 URL을 직접 매핑하거나 관습을 통한 자동 매핑도 지원한다.

http://guides.rorlab.org/routing.html


  • 직접 매핑

    GET /patients/17

    위 HTTP 요청에 대하여 직접 매핑은 아래와 같이 할 수 있다

    get '/patients/:id', to: 'patients#show'

    이 요청은 patients 컨트롤러의 show 액션에 할당되며, params에는 { id: '17' } 해시가 포함된다

     

  • 리소스 기반 자동 라우팅 : Rails 기본
    리소스 기반의 라우팅(이하 리소스 라우팅)을 사용하는 것으로 공통 라우팅을 간단하게 선언할 수 있다. RESTful한 라우팅을 선언하는 것으로 컨트롤러의 index, show, new, edit, create, update, destroy 액션을 별도로 선언하지 않고 한줄로 완료할 수 있다.

    resources :photos

    라고 라우팅 설정을 했다고 가정하면

    HTTP 메서드
    경로
    컨트롤러#액션
    목적
    GET/photosphotos#index모든 사진 목록을 표시
    GET/photos/newphotos#new사진을 1개 생성하기 위한 HTML 양식을 반환
    POST/photosphotos#create사진을 1개 생성
    GET/photos/:idphotos#show특정 사진을 보여줌
    GET/photos/:id/editphotos#edit사진 편집용의 HTML 양식을 반환
    PATCH/PUT/photos/:idphotos#update특정 사진을 갱신
    DELETE/photos/:idphotos#destroy특정 사진을 삭제

    위와 같은 기본 설정이 관습에 의해 생성된다.

    • 자동 라우팅을 통해 URL용 헬퍼가 지원된다
      예)  resources :photos  를 통해 예를 들어보면,

      helper path (*_path Helper)
      실제 path
      phots_path/photos
      new_photo_path/photos/new
      edit_photo_path(:id)/photos/:id/edit
      photo_path(:id)/photos/:id
        

       

    • 복수의 리소스를 동시에 정의할 수 있다

      resources :photos, :books, :videos

      위의 설정은 아래의 설정과 동일하다

      resources :photos
      resources :books
      resources :videos
    • 단수형 리소스
      상황에 따라서 ID 참조가 필요없는 리소스가 필요할 때가 있다. 예를 들어 /profile 에서는 항상 현재 로그인한 유저의 정보를 보여주고, 다른 사용자의 id를 참조할 필요가 없다.
      이런 경우는 아래와 같이 라우팅 설정을 할 수 있다 (단일 설정)

      get 'profile', to: 'users#show'


      단수형에 대한 RESTful 한 리소스 설정은 아래와 같다.  (resources → resource)

      resource :geocoder

      단수형에 대해서는 6개의 라우팅을 생성한다

      HTTP 메서드
      경로
      컨트롤러#액션
      목적
      GET/geocoder/newgeocoders#newgeocoder 작성용 양식을 반환
      POST/geocodergeocoders#creategeocoder를 생성
      GET/geocodergeocoders#show하나뿐인 geocoder 리소스를 표시
      GET/geocoder/editgeocoders#editgeocoder 수정용 HTML 양식을 반환
      PATCH/PUT/geocodergeocoders#update하나뿐인 geocoder 리소스를 갱신
      DELETE/geocodergeocoders#destroygeocoder 리소스를 삭제

      *단수형 리소스 컨트롤러와 복수형 리소스 컨트롤러는 같은 컨트롤러에 할당된다. ( resources :photo 와 resource :photo 는 같은 컨트롤러 PhotosController에 할당된다)

      단수형 헬퍼 메소드는 아래와 같이 3개가 생성된다

      helper path (*_path Helper)
      실제 path
      new_geicider_path/geocoder/new
      edit_geocoder_path/geocoder/edit
      geocoder_path/geocoder
    • 네임스페이스를 통한 라우팅
      다수의 관리용 컨트롤러들을 묶어서 특정 라우팅 path에 두도록 하고 싶을 것이다. (ex. admin/geocoder/~,   admin/photos/~,   admin/books/~)
      이런 경우는 아래와 같이 네임스페이스 설정을 통해 특정 path를 강제할 수 있다

      namespace :admindo
        resources :posts, :comments
      end
    •  중첩된 리소스
      아래와 같은 모델이 있다고 가정해보자.

      classMagazine < ActiveRecord::Base
        has_many :ads
      end
      classAd < ActiveRecord::Base
        belongs_to :magazine
      end

      매거진 내에는 다수의 Ad가 존재하게 된다. 위와 같이 리소스가 중첩되는 경우에는 아래와 같이 라우팅을 선언할 수 있다

      resources :magazinesdo
        resources :ads
      end


      HTTP 메서드
      경로
      컨트롤러#액션
      목적
      GET/magazines/:magazine_id/adsads#index잡지 1권에 포함되는 광고를 모두 표시한다.
      GET/magazines/:magazine_id/ads/newads#new어떤 잡지에 광고를 추가할 수 있는 HTML 양식을 반환한다.
      POST/magazines/:magazine_id/adsads#create어떤 잡지 1권에 잡지용의 광고를 하나 추가한다.
      GET/magazines/:magazine_id/ads/:idads#show어떤 잡지 1권에 포함되는 광고를 하나 보여준다.
      GET/magazines/:magazine_id/ads/:id/editads#edit어떤 잡지 1권에 포함되는 광고 하나를 수정할 수 있는 HTML 양식을 반환한다.
      PATCH/PUT/magazines/:magazine_id/ads/:idads#update어떤 잡지 1권에 포함되는 광고 하나를 갱신한다.
      DELETE/magazines/:magazine_id/ads/:idads#destroy어떤 잡지 한권에 포함되는 광고를 하나 삭제한다.

       

    • 최소한의 정보로 리소스를 표현하는 설정도 가능하다

      resources :postsdo
        resources :comments, only: [:index, :new, :create]
      end
      resources :comments, only: [:show, :edit, :update, :destroy]



  • Resourceful하지 않은 라우팅
    Resourcefulg하지 않은 라우팅이 필요할 경우가 있다. 이런 경우 아래와 같이 설정하면 된다. 

    get ':controller(/:action(/:id))'

    :controller    → controller 이름
    :action         → 컨트롤러에 존재하는 액션의 이름과 매칭 (메소드)
    :id                → ID
    필수가 아닌 항목에 대해서는 () 로 표현. action, id

    • 쿼리 문자열

       

      get ':controller/:action/:id'

       /photos/show/1?user_id=2


      위의 라우팅에서 들어오는 쿼리 문자열은 아래와 같이 설정 된다.

      params는 { controller: 'photos', action: 'show', id: '1', user_id: '2' }가 된다

 

 

기타 자세한 사항은 레일즈 문서 참고






 

Controller

 

컨트롤러는 요청을 해석하고 적절한 응답을 돌려줄 책임이 있다. Rails에서는 라우팅한 결과에 따라 컨트롤러가 모델과 뷰의 사이를 중개한다. 
http://guides.rorlab.org/action_controller_overview.html 

 

 

 

  • 명명 규칙
    컨트롤러의 이름은 기본적으로 복수형을 사용한다. 반드시 지켜야 하는 사항은 아니다. 다만 관습에 의한 설정을 사용하기 위해서는 복수형을 사용해야 한다.
  • 메소드와 액션
    애플리케이션이 클라이언트로부터 요청을 받으면 라우팅에 의해서 실행할 컨트롤러와 액션이 결정되고, Rails는 그 컨트롤러의 액션명과 동일한 이름을 가지는 메소드를 실행한다. 
  • 매개 변수
    • 쿼리 문자열
    • POST 데이터

    컨트롤러는 위의 두가지 매개 변수를 동일하게 params라는 해시를 통해 접근할 수 있다. 

    class ClientsController < ApplicationController
      # 이 액션에서는 쿼리 문자열 매개 변수가 사용됩니다.
      # 전송측에서 HTTP GET 요청을 사용하기 때문입니다.
      # 단 매개 변수에 접근하는 방법은 아래의 방식과 다르지 않습니다.
      # 유효한 고객 목록을 얻기 위해 이 액션의 URL은 다음과 같이 되어 있습니다.
      # clients: /clients?status=activated
      def index
        if params[:status] == "activated"
          @clients = Client.activated
        else
          @clients = Client.inactivated
        end
      end
     
      # 이 액션에서는 POST 데이터를 사용하고 있습니다.
      # 이 매개 변수는 일반적으로 사용자가 전송한 HTML 폼으로부터 생성됩니다.
      # 이것은 RESTful한 접근이며, URL은 "/clients"가 됩니다.
      # 데이터는 URL이 아닌 요청의 body에 포함되어 전송됩니다.
      def create
        @client = Client.new(params[:client])
        if @client.save
          redirect_to @client
        else
          # 아래 줄에서는 기본 랜더링 동작을 덮어씁니다.
          # 기본으로는 "create" 뷰가 랜더링됩니다.
          render "new"
        end
      end
    end
  • Strong parameters
    컨트롤러가 받은 변수에 대해 갱신을 허가하고, 어떤 설정에 대해서는 갱신을 금지할지 명시적으로 결정하기 위함이다.
    매개변수에 대해 required(필수)를 지정할 수 있으며, 400 bad request를 돌려줄 수 있다

    class PeopleController < ActionController::Base
      # 이 코드는 ActiveModel::ForbiddenAttributes 예외를 던집니다.
      # 명시적으로 검증을 하지 않고 매개 변수를 그냥 통째로 넘기고 있기 때문입니다.
      def create
        Person.create(params[:person])
      end
     
      # 이 코드는 매개 변수에 person이라는 키가 존재하는 경우에만 성공합니다.
      # person이라는 키가 없는 경우에는 ActionController::ParameterMissing 예외를 던집니다.
      # 이 예외는 ActionController::Base가 잡아 400 Bad Request로 반환합니다.
      def update
        person = current_account.people.find(params[:id])
        person.update!(person_params)
        redirect_to person
      end
     
      private
        # private 메소드를 사용해서 매개 변수 검증을 캡슐화합니다.
        # 이를 통해 create와 update에서 같은 검증을 쉽게 재사용할 수 있습니다.
        # 또한 허가할 속성을 사용자마다 다르게 만들 수도 있습니다.
        def person_params
          params.require(:person).permit(:name, :age)
        end
    end



    params.permit(:id)

    params에 :id 키가 있다면 화이트리스트 검증을 통과한다. 

     

    중첩된 매개 변수에 대해서도 아래와 같이 검증할 수 있다.

     

    params.permit(:name, { emails: [] },
                  friends: [ :name,
                             { family: [ :name ], hobbies: [] }])

     

     

  • 세션
    Rails에서는 세션 기능을 지원한다. 모든 방식은, 세션의 식별자를 쿠키에 보존하며 아래와 같은 클래스를 통해 지원한다.

    ActionDispatch::Session::CookieStore - 모든 세션을 클라이언트 측의 쿠키에 저장
    ActionDispatch::Session::CacheStore - 데이터를 Rails의 캐시에 저장
    ActionDispatch::Session::ActiveRecordStore - 액티브 레코드를 사용해서 데이터베이스에 저장(activerecord-session_store gem이 필요)
    ActionDispatch::Session::MemCacheStore - 데이터를 memcached 클러스터에 저장(이 방식은 오래되었으므로 이보다는 CacheStore를 검토하길 권장)



     
  • 세션 접근
    컨트롤러에서 session 메소드를 사용해서 접근이 가능하다

     

    class ApplicationController < ActionController::Base
     
      private
     
      # 세션에 저장되어 있는 id로 사용자를 검색합니다.
      # :current_user_id는 Rails 애플리케이션에서 사용자 로그인 정보를 다루는 일반적인 방법입니다.
      # 로그인하면 세션 값을 저장하고, 로그아웃 하면 세션 값을 삭제합니다.
      def current_user
        @_current_user ||= session[:current_user_id] &&
          User.find_by(id: session[:current_user_id])
      end
    end

     

    세션을 사용하고 싶다면, 해쉬와 비슷한 방식으로 사용이 가능하다

    class LoginsController < ApplicationController
      # "Create" a login, aka "log the user in"
      def create
        if user = User.authenticate(params[:username], params[:password])
          # 세션에 사용자 ID를 저장하여, 다음 요청에서 사용할 수 있게 합니다.
          session[:current_user_id] = user.id
          redirect_to root_url
        end
      end
    end

     

    세션에서 데이터의 일부를 제거하고 싶은 경우에는 키에 nil을 할당하면 된다.

     

    class LoginsController < ApplicationController
      # 로그인을 해제합니다(=로그아웃)
      def destroy
        # 세션 id로부터 user id를 제거
        @_current_user = session[:current_user_id] = nil
        redirect_to root_url
      end
    end

     



  • 쿠키
    cookies 메소드를 통해서 쿠키에 접근할 수 있다. 세션과 비슷한 사용방법

     

    class CommentsController < ApplicationController
      def new
        # cookie에 덧글 작성자의 이름이 남아 있다면 필드에 자동으로 입력한다.
        @comment = Comment.new(author: cookies[:commenter_name])
      end
     
      def create
        @comment = Comment.new(params[:comment])
        if @comment.save
          flash[:notice] = "Thanks for your comment!"
          if params[:remember_name]
            # 덧글 작성자의 이름을 저장
            cookies[:commenter_name] = @comment.author
          else
            # 덧글 작성자의 이름이 쿠키에 남아있다면 삭제
            cookies.delete(:commenter_name)
          end
          redirect_to @comment.article
        else
          render action: "new"
        end
      end
    end

    세션을 삭제할 때에는 키에 nil을 대입했지만, cookie를 삭제하는 경우에는 cookies.delete(:key)를 사용해야 한다.


     
  • 필터
    액션의 직전(before), 직후(after), 또는 그 둘다(around)에 실행되는 메소드이다. 필터는 상속이 가능하기 때문에 ApplicationController에 필터를 설정하면 애플리케이션의 모든 컨트롤러에 적용이 된다.

    class ApplicationController < ActionController::Base
      before_action :require_login
     
      private
     
      def require_login
        unless logged_in?
          flash[:error] = "You must be logged in to access this section"
          redirect_to new_login_url # 처리를 중지한다
        end
      end
    end


    around 사용법

    class ChangesController < ApplicationController
      around_action :wrap_in_transaction, only: :show
     
      private
     
      def wrap_in_transaction
        ActiveRecord::Base.transaction do
          begin
            yield
          ensure
            raise ActiveRecord::Rollback
          end
        end
      end
    end



  • Request 객체
    Request 객체의 주요 속성
     

    request의 속성
    목적
    host요청에 사용된 호스트 이름
    domain(n=2)호스트의 이름(TLD)의 우측으로부터 n번째 세그먼트
    format클라이언트로부터 요청받은 Content-Type
    method요청에서 사용된 HTTP 메소드
    get?, post?, patch?, put?, delete?, head?HTTP메소드가 GET/POST/PATCH/PUT/DELETE/HEAD 중 각각 맞는 메소드에 해당하는 경우 true를 돌려줌
    headers요청에 포함되어있는 헤더를 포함하는 해시를 돌려줌
    port요청에 사용된 포트 번호(정수)
    protocol사용된 프로토콜을 포함한 주소 문자열을 돌려줌(예를 들어, "http://....." 이런 형태)
    query_stringURL에서 사용된 쿼리 문자열("?" 뒷 부분)
    remote_ip클라이언트의 ip 주소
    url요청에서 사용된 URL 전체



  • Response 객체
    Response 객체를 직접 사용할 일은 거의 없다.

    response의 속성
    목적
    body클라이언트에 돌려줄 데이터의 문자열. 대부분의 경우 HTML
    status응답의 HTTP 상태 코드(200 OK, 404 file not found 등)
    location리다이렉션을 할 URL
    content_type응답의 Content-Type
    charset응답에 사용될 문자셋. 기본은 "utf-8"
    headers응답에 사용될 헤더들

 

 

 



Layout & Rendering

Controller - View - Model 사이에서 일어나는 동작에 대한 설명이다. 
http://guides.rorlab.org/layouts_and_rendering.html 

 

  • 응답 생성

    HTTP 응답의 생성방법은 3가지가 있다
    1. render 호출해서 브라우저에 돌려줄 응답 생성
    2. redirect_to 호출해서 HTTP 리다이렉트 코드를 브라우저에 돌려줌
    3. head를 호출해서 HTTP 헤더로만 구성된 응답을 생성


  • 기본 출력

    기본 설정이 되어있다는 가정 하에... (라우팅 설정, view 페이지(index.html.erb))
     

    class BooksController < ApplicationController
      def index
        @books = Book.all
      end
    end

    관습에 의해 렌더링에 대한 설정 없이도 위의 코드는 HTTP 응답을 내려준다. 
    여기서의 원칙은 액션의 마지막 부분에서 명시적인 렌더링 지시가 없을 경우에는 컨트롤러가 사용가능한 뷰 목록의 경로로부터 {action명}.html.erb 의 뷰 템플릿을 찾아 사용한다.
     
    뷰(index.html.erb) 에서는 아래와 같이 작성이 가능하다.

    <h1>Listing Books</h1>
      
    <table>
      <tr>
        <th>Title</th>
        <th>Summary</th>
        <th></th>
        <th></th>
        <th></th>
      </tr>
      
    <% @books.each do |book| %>                       ## for문을 통해 모든 model에 대하여 출력
      <tr>
        <td><%= book.title %></td>
        <td><%= book.content %></td>
        <td><%= link_to "Show", book %></td>
        <td><%= link_to "Edit", edit_book_path(book) %></td>
        <td><%= link_to "Remove", book, method: :delete, data: { confirm: "Are you sure?" } %></td>
      </tr>
    <% end %>
    </table>
      
    <br>
      
    <%= link_to "New book", new_book_path %>

    실제 렌더링 작업은 ActionView::TemplateHandlers의 서브클래스에서 실행된다.

     

  • render 사용하기
    대부분의 경우, ActionController::Base#render 메소드가 브라우저에 애플리케이션의 내용을 출력을 출력하는 일을 담당한다. render 메소드는 다양한 방법으로 커스터마이즈할 수 있다. Rails 템플릿의 기본 뷰를 출력할 수도 있고, 특정 템플릿, 파일, 인라인 코드를 지정해서 출력하거나, 아무것도 출력하지 않는 것도 가능하다



    • 아무것도 출력하지 않을 경우 

      render nothing: true



    • Action View 출력

      기본 설정과 다른 템플릿을 지정이 가능하다.

      defupdate
        @book= Book.find(params[:id])
        if@book.update(book_params)
          redirect_to(@book)
        else
          render "edit" ##문자열 대신 심볼도 가능
        end
      end



    • 다른 컨트롤러의 템플릿 사용

      render "products/show"

      Rails는 경로에 / 가 포함되어있으면 다른 컨트롤러에 속해있는 템플릿이라고 인식한다. (Rails 2.2 이하에서는 template: 키워드가 필수)

    • 텍스트 출력

      render plain: "OK"

      plain 옵션을 통해 문자열을 그대로 브라우저에 전송할 수 있다

      *기본적으로 plain 옵션은 레이아웃을 무시하고 렌더링된다. 레이아웃을 포함해서 렌더링 하고 싶은 경우에는 layout: true 옵션을 추가하면 된다.

       

    • JSON 렌더링

       

      render json: @product

      json 옵션을 추가하면 to_json 메소드를 추가하지 않아도 된다. 

    • render 옵션
      • :content_type
        Rails의 기본 출력 MIME content-type은 text/html 이다. (JSON, xml 제외) content-type을 변경하고 싶은 경우에 사용한다. 
      • :layout
        layout 옵션을 통해 현재의 액션에서 특정 파일을 레이아웃으로 사용할 수 있다 
      • location
        HTTP의 Location 헤더를 설정할 수 있다 
      • status
        HTTP 상태코드를 변경할 수 있다 
    • layout 사용
      Rails는 레이아웃이 app/views/layouts에 있는지 확인한다. (ex. PhotosController 클래스의 액션을 렌더링할 경우, photos.html.erb가 있는지를 확인)
      Rails는 컨트롤러나 액션별로 특정 레이아웃을 더 정확하게 지정할 수 있다.

    • 컨트롤러 레이아웃 지정

      classProductsController < ApplicationController
        layout "inventory"
        #...
      end

       

      아래와 같이 ApplicationController에 레이아웃 설정을 한다면, 어플리케이션 전체에 걸친 기본 레이아웃을 설정하게 된다.

       

      class ApplicationController < ActionController::Base
        layout "main"
        #...
      end

       


      실행시에 레이아웃을 지정하고 싶다면 아래와 같이 심볼을 이용하면 된다. 

       

       class ProductsController < ApplicationController
        layout :products_layout
        
        def show
          @product = Product.find(params[:id])
        end
        
        private
          def products_layout
            @current_user.special? ? "special" : "products"
          end
        
      end

       

      이외에도 메서드명을 이용하여 조건부로 레이아웃을 사용이 가능하며, 컨트롤러 상속시에 레이아웃 설정도 그대로 상속된다. (자세한 내용은 공식 문서 참고)

       

       

       

    • redirect_to 사용

      redirect_to photos_url
      redirect_to action: :index
    • 본문이 없는 응답
      body가 없는 응답을 전송할 수 있다. HTTP 상태 코드를 표현하는 심볼을 넘길 수 있다.

      head :bad_request
      head :created, location: photo_path(@photo)



  • 레이아웃 구성
    레이아웃을 구성하기 위해 3가지 도구를 사용한다.
    • Asset tags
    • yield와 content_for
    • Partials

  • Asset Tags

    피드, JavaScript, 스타일시트, 이미지, 동영상과 음성의 링크를 위한 HTML 생성용으로 아래 6개의 애셋 태그 헬퍼를 사용 할 수 있다. (상세 내용은 문서를 참고)

    auto_discovery_link_tag
    javascript_include_tag
    stylesheet_link_tag
    image_tag
    video_tag
    audio_tag
     

  •  yield
    레이아웃에서 뷰에 삽입해야할 장소를 지정할 때 사용한다.
    기본적인 사용법으로 yield는 하나만 사용하고, 지정된 뷰의 컨텐츠 전체를 그 위치에 삽입한다.
     
    아래와 같이 yield를 여러 곳에서 호출하는 레이아웃 작성도 가능하다

    <html>
      <head>
      <%= yield :head %>
      </head>
      <body>
      <%= yield %>
      </body>
    </html>

    (뷰의 메인 본문은 이름이 없는 yield에 삽입)
     

  •  content_for
    content_for 메소드를 사용하면, 컨텐츠를 이름이 붙은 yield 블록으로 호출해 레이아웃에 삽입할 수 있다.

    <% content_for :head do %>
      <title>A simple page</title>
    <% end %>
     
    <p>Hello, Rails!</p>

    content_for 메소드는 레이아웃이 'sidebar', 'footer'같은 영역으로 분리되어있고, 각각 다른 컨텐츠를 삽입하고 싶은 상황에서 사용한다. 

    결과는 아래와 같다.

    <html>
      <head>
      <title>A simple page</title>
      </head>
      <body>
      <p>Hello, Rails!</p>
      </body>
    </html>



  •  Partial
    위와는 다른 방법으로 렌더링을 편하게 만들기 위함이다. 

    뷰 내에서 render 메서도를 호출해서 사용할 수 있다.

    <%= render "menu" %>

     

    예시
    users/index.html.erb

    <%= render "shared/search_filters", search: @q do |f| %>
      <p>
        Name contains: <%= f.text_field :name_contains %>
      </p>
    <% end %>

    roles/index.html.erb

    <%= render "shared/search_filters", search: @q do |f| %>
      <p>
        Title contains: <%= f.text_field :title_contains %>
      </p>
    <% end %>

    shared/_search_filters.html.erb

    <%= form_for(@q) do |f| %>
      <h1>Search form:</h1>
      <fieldset>
        <%= yield f %>
      </fieldset>
      <p>
        <%= f.submit "Search" %>
      </p>
    <% end %>

     

    뷰 템플릿에 존재하는 이 코드는 그 장소(디렉토리)에서 _menu.html.erb라는 이름의 파일을 렌더링한다. 관습에 의해 파샬 파일명은 항상 언더스코어로 시작된다. 

    *모든 페이지에서 공유되는 컨텐츠라면 파셜을 레이아웃에서 직접 사용해도 좋다. 다만, 파샬 파일을 레이아웃 디렉토리에 둘 수 없다.  
     

    • 지역 변수 넘겨주기

      <h1>New zone</h1>
      <%= render partial: "form", locals: {zone: @zone} %>
    •  모델 객체를 파샬로 렌더링하기

      <%= render @customer %>

       

      위의 렌더링 설정은 _customer.html.erb를 통해 렌더링 될 것이다.
       

    • 컬렉션 렌더링하기

      <h1>Products</h1>
      <%= render partial: "product", collection: @products %> 

      _product.html.erb

      <p>Product Name: <%= product.name %></p>


      위의 렝더링 설정은 아래와 같이 줄여 쓸 수 있다. (파일명이 _product.html.erb 똑같다면)

      <h1>Products</h1>
      <%= render @products %>

    기타 자세한 사항은 레일즈 문서 참고

 

 

 

 

 

 

Rails 커맨드 

Rails를 사용하면서 중요한 명령어가 몇가지 있다. 

 

  • rails console
  • rails server
  • bin/rails
  • rails generate
  • rails dbconsole
  • rails new app_name

 

rails new : 새로운 rails 어플리케이션을 생성하는 방법

rails server : rails 에 포함되어있는 Puma라는 웹서버가 실행된다. 어플리케이션 서버를 띄울 때 사용한다.
                     -e 로 environment 설정을, -p로 포트설정을 할 수 있다. 
rails generate : generate 명령으로 템플릿을 사용하여 다양한 코드를 생성할 수 있다. boilerplate code를 작성할 필요가 없어진다. 

  • controller
    Usage: rails generate controller NAME [action action] [options]
    컨트롤러, 뷰 파일, 테스트 파일, 뷰 헬퍼, JavaScript, CSS 파일을 자동 생성해준다.
  •  model
    Usage: rails generate model NAME [field[:type][:index] field[:type]

    scaffold 로 모델, 모델을 위한 마이그레이션, 컨트롤러, 뷰, 테스트 코드 를 모두 포함하여 생성이 가능하다. 
    scaffold로 생성하는 경우, 제너레이터는 모델, 컨트롤러, 헬퍼, 레이아웃, 기능 테스트, 유닛 테스트, 스타일시트용의 데이터가 존재하는지를 체크하고, 뷰, 컨트롤러, 모델, 마이그레이션을 생성하고, 이 리소스를 가리키는 라우팅을 추가하고 마지막으로 이 모든 것을 위한 테스트를 생성한다
    그리고 migrate를 실행하여 마이그레이션을 적용해야 한다. (테이블 변경점을 적용)
    rails db:migrate 

rails console : 커맨드라인을 통해 Rails 어플리케이션을 직접 다룰 수 있다. 사용법은 IRB와 동일

rails dbconsole : 적절한 DBMS 를 찾고, DB 커맨드라인을 실행한다. (SQL)

rails destroy : generate 와 반대. 제너레이터 명령이 무엇을 실행했는지 확인하고, 그 이전 상태로 되돌려준다.



기타 자세한 내용은 문서를 참고.

 

 

 

 

 

 

Error handling

 

  • begin/rescue block

    begin
      do_something
    rescue
      handle_exception
    end
  • Around filter

    class ApplicationController < ActionController::Base
      around_action :handle_exceptions
     
      private
      def handle_exceptions
        begin
          yield
        rescue NoPermissionError
          redirect_to 'permission_error'
        end
      end
    end
  • rescue_form

    class ApplicationController < ActionController::Base
     rescue_from 'NoPermissionError' do |exception|
       redirect_to 'permission_error'
     end
    end
  •  exceptions_app

     

 

 

 

Caching

저레벨 캐싱 : 특정 값이나 쿼리의 결과만을 캐싱하고 싶은 경우에 사용. 블록을 인수로 넘기면 블록의 실행 결과가 주어진 키에 대해서 캐싱된다.

Rails.cache.fetch 메소드를 사용


class Product < ApplicationRecord  def competing_price
    Rails.cache.fetch("#{cache_key}/competing_price", expires_in: 12.hours) do
      Competitor::API.find_price(id)
    end
  end
end


위에서는 #{cache_key}를 사용하고 있다.  

 

 

 

 

 

기타

validate

:rails 는 validates 키워드를 통해 데이터를 저장할때, 유효성 검사 메소드를 지원한다

class Person < ApplicationRecord
  validates :name, presence: true
end
  
Person.create(name: "John Doe").valid? # => true
Person.create(name: nil).valid? # => false


validation trigger method

  • create
  • create!
  • save
  • save!
  • update
  • update!


skipping validations

  • decrement!
  • decrement_counter
  • increment!
  • increment_counter
  • toggle!
  • touch
  • update_all
  • update_attribute
  • update_column
  • update_columns
  • update_counters

 

! : 메소드가 객체의 상태를 수정한다는 것을 의미

? : boolean을 리턴하는 true, false 를 묻는 메소드를 의미



respond_to

respond_to 는 rails 메소드. 해당 함수를 통하는 호출이 있었을 때, 어떻게 반응하도록 하겠는가를 정한다

def index
  @people = Person.all
  respond_to :html, :js
end


위의 코드처럼 응답 요청에 대해서 whitelist화를 할 수 있다. (MIME html, js 요청에 대해서만 응답을 함)
 

아래처럼 block 처리를 이용할 수도 있다.

def index
  @people = Person.all

  respond_to do |format|
    format.html { redirect_to(person_list_url) }
    format.js
    format.xml { render xml: @people }
  end
end



 

 

* ActiveRecord에서 scope
Active record로 DB에서 데이터를 조회할 때 조회 범위를 줄여준다거나 필터링 할때 주로 사용한다. (자주 사용되는 쿼리를 지정할 수 있다)


'기타' 카테고리의 다른 글

Ruby 기본 문법  (0) 2017.09.12
Comments