Skip to main content
네이티브
입문네이티브

OTA 업데이트: 게시된 코드가 설치 앱에서 실행되기까지

앱 전체를 다시 설치하지 않고 JavaScript와 자산을 전달하는 범위, 게시·다운로드·실행의 차이, 시작 정책과 신뢰 검증을 하나의 흐름으로 이해한다.

마지막 검증 재검증 정책: 제품 버전 의존: 새 주요 버전마다 재검증
목차
표준·구현·측정·해석 표시는 무엇인가요?
  • 표준웹 표준이나 언어 명세가 정한 동작
  • 구현특정 기술이나 브라우저가 실제로 구현한 동작
  • 측정명시한 환경에서 직접 실행해 관찰한 결과
  • 해석앞선 근거에서 도출한 설계 판단
  • 미확인아직 공식 근거나 재현 결과를 확인하지 못한 내용

30초 요약

OTA 업데이트(Over-the-Air Update)는 앱 전체를 스토어에서 다시 설치하지 않고, 기존 설치 앱과 호환되는 JavaScript·스타일·이미지 같은 업데이트 파일을 네트워크로 전달하는 방식이다. 이미 설치된 네이티브 코드와 권한을 새로 만드는 기능은 아니다.

반복해서 기억할 문장은 이것이다.

게시 ≠ 다운로드 ≠ 실행

게시 → 후보 확인 → 매니페스트(필요한 파일 목록) 검증 → 빠진 파일 다운로드
→ 로컬 저장 → 시작 정책이 선택 → 실행 → 채택(기기가 실제 실행하기 시작함) 관찰

서버에 새 업데이트가 있다는 사실은 전달 과정의 출발점일 뿐이다. 기기가 대상과 호환돼야 하고, 필요한 파일을 모두 받아 신뢰 여부를 확인한 뒤, 정해진 시점에 그 업데이트를 선택해야 실제 JavaScript가 바뀐다.

OTA가 바꿀 수 있는 범위는 설치 바이너리가 정한다

OTA로 전달 가능한 변경은 파일 확장자만 보고 정하지 않는다. 변경된 JavaScript가 기존 바이너리가 이미 제공하는 기능만 요구하는지를 본다.

변경기존 바이너리에 필요한 것일반적인 전달 경계
React 문구·상태 계산·스타일 수정이미 있는 React Native 실행환경호환된다면 OTA 후보
이미지·글꼴 교체파일 형식을 읽을 기존 코드호환된다면 OTA 후보
새 네이티브 카메라 함수 호출새 네이티브 기능 구현과 연결 코드새 바이너리 필요
새 권한·플랫폼 설정 추가Android·iOS 설정 변경새 바이너리 필요
JavaScript 엔진·React Native 네이티브 코드 변경새 네이티브 실행환경새 바이너리 필요

14편의 호환성 식을 다시 쓰면 다음과 같다.

업데이트가 요구하는 네이티브 기능 ⊆ 설치 바이너리가 제공하는 네이티브 기능

OTA는 이 조건을 만족하는 업데이트 계층의 전달 경로다. 조건 자체를 없애거나 설치된 네이티브 바이너리를 원격으로 다시 빌드하는 기능이 아니다.

게시, 다운로드, 실행은 서로 다른 상태다

한 업데이트가 게시된 뒤에도 기기마다 상태가 다를 수 있다.

기기 A: 이전 업데이트 실행 중 / 새 업데이트 미확인
기기 B: 이전 업데이트 실행 중 / 새 업데이트 다운로드 중
기기 C: 이전 업데이트 실행 중 / 새 업데이트 저장 완료
기기 D: 새 업데이트 실행 중

차이가 생기는 이유는 단순하다.

  • 앱을 아직 완전히 종료한 상태에서 다시 시작하지 않았다.
  • 오프라인이거나 연결이 느려 확인·다운로드가 끝나지 않았다.
  • 플랫폼·런타임 버전·배포 대상 조건이 맞지 않는다.
  • 다운로드는 끝났지만 다시 시작하거나 다시 불러오지 않았다.
  • 검증에 실패해 업데이트를 실행 후보로 저장하지 않았다.

따라서 “몇 시에 게시했는가”만으로 배포 완료를 판단하지 않는다. 실제 기기가 목표 업데이트를 실행하기 시작한 채택, 다운로드 실패와 실행 실패를 함께 본다.

매니페스트가 필요한 파일 묶음을 정의한다

