Seunghan

👋 안녕하세요, AI-Native Engineer 승한입니다.

AI를 활용해 모바일 앱과 웹 서비스를 빠르게 만들고 있습니다. 사용자 경험을 개선하고 실생활 문제를 해결하는 것에 관심이 있습니다.

LLM이 지어낸 법령을 DB로 걸러내기 — 한국어 법률 인용 환각 방지 실전

법률 AI 서비스를 만들다 보면 이런 순간이 온다. LLM이 자신감 있게 “민법 제103조의2에 따라…” 라고 답변을 줬는데, 확인해보니 제103조의2라는 조문은 존재하지 않는다. 본조인 제103조만 있고 가지조문은 만들어진 것이다. 이게 얼마나 심각한 일인지는 이미 유명한 사건이 증명했다. 2023년 미국 Mata v. Avianca 소송에서 뉴욕의 한 변호사가 ChatGPT가 생성한 판례를 법원 제출서류에 인용했다가 제재를 받았다. 판례가 전부 지어낸 것이었던 거다. 법률 도메인에서 AI 환각은 그냥 버그가 아니라 법률 책임 문제로 번진다. 이번 작업에서 내가 만든 서비스도 같은 위험에 노출돼 있었다. 사용자가 법령 개정 diff를 보면서 “이 개정이 우리 회사에 어떤 영향?” 같은 후속 질문을 하면, LLM이 답변을 돌려주면서 근거 조문을 인용한다. 그 인용이 진짜인지 아닌지를 사용자에게 떠넘길 수는 없었다. ...

2026-04-09 · 10분 소요 · Seunghan

AI로 디자인 시스템 마이그레이션했는데 사실 CSS 리스킨이었다 — Token/Component/Template/Page 4-layer 재설계 회고

Rails 8 + Hotwire 프로젝트에 iOS 26 Liquid Glass 디자인 시스템을 전면 도입했다. 7주간 39개 페이지를 마이그레이션했고, 디자인 가이드 위반 417건이 0건이 됐다. 18개 파일로 구성된 마이그레이션 설계서도 있었고, 단계별(Phase 1-7) 체크리스트도 있었다. 스스로 만족했다. 그 다음에 사용자가 한마디 했다. “토큰/컴포넌트/템플릿/페이지 형태로 반영이되어야하는데 디자인시스템부터 점검해” 그 문장 하나로 전부 무너졌다. 점검해보니 내가 한 건 디자인 시스템이 아니라 CSS 리스킨이었다. 이 글은 그 깨달음과 재설계 과정의 기록이다. AI 코딩의 함정 — 페이지마다 “시스템처럼” 보이게 만들기 처음에는 단계별로 잘 진행했다고 생각했다. 토큰 파일을 만들었고(iOS 26 79개 컬러 × 4모드), 6개 공통 ERB 파셜을 만들었고(toolbar, list_row, button 등), 각 페이지마다 전용 CSS 파일을 분리했다. 39개 페이지가 모두 새 디자인으로 바뀌었고, grep으로 위반을 측정했더니 0건이었다. ...

2026-04-08 · 10분 소요 · Seunghan

프로필 페이지 발표자료 핀 시스템 — Instagram 스타일 +N 오버레이 카드 만들기

공개 프로필 페이지를 만들고 있었다. link-in-bio 스타일로, /@username 경로에서 사용자의 소개, 링크, 발표자료를 보여주는 페이지다. 발표자료가 9개 올라가 있었는데, 전부 나열하니까 프로필이 포트폴리오 사이트처럼 변해버렸다. 스크롤이 길어지고, 정작 중요한 링크들이 묻혔다. 사용자가 원하는 3개만 “핀"해서 보여주고, 나머지는 별도 페이지로 유도하는 게 맞았다. 그런데 “더보기"를 어떻게 보여줄지가 문제였다. 별도 버튼? 빈 카드? 결국 Instagram 앨범처럼 마지막 썸네일 위에 반투명 오버레이를 올리는 방식으로 갔다. 이 글은 그 과정의 기록이다. 기존 구조: 전부 보여주기 처음 구현은 단순했다. 컨트롤러에서 published.on_profile 스코프로 가져온 발표자료를 전부 넘기고, 프론트에서 2열 그리드로 렌더링했다. ...

2026-04-05 · 7분 소요 · Seunghan

Inertia.js v2→v3 마이그레이션 — Svelte 5 + Rails 8 실전 삽질 기록

