RSS듀오랩스
모바일 앱

EAS 없이 Expo 로컬 빌드: 서명 자료를 모으고도 남는 다섯 가지

작성자
듀오랩스 대표·7분 읽기

클라우드 빌드를 떠나기로 하고(왜 옮기는지는 따로 적었습니다) 서명 자료부터 모았습니다. iOS 배포 인증서와 프로비저닝 프로파일, 안드로이드 업로드 키스토어와 비밀번호 두 개, 스토어 API 키까지 한자리에 놓고 확인했습니다. 코드 서명 신원도 유효하게 잡혔습니다.

그래서 이제 빌드하면 되는 줄 알았습니다. 빌드 전에 앱 하나를 재봤더니 아니었습니다.

아예 없는 네이티브 프로젝트

리포에 android/ios/ 도 없습니다. Expo 의 CNG(Continuous Native Generation)를 쓰면 네이티브 프로젝트는 소스가 아니라 산출물입니다. expo prebuild 가 설정을 읽어 그때그때 만듭니다.

.gitignore 에도 그렇게 적혀 있습니다.

/ios/
/android/

여기에 함정이 하나 있습니다. 서명 설정은 android/app/build.gradle 과 Xcode 프로젝트에 들어갑니다. 둘 다 생성되는 파일입니다. 손으로 고쳐 놓고 다음에 prebuild 를 한 번 돌리면 그대로 사라집니다.

그래서 서명 설정은 파일이 아니라 config plugin 으로 넣어야 합니다. 플러그인은 생성 과정에 끼어들어 매번 같은 수정을 다시 적용합니다. 이 앱에는 아직 플러그인이 없습니다. 그것부터 만들어야 합니다.

이 컴퓨터에 없는 버전 번호

eas.json 에 이렇게 적혀 있습니다.

"cli": { "appVersionSource": "remote" }

versionCodebuildNumber 를 리포가 아니라 서버가 들고 있다는 뜻입니다. 실제로 app.json 에는 두 값이 아예 없습니다. 지금까지는 클라우드 빌드가 올릴 때마다 하나씩 올려 줬습니다.

물어보면 알려줍니다.

$ npx eas-cli build:version:get -p android
Android versionCode - 2
$ npx eas-cli build:version:get -p ios
iOS buildNumber - 3

로컬에서 빌드한다면 다음 번호는 3과 4입니다. 모르고 2로 올리면 스토어가 이미 쓴 번호라며 거부합니다. 빌드가 20분 걸린 다음에 알게 되는 종류의 거부입니다.

빌드 서비스가 심어 주던 OTA 채널

이 앱은 expo-updates 를 씁니다. 업데이트를 어느 채널에서 받을지는 eas.json 에 있습니다.

"production": { "channel": "production" }

이 값은 네이티브 프로젝트 안에 심겨야 앱이 읽습니다. 심는 일을 지금까지 클라우드 빌드가 해 왔습니다. 로컬 빌드는 그 단계를 거치지 않으므로, 아무것도 하지 않으면 채널이 비어 있는 앱이 나갑니다. 설치는 되고 실행도 되는데 업데이트만 영영 못 받습니다.

runtimeVersionfingerprint 정책인 것도 같이 봐야 합니다. 네이티브 구성이 달라지면 지문이 달라지고, 지문이 다르면 기존에 올려둔 업데이트가 이 빌드에 적용되지 않습니다.

안드로이드에만 먼저 필요한 도구

이 컴퓨터
Xcode 26.6
JDK 없음
Android SDK 없음

iOS 는 도구가 이미 있어서 서명 자료만으로 다음 단계로 갈 수 있습니다. 안드로이드는 JDK 17 과 Android SDK 를 먼저 깔아야 시작합니다.

이게 발목을 잡은 자리가 하나 더 있습니다. 키스토어의 비밀번호 두 개가 제 역할인지 확인하려면 keytool 이 필요한데, 그게 JDK 와 함께 옵니다. 저장소 비밀번호와 키 비밀번호는 다른 값이고 바꿔 넣으면 서명이 깨집니다. 그러니 확인해야 하는데, 확인 도구가 아직 없습니다.

디버그 키로 서명되는 릴리스

처음 이 글을 쓸 때는 이 항목만 확인하지 못한 채로 두었습니다. 확인하려면 prebuild 를 돌려 파일을 만들어야 했기 때문입니다. 돌려 보고 다시 적습니다.

생성된 android/app/build.gradle 은 이렇습니다.

signingConfigs {
    debug {
        storeFile file('debug.keystore')
        storePassword 'android'
        keyAlias 'androiddebugkey'
        keyPassword 'android'
    }
}
buildTypes {
    release {
        // Caution! In production, you need to generate your own keystore file.
        signingConfig signingConfigs.debug
    }
}

릴리스가 디버그 서명 설정을 그대로 가리킵니다. 그 설정이 쓰는 키스토어의 비밀번호는 android 이고 별칭은 androiddebugkey 입니다. 리액트 네이티브를 써 본 사람이면 다 아는 값이고, 알라고 만든 값입니다. 개발 중에 기기에 설치하는 데만 쓰라는 뜻입니다.

주석까지 친절하게 달려 있습니다. 그런데 주석은 빌드를 막지 않습니다. 그대로 릴리스를 빌드하면 서명은 되고 AAB 도 나옵니다. 거부는 그 파일을 Play 에 올릴 때 옵니다.

iOS 쪽도 같이 봤습니다. 생성된 Xcode 프로젝트에는 DEVELOPMENT_TEAM 만 들어 있고 CODE_SIGN_STYLE 은 아예 없었습니다. 없으면 자동 서명입니다. 손에 든 프로파일로 서명하려면 Manual 로 바꾸고 프로파일 이름과 서명 신원을 함께 지정해야 합니다.

시작에 불과했던 서명

정리하면 이렇습니다. 서명 자료를 모으는 일은 클라우드 빌드를 떠나는 데 필요하지만 충분하지 않습니다. 빌드 서비스는 서명만 해 주던 게 아니라 번호를 매기고, 채널을 심고, 네이티브 프로젝트를 만들고, 도구를 준비하는 일을 함께 하고 있었습니다.

떠날 때 눈에 보이는 것은 서명 자료뿐입니다. 나머지는 없어진 뒤에야 무엇이 있었는지 알게 됩니다. 그래서 빌드를 시도하기 전에 한 번 재보는 편이 낫습니다. 다섯 가지 중 네 가지는 빌드를 돌리기 전에 명령 한 줄로 확인할 수 있었습니다.

다음은 prebuild 를 돌려 네이티브 프로젝트를 만들고, 거기서 서명 설정이 실제로 어떻게 되어 있는지 보는 일입니다.

마지막 수정:

공유하실 때는 출처(Duolabs)와 원문 주소를 표시해 주세요.