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

루비 젬(Ruby Gems), Gemfile, 번들러(Bundler) 완벽 가이드

루비 젬(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의 역할, 그리고 GemfileGemfile.lock의 차이까지 함께 살펴보았습니다.

이 글이 여러분의 루비 개발에 도움이 되기를 바랍니다. 궁금한 점이 있다면 위의 명령어 표를 참고해 직접 실습해 보세요. 즐거운 코딩 되세요 🙂