Skip to content

fix: 스토리지 차단 환경에서 랜딩 리다이렉트 크래시 방어#283

Merged
JeLee-river merged 45 commits into
devfrom
fix/safe-localstorage-landing-redirect
Jul 8, 2026
Merged

fix: 스토리지 차단 환경에서 랜딩 리다이렉트 크래시 방어#283
JeLee-river merged 45 commits into
devfrom
fix/safe-localstorage-landing-redirect

Conversation

@jaeyoung-kwon

Copy link
Copy Markdown
Contributor

📌 What

스토리지 접근이 막힌 환경(시크릿·인앱 브라우저·일부 링크 미리보기 크롤러)에서 www.bombom.news 진입 시 _bombom 라우트가 크래시하며 TanStack Router 기본 에러 UI(Something went wrong! Show Error.)가 노출되던 문제를 수정합니다.

증상

❓ Why

/ 진입은 /_bombom/_main/에 매칭돼 _bombom.beforeLoad가 반드시 실행됩니다. 여기서 localStorage.getItem(LANDING_VISITED_KEY)를 무방비로 호출하는데, 스토리지가 차단된 컨텍스트에서는 window.localStorage 접근 자체가 SecurityError를 던집니다.

  • beforeLoad가 reject → 해당 라우트의 기본 errorComponent가 본문을 덮음
  • 이 에러는 라우트 단위 CatchBoundary가 삼켜서 최상위 SentryErrorBoundary까지 올라오지 않고, 라우터에 Sentry 캡처 훅도 없어 Sentry에도 잡히지 않은 채 조용히 노출됨
  • 링크 미리보기 크롤러가 JS 실행 후 이 에러 화면을 본문으로 긁어가 OG 카드 설명이 에러 문구로 표시됨

추가로 utils/localStorage.ts는 모듈 최상단에서 const storage = window.localStorage를 잡고 있어, 이 유틸을 쓰는 lazy 청크들도 스토리지 차단 시 import 단계에서 터질 수 있는 잠재 크래시가 있었습니다.

🔧 How

  • createStorageget/set/remove를 각각 try/catch로 감싸고, 모듈 최상단 window.localStorage 참조를 제거해 접근을 지연시킴. 실패 시 getdefaultData 반환, set/remove는 noop.
  • 랜딩 방문 플래그를 안전 래퍼(landingVisitedStorage = createStorage<boolean>(KEY, false))로 통일해 _bombom.beforeLoad·landing.tsx의 raw localStorage 호출을 제거. 스토리지 차단 시에는 false → 기존 의미대로 /landing 리다이렉트로 폴백.
    • 기존 저장값('true')은 JSON 파싱과 호환되어 재방문자 플래그가 유지됩니다.
  • 라우터에 defaultErrorComponent: PageErrorFallback를 추가해, 앞으로 어떤 라우트가 죽어도 영문 기본 UI 대신 한국어 폴백이 뜨도록 안전망 추가.
  • 스토리지 접근 throw 케이스 회귀 테스트 3건 추가.

검증

  • 라이브 사이트에서 localStorage 접근이 throw하도록 주입 → /Something went wrong! Show Error. 렌더됨을 재현(원인 확정)
  • 수정 후 createStorage.get()SecurityError에서 false 반환 → /landing 리다이렉트로 이어짐을 유닛 테스트로 검증
  • tsc --noEmit / eslint / jest 전부 통과

👀 Review Point

  • utils/localStorage.ts 안전화 (throw 시 기본값/noop, 최상단 참조 제거)
  • 랜딩 방문 플래그 안전 래퍼로 통일 (_bombom.beforeLoad, landing.tsx)
  • 라우터 defaultErrorComponent 안전망
  • 회귀 테스트 추가

후속(별도 PR 제안): defaultErrorComponent는 UI만 교체하고 라우트 렌더 에러를 Sentry로 보고하진 않습니다. 관측이 필요하면 폴백/라우터 에러 훅에서 Sentry.captureException 연동을 검토해요.

🤖 Generated with Claude Code

JeLee-river and others added 30 commits March 10, 2026 17:23
* chore: 랜딩페이지 robots 태그 수정

* chore: 사이트맵 업데이트
* feat: nanumgothic으로 폰트 변경

* feat: 800-> 700 font weight 변경
jaeyoung-kwon and others added 14 commits May 19, 2026 01:16
* feat: 어드민에 Flyway 버전 형상 뷰어 추가