매니페스트는 “이 업데이트를 실행하려면 무엇이 필요한가”를 적은 목록이다. 현재 프로토콜의 핵심 필드를 학습용으로 줄이면 다음과 같다.

{
  "id": "불변 업데이트 ID",
  "runtimeVersion": "호환 네이티브 계약",
  "launchAsset": { "url": "JavaScript 진입 파일", "hash": "..." },
  "assets": [
    { "url": "이미지 또는 글꼴", "hash": "..." }
  ]
}

흐름은 “최신 JavaScript URL을 바로 실행한다”보다 길다.

1. 플랫폼·런타임·대상 조건을 담아 업데이트를 확인한다.
2. 응답의 매니페스트에서 업데이트 ID와 필요한 파일을 읽는다.
3. 로컬에 없는 파일을 내려받는다.
4. 파일 해시와 신뢰 검증을 통과한 결과를 로컬에 저장한다.
5. 시작 정책이 저장된 업데이트 중 실행할 대상을 고른다.

이 글은 전달 단계를 구분하는 데 집중한다. 여러 파일이 일부만 적용되는 상태를 어떻게 막고 실패한 다운로드에서 어떻게 복구하는지는 16편의 원자적 업데이트에서 다룬다.

내장 업데이트는 네트워크 밖의 시작 기준점이다

내장 업데이트가 있으면 첫 설치 직후나 오프라인에서도 서버 응답 없이 시작할 수 있다. 이후 호환되는 업데이트를 내려받으면 로컬 저장소에 실행 후보가 더 생긴다.

설치 바이너리
  └─ 내장 업데이트 A
 
기기 로컬 저장소
  ├─ 다운로드 업데이트 B
  └─ 다운로드 업데이트 C

서버의 최신 이름을 매번 직접 실행하는 것이 아니다. 기기가 이미 완전하게 저장하고 검증한 후보 중 시작 규칙에 맞는 것을 고른다. 그래서 서버 장애나 오프라인이 곧바로 “앱에 실행할 코드가 없음”을 뜻하지는 않는다. 다만 내장 업데이트 자체가 현재 서버 자료나 서비스 계약과 계속 호환되는지는 별도의 운영 책임이다.

언제 확인하고 실행할지는 사용자 경험 정책이다

이 기본값은 시작 속도를 보호하지만 채택까지 시간이 걸린다. “가장 최신 코드”와 “지금 바로 열리는 앱”을 동시에 항상 보장할 수 없기 때문이다.

전략얻는 것감수할 것
시작 중 비동기 확인네트워크가 느려도 기존 코드로 빠르게 시작보통 다음 시작까지 채택 지연
앱 사용 중 확인·다운로드다음 시작 전에 파일을 준비할 기회데이터·배터리 사용과 상태 관리
다운로드 뒤 사용자에게 다시 불러오기 요청채택 시점을 사용자와 조율작업 중단과 안내 화면 필요
시작을 막고 최신본 대기특정 경우 최신본을 먼저 실행느린 연결·서버 장애 때 시작 지연

React 자체는 OTA 다운로드 시점을 정하지 않는다. React 컴포넌트에서 현재 업데이트 상태를 표시할 수는 있지만, 새 JavaScript를 실행하려면 실행환경을 다시 불러오거나 앱을 다시 시작해야 한다. 이때 메모리에만 있던 폼 입력, 화면 위치와 진행 중 작업은 사라질 수 있다. 적용 시점은 단순한 네트워크 옵션이 아니라 사용자 작업을 언제 끊어도 되는지에 대한 제품 정책이다.

현재 Expo는 필수 업데이트를 위한 일급 기능, 즉 설정 하나로 모든 사용자의 실행을 강제하는 기능을 제공하지 않는다. 필요하다면 확인·다운로드·안내·다시 불러오기와 실패 대체 경로를 애플리케이션이 설계해야 한다. “긴급”이라는 이름만으로 네트워크 대기를 무한정 강제하지 않는다.

내용 무결성과 게시자 진위는 다른 질문이다

파일을 안전하게 받았는지 확인할 때 두 질문을 나눈다.

  • 내용 무결성 — 받은 파일이 매니페스트가 가리킨 바이트와 같은가?
  • 게시자 진위 — 이 매니페스트를 앱이 신뢰하기로 한 주체가 승인했는가?

