React Native 에서의 Ruby, Bundler, Gemfile

eeensu·2026년 6월 11일

React Native

목록 보기
50/63

개요

React Native CLI 프로젝트를 개발하다 보면 bundle install, bundle exec pod install, bundle exec fastlane ios beta 같은 명령을 매일 마주친다. JS 개발자에게는 낯선 Ruby 생태계지만, 실은 JS의 pnpm/npm 세계와 1:1로 대응되는 구조다. 매핑이 머리에 들어오면 어렵지 않다.

이 문서는 같은 RN CLI 프로젝트를 실무에서 빌드·배포하는 데 꼭 알아야 하는 만큼만 정리한다.



1. 한눈에 보기 — JS와 Ruby의 의존성 관리 매핑

Ruby (Bundler)JS (pnpm/npm)역할
gemnpm 패키지재사용 라이브러리 단위
Gemfilepackage.json의존성 선언 파일
Gemfile.lockpnpm-lock.yaml정확한 버전 잠금 파일
Bundlerpnpm / npm의존성 관리 도구
bundle installpnpm install의존성 설치
bundle exec <cmd>pnpm exec <cmd> / npx프로젝트가 명시한 버전으로 실행
RubyGemsnpm registry패키지 저장소 (https://rubygems.org)
rbenv / asdfnvm / volta / fnm언어 런타임 버전 관리자
.ruby-version.nvmrc프로젝트의 런타임 버전 핀

이 표 하나가 사실상 전부다. "Ruby 진영의 의존성 관리 도구가 Bundler이고, 그 설정 파일이 Gemfile이다" — 끝.



2. 왜 RN 프로젝트에 Ruby가 끼어드는가

답은 단순하다. RN의 빌드·배포 체인이 Ruby로 만들어진 두 도구 위에 서 있기 때문이다.

RN 프로젝트
├── package.json     → JS 의존성 (pnpm이 관리)
├── ios/
│   ├── Podfile      → iOS 네이티브 의존성 (CocoaPods가 관리)  ← Ruby
│   └── fastlane/
│       └── Fastfile → iOS 배포 자동화 (Fastlane이 관리)        ← Ruby
├── android/
│   └── fastlane/
│       └── Fastfile → Android 배포 자동화 (Fastlane이 관리)    ← Ruby
└── Gemfile          → 위 Ruby 도구들의 버전 (Bundler가 관리)

RN 개발자가 실무에서 만나는 Ruby 도구는 두 가지다.

(1) CocoaPods — iOS 네이티브 의존성 관리자

  • iOS 네이티브 라이브러리(.framework, RN 네이티브 모듈 등)를 설치/링크하는 도구
  • **Ruby로 작성됨. 그래서 Gemfile에 gem 'cocoapods'가 들어 있다
  • RN 0.71+부터는 Gemfile이 RN 코어 템플릿에 기본 포함되어, 이 RN 버전에는 이 CocoaPods 버전을 써라라고 박아둔다. 환경마다 CocoaPods 버전이 다르면 같은 명령이 다른 결과를 내기 때문이다

(2) Fastlane — 모바일 빌드·배포 자동화

iOS/Android의 빌드, 코드 서명, 스토어 업로드를 스크립트화하는 CLI 이며 Ruby로 작성된다.

CocoaPods도 Fastlane도 Ruby로 만들어진 도구라서, RN 개발자도 Ruby 생태계를 어쩔 수 없이 한 번은 마주치게 된다. 다만 Ruby로 코드를 직접 짤 일은 거의 없다 — Fastfile을 손볼 때만 약간 필요할 뿐, 평소엔 명령어만 알면 된다.



3. Ruby 언어란?

  • 1995년 일본의 마츠모토 유키히로(Matz)가 만든 동적 객체지향 인터프리터 언어
  • macOS에 오랫동안 기본 탑재되어 있어, Apple 개발 도구 생태계의 사실상 표준이 되었다
  • 사람이 읽기 쉬운 문법 — 메서드 호출의 괄호를 생략할 수 있고 블록(do ... end)이 자연어처럼 읽힌다. 그래서 Fastfile, Podfile처럼 "설정 파일처럼 보이는 스크립트"를 만들기 좋다
# Ruby — Fastfile에서 흔히 보는 모양
lane :beta do
  match(type: "appstore")
  build_app(scheme: "my_app")
end

들여쓰기와 do ... end, 문자열 보간 "#{var}"만 익숙해지면 JS 개발자도 큰 어려움 없이 읽는다.



4. Ruby 셋업 — 어떤 Ruby가 실제로 잡히는가

Bundler·CocoaPods·Fastlane 모두 Ruby 위에서 동작하므로 Ruby가 먼저 깔려 있어야 한다. 그런데 macOS에는 Ruby가 들어오는 경로가 최소 세 가지라, 어느 게 잡히는지 의식하지 않으면 사람마다 결과가 달라진다.

Ruby가 들어오는 세 가지 경로

경로위치특징
macOS 시스템 Ruby/usr/bin/ruby보통 2.6.10. 권한 문제(gem installsudo), Apple도 점차 분리 중. 쓰지 말 것
Homebrew Ruby/opt/homebrew/opt/ruby/bin/ruby (Apple Silicon)brew install ruby로 설치. 항상 최신 한 버전만. 빠르고 간단하지만 버전 고정 불가
rbenv / asdf~/.rbenv/versions/X.Y.Z/...프로젝트별 버전 핀 가능. 팀 표준화하기 좋음

which ruby 한 번 쳐보면 자기 머신이 어느 경로를 쓰는지 즉시 알 수 있다.

.ruby-version 파일

프로젝트 루트의 .ruby-version이 프로젝트에 어떤 Ruby 버전을 써야 하는지를 명시한다. rbenv·asdf가 이 파일을 자동으로 읽어 해당 버전을 활성화한다. JS의 .nvmrc와 동일한 역할이다.

# .ruby-version 예시
3.1.4


5. Gem · Gemfile · Bundler · bundle exec

1) Gem