Rails 8 + Svelte 5 프로젝트에서 @inertiajs/svelte를 v2에서 v3로 올렸다. “패키지 버전만 올리면 되겠지"라는 안일한 생각으로 시작했다가 반나절을 날렸다. 이 글은 그 삽질의 기록이다. 왜 업그레이드해야 했나 프로젝트에서 Svelte 5를 쓰고 있었는데, @inertiajs/svelte v2는 Svelte 5를 “대충” 지원했다. 문제는 persistent layout이었다. Svelte 5는 컴포넌트를 함수로 컴파일하는데, Inertia v2는 page.default.layout = AppLayout 처럼 클래스 기반 컴포넌트에 속성을 추가하는 방식을 썼다. Svelte 5에서는 이게 작동하지 않았다. 결과적으로 40개 넘는 페이지에 <AppLayout>을 수동으로 감싸야 했다. 유지보수 악몽이었다. ...

2026-04-04 · 7분 소요 · Seunghan

pnpm ERR_PNPM_OUTDATED_LOCKFILE — CI 배포 실패 진단과 해결

Render에서 배포가 터졌다. 에러 메시지는 짧고 명확했지만 원인은 생각보다 다양했다. ERR_PNPM_OUTDATED_LOCKFILE Cannot install with "frozen-lockfile" because pnpm-lock.yaml is not up to date with <ROOT>/apps/legal_audit_web/package.json Note that in CI environments this setting is true by default. If you still need to run install in such cases, use "pnpm install --no-frozen-lockfile" Failure reason: specifiers in the lockfile don't match specifiers in package.json: * 1 dependencies were added: @ios26_design_system/svelte-inertia@^1.0.0 로컬에서는 잘 됐는데 CI에서만 죽는 전형적인 패턴이다. 원인과 해결법을 기록해둔다. ...

2026-04-02 · 4분 소요 · Seunghan

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 · 4분 소요 · Seunghan

Git 브랜치 정리 — worktree 에러 포함 완전 클린업 가이드

프로젝트를 어느 정도 운영하다 보면 브랜치가 쌓인다. feature 브랜치, claude가 만든 자동 브랜치, dependabot 브랜치까지 정리 안 하면 git branch -a 결과가 화면 가득 찬다. 오늘 작업하다 머지된 브랜치들 싹 정리했는데, feature/link-in-bio 삭제하려고 하니까 이런 에러가 났다. error: cannot delete branch 'feature/link-in-bio' used by worktree at '/path/to/.worktrees/link-in-bio' 처음엔 그냥 -D 옵션으로 강제 삭제하면 되지 않나 싶었는데, 그게 올바른 방법이 아니다. worktree를 먼저 제거하는 게 맞다. PR이 이미 머지됐는지 확인하는 법 브랜치 정리 전에 먼저 해야 할 게 있다. 각 브랜치가 main에 이미 포함됐는지 확인하는 것. ...

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

Hotwire Native에서 Flutter BLoC으로 — 네이티브 앱 전환 실전기

Rails 웹앱에 Hotwire Native로 iOS 앱을 감싸서 출시했는데, 접속 오류로 심사가 반복 반려됐다. WebView 기반의 한계를 느끼고 Flutter + BLoC 패턴으로 순수 네이티브 전환을 결정했다. 이 글은 실제로 웹앱을 Flutter 앱으로 전환하면서 겪은 설계, 삽질, 해결 과정을 정리한 것이다. Hotwire Native가 안 된 이유 Hotwire Native는 WKWebView 위에 얇은 네이티브 셸을 씌우는 구조다. Turbo Navigator가 URL 기반으로 네비게이션을 처리하고, Bridge Component로 네이티브 UI를 부분적으로 제어한다. 웹 개발자 입장에서는 최소 비용으로 앱을 만들 수 있어서 매력적이다. ...

2026-03-31 · 9분 소요 · Seunghan

Hotwire Native에서 Flutter로 — Rails 앱의 모바일 네이티브 전환 실전기

Rails 8 + Hotwire로 웹을 만들고, Hotwire Native로 iOS/Android를 감싸면 꽤 그럴듯한 앱이 나온다. WebView가 서버 렌더링 HTML을 그대로 보여주니 코드 한 벌로 3플랫폼을 커버할 수 있다. 실제로 이 방식으로 프로덕션에서 잘 돌아가는 앱을 운영하고 있었다. 그런데 점점 한계가 보이기 시작했다. 오프라인 지원이 안 되고, 네이티브 애니메이션도 못 쓰고, WebView 특유의 뚝뚝 끊기는 느낌이 있었다. 결국 Flutter로 풀 네이티브 전환을 결정했고, 설계부터 프로덕션 배포까지 약 2주 만에 끝냈다. 그 과정을 정리한다. ...

2026-03-31 · 9분 소요 · 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 · 8분 소요 · Seunghan
개인정보처리방침 이용약관 면책조항 문의