여러분, 모두 모여 앉으세요. 할아버지가 옛날이야기를 들려드리겠습니다. 인생이 고달팠던 그 시절, 젬(gem) 하나 설치하는 데도 머리를 감싸 쥐고 이를 악물며 고생해야 했던 시절 말입니다.
제가 루비를 처음 배우기 시작했을 무렵에는 번들러(Bundler)가 없었습니다. 젬을 설치하려면 정말 힘든 과정을 거쳐야 했죠. 레일즈(Rails)에서는 rake gems:install 명령어를 수없이 반복 실행하고, 도중에 발생하는 버그를 하나씩 잡아가며, 드디어 에러 없이 명령이 통과할 때까지 기다려야 했습니다. 오늘은 젬이 무엇인지, 어떻게 동작하는지 살펴본 후, 옛날 방식 그대로 젬을 하나 만들어 보겠습니다.
젬(Gem)이란 무엇일까요?
루비젬(RubyGem)은 다른 사람들이 작성한 기능을 내 코드에 손쉽게 확장해 넣을 수 있는 방법입니다. 예를 들어 인증/권한 관련 코드를 직접 작성하는 대신 Devise를 사용할 수 있고, 업로드된 이미지의 크기를 조절하고 싶다면 CarrierWave를 활용하면 됩니다. 이렇게 하면 재사용 가능한 코드를 작성해 다른 사람들과 공유할 수도 있습니다.
그럼 어떻게 동작할까요?
가장 기본적인 형태로, 젬은 코드와 <name>.gemspec 파일을 담고 있는 압축 디렉터리에 불과합니다. 이 .gemspec 파일에는 젬의 이름, 로드할 파일 목록, 의존성 등 젬에 대한 메타데이터가 들어 있습니다.
gem install 또는 bundle 명령어는 소스 저장소에서 zip 파일을 다운로드해 하드 드라이브에 압축을 해제합니다. 젬이 어디에 위치해 있는지 확인하려면 bundle info <gem name>을 실행하거나, bundle open <gem name>으로 젬 디렉터리를 바로 열어볼 수 있습니다.
애플리케이션에 젬을 로드하기 위해 RubyGems는 Kernel 클래스의 require 함수를 몽키패치(monkey-patch)합니다. 먼저 디스크에서 파일을 읽으려 시도하고, 실패하면 시스템에 설치된 각 젬에서 해당 파일을 찾습니다. 젬 안에서 파일을 발견하면 로드 경로(load path)에 추가하는 방식으로 해당 젬을 "활성화(activate)"합니다.
Bundler를 사용한다면 setup 호출 시점에 지정된 각 젬을 로드 경로에 추가합니다. 덕분에 RubyGems가 일일이 경로를 찾아 헤매는 수고를 덜 수 있고, Gemfile(.lock)에서 선택한 것과 다른 버전의 젬이 로드되는 상황도 방지됩니다.
직접 만들어 볼까요?
자신만의 젬을 만드는 가장 쉬운 방법은 Bundler로 젬 스캐폴드(scaffold)를 생성하는 것입니다. 여기에는 적절한 디렉터리 구조, 라이선스, 행동 강령(code of conduct), 테스트 환경까지 포함되어 있어 편리합니다.
하지만 오늘은 코드 파일 하나와 메타데이터를 담은 gemspec 파일 하나, 단 두 개의 파일만으로 미니멀한 젬을 직접 만들어 보겠습니다. 우리가 만들 젬은 호출되면 사용자에게 인사를 건넵니다. 먼저 젬을 위한 디렉터리를 생성해 보죠.
mkdir howdy
cd howdy
이 디렉터리 안에 코드를 담을 lib 폴더와 메타데이터를 담을 howdy.gemspec 파일을 만듭니다. 전체 구조는 대략 다음과 같습니다:
tree
.
├── howdy.gemspec
└── lib
└── howdy.rb
howdy 젬의 코드는 다음과 같습니다:
lib/howdy.rb
class Howdy
def greet
"howdy!"
end
end
미니멀한 howdy.gemspec 파일에는 버전, 저자 등의 정보가 담깁니다. 또한 젬을 빌드할 때 포함할 파일들을 지정합니다. 이를 통해 젬 사용자가 테스트 파일 등 젬 코드 실행에 불필요한 파일까지 다운로드하는 일을 막을 수 있습니다.
Gem::Specification.new do |spec|
spec.name = "howdy"
spec.version = "0.0.1"
spec.authors = ["Robert Beekman"]
spec.email = ["robert@example.com"]
spec.summary = %(Greets the user)
spec.description = %(Howdy is a gem that greets the user when called)
spec.license = "MIT"
spec.files = ["lib/howdy.rb"]
end
젬을 빌드하려면 gem build howdy.gemspec 명령어를 사용합니다. 그러면 여러분의 코드가 담긴 howdy-0.0.1.gem 파일이 생성됩니다. 다른 사람들이 젬을 사용할 수 있게 하려면 gem publish 명령어로 rubygems.org에 배포하면 됩니다.
마무리
지금까지 아주 기본적인 젬을 만들고 배포하는 데 필요한 단계를 살펴보았습니다. 젬의 역사를 탐험하고 올드스쿨 방식으로 직접 만들어 보는 과정이 즐거웠기를 바랍니다. 앞서 언급했듯이 이번 실습은 순수하게 교육 목적이었으며, 실제 개발에서는 Bundler로 젬 스캐폴드를 생성하는 방식을 권장합니다.
그럼 이만, 젊은이들! 좋은 아이디어나 질문, 의견이 있다면 언제든지 알려주세요.