Apple Sso 403 Email Verified Type Mismatch

Apple Sign-In 403 에러: email_verified 타입 불일치와 복붙 버그 3종 세트

Apple Sign-In이 403 Forbidden으로 실패하는데, Google Sign-In은 정상 동작하는 상황이었다. 동일한 스택(Rails 8 + Flutter)의 다른 프로젝트에서는 Apple 로그인이 잘 되고 있어서 비교 분석했다. 증상 Apple 로그인: 403 Forbidden Google 로그인: 정상 성공 에러 메시지: "Email not verified by Apple" 원인 1: email_verified 타입 불일치 (핵심) Apple과 Google은 JWT에서 email_verified 필드를 다른 타입으로 반환한다. Provider email_verified 타입 값 예시 Google boolean true Apple string 또는 boolean "true" 또는 true 문제의 코드: ...

2025-10-25 · 3분 소요 · Seunghan
Flutter Auth Cross Audit

7개 Flutter 앱 인증 보안 크로스 감사 - iOS 제출 전 점검

이전 글에서 Flutter + Rails 앱의 세션 버그 3개를 고쳤다. 고치고 나니 궁금해졌다. 다른 프로젝트에도 같은 문제가 있지 않을까? iOS 1.0 제출을 앞둔 7개 Flutter 앱을 대상으로 인증/보안 크로스 감사를 진행했다. 감사 결과 요약 프로젝트 인증 방식 결과 앱 A (부동산 계약서) 자체 JWT + SecureStorage ✅ 양호 앱 B (AI 여행) 자체 JWT + SharedPreferences 🔴 3건 앱 C (팀 관리) 자체 JWT + SharedPreferences 🔴 2건 앱 D (운세/MBTI) Firebase Auth + Supabase 🔴 1건 앱 E (필름 스캐너) Supabase Auth ✅ 양호 앱 F (AI 미디어) Supabase Auth ✅ 양호 앱 G (음성 대화) - ⏭️ 미확인 Supabase SDK가 인증을 관리하는 앱은 모두 양호했고, 자체 JWT 구현 앱에서만 문제가 있었다. ...

2025-10-21 · 2분 소요 · Seunghan
Rails Missing Migration Sessions Table

Production DB에 테이블이 없다: schema.rb와 migration 파일 불일치 사고

회원가입, 로그인이 전부 안 된다는 제보를 받았다. 앱에서는 “예상하지 못한 오류가 발생했습니다"만 반복. 증상 회원가입 시도 → 500 Internal Server Error 로그인 시도 → 동일하게 500 Health check API → 200 OK, DB 연결 정상 서버는 살아있고 DB도 연결되어 있는데, 인증 관련 기능만 전멸. 조사 과정 1단계: 서버 상태 확인 SSH로 접속해서 Rails 환경 확인. rails runner "puts Rails.env" # => production rails runner "puts User.count" # => 13 서버 정상, DB 연결 정상, 유저 데이터도 존재. ...

2025-10-18 · 4분 소요 · Seunghan
Rails Flutter Server Health Check 4 Issues

Rails + Flutter 앱 서버 점검기: 한 번에 터진 4가지 문제와 해결

앱 테스트 빌드를 올리고 직접 돌려보니 한꺼번에 4가지가 안 됐다. Google 로그인 실패, AI 일정 생성이 엉뚱한 결과, 알림 버튼 누르면 크래시, 인기 여행지 섹션이 텅 비어있음. 하나씩 원인을 찾고 고친 과정을 정리한다. 1. Google SSO는 실패하는데 Apple 로그인은 성공 증상 Apple Sign-In은 정상 동작하지만 Google Sign-In만 500 에러. 클라이언트에서는 로그인 실패 토스트만 보인다. 원인 컨트롤러는 이전 커밋에서 수정했지만, Model의 from_omniauth 메서드는 그대로였다. # User 모델 — 마이그레이션 후에도 옛날 컬럼명 참조 def self.from_omniauth(auth) user = find_or_initialize_by(provider: auth.provider, uid: auth.uid) # uid 컬럼 없음 user.image = auth.info.image # image 컬럼도 없음 end DB 스키마에서는 uid → provider_uid, image → avatar_url로 마이그레이션된 상태. 컨트롤러 쿼리는 수정했지만 모델 내부 메서드가 여전히 옛 컬럼을 참조하고 있었다. ...

