main-logo

라우팅 인터셉트 기능을 활용한 모달 인터랙션 향상

Next.js 14 - Intercepting Routes로 모달의 UX/UI 개선하기

profile
doworld
2024년 09월 06일 · 0 분 소요

들어가며

Intercepting Routes라고 혹시 들어보셨나요?
Next.js 13.3 버전에 추가된 기능이라고 하는데 저는 최근에 알게 되었습니다.
이 기능을 활용하면 모달의 사용성과 사용자 경험을 개선할 수 있을 것 같다는 생각을 하여 공유하고자 합니다.

Intercepting Routes란?

Intercepting Routes는 Next.js에서 도입된 새로운 라우팅 기능입니다. 이 기능을 사용하면 현재 레이아웃 내에서 다른 경로의 콘텐츠를 "가로채서" 표시할 수 있습니다.

사용자가 특정 경로로 이동할 때, 그 경로의 콘텐츠를 현재 페이지 컨텍스트 내에서 표시하는 것입니다. 이는 주로 모달, 슬라이드오버, 또는 다른 형태의 오버레이 UI 요소를 구현할 때 유용합니다.

작동 방식 및 구현 방법

Intercepting Routes는 특별한 파일 명명 규칙을 사용하여 구현됩니다. 폴더 이름에 (..) 를 사용하여 인터셉트할 경로를 지정합니다. 상대경로 표기법인 ../ 과 유사하지만 세그먼트에 대해 적용됩니다.

  • (.) 동일한 수준의 세그먼트와 일치합니다.
  • (..) 한 수준 위의 세그먼트와 일치합니다.
  • (..)(..) 두 수준 위의 세그먼트와 일치합니다.
  • (...) 루트 앱 디렉토리부터의 세그먼트와 일치합니다.

구현 시에는 두 가지 경로를 모두 만들어야 합니다. 

  1. 인터셉트될 실제 경로
  2. 인터셉팅 경로 

이렇게 하면 직접 URL을 입력했을 때는 전체 페이지로 이동하고, 앱 내에서 네비게이션할 때는 인터셉트된 버전이 표시됩니다.

예시

https://www.instagram.com/pxdstory/

PC에서의 인스타그램입니다.
사진을 선택하면 링크 이동으로 화면이 전환되는 것이 아니라 현재 페이지 컨택스트가 유지되고 모달로 열고 닫을 수 있습니다.
이 과정에서 모달을 열고 닫을 때 URL이 변경되는 것을 확인 할 수 있습니다.
모달이 열린 상태에서 새로고침을 누를 경우 모달 페이지의 상세 페이지로 이동합니다.

UX/UI 개선 포인트

사용자는 현재 페이지의 컨텍스트를 유지하며 새로운 정보를 확인할 수 있습니다.
모달이나 슬라이드오버 형태로 새로운 콘텐츠를 표시할 수 있어 사용자에게 더 부드럽고 자연스러운 전환 경험을 제공합니다.
그리고 필요한 부분만 업데이트할 수 있어 페이지 로딩 시간을 줄이고 웹사이트의 응답성을 향상시킵니다.
브라우저의 히스토리 API와 잘 통합됩니다. 사용자는 브라우저의 뒤로 가기 버튼을 사용하여 이전 상태로 쉽게 돌아갈 수 있습니다.

전통적인 모달은 주로 JavaScript로 구현되어 URL과 무관했습니다.
Intercepting Routes를 사용하면 모달의 내용을 URL과 연결할 수 있어, 다음과 같은 이점이 있습니다:

  • 일관성: 모달의 내용이 고유한 URL을 갖게 되어 디자인의 일관성을 유지하기 쉬워집니다.
  • 공유 가능성: 모달의 내용을 직접 공유할 수 있어 사용자 경험이 향상됩니다.
  • SEO 친화적: 모달 내용도 개별 페이지로 존재하므로 검색 엔진 최적화에 도움이 됩니다.

더이상 모달과 URL을 연결하려고 모달의 상태를 URL에 저장하지 않아도 됩니다.

실제 구현

Intercepting Routes를 구현하는 기본적인 방법을 살펴보겠습니다.

app 폴더에 이미지와 같이 구성하였고, 주요 코드는 아래와 같습니다.

전체 코드 레포지토리 : https://github.com/doworld-dev/intercepting-routes

// app/photos/page.tsx
<ul className={styles.photoList}>
  {photos.map(({ id, imageSrc }) => (
    <li key={id} className={styles.photoItem}>
      <Link href={`/photos/${id}`} className={styles.photoLink}>
        <Image
          src={imageSrc}
          width={300}
          height={300}
          alt={`Photo ${id}`}
        />
      </Link>
    </li>
  ))}
</ul>

// app/photos/layout.tsx
const PhotosLayout = ({ children, modal }: PhotosLayoutProps) => {
  return (
    <>
      {children}
      {modal}
    </>
  );
};

// app/photos/@modal/(..)photos/[id]/page.tsx
const PhotoModal = ({ params: { id } }: PhotoModalProps) => {
  const photo: Photo = photos.find((p) => p.id.toString() === id)!;

  return (
    <Modal>
      <PhotoCard photo={photo} />
    </Modal>
  );
};

// app/photos/[id]/page.tsx
const PhotoPage = ({ params: { id } }: PhotoPageProps) => {
  const photo: Photo = photos.find((p) => p.id.toString() === id)!;

  return (
    <div>
      <Link href="/photos">Back</Link>
      <PhotoCard photo={photo} />
      <p>상세 페이지</p>
    </div>
  );
};

<Link href={`photos/${id}`}>를 통해 링크를 눌렀을 때 /(..)photos/[id] 경로에 있는 페이지가 인터셉트 되면서 모달로 보여줍니다.
새로고침을 하거나 /photo/1 과 같이 직접 URL로 접근을 할 경우 /photo/[id] 의 페이지가 로드됩니다.

예제에서 Intercepting Routes와 함께 Parallel Routes라는 것을 함께 사용해보았습니다.
슬롯을 통해 동일한 라우트에서 여러 페이지를 동시에 또는 조건부로 렌더링 할 수 있는 기능입니다.
app 디렉토리 하위에 작성된 코드에서만 사용할 수 있습니다. 
@folder 를 선언해서 만들 수 있고공유된 부모 레이아웃에 props로 전달됩니다.

app/photos/layout.tsx 의 modal 부분과 app/photos/@modal 폴더 참고

마치며

Intercepting Routes의 기능을 통해 우리는 원활한 페이지 전환으로 더 부드러운 사용자 경험을 제공할 수 있고, 복잡한 UI 패턴을 보다 수월하게 구현 할 수 있습니다. 빠른 로딩과 응답성, SEO에 친화적인 부분도 도움이 되고요.

https://intercepting-routes-next.vercel.app/photos 주소로 접속하시면 예시로 보여드린 인스타그램과 동일한 방식으로 동작하는 것을 확인하실 수 있습니다.

이것으로 intercepting routes 기능의 소개를 마치겠습니다.

감사합니다.