본문 바로가기

Capacitor 플러그인 패치 방법 (Bun, npm, pnpm)

bun patch, patch-package 또는 pnpm patch를 사용하여 Capacitor plugin을 패치하고, iOS 및 Android 네이티브 수정을 적용하며, fork 또는 upstream 여부를 결정하는 방법을 알아봅니다.

Capacitor 플러그인 패치 방법 (Bun, npm, pnpm)

Capacitor plugin을 패치하려면 다음 경로에서 plugin 파일을 수정하세요: node_modules변경 사항을 다음 명령어로 diff 파일로 저장하세요: bun patch --commit (또는 patch-package npm 및 Yarn 사용 시, pnpm patch-commit pnpm 사용 시), 생성된 파일을 커밋하여 patches/, 그런 다음 실행하세요 bunx cap sync 네이티브 프로젝트에 반영하세요. 패키지 매니저가 팀원과 CI 환경의 모든 install 과정에서 패치를 자동으로 다시 적용합니다.

플러그인에 작은 버그가 있거나, 빌드를 막는 버전 제약이 있거나, upstream에 수정 사항이 머지되었지만 아직 릴리스되지 않은 경우 패칭(Patching)이 가장 좋은 해결책입니다. 이 가이드에서는 각 패키지 매니저별 워크플로우, 네이티브 패치가 Xcode와 Gradle에 반영되는 방식, 그리고 fork나 upstream PR이 더 나은 선택인 경우를 다룹니다.

네이티브 플러그인 code 패칭이 효과적인 이유

Capacitor 플러그인은 세 가지 부분으로 구성된 npm 패키지입니다: JavaScript는 dist/에 있으며, Android code는 android/에, iOS code는 ios/ 에 위치하며, podspec 및/또는 Package.swift를 포함합니다. Capacitor는 네이티브 파트를 다음에서 직접 컴파일합니다: node_modules:

  • Android: android/capacitor.settings.gradle 각 플러그인의 다음 항목을 포함합니다: android 다음 폴더에서 node_modules.
  • CocoaPods를 사용하는 iOSPodfile이 각 플러그인을 참조합니다. :path => '../../node_modules/...'.
  • SPM을 사용하는 iOS: CapApp-SPM/Package.swift 로컬 경로로 각 플러그인을 참조합니다. node_modules.

따라서 ~를 변경하면 node_modules/@scope/plugin/ios/Sources/... 재빌드 후 앱 바이너리에 반영됩니다. cap sync 별도로 배포할 필요는 없습니다.

패치를 적용하기 전에

  1. 새 릴리스가 있는지 확인하세요. 플러그인의 changelog와 열려 있는 PR을 확인하세요. 패치하는 것보다 업그레이드하는 것이 더 좋습니다.
  2. 라이선스를 확인하세요. MIT 및 Apache-2.0은 개인적인 수정을 허용합니다. GPL 또는 MPL-2.0 같은 copyleft 라이선스는 앱 배포 시 수정된 소스 코드를 공개해야 할 수도 있습니다. 배포 전에 반드시 확인하세요.
  3. 이유를 기록해 두세요. 커밋 메시지에 이슈 URL이나 한 줄 요약을 남기세요. 6개월 뒤에는 아무도 왜 그렇게 했는지 기억하지 못합니다. patches/ 파일이 포함되어 있습니다.

Bun를 이용한 패치 방법

Bun에는 패치 기능이 내장되어 있습니다. 별도의 패키지나 postinstall 스크립트가 필요 없습니다.

# 1. Prepare the package for editing
bun patch @capacitor/haptics

# 2. Edit files under node_modules/@capacitor/haptics

# 3. Save the patch
bun patch --commit node_modules/@capacitor/haptics

Step 1을 통해 Bun에서 패키지의 프라이빗 복사본을 node_modules가져오므로, 수정 사항이 Bun의 글로벌 캐시에 영향을 주지 않습니다. Step 3에서 diff를 작성하고 이를 package.json:

{
  "patchedDependencies": {
    "@capacitor/haptics@8.0.2": "patches/@capacitor%2Fhaptics@8.0.2.patch"
  }
}

둘 다 커밋하세요 package.json 및 patches/ 폴더입니다. 매 bun install, 다음을 포함하여 bun install --frozen-lockfile CI에서 패치가 적용됩니다.

키에는 정확한 버전이 포함됩니다. 플러그인 버전을 올리면 Bun이 더 이상 패치와 일치하지 않으므로, 새 버전에 맞춰 다시 생성하거나 수정 사항이 반영되었다면 삭제하세요.

npm 또는 Yarn의 patch-package로 패치하기:

npm install --save-dev patch-package

다음 postinstall 설치할 때마다 패치가 적용되도록 스크립트를 설정하세요:

{
  "scripts": {
    "postinstall": "patch-package"
  }
}

