스크롤 한 줄, 마이그레이션 한 달, 제약 한 페이지—설계 판단이 UX를 결정하는 세 가지 순간

스크롤 한 줄, 마이그레이션 한 달, 제약 한 페이지—설계 판단이 UX를 결정하는 세 가지 순간

Next.js 뒤로가기 버그, Billboxx v2 재건, 그리고 '만들기 전 제약 걸기'—세 가지 사례가 동시에 가리키는 것은 기술 선택이 아니라 판단의 타이밍이다.

Next.js App Router 스크롤 복원 프론트엔드 마이그레이션 제품 설계 제약 UX 디테일 popstate Billboxx v2 프로덕트 프리미티브
광고

스크롤이 맨 위로 튀어 오르는 순간, 사용자는 말없이 이탈한다. 에러 메시지도 없고, 콘솔 경고도 없다. 그냥 경험이 끊긴다. 이 작은 균열 하나가 UX 디테일이 얼마나 날카롭게 신뢰를 갉아먹는지를 보여준다. 이번 주 눈에 들어온 세 가지 사례—Next.js 스크롤 복원 트러블슈팅, Billboxx v2 마이그레이션 회고, 그리고 '만들기 전 제약 걸기' 철학—는 전혀 다른 맥락처럼 보이지만 결국 같은 질문을 향한다. 설계 판단을 언제, 어떻게 내리는가.

프레임워크가 구분하지 않는 것을 개발자가 구분해야 한다

velog에 올라온 트러블슈팅 글은 Next.js App Router의 흔한 함정 하나를 정확히 짚는다. App Router는 페이지 이동 시 기본적으로 scrollTo(0, 0)을 실행하는데, 문제는 이게 '앞으로 이동'과 '뒤로 가기(popstate)'를 구분하지 않는다는 점이다. 목록 페이지에서 스크롤을 내려 카드를 클릭하고 상세 페이지를 보다가 뒤로가기를 눌렀을 때, 이전 위치가 아니라 최상단으로 튀어 올라가는 현상이 바로 이 무차별적 스크롤 리셋에서 비롯된다.

해결책은 브라우저 이벤트 레벨로 내려가는 것이었다. popstate 이벤트가 pathname 변경보다 먼저 발생한다는 브라우저 이벤트 순서를 활용해, useRef로 플래그를 세우고 pathname effect에서 이를 확인해 스크롤 초기화를 건너뛰는 방식이다. useState 대신 useRef를 쓴 이유도 명확하다—스크롤 제어는 렌더링과 무관한 사이드 이펙트이므로 리렌더를 유발해선 안 된다. requestAnimationFrame으로 DOM 업데이트 이후 스크롤을 실행하는 타이밍 제어까지 더하면 의도한 대로 정확하게 동작한다.

이 사례가 흥미로운 건 버그 자체보다 판단의 구조다. 프레임워크가 커버하지 않는 영역에서 브라우저 네이티브 이벤트와 React 생명주기를 어떻게 조합할지 개발자가 직접 설계해야 했다. 프레임워크를 믿되, 프레임워크의 경계를 파악하고 그 밖에서 일어나는 일을 직접 제어하는 역량—이게 UX 디테일과 기술 숙련도가 맞닿는 지점이다.

마이그레이션의 '어두운 면'은 happy path 바깥에 있다

dev.to에 게재된 Billboxx v2 회고는 핀테크 앱의 프론트엔드 재건 과정을 솔직하게 기록한다. React 19 + Vite + TanStack Router + Zod라는 스택 선택보다 더 인상적인 건 글의 시작 문장이다. "모든 마이그레이션 이야기는 비슷하게 들린다. 새 스택, 깔끔한 UI, 더 나은 성능. 하지만 실제 핀테크 소프트웨어는 밝은 면만으로 재건되지 않는다."

이 팀이 선택한 아키텍처 결정들은 각각 이유가 명확했다. TanStack Router의 route-driven 구조는 제품 영역 간 명시적 경계를 만들었고, Zod 기반 환경 변수 검증은 런타임 전에 설정 버그를 제거했다. Axios 인터셉터로 토큰 갱신을 패턴화한 것은 인증을 '즉흥'에서 '반복 가능한 경로'로 바꿨다. 개별 기술 선택보다 각 결정이 어떤 문제를 예방하는지 를 팀이 명확히 알고 있었다는 점이 핵심이다.

