CodePush의 App Center로부터의 독립 선언문
MS의 Visual Studio App Center가 2025년 3월 31일 부로 지원이 종료된다.
그렇다 이 글은 CodePush의 App Center로부터의 (열강들의 도움을 받은,,) 독립 선언문 이다.
👋 잘가라 CodePush 아니,, App Center
CodePush는 MS에서 만들었고 Visual Studio App Center를 통해 운영되고 있으며 React Native(이하 RN)로 만든 앱을 다시 스토어에 배포하지 않고도 JS 번들과 리소스를 원격에서 업데이트 할 수 있는 OTA 솔루션이다.
(RN은 메타에서 만들었는데 CodePush는 왜 MS가 운영하고 관리하는지는(...) 궁금해졌는데 이따 알아보도록 하자 그건.)

OTA에 대한 자세한 설명은 생략한다.
처음엔 막연히 CodePush가 종료된다! 이렇게 알고 있었는데 정확하게는 App Center 자체가 서비스를 종료하기 때문에 CodePush를 위한 App Center의 인프라와 서비스도 자연스럽게 종료된다고 볼 수 있다.
그러므로 CodePush라는 이름의 서비스는 존재하지만 그를 지원해줬던 App Center를 대체할 무언가가 필요해졌다.

앱 센터 공식 홈페이지 중 일부
🤷♂️ 그럼 뭘 대체해야하는데?
App Center에서 기존에 하던 역할을 살펴보자, 크게 두 가지로 나눌 수 있다.
- CodePush 서버 호스팅 및 CLI를 통한 배포 및 관리
- 앱 및 사용자 분석, 로그 확인, 모니터링 대시보드 기능
CodePush 기능의 기초적이면서 가장 핵심이 되는 것은 자바스크립트 번들과 히스토리를 업로드할 수 있는 저장소와 그런 것들을 가능케 하는 인프라이다. 이는 위 역할에서 1번에 해당하며 2번은 상대적으로 1번에 비해 중요도가 낮다고 볼 수 있다.(정말 낮은거 맞아,,?)
이부자리에 누워 어떻게 해야할까 막연한 고민에 빠졌다. 핵심 기능 개발을 위해서는 서버가 필요한가? 앱 내에 설치한 패키지에서 명령어를 해석하여 S3로 업로드 하고 다운로드만 잘 해주면 될 것 같은데... 그래도 결국 안정적인 운영과 배포 모니터링을 위해서는 서버와 콘솔 구축이 필수적이겠지,,? 라는 생각을 하며 잠에 들었던 기억이 난다.
그렇게 본격적으로 CodePush와 App Center 대체재에 대한 리서치를 시작했다.
대체재를 찾기 위한 여정 🛩️
CodePush Standalone Server

MS에서 공개한 Standalone Server의 README 중 일부
우선 MS에서는 Standalone Server의 초석이 되는 코드를 오픈소스로 제공하고 있다. 일단 해당 저장소를 로컬로 가져와서 간략히 뜯어봤다.
코드는 node.js 기반이고 MS에서 공개한 것이다보니 Azure의 Blob Storage와 Table Storage를 이용하고 있다. Azure를 사용한다면 큰 수정 없이 수월하게 서버를 구축할 수 있을 것으로 보인다. 그러나 다른 클라우드를 이용한다면 수정이 불가피하고 우리 회사는 AWS를 이용하기 때문에 Blob Storage와 Table Storage에 대응 되는 S3와 DynamoDB 기반의 코드로 전환이 필요하다 생각했다.
문제는 제공된 인터페이스와 구현해야할 함수의 양이다.
아래의 이미지는 code-push-server의 storage interface인데 계정, 앱, 구성원, 배포 환경, 패키지, Blob, 엑세스 키를 관리하는 완전한 저장소 시스템의 구현을 위한 인터페이스라고 볼 수 있다.

이게 뭐꼬,,, OTL
막상 코드를 까보니 생각보다 해야할 게 많다고 느꼈다. 당시 기준으로 3월 말까지는 두 달 가까이 남았었지만 회사 내에서 내게 주어진 리소스는 여유롭지가 않아서 하나하나 저것들을 다 구현하고 테스트할 순 없었다. 무엇보다 모든 기능이 다 들어간 올인원, 토탈패키지라고 볼 수 있어서 CodePush의 핵심이 아닌 다른 기능도 많이 포함되어 있어서 많은 고민이 들었다.
일단 내게 주어진 임무는 코드 베이스를 바탕으로 한땀한땀 수정을 해서 완전한 시스템을 만든다가 아니라 제한된 리소스에서 최대한 빠르게 App Center의 핵심 기능을 대체할 무언가를 만들어낸다였다. 물론 완벽한 시스템을 만드는 것은 개발자로서는 좋은 경험이고 만족스럽겠지만 비즈니스적으로 큰 임팩트도 없고 도움이 되지는 않을 것이다. 그 점에 입각하면 저걸 다 뜯어고쳐가며 일일이 만드는 선택지는 날라갔다. 그래서 다른 이들이 먼저 개선하거나 만든 코드는 없을까 찾기 시작했다.