그 다음 다음 폴더의 파일을 수정하고 node_modules 패치를 생성하세요:

npx patch-package @capacitor/haptics

다음 파일을 작성합니다 patches/@capacitor+haptics+8.0.2.patch. 커밋하세요. 업그레이드 후 패치가 적용되지 않는다면, patch-package 명확한 메시지와 함께 설치에 실패합니다.

Yarn Berry는 자체적인 yarn patch <pkg> / yarn patch-commit -s <path> 흐름을 통해 패치를 resolutions 필드에 저장합니다.

pnpm으로 패치하기

pnpm patch @capacitor/haptics
# edit the temporary folder pnpm prints
pnpm patch-commit <path-printed-above>

pnpm은 패치를 아래에 기록하고 patchedDependencies 설치 시 이를 적용합니다.

예시: 플러그인의 Native Build 오류 수정하기

흔한 사례: 앱은 AGP 9를 사용하는데, 오래된 플러그인이 android/build.gradle 여전히 다음을 참조하여 proguard-android.txtAGP 9에서 거부되는 경우입니다. 새 릴리스를 기다릴 수 없을 때 유용합니다.

bun patch some-capacitor-plugin

편집 node_modules/some-capacitor-plugin/android/build.gradle:

 buildTypes {
     release {
         minifyEnabled false
-        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
     }
 }

저장 및 동기화:

bun patch --commit node_modules/some-capacitor-plugin
bunx cap sync android
cd android && ./gradlew assembleDebug

이 에러의 전체 배경은 다음에서 확인하세요: AGP 9에서 Capacitor 플러그인 빌드 에러 해결하기.

예시: iOS 의존성 제약 조건 완화하기

두 플러그인이 동일한 SDK의 서로 다른 버전을 고정하면 CocoaPods가 이를 해결하지 못합니다. 플러그인이 더 새로운 SDK에서 작동하는 것을 확인했다면, podspec의 제약을 완화하세요:

-  s.dependency 'FirebaseMessaging', '11.15.0'
+  s.dependency 'FirebaseMessaging', '>= 11.15.0', '< 13.0'

SPM 앱의 경우 동일한 제약 조건은 플러그인의 다음 위치에 있습니다: Package.swift:

-  .package(url: "https://github.com/firebase/firebase-ios-sdk.git", exact: "11.15.0")
+  .package(url: "https://github.com/firebase/firebase-ios-sdk.git", "11.15.0"..<"13.0.0")

그 다음:

bunx cap sync ios

CocoaPods에서는 다음이 필요할 수 있습니다: cd ios/App && pod update FirebaseMessaging 한 번만 실행하세요. 왜냐하면 Podfile.lock 여전히 이전 버전을 가지고 있기 때문입니다. SPM에서는 다음을 사용하세요: File > Packages > Resolve Package Versions Xcode에서.

Capacitor 코어 자체를 패치하기

때로는 버그가 @capacitor/android, @capacitor/ios 또는 CLI에 있으며, 해결책은 업스트림 PR에 포함되어 있을 수 있습니다. 동일한 방식으로 해당 패키지들을 패치할 수 있지만, 모든 팀이 결국 똑같은 diff를 직접 관리해야 하는 번거로움이 생깁니다.

Capgo는 이를 위해 @capgo/capacitor-patch 를 제공합니다. 이는 업스트림 PR과 연결된 버전별 소규모 패치 카탈로그를 담고 있는 hook 전용 패키지입니다. 사용자가 직접 선택하기 전까지는 아무런 동작도 하지 않습니다:

bun add @capgo/capacitor-patch
bunx capgo-capacitor-patch list --all
import type { CapacitorConfig } from '@capacitor/cli';

const config: CapacitorConfig = {
  appId: 'com.example.app',
  appName: 'Example',
  webDir: 'dist',
  plugins: {
    CapacitorPatch: {
      patches: ['upstream-pr-8418-android'],
      strict: true,
    },
  },
};

export default config;

패키지 패치가 먼저 실행됩니다 cap sync 및 cap update, 그리고 네이티브 프로젝트 패치는 그 이후에 실행됩니다. bunx capgo-capacitor-patch doctor does a dry run. With strict: true를 사용할 때 선택한 패치가 더 이상 적용되지 않으면 sync가 실패하며, 이를 통해 업그레이드로 인해 패치가 불필요해졌음을 알 수 있습니다. 자세한 내용은 플러그인 문서 의 전체 옵션 목록을 확인하세요.

Patch, fork, 아니면 upstream?

상황 가장 좋은 선택지
몇 줄의 수정, 이미 upstream에 머지됨 Patch 후 다음 릴리스 때 제거
버전 제약이 너무 엄격함 Patch 후 upstream에 issue 제기
upstream에 아직 수정 사항 없음 Patch 후 동일한 diff로 PR 전송
새로운 기능 또는 대규모 리팩토링 Fork 후 본인 scope로 배포
플러그인 관리 중단됨 Fork를 생성하거나 유지 관리되는 플러그인으로 교체하세요

