2013년 1월 14일 월요일

Action View Form Helper (2)

이 동영상은 지난 2013년 1월12일 제14회 ROR Lab. 시즌2 오프라인 강좌의 스크린캐스트를 편집하여 제작한 것이다. 이번 모임은 역삼동 PMK빌딩 강의실에서 진행되었으며, 채종원님이 강의실을 준비해 주었다. 윤승준, 조재우, 정창훈, 변준호, 정봉근, 임대림, 채종원님이 참석했다.  


RailsGuide의 "Action View Form Helpers" 후반부 내용에 대해서 강의했고, 마지막에는 Customizing Form Builder에 대한 live coding을 실제 터미널에서 코딩을 해가면서 진행하였다. 이 부분에 대한 동영상은 저작권문제로 Youtube에서 참석자에게만 공유하기로 했다.

당일 강의의 핵심내용은 Form Builder를 customizing하는 것이었다.
이와 관련하여 Formtastic, Simple_form 과 같은 유명한 젬이 있지만, Railscasts.com의 Ryan이 강의한 내용을 중심으로 직접 자신의 기호에 맞게 Form Builder를 subclassing하여 Form builder의 메소드를 overriding하는 방법을 소개하였다.



2013년 1월 4일 금요일

Action View Form Helper (1)



본 스크린캐스트는 지난 2012년 12월 29일에 온라인으로 강의했던 내용이다.
ROR Lab.에서는 Ruby on Rails 공식 홈페이지에서 제공하는 RailsGuide 내용을 교재로 격주로 강좌를 해오고 있다. 레일스를 처음시작하는 국내 개발자들을 위해서 2011년 12월에 첫 강좌를 오프라인으로 시작하였으며, 최근에는 온라인으로 강좌를 전환하여 진행하고 있다. 본 강의내용은 Action View Form Helper에 대한 것이다. 다음 강좌는 2013년 1월12일에 오프라인으로 진행할 예정이다.

2012년 12월 31일 월요일

Ruby Symbols

루비 전문프로그래머들도 symbol에 대한 개념적인 설명을 약간씩 달리하고 있다. 따라서, 루비를 처음 접하는 초보자의 경우에는, symbol이라는 특수한 객체에 대한 개념을 이해하기가 여간 어려운 것이 아니다. 이해를 돕기 위해서 우리는 루비의 변수, 상수, 심볼, 문자열에 대한 설명을 순서대로 해보기로 한다.
  • 변수와 상수
  • 상수와 심볼
  • 심볼과 문자열
  • 루비온레일스에서의 심볼 사용예

변수와 상수

변수는 특정 데이터의 메모리상의 위치를 말한다. 따라서 아래와 같이 변수에 문자열 "abc"를 할당하게 되면,

var = "abc"

String 클래스가 "abc"값으로 초기화 되어 문자열 객체를 만들게 된다. 이 때  변수 var는 이 객체의 메모리상의 위치를 가리키게 되는 것이다.

우리는 이 변수 var를 참조할 때마다 var가 가리키는 메모리상의 위치에 있는 객체의 값을 반환하게 되는 것이다.

>> var = "abc" #=> "abc"
>> var #=> "abc"
>> var.class #=> String
>> var.object_id #=> 70287904281360
>> var.object_id #=> 70287904281360
>> var.object_id #=> 70287904281360

따라서, var라는 변수는 문자열 "abc"를 가지고 있지만 실제로는 var라는 변수가 가리키는 메모리상의 값을 불러오게 되는 것이다. var 변수를 참조할 때마다 동일한 값을 가져오는 것은 당연한 것이다.

변수는 mutable한 속성을 가지고 있어서 얼마든지 다른 값으로 할당할 수 있다.

>> var = "bcd" #=> "bcd"
>> var #=> "bcd"
>> var.object_id #=> 70287903718960
>> var.object_id #=> 70287903718960
>> var.object_id #=> 70287903718960

이 때 변수 var의 참조값은 변경된다. 즉, var 변수에 다른 값을 할당하는 순간 또 다시 String 클래스가 새로운 값 "bcd"로 초기화되어 문자열 객체가 만들어지고 var 변수 해당 객체의 메모리상의 새로운 위치를 가리키게 되는 것이다. 따라서 변경된 var 변수의 object_id 값은 변경전의 값과 달라지지만 var 변수를 참조할 때마다 동일값을 얻게 되는 것이다.

이제, 변수와 비슷한 상수(constant)를 비교해 보자. 변수와 구분되는 결정적인 차이점은, 첫째, 상수는 알파벳 대문자로 시작하다는 것이다. 둘째, 상수는 다른 값으로 변경할 수 없다는 것이다.

따라서, 변수이름을 정할 때 무심고 첫문자를 대문자로 하면 의도하지 않게 상수가 되어 버린다는 것을 유의해야 한다. 상수값을 실수로 변경하게 되면,

>> Abc = "abc"  #=> "abc"
>> Abc.class  #=> String
>> Abc.object_id  #=> 70144975225980
>> Abc.object_id  #=> 70144975225980
>> Abc.object_id  #=> 70144975225980
>> Abc = "bcd"
(irb):9: warning: already initialized constant Abc  #=> "bcd"

경고 메시지가 보이게 된다. 즉, 상수는 변경할 수 없다는 말이다.

상수와 심볼

값을 변경할 수 없는 상수가 문자열을 가지고 있는 특수한 상황에서, 루비는 symbol 이라는 특별한 클래스를 제공해 주고 있다. 즉, symbol 객체는 문자열 상수를 생각하면 쉽게 이해할 수 있을 것이다. 그러나 상수와는 달리 표기법에서 차이가 있다. 따옴표로 둘러싼 문자열 앞에 콜론(:)을 붙여주면 되는데, 이 때 따옴표는 생략할 수 있다. 따라서, 아래의 예는 모두 symbol을 나타낸다.

>> :abc  #=> :abc
>> :"abc"  #=> :abc

심볼과 문자열

위에서와 같이 문자열을 심볼로 변환해 주는 메소드(string.to_sym 또는 string.intern)를 사용하면 쉽게 변경할 수 있다.

>> "abc".to_sym  #=> :abc
>> "abc".intern  #=> :abc

반대로 symbol을 문자열로 변환할 수도 있다.

>> :abc.to_s  #=> "abc"

이상에서와 같이, 문자열과 symbol은 서로 클래스 변경을 쉽게 할 수 있다. 당연한 결과지만 문자열과 symbol에 대한 object_id를 살펴보면 금방 그 의미를 유추해 볼 수 있다.