해시는 첫 질문에 답한다. 파일 내용에서 계산한 고정 길이 요약값이 매니페스트 값과 다르면 전송 중 손상되었거나 다른 파일이다. 그러나 공격자가 매니페스트와 해시를 함께 바꿀 수 있다면 해시만 맞는다고 승인된 게시자까지 증명되지는 않는다.

여기서 개인 키는 외부에 공개하지 않는 서명용 비밀값이고, 디지털 서명은 그 키로 만든 검증값이다. 신뢰 인증서는 클라이언트가 서명을 확인할 공개 키 정보를 담아 앱에 미리 넣은 자료다.

개인 키로 매니페스트 서명

서명된 매니페스트 ──해시로 참조──> JavaScript·이미지·글꼴

설치 앱에 넣은 신뢰 인증서로 검증

HTTPS는 전송 중 연결을 보호하지만 코드 서명과 같은 질문에 답하지 않는다. 코드 서명을 쓴다면 개인 키 접근을 배포 권한으로 취급하고, 유출·만료에 대비한 교체 절차를 연습해야 한다. 현재 EAS Update에서는 서명 인증서가 네이티브 실행환경 일부이므로 인증서 교체나 서명 제거에 새 런타임 버전과 새 바이너리가 필요하다.

서명이 유효하다는 사실도 코드가 버그 없이 동작한다는 증명은 아니다. “누가 승인했고 내용이 바뀌지 않았는가”와 “사용자에게 안전한 동작인가”는 별도의 검증이다.

기술적으로 가능해도 스토어 정책이 허용한다고 단정할 수 없다

이 정책 문구에서 “JavaScript면 언제나 허용된다”거나 “버그 수정이면 언제나 허용된다”는 결론을 자동으로 만들 수 없다. Google의 예외 문구가 모든 React Native OTA 변경을 포괄 승인한다고 일반화하지도 않는다. 앱의 기능 변화, 실행 방식, 배포 지역과 최신 정책 원문을 실제 제출 시점에 검토해야 한다.

기술 선택과 정책 판단을 분리하면 다음 질문이 남는다.

  1. 이 변경은 기존 심사를 받은 앱의 기능 범위 안에 있는가?
  2. 네이티브 실행 코드나 권한·플랫폼 기능을 새로 내려받는가?
  3. 사용 중인 스토어와 배포 지역의 최신 정책은 무엇을 제한하는가?
  4. 심사팀과 사용자에게 기능 변경을 투명하게 설명할 수 있는가?

정책은 바뀔 수 있고 개별 앱 판단도 다를 수 있다. 이 글은 법률 자문이나 심사 통과 보증이 아니라, OTA 도구의 기술적 가능성과 스토어 허용 범위를 같은 것으로 보지 않기 위한 기준을 제공한다.

Web, React, React Native에서 같은 원리가 보이는 위치

Web은 사용자가 페이지를 열 때 서버와 캐시에서 JavaScript·스타일 파일·이미지를 가져올 수 있다. 배포 결과가 있어도 브라우저 캐시나 열려 있는 탭 때문에 모든 사용자가 같은 순간 새 코드를 실행하지 않는다. “배포 ≠ 실행”이라는 구분은 같다.

React는 화면을 계산하는 라이브러리다. 어떤 서버에서 업데이트를 찾고 언제 파일을 저장할지는 React가 결정하지 않는다. 다만 새 번들을 다시 불러오면 React 트리와 메모리 상태가 다시 만들어질 수 있으므로, 사용자 경험 설계가 React 화면과 만난다.

React Native에서는 설치된 네이티브 바이너리가 JavaScript 실행환경과 네이티브 기능 경계를 제공한다. expo-updates 같은 네이티브 업데이트 클라이언트가 확인·검증·저장·시작 선택을 맡고, 그 위에서 호환되는 React JavaScript가 실행된다.

관측은 게시 수가 아니라 기기 상태를 따라간다

최소한 다음 질문에 답할 수 있게 기록한다.

단계답할 질문예시 신호
게시어떤 불변 결과물을 어느 대상에 열었는가?업데이트 ID, 콘텐츠 다이제스트, 플랫폼·런타임
확인대상 기기가 후보를 받았는가?확인 성공·없음·네트워크 오류
다운로드필요한 파일을 완전히 저장했는가?완료·실패, 크기, 소요 시간
실행실제 어느 업데이트가 시작됐는가?현재 업데이트 ID, 네이티브 빌드 ID
채택대상 집합 중 얼마나 실행했는가?시간에 따른 실행 기기 비율
품질새 조합이 정상 동작하는가?시작 실패, 충돌률, 핵심 흐름 오류

