Pages Routing : 기본 세팅 (+쿼리 스트링, 동적 라우팅, 404)
페이지 라우터든 앱 라우터든 Next.js에서 프로젝트를 생성하고 나면 Next.js에서 기본적으로 작성해 놓은 페이지가 보이게 된다. 이것들을 먼저 제거해주는 것부터 시작해서 페이지 라우터 방식에서는 프로젝트 초기 세팅을 어떻게 하는지 살펴보겠다.
index.tsx 내용 제거
dev 명령어로 개발 서버를 실행시켜 보면 이러한 페이지가 나온다. 내가 만든 페이지가 아니므로 index.tsx 파일에서 제거해주겠다. 아래 사진에서는 import문을 지우지 않았는데 import문과 inter까지 전부 지워주면 된다.
그러면 이렇게 된다.
global.css 제거
배경에 깔린 이상한 줄무늬가 맘에 들지 않는다. 이런 전역 스타일을 제거해준다.
그러면 이렇게 된다.
새로운 라우트(페이지) 만들기
페이지 라우터 방식에서는 기본적으로 파일 이름을 기반으로 라우트가 생성된다. 그러나 폴더 이름으로 만들어도 된다. 그래서 여러가지 방법이 존재할 수 있는데, 라우팅을 중첩해서 타고 타고 들어가는 라우트를 만들려면 폴더를 만들어서 관리하면 된다. 그 모든 방법을 한 번 소개해보겠다.
기본방식 : 파일 이름으로 라우트 만들기
이렇게 mypage.tsx라는 파일을 만들었다. 그러면 /mypage 라는 경로로 접속했을 때 h1 태그로 My Page라는 텍스트가 보여야 한다. 진짜로 그런지 한 번 개벌 서버에서 접속해보겠다.
폴더 이름으로 라우트 만들기
mypage라는 폴더를 만들고 mypage.tsx가 아니라 index.tsx로 만들면 src/pages/mypage.tsx를 만든 것과 동일하게 라우트가 만들어진다.
폴더 이름으로 중첩 라우팅
만약 여기서 mypage/setting까지 만들려면 어떻게 해야 할까?
mypage/index.tsx는 /mypage로 접속했을 때 보여지는 페이지로 두고, mypage/setting.tsx를 만들면 /mypage/setting 으로 접속했을 때 보여지는 페이지로 만들 수 있다.
또는 이왕 폴더로 정리한 김에 페이지 파일은 index.tsx로만 통일하는 방법도 있겠다. /mypage/index.tsx는 /mypage로 접속했을 때 보여지는 페이지, /mypage/setting/index.tsx는 /mypage/setting으로 접속했을 때 페이지를 만들면 된다.
쿼리스트링 사용 방법
Query String이란 URL의 매개 변수를 이야기한다. URL을 통해서 데이터를 주고 받는 HTTP 통신의 한 방법인데, URL에 어떠한 값이 들어가다 보니 "이것도 라우트인가?" 생각할 수 있지만 라우트는 아니다. 그냥 단순히 데이터만 주고 받는 것이다.
아이디나 패스워드 같은 중요한 정보를 주고 받으면 안 되고, 검색어나 글 번호, 유저 닉네임 같은 간단한 정보를 URL에 실어 주고 받도록 하는 데 많이 사용되고 있다.
네이버에 검색을 아무거나 해보면 ? 물음표 뒤에 나오는 것들이 실제 페이지의 라우트 주소가 아니라 쿼리스트링인 것이다.
쿼리 스트링을 자세히 보면 & 기호로 여러 개를 실어서 보낼 수 있다는 특징이 있고, = 기호를 통해서 좌항, 우항의 값이 다르다는 걸 볼 수 있다. 좌항, 우항은 key=value가 페어로 되어 있다는 내용이다.
query string의 max length, 즉 최대 몇 자까지 URL로 데이터를 주고 받을 수 있냐는 것은 브라우저 마다 다르다. 아래는 인터넷 익스플로러 제외하고는 공식적인 수치는 아니고 개발자들이 일일이 테스트 해 본 수치이다. 뭐 결론적으로는 꽤 많은 양을 주고 받을 수 있다는 이야기다.
- Edge : 81,578자
- Chrome : 64,000자까지 브라우저에서 표시 가능. 그러나 실제로는 100,000자까지 사용 가능.
- Firefox : 65,536자까지 브라우저에서 표시 가능, 그러나 실제로는 100,000자까지 사용 가능.
- Safari : 80,000자까지 브라우저에서 표시 가능.
- Internet Explorer : 2,048자
주소창에 이렇게 key를 nickname으로, value를 장원영으로 입력했다고 가정해보겠다. 사용자가 이렇게 입력해서 들어올 일은 없고 보통 다른 페이지에서 버튼을 누르거나 했을 때 쿼리스트링까지 담아서 같이 페이지로 넘겨주는 형태가 될 것이다. 이렇게 URL에 있는 쿼리스트링을 mypage에서 꺼내 오려면 Next.js에서 기본적으로 제공하는 훅인 useRouter 훅을 사용하면 된다.
import React from 'react'
import { useRouter } from 'next/router'
function MyPage() {
const router = useRouter()
const nickname = router.query.nickname;
return (
<div>
<h1>My Page</h1>
<h2>유저의 닉네임은 {nickname}입니다.</h2>
</div>
)
}
export default MyPage
next/router에서 useRouter 훅을 꺼내 오고, router라는 변수로 useRouter 훅을 호출한 다음, router 변수가 router 객체로 만들어지면 router 객체에 담겨 있는 값 중 query에 접근해서 nickname이라는 key를 꺼내오면 된다. 그 값을 꺼내와서 nickname에 담아주면 리턴문에서도 데이터를 바인딩하여 값을 사용할 수 있다.
위에 거나 아래 거나 결과적으로 같긴 한데, destructuring 해서 꺼내 와도 된다.
import React from 'react'
import { useRouter } from 'next/router'
function MyPage() {
const router = useRouter()
const { nickname } = router.query;
return (
<div>
<h1>My Page</h1>
<h2>유저의 닉네임은 {nickname}입니다.</h2>
</div>
)
}
export default MyPage
주의할 점이 있다. useRouter 훅을 import 할 때 자동완성에 보면 next/router와 next/navigation 두 개가 뜨는 것을 볼 수 있다.
- next/router : Pages Router 방식에서 사용하는 useRouter 훅
- next/navigation : App Router 방식에서 사용하는 useRouter 훅
useRouter 훅에서 어떤 값을 제공하는지 콘솔에서 확인해보겠다.
const router = useRouter()
const { nickname } = router.query;
console.log(router);
많은 값이 담겨 있는데, 라우트를 이동시킬 때 사용하는 push, 뒤로가기 기능인 back, 새로고침 기능인 reload, 현재 경로를 나타내는 pathname 등 자주 사용하는 값들도 보인다. 우리는 이 중에서 query의 값을 사용하는 것이다.
query string에 많은 값이 담겨 있으면 어떻게 될까?
import { useRouter } from 'next/router';
import React from 'react'
function MyPage() {
const router = useRouter()
const { nickname, age, location } = router.query;
console.log(router);
return (
<div>
<h1>My Page</h1>
<h2>유저 닉네임 : {nickname}</h2>
<h2>유저 연령 : {age}</h2>
<h2>유저 거주지 : {location}</h2>
</div>
)
}
export default MyPage
많은 값이 담겨 있어도 구조 분해 할당으로 잘 꺼내올 수 있고, query에도 잘 담기는 것을 볼 수 있다.
데이터를 잘 받아오는 것을 잘 나오는 것을 확인할 수 있다.
동적 라우팅 설정 방법
동적 라우팅은 쿼리 스트링과 비슷한 성격인 것 같지만, 그 구조가 다르다. 예를 들어서 마이페이지에서 '장원영'이라는 데이터를 받아 오려면 쿼리스트링으로는 ?nickname=장원영 이런 형태로 받아 왔지만 동적 라우팅으로 받아 오려면 /mypage/장원영 이런 형태로 하나의 라우트를 차지하게 설정해야 한다.
보통 동적 라우팅은 대괄호로 감싸서 [id] 처럼 표현하지만, 안의 파일명이 뭐가 됐든 자유작명이고 [] 대괄호로 감싼다는 게 포인트이다. 대괄호로 감싸게 되면 그 파일명으로는 별도의 라우팅이 생성되진 않고, 위 폴더 구조를 보았을 때 /mypage와 /mypage/setting 까지는 라우팅이 되어 있는데, setting 말고 다른 게 붙으면 그것들을 모두 nickname이라는 Key에 담긴 value라고 보겠다는 의미이다. 따라서 위 구조에 따르면 /mypage로 접속해도 페이지가 보이고, /mypage/장원영 으로 접속해도 보이고 /mypage/안유진 으로 접속해도 보이고, /mypage/setting으로 접속해도 보인다.
하지만 위와 같은 구조에서 조심해야 할 것은 /mypage/setting이라는 라우트를 별도로 갖고 있기 때문에 진짜 유저의 닉네임이 setting이라고 하더라도 그 페이지로는 접속할 수 없고 /mypage/setting 페이지가 우선되어 이 쪽으로 접속된다는 것이다. 위와 같은 구조를 피하는 것이 가장 좋겠지만 불가피하다면 setting과 같이 이미 라우트로 설정되어 있는 키워드는 닉네임으로 설정하지 못하게 막아두어야 한다.
쿼리 스트링에서 값을 꺼내올 때와 똑같이 useRouter 훅을 사용하고 있는데, 아래 사진을 보자.
nickname이라는 쿼리스트링을 넘겨 받은 것처럼 nickname이라는 키에 값이 자동으로 할당되는 것을 볼 수 있다. 그리고 pathname으로 현재 경로를 보면 대괄호로 묶여 있는 동적라우팅 형태를 띄고 있는 것이 보인다.
그러면 쿼리스트링을 사용할 것이냐, 동적 라우팅을 사용할 것이냐? 어쨌든 결과는 같기 때문에 헷갈릴 수 있는데 검색 페이지에서 검색창에 입력된 값처럼 수많은 데이터를 전달하는 게 아니라 마이페이지처럼 페이지 자체는 정적이고 받아오는 값이 많지 않다면 dynamic route로 설정하는 것이 좋다. 쿼리스트링은 매개 변수일 뿐이고 실제 URL 주소가 아니기 때문에 SEO에 잡히지 않는다. 따라서 SEO에 이러한 방식이 유리하고, 또 프로젝트의 디렉토리를 보았을 때 [] 대괄호로 동적 라우팅 설정을 해두면 개발자가 '아, 이 페이지는 라우트를 동적으로 받는구나'하고 이해하기가 쉽다. 이 부분은 별도로 다루겠다.
Catch All Segment Dynimic Routing
그런데 하나 문제가 있다. [nickname].tsx는 /mypage/장원영 이렇게 하나의 동적인 데이터만 받아올 수 있는데, /mypage/아이브/장원영 이렇게 중첩된 동적 라우트에 전부 대응하려면 어떻게 해야 할까? 이 때 Catch All Segment Dynamic Routing 방식으로 파일명을 만들면 된다.
사용법은 쉽다. 구조 분해 할당 하듯이 [...nickname].tsx 이런 식으로 spread operator를 사용해서 파일 이름을 지으면 된다.
[nickname].tsx 일 때는? 당연히 404 not found가 뜬다.
[...nickname].tsx 일 때는? 접속이 잘 된다.
오잉? 근데 왜 아이브장원영 이렇게 다 붙어서 나올까? 콘솔을 보니 슬래시 기준으로 세그먼트들이 nickname이라는 key 안에 배열로 순서대로 할당된 것을 볼 수 있다.
import { useRouter } from 'next/router'
import React from 'react'
function MyPage() {
const router = useRouter();
const { nickname } = router.query;
console.log(router);
const groupName = nickname ? nickname[0] : '';
const nickName = nickname ? nickname[0] : '';
return (
<div>
<h2>유저의 그룹은 {groupName}입니다.</h2>
<h2>유저의 닉네임은 {nickName}입니다.</h2>
</div>
)
}
export default MyPage
이렇게 각 인덱스에 접근해주면 각각의 데이터를 바인딩 할 수 있다. nickname : nickname[0] : ''; 처럼 조건문을 표현식으로 쓴 이유는 nickname에서 각 인덱스에 값이 undefined 일 수 있기 때문에 타입스크립트 컴파일 에러가 떠서 그렇다.
잘 받아 온다.
Optional Catch All Segment
그런데 또 문제가 있다. 그만 문제가 생겼으면 좋으련만 아무튼 문제가 또 있다.
/mypage로 접속하는 거나 /mypage/장원영 으로 접속하는 거나 페이지 구조가 어차피 똑같다면 굳이 /mypage/index.tsx를 만들 필요가 없지 않을까? 만약 /mypage 자체는 아무 페이지도 아니어서 의도적으로 /mypage로 접속했을 때 404 not found를 띄우는 경우라면 모를까, /mypage가 /mypage/장원영과 다른 구조의 페이지가 아니라면 모를까, 같은 컴포넌트를 렌더링 하는 페이지라면 굳이 닉네임 하나 받아오겠다고 /mypage/index.tsx와 /mypage/[nickname].tsx로 파일을 두 개를 만들 필요가 없어 보인다.
그런데 아래 폴더 구조를 보자.
폴더 구조를 이렇게 만들면 /mypage/장원영, /mypage/아이브/장원영/21/인천시 뭐 아무리 길게 써도 전부 대응이 가능하지만, 정작 /mypage 세그먼트로는 접속이 안 된다.
이것까지 모두 대응하는 방법이 Optional Catch All Segment 방식의 dynamic routing이고, [[...nickname]].tsx 처럼 대괄호를 이중으로 감싸주면 /mypage 뒤에 세그먼트가 붙든 붙지 않든 모두 대응이 가능한 동적 라우팅이 된다.
위 폴더 구조를 보면 index.tsx 파일이 없기 때문에 /mypage/아이브/장원영/... 으로는 접속이 가능하지만 /mypage로는 404 not found가 떠야 하는 게 맞다. 그런데 optional catch all segement를 사용하는 dynamic routing을 했기 때문에 /mypage로도 접속이 되고 /mypage/... 로도 접속이 될 것이다. 진짜인지 확인해보겠다.
잘 된다.
404 Not Found Page
마지막으로 알아볼 것은 404 Not Found 메시지를 출력해주는 페이지를 만드는 방법이다. 404 에러는 유저가 없는 경로를 입력했을 때 보여주는 페이지인데, 별도로 설정하지 않아도 Next.js에서 기본적으로 보여주는 페이지가 있다. 그런데 우리가 국내 서비스를 만들 거라면 영어로 뜨는 이런 페이지는 사용자 경험이 떨어질 수 있기 떄문에 직접 커스터마이징 해주는 게 좋다.
위 이미지가 Next.js에서 기본적으로 제공하는 404 페이지이다.
이것을 직접 만드는 방법은 간단한다. pages/404.tsx 파일을 만들어주면 된다. 파일명은 저렇게 해야 하고 컴포넌트 이름은 그냥 page해도 되고 자유작명이다.
잘 작동한다. 나중에 더 예쁘게 만들어주면 될 것 같다.
끝.
'Programing > Next.js' 카테고리의 다른 글
Next.js의 구 라우팅 방법, Pages Router (1) | 2024.08.27 |
---|---|
Next.js를 사용하는 이유 (자유도의 관점, CSR vs pre-rendering) (8) | 2024.08.25 |
Next.js 서버/클라이언트 환경에서 TanStack Query 사용하기 (0) | 2024.08.09 |
Next.js API Route로 supabase 통신하기 (0) | 2024.08.08 |
API Route Handler + Supabase (0) | 2024.08.07 |
댓글