본문 바로가기

javascript/nextjs

[nextjs] File system routing

https://nextjs.org/learn/basics/data-fetching

 nextjs 프레임워크가 리액트와 차별화되는 특징 중 하나는 서버측에서 페이지를 일부 생성하여 사용자에게 전달할 수 있다는 점이다. 이때 리액트에서 라우팅이 react-router 라이브러리에 의존하고, <Routes> 및 <Route> 컴포넌트에 의해 수행되었던 것과는 달리, nextjs는 파일 시스템 라우팅을 기본적으로 채택한다. 이 시스템에 대해 알아보자.


nextjs의 페이지

 nextjs는 파일 시스템 라우팅을 지원하며, 모든 페이지 컴포넌트들은 pages 폴더 내부에 존재한다. 

pages 폴더 내부에 존재하는 페이지들

파일 시스템 라우팅은 파일 시스템 상의 경로에 기반하여 사이트의 경로를 지정하는 방식으로, 웹사이트의 경로 ( 보통 path로 표현 ) 와 서버의 파일 시스템 상 상대 경로가 논리적으로 동일하게 된다.

 예를 들어 누군가 https://my-site123.com/login 이라는 주소를 통해 특정 사이트의 로그인 페이지에 접근한다고 가정하자. 만약 이 서버의 페이지가 nextjs에 기반한 파일 시스템 라우팅을 이용하고 있다면, login 페이지는 페이지 컴포넌트들의 기본 엔트리인 pages 폴더 바로 아래에 /pages/login.tsx 으로 존재하게 된다. 이 경우 url의 path 부와 엔트리를 제외한 파일시스템 상의 path 부는 동일하다. 이처럼 파일 시스템 라우팅을 이용하면 웹 및 파일 시스템 상 페이지의 논리 구조가 동일하여 직관적으로 페이지를 구성할 수 있다는 장점이 있다.

 

동적 경로(Dynamic Route)

 https://my-site123.com/login 주소가 /login/user 과 같은 형식으로 확장되는 경우도 존재할 수 있다. 이 상황에서는 /login 및 /login/user 두 주소 모두에 대한 접근이 가능해야 한다. 그러나 파일 시스템 상 login 및 user이 동시에 파일일 수는 없다. /A/B/C/D 의 주소가 있다면 최소한 A, B, C는 디렉토리에 해당하며, D는 파일이거나 디렉토리가 되어야 한다. 그렇다면 /login/user.tsx 파일이 존재할 때, /login은 어떻게 표현할까?

 nextjs는 위와 같은 경우 index 페이지를 지정한다. index 페이지는 보통 기본 페이지를 나타내며, 특정 경로 자체에 해당하는 라우팅에 대응된다. 예를 들어 /login 에 대한 index 페이지는  /pages/login/index.tsx 경로에 존재하게 되는데, 이는 /pages/login 경로에 대응된다. 특정 경로에서 파생되는 경로가 있어, 파일 시스템 상 해당 경로 이름으로 파일을 만들 수 없는 경우 index 라는 이름으로 경로를 나타낸다.

 사이트에 따라 위처럼 마지막 특정 주소의 값이 바뀌면 글의 내용만 바뀌고, 세부적인 레이아웃은 바뀌지 않는 경우가 있다. 위 주소에 대응되는 페이지가 pages/wiki/레밍속.tsx  및 pages/wiki/UR-100MR.tsx 이라는 형태로 개별적으로 존재할 수도 있겠지만, 대부분의 상황에서는 마지막 주소에 대응되는 데이터베이스 내 정보를 가져와, 동일한 페이지에 대해 정보만 바꿔 렌더링하는 것이 일반적일 것이다. 이런 상태에서 마지막 주소 '레밍속' 및 'UR-100MR'은 특정 데이터를 나타내는 동적 경로(Dynamic Route)가 된다. nextjs에서는 [name].tsx 와 같이 대괄호를 이용하여 동적 경로를 나타낸다.

 Dynamic Routes

위 이미지에서는 genshin 폴더와 하위 파일 [type].tsx 및 index.tsx을 볼 수 있다. 여태까지의 설명에 따르면 [type].tsx은 ~~/genshin/type 형태의 동적 경로에 대응되고, index.tsx은 ~~/genshin 경로에 대응된다.

 동적 경로가 길게 나타나는 경우도 존재할 수 있다. 이 경우 점을 3개 붙여 [...something].tsx 형태로 페이지를 구성한다.

 동적 경로를 나타낼 때 대괄호 내에 적히는 이름은 무엇으로 지정하더라도 상관없다. 그러나 일단 이름을 정하면, 이 이름은 해당 동적 경로 페이지 내에서 의미를 가진다. 위 예시의 [type].tsx 에서 type은 어떤 이름이 되더라도 상관 없다. 그러나, 일단 이름을 정하면 이 이름에 대응되는 문자열이 params 객체에 생겨난다. 

getStaticPaths 메서드로 생성
사용자의 요청(req)에서 추출

 서버 환경에서 페이지를 생성하는 경우, getStaticProps 혹은 getServerSideProps 메서드를 이용하여 초기 정보를 가져온다. 만약 클라이언트 측에서 .../genshin/characters 라는 경로에 대해 요청하면, 서버는 [type] 에 대응되는 'character' 문자열을 ctx.params 객체 아래에 key-value 형태로 지정한다. 이 상황에서는 동적 경로 type 및 이에 대응되는 character은 ctx.params.type === 'character' 이 된다. 따라서 동적 경로를 이용할 때는 동적 경로의 이름을 일관되게 사용해줄 필요가 있다는 점을 명심하자.


결론 및 요약

 nextjs에서는 파일 시스템 기반의 라우팅 방식을 채택하고 있다. 이에 따라 url상의 경로 및 파일 시스템의 pages 폴더를 엔트리로 한 경로는 서로 논리적으로 대응된다. 기본적인  경로(/login)는 1) 동일 이름의 파일(/login.tsx) 혹은 2) 동일 이름 폴더 내 index 파일 (/login/index.tsx) 을 통해 나타낼 수 있다. 동적 경로는 대괄호([type]) 를 이용하여 나타낼 수 있으며, 동적 경로에 사용한 이름(type) 은 이후 getStaticProps 및 getServerSideProps 의 ctx.params 객체에 key-value 형태로 지정될 수 있다(ctx.params.type === 'character').

 실제로 파일 시스템 기반 라우팅을 이용해본 결과, 페이지를 정말 직관적으로 나타낼 수 있다는 점이 장점으로 다가왔다. 다만 하나의 경로 하위에 너무 많은 페이지가 존재한다면, 이를 관리하기 어려울 수도 있을 것 같다. 각 페이지에 대응되는 css 파일을 리액트를 사용할 때처럼 같이 저장하기에는 하나의 폴더 내에 너무 많은 파일이 생기지는 않을까? 이러한 생각을 하면서 어떻게 관리하는 것이 좋을까에 대해 고려하는 것도 좋아보인다.

(동일 이름 폴더 내 index 파일)