Flutter TestFlight 업로드 삽질 — exportArchive Failed to Use Accounts 해결

Flutter로 앱을 만들고 TestFlight에 올리려는데, 아카이브 빌드는 성공하고 IPA export에서 멈췄다. exportArchive Failed to Use Accounts라는 에러가 뜨는데, 구글링해도 명쾌한 답이 없었다. 결국 3가지 다른 에러를 연달아 만나면서 해결했고, 그 과정을 정리한다. 에러 1: exportArchive Failed to Use Accounts flutter build ipa --release --dart-define=ENV=prod --export-options-plist=ios/ExportOptions.plist 아카이브 빌드는 잘 된다: ✓ Built build/ios/archive/Runner.xcarchive (197.5MB) [✓] App Settings Validation • Version Number: 1.0.1 • Build Number: 9 • Display Name: My App • Bundle Identifier: com.example.app 근데 바로 다음 줄에서: ...

2026-03-31 00:00 · 4분 소요 · Seunghan

iOS 26 Liquid Glass 디자인 시스템을 웹에 적용하기 — Svelte 5 + CSS Custom Properties

WWDC 2025에서 Apple이 발표한 Liquid Glass는 iOS 7 이후 가장 큰 UI 변화였다. 반투명 유리 재질에 빛이 굴절되는 듯한 효과가 핵심인데, 이걸 실제 웹 프로젝트에 적용해봤다. Figma Community Kit에서 디자인 토큰을 추출하고, CSS Custom Properties로 변환한 뒤, Svelte 5 컴포넌트로 만들어서 Rails + Inertia.js 프로젝트의 실제 페이지에 붙이는 전 과정을 정리한다. 결론부터 말하면, iOS 26 디자인 시스템은 웹에서도 충분히 구현 가능하다. 다만 다크모드 셀렉터 불일치 같은 함정이 있어서, 기존 프로젝트에 얹을 때는 CSS 변수 네이밍 컨벤션을 꼼꼼히 맞춰야 한다. ...

2026-03-31 00:00 · 8분 소요 · Seunghan

Flutter에서 iOS Live Activity 잠금화면 미리보기 만들기 — CustomPainter로 네이티브 위젯 재현

왜 Live Activity 미리보기가 필요했나 여행 앱에서 iOS Live Activity를 구현하고 나면, 사용자에게 표시 모드를 선택하게 하는 설정 화면이 필요하다. 일정 모드, 예산 모드, 자동 모드, 결합 모드 — 이렇게 네 가지 옵션이 있는데, 문제는 이 모드를 바꿨을 때 잠금화면에서 실제로 어떻게 보이는지 사용자가 알 수 없다는 점이었다. 설정 화면 하단에 Dynamic Island compact 형태의 간단한 미리보기는 있었다. 하지만 사용자가 Live Activity를 가장 많이 보는 곳은 잠금화면이다. 작은 알약 모양 미리보기로는 “이 모드를 선택하면 잠금화면이 이렇게 바뀝니다"를 전달하기 어려웠다. ...

2026-03-26 00:00 · 8분 소요 · Seunghan

앱스토어 마케팅 스크린샷 자동화 — Puppeteer + features.json 분석 주도 파이프라인

