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

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

2026-03-22 · 9분 소요 · 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 · 8분 소요 · Seunghan

fetch() + PATCH + 302 Redirect = 보이지 않는 버그

Stimulus 컨트롤러에서 badge 선택 UI를 만들었다. 옵션을 클릭하면 fetch()로 PATCH를 보내고, 서버가 업데이트한 뒤 성공/실패를 표시하는 단순한 구조다. 그런데 DB는 업데이트되는데 UI가 실패 표시를 하면서 원래 값으로 되돌아갔다. 서버 로그를 열기 전까지는 원인을 전혀 짐작할 수 없었다. 증상 badge를 클릭하면: 잠깐 선택 스타일이 바뀜 곧바로 원래 값으로 revert 에러 인디케이터(X) 표시 다른 필드(모드, 대진표 유형)는 정상 동작하는데, 특정 필드만 실패했다. 모델 validation 문제도 아니고, 권한 문제도 아니었다. 서버 로그에서 본 진짜 원인 Started PATCH "/resources/54" for ::1 Processing by ResourcesController#update as TURBO_STREAM Parameters: {"resource"=>{"field_name"=>"new_value"}, "id"=>"54"} ... UPDATE "resources" SET "field_name" = 1 WHERE "id" = 54 COMMIT Redirected to http://localhost:3000/resources/54/dashboard Completed 302 Found in 22ms Started PATCH "/resources/54/dashboard" for ::1 ActionController::RoutingError (No route matches [PATCH] "/resources/54/dashboard"): DB 업데이트는 성공했다. 그런데 서버가 redirect_to dashboard로 302를 보냈고, fetch가 그 redirect를 따라가면서 PATCH method를 유지한 채 dashboard URL로 요청을 보냈다. Dashboard는 GET만 받으므로 RoutingError가 터졌다. ...

2026-03-20 · 5분 소요 · Seunghan
Bracket FAB Audit Log Rails

대진표에 FAB 피드백 버튼, 수정 권한 체계, Audit Log 붙이기 — Rails 8 삽질 기록

한 번에 세 가지 기능을 동시에 설계하다 보면 서로 얽히는 부분이 생긴다. 이번에는 대진표 관리 앱에 다음을 추가했다. FAB 피드백 버튼 — 우측 하단 플로팅 버튼 → Telegram 전송 역할 기반 대진표 수정 권한 — 대회 vs 친선 모드에 따라 일반 참가자에게 수정 권한 부여 여부 선택 Audit Log — 누가 언제 무엇을 바꿨는지 전/후 데이터와 함께 기록 각각은 단순해 보이지만, 셋을 한꺼번에 설계하다 보니 “어디서 권한을 체크하고, 어디서 로그를 남기고, 어디까지 UI에 노출하는가"에 대한 결정이 계속 붙었다. ...

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

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

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

2026-03-12 · 5분 소요 · Seunghan
Rails Turbo Actioncable 500 Debug

Rails Turbo Stream 500 에러 3종 세트 디버깅 — broadcast, SolidCable, Telegram Markdown

Rails 8 + Hotwire(Turbo) 기반 앱을 운영하다 보면 broadcast_append_to 계열 콜백이 조용히 500을 내뱉는 경우가 있다. 거기에 SolidCable 초기 설정 문제와 Telegram Bot 메시지 파싱 오류가 겹치면 로그 해석도 헷갈린다. 이번에 세 가지가 한꺼번에 터져서 순서대로 해결한 과정을 정리한다. 이 글에서 다루는 세 문제는 서로 독립적이지만, 실제 운영 환경에서는 이렇게 한꺼번에 맞닥뜨리는 경우가 많다. 각 문제를 격리해서 하나씩 해결하는 접근이 중요하다. 문제 1: No unique index found for id — broadcast 콜백 500 현상 메시지나 알림을 생성할 때 컨트롤러에서 500이 발생한다. 로그를 보면: ...

2026-01-09 · 7분 소요 · Seunghan
개인정보처리방침 이용약관 면책조항 문의