Frontend
EAS Update(OTA)의 환경 변수 함정
전성진··수정됨 2026.07.21

1. 문제 상황
앱 스토어에 배포된 앱에서 다음과 같은 이상 현상이 발생했다.
앱을 처음 실행하면 → 상용 API 정상 연결
앱을 백그라운드에 뒀다가 다시 열면 → 개발 API로 연결됨
처음에는 앱 재시작 로직, 세션 처리, 캐시 문제로 의심했다.
하지만 실제 원인은 전혀 다른 곳에 있었다.
2. 원인: eas build와 eas update는 환경 변수를 다르게 처리한다
Expo 프로젝트에서는 보통 eas.json에 환경 변수를 다음과 같이 설정한다.
{
"build": {
"production": {
"channel": "production",
"env": {
"EXPO_PUBLIC_API_BASE_URL": "https://api.example.com",
"EXPO_PUBLIC_IS_MOCK": "false"
}
}
}
}
eas build --profile production으로 빌드할 때는 위 env 블록이 네이티브 바이너리에 번들링된다.
즉, 앱 스토어에 올라가는 최초 빌드에는 정상적으로 상용 API 주소가 포함된다.
문제는 OTA 업데이트에서 발생한다.
npx eas update --branch production --message "수정 내용"
eas update는 eas.json의 env 블록을 읽지 않는다.
대신 명령을 실행하는 시점의 로컬 환경 변수와 .env 파일들을 읽어서 JS 번들을 빌드한다.
명령환경 변수 소스eas build --profile productioneas.json의 env 블록eas update --branch production로컬 환경 변수 및 .env 파일
결과적으로 eas build와 eas update는 같은 production 대상이라도 환경 변수 주입 방식이 다르다.
3. 두 번째 함정: .env.local의 우선순위
Expo / Node.js 기반 환경에서 .env 파일은 일반적으로 다음 우선순위로 로드된다.
.env.local.env.[NODE_ENV]예:
.env.production,.env.development
.env
즉, .env.local이 가장 높은 우선순위를 가진다.
로컬 개발 편의를 위해 .env.local에 다음과 같이 개발 API를 넣어두는 경우가 많다.
EXPO_PUBLIC_IS_MOCK=false
EXPO_PUBLIC_API_BASE_URL=https://dev-api.example.com
이 상태에서 eas update --branch production을 실행하면,.env.local이 가장 먼저 적용되기 때문에 production 브랜치에 배포되는 OTA JS 번들에 개발 API URL이 그대로 포함될 수 있다.
즉, 앱 스토어 빌드는 상용 API를 바라보고 있어도,
OTA 업데이트로 내려간 JS 번들은 개발 API를 바라보는 상황이 발생한다.
4. 세 번째 함정: .env.development는 eas update 시 자동 적용되지 않는다
다음과 같이 생각할 수 있다.
.env.development에 개발 API를 넣었으니,eas update --branch preview를 실행하면 자동으로 개발 API가 적용되지 않을까?
하지만 그렇지 않다.
eas update는 기본적으로 NODE_ENV=production 환경에서 실행된다.
따라서 .env.development는 NODE_ENV=development일 때만 로드된다.
실행 명령NODE_ENV로드되는 .env 파일npx expo startdevelopment.env.development, .env.localeas buildproduction.env.production, .env.localeas updateproduction.env.production, .env.local
결론적으로 eas update는 브랜치가 preview든 production이든 기본적으로 NODE_ENV=production으로 실행된다.
따라서 .env.development가 자동으로 적용될 것이라고 기대하면 안 된다.
5. 버그가 백그라운드 전환 시 나타난 이유
현상은 다음 순서로 발생했다.
앱 최초 설치
eas build로 생성된 네이티브 바이너리 실행eas.json의 production env 적용상용 API 정상 연결
앱 백그라운드 대기 중
Expo Updates가 새 OTA 번들을 자동 다운로드
앱 포어그라운드 복귀 또는 다음 실행
다운로드된 OTA 번들이 적용됨
이 OTA 번들에는
.env.local의 개발 API URL이 포함되어 있음결과적으로 개발 API로 연결됨
즉, 첫 실행은 정상인데 앱을 다시 열었을 때 문제가 발생한 이유는,
최초 실행 시점에는 네이티브 빌드에 포함된 상용 API가 사용되고,
이후 OTA 업데이트가 적용되면서 개발 API가 포함된 JS 번들로 교체되었기 때문이다.
6. 해결 방법
6-1. 즉각 해결: 올바른 환경 변수로 OTA 재배포
production OTA를 다시 배포할 때 환경 변수를 명시적으로 주입한다.
EXPO_PUBLIC_API_BASE_URL=https://api.example.com \
EXPO_PUBLIC_IS_MOCK=false \
npx eas update --branch production --message "fix: 환경 변수 수정"
이렇게 하면 로컬 .env.local에 잘못된 값이 있더라도,
명령 실행 시점에 명시한 환경 변수가 우선 적용된다.
6-2. 근본 해결: .env.local에서 API URL 제거
.env.local은 개인 로컬 설정용으로만 사용하는 것이 안전하다.
예를 들어 로컬 IP, 개인 테스트 플래그, 임시 옵션 정도만 넣고,
공용 API URL은 넣지 않는 편이 좋다.
# .env.local
EXPO_PUBLIC_IS_MOCK=false
# API URL은 .env.local에 넣지 않는다.
# EXPO_PUBLIC_API_BASE_URL=https://dev-api.example.com
개발 서버 실행 시에는 .env.development의 값이 자동으로 적용되므로,.env.local에 API URL을 중복 선언할 필요가 없다.
7. 권장 환경 파일 구성
.env.development
EXPO_PUBLIC_API_BASE_URL=https://dev-api.example.com
EXPO_PUBLIC_IS_MOCK=false
.env.production
EXPO_PUBLIC_API_BASE_URL=https://api.example.com
EXPO_PUBLIC_IS_MOCK=false
.env.local
# 개인 로컬 설정만 작성
# 공용 API URL 작성 금지
8. 정리
구분환경 변수 소스주의사항eas buildeas.json의 env 블록profile / channel 기반 분리 가능eas update로컬 환경 변수 및 .env 파일.env.local이 우선 적용될 수 있음eas update의 NODE_ENVproduction.env.development는 자동 적용되지 않음
9. OTA 업데이트 전 체크리스트
OTA 업데이트를 배포하기 전에는 반드시 아래 항목을 확인한다.
.env.local에 API URL이 하드코딩되어 있지 않은지 확인production OTA 배포 시 상용 API URL이 적용되는지 확인
preview OTA 배포 시 의도한 API URL이 적용되는지 확인
eas update실행 전에 현재 로컬 환경 변수를 확인필요하면 환경 변수를 명시적으로 주입하여 실행
배포 후 실제 앱에서 API Base URL 로그 또는 네트워크 요청 확인
10. 핵심 결론
eas build와 eas update는 같은 production 환경처럼 보여도 환경 변수 처리 방식이 다르다.
eas build → eas.json의 env 블록 사용
eas update → 로컬 환경 변수 및 .env 파일 사용
특히 .env.local은 우선순위가 높기 때문에,
로컬 개발용 API URL이 들어있으면 production OTA 번들을 오염시킬 수 있다.
따라서 OTA 업데이트를 사용하는 프로젝트에서는.env.local에 공용 API URL을 넣지 않고,
production 업데이트 시 환경 변수를 명시적으로 주입하는 방식이 가장 안전하다.
읽기 도구
약 8분 읽기
이 글이 도움이 되었나요?