RoR day 4, 레일스 맛이 첨가된 루비

오늘은 튜토리얼의 4번째 장 Rails-flavored Ruby(레일스맛이 나는 루비?)를 따라가 보겠습니다. 이번 장은 레일스의 기능을 이해하기 위해 필요한 루비 기초지식을 주내용으로 합니다.

지난 번 만들었던 sample_app을 조금 더 개선해 보려고 합니다. layout을 사용해서 뷰의 중복을 줄였던 부분이 있었습니다. 그 layout 파일에는 PageController의 변수(title) 설정에 따라 title 태그를 만들어주는 부분이 있었습니다.

<title>Ruby on Rails Tutorial Sample App | <%= @title %></title>

이 부분을 개선해 보겠습니다. PageController에서 title 변수에 값을 설정하지 않는 경우에도 title 태그가 설정된 기본값으로 표시되게 만들고 싶습니다. 레일스의 helper를 사용하려고 합니다. helper는 뷰에서 반복적으로 사용되는 기능을 모은 것이라고 볼 수 있습니다.

module ApplicationHelper

  # Return a title on a per-page basis.
  def title
    base_title = "Ruby on Rails Tutorial Sample App"
    if @title.nil?
      base_title
    else
      "#{base_title} | #{@title}"
    end
  end
end

이렇게 helper 클래스에 title 이라는 메소드를 추가했습니다. 이 메소드는 layout 파일에서 아래와 같이 사용됩니다.

<title><%= title %></title>

보다시피 변수를 참조하던 것(<%= @title %>)이 메소드 호출(<%= title %>)로 변경되었습니다.  helper 클래스의 title 메소드가 어떻게 동작하는지는 스타일시트에 대해 잠깐 돌아본 뒤 설명해 드리겠습니다.

스타일 시트(CSS, Cascading Style Sheet)를 연결하는 것은 아주 쉽습니다. 우선 스타일 시트 파일을 public/stylesheets 디렉토리에 위치시킵니다. 그리고 뷰 파일에서 직접 link 태그를 통해 연결을 해도 되고 아래처럼 레일스의 함수를 사용할 수도 있습니다.

    <%= stylesheet_link_tag 'blueprint/screen', :media => 'screen' %>
    <%= stylesheet_link_tag 'blueprint/print',  :media => 'print' %>

보기에 좀 이상할 수 있지만 잠시 뒤 루비를 설명하며 말씀드리겠습니다.


루비를 간단하게 테스트할 수 있는 방법으로 콘솔(console)이 있습니다.

$ rails console
Loading development environment (Rails 3.0.3)

콘솔로 들어왔습니다! IRB(Interactive RuBy Shell) 환경과 유사하며 스크립트 언어로서 대화식으로 프로그램을 만들 수 있습니다. 짧게 rails c 라고 해도 됩니다 🙂 몇 가지 루비의 특징을 나열식으로 설명하겠습니다.

  • comments : 루비의 주석은 #으로 시작합니다.
  • strings : 문자열은 프로그램이 다루는 주된 자료형의 하나로서 루비는 편리하게 사용할 수 있는 다양한 기능을 제공합니다. 문자열은 기본적으로 쌍따옴표(double-quote, “)으로 묶인 리터럴 입니다.
    • 여러 개의 문자열을 합치기 : “foo” + “bar” => “foobar”가 됩니다. “#{first_name} #{last_name}”도 first_name 변수의 값이 foo 이고 last_name 변수의 값이 bar 라면 동일한 결과를 나타냅니다.
    • 문자열 출력하기 : puts, print 함수를 사용할 수 있습니다.
    • 홑따옴표(단일 인용부호, single-quete) 문자열은 쌍따옴표 문자열과 대부분 동일합니다. 차이점은 ‘#{first_name} #{last_name}’에서는 문자열 합치기가 발생하지 않습니다. 있는 그대로의 문자열이 값이 됩니다.
  • obejcts and message passing : 루비에서 모든 것은 객체 입니다. nil도 하나의 객체입니다. 객체가 가진 정보를 알아내기 위해서 또는 객체에 어떤 정보를 전달하기 위해서 메시지를 보낼 수 있습니다. 간단한 예제를 살펴보겠습니다.
    • 문자열의 길이 알아내기 : “foobar”.length 는 6이라는 값을 반환합니다. 이 값은 문자열 “foobar”의 길이입니다.
    • 문자열이 비어있는지 확인하기 : “foobar”.empty? 는 false 값을 반환합니다. “”.empty? 는 true 값을 반환합니다.
    • 문자열로 바꾸기 : 12345.to_s 는 “12345” 문자열을 반환합니다.
    • nil인지 확인하기 : “foo”.nil? 은 false를 반환합니다. nil.nil? 은 true를 반환합니다. 이를 통해 어떠한 객체가 존재하는지 여부를 확인할 수 있습니다.
  • method : 메소드 정의는 def ~ end 절로 묶인 statement 입니다.
    • 루비 메소드의 특징으로는 암묵적인 반환(implicit return)이 있습니다. 메소드에 별도의 return이 없을 경우 마지막 문(statement)의 값이 그 메소드의 리턴값이 됩니다.
    • 다음은 예제입니다.
      def string_message(string)
      if string.empty?
      “It’s an empty string!”
      else
      “The string is nonempty.”
      end
      end

