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를 가진 번들을 찾지 못해서 발생하는 문제였다.
이 부분은 아직 해결 중에 있으므로 해결 되면 글을 수정하도록 하겠다.
후기
나의 모자람을 많이 알게된 계기가 되었다. 아주 오랜만에 node.js와 파일 시스템 관련된 코드들을 만지게 되었고 CLI와 관련된 패키지들은 처음 겪는 경험을 했다. 늘 하던 일이 아니라 익숙치는 않았지만 재미 있었다.
그리고 ESM과 CJS에 대해서 이해도가 떨어짐을 느꼈다. CodePush와 관련된 config.ts 파일을 import했는데 해석을 잘 못해서 애를 먹었다. Babel 설정도 이래저래 건드려보았지만 원하는대로 쉽게 동작하진 않았다.
ESM과 CJS 그리고 transpiler에 대한 공부가 필요해보이고 App Center를 리타이어하고 CodePush 핵심 기능을 유지하기만 했지 아직 남겨진 숙제들이 많다.
PR Preview 설정, 배포 최적화, 모니터링 환경 구축 등,, 아마 그렇게 하려면 서버가 필요할 것 같긴한데 그렇게 어려워 보이지는 않아서 언젠가는 스스로 서버 구축까지 완료하고 싶다. 한꺼번에 다 할 순 없지만 천천히 짬을 내서 이어나가야겠다.
잘 모르는 것들을 만지게 되어서 처음에는 막막 했는데 하니까 되더라,, 하면 된다. 개발할 때 잘 모르겠다 싶으면 일단 시작하고 부딪혀보고 깨져봐야 되는 걸 다시 한 번 깨닫는다. 요즘엔 AI도 잘 되어있기 때문에 그 허들도 많이 낮아졌다. 도전하고 시도하는 것을 두려워 하지 말라.
RN은 메타에서 만들었는데 CodePush는 왜 MS에서,,
글 맨 위 적은 나의 궁금증을 해소하기 위한 추가적인 내용이다.
CodePush의 탄생
CodePush는 2015년에 탄생했고 원래는 오픈소스 프로젝트로 시작되었다. 초기엔 React Native 뿐 아니라 Cordova도 지원했다고 한다.
MS의 CodePush 인수
그러던 중 2016년 MS에서 CodePush의 가능성을 보고 프로젝트를 인수했다. 이 시기에 App Center의 전신인 MS HockeyApp과 연계되어 지원이 강화되었고 CLI를 제공하여 손쉽게 업데이트 배포가 가능해졌다.
App Center와의 통합
MS에서 Visual Studio App Center를 출시하면서 CodePush를 App Center의 핵심 기능으로 포함했다. 그러면서 CodePush의 기능들이 하나둘 개선되었다.
위 내용대로 CodePush는 오픈소스에서 시작했다. 메타는 RN 오픈소스 생태계를 활성화 하는데에 좀 더 초점을 맞췄고 MS는 Azure라는 자사 클라우드 서비스를 강화하고 있었고 그 일환으로 모바일 앱 서포트를 위한 App Center 서비스를 적극적으로 지원했다고 보인다.
그냥 흔한 오픈소스 생태계의 생리인 거 같다. React도 그렇고 유독 메타가 그런 것 같다고 생각이 들긴 하는데 최소화된 무언가를 내놓고 유저들이 만드는 생태계를 적극 지원하는 형태.
TS/JS를 다루고 웹 생태계에 속한 개발자로서 라이브러리들을 쓰기만 해왔지 개발하거나 기여해본 적 은 거의 없는데 이번 경험으로 흥미도 생겼고 좀 더 알아보고 기여할 수 있는 것들이 있다면 기여해보고 싶단 욕심이 생겼다.