앱 출시를 앞두고 스토어 스크린샷을 만들어야 하는 상황이 됐다. Figma로 하나씩 만드는 건 너무 비효율적이고, 10가지 디자인 시안에 5개 기능 화면을 조합하면 50장인데 수작업으로 한다는 건 말이 안 된다. 그래서 HTML/CSS로 마케팅 프레임을 만들고 Puppeteer로 PNG를 뽑아내는 파이프라인을 짰다. 처음엔 대충 만들었다가 구조적으로 틀린 부분이 있다는 걸 나중에 깨달았는데, 그 과정이 의외로 중요한 교훈을 남겼다. 처음 접근 방식의 문제 처음엔 이런 식으로 기능 배열을 하드코딩했다. const FEATURES = [ { id: 'expense', title: '지출을 한번에', sub: 'AI가 영수증을 읽어드립니다', screen: '02_expense_detail' }, { id: 'camera', title: '사진 찍으면 끝', sub: 'OCR로 즉시 기록', screen: '03_camera_hub' }, // ... ]; 문제는 이 파일명들(02_expense_detail.png, 03_camera_hub.png)이 실제로 존재하지 않았다는 것이다. 캡처 스크립트가 자동으로 화면을 이동하다가 실패해서 홈 화면만 5번 찍혔는데, 파일명은 다 달랐다. 결과적으로 모든 슬롯이 placeholder(검은 화면)였다. ...

2026-03-26 00:00 · 6분 소요 · Seunghan

Flutter GoRouter ShellRoute로 바텀 네비게이션 전역 유지하기 — 삽질부터 Liquid Glass 인터랙션까지

바텀 네비가 사라지는 순간 Flutter 앱을 만들다 보면 어느 순간 이런 상황을 만난다. 메인 화면에 탭 5개짜리 바텀 네비게이션 바가 있고, 할일 수정이나 새 메모 생성 버튼을 누르면 context.push('/tasks/new')로 화면을 전환한다. 그런데 화면이 전환되는 순간 바텀 네비가 통째로 사라진다. iOS 네이티브 앱에서는 탭 안에서 push 하면 탭 바가 유지된다. Apple의 메모 앱에서 메모를 열어도, 미리 알림에서 항목을 수정해도 하단 탭 바는 그대로 있다. 그런데 Flutter에서는 기본적으로 이렇게 동작하지 않는다. 원인을 찾아보니 라우터 구조 자체의 문제였다. ...

2026-03-24 00:00 · 9분 소요 · Seunghan

Hotwire Native iOS에서 삭제 버튼이 안 눌리는 이유 — WKUIDelegate와 turbo_confirm의 함정

삭제 버튼을 눌렀는데 아무 일도 일어나지 않는다 Rails + Hotwire로 웹앱을 만들고, Hotwire Native(구 Turbo Native)로 iOS 앱을 감싸서 배포하는 구조를 쓰고 있었다. 웹에서는 모든 것이 잘 동작했다. 삭제 버튼을 누르면 “정말 삭제하시겠습니까?” 확인 다이얼로그가 뜨고, 확인을 누르면 삭제가 진행됐다. 그런데 iOS 네이티브 앱에서 같은 버튼을 누르면 아무 반응이 없었다. 에러도 없고, 크래시도 없고, 그냥 조용히 무시됐다. 상태 변경 버튼, 라운드 추가/삭제 버튼, 토너먼트 삭제 버튼 — turbo_confirm이 붙은 모든 버튼이 죽어있었다. ...

2026-03-22 00:00 · 9분 소요 · Seunghan

iOS WebView 채팅 메시지가 박스를 뚫고 나간다 — overflow-wrap: anywhere로 해결한 크로스프로젝트 수정기

증상: 긴 메시지가 채팅 버블을 뚫고 나간다 Rails 8 + Hotwire Native로 만든 iOS 앱에서 채팅 기능을 테스트하던 중 문제를 발견했다. 긴 URL이나 공백 없는 연속 문자열을 보내면 메시지가 채팅 버블 영역을 벗어나 화면 밖으로 튀어나가는 현상이 발생했다. 웹 브라우저에서는 멀쩡하게 보이는데, iOS 네이티브 앱(WKWebView)에서만 문제가 재현됐다. 가로 스크롤이 생기고, 메시지 영역 전체 레이아웃이 깨져버린다. 처음엔 “Tailwind break-words 넣어놨는데 왜 안 되지?” 싶었지만, 파고 들어가보니 CSS overflow-wrap, flexbox intrinsic sizing, iOS WebKit 호환성이 복합적으로 얽힌 문제였다. ...

2026-03-22 00:00 · 9분 소요 · Seunghan

Turbo Native iOS에서 data-turbo-confirm이 동작하지 않는 이유 — WKUIDelegate 누락 문제

버튼을 눌러도 아무 일도 일어나지 않는다 Rails 8 + Hotwire Native으로 iOS 앱을 만들고 있었다. 웹에서는 잘 동작하는 삭제 버튼이 네이티브 앱에서는 완전히 먹통이었다. <%= button_to "삭제", tournament_path(@tournament), method: :delete, form: { data: { turbo_confirm: "정말 삭제하시겠습니까?" } } %> 웹 브라우저에서 클릭하면 “정말 삭제하시겠습니까?” 확인 다이얼로그가 뜨고, 확인하면 삭제가 진행된다. 그런데 iOS 앱에서는 버튼을 탭해도 아무 반응이 없다. 에러도 없고, 로그도 없고, 그냥 조용히 무시된다. 처음에는 turbo_confirm을 form: 옵션에 넣느냐 data: 옵션에 넣느냐의 문제인 줄 알았다. button_to의 turbo_confirm은 form 태그의 data 속성으로 전달해야 하기 때문이다. 하지만 코드는 정확했다. 웹에서 되는데 네이티브에서만 안 되니까 iOS 쪽 문제가 확실했다. ...

2026-03-22 00:00 · 10분 소요 · Seunghan

Hotwire Native + Rails 8 삽질 7가지 — 실기기에서만 터지는 버그들

Rails 8 + Hotwire Native으로 만든 모바일 앱의 대시보드 페이지를 Render에 배포한 뒤 실기기에서 점검하면서 만난 삽질 7가지를 정리했다. WKWebView 위에서 돌아가는 하이브리드 앱 특성상, 데스크톱 브라우저에서는 발견되지 않는 함정들이 많았다. 이 글에서 다루는 주요 키워드: Hotwire Native 모바일 레이아웃, Content Security Policy CDN 차단, Turbo const/let 재선언 에러, backdrop-filter 성능, Stimulus 컨트롤러 자동 등록, CSS contain 최적화. 프로젝트 환경 Backend: Rails 8 + PostgreSQL Frontend: Hotwire (Turbo + Stimulus) + ERB + Tailwind CSS 4 Mobile: Hotwire Native (iOS WKWebView) Realtime: ActionCable (WebSocket) Deploy: Render.com Asset Pipeline: importmap-rails (CDN pin 방식) 대시보드 페이지 구성: 코트 카드 grid (코트 수 x 라운드 수), 선수 DnD 리스트, 경기 목록, 교류 현황 통계. 코트 5개 x 8라운드 = 40장의 카드가 한 페이지에 렌더링되는 구조. ...

2026-03-21 00:00 · 8분 소요 · Seunghan

Rails 8 Hotwire 실전 삽질기 — DnD 배정, N+1 자동 감지, 테마별 Favicon

Rails 8 + Hotwire로 실시간 토너먼트 대시보드를 만들면서 하루 동안 겪은 3가지 삽질과 해결 과정. 이 문제들은 엣지 케이스가 아니라, Hotwire 프로그래밍 모델이 런타임에서 드러내는 자연스러운 마찰 지점들이다. 1. Turbo Stream + Stimulus DnD: DOM 교체 후 이벤트가 사라진다 문제 선수 칩을 코트 카드에 드래그하면 서버에 POST → Turbo Stream으로 코트 카드와 선수 목록을 교체하는 구조를 만들었다. 첫 번째 드래그는 잘 된다. 두 번째부터 아무 반응이 없다. 이벤트도, 요청도, 응답도 없다. ...

2026-03-21 00:00 · 9분 소요 · Seunghan

Hotwire Native iOS — 로그인 모달 충돌, Tailwind 4 사이드바, path config 삽질 기록

Hotwire Native iOS 앱에서 하루 동안 세 가지 버그를 잡았다. 각각 원인이 다르지만 공통점이 있다: 겉으로 보이는 증상과 실제 원인이 전혀 다른 곳에 있었다. 1. 로그인 페이지가 홈 탭에서만 보이는 문제 증상 4개 탭(홈, 과제, 알림, 마이)이 있는 앱에서, 비로그인 상태로 앱을 열면 홈 탭에서만 로그인 페이지가 뜨고, 나머지 탭을 누르면 빈 화면이나 에러가 표시된다. Rails 서버는 4개 탭 모두 /login으로 정상 리다이렉트하고 있었다. 원인: path-configuration의 context: "modal" Hotwire Native의 path-configuration에서 /login이 이렇게 설정되어 있었다: ...

2026-03-19 00:00 · 5분 소요 · Seunghan

Rails 8 + Hotwire Native iOS — 실시간 알림 뱃지 & 사이드 메뉴 네비게이션 구현

Rails 8 기반 Hotwire Native iOS 앱에서 두 가지 문제를 해결한 기록이다. 알림 뱃지 실시간 갱신 — 서버에서 알림이 생성되는 순간 앱 아이콘 뱃지와 내비게이션 벨 버튼을 즉시 업데이트 사이드 메뉴 네비게이션 누락 — tournament ID 같은 동적 파라미터가 필요한 URL을 사이드 메뉴에서 올바르게 이동 1. 문제 배경 알림 뱃지 APNs 푸시 알림의 badge 필드를 설정하지 않으면 iOS 앱 아이콘에 숫자가 표시되지 않는다. 또한 알림을 읽어도 뱃지가 초기화되지 않는 문제가 있었다. ...

2026-03-17 00:00 · 7분 소요 · Seunghan
Rails 8 Hotwire Native Production Checklist

Rails 8 + Hotwire Native iOS 실전 삽질 체크리스트 — 세션, CSRF, 채팅, 코트맵까지

Rails 8 + Hotwire Native로 iOS 앱을 만들면서 겪은 실전 이슈들을 정리했다. 공식 문서에 안 나오는 것들 위주로. 1. WKWebView 세션 쿠키가 앱 종료 시 날아간다 증상 앱을 완전히 종료(kill) 후 재실행하면 로그인이 풀린다. 원인 Rails 기본 cookie_store는 만료 시간이 없는 세션 쿠키를 생성한다. WKWebView는 앱 종료 시 세션 쿠키를 삭제할 수 있다. 해결 # config/initializers/session_store.rb Rails.application.config.session_store :cookie_store, key: "_app_session", expire_after: 30.days, same_site: :lax expire_after를 설정하면 영속 쿠키가 되어 앱 종료 후에도 유지된다. same_site: :strict는 절대 사용하지 말 것. WKWebView에서 쿠키 전송이 안 된다. ...

2026-03-17 00:00 · 5분 소요 · Seunghan

Rails 8 + Hotwire Native 앱의 역할 기반 UI 분리와 모바일 최적화 삽질기

Rails 8 + Hotwire Native 조합으로 iOS 앱을 운영하는 중에, 하루 동안 발생한 여러 문제를 연쇄적으로 해결한 기록이다. 작은 UI 깨짐에서 시작해서 권한 체계 재설계까지 이어진 과정을 정리한다. Hotwire Native의 핵심 매력은 하나의 Rails 앱으로 웹과 네이티브 iOS/Android를 동시에 지원한다는 점이다. 하지만 이 구조는 “웹에서 잘 보이면 앱에서도 잘 보인다"는 착각을 쉽게 심어준다. 실제로는 WKWebView의 렌더링 환경, 네이티브 네비게이션 바의 존재, 역할별 UI 분기 등 웹 브라우저와 전혀 다른 고려사항이 따라온다. ...

2026-03-17 00:00 · 7분 소요 · Seunghan
Rails Stimulus DnD Mentor Board Troubleshooting

Rails + Stimulus 드래그앤드롭 멘토 배정 보드에서 만난 삽질 5가지

Rails 8 앱에서 멘토-팀 배정을 드래그앤드롭으로 관리하는 보드를 만들었다. Stimulus 컨트롤러 + fetch + 서버 사이드 HTML 교체 방식이었는데, “되는 줄 알았던” 기능들이 프로덕션에서 하나씩 터졌다. 1. Stimulus 컨트롤러가 아예 로드 안 됨 증상 data-controller="mentor-assignment-board"를 붙였는데 드래그가 안 먹는다. 브라우저 콘솔에 에러도 없다. 원인 importmap-rails를 쓰는 프로젝트에서 한 번이라도 rails assets:precompile을 실행하면 public/assets/ 디렉토리가 생긴다. 이후 개발 환경에서도 Rails는 이 정적 파일을 우선 서빙한다. 문제는 precompile 시점에 존재하지 않았던 Stimulus 컨트롤러들이 public/assets/에 없다는 것. Rails가 public/assets/를 먼저 보기 때문에, app/javascript/controllers/에 있는 새 파일을 무시한다. ...

2026-03-12 00:00 · 4분 소요 · Seunghan

Rails 깜짝 과제 기능 + 1회성 알림 배너 — 기존 모델 재활용과 localStorage 활용

스터디를 운영하다 보면 세션 중간에 즉석으로 과제를 내야 할 때가 있다. 기존 관리자 페이지를 통하면 여러 단계를 거쳐야 하고, 멘티들은 새 과제가 생긴 걸 바로 알 수 없다는 문제가 있었다. 이 글에서는 새 모델 없이 기존 시스템을 재활용하여 깜짝 과제 기능을 만들고, 1회성 알림 배너로 멘티에게 즉시 알려주는 구현 과정을 정리한다. 문제 정의 과제 생성이 느리다: 관리자 대시보드에서 여러 필드를 채워야 한다 멘티가 모른다: 새 과제가 생겨도 목록을 직접 확인하기 전까지 알 수 없다 1회성이어야 한다: 알림을 본 뒤에는 다시 보여주지 않아야 한다 설계 결정: 새 모델 vs 기존 모델 재활용 처음에는 QuickAssignment나 Notification 같은 새 모델을 만들 수 있었지만, 분석해보니 기존 구조로 충분했다. ...

2026-03-12 00:00 · 5분 소요 · Seunghan

App Store 스크린샷 리젝 2.3.3 해결기 — AI 생성 이미지에서 실제 앱 캡처로

App Store에 첫 앱을 제출했는데, 스크린샷 문제로 리젝당했다. 해결까지의 삽질 기록. 리젝 사유 Guideline 2.3.3 - Performance: Accurate Metadata The screenshots do not show the actual app in use in the majority of the screenshots. 심사 디바이스: iPad Air 11-inch (M3) 원인 Gemini Image Generation API로 Neo-Brutalism 스타일의 가짜 UI 마케팅 이미지를 만들어서 스크린샷으로 제출했다. 앱 화면과 전혀 다른 디자인이었으니 당연한 결과. Apple이 요구하는 건: 대다수(majority) 스크린샷이 실제 앱 사용 화면이어야 함 마케팅/프로모션 자료만으로는 부적절 스플래시/로그인 화면만으로도 부족 다만, 실제 앱 화면 + 텍스트 오버레이 조합은 허용된다. 대부분의 앱이 이 방식을 쓴다. ...

2026-03-11 00:00 · 4분 소요 · Seunghan

Chrome 확장 개발 삽질 모음 — 도메인 화이트리스트, 이벤트 리스너 중복, 클로저 함정

Chrome 확장을 유지보수하다 보면 “분명히 동작해야 하는데 왜 안 되지?“라는 상황을 꽤 자주 만난다. 이번에 짧은 시간 안에 4가지 실수를 연달아 저질렀고, 각각 원인이 달랐다. 기록해둔다. 1. 디스패치 블록의 return이 범용 감지를 막는다 Content script 끝부분에는 보통 이런 패턴이 있다. if (isSomeSpecificPage()) { doSomethingSpecific(); return; // ← 여기서 끝 } // 범용 DOM 감지 (MutationObserver 등) const observer = new MutationObserver(() => { ... }); observer.observe(document.body, { childList: true, subtree: true }); 특정 도메인에서만 동작하는 기능을 추가하면서 return으로 빠져나왔더니, 그 도메인의 팝업 창에서 범용 DOM 감지가 아예 실행되지 않았다. ...

2026-03-09 00:00 · 4분 소요 · Seunghan

Flutter iOS TestFlight 업로드 실패: objective_c.framework 시뮬레이터 슬라이스 오류

Flutter 앱을 flutter build ipa --release로 빌드하고 TestFlight에 업로드했더니 altool이 거절했다. 원인, 삽질 과정, Makefile 자동화까지 정리한다. 오류 메시지 UPLOAD FAILED with 3 errors Invalid executable. The "Runner.app/Frameworks/objective_c.framework/objective_c" executable references an unsupported platform in the x86_64 slice. Simulator platforms aren't permitted. Invalid executable. The "Runner.app/Frameworks/objective_c.framework/objective_c" executable references an unsupported platform in the arm64 slice. Simulator platforms aren't permitted. Unsupported Architectures. The executable for Runner.app/Frameworks/objective_c.framework contains unsupported architectures '[x86_64]'. flutter build ipa는 성공했고 IPA 파일도 정상 생성됐다. 문제는 빌드 단계가 아니라 업로드 단계에서 발생했다. 즉, Xcode 설정이나 Flutter 프로젝트 구성 자체의 문제가 아니라 Flutter가 조용히 임베드하는 서드파티 프레임워크 바이너리 안에 문제가 있다는 뜻이다. ...

2026-03-09 00:00 · 6분 소요 · Seunghan

Flutter 앱에 iOS 위젯 추가하기 — pbxproj 수동 편집부터 딥링크까지

Flutter 앱에 iOS 홈 화면 위젯을 붙이는 작업을 했다. 처음엔 단순해 보였는데 생각보다 손댈 곳이 많았다. Xcode GUI를 쓰면 간단하지만 CLI 환경에서 project.pbxproj를 직접 수정해야 하는 경우를 위해 전 과정을 정리한다. 목표 홈 화면 위젯 2종: 2×2(systemSmall), 2×1(systemMedium) 위젯에서 앱의 미처리 항목 수를 실시간으로 표시 위젯 버튼 탭 → 앱 특정 화면으로 이동 (딥링크) 1. 위젯 익스텐션 파일 구성 ios/ ├── Runner/ │ ├── AppDelegate.swift │ ├── Info.plist │ └── Runner.entitlements └── ReceiptWidget/ ← 새로 추가 ├── ReceiptWidget.swift ├── Info.plist └── ReceiptWidget.entitlements ReceiptWidget.swift 하나에 Provider, View, Widget, Bundle을 모두 담았다. 사이즈별로 View를 분리하고 @Environment(\.widgetFamily)로 분기하는 패턴이 깔끔하다. ...

2026-03-08 00:00 · 5분 소요 · Seunghan

iOS 앱 배포 막히는 순간들 — Bundle ID 이전 불가·ITMS-90683·AI 아이콘 생성까지

Flutter 앱을 TestFlight에 올리는 과정에서 겪은 삽질들을 기록한다. Apple Developer 계정 전환, Bundle ID 등록, 권한 누락 에러, 그리고 AI로 아이콘과 스크린샷을 자동 생성하는 방법까지. 1. Apple Developer 계정이 다를 때 — Bundle ID 이전은 불가 앱을 A 계정(Team A)에서 개발하다가 B 계정(Team B)으로 배포하려고 했다. 기존 Bundle ID가 A 계정에 이미 등록되어 있어서 B 계정으로 등록하려 하면 409 Conflict 에러가 난다. { "errors": [{ "status": "409", "code": "ENTITY_ERROR.ATTRIBUTE.INVALID", "detail": "An App ID with Identifier 'com.xxx.yyy' is not available." }] } Bundle ID는 계정 간 이전이 불가능하다. 해결책은 두 가지다: ...

2026-03-08 00:00 · 4분 소요 · Seunghan

Rails 앱을 Hotwire Native로 iOS 앱 만들어 TestFlight 올리기까지의 삽질 기록

Rails 8로 만든 긴급 신고 웹앱 바로신고를 Hotwire Native으로 iOS 앱으로 감싸서 TestFlight에 올리기까지의 과정을 정리합니다. 기술 스택 Backend: Rails 8 + Turbo iOS: Hotwire Native 1.2.2 + XcodeGen 빌드: Makefile 자동화 프로젝트 구조 ios/ ├── project.yml # XcodeGen 설정 ├── ExportOptions.plist # App Store 내보내기 ├── Makefile # 빌드 자동화 └── BaroSingo/ ├── AppDelegate.swift ├── SceneController.swift ├── AppTab.swift ├── Bridge/ │ ├── FormComponent.swift │ ├── HapticComponent.swift │ └── ShareComponent.swift └── Resources/ ├── Assets.xcassets/ └── path-configuration.json 삽질 1: Hotwire Native API 변경 Hotwire.config.userAgent — 읽기 전용 // ❌ 컴파일 에러: 'userAgent' is a get-only property Hotwire.config.userAgent = "BaroSingo iOS" // ✅ 해결: makeCustomWebView 사용 Hotwire.config.makeCustomWebView = { configuration in let webView = WKWebView(frame: .zero, configuration: configuration) webView.customUserAgent = "BaroSingo iOS/1.0 Turbo Native" return webView } Hotwire.loadPathConfiguration — 존재하지 않는 API // ❌ 컴파일 에러: no member 'loadPathConfiguration' Hotwire.loadPathConfiguration(from: [source]) // ✅ 해결: config.pathConfiguration.sources 직접 설정 Hotwire.config.pathConfiguration.sources = [ .file(Bundle.main.url(forResource: "path-configuration", withExtension: "json")!), .server(URL(string: "\(baseURL)/api/hotwire/path-configuration")!) ] Bridge Component에서 ViewController 접근 // ❌ 컴파일 에러: optional type must be unwrapped delegate.webView?.findViewController() // ✅ 해결: delegate?.destination 사용 guard let viewController = delegate?.destination as? UIViewController else { return } 교훈: Hotwire Native는 버전별 API 변경이 잦다. 공식 소스코드와 실제 동작하는 프로젝트를 참고하는 게 가장 확실하다. ...

2026-03-07 00:00 · 4분 소요 · Seunghan
Rails Sso Universal Links Hotwire Native

Rails 두 앱 사이에 SSO 구현하기 — 세션 유실·리다이렉트 루프·Universal Links 삽질까지

두 개의 Rails 8 서비스가 있다. 하나는 메인 앱(IdP 역할), 다른 하나는 연동 서비스(RP 역할). 연동 서비스 로그인 페이지에 “메인 앱으로 로그인” 버튼을 넣고, SSO로 인증 후 돌아오는 플로우를 구현했다. 거기에 iOS Hotwire Native 앱이 설치돼 있으면, 브라우저 대신 네이티브 앱에서 인증이 진행되도록 Universal Links까지 붙였다. OAuth 2.0 같은 표준 프로토콜을 쓰지 않고 직접 구현한 이유는 두 서비스 모두 직접 운영하는 내부 시스템이고, 외부 IdP(Auth0, Cognito 등)를 붙이기에는 오버엔지니어링이었기 때문이다. 핵심 개념(토큰 발급, 검증, 세션 관리)은 표준과 동일하다. ...

2026-02-17 00:00 · 9분 소요 · Seunghan
Rails Flutter Iap Unimplemented Features Audit

Rails + Flutter 앱 미구현 항목 점검 및 인앱 결제(IAP) 연동 기록

음성 메시지 기반 소셜 앱을 출시 준비하면서 미구현 항목을 전수 점검했다. route는 있는데 controller action이 없거나, Flutter UI는 완성됐는데 결제 로직이 // TODO 로 막혀 있는 경우들이 꽤 있었다. 정리하고 하나씩 구현한 기록. 미구현 항목 점검 방법 백엔드 점검 가장 빠른 방법은 routes.rb와 실제 controller를 비교하는 것이다. bundle exec rails routes | grep -v "^ #" route가 있는데 controller에 해당 action이 없으면 런타임에 ActionController::MethodNotImplemented 에러가 난다. 미리 찾아내는 게 낫다. 프론트엔드 점검 Flutter는 // TODO, SnackBar(content: Text('기능 준비 중')) 패턴을 검색하면 빠르다. ...

2026-01-30 00:00 · 5분 소요 · Seunghan
Hotwire Native Ios Tab Bar Patterns

Hotwire Native iOS 탭바 앱 구축 — HotwireTabBarController 적용기와 삽질 모음

Rails 앱을 Hotwire Native로 래핑할 때 단일 Navigator 대신 HotwireTabBarController 패턴으로 전환하면서 생긴 문제들을 정리한다. 시뮬레이터에서는 안 보이던 버그가 TestFlight에서 터지고, 로컬 개발 환경 설정이 꼬이는 등 여러 지점에서 시간을 날렸다. 1. HotwireTabBarController 기본 구조 단일 Navigator 대신 탭별로 독립적인 Navigator와 WKWebView를 갖는 구조다. // AppTab.swift enum AppTab: String, CaseIterable { case home, ai, request var systemImage: String { switch self { case .home: return "house" case .ai: return "message" case .request: return "checkmark.circle" } } var selectedSystemImage: String { switch self { case .home: return "house.fill" case .ai: return "message.fill" case .request: return "checkmark.circle.fill" } } var url: URL { let base = AppDelegate.baseURL switch self { case .home: return base.appendingPathComponent("dashboard") case .ai: return base.appendingPathComponent("conversations") case .request: return base.appendingPathComponent("service_requests") } } var hotwireTab: HotwireTab { HotwireTab( title: "", image: UIImage(systemName: systemImage)!, selectedImage: UIImage(systemName: selectedSystemImage)!, url: url ) } } // SceneController.swift 핵심 부분 private lazy var tabBarController: HotwireTabBarController = { let controller = HotwireTabBarController(navigatorDelegate: self) controller.load(AppTab.allCases.map(\.hotwireTab)) // 탭 아이콘만 표시, 텍스트 제거 controller.viewControllers?.forEach { vc in vc.tabBarItem.title = nil vc.tabBarItem.imageInsets = UIEdgeInsets(top: 6, left: 0, bottom: -6, right: 0) (vc as? UINavigationController)?.delegate = self } return controller }() 탭 제목을 없애고 아이콘만 남기려면 tabBarItem.title = nil과 imageInsets 조정이 같이 필요하다. title만 nil로 하면 아이콘 위치가 내려가지 않아서 어색하게 보인다. ...

2025-12-26 00:00 · 5분 소요 · Seunghan
Hotwire Native Webview 8 Fixes

Hotwire Native WebView 삽질 모음 — 네이티브 앱에 Rails WebView 래핑할 때 자주 겪는 8가지 문제

Rails 앱을 Hotwire Native(Turbo Native)로 래핑해서 iOS/Android 네이티브 앱을 만들다 보면, 브라우저에서는 멀쩡한데 WebView에서만 이상하게 동작하는 것들이 꽤 많다. 실제로 작업하면서 겪은 문제와 적용한 수정을 한 곳에 정리해 둔다. 대부분 CSS 몇 줄 또는 path configuration JSON 한 줄로 끝난다. 1. 더블탭 줌 / 300ms 클릭 딜레이 증상 버튼을 빠르게 두 번 탭하면 화면이 확대된다. 단순 탭에도 눌렸다는 느낌이 살짝 늦다 (약 300ms). 원인 iOS WKWebView는 더블탭 줌 제스처를 감지하기 위해 첫 번째 탭 이벤트를 ~300ms 동안 잡아둔다. user-scalable=yes(viewport 기본값) 상태에서는 핀치 줌과 더블탭 줌이 활성화되어 있다. ...

2025-11-25 00:00 · 4분 소요 · Seunghan
Rails Stimulus Controllers Lookbook Debug

Rails + Stimulus 컨트롤러 11개 구현기: 스크롤·캐러셀·텍스트 애니메이션

Rails + ViewComponent + Lookbook 조합으로 컴포넌트 라이브러리를 만들 때, Stimulus 컨트롤러가 전부 스텁(빈 껍데기) 상태로 남아있는 상황을 맞닥뜨렸다. 13개 컨트롤러 중 3개만 동작하고 나머지 10개는 connect() {} 한 줄짜리였다. 이걸 전부 구현하면서 겪은 삽질을 정리한다. 이 글은 단순히 코드를 붙여넣는 게 아니라, 각 컨트롤러를 구현하면서 왜 그런 방식을 선택했는지, 어떤 문제가 발생했는지, 그리고 어떻게 해결했는지에 초점을 맞춘다. 구현 대상 총 11개 컨트롤러를 4단계로 나눠서 구현했다. 복잡도와 의존성을 기준으로 순서를 정했다. DOM 직접 조작 → 스크롤 연동 → RAF 애니메이션 → 인터랙티브 캐러셀 순서로 진행하면 각 단계에서 배운 패턴이 다음 단계에 자연스럽게 이어진다. ...

2025-11-18 00:00 · 11분 소요 · Seunghan
Flutter Ipa No Codesign Api Key Testflight

flutter build ipa가 갑자기 실패한다면 — Development 인증서 없이 TestFlight 배포하는 법

Flutter iOS 앱을 여러 Apple 계정으로 관리하다 보면 한 프로젝트에서는 make testflight가 잘 되는데 다른 프로젝트에서는 동일한 Makefile이 실패하는 상황이 생긴다. 오늘 겪은 케이스를 정리한다. 증상 ❌ Error (Xcode): No signing certificate "iOS Development" found: No "iOS Development" signing certificate matching team ID "XXXXXXXX" with a private key was found. flutter build ipa 실행 시 위 오류로 실패한다. Distribution 인증서는 키체인에 있는데 Development 인증서가 없다는 메시지다. 원인: flutter build ipa 내부에서 일어나는 일 flutter build ipa는 내부적으로 다음 순서로 동작한다. ...

2025-11-04 00:00 · 4분 소요 · Seunghan
Flutter Ios Build Dark Mode Logout Debugging

TestFlight 올리려다 터진 Flutter iOS 빌드 오류 5종 — 다크모드·로그아웃 버그까지 한 번에

빌드를 올리려는데 한꺼번에 여러 문제가 터졌다. 코드 생성기가 실패하고, 없어진 파일이 있고, 빌드 번호 규칙을 몰라서 거절당하고, UI는 다크모드가 하드코딩되어 있고, 로그아웃은 토큰을 안 지웠다. 하나씩 정리한다. 1. Retrofit 옵션 파라미터 문법 오류 → .g.dart 생성 실패 증상 dart run build_runner build 실행 시 일부 API 서비스 파일에서: Expected to find ')' 원인 Retrofit의 추상 메서드에서 옵션 파라미터({}) 위치를 잘못 씀. // ❌ 잘못된 문법 — 닫는 중괄호 뒤에 쉼표 Future<Response> getItems( @Path('id') String id, {@Query('type') String? type}, // ← 이렇게 쓰면 안 됨 ); // ✅ 올바른 문법 — 포지셔널 파라미터 뒤에 { 바로 열기 Future<Response> getItems( @Path('id') String id, { @Query('type') String? type, }); Dart 문법에서 옵션 파라미터는 마지막 포지셔널 파라미터 바로 뒤에 {를 열어야 한다. },로 닫은 뒤 쉼표를 찍으면 파서가 다음 인자로 인식하려다 실패한다. ...

2025-11-01 00:00 · 5분 소요 · Seunghan
Chrome Extension Insurance Autofill Playwright Gif

크롬 확장 content script — 한국 보험사 자동입력, HTML 목업 스크린샷, MOV→GIF

브라우저 확장 프로그램에서 form 자동입력 기능을 확장하면서 삽질한 내용들을 정리한다. 1. 다이렉트 자동차보험 사이트 content script 자동입력 문제: JS 렌더링 사이트는 WebFetch로 form 구조를 못 읽는다 한국 보험사 다이렉트 사이트들은 대부분 SPA/RIA 구조다. 삼성화재: SFMI 자체 RIA 프레임워크 현대해상, DB손보: Spring MVC .do URL 패턴 KB손보, 메리츠: 모바일/PC 별도 도메인 WebFetch로 URL을 긁어봤자 form 필드 구조가 나오지 않는다. 직접 접속해서 DevTools로 확인하거나, 업계 공통 패턴으로 커버하는 방법 중 후자를 선택했다. ...

2025-10-28 00:00 · 5분 소요 · Seunghan
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 로그인이 잘 되고 있어서 비교 분석했다. 결론부터 말하면, 세 가지 독립적인 버그가 동시에 존재했고, 모두 “Google SSO 코드를 복붙해서 Apple SSO를 만든” 과정에서 생겨났다. 증상 Apple 로그인: 403 Forbidden Google 로그인: 정상 성공 에러 메시지: "Email not verified by Apple" 개발 환경에서는 재현되지 않고 프로덕션에서만 발생 (Apple 테스트 계정 이슈) 배경: Apple과 Google JWT는 다르다 OAuth 2.0 / OIDC 표준은 email_verified 필드가 boolean이어야 한다고 명시하고 있다. 하지만 현실에서 Apple은 이 필드를 문자열 "true"로 반환하는 경우가 있다. 이건 Apple의 공식 문서에도 명확히 나와 있지 않은 엣지 케이스다. ...

2025-10-25 00:00 · 6분 소요 · Seunghan
Ios Sso Entitlements Testflight Errors

iOS TestFlight 배포 삽질 모음: SSO 에러부터 entitlements mismatch까지

Flutter 앱 여러 개를 TestFlight에 올리면서 반복적으로 마주친 에러들을 정리했다. 1. Apple Sign-In 에러 1000 SignInWithAppleAuthorizationException(AuthorizationErrorCode.unknown, The operation couldn't be completed. (com.apple.AuthenticationServices.AuthorizationError error 1000.)) 원인 Runner.entitlements에 Sign in with Apple capability가 없어서 발생한다. 해결 두 곳 모두 설정해야 한다. ① ios/Runner/Runner.entitlements <key>com.apple.developer.applesignin</key> <array> <string>Default</string> </array> ② Apple Developer Console developer.apple.com → Identifiers → 앱 Bundle ID 선택 → Sign in with Apple 체크 → Save 프로비저닝 프로파일이 이미 있다면 재생성이 필요하다. ...

2025-08-30 00:00 · 4분 소요 · Seunghan
Flutter Dead Ui Fix Xcode26 Widget Bug

Flutter 미구현 UI 컴포넌트 연결 + Xcode 26 베타 WidgetKit 설치 버그 우회

Flutter 앱 작업 중 두 가지 문제를 연달아 처리했다. 하나는 UI 차원의 문제 — onTap: () {} 로 껍데기만 있는 컴포넌트들을 실제로 연결하는 작업. 다른 하나는 Xcode 26.2 베타에서 시뮬레이터에 앱을 설치하면 익스텐션 때문에 앱 자체가 설치되지 않는 문제다. 1. 동작하지 않는 UI 컴포넌트 연결 Flutter 개발 중 흔히 발생하는 상황: 화면은 다 만들어졌는데 버튼에 onPressed: () {}, 카드에 onTap: () {}만 달려 있고 실제 동작이 없는 상태. 패턴별 정리 알림 벨 아이콘 ...

2025-07-16 00:00 · 4분 소요 · Seunghan
Ios Codesign Testflight Full Setup

iOS 코드 서명 처음부터 끝까지 — Distribution Cert에서 TestFlight 업로드까지 수동 세팅

Flutter 앱을 TestFlight에 올리는 과정에서 코드 서명 관련 설정을 처음부터 다시 잡으면서 정리한 내용이다. Xcode 자동 서명이 아닌 수동 + App Store Connect API Key 방식으로 진행했다. 전체 흐름 [1] Distribution Certificate 발급 [2] APNs Certificate 발급 (CSR 생성 필요) [3] App ID에 Push Notifications 활성화 [4] Provisioning Profile 생성 (App Store, Push 포함) [5] xcodebuild archive + export (API Key 인증) [6] xcrun altool로 TestFlight 업로드 1. Distribution Certificate Apple Developer → Certificates → + → Apple Distribution 선택. ...

2025-06-18 00:00 · 4분 소요 · Seunghan
개인정보처리방침 이용약관 면책조항 문의