>> "abc".object_id  #=> 70144975069500
>> "abc".object_id  #=> 70144975056060
>> "abc".object_id  #=> 70144975050760
>> :abc.object_id  #=> 459368
>> :abc.object_id  #=> 459368
>> :abc.object_id  #=> 459368

문자열은 사용할 때마다 object_id 값이 달라진다. 즉, 그때마다 새로운 String 객체가 만들어진다는 말이다. 그러나 symbol은 항상 동일한 object_id 값을 가지게 된다는 것이다. 이것은 무슨 의미일까. symbol은 이름과 값을 동시에 가진다는 것는 것이다. 변수의 경우는 변수명이 있고 그 변수명이 가지는 값은 변수명이 가리키는 메모리상의 위치에 존재하는 값이라는 말이다. 그러나 :abc 라는 심볼의 경우는, abc라는 심볼이름과 "abc"라는 값을 동시에 가지게 된다는 말이다. 따라서 언제든지 :abc와 같이 참조할 수 있다는 말이다. 동일한 값을 얻기 위해 변수에 symbol을 할당할 필요가 없다는 결론이다. 또한 동일한 symbol 참조할 때마다 항상 동일한 메모리상의 위치에 있는 값을 가져오게 되어 문자열과는 달리 heap 메모리를 쓸데 없이 낭비하지 않고 문자열에 비해 40%정도의 처리속도의 향상을 가져오게 된다는 장점이 있다. 그러나 symbol 도 10,000개를 넘어가면 symbol table에 등록이 되지 않는다고 한다. 또한 사용하지 않는 symbol에 대해서도 garbage collection이 자동으로 이루어지 않는다고 한다. 이러한 symbol의 종류와 갯수를 모두 보고 싶은 경우에는 아래와 같이 하면 된다.

>> Symbol.all_symbols  
=> [:"core#set_method_alias", :"core#set_variable_alias", :"core#undef_method", :"core#define_method", :"core#define_singleton_method", :"core#set_postexe", :each, :length, :size, :lambda, :intern, :gets, :succ, :method_missing, :send, :__send__, ... ]

>> Symbol.all_symbols.size  #=> 2947

루비온레일스에서의 심볼 사용예

그렇다면 루비온레일스에서 symbol은 주로 어떤 용도로 사용할까.
아마도 해시 키로 symbol을 가장 많이 사용하지 않나 생각된다. 또한 키값으로도 문자열 객체나 symbol 객체 중에 어떤 것을 사용해도 되는 경우가 많다.

폼에서 서밋하여 넘겨 받은 값들을 루비온레일스에서는 params 라는 해시로 접근할 수 있다. 이 params 해시는 쿼리스트링으로 넘어오는 변수나 Post 변수로 넘어오는 변수 모두에 대해서 사용할 수 있다. params[:person] 와 같이 컨트롤러 액션에서 사용하면 넘어 온 변수 중에서 :person 키를 가지는 값을 얻을 수 있는데, @person 인스턴스 변수를 이용해서 폼을 작성한 경우 @person 인스턴스 변수로 참조되는 person 객체에 대한 속성은 params[:person][:name] 와 같이 조회할 수 있게 되는 것이다. 물론 이 해시는 중첩 구조를 가지게 된다.

class PersonController < ApplicationController
  def new
    @person = Person.new
  end

  def create
    @person = Person.new(params[:person])

    respond_to do |format|
      if @person.save
        format.html { redirect_to @person, notice: 'Person was successfully created.' }
        format.json { render json: @person, status: :created, location: @person }
      else
        format.html { render action: "new" }
        format.json { render json: @person.errors, status: :unprocessable_entity }
      end
    end
  end
end

루비 1.9부터는 해시 표기법상 단축형이 지원된다. 따라서 아래의 표기법은 동일 한 것이다.

>> h = { :name => "Lucius", :age => "50" }
>> h = { name: "Lucius", age: "50" }

symbol에서 사용하는 특이한 메소드 to_proc를 소개한다. 매우 편리한 메소드이며 흔히 사용하는 것이다. 사용법 아래와 같다.

>> (1..3).collect { | i | i.to_s }
>> (1..3).collect(&:to_s)

즉, 메소드명을 심볼형으로 사용하여 블록을 반환해 주는 Symbol의  "&" (= to_proc) 메소드를 사용할 수 있다.

지금까지의 내용을 정리해 보면, symbol은 문자열값을 가지는 상수로 생각할 수 있다. 변수와 같이 항상 메모리상의 같은 위치를 참조하게 되고, 상수와 같이 값을 변경할 수 없고, 변수에 할당하지 않고 사용하여  언제나 동일한 심볼을 사용할 수 있다. 이에 반해 문자열을 재사용할 때는 반드시 변수에 할당하여 사용하지만 할당할 때마다 새로운 문자열 객체가 만들어져 메모리상에 다른 위치를 가리키게 된다는 것이다.

본 내용에 대한 스크린캐스트 동영상도 함께 제작하여 유투브에 공유하였다.

  


참고문서


2012년 12월 25일 화요일

루비에서의 Delegation 방법

이전 글에서는 루비온레일스에서의 delegate 메소드를 사용하는 방법에 대해서 정리했었다.
사실 루비에 대한 delegation 방법을 먼저 알아보는 것이 우선이 되어야 함이 당연하겠지만, 그 나름대로 도움이 되는 것 같다.

Khell 이 2009년에 블로그에 올린 글이 도움이 된다. 루비에서의 Delegation 방법은 두가지가 있다고 한다.

  • 정적인 코드로부터 동적인 코드를 분리하라
  • 상속보다는 composition을 사용하라.

이 두개의 일반적인 개발원칙은 객체지향 프로그래밍 세계에 입문하는 개발자들이 준수해야 할 것들이다. 첫번째 원칙에 대해서는 이의의 여지가 없지만, 두번째 원칙에 대해서는 이의를 제기한다면 그 이유를 아래에 예를 들어 설명해 보겠다.

하나의 heat sensor를 가지는 임의의 Robot 모델이 있다고 가정한다면 아래와 같이 아주 간단한 UML을 작성할 수 있을 것이다.


이 디자인은 몇가지 문제점을 가지고 있다.

  1. heat sensor가 없는 다른 종류의 Robot이 존재할 수 있다는 것이며 이것은 첫번째 원칙에 위배되는 것이다. 
  2. heat sensor에 대한 기능개선이 필요할 때마다 Robot 클래스를 수정해야하는데 이 또한 첫번째 원칙에 위배되는 것이다. 
  3. heat sensor 메소드가 Robot 클래스내로 노출된다는 것이다.

