루비 젬(Ruby gem)이란 무엇일까요?
젬은 다운로드하여 설치할 수 있는 패키지입니다. 설치된 젬을 require하면 루비 프로그램에 추가 기능을 손쉽게 더할 수 있습니다.
젬으로 할 수 있는 일:
- Rails 앱에 로그인 기능 추가하기
- 외부 서비스(API 등)와 손쉽게 연동하기
- 웹 애플리케이션 구축하기
이것은 몇 가지 예시일 뿐입니다. 모든 젬은 저마다 고유한 목적을 가지고 있습니다.
왜 젬을 사용할까요?
- 루비에서 라이브러리와 도구를 공유하는 표준 방식이기 때문입니다
- 젬의 파일 구조와 형식이 잘 정리되어 있어 동작 방식을 이해하기 쉽습니다
- 모든 젬에는 사양(spec) 파일이 포함되어 있어 의존성(필요한 다른 젬)을 명시하므로, 코드가 동작하는 데 필요한 모든 것을 갖추게 됩니다
RubyGems 덕분에 우리는 gem install 한 줄이면 도달할 수 있는 풍부한 라이브러리 생태계를 누릴 수 있습니다!
특정 프로젝트에 필요한 젬 목록은 Gemfile이라는 특수 파일에 작성하며, Bundler가 이를 자동으로 설치해 줍니다. 두 가지 모두 이 가이드 뒷부분에서 자세히 다룹니다.
대표적인 젬의 예시
- Rails — ActiveRecord, ActiveSupport 등 모든 구성 요소가 루비 젬으로 배포됩니다
- Pry — irb의 강력한 대안
- Nokogiri — 널리 쓰이는 XML·HTML 파서
대부분의 젬은 순수 루비 코드로 작성되어 있습니다.
일부 젬은 성능 향상을 위해 루비 C 확장(C extension)을 포함합니다. 이 확장은 젬을 설치할 때 자동으로 빌드되지만, 경우에 따라 RubyGems가 관리하지 않는 추가 소프트웨어를 직접 설치해야 할 수도 있습니다.
자, 이제 직접 젬을 만들어 보면서 젬을 구성하는 파일들을 살펴보며 더 깊이 알아보겠습니다.
루비 젬 만드는 방법
bundle gem <이름> 명령어 하나로 새 젬에 필요한 파일들을 준비할 수 있습니다.
예시:
bundle gem awesome_gem
젬은 다음과 같은 구조로 구성됩니다:
├── awesome_gem.gemspec
├── bin
│ ├── console
│ └── setup
├── Gemfile
├── lib
│ ├── awesome_gem
│ │ └── version.rb
│ └── awesome_gem.rb
├── Rakefile
├── README.md
└── test
├── awesome_gem_test.rb
└── test_helper.rb
.gemspec 파일 이해하기
.gemspec 파일에는 젬에 관한 모든 정보가 담겨 있습니다.
포함되는 항목:
- 젬 이름
- 젬 요약(짧은 설명)
- 작성자 이름
- 의존성 목록
- 젬에 포함할 파일 목록
- 선택 사항: 작성자 이메일 주소, 프로젝트 URL(홈페이지), 실행 파일, C 확장, 상세 설명 등
젬 버전 자체는 lib/<젬_이름>/version.rb 안에 상수로 정의됩니다.
gemspec 예시:
Gem::Specification.new do |spec| spec.name = "awesome_gem" spec.version = AwesomeGem::VERSION spec.authors = ["Jesus Castello"] spec.summary = "Example gem for article about Ruby gems" spec.files = Dir['**/**'].grep_v(/.gem$/) spec.require_paths = ["lib"] spec.add_development_dependency "bundler", "~> 1.16" spec.add_development_dependency "rake", "~> 10.0" spec.add_development_dependency "minitest", "~> 5.0" end
require_paths 배열은 젬을 require할 때 루비가 파일을 찾는 위치를 지정합니다. 덕분에 코드를 lib/<젬_이름>/ 아래에 두고 require "<젬_이름>/<파일_이름>" 형태로 불러올 수 있습니다.
예를 들어, lib/awesome_gem/parser.rb라는 파일은 젬 내 어디에서든 require "awesome_gem/parser"로 불러올 수 있습니다.
대부분의 require 문은 lib/<젬_이름>.rb(/lib 루트에 있는 유일한 파일)에 작성합니다. 젬을 require할 때 로드되는 바로 그 파일입니다!
개발 의존성 vs 런타임 의존성
add_development_dependency는 개발 과정에서 사용하는 젬(minitest, RSpec, pry 등)을 정의합니다.
반면 add_dependency는 실제 코드 동작에 필요한 젬을 정의합니다.
기본값으로 되어 있는 summary와 description을 수정하고 나면, bundle gem이 생성해 준 bin/console 프로그램으로 젬이 로드된 irb 세션을 열 수 있습니다.
예시:
$ bin/console irb(main):001:0> irb(main):002:0> irb(main):003:0> AwesomeGem => AwesomeGem irb(main):004:0> irb(main):005:0> AwesomeGem::VERSION => "0.1.0"
이후 gem build <이름>.gemspec으로 젬을 패키징하고, gem push로 rubygems.org에 배포하면 됩니다.
Bundler란 무엇인가?
루비 젬을 공부하다 보면 Bundler라는 용어도 자주 마주치게 됩니다.
그렇다면 Bundler는 정확히 무엇일까요?
Bundler는 의존성 관리 도구입니다.
"RubyGems가 이미 이 역할을 하지 않나?"라고 생각할 수 있습니다. 맞습니다. 하지만 RubyGems는 젬 자체의 의존성만 관리합니다.
여러분이 만드는 일반 루비 애플리케이션은 젬으로 패키징되지 않으므로 이 기능의 혜택을 받지 못합니다. 바로 그래서 Bundler가 존재하는 것입니다!
Gemfile 이해하기
Gemfile이라는 파일을 본 적이 있나요?
바로 여러분의 루비 애플리케이션에서 사용할 젬을 선언하는 곳입니다.
여기에 나열된 젬들은 별도로 require하지 않아도 자동으로 로드됩니다.
Gemfile의 모습:
ruby '2.5.0' gem 'rails', '~> 5.2.1' gem 'sqlite3' gem 'puma', '~> 3.11' gem 'bootsnap', '>= 1.1.0', require: false
Bundler(RubyGems 2.0부터는 RubyGems 자체도)는 이 파일을 읽어 지정된 버전의 젬들을 설치합니다.
bundle install 명령을 실행하면 다음과 같은 출력을 볼 수 있습니다:
Using turbolinks-source 5.1.0 Using turbolinks 5.1.1 Using uglifier 4.1.18 Using web-console 3.6.2 Bundle complete! 18 Gemfile dependencies, 78 gems now installed. Use `bundle info [gemname]` to see where a bundled gem is installed.
버전 지정 기호(~>)의 의미
Gemfile에서 젬 버전을 선언할 때 사용하는 ~> 같은 기호는 무엇일까요?
이 기호들은 버전 범위를 지정할 수 있게 해줍니다.
예를 들어 "1.2 이상 2.0 미만 버전을 원한다"고 표현할 수 있습니다.
gem 'puma', '~> 1.2'
~>는 다음 범위의 축약 표현입니다:
gem 'puma', '>= 1.2', '< 2.0'
~> 5.2.1의 경우 정확히 다음을 의미합니다:
'>= 5.2.1', '< 5.3'
버전 숫자가 구체적일수록 허용되는 버전 범위도 좁아집니다.
Gemfile 옵션과 Gemfile.lock
Gemfile에서 젬을 선언할 때 활용할 수 있는 몇 가지 옵션이 있습니다.
예를 들어, rubygems.org가 아닌 GitHub 같은 다른 소스에서 젬을 가져올 수 있습니다.
프로젝트의 최신 버전이 아직 rubygems.org에 배포되지 않았을 때 특히 유용합니다.
gem "rails", git: "git@github.com:rails/rails.git"
branch 옵션을 사용하면 master가 아닌 다른 브랜치의 코드를 사용할 수도 있습니다.
예시:
gem "awesome_print", git: "git@github.com:awesome-print/awesome_print.git", branch: "v2"
require: false 옵션
또 자주 보게 되는 옵션이 require: false입니다.
이것은 무슨 역할을 할까요?
Bundler에게 해당 젬을 자동으로 require하지 말라고 지시하는 것입니다. 즉, 필요할 때 코드에서 직접 require해야 합니다.
이 옵션은 사용 범위가 제한적인 젬에 유용합니다. 예를 들어 특정 rake 태스크에서만 사용하고 Rails 컨트롤러나 모델에서는 사용하지 않는 젬이 대표적입니다. 필요할 때만 로드하므로 앱의 메모리를 절약할 수 있다는 장점이 있습니다.
환경별 젬 그룹화
환경별로 젬을 그룹화할 수도 있습니다. 개발 환경에서만 설치·로드되는 젬(capybara, pry 등)을 따로 관리할 수 있습니다.
Gemfile.lock의 역할
마지막으로, Bundler는 Gemfile.lock 파일을 생성합니다.
둘의 차이는 무엇일까요?
Gemfile.lock은 자동 생성되며, 실제로 설치된 모든 젬의 정확한 버전을 기록합니다.
Bundler는 이 버전들을 그대로 설치하므로, 애플리케이션을 프로덕션에 배포하거나 다른 개발자와 프로젝트를 공유할 때 모두가 동일한 젬 세트로 작업하게 됩니다.
유용한 젬 & Bundler 명령어
| 명령어 | 설명 |
|---|---|
| gem list | 설치된 모든 젬 목록을 표시합니다. 이름으로 필터링하는 인자를 받을 수 있습니다 (예: gem list active) |
| gem which <이름> | 해당 젬이 설치된 경로를 알려줍니다. |
| gem search <이름> | 설정된 소스(기본값: rubygems.org)에서 젬을 검색합니다. 정규표현식을 사용할 수 있습니다 (예: gem search "\Aawesome_") |
| gem env | 젬 환경 정보(버전, 경로, 설정)를 표시합니다. |
| gem install <이름> -v <버전> | 특정 버전의 젬을 설치합니다 (예: gem install sinatra -v 2.0.0) |
| bundle viz | 현재 프로젝트의 의존성 그래프를 시각화하여 생성합니다. |
| bundle show | Bundler로 설치된 특정 젬의 정보를 표시합니다. Gemfile이 있는 폴더 안에서 실행해야 합니다. |
| bundle outdated | 현재 프로젝트에서 업데이트가 필요한 젬 목록을 표시합니다. --groups 옵션으로 그룹별 정렬이 가능합니다. |
| bundle console | 현재 프로젝트 Gemfile의 젬들이 로드된 irb 세션을 실행합니다. |
마무리
이번 글에서는 루비의 패키지 시스템인 RubyGems에 대해 알아보았습니다. 젬의 구조, Gemfile이란 무엇인지, Bundler의 역할, 그리고 Gemfile과 Gemfile.lock의 차이까지 함께 살펴보았습니다.
이 글이 여러분의 루비 개발에 도움이 되기를 바랍니다. 궁금한 점이 있다면 위의 명령어 표를 참고해 직접 실습해 보세요. 즐거운 코딩 되세요 🙂