특히 릴리즈 프로세스를 제품의 일부로 다룬 관점이 날카롭다. PWA 지원, 서비스 워커 업데이트 프롬프트, conventional commits 기반 변경 로그 자동화, Vercel 배포 자동화까지—핀테크에서 배포 파이프라인이 불안정하면 고객 경험이 불안정하다는 인식이 설계 전반에 깔려 있다. 릴리즈를 '의식(ceremony)'이 아니라 '엔지니어링 작업'으로 다룬 것이다.

하지만 회고의 진짜 무게는 대시보드 스크린샷이 아니라 그 이후의 커밋 히스토리에 있다. 빌드 수정, 쿼리 무효화 정리, 컴플라이언스 수정, 파일 업로드 타입 교정—이 시퀀스가 '노이즈'가 아니라 마이그레이션이 현실과 부딪히며 성숙해지는 과정이라는 해석이 설득력 있다. 좋은 마이그레이션은 대규모 론칭이 아니라 그 이후에 쌓이는 작은 수정들로 완성된다.

만들기 전에 거는 제약이 UX의 출발점이다

GeekNews에서 화제가 된 'Making Before Constraints' 글은 제품 설계의 철학적 기반을 건드린다. 핵심은 세 가지 제약이다. ① 아이디어를 한 페이지로 정리할 수 없다면 아직 만들 준비가 안 된 것이다. ② 제품과 별개로 살아남을 수 있는 핵심 기술(core tech)을 함께 만들어야 한다. ③ 사용자에게 항상 드러나는 하나의 결정적 제약(defining constraint)이 제품 정체성을 규정해야 한다.

세 번째 제약이 가장 프론트엔드적으로 흥미롭다. Minecraft는 블록, IKEA는 flat-pack, Linux는 'everything's a file'—이 defining constraint들은 설계 결정 공간을 줄이는 동시에 UX의 일관성을 보장한다. Hacker News 댓글에서 누군가 이를 "product primitives" 라고 부른 것도 정확하다. Notion의 block, Excel의 cell, Figma의 frame처럼 핵심 primitive 수가 적을수록 사용자가 배워야 할 최상위 개념이 줄고, 조합 가능성은 오히려 깊어진다.

one-pager 원칙도 그냥 문서화 습관이 아니다. 한 페이지로 정리하는 과정 자체가 '우리가 무엇을 만들고 무엇을 만들지 않는지'를 팀 전체가 같은 언어로 합의하게 만든다. 한 댓글이 정확하게 짚었다—"즉흥적으로 밀어붙인 프로젝트는, 자기 생각을 명확히 말하지 않았던 사람들까지 결국 전부 실망하게 된다."

세 사례가 함께 가리키는 것

스크롤 복원 버그, 핀테크 앱 재건, 제품 설계 전 제약 걸기—이 세 가지는 규모도 맥락도 전혀 다르다. 하지만 공통 구조가 있다. 사용자 경험의 품질은 기술 선택의 순간이 아니라 설계 판단의 타이밍에서 결정된다. Next.js가 popstate를 처리하지 않는다는 사실을 뒤늦게 발견하면 UX가 끊긴다. 마이그레이션에서 릴리즈 파이프라인을 후순위로 미루면 배포가 불안정해진다. 제품을 만들기 전에 defining constraint를 정하지 않으면 모든 것을 하려는 비대한 결과물이 나온다.

프론트엔드 개발자에게 이 세 사례가 남기는 질문은 하나다. 지금 내리는 이 결정이, 사용자 경험에 영향을 미치는 설계 판단인가—아니면 그냥 코드를 짜고 있는 것인가. 그 차이를 인식하는 순간, 트러블슈팅도 마이그레이션도 제품 설계도 다르게 보이기 시작한다.

출처

더 많은 AI 트렌드를 Seedora 앱에서 확인하세요