이 클래스에 약간의 기능개선을 추가해 보자.


이것은 상속에 근거해서 다시 디자인한 것인데 이 또한 위에서 언급한 첫번째 문제는 해결할 수는 있지만 heat sensor에 관련된 나머지 두가지 문제는 여전히 해결할 수 없게 된다. 또 다시 기능개선을 해 보자.


바로 이러한 형태의 디자인이 상속보다는 composition에 근거해서 작업한 것이 되며, 비로소 세가지 문제점들을 모두 해결할 수 있게 되는 것이다. 또한 덤으로 새로운 것을 얻게 되는데, 미래에 사용하기 위한 HeatSensor라는 클래스라는 것이다.

그렇다면 `delegation`이란 무엇인가?

여기서 delegation이란 `기능위임` 정도로 번역할 수 있을 것이다. delegation이란 contained part(?)로 기능을 위임하는 과정을 말한다. 마지막 UML을 자세히 보면, VolcanoRobot 클래스는 heat sensor와 관련된 세개의 메소드를 가지게 되는데, 이들은 래퍼(wrapper) 메소드로서의 역할을 하게 된다. 즉, HeatSensor 클래스의 해당 메소드를 호출하는 일 외에는 아무런 추가작업을 하지 않는다는 말이다. 바로 이것이 delegation이 하는 일이라고 보면 될 것이다. VolcanoRobot 클래스의 세가지 메소드는 contained part(delegates:HeatSensor)로 각각의 작업을 위임하게 되다는 것이다. delegation을 composition으로 구현해 줌으로써 위에서 언급한 바와 같이 유연하고 멋진 해결책을 제공할 수 있게 되고 또한 첫번째 개발원칙인 정적인 것으로부터 동적인 코드를 분리할 수 있게 되는 것이다. 그러나 여기서 약간의 댓가를 치려야 하는데, 바로 래퍼(wrapper) 메소드를 구현해 주어야 하며 이 래퍼 메소드 호출을 처리하는데 별도의 시간이 소요된다는 것이다.

루비에서의 delegation 처리방법

설명을 위해서 아래에 코드 예를 보도록 하자.
여기에는 로봇팔과 열감지를 가지고 박스 포장하기, 박스 쌓아두기, 열측정하기와 같은 여러가지 일을 하는 다용도 Robot 클래스를 정의해 놓았다.

class Robot
  def initialize
    @heat_sensor = HeatSensor.new
    @arm = RobotArm.new
  end
 
  def measure_heat(scale="c")
    @heat_sensor.measure(scale)
  end
 
  def stack(boxes_number=1)
    @arm.stack(boxes_number)
  end
 
  def package
    @arm.package
  end
end
 
class HeatSensor
  #Celsius or Fahrenheit scale
  def measure(scale="c")
    t = rand(100)
    t = scale=="c" ? t : t * (9/5)
    puts "Heat is #{t}° #{scale.upcase}"
  end
end
 
class RobotArm
  def stack(boxes_number=1)
    puts "Stacking #{boxes_number} box(es)"
  end
 
  def package
    puts "Packaging"
  end
end
 
robo = Robot.new #=>#, @heat_sensor=#>
robo.stack 2 #=>Stacking 2 box(es)
robo.package #=>Packaging
robo.measure_heat #=> Heat is 59° C

보다시피, Robot 클래스에는 stack, package, measure_heat 세개의 메소드가 정의되어 있고 이들은 contained 객체에 해당하는 메소드를 호출하는 일 외에는 아무런 추가작업을 하지 않는다. 이것은 contained 객체가 많을 때 특히, 번잡스러운 일이 아닐 수 없다. 그러나, 루비에는 이런 경우에 해결방안으로 두가지 라이브러리를 제공해 준다. Forwardable과 Delegate. 각각에 대해서 알아 보도록 한다.

Forwardable Lib

Forwardable lib은 delegation을 지원하는 라이브러리이며 두개의 모듈이 있다. 하나는 `Fo rwardable`,  다른 하나는 `SingleForwardable` 이다.

Forwardable 모듈

Forwardable 모듈은, def_delegatordef_delegators 메소드를 이용해서, 특정 메소드를 지정 객체에 위임하는 방법을 제공해 준다.

def_delegator(obj, method, alias = method) : obj로 위임할 delegate 메소드를 정의한다. alias를 지정하여 delegate 메소드명을 변경할 수 있다.

def_delegators(obj, *methods) : 복수개의 delegator 메소드를 정의하지만 이 때는 alias 기능이 없다.

위의 Robot 클래스를 Forwardable 모듈을 사용해서 변경해 보도록 하자.

require 'forwardable'
class Robot
  # Extending provides class methods
  extend Forwardable
  # Use of  def_delegators
  def_delegators :@arm,:package,:stack
  # Use of  def_delegator
  def_delegator :@heat_sensor, :measure ,:measure_heat
  def initialize
    @heat_sensor = HeatSensor.new
    @arm = RobotArm.new
  end
end
 
class HeatSensor
  #Celsius or Fahrenheit scale
  def measure(scale="c")
    t = rand(100)
    t = scale=="c" ? t : t * (9/5)
    puts "Heat is #{t}° #{scale.upcase}"
  end
end
 
class RobotArm
  def stack(boxes_number=1)
    puts "Stacking #{boxes_number} box(es)"
  end
 
  def package
    puts "Packaging"
  end
end

보다시피 코드가 더 산뜻해졌다.

SingleForwardable 모듈

SingleForwardable 모듈은, def_delegators 메소드를 이용하여, 특정 메소드를 지정 객체에 위임하는 방법을 제공해 준다. 이 모듈은 Forwardable과 비슷해 보이지만, 클래스내에서가 아니라 객체에 대해서 작동한다는 것이 차이점이다.

require "forwardable"
require "date"
date = Date.today #=> #
# Prepare object for delegation
date.extend SingleForwardable #=> #
# Add delegation for Time.now
date.def_delegator :Time, "now","with_time"
puts date.with_time #=>Thu Jan 01 23:03:04 +0200 2009

Delegate Lib

Delegate lib은 기능위임을 제공하는 또다른 라이브러리이다. 두가지 사용 방법이 있다.

DelegateClass 메소드

