React 앱을 빠르게 키울 때 진짜 병목은 아키텍처가 아니라 인터랙션이다

React 앱을 빠르게 키울 때 진짜 병목은 아키텍처가 아니라 인터랙션이다

TanStack Router로 125개 라우트를 관리하고 dnd-kit으로 드래그를 구현하면서 드러난 것—기술 선택의 트레이드오프는 항상 가장 작은 UI 디테일에서 먼저 터진다.

dnd-kit TanStack Router Cloudflare Pages 드래그 앤 드롭 접근성 마이크로 인터랙션 React 확장 아키텍처
광고

확장의 병목은 생각보다 작은 곳에 있다

'빠르게 확장한다'는 말을 들으면 대부분 라우팅 구조나 번들 전략을 떠올린다. 그런데 실제 프로덕션 경험을 들여다보면 이야기가 달라진다. dev.to에 공개된 freefixo.com 구축기와 velog의 dnd-kit 드래그 앤 드롭 구현기는 서로 다른 스케일의 문제를 다루고 있지만, 핵심 교훈은 놀랍도록 겹친다. 기술 선택의 트레이드오프는 항상 가장 작은 UI 디테일에서 먼저 터진다.

TanStack Router + Cloudflare Pages: 확장의 조건을 먼저 설계하라

125개 이상의 계산기를 혼자 구축한 경험은 '파일 하나 = 계산기 하나'라는 패턴이 얼마나 강력한지를 증명한다. TanStack Router의 파일 기반 라우팅은 새 도구를 추가할 때 기존 코드를 건드릴 필요가 없게 만든다. 타입 안전한 라우트 추론 덕분에 문자열 오타로 인한 런타임 버그도 사라진다. React Router 대신 TanStack Router를 선택한 이유가 바로 이 타입 안전성이었고, 검색 파라미터 핸들링의 학습 곡선이라는 트레이드오프를 감수할 만한 가치가 있었다고 저자는 평가한다.

Cloudflare Pages는 CSR SPA에 최적화된 선택이다. git push 한 번에 30초 안에 전 세계 CDN에 배포된다. 단, 함정이 하나 있다. 클라이언트 사이드 라우트에 직접 URL로 접근하면 기본적으로 404가 반환된다. _redirects 파일에 /* /index.html 200 한 줄을 추가해야 SPA 라우팅이 완성된다. 저자가 "창피할 만큼 오래 걸렸다"고 고백한 이 한 줄은, 인프라 선택이 UX에 직결되는 전형적인 사례다.

dnd-kit과 flex 레이아웃의 충돌: 라이브러리 예제를 맹신하지 말 것

dnd-kit 구현기는 더 흥미롭다. 공식 예제를 따라 DragOverlay를 적용했다가 두 개의 버그를 연속으로 마주쳤다. 첫 번째는 드래그 오버레이가 커서 위치와 어긋나는 문제, 두 번째는 드래그 중 폴더 이름이 글자 단위로 세로 분열되는 문제였다.

원인은 레이아웃 컨텍스트의 단절이었다. DragOverlay는 요소를 DOM 포탈로 꺼내 렌더링한다. 부모 flex 컨테이너의 align-items: stretch가 적용된 행은 포탈 밖으로 나오는 순간 그 컨텍스트를 잃는다. 폭 측정으로 보완을 시도했지만 타이밍에 따라 0이 잡히면서 내부 flex 자식들이 붕괴했다.

해결책은 놀랍도록 단순했다. DragOverlay를 제거하고 기본 in-place sortable로 전환하는 것이었다. useSortable이 반환하는 transform을 그대로 style에 적용하면, 요소는 포탈로 이탈하지 않고 리스트 안에서 커서를 따라 이동한다. 코드도 줄고 버그도 사라졌다.

이 경험이 주는 교훈은 명확하다. 라이브러리 예제를 따르기 전에 '내 레이아웃이 그 예제와 같은 전제를 갖는가'를 먼저 확인해야 한다. DragOverlay는 고정 폭 카드나 스크롤 컨테이너를 넘나드는 드래그에는 적합하지만, flex-stretch 기반 리스트 행에는 오히려 독이 된다.

접근성은 사후 조치가 아니라 설계 조건이다

dnd-kit을 선택한 이유 중 하나는 HTML5 드래그 앤 드롭 API 대신 포인터/키보드 이벤트 기반으로 동작한다는 점이다. KeyboardSensor를 함께 등록하면 키보드만으로도 리스트 순서를 변경할 수 있다. 드래그 핸들을 아이콘 버튼에만 분리한 설계(listenersattributes를 정렬 아이콘 버튼에만 부착)도 접근성과 직결된다. 행 전체가 아닌 핸들만 드래그 가능하게 만들면, 스크린 리더 사용자도 버튼의 역할을 명확하게 인식할 수 있다.

PointerSensoractivationConstraint: { distance: 5 }를 설정한 것도 마이크로 인터랙션 관점에서 중요하다. 5px 이상 움직여야 드래그로 인식하게 함으로써 단순 클릭과 드래그를 구분한다. 이 한 줄이 없으면 버튼 탭이 의도치 않은 드래그로 오인되어 사용자 경험을 망친다.

'빠른 확장'과 '미완성 API' 사이의 현실적 설계

두 사례 모두 미완성 상태로의 출시를 선택했다. dnd-kit 구현기에서는 백엔드의 '폴더 순서 저장' 엔드포인트가 아직 없어서 저장이 클라이언트 화면에서만 유효하다. 새로고침하면 서버 순서로 돌아온다. 그러나 API가 준비되면 handleSaveReorder에서 mutation 호출 한 줄만 추가하면 된다는 점에서 확장 가능한 구조를 미리 잡아뒀다.

freefixo.com 구축기에서도 메타 태그와 검색 기능을 출시 이후에 붙였다고 솔직하게 고백한다. 40개가 준비됐을 때 이미 출시할 수 있었는데 늦췄다는 후회도 함께. '완벽하게 준비될 때까지 기다리지 말라'는 조언은 진부하게 들리지만, 두 사례 모두 그 가치를 실제 경험으로 증명하고 있다.

전망: 패턴의 가치는 다음 기능이 얼마나 쉬운가로 측정된다

두 사례가 공통으로 가리키는 방향은 하나다. 확장 가능한 패턴을 초기에 확립해두면, 이후 기능 추가는 그 패턴을 반복하는 것으로 족하다. 계산기 하나 추가가 파일 하나와 config 한 줄이고, 폴더 순서 저장이 mutation 한 줄이라는 것—이것이 현대적인 React 아키텍처가 지향하는 방향이다.

TanStack Router의 파일 기반 라우팅과 타입 추론, dnd-kit의 composable한 훅 설계는 모두 같은 철학을 공유한다. 도구는 복잡성을 숨기는 것이 아니라, 복잡성을 예측 가능한 패턴으로 관리하게 돕는 것이어야 한다. 그리고 그 패턴이 접근성과 마이크로 인터랙션까지 포괄할 때, 비로소 확장 속도와 경험 품질이 함께 올라간다.

출처

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