이제 title helper를 이해할 수 있을 것 같습니다. title 메소드는 메소드 정의, 변수 값 배정, 부울 검사, 흐름 제어(if ~ else ~ end), 문자열 합치기 등의 요소들로 이루어져 있습니다. 루비에서 사용하는 자료형에 대해 조금 더 알아보겠습니다.

  • arrays : 배열은 순서로 접근할 수 있는 객체들의 집합입니다. 대괄호([])로 객체를 만들 수 있습니다.
    • 생성 : a = [2001, 9, 11, “New York”]
    • 접근 : 배열의 인덱스를 통해 각 요소에 접근할 수 있습니다. a[0], a[1], a[-1]
    • 길이 : 배열에 length 메소드를 호출합니다. a.length == 4
    • 정렬 : 배열이 비교가능한 요소로만 이루어져 있다면 a.sort
    • 순서 뒤집기 : a.reverse
    • 순서 섞기 : a.shuffle
    • 배열에 추가하기 : a << “ball”
  • ranges : range는 범위를 나타내는 객체입니다. 시작..끝 이렇게 사용합니다.
    • 배열로 전환하기 : 0~9까지의 숫자가 포함되어 있는 배열을 만듭니다. (0..9).to_a 문자에 대해서도 가능합니다. (‘a’..’e’).to_a
  • blocks : array와 range의 메소드는 블록(block)을 전달받아 처리할 수 있습니다. 블록은 다른 프로그래밍 언어에서 찾아보기 힘든 루비만의 특징 중 하나입니다. 블록은 중괄호(curly brace, {})로 표현됩니다.
    • 예제(each 메소드) : 다음의 코드는 2, 4, 6, 8, 10을 출력합니다. each 메소드는 배열이나 범위(range)의 각 요소를 순서대로 블록에 전달합니다.
      (1..5).each { |i| puts 2 * i }
    • 위의 코드는 아래와 동일합니다.
      (1..5).each do |i|
      puts 2 * i
      end
    • 이외에도 다양한 방법으로 블록을 사용할 수 있습니다. 알아서 찾아보세요.
  • hashes : 해시는 다양한 타입의 객체가 인덱스로 사용될 수 있는 배열입니다. 사전(dictionary) 타입이라고도 불리웁니다. 키와 값의 연관배열(associated array)이라고도 지칭합니다.
    • 생성 : 비어있는 해시를 만듭니다. user = {}
      값과 함께 해시 만들기. user = { “first_name” => “Michael”, “last_name” => “Hartl” }
    • 추가 : 해시에 새로운 요소를 추가하거나 기존에 있는 값을 교체합니다. user[“first_name”] = “Michael”
    • 참조 : 해시의 참조는 키를 통해 이루어 집니다. user[“first_name”] == “Michael”
    • 키 가져오기 : 배열로 이루어진 해시의 키를 가져옵니다. user.keys
    • 값 가져오기 : 배열로 이루어진 해시의 키를 가져옵니다. user.values
  • symbols : 해시의 키로써 심볼을 사용할 수 있습니다. 심볼은 콜론(:)으로 시작하는 문자열 같습니다(:name).
    • 예제 : flash = { :success => “It worked!”, :error => “It failed. :-(” }

CSS 다시 보기

스타일 삽입 부분을 다시 보겠습니다.

<%= stylesheet_link_tag 'blueprint/screen', :media => 'screen' %>
<%= stylesheet_link_tag 'blueprint/print',  :media => 'print' %>

stylesheet_link_tag는 함수입니다. 첫 번째 인수로 스타일시트의 위치가 전달되었습니다. 두 번째 인수는 해시를 사용하고 있습니다. 어떠한 매체에 적용될 것인지 결정하는 것 같습니다. 두 가지를 알 수 있습니다. 우선 루비에서는 함수를 호출할 때 괄호가 필수사항이 아닙니다. 아래의 두 문장은 같습니다.

stylesheet_link_tag('blueprint/screen', :media => 'screen')
stylesheet_link_tag 'blueprint/screen', :media => 'screen'

두 번째로 마지막 인자로 해시가 사용될 때 중괄호를 생략할 수 있습니다.

stylesheet_link_tag 'blueprint/screen', { :media => 'screen' }
stylesheet_link_tag 'blueprint/screen', :media => 'screen'

이 함수의 결과로 HTML 파일을 보면 아래와 같이 스타일시트 삽입부가 생성되어 있는것을 볼 수 있습니다.

<link href="/stylesheets/blueprint/screen.css" media="screen" rel="stylesheet" type="text/css" />
<link href="/stylesheets/blueprint/print.css" media="print" rel="stylesheet" type="text/css" />

 

루비에서의 클래스

루비에서의 모든 것은 객체 입니다. 객체를 만들기 위해 클래스를 사용합니다.

  • constructors : 클래스로 새로운 객체를 만듭니다. 보통 클래스명.new 메소드로 객체를 생성합니다. 문자열(String) 클래스나 배열, 해시와 같이 리터럴로 new 메소드를 대신하는 경우도 있습니다.
    • 문자열 객체를 예로 들어보겠습니다. 아래의 두 문장은 동일한 문자열 객체를 만들어냅니다.
  • s = "foobar"
    s = String.new("foobar")
    s.class
  • class inheritence : 클래스는 상속을 통해 기능을 전달받을 수 있습니다. 상속은 클래스 사이의 계층 구조를 만들어 냅니다.
    • 문자열 클래스의 상속을 예로 들어보겠습니다.
      class Word < String
      def palindrome?
      self == self.reverse # self is the string itself.
      end
      end
  • modifying built-in classes : 내장된 클래스에 기능을 추가하거나 변경할 수 있습니다. 위에 제시된 Word 클래스는 palindrome(회문) 이라는 하나의 메소드만을 가지고 있습니다. 메소드가 하는 일도 문자열을 뒤집은 뒤 원래의 문자열과 비교하는 것 뿐입니다. 문자열 클래스에 원래 있던 것 처럼 하고 싶어졌습니다.
    • 문자열 클래스에 새로운 메소드를 추가해 보겠습니다.
      class String
      # Return true if the string is its own reverse.
      def palindrome?
      self == self.reverse
      end
      end
    • 이렇게 추가된 메소드는 “eye”.palindrome? 과 같이 원래 문자열 클래스의 메소드였던 것 처럼 사용할 수 있습니다.
    • 내장 클래스를 확장/수정하는 것은 매우 강력할 수 있지만 그만큼 조심해야합니다. rails에서도 String 클래스에 blank? 메소드를 추가한 것을 확인할 수 있습니다.
  • controller class
    • 멀리 돌아왔습니다. 다시 PageController를 보며 이번 장에서 새로 배운 내용을 정리해 봅시다.
    • 이번 장에서 알게 된 몇 가지를 정리하면
      • 클래스와 상속
      • 메소드
      • 루비의 내장객체(String, Array, Range, Hash, Symbol)
  • class PagesController < ApplicationController
    
      def home
        @title = "Home"
      end
    
      def contact
        @title = "Contact"
      end
    
      def about
        @title = "About"
      end
    end

User Class

하나의 완전한 루비 클래스를 만들어 보겠습니다.

class User
  attr_accessor :name, :email

  def initialize(attributes = {})
    @name  = attributes[:name]
    @email = attributes[:email]
  end

  def formatted_email
    "#{@name} <#{@email}>"
  end
end

새로운 몇 가지를 설명하겠습니다.

  • attr_accessor 는 변수에 접근할 수 있게 해주는 키워드입니다. 위에서는 name 변수와 email 변수에 대한 접근자를 생성하게 됩니다.
  • initialize는 생성자입니다. User.new를 호출할 때 사용됩니다.

rails console로 간단히 테스트 해 보겠습니다.

>> require './example_user'     # This is how you load the example_user code.
=> ["User"]
>> example = User.new
=> #<User:0x224ceec @email=nil, @name=nil>
>> example.name                 # nil since attributes[:name] is nil
=> nil
>> example.name = "Example User"           # Assign a non-nil name
=> "Example User"
>> example.email = "user@example.com"      # and a non-nil email address
=> "user@example.com"
>> example.formatted_email
=> "Example User <user@example.com>"

많은 내용은 대충 전달해서 죄송합니다. 너무 늘어지면 아예 중단될 것 같아서요 🙂 봐주셈.

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다