Computer >> 컴퓨터 >  >> 프로그래밍 >> Ruby

루비 젬(Gem)은 어떻게 작동할까? CanCan 사례로 살펴보는 내부 동작 원리

Rails 개발이 매력적인 이유 중 하나는, 인증·권한 부여, 사이트 관리, 전자상거래 등 흔히 마주치는 프로그래밍 요구사항이라면 이미 실력 있는 개발자가 해결책을 만들어 '젬(gem)'이라는 패키지 형태로 공개해 두었다는 점입니다.

물론 Gemfile에 젬을 추가하기만 해도 그 편리함을 충분히 누릴 수 있습니다. 하지만 정작 이 코드들이 어떻게 프로젝트에 연결되고 동작하는지 궁금해 본 적은 없으신가요?

이번 글에서는 Ryan Bates가 만든 CanCan 인증 젬을 예로 들어 다음 세 가지 질문에 답해 보겠습니다. 시야를 조금 넓혀 보면 이 질문들은 사실 모든 젬에 적용됩니다.

  1. CanCan의 코드는 어디에 저장되어 있을까?
  2. can? 메서드는 어디에 정의되어 있을까?
  3. Rails는 언제, 어디에서, 어떻게 젬의 코드를 불러올까?

1단계: 젬 설치하기

먼저 Gemfile에 CanCan을 추가하고 bundle install을 실행합니다.

# Gemfile
gem "cancan", "~> 1.6.10"

$ bundle install

2단계: 젬 코드의 위치 찾기 — bundle show

Bundler를 사용하면 특정 젬이 어디에 설치되어 있는지 아주 쉽게 확인할 수 있습니다. 바로 bundle show 명령어입니다.

$ bundle show cancan
/Users/jasonswett/.rvm/gems/ruby-2.0.0-p0/gems/cancan-1.6.10

첫 번째 질문은 이걸로 해결됐습니다. 이제 can? 메서드는 어디에 정의되어 있을까요?

3단계: 메서드 정의 찾기 — grep

젬 디렉터리로 이동한 뒤 grep으로 메서드 정의를 검색해 보겠습니다.

$ cd /Users/jasonswett/.rvm/gems/ruby-2.0.0-p0/gems/cancan-1.6.10
$ grep -r 'def can?' *
lib/cancan/ability.rb: def can?(action, subject, *extra_args)
lib/cancan/controller_additions.rb: def can?(*args)

흥미롭게도 can? 메서드는 두 곳에 각각 정의되어 있습니다. 여기서 주목할 것은 lib/cancan/ability.rb에 있는 정의입니다.

# lib/cancan/ability.rb

def can?(action, subject, *extra_args)
  match = relevant_rules_for_match(action, subject).detect do |rule|
    rule.matches_conditions?(action, subject, extra_args)
  end
  match ? match.base_behavior : false
end

코드 내부 동작까지 완벽하게 이해하지 못해도 괜찮습니다. 중요한 포인트는 따로 있습니다. 바로 젬에서 문제가 발생했을 때, 이제 여러분도 직접 젬의 소스 코드를 파헤쳐 원인을 추적할 수 있다는 것입니다.

4단계: Rails는 언제 젬을 로드할까?

젬의 코드가 어디에 있는지, 그리고 그 안을 어떻게 들여다보는지 알았습니다. 그렇다면 Rails는 대체 언제 젬의 존재를 알아차리고 코드를 불러올까요?

답은 Rails 초기화(initialization) 과정 문서에서 찾을 수 있습니다. 관련 부분을 옮기면 다음과 같습니다.

표준 Rails 애플리케이션에는 애플리케이션의 모든 의존성을 선언하는 Gemfile이 있습니다. config/boot.rb는 ENV['BUNDLE_GEMFILE']을 이 파일의 경로로 설정합니다. Gemfile이 존재하면 bundler/setup이 require됩니다.

이 작업은 초기화 과정의 아주 이른 단계, 정확히는 두 번째 단계에서 일어납니다. 당연한 이치이기도 합니다. 프로젝트가 특정 젬에 의존한다면 그 젬을 참조하는 코드가 프로젝트 어디에든 존재할 수 있으므로, 최대한 일찍 로드해 두어야 애플리케이션의 어느 곳에서든 젬의 기능을 사용할 수 있기 때문입니다.

마무리

젬은 단순히 Gemfile에 한 줄 추가하는 것 이상의 의미를 지닙니다. bundle show로 설치 경로를 확인하고, grep으로 메서드 정의를 추적하면 겉모습만 사용하던 젬도 이제 자유롭게 탐구할 수 있습니다. 다음에 젬에서 예상치 못한 버그를 만난다면 주저하지 말고 소스 코드 속으로 들어가 보세요. 생각보다 문이 많이 열려 있습니다.