2025-10-15 · 5분 소요 · Seunghan
Multi Landing Page Netlify Workflow

앱 랜딩 페이지 8개를 하나의 저장소로 관리하는 법

앱을 여러 개 만들다 보면 각각 랜딩 페이지가 필요해진다. 별도 저장소를 8개 만드는 건 관리 비용이 너무 크고, 하나로 묶으면 배포가 복잡해진다. 결국 단일 저장소 + Netlify 개별 사이트 전략으로 정착했다. 디렉토리 구조 landing/ ├── index.html # 회사 메인 페이지 ├── [서비스-A]/ │ ├── index.html │ ├── privacy/ │ └── terms/ ├── [서비스-B]/ │ ├── index.html │ ├── privacy/ │ └── terms/ ├── [서비스-C]/ │ └── index.html │ ... └── Makefile 각 서비스는 독립 디렉토리. privacy/와 terms/ 하위 페이지는 App Store / Google Play 심사 제출용으로 필수다. ...

2025-10-11 · 3분 소요 · Seunghan
Hugo Blog Multi Site Management

Hugo 블로그 3개를 하나의 폴더에서 관리하는 구조

Hugo 블로그를 목적별로 3개 운영하고 있다. 개발 블로그 — 개발 삽질 기록, 기술 문서 (이 블로그) [앱명] 홈페이지 — 앱 소개 + 업데이트 블로그, 다국어(ko/en) 개인 블로그 — 비개발 글 각각 역할이 달라서 분리했지만, 관리는 한 곳에서 하고 싶었다. 디렉토리 구조 ~/domain/ ├── seunghan-xyz/ # 개발 블로그 │ ├── content/ │ │ ├── posts/ # 기술 포스트 │ │ ├── projects/ # 프로젝트 소개 │ │ └── about/ │ ├── themes/ │ │ └── PaperMod/ # git submodule │ ├── hugo.toml │ └── public/ # 빌드 결과물 │ ├── blogs/ │ └── blog_richdada/ │ ├── [앱명]-blog/ # 앱 홈페이지 겸 블로그 │ │ ├── content/ │ │ │ ├── posts/ │ │ │ ├── features/ │ │ │ ├── legal/ │ │ │ └── mcp/ │ │ ├── i18n/ # ko.yaml, en.yaml │ │ ├── hugo.toml │ │ └── netlify.toml │ │ │ └── personal-blog/ # 개인 블로그 │ ├── content/ │ │ └── posts/ │ └── hugo.toml │ └── dcode/ └── landing/ # 정적 랜딩 페이지들 테마 선택 개발 블로그: PaperMod # hugo.toml theme = 'PaperMod' 미니멀하고 빠르다. 코드 하이라이팅이 깔끔하고, 다크모드를 기본 지원한다. 검색, 아카이브, 목차 기능이 내장되어 있어서 추가 설정이 거의 필요 없다. ...

2025-10-08 · 3분 소요 · Seunghan
Flutter Sync Queue Aggressive Error Handling

Flutter Sync Queue에서 불필요한 에러가 반복 노출되는 문제 해결

모바일 앱에서 오프라인 동기화를 위해 Transactional Outbox 패턴을 구현하던 중, 동기화가 실제로는 정상 완료되었는데도 “동기화 실패” 에러가 반복적으로 사용자에게 노출되는 문제를 발견했다. 현상 앱에서 다음과 같은 에러가 반복적으로 발생했다: AppException: Failed to push changes: AppException: Push completed with failures; retry count: 2, pending changes remain in queue. 서버 로그를 확인하면 동기화 pull은 정상 동작하고, 실제 데이터도 이미 동기화된 상태였다. 구조 파악: Transactional Outbox 패턴 앱의 동기화 구조는 다음과 같다: ┌────────────────┐ ┌──────────────┐ ┌────────────────┐ │ Local DB │────▶│ Sync Queue │────▶│ Remote API │ │ (Drift/SQLite)│ │ (Outbox) │ │ (Rails) │ └────────────────┘ └──────────────┘ └────────────────┘ 로컬에서 데이터 변경 → sync queue에 pending 아이템 추가 performFullSync() 호출 시 pull → push 순서로 동기화 push 단계에서 queue의 각 아이템을 서버에 전송 성공하면 queue에서 제거, 실패하면 retry count 증가 원인 분석 _pushChanges() 메서드의 에러 처리 로직에 문제가 있었다: ...

