본문 바로가기

Differential Updater 교체 방법: Capacitor 가이드

Capgo를 사용하여 Capacitor 앱의 differential updater를 교체하는 방법을 알아보세요. code 변경 사항, CI, 안전한 롤아웃을 다루는 단계별 마이그레이션 가이드입니다.

Differential Updater 교체 방법: Capacitor 가이드

금요일 오후, 프로덕션 수정 사항이 준비되었지만 Capacitor 앱의 live update 메커니즘 때문에 배포가 도박처럼 느껴집니다. 어떤 기기는 새 bundle을 받지만, 다른 기기는 이전 에셋 세트를 유지하며, 롤백 경로는 어떤 버전이 먼저 도착했느냐에 따라 다르게 동작합니다. 처음 설치할 때는 단순한 통합처럼 보였던 updater를 교체하는 작업은 differential을 설정하는 것만큼 까다롭습니다. 기존 구성 요소를 깔끔하게 제거해야 하고, 새 구성 요소는 베이스와 일치해야 하며, 모든 측정값은 재현 가능해야 합니다.

이러한 비교가 중요한 이유는 업데이트 시스템이 단순한 download 버튼이 아니기 때문입니다. 시스템은 버전을 추적하고, bundle을 재구성하며, native와 JavaScript 경계를 적용하고, 패키지가 불완전하거나 호환되지 않을 때의 동작을 결정합니다. 실질적인 문제는 단순히 differential delivery를 어떻게 교체하느냐가 아닙니다. 마이그레이션 과정에서 또 다른 장애 요인을 만들지 않고 어떻게 updater를 교체하느냐입니다.

목차

새로운 업데이트 방식이 필요한 이유

첫 번째 경고는 보통 운영상의 미스터리로 나타납니다. 릴리스는 로컬 테스트를 통과하고 서버에는 업로드가 성공했다고 뜨지만, 고객 지원팀에는 사용자가 여전히 이전 화면을 보고 있다는 보고가 들어옵니다. 엔지니어링 팀이 번들 식별자, Native Build 버전, 채널 할당, 디바이스 로그를 대조해 보면, 업데이트 도구가 로컬에 저장된 것과 다른 베이스로 빌드된 패키지를 수락했다는 사실을 발견하게 됩니다.

이는 기어 설정이 잘못된 디퍼런셜과 비슷합니다. 부품은 호환되어 보이지만, 실제 구동 시 소음이 발생하죠. Capacitor 앱에서는 이러한 소음이 일관성 없는 에셋 상태, 추출 실패, 무한 재시도, 또는 번들의 일부만 복구되는 불완전한 롤백 등의 형태로 나타납니다.

유용한 진단은 가정이 아닌 증상에서 시작됩니다:

  • 버전 불일치: 설치된 네이티브 쉘, 다운로드된 번들, 그리고 업데이트 메타데이터가 의도한 호환 범위를 일치시키고 있는지 확인하세요.
  • 불분명한 롤백: 업데이트 실패 시 이전 번들이 완전히 복구되는지, 아니면 오래된 파일이 남는지 테스트하세요.
  • 부족한 관측성: 영향을 받은 디바이스, 패키지 버전, 채널, 실패 단계, 그리고 마지막 성공 실행 시점을 식별할 수 있는지 확인하세요.
  • 제어되지 않는 타겟팅: 베타, 스테이징 또는 특정 고객용 스트림용 아티팩트를 프로덕션 사용자가 받고 있는지 확인하세요.
  • 비효율적인 전송: 웹 에셋이 소량만 변경되었음에도 시스템이 전체 아카이브를 전송하는지 측정해 보세요. 이러한 설계 트레이드오프의 배경은 다음에서 다룹니다. delta update가 페이로드 크기를 줄이는 방법.

전문 자동차 정비사가 서스펜션 결함을 진단하기 위해 손전등으로 차량 하부를 점검하고 있습니다.

교체하기 전에 먼저 진단하세요

