목차
지난 편에서 MyLog에게 처음으로 제대로 된 기억을 만들어줬습니다.
기록을 작성한 뒤 앱을 완전히 종료해도 SQLite에 데이터가 남습니다.
이제 기록 앱답습니다.
그런데 한 가지 문제가 있습니다.
현재 MyLog는 누가 작성한 기록인지 모릅니다.
제 스마트폰에 기록이 있으면 그냥 제 기록이고,
다른 사람이 같은 앱을 설치하면 그 사람 스마트폰 안에 또 다른 기록이 생깁니다.
사용자라는 개념 자체가 없습니다.
이제 앱에게 물어볼 차례입니다.
지금 이 앱을 사용하고 있는 사람이 누구지?
여기서 등장하는 것이 Authentication, 인증입니다.
오늘은 MyLog에 회원가입과 로그인을 추가해보겠습니다.
그리고 이번 편부터는 조금 더 조심해야 합니다.
화면 하나 잘못 만드는 정도와
인증을 잘못 만드는 것은
문제의 무게가 꽤 다르기 때문입니다.
1. 로그인 화면부터 만들면 될까?
회원가입 기능을 만든다고 하면 가장 먼저 떠오르는 화면은 이것입니다.
이메일
[________________]
비밀번호
[________________]
[ 로그인 ]
회원가입그리고 AI에게 이렇게 말할 수도 있습니다.
로그인 화면 만들어줘.
AI는 30초 만에 그럴듯한 화면을 만들어줄 겁니다.
하지만 이것은 로그인 UI일 뿐입니다.
실제 인증에는 훨씬 많은 일이 숨어 있습니다.
사용자 입력
↓
인증 서버에 로그인 요청
↓
이메일과 비밀번호 확인
↓
인증 성공
↓
Session 발급
↓
로그인 상태 유지
↓
사용자 식별즉 로그인 화면은 현관문입니다.
그 뒤에는 자물쇠도 있고,
열쇠도 있고,
출입기록도 있습니다.
우리가 만들고 싶은 것은 현관문 그림이 아니라 실제로 잠기는 문입니다. 🔐
2. Authentication과 Authorization은 다르다
앞으로 자주 등장할 개념이라 여기서 확실히 구분해두겠습니다.
Authentication
너 누구야?
를 확인하는 과정입니다.
예를 들어
이메일
+
비밀번호
↓
김개발 사용자 확인회원가입과 로그인이 여기에 해당합니다.
Authorization
너 이거 해도 돼?
를 결정하는 것입니다.
예를 들어
사용자 A
→ 사용자 A의 기록 조회 가능
사용자 A
→ 사용자 B의 기록 조회 불가이것이 권한입니다.
정리하면
Authentication
누구인가?
Authorization
무엇을 할 수 있는가?입니다.
오늘은 Authentication부터 시작합니다.
사용자가 자기 기록만 볼 수 있도록 만드는 권한 문제는 다음 편의 클라우드 DB와 함께 제대로 다뤄보겠습니다.
3. 인증 시스템을 직접 만들지는 않는다
비밀번호 인증을 직접 구현하려고 하면 생각보다 굉장히 많은 문제가 따라옵니다.
회원 DB를 만들고,
비밀번호를 안전하게 저장하고,
이메일 인증을 만들고,
세션을 발급하고,
토큰을 갱신하고,
비밀번호 찾기를 만들고,
로그아웃을 처리하고...
비개발자의 첫 앱에서 직접 구현할 이유가 없습니다.
그래서 우리는 Supabase Auth를 사용하겠습니다.
Supabase의 Flutter SDK인 supabase_flutter는 인증과 사용자 관리뿐 아니라 이후 사용할 Postgres Database, Storage 등의 기능도 함께 사용할 수 있도록 제공됩니다.
앞으로 클라우드 DB를 사용할 계획도 있기 때문에 MyLog에는 꽤 자연스러운 선택입니다.
4. 잠깐, SQLite를 버리는 건가요?
아닙니다.
현재 구조는
MyLog
↓
SQLite
↓
로컬 기록입니다.
오늘은 여기에 인증이 추가됩니다.
MyLog
├── Supabase Auth
│ └── 사용자 확인
│
└── SQLite
└── 로컬 기록아직 기록 자체를 Supabase에 올리지는 않습니다.
그 작업은 #13에서 진행합니다.
이번에도 기능 하나씩 갑니다.
로그인과 클라우드 DB를 동시에 바꾸면 문제가 발생했을 때 인증 문제인지 데이터 문제인지 구분하기 어려워집니다.
5. 먼저 Supabase 프로젝트를 준비한다
Supabase 프로젝트를 만든 뒤 Flutter 프로젝트에 공식 Flutter 클라이언트를 추가합니다.
flutter pub add supabase_flutter그리고 앱 시작 시 Supabase를 초기화합니다.
현재 공식 Flutter Quickstart에서는 프로젝트 URL과 Publishable Key를 이용하는 방식을 안내하고 있습니다.
개념적으로는 이런 형태입니다.
await Supabase.initialize(
url: 'YOUR_SUPABASE_URL',
publishableKey: 'YOUR_SUPABASE_PUBLISHABLE_KEY',
);그리고 앱에서는 보통
final supabase = Supabase.instance.client;를 통해 사용할 수 있습니다.
여기서 바로 중요한 보안 이야기를 하나 해야 합니다.
6. Publishable Key와 Secret Key를 헷갈리면 안 된다
예전 Supabase 자료를 찾아보면
anon key
service_role key라는 표현을 많이 볼 수 있습니다.
하지만 2026년 현재 Supabase는 새로운 프로젝트에서
Publishable Key
Secret Key체계를 사용하도록 안내하고 있으며 기존 anon, service_role 방식은 레거시 키로 분류하고 있습니다. Supabase는 기존 레거시 키의 단계적 폐기를 2026년 말까지 진행할 예정이라고 안내하고 있습니다.
두 키의 차이는 굉장히 중요합니다.
Publishable Key
모바일 앱이나 웹 같은 클라이언트에서 사용할 수 있는 낮은 권한의 키
Secret Key
서버에서만 사용하는 높은 권한의 키
Supabase 공식 문서는 Secret Key가 Row Level Security를 우회할 수 있기 때문에 브라우저나 모바일 앱에 절대로 노출하면 안 된다고 명확하게 안내합니다.
즉 이런 요청은 위험합니다.
Supabase 연결이 안 되니까 Secret Key를 Flutter 앱에 넣어줘.
AI가 그렇게 해결책을 제안하더라도 거절해야 합니다.
작동은 할 수 있습니다.
그런데 문이 안 열린다고 마스터키를 현관 앞에 테이프로 붙여두는 것과 비슷합니다.
너무 잘 열립니다. 누구에게나. 🔑
7. 프로젝트 규칙에도 하나 추가하자
#08에서 만든 PROJECT_RULES.md에도 규칙을 추가하겠습니다.
## Supabase Security
- Flutter 클라이언트에서는 Publishable Key만 사용한다.
- Secret Key 또는 service_role Key를 클라이언트 코드에 넣지 않는다.
- 관리자 권한이 필요한 작업은 백엔드에서 수행한다.
- 비밀번호를 앱 DB나 로그에 저장하지 않는다.AI에게 말로 한번 경고하는 것보다 프로젝트 규칙으로 남기는 편이 낫습니다.
8. 먼저 회원가입 흐름부터 정한다
MyLog의 회원가입은 최대한 단순하게 갑니다.
회원가입 화면
↓
이메일 입력
↓
비밀번호 입력
↓
회원가입 요청
↓
이메일 확인
↓
로그인 가능Supabase Flutter에서는 signUp()을 이용해 이메일과 비밀번호 기반 사용자를 만들 수 있습니다.
예를 들면
final response = await supabase.auth.signUp(
email: email,
password: password,
);입니다.
코드는 간단합니다.
하지만 여기에서 중요한 것은 코드보다 회원가입 이후의 상태입니다.
9. 회원가입했는데 왜 바로 로그인되지 않을까?
처음 구현하면 여기에서 당황할 가능성이 있습니다.
회원가입은 성공했습니다.
그런데
response.user는 있는데
response.session은 없습니다.
오류일까요?
꼭 그렇지는 않습니다.
현재 Supabase Hosted 프로젝트에서는 이메일 인증을 사용하도록 설정할 수 있으며, 기본적으로 이메일 확인이 활성화된 경우 회원가입 직후에는 사용자 객체는 생성되지만 세션은 생성되지 않을 수 있습니다.
사용자가 확인 이메일의 링크를 눌러 이메일을 검증한 뒤 로그인하게 됩니다.
즉 흐름은
회원가입
↓
"인증 메일을 확인해주세요"
↓
사용자 이메일 확인
↓
로그인이 됩니다.
이메일 인증을 끄면 가입 직후 세션이 만들어질 수 있지만,
제품으로 운영한다면 왜 이메일 확인을 생략하는지 먼저 생각해봐야 합니다.
AI가
테스트 편의를 위해 이메일 인증을 비활성화하겠습니다.
라고 했다면 개발 중에는 편할 수 있습니다.
하지만 그 설정을 그대로 출시하지 않도록 구분해야 합니다.
10. 개발용 설정과 운영용 설정은 다를 수 있다
이런 차이가 앞으로 많이 등장합니다.
개발 단계에서는
이메일 확인 OFF로 빠르게 테스트할 수도 있습니다.
하지만 실제 출시 환경에서는
이메일 확인 ON으로 운영할 수 있습니다.
중요한 것은 AI가 설정을 바꿨다면
무엇을 왜 변경했는지 기록하는 것입니다.
예를 들어 프로젝트 문서에 남깁니다.
개발 환경에서는 인증 테스트 편의를 위해
이메일 확인을 임시 비활성화할 수 있다.
출시 전 운영 환경의 Email Confirmation 설정을
반드시 다시 검토한다.이런 작은 메모가 출시 직전 상당히 유용합니다.
11. 운영 서비스에서는 이메일 발송도 생각해야 한다
회원가입 인증과 비밀번호 재설정에는 이메일 발송이 필요합니다.
Supabase는 테스트를 위한 기본 이메일 발송 서비스를 제공하지만 공식 문서에서는 기본 서비스에 제한이 있으며 실제 운영 환경에서는 Custom SMTP 설정을 고려하도록 안내합니다. 현재 기본 서비스는 이메일 발송량에도 낮은 제한이 있습니다.
이건 지금 구현하지 않겠습니다.
대신 출시 체크리스트에 남깁니다.
[ ] 운영용 SMTP 설정 확인이게 바로 MVP와 제품의 차이입니다.
회원가입 버튼이 작동하는 것만 보면 MVP입니다.
실제로 사용자가 인증 메일을 안정적으로 받을 수 있는지까지 보면 제품 쪽으로 한 단계 더 갑니다.
12. 이제 로그인 기능을 만든다
회원가입된 사용자는 이메일과 비밀번호로 로그인할 수 있습니다.
Supabase Flutter에서는 현재
signInWithPassword()를 사용할 수 있습니다.
대략 이런 형태입니다.
final response =
await supabase.auth.signInWithPassword(
email: email,
password: password,
);성공하면
User
Session정보를 얻을 수 있습니다.
여기서 Session이라는 중요한 개념이 등장합니다.
13. Session은 무엇일까?
로그인할 때마다 앱이 서버에
이메일하고 비밀번호가 다시 맞는지 확인해주세요.
라고 보내는 것은 아닙니다.
한 번 인증이 성공하면 서버는 일종의 출입증을 줍니다.
이것이 세션이라고 생각하면 됩니다.
이메일 + 비밀번호
↓
로그인 성공
↓
Session 발급
↓
이후 요청에서 사용자 확인세션 안에는 Access Token과 Refresh Token 같은 정보가 사용됩니다.
세부 JWT 구조를 지금 공부할 필요는 없습니다.
우리가 알아야 할 것은
로그인 이후에는 비밀번호가 아니라 인증 세션을 이용해 사용자를 식별한다.
입니다.
14. 그래서 비밀번호를 SQLite에 저장하면 안 된다
여기서 지난 편의 SQLite와 연결됩니다.
이런 생각이 들 수 있습니다.
앱을 다시 켰을 때 자동 로그인하려면 이메일과 비밀번호를 SQLite에 저장하면 되겠네?
안 됩니다.
email
password를 평문으로 직접 저장해놓고 자동 로그인하는 방식은 피해야 합니다.
비밀번호 자체는 인증 서비스가 안전하게 처리하도록 맡기고,
앱에서는 인증 SDK가 제공하는 세션 관리 방식을 사용해야 합니다.
Supabase의 Flutter SDK는 앱 시작 시 로컬 저장소에서 기존 세션을 가져올 수 있으며 currentSession으로 현재 세션을 확인할 수 있습니다. 다만 v2에서는 초기화 직후 가져온 세션이 이미 만료됐을 가능성도 있기 때문에 필요하다면 세션 상태와 onAuthStateChange를 함께 확인해야 합니다.
바이브 코딩에서는 이것을 아주 단순한 규칙으로 기억하겠습니다.
자동 로그인을 만들려고 비밀번호를 직접 저장하지 않는다.
15. 앱 시작 화면이 이제 달라진다
지금까지 MyLog는 실행하면 무조건 기록 목록이 나왔습니다.
이제는 로그인 여부를 먼저 확인해야 합니다.
앱 실행
↓
Session 있음?
├── YES → Record List
│
└── NO → Login이것이 오늘 새롭게 추가되는 Auth Gate입니다.
이름은 어렵지만 역할은 단순합니다.
문지기입니다. 🚪
로그인 여부를 확인하고 어느 화면으로 보낼지 결정합니다.
16. 로그인 상태 변화도 들어야 한다
사용자는 앱 실행 중 로그인할 수도 있고,
로그아웃할 수도 있고,
토큰이 갱신될 수도 있습니다.
Supabase Flutter는 onAuthStateChange 스트림을 통해 signedIn, signedOut, tokenRefreshed 같은 인증 이벤트를 받을 수 있습니다. 현재 공식 문서에서는 네트워크 오류가 스트림 오류로 전달될 수 있으므로 onError 처리도 함께 두도록 안내하고 있습니다.
개념적으로는
로그인 성공
↓
SIGNED_IN
↓
앱 화면 변경그리고
로그아웃
↓
SIGNED_OUT
↓
로그인 화면으로 이동입니다.
앱 전체가 인증 상태를 기준으로 움직이기 시작합니다.
17. 로그인 화면에는 어떤 상태가 필요할까?
#09에서 화면보다 상태를 먼저 생각하자고 했습니다.
로그인 화면은 단순히 입력창 두 개만 있는 것이 아닙니다.
최소한 다음 상태가 있습니다.
기본 상태
로그인 처리 중
로그인 성공
로그인 실패예를 들어 사용자가 로그인 버튼을 눌렀습니다.
서버 응답을 기다리는 동안 버튼을 계속 누를 수 있게 두면 중복 요청이 발생할 수 있습니다.
그래서
로그인 중...
[ 처리 중 ]처럼 표시하고 일시적으로 버튼을 비활성화할 수 있습니다.
이런 작은 차이가 MVP와 제품 품질 사이에서 조금씩 쌓입니다.
18. 오류 메시지를 그대로 보여주지 말자
로그인이 실패했습니다.
AI가 서버에서 받은 오류를 그대로 화면에 출력합니다.
AuthApiException(message: Invalid login credentials...)개발자는 이해할 수 있습니다.
사용자는 당황합니다.
사용자에게는
이메일 또는 비밀번호를 확인해주세요.처럼 이해하기 쉬운 메시지를 보여주는 편이 낫습니다.
다만 오류를 전부 똑같이 숨기라는 의미는 아닙니다.
네트워크가 끊겼다면
네트워크 연결을 확인해주세요.정도의 구분은 유용합니다.
즉
개발자용 오류
≠
사용자용 안내입니다.
19. 그리고 비밀번호를 로그로 찍지 않는다
AI에게 디버깅을 맡기다 보면 가끔 이런 코드가 들어갈 수 있습니다.
print('email: $email');
print('password: $password');개발 중에는 문제 찾기가 쉬워 보입니다.
하지만 비밀번호를 로그에 남기는 것은 피해야 합니다.
프로젝트 규칙에 넣겠습니다.
- 비밀번호를 console, log, analytics에 출력하지 않는다.
- Access Token과 Refresh Token 전체 값도 로그에 출력하지 않는다.오류를 잡는다고 열쇠 복사본을 복도 벽에 붙이지는 않습니다.
20. AI에게 인증 구현을 시킬 때 특히 위험한 요청
이런 프롬프트는 피하는 것이 좋습니다.
Supabase 로그인 전체 알아서 구현해줘.
인증은 결정해야 할 것이 많습니다.
AI가
이메일 확인 여부,
세션 처리,
로그인 화면 이동,
오류 메시지,
키 관리
등을 전부 추측하게 됩니다.
우리가 이미 배운 방식으로 범위를 나눕니다.
21. 먼저 계획부터 요청한다
예를 들어 이렇게 요청할 수 있습니다.
PRD와 PROJECT_RULES를 먼저 확인해줘.
이번 단계에서는 MyLog에
Supabase Auth 기반 이메일 인증을 추가한다.
구현 범위:
1. Supabase Flutter 연결
2. 이메일 회원가입
3. 이메일/비밀번호 로그인
4. 현재 Session 확인
5. 로그인 상태에 따른 화면 분기
6. 로그아웃
보안 규칙:
- Flutter 앱에서는 Supabase Publishable Key만 사용한다.
- Secret Key 또는 service_role Key는 절대 클라이언트에 넣지 않는다.
- 비밀번호를 SQLite에 저장하지 않는다.
- 비밀번호 및 인증 토큰 전체 값을 로그에 출력하지 않는다.
이번 단계에서는 구현하지 않는다:
- Google 로그인
- Apple 로그인
- 소셜 로그인
- 비밀번호 재설정
- MFA
- 클라우드 Record 저장
- 사용자별 DB 권한 정책
- 계정 삭제 실제 구현
아직 코드를 수정하지 말고
1. 변경할 파일
2. 인증 흐름
3. Session 처리 방식
4. 오류 처리 방식
5. 필요한 Supabase 설정
6. 보안상 확인해야 할 사항
을 먼저 설명해줘.이번에도 AI는 일단 설계자입니다.
그다음 개발자로 전환합니다.
22. 화면도 최소한으로 구성한다
이번에 필요한 화면은 두 개 정도입니다.
Login Screen
Sign Up Screen그리고 기존
Record List Screen이 있습니다.
흐름은
로그인 안 됨
↓
Login Screen
├── 로그인 성공
│ ↓
│ Record List
│
└── 회원가입
↓
Sign Up Screen입니다.
회원가입 후 이메일 확인이 필요한 설정이라면
회원가입 성공
↓
이메일 확인 안내도 필요합니다.
23. 로그인 성공 여부만 보지 말고 재실행 테스트를 한다
AI가 구현을 끝냈습니다.
이제 테스트합니다.
테스트 1
신규 이메일로 회원가입
→ 회원가입 성공 여부 확인
테스트 2
이메일 확인이 활성화돼 있다면
→ 인증 안내가 제대로 표시되는지 확인
테스트 3
정상 계정으로 로그인
→ 기록 목록으로 이동하는가?
테스트 4
잘못된 비밀번호 입력
→ 앱이 죽지 않고 적절한 안내가 나오는가?
테스트 5
로그인 성공 후 앱 완전 종료
다시 실행
→ 로그인 상태가 적절하게 복원되는가?
테스트 6
로그아웃
→ Login Screen으로 돌아가는가?
테스트 7
로그아웃 후 앱 재실행
→ 로그인 화면이 나오는가?
이 정도는 직접 손으로 확인해보겠습니다.
24. 인터넷을 끊어보는 테스트도 해보자
인증 기능부터는 네트워크가 필요합니다.
그래서 인터넷을 끄고 로그인 버튼을 눌러봅니다.
좋은 앱이라면
그대로 멈추거나,
무한 로딩하거나,
크래시가 나서는 안 됩니다.
적어도
네트워크 연결을 확인한 뒤 다시 시도해주세요.같은 상태가 필요합니다.
AI는 정상적인 인터넷 환경에서 작동하는 코드를 만드는 데 집중하기 쉽습니다.
우리는 가끔 일부러 현실을 망가뜨려봅니다.
제품은 Wi-Fi가 빵빵한 개발자 책상에서만 살지 않으니까요.
25. 로그인했는데 현재 SQLite 기록은 어떻게 할까?
여기서 재미있는 문제가 생깁니다.
MyLog에는 이미 로컬 기록이 있습니다.
그리고 오늘 로그인 기능을 추가했습니다.
그러면 로그인 전 작성했던 SQLite 데이터는 누구 기록일까요?
아직은 답이 없습니다.
현재 버전에서는
인증
↓
사용자 확인
SQLite
↓
기기 기록두 기능이 별도로 존재합니다.
오늘은 억지로 연결하지 않겠습니다.
다음 편에서 데이터 구조를
Record
id
userId
title
content
createdAt형태로 확장하고,
Supabase Postgres에 저장하면서 사용자와 기록을 실제로 연결해보겠습니다.
바로 그때 Authorization도 등장합니다.
26. 여기서 굉장히 중요한 userId가 생긴다
로그인에 성공하면 Supabase 사용자는 고유한 ID를 가집니다.
개념적으로
User
id = abc123
email = user@example.com처럼 생각할 수 있습니다.
앞으로 기록에
userId = abc123를 넣으면
이 Record를 작성한 사람은 abc123 사용자다.
라고 연결할 수 있습니다.
이게 로그인 기능을 붙이는 진짜 이유입니다.
로그인 화면 자체가 목적이 아닙니다.
데이터의 주인을 식별하는 것이 중요합니다.
27. 로그인했다고 데이터가 안전해진 것은 아니다
이건 다음 편을 위해 반드시 기억해야 합니다.
로그인을 구현했다고
이제 사용자별 데이터가 안전하겠네.
라고 생각하면 안 됩니다.
앱 화면에서
내 기록만 조회하도록 작성하는 것과
DB 자체에서
다른 사람의 데이터 접근 금지를 강제하는 것은 다릅니다.
Supabase에서는 Row Level Security, RLS를 이용해 사용자별 데이터 접근 정책을 만들 수 있습니다. 예를 들어 auth.uid()와 user_id를 비교해 현재 로그인한 사용자가 자신의 데이터만 볼 수 있도록 제한할 수 있습니다.
이 이야기는 #13에서 아주 중요하게 다루겠습니다.
로그인은 신분증 검사이고,
RLS는 실제 출입통제입니다.
신분증을 확인했다고 건물의 모든 문을 열어두면 곤란합니다.
28. 회원가입 기능을 넣는 순간 계정 삭제도 계획해야 한다
이번에는 앱스토어 출시까지 생각하는 연재니까 여기서 한 가지를 미리 챙기겠습니다.
Apple의 현재 App Store Review Guidelines에서는 앱이 계정 생성을 지원한다면 앱 안에서 계정 삭제도 제공해야 한다고 명시하고 있습니다. Apple은 단순히 계정을 비활성화하는 것만으로는 충분하지 않고, 관련 개인 데이터 삭제까지 고려해야 한다고 안내합니다.
즉 우리가 오늘
회원가입을 추가하는 순간 미래 작업 목록에는
계정 삭제도 함께 들어갑니다.
제품 개발이 재미있는 이유입니다.
기능 하나가 혼자 오지 않습니다.
#04에서 했던 이야기가 또 돌아왔습니다.
29. 그런데 앱에서 Supabase 관리자 삭제 API를 바로 쓰면 안 된다
Supabase에는 관리자가 사용자를 삭제하는 Admin API가 있습니다.
문제는 이런 관리자 기능에는 높은 권한이 필요하다는 점입니다.
Supabase 공식 문서에서도 사용자 삭제와 같은 Admin 작업은 서버 측에서 수행해야 하며 service_role 또는 그에 대응되는 Secret Key를 클라이언트에 노출해서는 안 된다고 안내합니다.
따라서 나중에 계정 삭제를 구현할 때
Flutter 앱
↓
Secret Key로 사용자 삭제같은 구조를 만들면 안 됩니다.
대신
Flutter 앱
↓
안전한 백엔드 / Edge Function
↓
사용자 확인
↓
Admin 권한으로 계정 삭제같은 구조를 검토하게 됩니다.
이것은 후반부에서 앱스토어 출시를 준비할 때 다시 제대로 만들겠습니다.
지금은 TODO를 남기는 것으로 충분합니다.
30. Google이나 Apple 로그인은 왜 아직 안 넣을까?
이메일 로그인이 완성되면 욕심이 생깁니다.
Google로 로그인
Apple로 로그인
Kakao 로그인
Naver 로그인예쁘게 버튼을 줄 세우고 싶습니다.
하지만 지금은 하지 않습니다.
OAuth가 들어가면
Redirect URL,
플랫폼 설정,
앱 스킴,
Provider 설정,
토큰 처리
등 고려할 것이 추가됩니다.
우리 목표는 인증의 기본 흐름을 이해하는 것입니다.
회원가입
로그인
Session
로그아웃이 네 가지가 먼저입니다.
도어락 설치법을 배우는 중인데 지문, 홍채, 얼굴인식을 동시에 달지는 않겠습니다. 😎
31. 로그인 기능에서 완료 조건을 다시 만들어보자
"로그인 기능 구현 완료"
라는 문장은 너무 모호합니다.
완료 조건을 좀 더 구체적으로 잡겠습니다.
회원가입
- 이메일과 비밀번호를 입력할 수 있다.
- 정상적인 정보로 회원가입할 수 있다.
- 이메일 확인이 필요한 경우 사용자에게 안내한다.
- 회원가입 중 오류가 발생하면 앱이 종료되지 않는다.
로그인
- 이메일과 비밀번호를 입력할 수 있다.
- 정상 계정으로 로그인할 수 있다.
- 잘못된 인증정보에서는 안내 메시지가 표시된다.
- 로그인 중 중복 요청을 막는다.
Session
- 로그인 성공 후 기록 목록으로 이동한다.
- 앱을 재실행했을 때 기존 Session을 확인한다.
- 유효한 Session이면 로그인 화면을 다시 요구하지 않는다.
로그아웃
- 사용자가 로그아웃할 수 있다.
- 로그아웃 후 로그인 화면으로 돌아간다.
- 앱 재실행 후에도 로그아웃 상태가 유지된다.이제 AI가
완료했습니다.
라고 하면 실제로 확인할 수 있습니다.
32. AI가 인증 코드를 만들었으면 Diff를 더 꼼꼼히 보자
인증이나 보안과 관련된 작업에서는 평소보다 변경 사항을 조금 더 유심히 보겠습니다.
특히 확인할 것은 다음과 같습니다.
Secret Key가 들어갔는가?
비밀번호를 저장하는 코드가 있는가?
비밀번호를 print 하고 있는가?
토큰을 전체 로그에 출력하는가?
요청하지 않은 인증 Provider를 추가했는가?
이메일 인증 설정을 임의로 변경했는가?
기존 SQLite 데이터를 삭제했는가?코드를 전부 이해하지 못해도 이런 위험 신호 정도는 확인할 수 있습니다.
33. AI에게 보안 리뷰도 따로 시켜보자
구현한 AI에게
네가 만든 코드 안전해?
라고 물으면 높은 확률로 괜찮다고 할 겁니다. 😅
가능하다면 별도의 리뷰 관점으로 한번 더 확인합니다.
예를 들어
현재 인증 관련 변경 Diff를
보안 리뷰 관점에서 검토해줘.
기능을 새로 구현하거나 코드를 수정하지 말고
문제점만 보고해줘.
특히 다음 항목을 확인해줘.
1. Secret Key 또는 service_role 노출
2. 비밀번호 평문 저장
3. 비밀번호/Token 로그 출력
4. Session 처리 오류
5. 사용자 입력 검증 부족
6. 로그인 실패 예외 처리
7. 네트워크 오류 처리
8. 로그아웃 후 Session 잔존 가능성
각 문제는
위험도와 수정 필요성을 함께 알려줘.AI 하나를 개발자로 썼다면,
잠깐 다른 모자를 씌워 보안 리뷰어로 사용하는 것입니다.
34. 정상이라면 Git Commit
직접 테스트까지 끝났습니다.
회원가입도 되고,
로그인도 되고,
앱을 껐다 켜도 로그인 상태가 이어지고,
로그아웃도 정상입니다.
그러면 변경 내용을 확인합니다.
git statuspubspec.yaml에는 supabase_flutter가 추가됐을 겁니다.
Supabase 초기화 코드와 인증 화면도 생겼을 겁니다.
정상이라면 커밋합니다.
예를 들면
feat: Supabase Auth 회원가입 로그인 구현이제 Git 기록에도 새로운 이정표가 생겼습니다.
Flutter 프로젝트 생성
↓
기록 작성·목록·상세
↓
SQLite 로컬 저장
↓
회원가입·로그인 ← SAVE앱이 조금씩 제품다운 구조를 갖춰갑니다.
35. 오늘 MyLog에는 어떤 변화가 생겼을까?
지난 편까지 MyLog는 사용자가 없었습니다.
기기
↓
SQLite
↓
Record오늘부터는 새로운 계층이 생겼습니다.
사용자
↓
Supabase Auth
↓
Session
↓
MyLog하지만 기록은 아직 로컬입니다.
전체 그림은 지금 이렇게 볼 수 있습니다.
┌─────────────────┐
│ Supabase Auth │
│ 사용자 / Session │
└────────┬────────┘
│
↓
[ MyLog App ]
│
↓
┌─────────────────┐
│ SQLite │
│ Local Records │
└─────────────────┘다음 편에서는 오른쪽 퍼즐 조각을 하나 더 붙입니다.
Supabase Database그리고 처음으로
사용자 A의 기록과 사용자 B의 기록을 분리
하게 됩니다.
36. 오늘의 삽질 방지 팁 🛠️
로그인 구현 중 막혔을 때 이것만큼은 하지 않는 것을 권합니다.
권한 문제인 것 같으니까 service_role 키 넣어서 해결해줘.
문제가 사라질 수도 있습니다.
그런데 그건 해결이 아니라 보안 장치를 제거한 것에 가깝습니다.
인증이나 권한 문제가 생기면 AI에게
보안 수준을 낮추거나
Secret Key를 클라이언트에 넣는 방식은 사용하지 말고
현재 인증 흐름과 권한 설정에서
문제 원인을 먼저 찾아줘.라고 명확히 요청하는 것이 좋습니다.
바이브 코딩에서 정말 위험한 코드는
실행되지 않는 코드가 아닙니다.
너무 잘 실행되는데 보안이 없는 코드입니다.
37. 오늘의 완료 조건
이번 편도 마지막으로 체크해보겠습니다.
[ ] Authentication과 Authorization의 차이를 이해했다.
[ ] MyLog에 supabase_flutter를 연결했다.
[ ] Flutter에서는 Publishable Key를 사용한다는 것을 확인했다.
[ ] Secret Key를 클라이언트에 사용하면 안 되는 이유를 이해했다.
[ ] 이메일 회원가입이 동작한다.
[ ] 이메일 확인 설정에 따른 회원가입 흐름 차이를 이해했다.
[ ] 이메일과 비밀번호로 로그인할 수 있다.
[ ] 비밀번호를 SQLite나 로그에 저장하지 않는다.
[ ] 로그인 중 상태와 오류 처리가 있다.
[ ] 현재 Session을 기준으로 첫 화면을 결정한다.
[ ] 앱 재실행 후 로그인 상태를 확인했다.
[ ] 로그아웃할 수 있다.
[ ] 네트워크 오류 상황을 테스트했다.
[ ] 기존 SQLite 기록 기능이 깨지지 않았다.
[ ] 계정 생성 기능을 넣었으므로 향후 계정 삭제 기능이 필요함을 기록했다.
[ ] 정상 상태를 Git Commit으로 남겼다.여기까지 완료했다면 MyLog는 이제 처음으로
"누가 앱을 사용하고 있는가"
를 알 수 있게 됐습니다.
38. 그런데 로그인했다고 아직 내 데이터가 되는 것은 아니다
여기서 오늘 가장 중요한 문장을 하나 남기겠습니다.
사용자를 알아냈다고 데이터의 소유권까지 자동으로 생기는 것은 아니다.
현재는 로그인한 사용자가 있어도 SQLite 기록은 그냥 기기에 저장되어 있습니다.
다음 편에서는 Record에
userId를 추가합니다.
그리고 데이터를 Supabase Postgres에 올립니다.
그다음 정말 중요한 테스트를 해볼 겁니다.
사용자 A로 로그인해서 기록을 작성합니다.
로그아웃합니다.
사용자 B로 로그인합니다.
사용자 A의 기록이 보이면?
🚨 실패입니다.
UI에서 숨기는 정도로 끝내지 않고 데이터베이스 자체에서 접근을 막아야 합니다.
바로 Row Level Security, RLS가 등장할 차례입니다.
다음 편
[바이브 코딩 실전 #13] 내 폰의 데이터를 클라우드로 | Database와 API 연결하기
다음 편부터 MyLog는 단순한 로컬 앱에서 처음으로 온라인 서비스가 됩니다.
그리고 서버와 연결되는 순간부터
인터넷 끊김,
로딩,
권한,
사용자별 데이터,
API 오류
같은 친구들이 우르르 입장합니다.
앱 개발 파티가 조금씩 북적거리기 시작합니다. ☁️📱
연재 기준: 2026년 8월Supabase Auth의 SDK, API Key 체계, 인증 설정은 변경될 수 있습니다. 현재 Supabase는 클라이언트에서 Publishable Key 사용을 권장하며 기존 anon/service_role 레거시 키는 2026년 말까지 단계적 폐기 예정이라고 안내하고 있습니다. 또한 Apple은 계정 생성 기능이 있는 앱에 앱 내 계정 삭제 기능을 요구하고 있으므로 실제 스토어 제출 전 최신 공식 정책을 다시 확인해야 합니다.
#바이브코딩 #VibeCoding #AI코딩 #Flutter #Supabase #SupabaseAuth #Flutter로그인 #회원가입 #사용자인증 #Authentication #Session #비개발자앱개발 #앱개발 #앱보안 #MVP개발 #MyLog #바이브코딩실전