해당 저장소의 fork 수를 보면 적지 않은 사람들이 포크를 떴음을 알 수 있지 만 전세계에서 RN을 쓰고 있는 회사는 수 천개가 될 것을 감안하면 그렇게 많은 숫자라곤 볼 수 없다.
그래서 포크 뜬 저장소 중에서 star 수가 높은 것들을 몇 개 뒤졌고 그 외로는 분명 이게 돈이 될거란 생각이 들어서 누군가는 이미 밥상 다 차려놓고 App Center의 대체재를 찾는 (나같은ㅋㅋ) 사람을 기다리고 있지 않을까? 란 생각이 하며 디깅을 시작했다.
그렇게 검토를 거쳐 세 가지의 대체재가 남았다.
-
우선 제일 처음으로 발견한 것이다. 요금제가 여섯가지로 나뉘어져 있고 AWS를 이용하기 위해서는 Enterprise 모델을 선택해야 하는데 가격이 따로 책정되어 있지 않고 Contact us 라고 있는 게 걸려서 다른 대안으로 넘어가게 되었다.
-
토스 소속의 강선규님이 만드신 서비스이다. 버전 컨트롤과 콘솔을 제공하며 깔끔하게 문서화가 잘 되어있고 무엇보다 프로젝트의 오너가 한국 분이라서 좀 더 편하게 접근할 수 있다는 점이 매력적었으나, 2월 초 기준으로 Supabase와 Cloudflare 기반의 Provider만 제공하고 있어서 다른 대안을 찾기로 결정했었다. 이 글을 쓰는 지금은 AWS S3 + Lambda@Edge 기반의 Provider도 제공하고 있다.
-
그리고 MS에서 제공하는 저장소를 포크뜬 것 중에 가장 많은 star를 받은 저장소이다. 제공된 인터페이스를 AWS S3와 Redis 기반으로 구현한 코드이다. vendor lock을 풀어달라는 issue에 본인이 만든 걸 베이스로 유용할 것이다라고 직접 홍보를 하더라.
기존에 쓰고 있던 클라우드가 아닌 Azure나 Supabase 같이 다른 클라우드를 쓰는 것은 아무래도 러닝커브가 높으며 백엔드 리소스도 필요하기 때문에 AWS를 벗어난 선택지들은 모두 기각되었다.
선택이 3번안으로 좁혀지고 있던 그 때, 숨고의 테크 블로그에서 AppCenter 없이 React Native CodePush 배포하기란 글을 보게 되었다.
선택과 집중 🔥
위 블로그 글은 본문 도입부에 따르면 CodePush 라이브러리와 호환성을 유지하면서 별도의 API 서버 없이 App Center 종료에 대응할 수 있었던 방법을 소개하고 있다. 그 부분을 읽는데 CodePush 리서치를 시작하기 전 날 잠자리에서 했던 고민이 떠올랐다.
글을 찬찬히 뜯어본 결과 내가 처음에 생각했던대로 패키지 내에서 명령어를 받아 스토리지에 업로드 하는 방식이었다. 그리고 이 방식이 지금 내가 가진 리소스 내에서 가장 빠른 시간 내에 App Center 없이 CodePush의 핵심 기능을 유지할 수 있도록 개선할 수 있는 방법이라 판단되었 다. 그래서 숨고에서 공개하고 있는 저장소를 fork를 떠서 그것을 기반으로 작업을 시작했다.
와중에 블로그 글과 코드에 대해 궁금한 점이 생겼는데 글 내에는 댓글창도 없었고 글의 저자이신 Floyd Kim님에게 접근할 수 있는 수단이 없었다. 숨고에 재직하시는 지인이 있어서 그 분께 문의하여 이메일을 받아서 질의를 하고 답을 받았다.

