Project Detail
BlockGuard
노인 대상 보이스피싱 · 금융 사기 예방 모바일 웹
KUIT 5기 다학제간 팀 프로젝트로 진행해 최우수상을 수상한 작품. 의심 메세지·전화번호·URL·이미지·상황을 13단계로 받아 AI 위험도를 보여주는 사기 분석과, 가족 사칭 · 대출 사기 등 실제 사기 시나리오를 재현하는 실전 시뮬레이션, 사기 관련 뉴스, 보호자 긴급 신고까지 한 흐름으로 묶음. 프론트엔드는 두 명이 공동 구현했고, 본인은 사기 분석 설문·결과 · 시뮬레이션 5종 · 뉴스 · 보호자 알림 · 모바일 호환성 영역을 담당.
앱 흐름
홈
뉴스 · 진입점 — 사기 분석 / 시뮬레이션 / 뉴스 세 갈래로 분기
사기 분석
설문 시작
localStorage 초기화
1~13단계 입력
useFraudSurvey · useImageSave
분석 중 (Lottie)
useQuery loading
결과 · 위험도 표
5분 캐시 · refetchOnMount: false
실전 시뮬레이션
시뮬레이션 진입
가족 사칭 · 대출 사기 등 5종
메세지/전화 렌더링
useDelayRender · isDone 게이트
사후 설명 · 정답 풀이
공용 결과 컴포넌트
사기 관련 뉴스
뉴스 목록 / 카테고리
fixed 헤더 + 카테고리 바
뉴스 상세
이미지 비율 유지
결과 페이지에서 합류
- · 위험도 높음 → 보호자 긴급 신고 (Drawer + SMS 템플릿)
- · 사기 세부 유형 → 대분류 뉴스 탭으로 자동 이동
홈에서 분석 · 시뮬레이션 · 뉴스 세 갈래로 분기하며, 사기 분석 결과는 위험도에 따라 보호자 알림과 사기 유형별 뉴스로 자연스럽게 합류.
기술 의사결정
13단계 사기 분석 설문 — 영속화와 결과 캐싱을 한 흐름으로 묶기
Context
사기 분석은 텍스트·전화번호·URL·이미지·상황 설명을 13단계에 걸쳐 입력받는 무거운 폼. 노인 사용자가 실수로 새로고침하거나 외부 앱(전화·메신저)으로 이탈했다 돌아왔을 때 입력이 모두 사라지면 다시 끝까지 작성하는 부담이 너무 큼.
Problem
초기 구현은 fraudStore 단일 store 에 진행 상태를 두고 단계별 검증을 컴포넌트 안에서 반복 작성. 새로고침 시 진행 상태가 초기화되고, 결과 페이지로 진입 후 뒤로가기/재진입 때마다 분석 API 가 다시 호출되어 노인 사용자가 같은 결과를 또 기다리는 문제가 발생.
Action
fraudStore 를 `useFraudSurvey` 커스텀 훅으로 리팩토링. 각 단계 필수 키·다중 선택 여부를 `STEP_CONFIG` 한 곳에서 선언하고 `canProceed` 검증을 일관 처리. 매 답변 변경 시 `localStorage.setItem("surveyAnswers", ...)` 로 자동 영속화하고, Outlet 자식 라우트는 `useFraudSurveyContext()` 로 동일 상태를 공유. 결과 페이지(`AnalysisResultPage`) 는 useQuery 의 idle/loading/error/success 를 명시적 분기로 처리하고, `queryKey: ['fraud-result', JSON.stringify(allAnswers)]` + `staleTime: 5분` + `refetchOnMount: false` 로 동일 답변에 대한 재호출을 차단.
Result
새로고침·외부 앱 이탈 후에도 설문 진행 상태 100 % 복원. 결과 페이지 재방문 시 5 분 이내 API 재호출 0 회. 단계별 검증 로직 중복 제거로 새 단계 추가 시 STEP_CONFIG 한 줄만 수정.
사기 시뮬레이션 — 시간차 메세지 렌더링과 조기 이탈 차단
Context
보이스피싱 시뮬레이션(가족 사칭 · 대출사기 전화·문자) 은 "실제 사기처럼 천천히 펼쳐지는 메세지" 가 학습 핵심. 노인 사용자가 한 화면에서 모든 메세지를 한 번에 보면 사기 흐름의 긴장감과 학습 효과가 사라짐.
Problem
초기에는 모든 메세지를 한 번에 렌더했고, 사용자가 첫 메세지가 뜨자마자 다음 화면으로 클릭해 시뮬레이션의 학습 효과가 무너졌음. 또한 Lottie 애니메이션이 부모 입력 변경마다 리렌더되며 깜빡이는 문제도 발견됨.
Action
`useDelayRender(items, delayMs)` 훅을 만들어 메세지 1.5 초 · 전화 대사 3 초 간격으로 stagger 렌더링하고, 모든 항목 렌더링이 끝나기 전까지는 `isDone` 플래그로 다음 화면 클릭 자체를 차단. 지연 상수는 `delay-ms.ts` 로 분리(`MESSAGE_DELAY_MS = 1500`, `VOICECHAT_DELAY_MS = 3000`) 해 시나리오 간 일관 유지. Lottie 깜빡임은 입력값을 받는 부모 컴포넌트와 분리한 메모이즈드 모달로 감싸 해결.
Result
메세지가 전부 노출되기 전 다음 화면으로 이탈하는 케이스 0 건. Lottie 가 입력 변경마다 재마운트되는 문제 제거로 시뮬레이션 몰입도 회복. 새 시뮬레이션 추가 시 지연 상수와 useDelayRender 만 조합해 일관된 시간차 UX 재사용.
이미지 첨부 useImageSave — URL 메모리 누수와 5MB 제한, Base64 영속화
Context
사기 분석 13단계 중 이미지 첨부 단계(피싱 의심 문자/통화 캡처) 는 한 번에 여러 장이 올라옴. 미리보기를 위해 `URL.createObjectURL` 을 쓰는데, 컴포넌트 언마운트나 개별 이미지 삭제 시 revoke 를 잊으면 모바일 환경에서 메모리가 빠르게 쌓임.
Problem
초기 구현은 미리보기 URL 을 만들기만 하고 정리하지 않아 13단계를 오가며 메모리 사용량이 누적. 또한 대용량 사진(특히 노인 사용자의 카메라 원본) 을 그대로 전송하면 모바일 셀룰러 환경에서 업로드가 자주 실패. 새로고침 시 첨부한 이미지가 모두 사라져 사용자가 재첨부해야 하는 문제도 함께 존재.
Action
이미지 관련 로직을 모두 `useImageSave` 커스텀 훅으로 분리. 미리보기 URL 의 라이프사이클(생성 → 개별 삭제 시 즉시 revoke → 언마운트 시 일괄 revoke) 을 한 군데서 책임지게 함. 5 MB 초과 파일은 업로드를 거부하고 Sonner 토스트("이미지 용량은 최대 5MB 까지 첨부 가능합니다") 로 즉시 피드백. 새로고침 시에도 첨부 상태를 유지하기 위해 File 객체를 Base64 로 변환해 localStorage 에 저장하고 마운트 시 복원.
Result
미리보기 URL 누수 0 건 (revoke 누락 케이스 제거). 5 MB 초과 첨부로 인한 모바일 업로드 실패 차단. 새로고침 후 첨부 이미지 100 % 복원으로 13단계 도중 재첨부 0 회.
iOS · 모바일 호환성 — 100dvh · overscroll · 스크롤 헤더 색상 전환
Context
주 사용자가 노인이고, 그 가족이 함께 쓰는 환경이라 iOS Safari 비중이 매우 높음. 100vh 기준 레이아웃은 주소창이 나타날 때 잘리고, 스크롤 끝에서 화면이 튕기는 고무줄 효과 때문에 결과 페이지가 의도하지 않게 위·아래로 흔들리며 위험도 표가 한눈에 안 들어옴.
Problem
`100vh` 기반 레이아웃은 iOS 주소창 토글마다 높이가 변해 시뮬레이션·결과 페이지 하단이 잘림. 스크롤 가능한 컨테이너 중첩으로 인해 결과 페이지에서 overscroll bounce 가 발생하며 헤더와 본문이 겹쳐 보임. 단색 헤더는 위쪽(밝은 결과 영역) 과 아래쪽(어두운 위험도 표 영역) 모두에서 대비를 잃음.
Action
전 페이지 레이아웃을 `h-dvh` (`100dvh`) + `flex-1` 조합으로 통일해 주소창 변화에 영향받지 않도록 함. 결과 페이지는 `overscroll-none` 으로 스크롤 고무줄 차단, 외부/내부 컨테이너 중복 `overflow` 를 단일 `<main>` 의 `flex-1 overflow-y-auto` 로 정리. 스크롤 위치(145 px 임계) 에 따라 헤더 배경색을 전환하는 `useScrollHeader` 훅을 만들어, 결과 영역에서는 투명, 위험도 영역에서는 채워진 헤더로 자연스럽게 전환되도록 함 (`transition-colors duration-150`).
Result
iOS 주소창 토글로 인한 하단 잘림 0 건. 결과 페이지 overscroll bounce 0 건. 헤더 색상 전환으로 어느 스크롤 위치에서도 헤더 텍스트 가독성 유지.