최상위 DelegateClass 메소드를 이용하면 클래스 상속을 통해서 delegation을 쉽게 할 수 있게 된다. 아래의 예에서, CurrentDate라는 새로운 클래스를 정의하게 되는데, 이 클래스는 date 객체에 기능위임을 하자마자, 현재의 날짜 정보와 몇가지 추가 메소드를 가지게 된다.

require "delegate"
require "date"
# Notice the class definition
class CurrentDate < DelegateClass(Date)
  def initialize
    @date = Date.today
    # Pass the object to be delegated to the superclass. 
    super(@date)
  end
 
  def to_s
    @date.strftime "%Y/%m/%d"
  end
 
  def with_time
    Time.now
  end
end
 
cdate = CurrentDate.new
# Notice how delegation works
# Instead of doing cdate.date.day and defining attr_accessor for the date , i'm doing c.day
puts cdate.day #=>1
puts cdate.month #=>1
puts cdate.year #=>2009
# Testing added methods
# to_s
puts cdate #=> 2009/01/01
puts cdate.with_time #=> Thu Jan 01 23:22:20 +0200 2009

SimpleDelegator 클래스

이것은 변경될 수 있는 객체에 대해서 위임할 수 있도록 해 준다.

require "delegate"
require "date"
today = Date.today #=> #
yesterday = today - 1 #=> #
date = SimpleDelegator.new(today) #=> #
puts date #=>2009-01-01
# Use __setobj__ to change the delegate
date.__setobj__(yesterday)#=> #
puts date #=>2008-12-31

보다시피, 2개의 객체를 만들어 각각 기능위임을 했다.

루비온레일스에 대해서는...

레일스는 `delegate`라는 새로운 기능을 추가해 준다. 즉, delegate 클래스 메소드를 제공해 주어 contained 객체를 자신의 메소드로 쉽게 노출되도록 해 준다. 하나 또는 복수개의 메소드(심볼 또는 문자열로 명시)와 `:to` 옵션에 대상 객체의 이름을 넘겨 주면 된다. 최소한 하나의 메소드와 `:to` 옵션은 반드시 명시해 주어야 한다.

더미 프로젝트를 만든 후에 콘솔을 연다.

$ rails dummy 
  ...
$ cd dummy
$ruby script/console
Loading development environment (Rails 2.2.2)
>> Person = Struct.new(:name, :address)
=> Person
>> class Invoice < Struct.new(:client)
>>   delegate :name, :address, :to => :client
>> end
=> [:name, :address]
>> john_doe = Person.new("John Doe", "Vimmersvej 13")
=> #
>> invoice = Invoice.new(john_doe)
=> #>
>> invoice.name
=> John Doe
>> invoice.address
=>Vimmersvej 13

ActiveRecord에서 효과적으로 사용하는 방법을 알기 위해서 반드시 레일스 API 문서에서 코드 예문을 찾아 보도록 한다.

마지막으로, 마치기 전에, 아래의 레일스 API 문서 중의 delegate 메소드 코드를 보기 바란다. 여기에는 코드에 대한 설명을 코멘트로 추가해 두었다.

class Module
  # Delegate method 
  # It expects an array of arguments that contains the methods to be delegated 
  # and a hash of options
  def delegate(*methods)
    # Pop up the options hash from arguments array
    options = methods.pop
    # Check the availability of the options hash and more specifically the :to option
    # Raises an error if one of them is not there
    unless options.is_a?(Hash) && to = options[:to]
      raise ArgumentError, "Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, :to => :greeter)."
    end
 
    # Make sure the :to option follows syntax rules for method names 
    if options[:prefix] == true && options[:to].to_s =~ /^[^a-z_]/
      raise ArgumentError, "Can only automatically set the delegation prefix when delegating to a method."
    end
 
    # Set the real prefix value 
    prefix = options[:prefix] && "#{options[:prefix] == true ? to : options[:prefix]}_"
 
   # Here comes the magic of ruby :) 
   # Reflection techniques are used here:
   # module_eval is used to add new methods on the fly which:
   # expose the contained methods' objects
    methods.each do |method|
      module_eval("def #{prefix}#{method}(*args, &block)\n#{to}.__send__(#{method.inspect}, *args, &block)\nend\n", "(__DELEGATION__)", 1)
    end
  end
end

이글이 루비의 delegation에 대한 개념을 잡는데 조금이나마 도움이 되었기 바라면서 글을 마친다.

Khell에게 감사하며...


참고원문 : Khell's Blog

'delegate' 파견하여 권한을 위임하다?

루비 프로젝트에서는 `delegation` 기능을 흔히 사용한다. 저자는 전문 프로그래머가 아니라서 루비온레일스에서 가끔 delegate라는 메소드를 만날 때마다 짜증스러웠다. 왜냐하면 잘 이해를 하지 못했기 때문이다.^^

2009년도에 작성된 내용이지만, 루비와 루비온레일스에서 `delegation(저자譯:기능위임)` 개념을 이해하는데 훌륭한 글이라고 판단되는 두개의 블로그를 공부하고, 나중을 위해서 정리해 둔다.

루비온레일스에서, 하나의 객체가 임의의 behavior(여기서는 속성에 대한 말로 액션 정도로 생각하면 되겠다)를 외부에서 호출할 경우, 실제로는 해당 behavior를 실행시 발생하는 모든 책임을 associate 모델 객체(레일스에서의 관계 모델)로 전가하는 개발방식을 `Delegation Design Pattern(저자譯:기능위임 코딩법)이라고 설명한다.

대개는 `method_missing` 메소드를 이용하여, 정의되어 있지 않은 메소드에 대한 호출을 중간에서 가로채어 임의의 처리루틴(handler)으로 포워딩해 주는 방법으로 해결하지만, 이러한 방법이 반드시 좋을 것만은 아니라고 한다. 루비에서는 이러한 기능위임 코딩법을 구현하기 위한 더 좋은 방법을 제공하고 있다.

루비표준라이브러리에는 이러한 기능위임 코딩법을 지원하기 위한 `Delegate module`이라는 것이 있는데, 일반적인 방법보다 더 복잡해서 기피하는 경향이 있는 것 같다. Simone Carletti의 경우 이 방법을 절대 사용하지 않는다고 한다.

레일스 프로젝트를 개발할 때 `ActiveSupport`가 기본으로 포함되어 있어 `Module#delegate` 확장모듈을 이용하면 이러한 기능위임 코딩법을 보다 명료하고 쉽게 구현할 수 있게 해 준다. 바로 이 delegate 모듈을 임의의 클래스나 모듈에서 사용하면 특정 메소드를 associate 객체로 기능위임(delegate)할 수 있게 되는 것이다.