올바른 Fork 방법

Fork를 했다면, 설치 재현성을 위해 패키지를 배포하세요:

# in the fork, rename to your scope in package.json: "@acme/capacitor-foo"
bun run build
bun publish --access restricted

git URL을 통한 설치도 가능하지만, 플러그인의 dist/ 폴더가 해당 브랜치에 존재하거나 스크립트로 prepare 빌드되어야 합니다. 모든 패키지 매니저가 기본적으로 의존성 라이프사이클 스크립트를 실행하는 것은 아니므로, 패키지를 배포하는 것이 가장 확실합니다.

npm 이름은 CLI이 SPM을 위해 생성하는 네이티브 모듈 이름을 변경하므로 (@acme/capacitor-foo 로 변경됩니다 AcmeCapacitorFoo) Fork한 프로젝트의 설정을 Package.swift 그에 맞춰 업데이트해야 합니다. 자세한 내용은 Capacitor 플러그인을 SPM으로 마이그레이션.

기능 누락으로 플러그인을 Fork하기 전에 먼저 확인하세요: 그리고 Capgo 플러그인 디렉토리. 이미 해당 기능을 지원하는 유지 관리 중인 플러그인이 있을 수도 있습니다.

Upstreaming

목표는 패치를 삭제하는 것입니다. 최소한의 재현 코드(repro), Capacitor 및 플러그인 버전, 그리고 패치 diff를 포함하여 이슈를 생성하세요. 그 다음 PR을 올리세요. 메인테이너는 이슈 리포트만 있는 것보다 작고 검증된 수정 사항을 훨씬 더 빠르게 머지합니다.

패치와 Live Update

웹 번들은 다음 경로의 플러그인 JavaScript를 포함합니다: dist/, so a patch to the JS side ships with the next web build. With Capgo Live Update 사용하기 스토어 출시 없이도 사용자에게 도달할 수 있습니다.

Native 패치는 다릅니다. Swift, Kotlin, Java, Gradle, podspec 등의 변경 사항은 Package.swift 스토어에 제출된 새로운 Native Build에서만 적용됩니다. 이전 바이너리를 실행 중인 사용자에게 Native 패치에 의존하는 웹 번들을 배포하지 마세요. Capgo의 호환성 체크 번들과 채널 간의 네이티브 플러그인 버전을 비교하여 이를 감지합니다.

문제 해결

패치가 CI에서 적용되지 않았습니다. 다음을 확인하세요: patches/ 커밋되었으며 무시되지 않았는지 확인하세요. patch-package의 경우 CI에서 다음을 사용하지 않는지 확인하세요. --ignore-scripts, 이 과정에서 다음이 건너뛰어집니다. postinstall. Bun의 경우, 다음을 확인하세요. patchedDependencies 커밋된 상태에 포함되어 있는지 확인하세요. package.json.

네이티브 변경 사항이 반영되지 않습니다. 파일을 수정했지만 다시 빌드하지 않았습니다. 다음을 실행하세요. bunx cap sync그 다음 클린 작업을 수행하세요: Xcode에서 Product > Clean Build Folder, Android의 경우 ./gradlew clean. SPM 사용 시 패키지 캐시도 초기화하세요.

bun patch --commit 변경 사항이 없다고 표시됩니다. 패키지에 포함되지 않은 파일(예: 다른 폴더에 호이스팅된 복사본)을 수정했습니다. 1단계에서 출력된 Bun 경로를 수정하세요.

플러그인 업그레이드 후 패치가 실패합니다. 새 릴리스에 수정 사항이 포함되었는지 확인하세요. 포함되지 않았다면 새 버전에 대해 패치를 다시 진행하세요.

플러그인은 빌드되지만 해당 기능 실행 시 앱이 크래시됩니다. 패치는 플러그인 자체 테스트 스위트를 건너뜁니다. 패치된 경로를 양쪽 플랫폼의 실제 기기에서 테스트하세요. Capacitor iOS 트러블슈팅 가이드 및 Android 트러블슈팅 가이드에서 네이티브 로그를 읽는 방법을 확인할 수 있습니다.

Capacitor 앱용 Live updates

웹 레이어 버그가 라이브 상태라면, 앱 스토어 승인을 며칠씩 기다리지 말고 Capgo를 통해 수정 사항을 배포하세요. 네이티브 변경 사항은 정상 리뷰 절차를 그대로 따르는 반면, 사용자는 백그라운드에서 업데이트를 받습니다.

블로그 최신 글

Capgo는 정말 프로페셔널한 모바일 앱을 만드는 데 필요한 최고의 인사이트를 제공합니다.