친절한 그,,
마개조에 앞서...
기본적인 골자는 이렇다. 기존 CodePush는 react-native-code-push
라는 패키지를 통해서 제공되고 있다. 그리고 그 패키지는 내부적으로 code-push
라는 패키지에 또 의존하고 있다.
react-native-code-push
패키지는 CodePush 서버로부터 최신 JS 번들을 받아와서 업데이트를 하는 기능을 주로 담당하고 있고 code-push
패키지를 통해서 App Center와 통신을 하고 있다.
code-push
패키지는 CodePush SDK 패키지이며 MS에서 제공하고 있는 Standalone Server 패키지인 code-push-server
또한 이와 호환성을 갖고 있다. 즉, App Center와 관련된 CLI 및 서버 API 등을 제공하는 패키지라고 볼 수 있다.
숨고에서 제공하는 패키지는 react-native-code-push
를 포크 뜬 것인데 여기서 code-push
에 대한 의존성을 완전히 제거한 것 이다. 쉽게 말하자면 CLI 및 API를 제공하는 서버 레이어를 없애고 한 단계 앞으로 땡겨와서 해당 패키지 내에서 기능을 제공하고 있다고 볼 수 있다.
자세한 것은 해당 패키지나 블로그 글을 참고하는 것이 좀 더 도움이 될 것이다.
커스터마이징 👨🏻🔧
Floyd님과 이메일로 질의응답을 하며 확인한 결과, 포크 뜰 당시의 패키지는 미완성이었다. supabase 기반의 config 파일이 존재했고 거기엔 번들 파일과 히스토리 업로드/조회/수정 등의 기능이 포함되어 있었다.
이를 참고하여 우리 회사 환경에 맞게 일부를 변경하고 AWS S3 기반으로 config 파일을 새로 만들었다. 참고할 수 있는 코드가 있어서 그런지 숟가락만 더 얹으면 될 일(?)이었다.
어떻게 동작하냐면 명령어와 옵션을 통해 여러 정보들을 받고 앱 스토어에 심사를 올리는 바이너리 앱 버전으로 히스토리 json 파일을 생성한다. 그리고 그 버전에서 CodePush 배포를 진행한다면 CodePush 버전 기록을 json 파일에 계속해서 쓰는 방식이다.
// command: npx code-push create-history -b 2.8.0 ~
// 2.8.0.json 생성
{}
// command: npx code-push release -b 2.8.0 ~
// 2.8.0 버전 타겟으로 CodePush 버전 배포
{
"1.0.0": {
mandatory,
enabled,
developmentKey,
packageHash,
bundleFilePath,
},
"1.0.1": {
mandatory,
enabled,
developmentKey,
packageHash
bundleFilePath,
},
...
}
처음엔 명령어를 통해서만 기능 수행이 가능했는데 옵션이 많아서 휴먼 에러가 날 가능성이 높다고 생각했다. 보통 배포스크립트를 통해서 자동 배포를 하기 때문에 딱히 명령어 입력을 직접 수행할 일은 없지만 내가 테스트를 하면서 그것에 불편함을 느꼈고 수동 배포를 진행해야할 때가 있을지도 모르니 좀 더 편리했으면 좋겠다는 생각이 들었다.

위와 같이 텍스트 입력이 아닌 리스트를 제공하고 선택하는 대화형 방식을 추가했다. 번들 파일이나 config 파일의 위치 따위의 수정할 일이 거의 없는 옵션들을 제외하고 배포에 필요한 정보들 위주로 선택을 할 수 있게 만들었다.
이런 쪽을 써본 적만 있지 직접 다뤄본 적이 없어서 commander.js에서 이런 방법도 제공하는 줄 알았지만 그건 아니더라. inquirer
란 패키지를 알게 되었고 이를 통해서 편리하게 수동으로 관리를 할 수 있게 되었다. 물론 옵션을 명령어에 포함해서 작성한다면 대화형 방식은 생략된다.

업데이트 완료!
그리하여 히스토리와 번들 파일 업로드를 진행하고 테스트 앱으로 업데이트 체크를 했다. 업데이트할 번들 패키지의 메타데이터도 잘 가져오고 변경점도 잘 반영되었다.
(위 이미지 상단의 기록ㅋ와 효과ㅋ이 변경점이다ㅎㅎ;)
근데 문제가 하나 생겼다. 우리 회사는 PR을 올리게 되면 스크립트를 통해서 PR에 해당하는 변경점의 번들 파일을 업로드하고 해당 번들의 developmentKey를 포함한 딥링크를 생성하여 QR을 만든다. 그래서 QR을 찍으면 앱을 실행하고 해당 PR의 변경점을 다운로드한다. 이렇게 QA나 리뷰를 이어갈 수 있는데, 이번에 App Center를 대체하면서 이 부분이 말썽을 일으켰다. developmentKey를 포함한 딥링크로 앱을 실행해도 PR 버전을 다운 받을 수 없었다. 업데이트 체크 함수에서 developmentKey를 parameter로 받긴 하는데 여기서 해당 developmentKey를 가진 번들을 찾지 못해서 발생하는 문제였다.
이 부분은 아직 해결 중에 있으므로 해결 되면 글을 수정하도록 하겠다.