`User` 모델에 `belongs_to` 로 관계선언을 하는 `Post` 모델에 대한 예를 들어 본다.

class Post
  belongs_to :user
end

class User
  has_many :posts
end

이때, 특정 post에 연결된 user의 name을 반환하기 위해 `post.name`을 호출하고자 한다면 어떻게 될까. 보통는 아래와 같이, `name`이라는 메소드를 만들게 될 것이다.

class Post
  belongs_to :user

  def name
    # let's use try to bypass nil-check
    user.try(:name)
  end
end

글의 주제에 벗어나는 이야기지만, 위의 코드에서 6번 라인에는 `try`라는 메소드를 사용했다. `try` 메소드는 파라메터로 넘겨진 인수에 대해서 nil 값이 넘어 오더라도 에러를 발생하지 않도록 해 주는 편리한 놈이다. 이 메소드를 사용하지 않을 경우에는 user.name unless user.name.nil? 와 같이 코딩해 주어야 에러를 모면하게 될 것이다.

자, 이때 동일한 코드를 아래와 같이 `delegate` 메소드를 사용하여 구현할 수 있다.

class Post
  belongs_to :user
  delegate :name, :to => :user, :allow_nil => true
end

이 `delegate` 메소드는 어떠한 문맥에서도 사용할 수 있다고 한다. 즉, 위에서 예를 든 ActiveRecord 모델 이외에서도 사용할 수 있는 말이다. 아래에서와 같이 `queue` 래퍼클래스(QueueManager)에서 특정 메소드가 내부 queue를 실행하도록 기능위임할 수 있다.

class QueueManager

  attr_accessor :queue

  # Delegates some methods to the internal queue
  delegate :size, :clear, :to => :queue

  def intialize
    self.queue = []
  end

end

m = QueueManager.new
m.size
# => 0
m.clear
# => []

위임할 메소드를 심볼형태로 인스턴스 변수, 클래스 변수, 또는 상수에 넘겨 줄 수 있다. 이때 최소한 하나의 메소드와 `:to` 옵션이 필요하게 된다.

`delegate` 메소드는 사용할 수 있는 옵션이 더 있다. `:prefix` 옵션을 true로 설정하게 되면 위임될 객체의 이름을 delegate 메소드 이름앞에 추가해 주게 된다. 물론, 객체이름 대신에 임의의 다른 이름을 지정할 수도 있다.

class Post
  belongs_to :user

  delegate :name, :to => :user, :prefix => true
  # post.user_name

  delegate :name, :to => :user, :prefix => "author"
  # post.author_name
end

`:allow_nil` 옵션을 사용하면 nil 값을 반환하는 객체에 대해서도 해당 delegate 메소드를 호출할 수 있게 해 준다. 이 때는 delegate 메소드를 호출시에 nil 값을 반환하게 된다. 디폴트상태에서는 `NoMethodError` 예외를 발생시키게 된다.

class Post
  belongs_to :user
  delegate :name, :to => :user, :prefix => true
end

Post.new.user_name
# raise NoMethodError

class Post
  belongs_to :user
  delegate :name, :to => :user, :prefix => true, :allow_nil => true
end

Post.new.user_name
# => nil

사실 `:to`옵션은 필수이므로 옵션이라 할 수 없다.

ActiveSupport의 `delegate` 확정모듈에 대해 자세하게 기술되어 있지만, 레일스 문서에 빠져 있어서 소스코드를 직접찾아 보면 그만한 가치가 있을 것이다.




참고원문 : Simone Carletti's Blog

2012년 12월 21일 금요일

“Can you do this and that?”


- Modernizr and HTML5/CSS3 -

지금까지는 웹브라우저의 호환성을 유지하기 위해 browser sniffing 기법(자바스크립트의 navigator.userAgent 프로퍼티를 이용하는 방식)을 사용해 왔다. 그러나 스마트 디바이스가 대세로 떠오르는 요즘에는 HTML5와 CSS3가 거의 웹표준으로 자리매김을 하고 있다. 그러나 브라우저마다 HTML5와 CSS3의 구현정도가 달라 기존의 browser sniffing 방식으로는 이러한 브라우저간의 호환성을 유지한다는 것은 바보 같은 일이 아닐 수 없다.

바로 여기서 `Modernizr` 의 필요성이 강조되는 것이다. `Modernnizr`는 브라우저의 종류를 점검하는 것이 아니라 특정 엘리먼트에 대한 지원여부를 점검(feature-detection)하는 것이다.

`Modernizr`는 오픈소스 자바스크립트 라이브러리로, 웹디자이너가 클라이어트 브라우저의 지원정도에 따른 차이를 없애고 결과적으로 모든 브라우저에서 같은 기능을 수행할 수 있게 해준다. 결국 특정 브라우저에서 구현하는 HTML5와 CSS3의 모든 기능을 다른 브라우저에서도 사용자 경험을 할 수 있도록 해 주는 장점을 가지게 되는 것이다.

웹페이지에 `Modernizr` 스크립트를 인클루드하면, 현재 브라우저가 CSS3(@font-face, border-radius, border-image, box-shadow, rgba() 등) 뿐만 아니라 HTML5의 기능(audio, video, localStorage, 새로 추가된 input 엘리먼트의 types과 attributes)에 대해서도 지원하는 정도를 점검할 수 있고 자바스크립트를 이용하여 지원 안되는 부분에 대한 기능을 추가할지 아니면 단순히 웹페이지의 기능을 축소할지에 대한 결정도 할 수 있게 된다. 게다가 `Modernizr`는 IE에서의 스타일 작업시 HTML5 엘리먼트를 사용할 수 있게도 해 준다.

이러한 `Modernizr`는 점진적인 기능개선 원칙하에 개발되었기 때문에, 웹사이트를 자바스크립트를 전혀 사용하지 않는 경우에서부터 기능개선을 위해서 단계별로 레이어를 추가할 수 있도록 지원한다. 오히려 이러한 단계별 개발방식을 권장하고 있다.

`Modernizr`로 시작하기

홈페이지로부터 최신 버전을 다운로드한 후 웹페이지의 <head> 섹션에 아래와 같이 인클루드한다.

<!DOCTYPE html> 
<html> 
  <head> 
  <meta charset="utf-8"> 
  <title>My Beautiful Sample Page</title> 
  <script src="modernizr-1.5.min.js"></script>
  </head> 
…


다음에 <html> 태그 엘리먼트에 `no-js` 클래스를 추가해 준다.

<html class="no-js">

`Modernizr`가 실행되면 이 클래스가 `js` 로 변경되어 웹브라우저에서 자바스크립트가 사용가능한 지 여부를 CSS로 알수 있도록 해준다. 또한 브라우저에서 감지되는 모든 feature 에 대한 해당 클래스명을 추가해 주게 되는데, 브라우저가 지원하지 않는 feature 에 대해서는 해당 클래스명 앞에 `no-`를 삽입해 주게 된다. 따라서 <html> 태그 엘리먼트는 페이지 로드시에 자바스크립트가 사용가능할 경우 아래와 같이 보이게 된다.

<html class="js canvas canvastext no-geolocation rgba hsla multiplebgs borderimage borderradius boxshadow opacity cssanimations csscolumns cssgradients cssreflections csstransforms csstransforms3d csstransitions video audio localstorage sessionstorage webworkers applicationcache fontface">

`Modernizr`는 또한 `Modernizr`라는 자바스크립트 객체를 만들어, 각 feature에 대한 불린값을 가지는 프로퍼티 리스트 정보를 제공해 준다. 따라서 Modernizr.canvas 값은 브라우저가 canvas 엘리먼트를 지원하는 경우 true 값을 반환하고 그렇지 않은 경우에는 false 값을 반환하게 된다. 이 자바스크립트 객체는 특정 feature에 대한 상세정보까지도 제공해 주는데, 예를 들어,  Modernizr.video.h264는 브라우저의 특정 코덱에 대한 지원여부를 알려준다. 또한 Modernizr.inputtypes.search는 `search` input 엘리먼트의 type에 대한 지원여부에 대해서 알려주게 된다.

이제 `Modernizr`를 적용하는 방법을 단계별로 알아 본다.

우선 보기에도 밋밋한 샘플 웹페이지를 하나 만든다.
그리고 여기에 약간의 스타일을 추가해 준다. 예를 들면, 보기 좋게하기 위해 전체적인 포맷이나 색상 그리고 레이아웃을 잡아 준다. 결과적으로 변경된 웹페이지는 이렇게 보일 것이다.

이제부터는 웹페이지의 기능을 추가해서 좀 더 흥미롭게 해 보자. <h1> 태그에 임의의 폰트를 사용하고, Modernizr에서 이용가능한 feature들의 리스트를 두개의 컬럼으로 배열하고, 이미지가 포함된  Modernizr관련 내용을 오른쪽으로 이동한다. 그리고 웹페이지의 외곽선을 변경해서 더 멋지게 보이도록 한다. 이와 같이 CSS를 이용해서 새로운 프로퍼티(추가된 스타일)를 추가하게 되면 웹페이지는 더 완성도 있게 보이겠지만 만약 특정 브라우저가 이러한 프로퍼티를 인식하지 못할 경우에는 해당 스타일을 무시한 채로 보이게 될 것이다. CSS의 캐스케이딩 기술을 이용하면 Modernizr를 사용하지 않은 상태에서도 border-radius와 같은 것을 사용할 수 있다. 그러나, Modernizr를 사용할 경우에는 뭔가 추가적인 기능을 이용할 수 있게 된다. 즉, 특정 브라우저가 이와 다른 프로퍼티를 지원하는 경우, 해당 프로퍼티를 원하는 데로 변경할 수 있게 된다는 것이다.

아래 두개의 CSS를 작성해서 예를 들어 본다.

.borderradius #content {
 border: 1px solid #141414;
 -webkit-border-radius: 12px;
 -moz-border-radius: 12px;
 border-radius: 12px;
}
.boxshadow #content {
 border: none;
 -webkit-box-shadow: rgba(0,0,0, .5) 3px 3px 6px;
 -moz-box-shadow: rgba(0,0,0, .5) 3px 3px 6px;
 box-shadow: rgba(0,0,0, .5) 3px 3px 6px;
}

첫번째 CSS 는 #content 엘리먼트의 둥근 모서리의 크기를 12px로 추가해 준다. 그러나 이미 #content 에 대해 "2px outset #666"로 외곽선을 지정한 바 있다. 이것은 외곽선의 모서리가 각이 저 있을 때에 어울려 보이지만 만약 둥근 모서리로 변경하게 되면 좀 어색하게 보이게 된다. 그러나 Modernizr 덕분에 특정 브라우저가 border-radius를 지원할 경우에만 외곽선을 "1px solid"로 렌더링하도록 할 수 있다.

두번째 CSS 는 #content 엘리먼트에 drop shadow를 추가해 주고 특정 브라우저가 box-shadow 프로퍼티를 지원할 경우에는 외곽선을 제거해 준다. 왜 이렇게 해 주어야 할까? 대부분의 브라우저는 border-with-border-radius 조합을 렌더링하지 못하지만 box-shadow는 멋지게 렌더링해 주기 때문이다. 여기서는 외곽선만 사용하는 것 보다는 drop shadow를 사용하기로 한다. 이렇게 하므로써 모든 CSS를 최상의 상태로 보이게 할 수 있게 된다. 즉, box-shadow를 지원하는 브라우저는 drop shadow를 제대로 보여 줄 것이고, border-radius를 지원하는 브라우저는 얇은 외곽선과 함께 둥근 모서리를 보여주게 되며, 둘 다 지원하는 않는 경우에는 원래의 2px outset  외곽선을 보여 주게 되는 것이다.

다음에는 헤더 태크에 특정 폰트를 추가한다. 아래에 <h1> 태그에 대해 이미 선언해 놓은 내용을 보여 준다.

h1 {
  color: #e33a89;
  font: 27px/27px Baskerville, Palatino, "Palatino Linotype", "Book Antiqua", Georgia, serif;
  margin: 0;
  text-shadow: #aaa 5px 5px 5px;
}

이것은 제대로 동작하여 27px 크기의 폰트가 모든 폰트에 대해서 어울려 보이지만, Beautiful 이라는 폰트에 대해서는 상대적으로 작게 보이게 된다. 이 폰트를 사용하기 위해 아래와 같이 CSS를 추가한다.

@font-face {
  src: url(Beautiful-ES.ttf);
  font-family: "Beautiful";
}
.fontface h1 {
  font: 42px/50px Beautiful;
  text-shadow: none;
}

우선 @font-face 선언을 통해서 추가한 폰트를 위해 path, filename, font-family 속성를 지정해 준다. 그리고 <h1> 엘리먼트에 추가한 폰트를 사용하도록 지정해 주는데 훨씬 큰 폰트 크기로 할당해 준다. 이렇게 한 이유는 추가하게 되는 Beautiful 폰트가 <h1> 엘리먼트에 지정한 다른 폰트에 비해 훨씬 작게 렌더링되기 때문이며 따라서 이 폰트를 렌더링할 때만 브라우저가 헤더 태그를 훨씬 크게 렌더링하도록 해 주어야 하기 때문이다.

여기에 더해서, Beautiful 폰트는 text shadow를 표시하는데 약간 문제를 가지고 있어서 브라우저가 이 폰트를 렌더링할 때 shadow를 제거했다. Modernizr의 feature들에 대한 목록을 두 컬럼으로 분리할 필요가 있다. 이를 위해서, CSS columns를 사용할 것인데 브라우저는 해당 목록의 순서를 유지한 채 두 컬럼으로 재배열하게 된다. 숫자로 순서를 표시하지는 않았지만 이 목록은 알파벳순으로 배열된다. 물론, 모든 브라우저가 아직 CSS columns를 지원하지 않아서 이 경우에는 float 속성을 이용하게 되는 이 때는 더 이상 알파벳순으로 소팅되지 않지만 하나의 컬럼으로 보이는 것보다는 더 낫게 보이게 된다.

.csscolumns ol.features {
  -moz-column-count: 2;
  -webkit-columns: 2;
  -o-columns: 2;
  columns: 2;
}
.no-csscolumns ol.features {
  float: left;
  margin: 0 0 20px;
}
.no-csscolumns ol.features li {
  float: left;
  width: 180px;
}

다시 Modernizr를 이용한다. 이때는 프로퍼티 변경이나 캐스케이딩을 이용하여 해결할 수 없는 경우에만 자주 사용하는 프로퍼티를 적용하기로 한다. 브라우저가 CSS columns를 지원할 경우에는 문제없이 원하는 결과를 얻을 수 있지만, 그렇지 못할 경우에는(<html> 태그에 "no-csscolumns" CSS 클래스가 추가되어 알 수 있다), 이 목록 항목들을 float 해서 비슷하게 보이도록 margin과 width를 변경해 준다. 이것은 좋은 방법은 아니지만 길다란 목록을 보는 것보다는 낫다는 것이다.

이미 눈치챗을 수도 있지만, 이전 예제에서 사용했던  border-radius와 box-shadow 프로퍼티와는 다르게 columns를 지정하게 되는데, 오페라(Opera) 브라우저만이 현재 vendor 명을 앞에 붙인 상태로 CSS columns를 지원하고, 모질라(Mozilla)의 경우는 아직까지 "columns"라는 단축형의 프로퍼티를 인식하지 못해서 -moz-column-count 속성을 사용해야 하기 때문이다.

지금까지 작업한 변경내용을 이와 같이 볼 수 있다.

마직막으로 훨씬 더 세련된 기능을 추가해 보도록 한다. WebKit 기반의 브라우저들은 CSS transitions, animations, 3D transforms 같은 멋진기능들을 지원하는데 여기서 몇가지를 적용해 보도록 한다. 이것들 중에 몇가지 프로퍼티는 간단하게 추가할 수 있고 이를 지원하지 않는 브라우저에서는 별문제없이 무시되는데, 그러나 어떤 프로퍼티는 이와 같이 추가하게 되면 모든 브라우저의 레이아웃이 깨져 버리는 사태가 발생할 수 있는데 Modernizr를 사용하면 이런 경우를  효과적으로 조절할 수 있게 된다.

먼저 아래와 같이 설정을 한다.

@-webkit-keyframes spin {
   0% { -webkit-transform: rotateY(0); }
 100% { -webkit-transform: rotateY(360deg); }
}
.csstransforms3d.cssanimations section {
 -webkit-perspective: 1000;
}

@keyframes는 새롭게 추가된 CSS animations 스펙 중에 하나인데, 현재는 WebKit 류의 브라우저에서만 지원한다. 이것은 원하는 프로퍼티만으로도 전체 animation을 선언할 수 있는데 원하는 스텝에서 변경작업도 수행할 수 있다. CSS로 animation을 적용하고자 할 때 duration, loop count, easing curves들을 지정해 줄 수 있지만,  여기서는 duration은 지정하지 않고 key frames 만 지정했다는 것에 주목해야 한다. 이렇게 하므로써 다른 엘리먼트에 대해서는 다른 속도를 지정하여 특정 animation을 재사용할 수 있어 유연성을 극대화 할 수 있다. animations에 대한 더 자세한 정보는 W3C Working Draft specification을 참고하면 된다.

다음은, 3차원 공간에서 엘리먼트를 회전할 수 있도록 <h2> 태그에 animation을 적용한다.

.csstransforms3d.cssanimations section h2 {
 background-image: url(modernizr-logo.png);
 overflow: hidden;
 -webkit-animation: spin 2s linear infinite;
}

여기서는 3차원 공간에서 이미지를 회전시킬 것이기 때문에, 배경이미지로 앤티앨리어스 처리된 멋진 Modernizr 로고를 사용하기 위해 PNG 버전으로 교체할 것이다. 또한 헤더내의 텍스트를 감추기 위해 overflow:hidden 프로퍼티를 추가하고 오프셋을 -9999px 설정해서 보이지 않게 해 준다. 3차원 공간에서 엘리먼트를 회원시키면 회전하는 동안 보이게 되는데 놀랍지만 그렇게 멋지게 보이지 않았다. 마지막으로, animation을 적용해서 2초로 세팅하고 선형 패션으로 무한루프로 회원하게 한다.

최종 웹페이지는 이와 같이 보이게 된다. WebKit 류 브라우저에서는 재밌는 animation이 보이게 된다. 바란건대 Modernizr를 이용하므로써 웹사이트 훨신 잘 제어할 수 있고 점직적인 기능향상을 잘 구현할 수 있기를 바란다. 여기서 설명한 것이 Modernizr의 모든 것이 아니지만 자바스크립트로 구동된 fallback을 생성하고 HTML5의 새롭고 멋진 feature들을 사용하는데 매우 중요한 내용이 될 것이다.


2012년 12월 19일 수요일

Polyfiller란?

레일스가이드의 Action View Form Helper 부분을 보면 "polyfiller"란 단어가 나온다. 일반사전에는 나오진 않는 단어이다. 위키피디아를 검색해 보면 이런 의미를 찾아 볼 수 있다.

In web development, a polyfill (or polyfiller) is downloadable code which provides facilities that are not built-in to a web browser.