업데이트 실패를 무조건 업데이트 도구 자체의 결함으로 간주하지 마세요. 잘못된 빌드 아티팩트, 잘못된 채널 규칙, 손상된 signing secret 또는 Native Build 호환성 변경도 동일한 증상을 일으킬 수 있습니다. 패키지 ID, 베이스 버전, 디바이스 상태, 업데이트 적용 후 앱 실행 성공 여부를 포함하여 실패 경로를 먼저 파악하세요.

메커니즘이 신뢰할 수 있는 답변을 제공하지 못하거나 안전한 복구가 불가능할 때 교체가 정당화됩니다. 팀에서 어떤 디바이스가 번들을 받았는지 증명할 수 없거나, 잘못된 릴리스를 빠르게 무효화할 수 없거나, 베이스 파일 누락 시 전체 패키지로 폴백(fallback)되는 것을 보장할 수 없다면, 설정 패치만으로는 다음 장애를 늦출 뿐입니다.

이 엔지니어링 원칙은 기어 어셈블리와 같습니다. 하우징을 제거하기 전에 결함이 있는 컴포넌트를 먼저 확인하세요. 전체 업데이터 마이그레이션은 초기 비용이 더 크지만, 불확실한 설치 위에 또 다른 조정을 덧대는 대신 깔끔한 프로토콜, 명시적인 버전 규칙, 테스트 가능한 롤백 경계를 제공합니다.

기존 메커니즘을 안전하게 제거하기

제거를 통제된 격리 절차로 다루세요. 패키지를 삭제한 뒤 다음 빌드에서 무엇이 남는지 확인하는 것이 목표가 아닙니다. 목표는 두 업데이트 메커니즘이 같은 웹 디렉토리, startup hook, 설정 키, 전달 엔드포인트를 두고 경쟁하지 않도록 하는 것입니다.

먼저 마이그레이션 브랜치를 동결하고 현재 동작을 기록하세요. 활성 플러그인 버전, 네이티브 설정, 환경 변수, CI 명령, 채널 이름, 다운로드된 bundle이 저장되는 위치를 기록해 두세요. 첫 클린 빌드가 실행될 때 의도한 변경과 실수로 빠뜨린 항목을 구분하려면 이 목록이 필요합니다.

클린 베이스라인 구축하기

임시 브랜치나 작업 복사본을 만든 다음, 의존성 트리와 Capacitor 설정을 검토하세요. “update”로 뭉뚱그려 검색하기보다 실제 패키지 이름으로 레거시 업데이터 패키지를 식별하세요. 다음 항목을 검토하세요: package.jsonlockfiles, capacitor.config.tsiOS 프로젝트 설정, Android 리소스, 그리고 download, sync, install 또는 rollback 메서드를 호출하는 startup code.

레포지토리에서 사용하는 패키지 매니저로 의존성을 제거하세요. 예를 들어 npm의 경우:

npm uninstall <legacy-updater-package>
npx cap sync

프로젝트 표준이 pnpm이나 Yarn이라면 해당 명령어를 사용하세요. 플레이스홀더는 의도적으로 배치한 것입니다. 마이그레이션 가이드가 임의의 패키지 이름을 만들어 잘못된 의존성에 대해 파괴적인 명령을 실행하도록 유도해서는 안 됩니다.

그 다음, 저장소에서 이전 플러그인 식별자, API 호출, 엔드포인트 이름, 설정 키, 이벤트 리스너를 검색하세요. 애플리케이션 레이어에서 불필요한 import와 초기화 code를 삭제하세요. 사용하지 않는 래퍼(wrapper)도 제거해야 합니다. 삭제된 업데이터를 호출하는 로컬 추상화 코드를 남겨두면, 빌드는 성공하더라도 앱 실행 시 문제가 발생하여 나중에 디버깅하기가 훨씬 어려워집니다.

네이티브 잔여물 정리

의존성 제거 후 iOS 및 Android 프로젝트를 엽니다. 패키지 매니저가 정리하지 못한 수동 추가 pod, Gradle 의존성, manifest 항목, 리소스 값, URL scheme, receiver 선언, 초기화 훅 등 네이티브 참조를 제거하세요. 단순히 이름이 관련 있어 보인다고 해서 파일을 삭제하지 마세요. 각 파일이 기존 업데이터에만 전용으로 속하는지, 그리고 공유 Capacitor 설정 중 해당 파일에 의존하는 것이 없는지 반드시 확인해야 합니다.

