목차
지난 편에서는 MyLog의 코드를 한번 정리했습니다.
화면은 화면 역할에 집중하고,
Supabase를 호출하는 코드는 Service 쪽으로 옮겼습니다.
조금씩 프로젝트다운 모습이 나오고 있습니다.
그런데 프로젝트를 살펴보다 보니 main.dart 근처에 이런 코드가 보입니다.
await Supabase.initialize(
url: 'https://xxxxxxxx.supabase.co',
publishableKey: 'sb_publishable_xxxxxxxxx',
);처음에는 별생각이 없습니다.
잘 실행됩니다.
그런데 갑자기 한 가지가 신경 쓰입니다.
이걸 GitHub에 올려도 되는 걸까?
조금 검색하다 보면 더 무서운 이야기도 나옵니다.
API Key를 코드에 절대 넣지 마세요.
그래서 부랴부랴 .env 파일을 만듭니다.
이제 안전한 걸까요?
오늘은 이 부분을 제대로 정리해보겠습니다.
결론부터 말하면 아주 중요한 차이가 있습니다.
코드에서 값을 분리하는 것과 비밀을 숨기는 것은 같은 일이 아닙니다.
1. API Key라고 전부 비밀번호는 아니다
처음에는 이름 때문에 가장 많이 헷갈립니다.
API Key
라고 하면 전부 숨겨야 하는 비밀번호처럼 느껴집니다.
하지만 실제로는 종류가 다릅니다.
예를 들어 현재 Supabase에는 크게 두 종류의 키가 있습니다.
Publishable Key
Secret KeySupabase 공식 문서에서는 Publishable Key를 웹, 모바일, 데스크톱 앱처럼 값을 완전히 숨길 수 없는 공개 클라이언트 환경에서 사용하는 낮은 권한의 키로 설명합니다.
반면 Secret Key는 높은 권한을 가지며 RLS를 우회할 수 있기 때문에 서버나 Edge Function 같은 보호된 백엔드 환경에서만 사용해야 합니다.
즉 둘 다 이름 끝에는 Key가 붙어 있지만 성격은 완전히 다릅니다.
2. MyLog에서 사용하는 Supabase Key는?
현재 Flutter 앱에서는
Supabase URL
Supabase Publishable Key를 사용하고 있습니다.
이 Publishable Key는 모바일 앱에서 사용하는 것이 허용된 값입니다.
Supabase도 Flutter 초기화 예제에서 url과 publishableKey를 클라이언트 앱에 전달하는 형태를 공식적으로 안내합니다.
그렇다면 이런 생각이 듭니다.
공개 가능한 값이면 그냥 코드에 적어도 되는 것 아닌가?
보안 관점에서는 Publishable Key 자체를 진짜 Secret처럼 숨길 수 없습니다.
하지만 소스 코드와 환경 설정을 분리하는 것에는 여전히 큰 의미가 있습니다.
왜냐하면 개발용과 운영용 값을 바꾸기 쉬워지고,
저장소에 불필요한 설정값을 남기지 않을 수 있고,
실수로 진짜 Secret을 코드에 넣는 습관도 예방할 수 있기 때문입니다.
3. 가장 중요한 사실, 모바일 앱 안의 값은 결국 볼 수 있다
여기서 오늘 가장 중요한 내용입니다.
Flutter 코드에서 문자열을 직접 적지 않고
.env
나
--dart-define
으로 넘겼다고 해보겠습니다.
그러면 Git에는 값이 안 들어갈 수 있습니다.
좋습니다.
하지만 최종적으로 앱이 그 값을 사용하려면 빌드된 앱 안에는 어떤 형태로든 그 값이 포함되어야 합니다.
Supabase 역시 모바일이나 데스크톱 앱은 빌드 결과물에서 키를 추출할 수 있기 때문에 공개 환경으로 취급한다고 설명합니다.
따라서
.env에 넣었다
=
사용자가 절대 볼 수 없다가 아닙니다.
정확히는
.env에 넣었다
=
소스 코드와 설정값을 분리했다에 가깝습니다.
이 차이를 반드시 기억해야 합니다.
4. .env는 투명망토가 아니다
바이브 코딩을 하다 보면 이런 말을 정말 자주 봅니다.
API Key는 .env에 넣으면 안전합니다.반은 맞고 반은 틀립니다.
서버에서는 .env에 저장된 Secret을 서버 밖으로 보내지 않는다면 좋은 관리 방법이 될 수 있습니다.
하지만 모바일 앱에서는 앱이 해당 값을 실제로 사용해야 한다면 결국 빌드 결과에 포함될 수 있습니다.
그래서 .env를
비밀을 마법처럼 사라지게 하는 금고
라고 생각하면 안 됩니다.
.env는 오히려
환경별 설정을 소스 코드와 분리하는 서랍
에 가깝습니다. 🗄️
5. 그렇다면 진짜 Secret은 어떻게 해야 할까?
답은 의외로 단순합니다.
앱에 넣지 않습니다.
예를 들어 이런 값이 있다고 해보겠습니다.
Supabase Secret Key
service_role Key
결제 서비스 Secret Key
외부 API의 관리자 Key이런 값이 필요한 작업이라면 구조를 바꿔야 합니다.
잘못된 구조는 이렇습니다.
Flutter App
↓
Secret Key
↓
외부 서비스Secret이 사용자의 스마트폰까지 내려갑니다.
앱을 분석하면 노출될 가능성이 있습니다.
대신 이렇게 만듭니다.
Flutter App
↓
우리 Backend / Edge Function
↓
Secret Key
↓
외부 서비스Secret은 서버에만 존재합니다.
Flutter 앱은
이 작업 해주세요.
라고 우리 서버에 요청할 뿐입니다.
서버가 필요한 Secret을 사용합니다.
6. Supabase Secret Key는 특히 조심해야 한다
MyLog에서는 이 부분이 정말 중요합니다.
현재 Supabase의 Secret Key는 높은 권한을 갖고 있으며 RLS를 우회할 수 있습니다. Supabase는 이를 모바일 앱, 브라우저, 공개 소스, 실행 파일 등에 포함하지 말라고 명확하게 안내합니다.
즉 이런 코드는 절대 만들지 않습니다.
const secretKey = 'sb_secret_xxxxxxxxx';AI가
권한 문제가 있으니 Secret Key를 사용하면 해결됩니다.
라고 하더라도 안 됩니다.
#13에서 어렵게 만들어놓은 RLS 경비원을 데려다 놓고,
Secret Key로 직원 전용 뒷문을 활짝 열어주는 꼴입니다. 🔓
7. 2026년 Supabase Key 이름도 달라지고 있다
예전 글이나 영상에서는 이런 이름을 많이 볼 수 있습니다.
anon key
service_role key2026년 현재 Supabase는 새로운
Publishable Key
Secret Key체계로 전환하고 있습니다.
대응 관계는 대략 이렇습니다.
기존 anon
→ Publishable Key
기존 service_role
→ Secret KeySupabase는 기존 anon과 service_role 키를 레거시 방식으로 분류하고 있으며 2026년 말까지 단계적으로 폐기할 예정이라고 안내하고 있습니다.
따라서 오래된 바이브 코딩 예제를 그대로 따라가기보다 현재 공식 문서의 Key 종류부터 확인하는 습관이 좋습니다.
AI 역시 오래된 학습 자료를 근거로 예전 키 이름을 제안할 수 있으니까요.
8. 이제 MyLog의 환경값을 코드 밖으로 빼보자
현재는 이런 식이라고 해보겠습니다.
await Supabase.initialize(
url: 'https://example.supabase.co',
publishableKey: 'sb_publishable_xxxxx',
);이것을 설정값으로 분리해보겠습니다.
Flutter에서는 컴파일 시점의 환경 값을 전달하고 String.fromEnvironment() 등으로 읽을 수 있습니다.
Dart 공식 문서에서도 이러한 compilation environment declaration을 개발/운영 백엔드 전환, 기능 플래그, 환경별 설정 등에 사용할 수 있다고 설명합니다.
예를 들면
class AppConfig {
static const supabaseUrl =
String.fromEnvironment('SUPABASE_URL');
static const supabasePublishableKey =
String.fromEnvironment('SUPABASE_PUBLISHABLE_KEY');
}그리고 초기화에서는
await Supabase.initialize(
url: AppConfig.supabaseUrl,
publishableKey: AppConfig.supabasePublishableKey,
);처럼 사용합니다.
이제 코드 자체에는 실제 프로젝트 값이 들어가지 않습니다.
9. 실행할 때 값을 전달한다
예를 들어 개발 환경을 실행하면서 다음과 같이 값을 전달할 수 있습니다.
flutter run \
--dart-define=SUPABASE_URL=https://example.supabase.co \
--dart-define=SUPABASE_PUBLISHABLE_KEY=sb_publishable_xxxxx동작은 합니다.
그런데 명령어가 너무 깁니다.
매일 이렇게 입력하다 보면 바이브 코딩보다 손가락 코딩 비중이 커집니다.
그래서 설정 파일을 사용하는 방법도 있습니다.
10. 환경별 설정 파일을 만든다
예를 들어 프로젝트에 이런 구조를 둘 수 있습니다.
config/
dev.json
prod.jsondev.json에는 개발 환경 값을 넣습니다.
{
"SUPABASE_URL": "https://dev-example.supabase.co",
"SUPABASE_PUBLISHABLE_KEY": "sb_publishable_xxxxx"
}그리고 실행합니다.
flutter run --dart-define-from-file=config/dev.jsonSupabase의 모바일 관련 공식 가이드에서도 Flutter에서 소스 코드 밖에서 빌드 설정을 전달하는 방법으로 --dart-define-from-file 같은 빌드 타임 방식을 사용할 수 있다고 안내하고 있습니다.
운영 빌드에서는
flutter build appbundle \
--dart-define-from-file=config/prod.json처럼 사용할 수 있습니다.
다시 강조하지만 여기 들어 있는 Publishable Key가 암호화된 Secret으로 변하는 것은 아닙니다.
환경을 분리하기 편해진 것입니다.
11. 개발 환경과 운영 환경을 분리하는 이유
처음에는 Supabase 프로젝트 하나만 사용해도 됩니다.
하지만 제품 단계로 갈수록 개발 환경과 실제 사용자 환경을 분리하는 것이 좋습니다.
예를 들면
Development
개발 테스트용 DB
테스트 사용자
테스트 데이터
Production
실제 사용자
실제 데이터
실제 운영 환경이렇게 나눕니다.
왜 필요할까요?
개발하다가 AI에게
records 테이블 데이터 전부 초기화해줘.
라고 요청했다고 생각해보겠습니다.
개발 DB라면 괜찮을 수 있습니다.
Production DB였다면 갑자기 손에서 땀이 납니다. 😨
환경 분리는 단순히 URL 관리 문제가 아닙니다.
실수의 피해 범위를 줄이는 안전장치입니다.
12. Flutter에는 Flavor라는 방법도 있다
프로젝트가 조금 더 커지면 개발용 앱과 운영용 앱을 완전히 구분할 수도 있습니다.
Flutter는 Android와 iOS/macOS에서 Flavor를 구성할 수 있으며 환경에 따라 앱 이름, API Endpoint, API Key, Feature Flag, 로그 수준 등을 다르게 적용할 수 있습니다.
예를 들어
MyLog Dev
MyLog두 앱을 스마트폰에 함께 설치할 수도 있습니다.
개발용은 테스트 서버에 붙이고,
운영용은 실제 서버에 붙입니다.
MyLog Dev
↓
Development Supabase
MyLog
↓
Production Supabase현재 MVP에서 바로 복잡한 Flavor 설정까지 갈 필요는 없습니다.
다만 스토어 배포 전에는 이런 환경 분리가 왜 필요한지 알게 될 겁니다.
13. .gitignore는 꼭 확인한다
환경별 설정 파일에 Git 저장소에 남기고 싶지 않은 값이 있다면 .gitignore에 추가합니다.
예를 들어
config/dev.json
config/prod.json
.env
.env.*처럼 프로젝트 상황에 맞게 관리할 수 있습니다.
그리고 Git에 올라갈 수 있는 예제 파일을 하나 만듭니다.
config/example.json내용은
{
"SUPABASE_URL": "YOUR_SUPABASE_URL",
"SUPABASE_PUBLISHABLE_KEY": "YOUR_PUBLISHABLE_KEY"
}정도입니다.
그러면 다른 환경에서 프로젝트를 받아도
어떤 설정이 필요한가?
를 알 수 있습니다.
14. 그런데 .gitignore를 나중에 추가하면 끝일까?
여기서 정말 많이 하는 실수가 있습니다.
이미 Secret을 Git에 Commit했습니다.
그다음 .gitignore에 파일을 추가합니다.
그리고 말합니다.
이제 괜찮지?
아닙니다.
Git은 기억력이 지나치게 좋습니다.
현재 파일에서 사라져도 과거 Commit에 값이 남아 있을 수 있습니다.
따라서 진짜 Secret이 Git 기록에 들어갔다면 기본 대응은
1. Secret 폐기 또는 Rotate
2. 새로운 Secret 발급
3. 코드에서 제거
4. Git 기록 노출 범위 확인
5. 필요하면 Repository History 정리입니다.
가장 먼저 해야 하는 것은 숨기는 것이 아니라 노출된 키를 더 이상 유효하지 않게 만드는 것입니다.
15. AI에게 Secret을 채팅으로 보내는 것도 습관적으로 피하자
바이브 코딩 중 오류가 생겼습니다.
AI에게 전체 환경 파일을 붙여넣습니다.
SUPABASE_SECRET_KEY=...
DATABASE_PASSWORD=...이런 방식은 피하는 편이 좋습니다.
AI에게 필요한 것은 실제 값이 아니라 구조와 오류 메시지인 경우가 많습니다.
예를 들어
SUPABASE_SECRET_KEY=<REDACTED>처럼 값을 가리고 전달할 수 있습니다.
프로젝트 규칙에도 넣겠습니다.
- 실제 Secret 값을 AI 프롬프트에 불필요하게 포함하지 않는다.
- 오류 분석에는 Secret 값을 제거한 설정 구조만 제공한다.비밀은 여러 사람에게 보여주지 않을수록 관리하기 쉽습니다.
AI도 예외로 만들 필요는 없습니다.
16. 환경변수에 넣어도 안 되는 Secret은 안 되는 것이다
이 부분을 다시 한번 강조하겠습니다.
Flutter에서 이렇게 사용한다고 해보겠습니다.
const secretKey =
String.fromEnvironment('SECRET_KEY');그리고 빌드할 때
flutter build appbundle \
--dart-define=SECRET_KEY=super-secret라고 넣었습니다.
소스 코드에는 값이 없습니다.
하지만 앱은 이 값을 사용해야 합니다.
따라서 최종 앱에 포함될 수 있습니다.
즉
코드에 직접 작성
→ 위험
dart-define로 주입
→ Git 관리에는 좋음
→ 모바일 Secret 보호 수단은 아님입니다.
환경변수라는 이름 때문에 보안 기능으로 착각하지 않는 것이 중요합니다.
Dart의 compilation environment 자체도 일반적인 운영체제 환경변수와는 별개의 컴파일 설정값이라고 공식 문서에서 구분하고 있습니다.
17. 그럼 외부 AI API Key 같은 것은 어떻게 하나?
나중에 앱에 외부 AI 기능을 붙인다고 가정해보겠습니다.
예를 들어 외부 API가 이런 Secret Key를 요구합니다.
sk-xxxxxxxx그 키를 Flutter 앱에서 직접 사용하면 안 되는 경우가 많습니다.
구조를 이렇게 만듭니다.
Flutter
↓
우리 Backend
또는
Supabase Edge Function
↓
외부 API그리고 Secret은
Edge Function 환경변수처럼 서버 측에서만 읽을 수 있는 위치에 저장합니다.
Flutter에는 내려보내지 않습니다.
이제 사용자가 앱을 분석해도 외부 서비스의 Secret Key는 들어 있지 않습니다.
18. 클라이언트와 서버의 경계를 다시 보자
#02에서 배운 구조가 여기서 다시 중요해집니다.
Client
사용자 스마트폰
↓
Server
우리가 통제하는 환경보안에서 중요한 질문은
이 값이 어느 파일에 있는가?
보다
이 값이 어느 신뢰 영역에 존재하는가?
입니다.
모바일 앱은 사용자의 기기에 있습니다.
우리가 완전히 통제할 수 없습니다.
그래서 기본적으로 Secret을 보관할 수 있는 신뢰된 서버 환경으로 보지 않습니다.
19. 그렇다면 사용자 로그인 Token은?
조금 다른 문제입니다.
로그인하면 Access Token과 Refresh Token 같은 인증 정보가 사용됩니다.
이런 값은 사용자의 로그인 세션을 나타내기 때문에 민감하게 다뤄야 합니다.
하지만 MyLog에서 우리가 직접
SQLite에 token 저장
SharedPreferences에 refresh token 저장같은 자체 인증 저장 시스템을 만들 필요는 없습니다.
Supabase Flutter SDK는 인증 세션을 관리하고 앱 초기화 시 로컬 저장소에서 기존 세션을 가져오는 흐름을 제공합니다. 현재 v2에서는 초기화 직후 로컬 세션이 이미 만료됐을 가능성이 있으므로 세션 유효성과 onAuthStateChange 등을 함께 확인할 필요가 있습니다.
우리는 인증 SDK가 맡아야 할 일을 굳이 다시 발명하지 않겠습니다.
20. 비밀번호는 환경변수에도 저장하지 않는다
또 하나 흔한 오해가 있습니다.
DB 비밀번호를 코드에 넣으면 안 되니까 .env에 넣으면 되겠네.서버의 환경변수라면 적절할 수 있습니다.
그런데 Flutter 앱에서 그 DB 비밀번호를 직접 사용한다는 구조 자체가 문제입니다.
사용자 비밀번호도 마찬가지입니다.
사용자 로그인 비밀번호
↓
직접 저장 X인증 서비스에 전달하고 세션 기반으로 동작합니다.
즉 Secret을 안전하게 관리하는 첫 번째 방법은
안전한 저장 장소를 찾는 것보다 애초에 클라이언트가 그 Secret을 가지지 않게 설계하는 것입니다.
21. 로그도 비밀정보의 탈출구가 될 수 있다
환경 파일은 잘 관리했습니다.
Secret도 코드에 없습니다.
그런데 AI가 디버깅을 위해 이렇게 작성했습니다.
print('Token = $accessToken');다시 새어 나갑니다.
또는
print('Request headers: $headers');안에 인증 헤더가 포함될 수도 있습니다.
따라서 보안 관리에는 코드뿐 아니라 로그도 포함됩니다.
비밀번호
Access Token 전체 값
Refresh Token
Secret API Key
인증 Header는 로그에 남기지 않는 것을 기본 원칙으로 잡겠습니다.
22. 이제 MyLog의 설정 구조를 정리해보자
현재 MyLog에서는 설정값을 크게 세 종류로 분류할 수 있습니다.
클라이언트 공개 가능 설정
Supabase URL
Supabase Publishable Key
Environment 이름
Feature Flag앱에 들어갈 수 있습니다.
다만 소스와 분리해 환경별로 관리합니다.
서버 전용 Secret
Supabase Secret Key
service_role Key
외부 서비스 Secret
관리자 CredentialFlutter 앱에는 절대 넣지 않습니다.
Backend나 Edge Function에 둡니다.
사용자 인증정보
Session
Access Token
Refresh TokenSDK의 인증 흐름을 사용하고,
우리가 임의로 평문 저장하거나 로그에 남기지 않습니다.
이 세 가지를 구분할 수 있으면 오늘 내용의 절반 이상은 끝났습니다.
23. 프로젝트 규칙을 업데이트한다
PROJECT_RULES.md에도 이번 원칙을 추가하겠습니다.
## Secrets and Configuration
- Secret Key를 Flutter 클라이언트에 포함하지 않는다.
- Supabase Flutter 앱에서는 Publishable Key만 사용한다.
- service_role 또는 Supabase Secret Key는 Backend에서만 사용한다.
- 환경별 설정값은 소스 코드와 분리한다.
- 환경 설정 파일의 Git 포함 여부를 반드시 확인한다.
- 사용자 비밀번호를 저장하지 않는다.
- Token 및 Secret 전체 값을 로그에 출력하지 않는다.
- Secret이 Git에 Commit되었다면 삭제만 하지 말고 즉시 Rotate한다.
- Client에 포함되는 값을 Secret이라고 가정하지 않는다.이제 AI가 다음 작업에서 실수할 가능성을 조금 더 낮췄습니다.
24. AI에게 현재 프로젝트를 보안 점검시켜보자
이번에는 새로운 기능 대신 한번 검색을 시켜보겠습니다.
MyLog 프로젝트의 비밀정보 관리 상태를 점검해줘.
아직 코드를 수정하지 마.
다음 항목을 확인해줘.
1. API Key가 소스 코드에 직접 작성되어 있는지
2. Supabase Secret Key 또는 service_role 사용 여부
3. Publishable Key와 Secret Key가 혼동된 부분
4. 실제 Credential이 Git 추적 파일에 들어 있는지
5. .gitignore 설정
6. Token이나 비밀번호가 로그에 출력되는지
7. 인증정보를 SQLite나 일반 설정 저장소에 직접 저장하는지
8. 개발용과 운영용 설정을 분리하기 어려운 구조인지
발견한 문제를
Critical
High
Medium
Low
순으로 분류하고
수정 전에 먼저 보고해줘.AI를 이번에는 개발자가 아니라 보안 점검 담당자로 사용합니다.
25. 그리고 Git에서도 직접 검색해보자
현재 파일만 볼 것이 아니라 이런 값이 들어 있는지도 확인해볼 수 있습니다.
예를 들면 IDE 전체 검색에서
sb_secret_
service_role
password
secret
token
apikey
api_key등을 찾아봅니다.
모든 검색 결과가 문제라는 뜻은 아닙니다.
예를 들어 변수 이름
publishableKey는 정상입니다.
우리가 찾는 것은 실제 민감한 값이 실수로 들어가 있는가입니다.
26. 환경 파일을 Git에서 제외했는지도 확인한다
git status를 실행합니다.
우리가 제외하려는 설정 파일이 나타나면 .gitignore가 제대로 적용되지 않은 것입니다.
반대로 Git에 포함시키려는
config/example.json같은 템플릿 파일은 정상적으로 보여야 합니다.
이 작은 확인 하나로 꽤 많은 사고를 막을 수 있습니다.
27. 개발용과 운영용을 명확하게 표시하자
나중에 Flavor를 만들게 된다면 앱 자체에도 개발 버전이라는 표시를 주는 것이 좋습니다.
예를 들어
MyLog DEV라는 앱 이름을 사용하거나,
개발 앱에만 작은
DEV표시를 보여줄 수 있습니다.
왜냐하면 실제 스마트폰에
MyLog Dev
MyLog두 개가 설치되어 있을 때 잘못된 앱으로 테스트하는 일을 막아주기 때문입니다.
사소해 보이지만 운영 단계에서는 이런 작은 표시가 꽤 유용합니다.
Flutter의 공식 Flavor 기능도 환경에 따라 앱 이름, 아이콘, API 설정 등을 다르게 구성할 수 있도록 지원합니다.
28. 오늘의 삽질 방지 팁 🛠️
AI가 이런 말을 했다면 한번 멈춰보세요.
API Key가 노출되지 않도록 .env로 옮겼습니다.바로 질문합니다.
이 값이 최종 모바일 앱의 빌드 결과에 포함되는가?
YES라면 그것은 진짜 Secret 보호책이 아닙니다.
다음 질문을 합니다.
이 키는 원래 클라이언트에 노출되어도 되도록 설계된 Publishable Key인가?
YES라면 괜찮을 수 있습니다.
NO라면 구조를 바꿔야 합니다.
Client에서 사용할 키인가?
↓
YES → 낮은 권한 + 서버 측 권한검사
+ 환경별 설정 관리
NO → Backend / Edge Function으로 이동이 질문 하나가 꽤 많은 보안 실수를 막아줍니다.
29. 환경변수는 보안 기능보다 구성 관리 기능에 가깝다
이번 편에서 이것 하나는 확실하게 기억하고 넘어가고 싶습니다.
많은 바이브 코딩 예제에서
.env
=
보안처럼 이야기합니다.
조금 더 정확하게 보면
.env
dart-define
Flavor는 주로 Configuration Management, 즉 설정 관리에 가깝습니다.
개발과 운영 환경을 분리하고,
코드에 특정 서버 주소를 박아두지 않고,
CI/CD에서 빌드마다 다른 값을 전달하는 데 유용합니다.
실제로 Flutter의 CI/CD 공식 문서도 빌드 과정에서 환경 설정을 전달하는 방법과 CI 시스템의 암호화 환경변수를 활용하는 방법을 안내합니다.
하지만 클라이언트 앱의 Secret을 안전하게 숨기는 만능 방패는 아닙니다.
30. 오늘 MyLog는 화면이 하나도 바뀌지 않았다
지난 #15도 화면 변화가 크지 않았습니다.
이번 편은 더합니다.
사용자가 보기에는 아무것도 달라지지 않았을 수도 있습니다.
하지만 프로젝트 내부에서는 중요한 변화가 생겼습니다.
이전
Flutter Source
Supabase URL
Publishable Key
설정값이후
Flutter Source
│
↓
AppConfig
│
↓
Build Configuration
├── Development
└── Production그리고 진짜 Secret은
Flutter에서 사라지고
Backend / Edge Function영역으로 이동합니다.
앱이 제품에 가까워질수록 이런 눈에 안 보이는 정리가 점점 중요해집니다.
31. 오늘의 완료 조건
이번 편도 체크해보겠습니다.
[ ] API Key가 전부 같은 보안 수준이 아니라는 것을 이해했다.
[ ] Supabase Publishable Key와 Secret Key를 구분할 수 있다.
[ ] Supabase Secret Key를 Flutter 앱에 넣지 않는다.
[ ] 기존 service_role도 클라이언트에서 사용하지 않는다.
[ ] 모바일 앱에서는 Secret을 완전히 숨길 수 없다는 것을 이해했다.
[ ] .env와 dart-define의 역할을 보안과 설정 관리로 구분했다.
[ ] Supabase URL과 Publishable Key를 소스 코드에서 분리했다.
[ ] 개발 환경과 운영 환경 설정을 분리할 준비를 했다.
[ ] 환경 설정 파일의 .gitignore 상태를 확인했다.
[ ] 실제 Secret이 Git 기록에 들어간 적이 없는지 확인했다.
[ ] 노출된 Secret은 삭제가 아니라 Rotate해야 한다는 것을 이해했다.
[ ] 비밀번호를 직접 저장하지 않는다.
[ ] Token과 Secret 전체 값을 로그에 출력하지 않는다.
[ ] PROJECT_RULES.md에 Secret 관리 규칙을 추가했다.
[ ] 현재 프로젝트를 AI에게 보안 관점으로 다시 검토시켰다.여기까지 됐다면 MyLog는 이제
코드가 작동하는 앱
에서 조금 더 나아가
어떤 값을 어디에 두면 안 되는지 아는 앱
이 됐습니다.
32. 로그인도 만들었고 RLS도 만들었는데 이제 안전한 걸까?
여기서 새로운 질문이 생깁니다.
우리는 이미
회원가입도 만들었고,
로그인도 만들었고,
Secret도 분리했고,
사용자별 RLS도 구성했습니다.
그럼 보안은 끝난 걸까요?
아직 아닙니다.
예를 들어 관리자 기능이 생겼다고 해보겠습니다.
일반 사용자
기록 조회 가능
관리자
모든 사용자 기록 관리 가능이제 단순히
로그인했는가?
만으로는 부족합니다.
이 로그인한 사용자가 무엇을 할 수 있는가?
를 더 세밀하게 결정해야 합니다.
#12에서 잠깐 만났던
Authentication과 Authorization
을 이제 제대로 구분할 차례입니다.
다음 편
[바이브 코딩 실전 #17] 로그인만 만들면 보안이 끝날까? | 인증과 권한의 차이 이해하기
다음 편에서는
로그인했다고 모든 API를 사용할 수 있게 하면 왜 위험한지,
Client 화면에서 버튼을 숨기는 것과 실제 권한 차단은 무엇이 다른지,
RLS와 Role을 어떻게 바라봐야 하는지,
그리고 AI가 자주 만들어버리는 “UI에서는 안 보이니까 안전한 관리자 기능”의 위험성까지 살펴보겠습니다.
로그인은 건물 입구를 통과한 것뿐입니다.
다음에는 어느 방까지 들어갈 수 있는지 정해보겠습니다. 🔐🏢
연재 기준: 2026년 8월Supabase는 현재 클라이언트용 Publishable Key와 서버용 Secret Key 체계로 전환하고 있으며 기존 anon과 service_role 키는 2026년 말까지 단계적 폐기 예정이라고 안내하고 있습니다. 또한 Flutter의 빌드 환경 설정과 Flavor 방식은 계속 변경될 수 있으므로 실제 적용 시점의 Dart, Flutter, Supabase 공식 문서를 함께 확인하는 것을 권장합니다.
#바이브코딩 #VibeCoding #AI코딩 #Flutter #APIKey #환경변수 #비밀정보관리 #앱보안 #Supabase #PublishableKey #SecretKey #DartDefine #FlutterFlavor #Gitignore #비개발자앱개발 #MyLog #바이브코딩실전