gem = Ruby의 패키지. fastlane, cocoapods, activesupport 모두 gem이다. npm 패키지와 정확히 같은 개념이다.

2) Gemfile

Gemfile은 Bundler가 읽는 의존성 선언 파일. 루트 Gemfile 일부:

source 'https://rubygems.org'           # 어디서 gem을 받을지

ruby ">= 2.6.10"                        # 호환 Ruby 버전

gem 'cocoapods', '>= 1.13', '!= 1.15.0', '!= 1.15.1'
gem 'activesupport', '>= 6.1.7.5', '!= 7.1.0'
gem 'xcodeproj', '< 1.26.0'

# Ruby 3.4부터 표준 라이브러리에서 빠진 것들 명시
gem 'bigdecimal'
gem 'logger'
gem 'benchmark'
gem 'mutex_m'

JS로 옮기면 이런 느낌이다.

{
  "dependencies": {
    "cocoapods": ">=1.13, !=1.15.1"
  }
}

다른 점은 두 가지.

  • 문법이 JSON이 아니라 Ruby DSL이다. gem 'cocoapods', '>= 1.13'은 그냥 Ruby 함수 호출이다.
  • != 1.15.0 같은 식으로 특정 버전 제외를 표현할 수 있다 — 빌드 실패를 일으키는 알려진 나쁜 버전을 피하는 장치다.

3) Gemfile.lock

bundle install이 자동 생성하는 잠금 파일. pnpm-lock.yaml과 같은 역할이다.

GEM
  remote: https://rubygems.org/
  specs:
    cocoapods (1.15.2)
    activesupport (7.2.3)

RUBY VERSION
   ruby 3.1.4p223

BUNDLED WITH
   2.6.7
  • Gemfile은 "범위"를, Gemfile.lock은 "정확한 버전"을 박는다
  • 반드시 git에 커밋한다. JS의 pnpm-lock.yaml을 커밋해야 하는 것과 똑같이, 이게 있어야 팀원 전원과 CI가 같은 버전을 쓴다
  • 없으면 매번 Bundler가 의존성을 새로 해석하므로, 사람마다 다른 버전이 깔릴 수 있다
  • BUNDLED WITH는 lock을 만든 Bundler 자신의 버전. RUBY VERSION은 해석된 Ruby 버전