교체할 패키지를 설치하기 전에 네이티브 의존성을 새로고침하고 양쪽 플랫폼을 모두 컴파일하세요. 이렇게 하면 정리 과정의 실패와 통합 과정의 실패를 구분할 수 있습니다. 깨끗한 네이티브 빌드가 성공했다고 해서 마이그레이션이 완벽하다는 뜻은 아니지만, 빌드가 실패한다면 기존 메커니즘이 완전히 제거되지 않았음을 의미합니다.

스크립트에도 동일한 원칙을 적용하세요. CI에서 오래된 upload 작업, 엔드포인트 변수, 서명 단계, artifact 복사 명령 및 릴리스 알림을 제거해야 합니다. 프로덕션 워크플로우뿐만 아니라 pull-request 워크플로우도 확인하세요. 방치된 preview job은 테스터가 받기 전까지는 문제가 없어 보이는 채널에 호환되지 않는 패키지를 배포할 위험이 있습니다.

실무 원칙: 기존 updater를 변경하고 새 updater를 추가하는 작업을 동일한 커밋에서 처리하지 마세요. 기존 기능을 명확히 제거한 뒤 빌드하고, 그 다음에 교체 작업을 진행해야 합니다.

다음 단계로 넘어가기 전에, 정리된 앱을 테스트 기기에 설치하여 더 이상 기존 서비스에 접속을 시도하지 않는지 확인하세요. 필요한 경우 앱 데이터를 삭제하고, startup logs를 점검하며, 앱이 bundled web assets를 정상적으로 로드하는지 확인하세요. 이 상태가 다음 단계를 위한 기준점이 됩니다. 다양한 접근 방식을 비교하려면 다음을 참고하세요. Capacitor, Ionic, Cordova를 위한 CodePush 대안.

새 플랫폼 설치 및 설정

교체용 updater는 불투명한 라이브러리가 아니라 명시적인 프로토콜로서 프로젝트에 도입되어야 합니다. bundle의 구성 요소, 적용 가능한 native 버전, 채널과 타겟 유저의 매핑 방식, 그리고 저장된 base를 사용할 수 없을 때 클라이언트의 동작 방식을 정의하세요. 설치 전에 이러한 규칙이 명확하지 않으면, 마이그레이션이 테스트는 통과할지 몰라도 프로덕션 환경에서는 불안정한 상태로 남게 됩니다.

클라이언트를 의도적으로 추가하기

저장소의 package manager를 사용하여 새 updater 패키지를 설치한 다음, native 프로젝트를 동기화하세요:

npm install <new-updater-package>
npx cap sync

실제 프로젝트에서는 벤더가 문서화한 패키지 이름과 설정 키를 사용하세요. 의존성 변경 사항을 관련 없는 Capacitor 업그레이드와 분리하여, Native Build 실패 시 원인을 빠르게 파악할 수 있도록 합니다.

애플리케이션 시작 시 예측 가능한 시점에 updater를 초기화하세요. 현재 Native Build, 활성 채널, 로컬 web bundle 버전, 업데이트 확인을 위한 서버 정책을 알고 있어야 합니다. 여러 화면에 업데이트 호출을 분산시키지 마세요. 단일 서비스가 체크, 다운로드, 검증, 설치 결정 및 에러 보고를 모두 관리해야 합니다.

클라이언트는 JavaScript bundle 업데이트와 native application 업데이트를 반드시 구분해야 합니다. web bundle은 native plugin을 추가하거나 플랫폼 권한을 변경할 수 없으며, 새로운 native bridge가 존재한다고 가정해서도 안 됩니다. 릴리스 메타데이터에 호환성을 인코딩하고, updater가 지원 범위를 벗어난 패키지를 거부하도록 설정하세요.

배포 전 채널을 먼저 생성하세요