2025-10-04 · 4분 소요 · Seunghan
Flutter Sso Localhost Rails Uninitialized Constant Debug

Flutter SSO 로그인 실패 + Rails 서버 크래시 동시 디버깅 기록

TestFlight에서 소셜 로그인(Apple, Google)이 전부 실패하는 버그를 잡다가 서버도 크래시되고 있다는 걸 같이 발견했다. 각각 원인이 달랐고 둘 다 잡아야 앱이 정상 동작했다. 증상 실기기(TestFlight)에서 Apple 로그인, Google 로그인 버튼을 누르면 다음 에러가 표시됐다: Apple 로그인 실패: DioException [connection error]: The connection errored: Connection refused This indicates an error which most likely cannot be solved by the library. Error: SocketException: Connection refused (OS Error: Connection refused, errno = 61), address = localhost, port = 56837 Google 로그인 실패: DioException [connection error]: ... address = localhost, port = 56839 두 가지가 이상했다: ...

2025-10-01 · 3분 소요 · Seunghan
Flutter Rails Auth Session Persistence Debugging

Flutter + Rails 인증 세션이 계속 풀리는 문제 - 3가지 원인과 해결

Flutter BLoC 앱에서 로그인을 해도 세션이 자꾸 풀린다. 분명 SecureStorage에 토큰도 저장하고, Dio 인터셉터로 401 시 자동 갱신도 구현했는데 왜? 서버 로그부터 시작해서 원인 3개를 찾고 모두 고친 과정을 정리한다. 기술 스택 모바일: Flutter + BLoC 패턴 + Dio HTTP + SecureStorage 서버: Rails 8 API + ActionCable WebSocket 인증: SHA-256 digest 기반 access token + JTI refresh token (90일) 실시간: ActionCable WebSocket (토큰 기반 인증) 증상 로그인 직후는 정상 동작 시간이 지나면 API 요청이 401로 실패 토큰 갱신은 되는 것 같은데 WebSocket이 끊어짐 결국 앱이 미인증 상태로 전환 원인 1: 레거시 코드의 유령 - DTA 잔존 메서드 발견 서버 로그에서 토큰 갱신 시 user.tokens 관련 에러가 간헐적으로 보였다. 이전에 devise_token_auth(DTA)를 사용하다가 자체 토큰 시스템으로 마이그레이션했는데, token_refresh_service.rb에 DTA 시절 코드가 남아 있었다. ...

2025-09-27 · 4분 소요 · Seunghan
Flutter Glassappbar Tabbar Overflow Colors White Lightmode

Flutter UI 전수조사 — GlassAppBar TabBar overflow와 Colors.white 라이트모드 버그

Flutter 앱을 어느 정도 만들다 보면 꼭 한 번씩 마주치는 두 가지 버그가 있다. 하나는 bottom overflowed by N pixels 에러, 다른 하나는 라이트모드에서 텍스트가 배경에 묻혀 보이지 않는 현상이다. 둘 다 원인은 단순한데, 전체 화면을 대상으로 전수조사하기 전까지는 “일부 화면에서 이상하다” 수준으로만 인식하기 쉽다. 이번에 앱 전체 50개 페이지를 한 번 훑어보고 나서야 패턴이 보였다. GlassAppBar + TabBar overflow의 진짜 원인 커스텀 GlassAppBar를 만들어서 쓰고 있었다. bottom: TabBar(...) 를 붙이면 AppBar 아래에 탭이 생기는 구조다. ...

2025-09-24 · 3분 소요 · Seunghan
개인정보처리방침 문의