- /flyway 라우트 + 사이드바 메뉴
- flyway API/쿼리 모듈 (overview/script/wip)
- FlywayViewer: 4단계 파이프라인, 같은번호 충돌·순서역전 배너, 검색/상태필터,
  마스터-디테일(CodeMirror SQL), 다음 안전 번호
- WipRegisterForm: 작업중 등록(새/기존 테이블, 기존 테이블 검색) → 백엔드 이슈 생성

* feat: Flyway 뷰어 UI 개선 — 순서역전 행 표시 / dev·prod 파이프라인 레이블

- STATUS_META: MERGE_PENDING→'dev 반영', DB_APPLIED→'prod 반영'
- 순서역전 행: 좌측 컬러 세로선(컬럼=빨강, 테이블=주황) + 인라인 배지
  ('⚠ 컬럼 역전' / '🔀 순서 역전')
- 상단 배너 severity 표기: TABLE→'테이블', COLUMN→'컬럼'으로 한글화
- SQL 패널: 선택 마이그레이션이 순서역전 대상이면 경고 블록 표시
  (공유 테이블/컬럼명 하이라이트, 강약 구분 색상)
* chore: 연속 읽기 보호막 API 타입 반영

(cherry picked from commit 338cdcc)

* design: 연속 읽기 보호막 사용 완료 아이콘 추가

(cherry picked from commit 3997771)

* feat: 연속 읽기 보호막 정보 컴포넌트 추가

(cherry picked from commit 119f0fd)

* feat: 연속 독서왕 내 순위 보호막 표시 추가

(cherry picked from commit 2f80a09)

* chore: 연속 읽기 보호막 목 데이터 추가

(cherry picked from commit 834124c)

* chore: 연속 읽기 보호막 API 타입 정리

(cherry picked from commit 711b1fb)

* fix: 체리픽 생성 타입 정합성 수정
localStorage 접근이 SecurityError로 throw하는 환경(스토리지 차단 인앱/크롤러)에서
_bombom beforeLoad가 실패해 라우트 기본 에러 UI가 노출되던 문제 수정.
createStorage를 try/catch로 감싸 실패 시 기본값 반환·noop 처리하고, 라우터에
defaultErrorComponent 안전망 추가.

@jaeyoung-kwon jaeyoung-kwon left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 PR Review

✅ 확실하게 수정이 필요한 항목을 찾지 못했습니다.

localStorage 접근이 SecurityError로 throw하는 환경에서 라우트 crash를 방지하는 방어적 수정입니다. createStorage의 get/set/remove를 모두 try/catch로 감싸고 모듈 최상단 window.localStorage 참조를 지연시켰으며, 랜딩 방문 플래그를 안전 래퍼로 통일했습니다. 기존 raw 문자열 'true'로 저장된 값도 JSON.parse('true') === true로 정상 호환되고, PageErrorFallback은 이미 SentryErrorBoundary fallback으로 쓰이던 컴포넌트를 재사용해 router defaultErrorComponent에 안전하게 연결됩니다. 회귀 테스트 3건도 각 케이스(get/set/remove throw)를 적절히 커버하고 afterEach에서 mock을 정상 복원합니다. introduced diff 범위 내에서 production 이슈, 보안, 데이터 손상, 사용자 플로우 차단 리스크를 발견하지 못했습니다.

🚨 0 Critical · ⚠️ 0 Major · 📝 0 Minor

📋 검증 과정
  • Claude structured review 결과 중 확신도가 있는 항목만 정리했습니다.
  • Critical/Major는 inline comment로 게시하고, Minor는 참고 항목으로 summary에 포함합니다.
  • 자동 생성된 OpenAPI 타입 선언 파일과 lock 파일은 리뷰 대상에서 제외합니다.

🤖 Claude PR Review


Generated by Claude Code

@jaeyoung-kwon jaeyoung-kwon changed the base branch from main to dev July 2, 2026 15:20

@JeLee-river JeLee-river left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이런 이슈가 있었나보네요.. 작업해주셔서 감사합니다~!

@JeLee-river JeLee-river merged commit 60f7033 into dev Jul 8, 2026
2 checks passed
@JeLee-river JeLee-river deleted the fix/safe-localstorage-landing-redirect branch July 8, 2026 14:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants