IBM®
메인 컨텐츠로 가기
    Korea [국가변경]    이용약관
 
 
   
        제품    서비스 & 솔루션    고객지원 & 다운로드    회원 서비스    
한국 developerWorks   >  Developer CoD  > developerworks

루비 메타프로그래밍, Part 2: eval 메서드





난이도 : 중급
2008년 4월 22일


연재순서
1회(2008년 3월): 프로그램을 작성하는 프로그램
2회(2008년 4월): eval 메서드


Part 1에서는 메타프로그래밍을 "컴퓨터 코드를 작성하는 프로그램을 만드는 작업"이라고 정의했다. 그렇다면 어떻게 코드를 데이터로 다룰 수 있을까?

메타프로그래밍 기능을 최초로 지원한 프로그래밍 언어는 인공지능 언어로 익히 알려진 리스프(Lisp)다. 리스프는 코드 저장을 위한 데이터 타입으로 '리스트(list)' 데이터 타입을 제공한다. 리스프의 리스트는 단방향 연결 리스트(linked list)로 구현되어 있는데, 이때 리스트의 원소는 또 다른 리스트도 포함할 수 있으므로 리스트는 트리(tree) 구조 데이터를 저장하는 용도로도 쓸 수 있다.

다른 프로그래밍 언어와 구별되는 리스프의 가장 큰 특징은 리스프 코드가 바로 리스트 형태로 작성된다는 점이다. 개발자가 작성한 프로그램 코드는 보통 실행되기 전에 컴파일러나 해석기의 파서(parser)를 통해 파스 트리(parse tree)라는 원시 코드 형태로 변환되는 과정을 거치는데, 리스프 코드가 리스트로 작성된다는 것은 리스프 프로그램이 처음부터 파스 트리 코드로 작성된다는 것을 의미한다(이 때문에 어떤 이들은 리스프는 신택스가 없는 언어라고 말하기도 한다. 파서의 주요 기능이 바로 프로그램의 코드 신택스를 분석해 파스 트리를 생성하는 일이기 때문이다).

트리는 프로그래밍에서 가장 많이 사용되는 데이터 구조 중 하나로 트리에 저장된 데이터를 프로그래밍 루틴을 통해 조작하는 것은 비교적 간단하다. 따라서 리스프에서의 메타프로그래밍은 리스트 데이터로 저장된 리스프 코드를 프로그래밍적으로 가공하는 방식으로 이뤄진다.

루비는 리스프와 달리 코드의 신택스가 많이 복잡하기 때문에 루비 코드를 저장하기 위한 데이터 타입을 정의하고 이를 통해 코드를 처리하는 접근 방법은 실용적이지 못하다. 따라서 루비에서의 메타프로그래밍은 루비 코드를 문자열(string) 데이터를 통해 가공한 후 이를 코드로 해석하는 방식을 주로 사용한다. 이러한 방식은 코드 생성에 있어 리스프 방식에 비해 유리한 면이 많다.

실제로 메타프로그래밍은 이미 패턴화된 코드를 자동으로 생성하는 목적으로 가장 많이 활용된다. 예를 들면 런타임(run time)에 메서드를 정의하는 코드를 문자열로 구성한 후 이를 eval 메서드를 통해 실행하는 식이다. 런타임에 코드를 생성하기 위해서는 생성하려는 코드를 위한 템플릿이 필요하다. 다음은 간단한 템플릿 코드 예를 보여준다.

Listing 1. 템플릿 코드
 

"def #{str}; @#{str}; end"


만약 로컬 변수 str에 "name"이라는 문자열 값이 저장되어 있었다면, 위의 템플릿 코드는 다음과 같이 변환될 것이다.

Listing 2. 문자열로 저장된 루비 코드

"def name; @name; end"


위와 같이 문자열로 저장된 루비 코드를 실행하기 위해서는 eval 메서드가 사용된다(eval은 evaluate의 준말로 코드를 평가한다는 의미다). 다음 코드를 한번 살펴보자.

Listing 3. 문자열로 저장된 코드 실행

str = "name"
eval "def #{str}; @#{str}; end"


위의 코드는 실행시 런타임에 name 메서드를 정의해준다. 따라서 위의 코드는 다음 코드와 논리적으로 같은 역할을 한다고 볼 수 있다.

Listing 4. name 메서드 정의

def name
  @name
end


Listing 3과 Listing 4의 차이점은 첫 번째 코드에서는 name 메서드의 정의가 런타임에 일어나고 두 번째 코드에서는 name 메서드의 정의가 컴파일 타임(compile time)에 일어난다는 것이다.

앞에서 예로 삼은 name 메서드는 @name 인스턴스 변수를 리턴해주는 접근자 메서드다. Part 1에서 사용했던 attr_reader 메서드는 이와 같은 접근자 메서드를 자동으로 정의하기 위한 메서드였다. 이제 attr_reader 메서드를 직접 정의해 보자. 원래의 attr_reader 메서드와 구분하기 위해 새 메서드의 이름은 my_attr_reader로 정한다.

Listing 5. base.rb

class Base
  def self.my_attr_reader(*names)
    names.each do |str|
      class_eval <<-EOS
      def #{str}
        @#{str}
      end
      EOS
    end
  end
end


위의 코드에서는 Base 클래스를 선언하면서 my_attr_reader 클래스 메서드를 정의한다. my_attr_reader 메서드는 인자로 넘겨진 모든 문자열(또는 심볼)이 가리키는 인스턴스 변수에 대한 접근자 메서드를 정의해준다(위의 코드에서 "<<-EOS ... EOS" 부분은 여러 줄의 문자열 데이터를 표기하는 루비의 "here document" 방식이다).

Listing 5에서는 앞에서 사용했던 eval 메서드 대신 class_eval 메서드가 사용된다. class_eval 메서드는 나중에 my_attr_reader 메서드가 다른 클래스를 선언하는 코드에서 호출될 때 인자로 주어진 문자열 코드를 그 클래스의 컨텍스트 내에서 실행해준다. 예를 들어 Person 클래스가 Base 클래스를 상속받고 Person 클래스를 선언하는 코드에서 my_attr_reader "name"이라는 코드가 호출된다면, Person 클래스에 @name 인스턴스 변수에 대한 접근자 메서드가 자동으로 추가된다.

이제 우리의 my_attr_reader 클래스 메서드를 사용하여 Part1에서 정의했던 Person 클래스를 새로 정의해보자.

Listing 6 - person.rb

class Person < Base
  my_attr_reader :name, :gender, :age

  def initialize(name, gender, age)
    @name, @gender, @age = name, gender, age
  end
end


Person 클래스는 Base 클래스로부터 my_attr_reader 클래스 메서드를 상속받기 때문에 위와 같이 Person 클래스를 선언하는 코드에서 my_attr_reader 메서드를 호출할 수 있다(루비의 attr_reader 클래스 메서드는 Module 클래스에 정의되어 있으며 어느 클래스에서도 호출될 수 있다).

다음은 irb 프롬프트에서 Base 클래스와 Person 클래스의 코드를 로드한 후, Person 클래스를 사용하는 과정을 보여준다.


c:\> irb --simple-prompt
>> load "base.rb"
=> true
>> load "person.rb"
=> true
>> p = Person.new("홍길동", "남자", 30)
=> #<Person:0x30d6f8 @name="홍길동", @gender="남자", @age=30>
>> p.name
=> "홍길동"
>> p.gender
=> "남자"
>> p.age
=> 30


Part 2에서는 my_attr_reader 클래스 메서드를 작성하는 과정을 통해 루비 메타프로그래밍의 핵심 테크닉을 살펴보았다. 다음 연재에서는 여기에서 익힌 메타프로그래밍 테크닉을 활용해 간단한 데이터베이스 ORM(Object/Relational Mapping) 프레임워크를 만들어 보기로 한다.



위로





필자 소개

황대산황대산 me@daesan.com

미국 예일대학교에서 수학을 전공했고, 현재 국내에서 프리랜서 개발자로 활동중이다. 루비/레일스 에반젤리스트를 자임하며 틈틈이 잡지 등에 관련 글을 기고하는 등 활발히 활동하고 있다. ‘웹 개발 2.0 루비 온 레일스’라는 책을 집필했다




이 문서 북마킹 하기

mar.gar.in mar.gar.in naver naver eolin eolin del.icio.us del.icio.us




developerWorks에 소개되었으면 하고 바라던 주제가 있으시거나, 관심 있는 기술에 대한 전문가의 지식과 견해가 궁금하시다면 Developer CoD 코너에 원하는 주제를 신청해주세요. 해당 분야 전문가를 필자로 섭외해, 여러분이 원하는 주제에 대한 맞춤형 컨텐츠를 제작해드립니다.
developer CoD 신청양식 다운로드   MS워드 아이콘   아래아한글 아이콘



[지난 Developer CoD 보기]


위로


사이트 여행

dW 커뮤니티
포럼 | 블로그 | Spaces
dW Student Community

로컬 컨텐츠

행사 및 세미나

기획 기사

개발자 입문

튜토리얼 및 교육

TOP 10 인기자료

SW 다운로드

RSS 피드

뉴스레터
  
자바스크립트가 작동이 중지되었습니다. 이 기능을 수행하시려면 브라우저에서 자바스크립스트를 작동시켜 주시거나 이곳을 클릭해주세요.
Special offers
Screencast
IBM SOA Sandbox 시험판
dW Student Community
로보코드
코드 트레이닝


    IBM 소개 개인정보 보호정책 문의