업데이트 ID만 기록하고 네이티브 빌드와 런타임 버전을 빼면 같은 JavaScript가 어느 실행환경에서 실패했는지 모른다. 14편의 실행 조합을 그대로 이어서 본다.

자주 실패하는 설계

게시 성공을 배포 완료라고 부른다

서버 업로드 성공은 기기 실행을 증명하지 않는다. 다운로드 완료와 실제 업데이트 ID별 채택을 분리해 관측한다.

새 네이티브 기능도 JavaScript와 함께 보낸다

기존 바이너리에는 구현이 없다. 네이티브 경계 변경을 먼저 분류하고 새 빌드와 호환성 계약을 만든다.

앱을 열 때마다 최신 다운로드가 끝날 때까지 막는다

느린 연결이나 서버 장애가 앱 시작 장애로 바뀐다. 정말 시작 차단이 필요한 조건과 최대 대기, 오프라인 대체 경로를 명시한다.

다운로드 직후 작업 중인 화면을 강제로 다시 불러온다

React 메모리 상태와 사용자 입력이 사라질 수 있다. 안전한 중단 지점, 상태 저장과 안내를 함께 설계한다.

HTTPS 하나로 업데이트 신뢰가 끝났다고 본다

전송 연결, 파일 무결성, 승인한 게시자, 코드 동작 안전성은 서로 다른 질문이다. 필요한 위협 모델에 따라 해시·코드 서명·배포 권한과 검증을 구성한다.

OTA 도구 지원을 스토어 정책 허용으로 해석한다

도구는 파일을 전달할 수 있을 뿐 개별 변경의 심사 준수 여부를 보증하지 않는다. 배포 전 최신 정책과 실제 변경 범위를 검토한다.

OTA 변경을 판단하는 순서

  1. 변경이 기존 설치 바이너리의 네이티브 기능만 요구하는지 확인한다.
  2. JavaScript·자산 결과물을 불변 ID와 매니페스트로 묶는다.
  3. 플랫폼·런타임·배포 대상을 정하고 스토어 정책상 허용 범위를 확인한다.
  4. 파일 무결성과 필요하면 게시자 진위를 검증할 신뢰 경계를 정한다.
  5. 확인·다운로드·다시 불러오기 시점을 사용자 작업과 네트워크 실패 기준으로 정한다.
  6. 호환 빌드에서 다운로드와 다음 시작을 실제로 검증한다.
  7. 게시 뒤 업데이트 ID별 다운로드·실행·채택·품질을 관찰한다.

“어떤 명령으로 게시할까?”보다 “무엇을 어느 실행환경에 어떤 시점으로 전달하고, 실제 실행을 무엇으로 증명할까?”를 먼저 묻는다.

한 문장으로 다시 설명하기

OTA 업데이트는 기존 설치 바이너리와 호환되는 JavaScript·자산을 스토어 재설치 없이 전달하는 경로이며, 게시는 실행이 아니다. 기기가 대상 업데이트를 확인하고 매니페스트가 가리키는 파일을 검증·저장한 뒤 시작 정책이 선택해야 새 코드가 실행된다. 네이티브 기능, 적용 시점, 사용자 상태, 게시 권한과 스토어 정책은 별도의 경계로 확인하고 실제 업데이트 ID별 채택과 품질을 관찰한다.

스스로 확인하기

  1. OTA로 바꿀 수 있는 범위를 파일 확장자가 아니라 어떤 조건으로 판단하는가?
  2. 게시·다운로드·실행을 같은 사건으로 볼 수 없는 이유는 무엇인가?
  3. 업데이트 매니페스트는 어떤 문제를 해결하며 어떤 파일을 가리키는가?
  4. 내장 업데이트는 오프라인과 서버 장애 때 어떤 기준점이 되는가?
  5. 기본 비동기 확인 전략이 시작 속도와 채택 속도 사이에서 만드는 교환은 무엇인가?
  6. 콘텐츠 해시와 코드 서명은 각각 어떤 질문에 답하는가?
  7. 기술적으로 가능한 OTA 변경도 최신 스토어 정책을 별도로 확인해야 하는 이유는 무엇인가?
  8. 게시 뒤 실제 채택을 판단하려면 어떤 기기 상태와 식별자를 관찰해야 하는가?

Active recall

기억에서 꺼내 보기