4) Bundler

Bundler는 위 두 파일을 읽어서 의존성을 해결하고 설치하는 도구. Ruby 1.9 시절부터 사실상 표준이며 최신 Ruby에 기본 포함되어 있다(없다면 gem install bundler).

자주 쓰는 명령:

bundle install         # Gemfile.lock 기준으로 설치 (pnpm install)
bundle update          # 범위 내에서 최신으로 lock 갱신 (pnpm update)
bundle update cocoapods # 특정 gem만 갱신
bundle outdated        # 더 새 버전이 있는지 확인
bundle exec <cmd>      # Gemfile 환경으로 명령 실행 (핵심)

5) bundle exec가 왜 필수인가

이게 RN 개발자가 가장 헷갈리는 부분이다. 결론부터:

bundle exec <cmd>이 프로젝트의 Gemfile에 박힌 정확한 버전의 <cmd>를 실행하라는 뜻이다.

문제 상황:

# 시스템에 cocoapods 1.16.0이 전역 설치돼 있다고 하자
$ gem install cocoapods          # → 1.16.0 깔림

# 그런데 프로젝트 Gemfile.lock에는 cocoapods 1.15.2가 잠겨 있다

$ pod install                    # 어느 버전이 실행될까?
# → 시스템 PATH상의 1.16.0이 실행됨. 잠긴 버전과 다름!
# → 같은 명령이 사람마다 다른 결과를 낼 위험

bundle exec는 이 충돌을 막는다.

$ bundle exec pod install        # → Gemfile.lock의 1.15.2가 실행됨

내부적으로 Bundler가 LOAD_PATH(npm의 node_modules처럼 라이브러리를 찾는 경로)를 Gemfile에 적힌 gem들로 한정해서 명령을 실행한다. pnpm exec/npx가 로컬 node_modules/.bin을 우선 쓰는 것과 정확히 같은 원리다.

규칙: RN 프로젝트의 pod install, fastlane ...은 항상 bundle exec를 앞에 붙여 실행한다.



6. Fastlane — 모바일 배포 자동화

1) 무엇이고 왜 쓰는가

Fastlane은 iOS/Android 앱의 빌드, 코드 서명, 스크린샷, 스토어 업로드, 베타 배포를 자동화하는 오픈소스 CLI다. 2014년 Felix Krause가 개인 프로젝트로 시작해 Fabric을 거쳐 Google에 인수되었고, 현재는 커뮤니티 주도로 유지된다.

수동 배포에는 보통 이런 단계가 들어간다.

iOS
1. Xcode 열기 → scheme · configuration 선택
2. provisioning profile · 인증서 동기화
3. Info.plist의 버전·빌드 번호·display name 갱신
4. Archive → IPA export
5. App Store Connect 업로드 → TestFlight 처리

Android
1. versionName / versionCode 수정
2. 키스토어로 서명
3. ./gradlew bundleRelease로 AAB 생성
4. Play Console 업로드 → 트랙 지정

매번 사람이 하면 (a) 실수가 잦고 (b) 누가 했는지 추적이 어려우며 (c) CI에서 재현이 안 된다. Fastlane은 이걸 "코드 한 파일" 로 만든다.


2) 왜 Ruby로 작성되었나

역사적인 이유다.

  1. macOS에 Ruby가 기본 탑재되어 있었다. 2014년 당시 iOS 도구는 모두 macOS에서만 돌고, macOS에는 Ruby가 깔려 있었다 — 사용자에게 별도 런타임 설치를 요구하지 않아도 됐다
  2. iOS 도구 생태계가 이미 Ruby였다. CocoaPods, xcodeproj, xcpretty, danger 등 주변 도구가 전부 Ruby였다
  3. Ruby의 DSL 친화성. Fastfile은 사실 Ruby 코드지만 "설정 파일처럼" 읽힌다. Rails 라우팅, Podfile, Vagrantfile이 모두 Ruby DSL인 것과 같은 이유다

지금도 Ruby인 이유는 이미 사용자/플러그인/CI 환경이 굳어 있어 바꿀 이유가 없기 때문이다.


7. 실무 앱에서의 구조 예시