내부 테스트, staging, production을 위한 별도의 스트림을 구축하세요. 채널은 단순한 라벨 그 이상입니다. 어떤 사용자가 패키지를 받을지 결정하는 가드레일 역할을 하며, 앱 시작, 네비게이션, 인증, 오프라인 동작 및 native bridge 호출을 검증할 수 있는 통제된 환경을 제공합니다.

프로모션 경로를 명문화하세요:

  1. 개발 배포 없이 로컬에서 빌드합니다.
  2. Internal 엔지니어링 및 QA 기기를 위한 서명된 아티팩트를 수신합니다.
  3. Staging production과 유사한 서비스 및 릴리스 구성을 테스트합니다.
  4. 프로덕션 이전 체크를 통과한 아티팩트만 허용합니다.

채널 할당은 하드코딩된 테스트 값 대신 관리형 환경 설정을 사용하여 가능한 한 애플리케이션 번들 외부에서 유지하세요. 프로덕션 빌드가 실수로 베타 스트림을 가리키는 것은 사용자 실수가 아닌 릴리스 프로세스의 실패입니다.

번들 경계 보호하기

전송 최적화 단계에서 변경된 파일만 전송하더라도, 서비스는 완전하고 결정론적인 웹 번들을 게시해야 합니다. 깨끗한 워크스페이스에서 아티팩트를 생성하고, 의도적인 이유가 없다면 소스 맵이나 개발용 파일은 제외하며, 결과 패키지에 대한 콘텐츠 식별자를 기록하세요.

설치 전 무결성 검증을 필수화하세요. 클라이언트는 다운로드된 데이터를 검증하고, 예상 디렉토리를 재구성한 뒤, 원자적(atomically)으로 전환해야 합니다. 검증에 실패하면 현재 작동 중인 번들을 유지하고, 진단에 필요한 충분한 컨텍스트와 함께 실패를 보고하세요.

안전한 업데이트 타이밍과 네이티브 제한 사항을 포함한 광범위한 OTA 모델은 다음에서 확인할 수 있습니다. OTA 업데이트 수행 방법.

https://capgo.app에서 가져온 스크린샷

설정은 저장소가 아닌 플랫폼의 secret manager나 보호된 CI 변수에 저장하세요. 빌드 작업용 읽기 권한과 릴리스를 삭제하거나 프로덕션 타겟을 변경할 수 있는 관리자 권한을 분리해야 합니다. 또한 updater는 전체 패키지 폴백(fallback)을 제공해야 합니다. 기기에 필요한 베이스 버전이 없는 경우, 시스템은 기준점이 없는 패치를 적용하려고 시도해서는 안 됩니다.

Build 및 CI 파이프라인 조정

실험 단계에서는 수동 배포가 괜찮을 수 있지만, 정식 릴리스 시스템으로 사용하기에는 위험합니다. 신뢰할 수 있는 파이프라인은 소스 리비전을 식별 가능한 아티팩트로 변환하고, 해당 아티팩트를 검사하고, 서명한 뒤, 대상 채널에 게시하고, 그 결과를 기록합니다. 작업이 개발자 워크스테이션에서 실행되든 호스팅된 러너에서 실행되든, 동일한 입력에는 항상 동일한 릴리스 결정이 내려져야 합니다.

클린 환경에서 아티팩트 빌드

재현 가능한 시퀀스로 시작하세요. lock된 의존성을 설치하고, lint 및 테스트를 실행하며, 웹 레이어를 빌드한 뒤, 결과 파일에서 Capacitor 출력을 생성합니다. 개발자의 작업 디렉토리를 기준으로 diff를 계산하지 마세요. 대신 CI에서 생성된 버전이 지정된 불변 번들 두 개를 비교하세요.

실용적인 파이프라인은 다음과 같이 역할을 분리합니다:

  • 검증: 업로드 전 유닛 테스트, 타입 체크, lint 및 프로덕션 웹 빌드를 실행합니다.
  • 호환성: 번들에 필요한 네이티브 기능이 대상 앱 빌드와 일치하는지 확인합니다.
  • 패키징: deterministic 아카이브 또는 파일 매니페스트를 생성하고 임시 파일은 제외합니다.
  • 서명: CI 환경이 보유한 보호된 자격 증명으로 릴리스에 서명합니다.
  • 게시: 브랜치, 태그 또는 릴리스 승인에 따라 선택된 채널로 아티팩트를 전송합니다.
  • 검증: 배포 서비스를 조회하여 게시된 버전, 체크섬, 채널이 일치하는지 확인합니다.

