[Next.js] 라우팅으로 모달 구현하기

파이리·2023년 8월 9일
4

Next.js 기능 개발

목록 보기
1/1
post-thumbnail

React에서는 모달을 구현해야 한다면 useState나 전역 상태를 통해 모달을 구현하였습니다.

const Modal = ({ children }) => (
	<div>{children}<div>
)
      
const Main = () => {
	const [open, setOpen] = React.useState(false)      
 	
    return (
    	<>
        	<button onClick={() => setOpen(true)}>모달 열기</button>
        	{open && <Modal> ... </Modal>}
        </>
    )
}

그러나 Next.js에서는 별도의 상태없이 모달을 라우팅만으로도 구현할 수 있습니다.


라우팅만으로 모달을 구현하기 위해서는 Next.js가 제공하는 라우팅 방법 중

을 알아야 합니다.

링크는 공식 문서를 번역한 내용이고, 모달을 구현하는 예시도 나와 있으니 참고하시면 좋을 것 같습니다

Parallel Routes

먼저, 병렬 라우트은 같은 라우트 안에 여러 개의 page.js를 랜더링하는 방법입니다. 병렬 라우트은 사용하면 세분화된 컴포넌트가 각각 랜더링되기 때문에 각 컴포넌트 별로 로딩/에러 처리에 유리하는 장점이 있습니다. 또한, 모든 컴포넌트가 로드되야 사용자에게 보여지는 것이 아니라 각 컴포넌트가 로드될 때마다 화면에 출력됨으로 빠르게 보여줄 수 있는 부분을 사용자에게 먼저 노출하여 사용자 경험에도 좋은 영향을 줄 수 있는 기능입니다.

병렬 라우팅을 사용하는 방법은 원하는 라우트 폴더에 @folderName 폴더를 만들고 page.js 파일을 생성합니다.

저는 모달을 만들 것이기 때문에 @modal 이라는 이름의 폴더를 만들고 내부에 page.tsx를 생성해주었습니다.
(저는 타입스크립트를 사용하기 때문에 .tsx 파일을 생성했습니다.)

그리고 현재 라우트 layout.tsx의 컴포넌트propsfolderName가 추가됩니다. 추가된 folderName 프로퍼티는 @folderName 폴더의 page.tsx가 반환하는 JSX를 값으로 가집니다. 이제 folderName 프로퍼티를 랜더링하고자 하는 위치에 배치시켜주기만 하면 됩니다.

import React from 'react';

const layout = ({ children, modal }: { children: React.ReactNode; modal: React.ReactNode }) => {
  return (
    <>
      {children}
      {modal}
    </>
  );
};

export default layout;

Intercepting Route

라우트 가로채기는 현재 페이지를 유지하면서 다른 라우트의 레이아웃을 표시할 때 사용할 수 있습니다.

예를 들어 /feed 라우트에서 어떤 사진을 클릭하면 /photo/[id]로 이동한다고 가정해보겠습니다. 이 때, /feed에서 랜더링된 항목들이 지워지지 않고 그 위에 /photo/[id]가 랜더링할 항목들이 보여져야 합니다.

이런 경우에 라우트 가로채기를 사용합니다.

단, 주소창에 직접 경로를 입력하거나, 새로고침을 하면 /feed에 오버레이 되는 것이 아니라 /photo/[id] 만 랜더링되게 됩니다.

라우트 가로채기를 사용하기 위해서는 (.)folderName 폴더를 만듭니다. 이때 (.)은 가로채려는 라우트의 위치에 따라 달라집니다.

  • (.)는 같은 수준의 라우트를 일치시킬 수 있습니다.
  • (..)는 한 수준 위의 라우트를 일치시킵니다.
  • (..)(..) 를 사용하여 두 수준 위의 라우트와 일치시킵니다.
  • (...)를 사용하여 루트 앱 디렉터리에서 라우트를 일치시킬 수 있습니다.

위 폴더명 컨벤션을 보면, 자신의 상위 라우트가 형제 라우트는 가로챌 수 있지만, 하위 라우트는 가로챌 수 없다는 것을 알 수 있습니다.

저는 폴더 구조가 꼬여서 중간에 이 부분에서 애먹었습니다.

만들 폴더에 page.tsx 파일을 만들면 완료입니다.

저는 Todo를 수정하는 모달을 만들 것이기 때문에 /edit/[id] 경로를 가로 챘습니다.
(/craete 경로도 가로챘습니다만, 추가로 언급하진 않겠습니다.)
현재 @modal과 가로채려는 /edit은 같은 수준의 라우트에 있기 때문에 폴더명은 (.)edit이 되었습니다.


아래는 각 폴더의 layout.tsx / page.tsx 입니다.


/todos/@modal/(.)edit/[id]/page.tsx

import React from 'react';
import { Modal, TodoForm } from '@/components';

const List = [
  {
    id: '1',
    title: 'HTML',
    decs: 'HTML5 study',
    completed: false,
    createAt: new Date(),
  },
  {
    id: '2',
    title: 'CSS',
    decs: 'CSS3 study',
    completed: true,
    createAt: new Date(),
  },
  {
    id: '3',
    title: 'javaScript',
    decs: 'javaScript study',
    completed: false,
    createAt: new Date(),
  },
];

const EditModal = ({
  params,
}: {
  params: {
    id: string;
  };
}) => {
  const { id, title, decs, completed, createAt } = List.find(({ id }) => id === params.id)!;

  return (
    <Modal>
      <h2 className="text-center font-bold text-4xl">Edit Todo!</h2>
      <TodoForm id={id} title={title} decs={decs} completed={completed} createAt={createAt} />
    </Modal>
  );
};

export default EditModal;

/todos/layout.tsx


import React from 'react';

const layout = ({ children, modal }: { children: React.ReactNode; modal: React.ReactNode }) => {
  return (
    <>
      {children}
      {modal}
    </>
  );
};

export default layout;

그리고 이제 각 TodoItem의 수정 버튼으로 누르면 /todos/edit/[id] 경로로 이동하게 합니다.

const TodoItem = ({ id, completed, title, decs, createAt }: Todo) => {
  const router = useRouter();

  return (
    <li>
      	...
        <button onClick={() => router.push(`/todos/edit/${id}`)}>
          <FiEdit />
        </button>
       	...
    </li>
  );
};

export default TodoItem;

동작

저의 TodoList 페이지입니다.

여기서 수정 버튼을 누르면 아래처럼 경로가 바뀌고 모달이 잘 노출됩니다.

경로 이동으로 모달이 열리도록 구현한 것이기 때문에 브라우저의 뒤로가기/앞으로 가기 기능만으로도 모달을 여닫을 수 있습니다.

profile
프론트엔드 개발자

2개의 댓글

comment-user-thumbnail
2023년 8월 9일

정보 감사합니다.

답글 달기
comment-user-thumbnail
2024년 1월 14일

도움 많이 되었습니다!! 감사합니다 :3

답글 달기