my_app/
├── Gemfile                       # 루트: cocoapods 중심 (iOS pod install용)
├── Gemfile.lock                  # 루트 잠금
├── ios/
│   └── fastlane/
│       ├── Fastfile              # iOS lane 정의 (dev/beta/pro/...)
│       ├── Appfile               # bundle id, Apple ID 등 메타
│       └── Matchfile             # match(인증서 동기화) 설정
└── android/
    ├── fastlane/
    │   ├── Fastfile              # Android lane 정의
    │   ├── Appfile               # 패키지명, json key 경로
    │   └── Pluginfile            # 플러그인: versioning_android
    ├── Gemfile                   # Android 전용: fastlane + plugin
    └── Gemfile.lock              # Android 전용 잠금
  • 루트 Gemfile은 CocoaPods 중심. pod install을 안정적으로 돌리기 위한 것
  • android/Gemfile은 Fastlane 중심. 안드로이드 빌드 자동화에 필요한 gem만 가짐
  • 실행 디렉터리에 따라 어느 Gemfile을 따르는지가 달라진다. 항상 cd ios && bundle exec ... 또는 cd android && bundle exec ... 식으로 디렉터리를 맞춰 실행하는 것이 안전하다


8. 실무 워크플로우

새 프로젝트를 클론했을 때

# 1) Ruby 버전 확인 (Gemfile.lock의 RUBY VERSION 기준)
ruby -v

# 2) Bundler 설치 (없다면)
gem install bundler

# 3) gem 설치 — 루트와 android 양쪽 필요
bundle install
(cd android && bundle install)

# 4) JS 의존성
pnpm install

# 5) iOS 네이티브 의존성
(cd ios && bundle exec pod install)

일상적으로

# iOS 네이티브 라이브러리 추가/업데이트 후
cd ios && bundle exec pod install

# iOS 베타 배포
cd ios && bundle exec fastlane beta

# Android 베타 배포
cd android && bundle exec fastlane beta

Gemfile을 수정했을 때

bundle install            # Gemfile.lock 갱신
git add Gemfile Gemfile.lock


9. 자주 마주치는 문제와 해결

증상원인해결
Could not find gem '...' in locked gemsGemfile.lock과 설치된 gem 불일치bundle install
Your Ruby version is X, but your Gemfile specified Y시스템 Ruby가 Gemfile 요구와 다름rbenv/asdf로 해당 버전 설치
pod install이 이상한 에러시스템 pod과 Gemfile의 pod 버전이 다름bundle exec pod install로 실행
match 단계에서 인증서 못 찾음인증서 repo 권한·MATCH_PASSWORD 부재repo 권한, 환경변수, Apple Developer 계정 점검
upload_to_play_store에서 401/403PLAY_STORE_JSON_PATH 부재/잘못.env 로드, 서비스 계정 권한 확인
bundle: command not foundBundler 미설치gem install bundler
Ruby 3.4 이후 빌드 깨짐표준 라이브러리에서 일부 모듈 빠짐루트 Gemfile처럼 bigdecimal, logger, benchmark, mutex_m 명시 (이미 적용됨)



10. 정리

bundle exec fastlane ios beta
└──┬───┘ └──┬──┘ └─┬┘ └─┬─┘
   │       │      │    └── Fastfile의 lane 이름 (실행할 작업)
   │       │      └─────── platform 지정 (default_platform 다르면 필요)
   │       └────────────── 실행할 CLI (fastlane gem이 제공)
   └────────────────────── Gemfile에 잠긴 버전으로 실행하라는 wrapper

세 줄로 요약하면:

  1. Gemfile = Ruby 진영의 package.json, 의존성 선언 파일
  2. Bundler = Ruby 진영의 pnpm, bundle install로 설치하고 bundle exec로 실행
  3. RN이 Ruby를 끌고 오는 이유는 CocoaPods + Fastlane 두 도구가 Ruby로 만들어졌기 때문. Ruby로 직접 코드를 짤 일은 거의 없고, 명령어와 위 매핑 표만 알면 충분히 다룰 수 있다
profile
안녕하세요! 프론트엔드 개발자입니다! (2024/03 ~)

0개의 댓글