소프트웨어 빌드 및 CI 파이프라인 최적화를 위한 지속적 개선 프로세스를 보여주는 6단계 인포그래픽입니다.

버전 식별을 명확하게 하세요

소스 리비전, 네이티브 호환성, 게시된 번들을 연결하는 릴리스 식별자를 사용하세요. 트러블슈팅 시 “latest”와 같이 변경 가능한 레이블에 의존하지 마세요. 업데이터는 설치된 버전과 후보 버전을 정확히 보고해야 하며, 릴리스 시스템은 의도적으로 롤백하거나 재게시할 수 있을 만큼 충분한 이력을 유지해야 합니다.

유용한 매니페스트는 다음을 기록합니다:

필드 왜 중요한가
Bundle 버전 설치된 자산과 대상 웹 자산 식별
Native 호환성 웹 번들이 사용 불가능한 브릿지 code를 참조하는 것을 방지
채널 대상 사용자 정의
Base 버전 Differential 재구성의 기준점
Integrity 값 데이터 손상 또는 변조 감지
Release 상태 배포 승인, 일시 중지 및 롤백 결정 지원

Base 버전은 Differential 전송에서 특히 중요합니다. RFC 3229의 일반 모델은 다음과 같습니다. 클라이언트가 저장된 리소스를 식별하고, 차이점 계산 알고리즘을 요청하면, 전체 리소스 대신 변경 사항만을 나타내는 응답을 받습니다. 애플리케이션 레벨에서도 기기에 저장된 Base가 유효할 때만 패치를 계산하고, 그렇지 않으면 전체 번들을 전송해야 합니다.

배포 경로 보안 강화

CI 러너를 권한이 부여된 릴리스 머신으로 취급하세요. 프로덕션 배포를 트리거할 수 있는 사용자를 제한하고, 서명 관련 자료가 로그에 남지 않도록 하며, 플랫폼의 secret 제어 기능을 통해 자격 증명을 주기적으로 교체해야 합니다. 또한 신뢰할 수 없는 pull request가 릴리스 secret에 접근하지 못하도록 차단하세요. DevArmor CI/CD 보안 관행은 권한, secret 및 파이프라인 경계를 강화하는 데 유용한 컨텍스트를 제공합니다.

프로덕션 배포 전에 dry-run 또는 staging 배포 단계를 추가하세요. 다운로드된 artifact를 깨끗한 시뮬레이터나 기기에서 테스트하고, 첫 실행, 재실행, 다운로드 중단, 서명 거부 및 롤백 동작을 확인해야 합니다. 가능한 경우 릴리스 작업을 멱등하게 유지하세요. 실패한 작업을 다시 실행했을 때 식별자가 중복된 모호한 패키지들이 생성되어서는 안 됩니다.

구체적인 구현 패턴은 다음을 참고하세요: Capacitor CI/CD 파이프라인 설정 가이드. 목표는 단순히 upload를 자동화하는 것이 아닙니다. 소스 커밋부터 기기 상태까지 모든 릴리스를 감사 가능하게 만드는 것입니다.

배포 및 성공 모니터링

Rollout은 업데이트 설계가 실제 부하 상황에서 테스트되는 단계입니다. 출시 첫날, 스테이징에서는 완벽해 보였던 패키지가 구형 베이스 기기, 다운로드 중단, 저장 공간 부족, 혹은 QA 매트릭스에서 누락된 앱 버전 등 예상치 못한 변수를 만날 수 있습니다. 퍼센트 기반 타겟팅은 조절 다이얼처럼 활용하되, 실제 안전장치는 배포를 즉시 중단하고 사용자를 검증된 번들로 되돌리는 능력이어야 합니다. 마치 새로운 차동 기어 세트를 설정하듯 Rollout을 진행하세요. 먼저 통제된 환경에서 피팅을 확인하고, 문제를 모니터링한 뒤, 맞물림이 안정적이라고 판단될 때 범위를 넓혀야 합니다.

