React.js 를 공부하다 보니 Next.js와 TypeScript를 자연스레 접하게 되었다. 이번 포스트는 Next.js의 기본적인 개념과 디렉토리 구조 및 Route에 대해 간략히 정리하는 글이다.
Next.js 란?
Next.js 는 간단하게 요약하면 React의 SSR(Server Side Rendering)을 구현할 수 있도록 도와주는 프레임워크이다.
React는 기본적으로 SPA(Single Page Application)로 개발하며 CSR(Client Side Rendering)으로 렌더링 되기 때문에 사용자가 최초 접속하는 시점에 모든 js 파일을 로드하고 서버에서 데이터를 가져오기 때문에 처음 접속시 시간이 지연 될 수 있다. 뿐만 아니라 렌더링을 클라이언트쪽에서 하고 렌더링이 되기 전 까지 빈 html 상태로 검색엔진 최적화(SEO)에 있어서 단점을 가지고 있다.
Next.js는 React.js의 이러한 단점들을 상쇄시켜줄 수 있는데 Next.js는 서버쪽에서 페이지를 미리 렌더링하여 HTML을 가져오기 때문에 웹 어플리케이션을 접속하는 사용자나 검색 엔진 크롤러에게 바로 렌더링된 페이지를 전달할 수 있다.
Next.js 의 기본 디렉토리 구조
간단한 예시를 위해 아래 명령문으로 타입스크립트 기반의 Next.js app 을 만들었다.
아래의 폴더구조는 Pages Router 기준의 디렉토리 구조이다. 이후 출시된 App Router의 사용법은 다른 포스트에서 정리해보려 한다.
npx create-next-app@latest --typescript
Next.js와 React.js 는 Route 에서도 큰 차이점이 있는데 React는 React Router(https://kyuntechblog.tistory.com/22 포스트 참고)를 사용하여 Route를 하는 반면 Next.js는 pages의 폴더구조에 따라 자동으로 Route 기능을 제공한다.
✔︎ index.tsx
pages 폴더에 index.tsx 가 "/" 로 접속하였을 때 처음으로 표출되는 인덱스 페이지가 된다.
(로컬에서 포트번호: 3000으로 실행하였을때 기준 'http://localhost:3000/' 접속시 index.tsx가 표출됨)
다른 화면을 만들고 싶으면 pages 폴더 안에 만들면 되는데 예를 들어 post.tsx라는 화면을 pages폴더 안에 만들면 "/post" 로 접속하면 post.tsx 화면으로 이동할 수 있다.
(로컬에서 포트번호: 3000으로 실행하였을때 기준 'http://localhost:3000/post' 접속시 post.tsx가 표출됨)
✔︎ _app.tsx
- 서버로 요청이 들어왔을때 제일 먼저 실행되는 컴포넌트를 의미한다.
- props로 받은 Component는 요청한 page의 컴포넌트를 의미하며 pageProps는 getsServerSideProps나 getStaticProps를 통해 내려 받은 props를 의미한다.
- pages 폴더안에 모든 페이지에 공통으로 들어가는 layout을 작성할 때 사용한다.
- global.css(전역 CSS)를 선언하여 적용할때 사용한다. _app.tsx 폴더 외에 다른 컴포넌트에서 위 화면과 같이 css 파일을 import 하면 아래와 같이 전역 css 는 _app 파일에서 선언하라는 에러가 뜬다.
_app.tsx에 선언된 전역 css와의 충돌을 피하기 위함이 아닌가 싶은데 다른 컴포넌트 내에서는 CSS Module을 사용하여 컴포넌트 단위로 css 를 적용할 수 있다 Import한 컴포넌트에서만 사용이 가능이 하기때문에 class 나 id 명의 충돌을 피할 수 있다.
위의 Home.module.css 처럼 중간에 module을 넣어서 CSS Module 파일을 만들고 사용할 css 명을 임의로 지정하여 import한다. 사용은 위 캡쳐화면과 같이 className 에 css명과 css module안에 선언된 css 를 이어서 사용하면 된다. { } 안에서 사용하기 때문에 삼항연산자와 같이 사용하거나 백틱을 사용하여 동적으로 css 를 적용하는 것도 가능하다.
✔︎ _document.tsx
공통적으로 사용할 head, meta정보, 폰트 등을 설정할때 사용하는 파일이며 _app 파일 다음으로 실행되는 파일이다.
서버에서만 실행되는 파일로 클라이언트에서는 확인이 불가능하다.
Pages Router
✔︎ 파일 시스템 기반의 라우팅
Next.js에서의 페이지 라우팅은 파일 시스템의 구조에 기반하고 있다. pages 폴더내에 생성된 폴더와 파일 구조가 URL 경로가 된다.
위 캡쳐 화면은 next.js로 만든 간단한 blog app의 pages 디렉토리 구조이다.
처음 접속시 : localhost:3000/ -> index.tsx
게시글 작성 화면 : localhost:3000/posts/create -> /posts/create.tsx
게시글 상세 화면 : localhost:3000/posts/detail/[id] -> /posts/detail/[id].tsx
게시글 수정 화면 : localhost:3000/posts/edit/[id] -> /posts/edit/[id].tsx
위와 같이 url 이 pages 디렉토리의 파일시스템 구조를 그대로 따라가고 있는 것을 확인할 수 있다.
✔︎ 동적 라우팅
Next.js 는 동적 라우팅을 지원하기 때문에 URL 경로에 따라 동적인 페이지를 생성할 수 있다.
위 캡쳐 화면의 게시글 상세화면과 게시글 수정 화면의 경우를 보면 알겠지만
// 게시글 상세화면 동적 라우팅
URL : localhost:3000/posts/detail/[id] -> 파일경로 : pages/posts/detail/[id].tsx
파일 이름에 대괄호([])를 사용하여 동적경로를 정의하고 해당경로에 대한 데이터를 동적으로 생성할 수 있다.
동적 라우팅을 하기 위해서 pages/posts/detail/[id].tsx 에서는 대괄호 안에 id 값을 받아와서 DB 나 Api 를 통해 해당 게시글의 정보를 받아와야 한다. id 값을 받기 위한 방법은 아래와 같다.
import { useRouter } from 'next/router';
const PostDetailPage = ({post}) => {
const router = useRouter();
const { id } = router.query;
.
.
.
// 게시글 정보 받아오는 로직 구현
return(
<div>
.
.
.
</div>)
}
export const getServerSideProps: GetServerSideProps = async (context) => {
const id = context.params.id
const post = await axios.get(`api_url?id=${id}`).then((res) =>res.data});
return {
props: {
post
},
};
};
export defualt PostDetailPage;
PostDetailPage 에서 id 값을 받아오는 방법은 위와 같이 두가지 방법이 있다.
- useRouter
첫번째로는 next/router에서 제공해주는 useRouter 훅을 사용하는 것이다. useRouter를 import 하고 router 사용을 선언한 후 router.query 로 id 값을 받아올 수 있다. 위와 같이 구현하게 될 경우에는 클라이언트쪽에서 Rest API 등 으로 데이터를 가져오는 방식으로 개발하게 될텐데 이로 인해 SEO(검색엔진 최적화)가 잘 이루어지지 않을 수 있다.
- context
두번째 방법은 getServerSideProps에서 context.params 에서 id 값을 받아 올 수 있는데 getServerSideProps는 서버 사이드 렌더링으로 서버쪽에서 미리 DB 에서 데이터를 가져오거나 fetch, axios등으로 데이터를 받아와 컴포넌트에 props로 넘겨주게 된다.
Next.js 에서는 클라이언트 사이드 렌더링과 서버 사이드 렌더링 모두 이용할 수 있으므로 표출하는 데이터가 잘바뀌지 않는 정적인 데이터인지 동적으로 자주 업데이트 되는 데이터인지에 경우에 따라 적절히 잘 사용해야 한다.