Gu Doyoon

PROJECT RECORD

  • React
  • TypeScript
  • Vite
  • Zustand
  • Tanstack Query
  • Tailwind CSS

P-Pick

한국관광공사 Open API를 활용해 주변 여행지를 숏폼으로 탐색하는 서비스. 복잡한 슬라이드 상태와 초기 네트워크 비용을 함께 개선했습니다.

2025.06 — 2025.082명프론트엔드 개발

MY CONTRIBUTION

  • Swiper 기반 숏폼 탐색 인터페이스 구현
  • Suspense 기반 목록 조회와 스켈레톤으로 로딩 상태 설계
  • 위치 권한 거부 시에도 탐색을 계속할 수 있는 대체 흐름 구현

기술적 의사결정

숏폼 탐색의 첫 대기 시간을 줄이다
문제

관광지 목록, 상세 정보, 이미지가 순차적으로 이어지는 Open API 구조에서 초기 화면에 너무 많은 요청과 이미지를 한 번에 보냈습니다. 결과적으로 첫 슬라이드를 보기 전까지 네트워크 대기가 길어졌습니다.

결정과 구현

전체를 먼저 가져오는 방식 대신, useSuspenseInfiniteQuery로 페이지 단위 목록을 만들고 Swiper Virtual로 화면에 필요한 슬라이드만 렌더링했습니다. 현재 위치를 기준으로 다음 페이지를 미리 요청하고, 상세 이미지는 실제로 필요한 시점까지 지연했습니다.

const { data, fetchNextPage, hasNextPage } = useSuspenseInfiniteQuery({ queryKey: ['places', location], queryFn: ({ pageParam }) => getPlaces({ location, pageParam }), getNextPageParam: (lastPage) => lastPage.nextPage, }); const slides = data.pages.flatMap((page) => page.items); // 현재 슬라이드 근처에서 다음 페이지를 선행 요청하고, // 상세 이미지는 슬라이드가 필요해질 때 로드한다.
확인한 변화
주요 성과 지표
초기 API 요청
10회 이상3회초기 요청 축소
초기 이미지 요청
38개 · 13.5MB약 8개 · 753KB초기 대역폭 축소
결과 요약
초기 진입 시 Chrome DevTools Network에서 요청 수와 전송 용량을 비교했습니다. API 구조를 바꾸지 못하는 조건에서도 요청 우선순위와 렌더링 범위를 다시 설계해 첫 콘텐츠를 더 빨리 보도록 만들었습니다.
무한 슬라이드의 복구 시점을 안정화하다
문제

이전 페이지를 앞쪽에 추가하면 기존 슬라이드의 인덱스가 밀립니다. 이미지가 없는 관광지에서는 Swiper의 slidesUpdated 이벤트가 기대한 시점에 오지 않아, 오버레이가 해제되지 않는 경우도 있었습니다.

복구 규칙

앞에 추가된 항목 수만큼 목표 인덱스를 보정하고, DOM 반영 직후 실행이 보장되는 useLayoutEffect에서 슬라이드 위치를 복구했습니다. 세션의 현재 슬라이드와 페이지 파라미터는 URL query로 옮겨 새로고침·공유·재진입에도 같은 지점을 되찾게 했습니다.

const restoredIndex = savedIndex + prependedItems.length; useLayoutEffect(() => { swiperRef.current?.slideTo(restoredIndex, 0); }, [restoredIndex]); // /places?slide-index=12&page-param=3 // 탐색 상태를 URL에서 다시 복원한다.
결과 요약
데이터 추가, Swiper 갱신, 이미지 유무가 서로 다른 타이밍으로 움직이는 문제를 상태 규칙과 복구 시점으로 분리해 안정화했습니다.