전체 업데이트 경로 모니터링

단순 전송뿐만 아니라 전체 경로를 관찰하세요. 기기가 모든 바이트를 다운로드하더라도 서명 검증, 압축 해제, 활성화, 또는 교체 후 첫 실행 단계에서 실패할 수 있습니다. 각 단계마다 번들 버전, 네이티브 앱 버전, 채널, 플랫폼, 개인정보를 보호하는 기기 식별자, 그리고 팀이 즉시 조치할 수 있는 에러 카테고리를 로그로 남겨야 합니다.

최소한 다음과 같은 운영 질문에 답할 수 있는 시그널이 필요합니다:

  • 기기가 업데이트 후보를 발견했는가?
  • 전체 번들을 받았는가, 아니면 differential 패치를 받았는가?
  • 무결성 검증을 통과했는가?
  • 활성화가 완료되었는가?
  • 그 후 앱이 성공적으로 실행되었는가?
  • 다음 정상 세션에서도 클라이언트 상태가 양호했는가?

이러한 이벤트 트레일은 각 팀에 필요한 정보를 제공합니다. Support 팀은 특정 기기에서 발생한 문제를 설명하기 위해 기기별 타임라인이 필요하며, 릴리스 담당자는 전체 기기 대상의 채택률, 실패율, 롤백 트렌드를 파악해야 합니다. 이를 위한 실용적인 모델은 Capacitor 앱에서 OTA 업데이트 성공 여부 추적하기 해당 단계들과 깔끔하게 매핑되어 대시보드 설계가 훨씬 수월해집니다.

새 프로젝트를 배포하고 이후 성능 성공 여부를 모니터링하는 2단계 프로세스를 요약한 전문적인 인포그래픽입니다.

Rollback을 릴리스 기능으로 활용하세요

배포 전 rollback 조건을 설정하세요. 앱 실행 실패 증가, 반복적인 무결성 오류, 특정 후보 버전에 머물러 있는 기기 수 증가, 또는 특정 플랫폼에 국한된 크래시 패턴 등이 좋은 트리거가 됩니다. 기기에 마지막으로 확인된 정상 bundle을 유지하여, 앱 스토어 출시를 기다리지 않고도 즉시 복구가 가능하도록 만드세요.

Rollback 시에는 증거 보존도 필요합니다. 실패한 패키지의 메타데이터와 logs를 보관해야 팀에서 문제를 재현하고, 영향을 받은 호환성 범위를 파악하며, 이전 버전을 복구할지 수정된 artifact를 배포할지 결정할 수 있습니다. 사용자에게 증상만 없애고 흔적을 지워버리는 rollback은 결국 다음 릴리스에서 동일한 실패를 반복하게 만듭니다.

Differential delivery의 기반이 되는 전송 모델은 아주 오래전부터 존재해 왔습니다. HTTP delta encoding은 2002년 1월 IETF에서 발표한 RFC 3229를 통해 공식적인 웹 표준이 되었습니다., 그리고 RFC 3229 명세는 여전히 한 가지 실용적인 이유로 유용합니다. Delta는 클라이언트에 유효한 base 버전이 있고, 결과물을 재구성하는 비용이 전체 리소스를 전송하는 비용보다 적을 때만 도움이 되기 때문입니다.

이것이 바로 애플리케이션을 인지하는 업데이터가 일반적인 HTTP 델타 방식보다 더 잘 작동하는 이유입니다. 서비스는 베이스 번들을 알고, 클라이언트는 재구성된 결과물을 검증할 수 있으며, 업데이터는 데이터가 맞지 않을 경우 전체 패키지로 폴백할 수 있습니다. 핵심적인 엔지니어링 결정 사항은 전달 프로토콜이 호환성, 무결성, 롤백, 그리고 관측 가능성을 명시적으로 지원하느냐이며, 이 답변에 따라 차분(differential) 전달 방식의 실질적인 효용이 결정됩니다.

Capacitor 앱을 위한 Live Updates

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

블로그 최신 글

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