웹브라우저에 내장되어 있지 않은 기능을 제공하는 다운로드가능한 코드를 polyfill 또는 polyfiller라고 한다.

이 단어는 HTML5와 CSS3 기능을 지원하지 않는 웹브라우저에서도 호환성을 갖도록 하기 위한 조치를 언급할 때 사용된다.

최근 스마트 디바이스가 IT를 주도하게 됨에 따라 HTML5와 CSS3에 대한 개발요구가 절대적으로 필요하게 되었는데, 최근의 다양한 웹브라우저에서는 아직 100% 지원을 하지 못하고 있고 전세계적으로도 아직 MS사의 IE의 6~8 버전을 많이 사용하고 있는 터라 코드 호환성의 문제로 웹개발자들은 힘들어 하고 있다.

저자의 경우에는 가장 최신의 IT 기술을 발빠르게 접목하고 있는 Ruby on Rails(루비로 만들어진 웹어플리케이션 개발 프레임워크)로 웹개발을 주로 하고 있어서, 더더욱 국내 브라우저 시장의 우위를 점하고 있는 IE과의 호환성은 항상 골치 아픈 문제이다.

서두에서 언급한 바 있는 RailsGuide의 내용 중에는 바로 이런 문제를 해결할 수 있는 방법에 대해서 소개하고 있는데, 한번쯤은 이에 대한 공부를 해 두어야 겠다고 생각했었다.

인터넷상에는 이에 대한 참고할 만한 자료가 많다. 구글검색을 통해서 알게 된 내용을 중심으로 본 글을 통해서 polyfiller 기법에 대한 내용을 정리해 보기로 한다.



가장 먼저 접하게 된 것은 바로 "Modernizr"와 "yepnope"에 대한 내용이다.
Modernizr의 철자를 보면 마지막에 -zer 로 끝나지 않고 'e' 자가 빠진 상태인 `-zr`로 되어 있다. 아마도 이름을 특이하게 보이게 하기 위해 이렇게 명명하지 않았나 싶다. 어쨌던 처음 접할 때부터 약간의 주의를 끌게 되는 `Modernizr` ㅎㅎㅎ.

`Modernizr`의 홈페이지를 찾아가 보면 문서화가 잘 되어 있다.  시간을 두고 차근차근 읽어 볼 필요가 있다. `Modernizr`의 개념과 설치, 사용법에 대해서 자세하게 안내되어 있다. 실전 튜토리얼도 찾아 볼 수 있다. 아쉬운 건 유료다. ㅎㅎㅎ. `Modernizr`를 이용하여 본인이 사용하는 웹브라우저의 호환성을 알아보는 웹서비스도 눈에 띈다. 이와 연관있는 서비스로 브라우저스코프라는 것도 있다.

웹개발시 그냥 자바스크립트를 인클루드하여 사용해도 되지만, 저자의 경우 루비온레일스프레임워크를 이용하여 개발을 주로하기 때문에 asset pipeline(레일스3.1+ )을 사용하여 `Modernizr` 자바스크립트를 인클루드할 수 있는 관련 젬을 검색해 보기로 했다. 다행히도 github.com에서 두 개의 젬을 찾을 수 있었는데, 하나는 `modernizr-rails`이고 다른 하나는 `rack-modernizr`이다.

`Modernizr`는 HTML5와 CSS3 요소의 지원여부를 클라이언트 웹브라우저에서 테스트하는 기능이 포함되어 있는 자바스크립트 라이브러리라고 생각하면 될 것 같다. 당연한 말이겠지만, 레일스 프레임워크에서 `Modernizr` 자바스크립트를 사용하기 위해서는 위에서 소개한 젬을 이용하여 `Modernizr`를 먼저 인클루드해야 한다.

결국 자바스크립트에서 어떻게 `Modernizr`를 사용하면 되는지를 알기만 하면 되겠다. John Athayde가 2012년 4월 30일 RailsView에 올린 글은 `Modernizr`를 이해하는 도움이 준다. 아래에  내용을 정리해 본다.


`Modernizr`의 custom build page를 보면 여러가지 옵션과 인클루드해야 할 항목을 볼 수 있다.

`Modernizr`의 코어 기능(Extra 섹션)으로 html5shiv, yepnode.js, respond.js(media queries) 와 CSS class를 추가하기가 있다.

html5shiv는 IE 6~9, 사파리 4.x(아이폰 3.x 폼함), 파이어폭스 3.x 브라우저에서 HTML5의 디폴트 스타일을 적용할 수 있게 해주고 특히 레거시 IE에서 HTML5의 section 엘리먼트를 사용할 수 있게 해 주는 툴이다. (shim or shiv) 프린트를 해야할 필요가 있을 때는 `w/printshiv` 옵션을 선택해야 한다.


yepnope.js는 이름에서 알 수 있듯이 호환성여부를 yes(yep) or no(nope) 로 알수 있도록 해 준다.  임의 조건에 따라 해당하는 스타일 클래스를 적용해 주는 작은 크기(3k)의 자바스크립트이다. 조건에 맞는 특정 파일을 서브할 수 있게 해준다.  이 로더는 Modernizr.load() 함수를 사용할 수 있게 해 준다.

`respond.js`를 인클루드하면 CSS3 Medial Queries 기능을 사용할 수 있다.  이것은 `Modernizr` 없이도 사용할 수 있다.

 `Add CSS Classes` 를 포함할 경우에는 특정 태그에 CSS 클래스를 추가할 수 있다.

CSS3 섹션은 CSS3 엘리먼트에 대한 테스트와 지원을 할 수 있게 해 준다. 특히 eBook을 작성하기 위해서는 @font-face, CSSGradients, box-shadow, rgba() 항목은 필수적으로 포함할 필요가 있다.


HTML5 섹션은 이미 html5shiv를 포함할 경우 이미 활성화되었기 때문에 실제로는 HTML5 태크를 포함하고 있지는 않다. 대신에 HTML5의 기능확장을 위한 엘리먼트을 포함하고 있다.

Misc. 섹션은 특정 브라우져에서 좀 더 복잡한 항목들을 사용할 수 있게 해 준다. 사실 box-sizing:border-box 를 사용하고자 한다면 polyfill 기능을 해제할 경우도 있다.

John Athayde는 2010년 Faruk Ateş 가 작성한 글을 읽어 볼 것을 권한다.  이 글에는 실제 코딩예를 들어 자세하게 설명하고 있어 이에 대한 내용도 다음 게시글에서 정리해 보기로 한다.