답을 쓰지 않아도 됩니다. 먼저 머릿속으로 답한 뒤 펼쳐서 정답·이유·흔한 오해를 비교하세요.

  1. 01OTA 서버에 새 JavaScript를 게시하면 모든 활성 사용자가 즉시 그 코드를 실행할까?

    정답

    아니다. 대상 후보가 된 뒤 기기가 확인하고, 필요한 파일을 내려받아 저장하고, 시작 또는 다시 불러오기 정책이 선택해야 실행된다.

    왜 그런가

    게시·다운로드·실행은 서로 다른 상태이며 기기별 네트워크와 앱 시작 시점도 다르다.

    관련 설명 다시 읽기
    지금 어느 정도 기억났나요?

    선택한 상태와 다음 복습일은 이 브라우저에만 저장됩니다.

  2. 02OTA 업데이트에 새 카메라 네이티브 기능을 호출하는 JavaScript를 넣으면 기존 설치 앱에도 그 구현이 생길까?

    정답

    생기지 않는다. 기존 바이너리가 제공하지 않는 네이티브 기능은 새 바이너리를 빌드하고 설치해야 한다.

    왜 그런가

    OTA는 호환되는 업데이트 계층을 교체할 뿐 이미 설치된 네이티브 계층을 바꾸지 않는다.

    관련 설명 다시 읽기
    지금 어느 정도 기억났나요?

    선택한 상태와 다음 복습일은 이 브라우저에만 저장됩니다.

  3. 03OTA 클라이언트는 JavaScript 파일 하나만 받으면 곧바로 실행 준비가 끝날까?

    정답

    아니다. 현재 Expo Updates 프로토콜에서는 먼저 매니페스트로 진입 파일과 필요한 자산 목록을 알고, 빠진 파일을 내려받아 무결성을 확인하고 저장한다.

    왜 그런가

    업데이트는 진입 파일 하나가 아니라 매니페스트와 그 매니페스트가 가리키는 자산의 묶음이다.

    관련 설명 다시 읽기
    지금 어느 정도 기억났나요?

    선택한 상태와 다음 복습일은 이 브라우저에만 저장됩니다.

  4. 04현재 expo-updates 기본 전략에서 기기가 새 업데이트를 내려받은 즉시 열려 있던 React 화면이 바뀔까?

    정답

    아니다. 릴리스 빌드는 앱을 완전히 종료한 상태에서 시작할 때 비동기로 확인·다운로드하고, 보통 다음 완전한 시작에서 내려받은 업데이트를 실행한다.

    왜 그런가

    시작을 네트워크 때문에 막지 않는 대신 게시부터 실제 실행까지 채택 지연이 생긴다.

    관련 설명 다시 읽기
    지금 어느 정도 기억났나요?

    선택한 상태와 다음 복습일은 이 브라우저에만 저장됩니다.

  5. 05내려받은 파일의 해시가 매니페스트 값과 같으면 누가 승인한 업데이트인지도 증명될까?

    정답

    해시는 받은 내용이 기대한 내용과 같은지 확인하지만 승인한 게시자를 단독으로 증명하지 않는다. 구성된 코드 서명은 앱이 신뢰하는 인증서에 대응하는 서명을 검증한다.

    왜 그런가

    내용 무결성과 게시 권한의 진위를 분리해야 전송 오류와 게시 경로 침해를 각각 다룰 수 있다.

    관련 설명 다시 읽기
    지금 어느 정도 기억났나요?

    선택한 상태와 다음 복습일은 이 브라우저에만 저장됩니다.

출처와 검증 범위

아래 날짜는 링크를 마지막으로 열어 본 날입니다. 문서 상단의 검증일은 글의 설명과 적용 범위를 다시 확인한 날입니다.

  1. Get started with EAS UpdateExpo · 공식 문서 · 확인 2026-08-04
  2. How EAS Update worksExpo · 공식 문서 · 확인 2026-08-04
  3. Downloading updatesExpo · 공식 문서 · 확인 2026-08-04
  4. Expo Updates v1Expo · 표준 · 확인 2026-08-04
  5. End-to-end code signing with EAS UpdateExpo · 공식 문서 · 확인 2026-08-04
  6. App Review GuidelinesApple Developer · 공식 문서 · 확인 2026-08-04
  7. Device and Network AbuseGoogle Play Console Help · 공식 문서 · 확인 2026-08-04
이 문서의 마지막까지 읽었습니다.