=
으로만 의존성을 설정하면 안 되나요?
Q: gem을 고정할 만한 가치가 있다고 생각합니다만, =
으로만
버전을 적어 모든 의존성을 Gemfile
에서 해결하고 Gemfile.lock
을
안 쓸 수는 없을까요?
A: gem은 각각의 의존성을 가집니다. 그리고 gem은 =
으로
의존성을 설정하지 않을 가능성이 있습니다. 더욱이, 모든 *gem들*의 의존성을 너무
엄격하게 고정하는 것은 좋은 생각이 아닙니다. Gemfile.lock
은 마지막으로
정상적으로 동작할 때의 애플리케이션의 모든 서드파티 코드를 정확히 기억하는 대신에,
애플리케이션에서 필요로 하는 의존성의 버전을 Gemfile
에 지정할 수 있도록 합니다.
nokogiri ~> 1.4.2
처럼 Gemfile
에서 느슨하게 의존성을 지정하면
bundle update nokogiri
를 실행해 ~> 1.4.2
의 요구 버전 범위를 여전히
만족하는 nokogiri
와 의존 gem**만** 업데이트할 수 있게 됩니다.
또, 정확한 버전 번호를 모르더라도 Gemfile
에서 gem 'nokogiri'
라고
적어 "난 nokogiri의 최신 버전을 쓰고 싶어."라고 말할 수 있습니다.
이 경우에도 여전히 애플리케이션이 서드파티 코드와 정확히 같은 버전으로 동작하는 것이
보장됩니다.
Q: 왜 gem을 관리하는데 번들러가 필요한지 이해하지 못하겠습니다. 그냥 gem을 받아서 서브모듈로 지정하고 각각의 서브모듈에 로드 경로를 지정하면 안 될까요?
A: 불행히도 그 해결책은 의존성의 의존성을 포함해서 모든 의존성을
수동으로 해결해야 할 필요가 있습니다. 한 번은 성공했다 하더라도, gem을 부분적으로 업데이트
할 때마다 재작업이 필요합니다. 예를 들어 rails
gem을 업데이트 하려 한다면
레일스의 의존성을 위한 모든 gem(rack
, erubis
, i18n
,
tzinfo
등)을 찾고 새 버전의 레일스의 의존성을 만족하는 버전을 찾을 필요가
있습니다.
솔직히, 이 문제는 컴퓨터가 풀기 좋은 문제입니다. 개발자가 시간을 낭비할 필요는 없죠.
더 걱정되는 것은, 수동 의존성 해결 과정에서 실수를 하면, 다른 의존성 간의 충돌에 대한
피드백 대신에 미묘한 런타임 에러를 받게 된다는 점입니다. 예를 들어 서브모듈에서 실수로
잘못된 rack
버전을 사용한다면, 레일스나 다른 의존성 관계의 gem이
존재하지 않는 메소드를 호출하려 해 정상적으로 실행되지 않습니다.
결론: 처음엔 간단해 보일지 몰라도 확실히 더 복잡합니다.
--without
그룹 안에 있는 gem을 다운로드하죠?
Q: bundle install --without production
을 실행했지만
번들러가 여전히 :production
그룹에 있는 gem을 다운로드 합니다. 왜죠?
A: 번들러의 Gemfile.lock
은 넘기는 옵션에 관계없이
Gemfile
의 모든 의존성에 대한 정확한 버전정보를 가지고 있습니다. 그렇게
안 한다면 프로덕션에 배포할 때 모든 의존성을 바꾸게 되고, 번들러의 장점을 없애게 될
것입니다. 더 이상 애플리케이션에서 개발, 테스트에 사용하는 gem이 프로덕션에서
사용하는 gem과 같은 gem이라는 확신을 못하게 됩니다. 덤으로 프로덕션의 의존성 추가는
배포 불가능 상태를 만들 수도 있습니다.
프로덕션에서만 사용하는 gem rack-debugging
이 있다고 해봅시다. rack-debugging
은
rack =1.1
에 의존합니다. bundle install --without production
으로
실행할 때는 프로덕션 그룹이 평가되지 않지만, 애플리케이션을 배포할 때 rack-debugging
이
rails
와 충돌한다는 메시지를 받게 될 것입니다. 왜냐하면 rails
가
의존하는 actionpack
은 rack ~> 1.2.1
을 필요로 하기 때문입니다.
다른 예로 Gemfile
에 gem 'rack'
이 있는 간단한 Rack 애플리케이션이
있다고 해봅시다. 거기의 :production
그룹에 rack-debugging
을
넣었다고 하죠. bundle install --without production
을 통해 설치했을 때는
:production
그룹은 평가되지 않고 애플리케이션은 개발환경에서
rack 1.2.1
을 사용하게 됩니다. 그리고 이 상황은 이미 배우셨지만,
rack-debugging
이 테스트했던 Rack의 버전과 충돌하게 됩니다.
반면에, bundle install
을 호출할 때 환경에 상관없이 **모든** 그룹을 평가하면,
rack-debugger
의 의존성을 발견하고 rack 1.1
을 합니다.
rack 1.1
은 Gemfile
안의 gem 'rack'
과도 호환되죠.
요약하자면, Gemfile 안의 의존성과 관계없이 모든 의존성을 항상 평가함으로써, 다른 환경에서 다른 그룹들을 사용하다 특정으로 환경을 바꾸려 할 때 발생할 수 있는 불편한 점을 미리 방지할 수 있습니다. 그리고 gem을 설치하지 않고 다운로드만 하므로, 프로덕션이나 개발 환경에서 gem의 **설치** 프로세스가 어려워질 것을 걱정할 필요가 없습니다.
Q: mysql
과 같은 설치할 때 특별한 플래그를 필요로 하는 C 확장 gem이 있습니다.
설치할 때 어떻게 플래그 값을 넘길 수 있을까요?
A: 먼저, mysql
gem의 대용품(drop-in replacement)인 mysql2
gem을
사용하면 문제 없습니다. 일반적으로 요즘 C 확장들은 필요한 헤더를 찾을 수 있습니다.
만약 정말로 플래그를 넘길 필요가 있다면, bundle config
명령어를 사용하시면 됩니다.
$ bundle config build.mysql --with-mysql-config=/usr/local/mysql/bin/mysql_config
번들러는 ~/.bundle/config
에 설정을 저장할 것이고, 이 설정은 같은 사용자가 bundle install
을
하면 매번 사용될 것입니다. 결과적으로 한 번만 필요한 gem에 빌드 플래그를 지정하면 그 gem은 언제나 성공적으로
설치 할 수 있습니다.
Q: 지금 인터넷에 접속할 수 없지만, 이전에 gem을 설치해 두었습니다. 어떻게 하면 번들러에서 gem 서버에 접속하지 않고 로컬 gem 캐시를 사용하게 할 수 있을까요?
A: bundle install 할 때 --local 플래그를 사용하시면 됩니다. --local 플래그는 번들러에게 원격 gem 서버 대신 로컬 gem 캐시를 사용하게 합니다.
$ bundle install --local
Q: rubygems 서버에서 번들러를 실행하면 매우 느립니다. 좀 더 빠르게 할 수는 없을까요?
A: rubygems 서버에서 번들러를 실행할 때 --full-index 플래그를 사용하세요. 이렇게 하면 api에 매번 요청을 보내는 대신에 모든 인덱스를 한 번에 다운로드합니다.
$ bundle install --full-index