Expo 앱 API 키 보안: 앱에 넣으면 안 되는 비밀 키
글: Mehdi Azizi
업데이트, 7분
Supabase 공개 키, 서버 비밀 키, Expo 환경 변수를 구분해 iPhone 앱에 넣어도 되는 값을 정하고, 테스트 계정 두 개로 접근 권한을 확인하세요.
환경 변수라고 해서 모두 비밀은 아니에요
Expo 앱에서 EXPO_PUBLIC_ 접두사가 붙은 변수는 앱 코드에서 쓰라고 만든 거예요. 여기에 서버용 비밀 값을 넣으면 안 돼요. Expo 문서에 따르면 이 값들은 컴파일된 JavaScript에 포함되고, 앱 사용자가 읽을 수 있어요. 그러니까 키를 .env 파일로 옮긴다고 보호되는 게 아니에요. 출처: Expo 환경 변수.
AI에게 "API 연결해 줘"라고 하기 전에, 넘겨주는 값부터 분류하세요. 서비스 주소인지, 공개를 전제로 만든 키인지, 아니면 권한이 필요한 작업을 허용하는 키인지요. 이 분류에 따라 코드를 어디서 실행해야 할지가 정해져요.
아래 표는 설계용 기준표예요. 여러분 앱을 감사한 결과가 아니에요.
| 항목 | 두는 위치 | 확인할 점 |
|---|---|---|
| 백엔드 공개 주소 | 앱 설정 | 올바른 환경을 가리키는지 확인해요. |
| Supabase publishable 키 또는 예전 anon 키 | 권한이 제대로 설정됐다면 앱에 둬도 돼요 | 데이터 규칙이 다른 사용자 계정 접근을 실제로 막는지 확인해요. |
| Supabase secret 키 또는 service_role 키 | 서버에만 | 모바일 번들에도, 클라이언트가 볼 수 있는 로그에도 나오지 않아야 해요. |
| AI 제공업체의 비밀 키 | 서버에만 | 앱은 여러분 서버를 호출하고, 서버가 사용자와 한도를 확인한 뒤 제공업체를 호출해요. |
Supabase는 클라이언트용 키와 신뢰할 수 있는 환경에서 쓰는 비밀 키를 구분해요. 공개 키는 프로젝트를 식별할 뿐이고, 데이터 권한을 대신하지 않아요. 출처: Supabase API 키.
예시: 가상의 독서 기록 앱
레아와 사미가 자기 책과 비공개 메모를 저장하는 앱이 있다고 해볼게요. 이 예시는 테스트를 준비하려고 만든 거고, Devoria 앱에서 실제로 얻은 결과가 아니에요.
평소 흐름은 문제없어 보여요. 레아가 로그인하면 자기 메모가 보여요. 그런데 이것만으로는 중요한 질문에 답이 안 돼요. 레아가 사미의 메모를 요청하면 서버가 거부할까요?
"로그인한 사용자의 메모만 표시" 같은 UI 필터는 화면 표시를 개선할 뿐이에요. 권한 검사는 데이터 쪽에서도 적용돼야 해요. Supabase에서는 Row Level Security 규칙으로 세션에 따라 접근 가능한 행을 제한할 수 있어요. Supabase는 API로 노출되는 테이블에 이 기능을 켜라고 권장하고, 읽기와 쓰기 정책도 설명해요. 출처: Row Level Security.
여러분 테스트 프로젝트에서는 각 메모를 소유자와 연결하고, 누가 읽고, 만들고, 수정하고, 삭제할 수 있는지 따로 정하세요. 파일도 확인하세요. 데이터베이스 행을 보호했다고 이미지가 비공개라는 증거는 아니에요.
테스트 계정 두 개로 하는 검수 절차
직접 관리하는 계정 두 개를 준비하고, 각각 다른 책 한 권과 알아보기 쉬운 짧은 메모를 넣으세요. 허용된 테스트 환경에서 가상 데이터를 쓰세요.
- 내 메모 읽기. A 계정에서 A 메모가 보여요.
- A에서 B의 메모 읽기. 요청해도 비공개 내용이 드러나지 않아요.
- A에서 B의 메모 수정하기. 서버가 거부하고, B 메모는 그대로예요.
- A에서 B 이름으로 생성하기. 서버가 이 소유자 지정을 거부해요.
- 세션 없이 읽기. 비공개 내용이 하나도 반환되지 않아요.
- A에서 B의 비공개 파일 열기. 파일에 접근할 수 없어요.
실제 결과, 테스트한 버전, 오류가 있었다면 그 내용까지 기록하세요. "규칙을 켜 놨어요"는 테스트 결과가 아니에요. 리소스에 있는 검수표를 이런 증거를 남기는 기본 틀로 써도 돼요.
유료 호출은 서버를 거치게 하세요
AI API로 책을 요약하는 기능이라면 흐름은 이렇게 될 수 있어요. 앱 → 여러분 서버 → 제공업체 → 여러분 서버 → 앱. 비밀 키는 서버에 남아요. 다만 이 구조만으로는 부족해요. 서버 접근도 통제해야 해요.
가상 예시에서 서버는 특히 세션을 확인하고, 책이 그 사람 것인지 확인하고, 반복 요청을 제한하고, 제공업체가 응답하지 않으면 이해할 수 있는 오류를 돌려줘야 해요. 사용자가 유료 접근 권한이 있는지는 클라이언트가 스스로 정하면 안 돼요.
테스트 네 가지를 준비하세요. 로그인하지 않은 사용자, 다른 계정의 책, 한도 도달, 제공업체 장애예요. 마지막 테스트로는 앱이 입력한 내용을 잃지 않고 로딩 상태에서 빠져나오는지 확인할 수 있어요.
코딩 어시스턴트에게 요청할 수 있는 것
여러분 저장소에 맞게 고쳐 쓸 수 있는 리뷰 지시문이에요. 비밀 값은 붙여 넣지 마세요.
모바일 코드와 서버에서 쓰는 변수를 이름만 표시해서 정리해 줘. 클라이언트 쪽에 있는 권한이 필요한 호출을 찾아 줘. 비공개 데이터에 대해 읽기와 쓰기 통제를 설명하고, 테스트 계정 두 개로 하는 검수 절차를 제안해 줘. 승인 없이 키 교체나 프로덕션 변경은 하지 마.
답변은 해당 코드와 함께 읽으세요. 어시스턴트가 불일치를 찾아낼 수는 있지만, 그 답변이 테스트 실행을 대신하지는 않아요. Claude Code로 앱 만들기 가이드에서 이 작업을 나누는 방법을 설명해요.
앱 개발을 계속하기 전에
세 가지를 쉽게 설명할 수 있어야 해요. 어떤 값이 공개인지, 어떤 작업이 서버를 거치는지, 한 계정이 다른 계정의 데이터를 못 본다는 걸 어떤 테스트가 보여 주는지요. 이 중 하나라도 애매하면 기능을 추가하기 전에 정리하세요.
iPhone 앱 만들기 페이지에서 이 단계가 전체 프로젝트에서 어디쯤인지 볼 수 있어요. 순서대로 진행하고 싶다면 iOS Kit가 4단계 과정과 계정, AI 기능이 이미 들어간 시작 코드를 제공해요. 어느 단계부터 다시 해야 할지 모르겠다면 무료 진단으로 프로젝트 순서를 정리하고, 제 Apple 리젝 방지 체크리스트도 받아 보세요.
자주 묻는 질문
Expo 앱에서 비밀 API 키는 어디에 둬야 하나요?
앱이 아니라 서버에 두세요. Supabase secret 키나 service_role 키, AI 제공업체의 비밀 키는 서버에만 있어야 해요. 앱은 여러분 서버를 호출하고, 서버가 사용자와 한도를 확인한 뒤 제공업체를 호출해요. 이 키들이 모바일 번들이나 클라이언트가 볼 수 있는 로그에 나오지 않는지 확인하세요.
.env 파일이 Expo에서 API 키를 보호해 주나요?
그것만으로는 안 돼요. EXPO_PUBLIC_ 접두사가 붙은 변수는 앱 코드용이라 서버 비밀 값을 넣으면 안 돼요. Expo 문서에 따르면 이 값들은 컴파일된 JavaScript에 포함되고 앱 사용자가 읽을 수 있어요. 그래서 키를 .env 파일로 옮기는 것만으로는 보호되지 않아요.
공개 API 키와 비밀 키는 뭐가 다른가요?
공개 키는 앱에 넣어 배포하도록 만든 키예요. 프로젝트를 식별하지만 데이터 권한을 대신하지는 않아요. 비밀 키는 권한이 필요한 작업을 허용하고, 신뢰할 수 있는 환경에서만 써요. Supabase의 publishable 키나 예전 anon 키는 권한이 제대로 설정됐다면 앱에 둬도 돼요. secret 키와 service_role 키는 서버에 둬요.
키를 노출하지 않고 Expo 앱에서 AI API를 호출하려면 어떻게 하나요?
호출을 서버로 거치게 하세요. 앱이 서버를 호출하고, 서버가 제공업체를 호출한 뒤 응답을 앱에 돌려줘요. 비밀 키는 서버에 남아요. 서버는 세션도 확인하고, 반복 요청을 제한하고, 제공업체가 응답하지 않으면 이해할 수 있는 오류를 돌려줘야 해요. 유료 접근 권한 여부는 클라이언트가 스스로 정하면 안 돼요.
각 사용자가 자기 데이터만 보는지 어떻게 확인하나요?
직접 관리하는 테스트 계정 두 개를 준비하고, 각각 알아보기 쉬운 메모를 넣으세요. A 계정에서 B 계정의 메모를 읽고, 수정하고, B 이름으로 생성해 보고, B의 비공개 파일도 열어 보세요. 세션 없이도 시도해 보세요. 모든 요청이 거부되거나 아무것도 드러나지 않아야 해요. 실제 결과를 기록하세요. "규칙을 켜 놨어요"는 테스트 결과가 아니에요.
AI에게 앱의 키 점검을 어떻게 요청하나요?
모바일 코드와 서버의 변수를 이름만 표시해서 정리해 달라고 하세요. 비밀 값은 붙여 넣지 마세요. 클라이언트 쪽에 있는 권한이 필요한 호출을 찾고, 테스트 계정 두 개로 하는 검수 절차를 제안해 달라고도 하세요. 승인 없는 키 교체나 프로덕션 변경은 금지하세요. 답변이 테스트 실행을 대신하지는 않아요.