[웹심화] Next.js
1. Next.js 란 무엇인가?
Next.js는 리액트 기반의 서버사이드 렌더링 프레임워크이다.
⭕️ CSR (Client Side Rendering)
CSR은 쉽게 말해서 클라이언트에서 렌더링을 모두 처리하는 것이다.
맨 처음 렌더링을 할 때 서버에서 페이지 전체를 받아서 보여주고, 사용자의 요청에 따라서 리소스를 서버에 요청하고 제공받아서 화면에 띄우는 방식이다.

서버에 HTML문서를 요청하는 것이 아니라 브라우저에서 자바스크립트로 화면을 렌더링 하는 것!
CSR은 SPA에 적합한 방식이기 때문에, 화면에 렌더링을 할 때 HTML은 텅텅 비어있다. 또한 맨 처음에 화면에 필요한 자바스크립트파일(라이브러리 + 프레임워크 + 소스코드 등)을 몽땅 다운받게 되기 때문에, 첫 렌더링에 많은 시간이 걸린다.
이에 따른 CSR의 대표적인 단점 두가지가 있다.
- 사용자가 첫 화면을 보기까지 많은 시간이 든다는 점
- SEO에 적합하지 않다는 점
두번째 단점은 HTML의 body 가 비어있기 때문에 검색엔진이 해당 웹페이지를 분석하기 어렵기 때문이다.
⭕️ SSR (Server Side Rendering)
SSR은 클라이언트에서 모든 것을 처리하지 않고, 웹사이트에 접속하면 서버에서 필요한 데이터를 모두 가져와서 HTML 파일을 만든 뒤 만들어진 HTML파일을 클라이언트에 보낸다. 클라이언트는 이 파일을 화면에 렌더링 하는 것이다.

서버에서 완전하기 만들어진 HTML을 화면에 로드하기 때문에 SEO에 적합하고, 서버로부터 화면을 렌더 하기 위한 필수적인 요소를 먼저 가져오기 때문에 이후에 설명할 클라이언트 사이드 렌더링 보다 초기로 로딩 속도가 빠르다.
하지만 화면안에 부분적인 렌더링을 위해서도 페이지 전체의 리소스를 서버로부터 받아와야 하기 때문에 동적인 인터렉션에 있어서는 효율성이 낮은 편이다.
또한 초기 로딩속도가 빠른 만큼 동시에 이것이 치명적인 단점이 되기도 하는데,
TTV(Time To View)와 TTI(Time To Interact) 간에 시간 간격이 존재하게 된다. 사용용자가 버튼을 클릭하거나 이동하려고 해도 아무런 반응이 없을 수 있다.
➕ Next.js의 작동원리
1. 초기에 사용자가 Server에 페이지 접속을 요청하면, SSR방식으로 렌더링 될 HTML을 보냄.
2. 브라우저에서 JavaScript를 다운로드하고 React를 실행함.
3. 사용자, 페이지가 서로 상호작용하여 다른 페이지로 이동할 땐, Server가 아닌 CSR방식으로 브라우저에서 처리함.
2. Next.js의 특징은?
- Server Side Rendering (SSR)
next.js의 가장 큰 특징. 위에서 설명했기 때문에 pass - Search Engine Optimization (SEO)
서버에서 HTML을 받아오기 때문에 SEO에 용이하다. 이것도 위에서 설명했기 때문에 pass - Pre-Rendering
next.js는 모든 페이지를 사전 렌더링한다. pre-rendering에도 두가지 방식이 있다.
⭕️ Static Generatiomn : HTML을 빌드 타임에 생성해두고 요청시마다 재사용하는 방법. "next build"를 하면 생성.
⭕️ Server Side Rendering : 요청시마다 HTML을 재생성하는 방법
next.js는 이 두가지 방법을 페이지마다 다르게 적용할 수 있다. - Dynamic Routes
별도의 라우팅 세팅 없이 페이지별 라우팅 기능을 제공한다. - Code Spliting
첫 페이지를 로드할 때 모든 코드를 호출하는 것이 아닌, 번들을 여러 조각 내어 처음에 필요한 부분만 전송해주는 방식이다. (로드시간을 단축시킴)
근데 코드분할은 Webpack에서도 제공하는 기능이긴 한데, 넥스트트 별도의 설정 없이 사용가능하다는 점이 특징이다.
3. Next.js 가 제공하는 기능
(위의 특징과 겹치지 않고 너무 기본적이지 않은 기능들만 추려옴)
single file components
style jsx를 사용함으로 컴포넌트 내부에 해당 컴포넌트만 스코프를 가지는 css를 만들수 있다.
(근데 보통 html 안에 스타일을 정의하진 않으니깐.. 많이 사용 안하지 않을까?)
function NextJsComponent(props) {
const variable = "web";
return (
<div className="title">
<h1>{props.heading}</h1>
<style jsx>
{`
h1 {
color: ${variable};
}
`}
</style>
</div>
);
}
typescript
타입스크립트 활용을 위해 웹팩을 만지거나 바벨을 만질 필요가 없다.
타입스크립트를 설치하고 (yarn add typescript @types/node @types/react) 명령어 (yarn run dev)만 하면 자동으로
tsconfig, next-end.d.ts가 생성되어 타입스크립트로 코딩이 가능하다.
_document.tsx
meta 태그를 정의하거나, 전체 페이지에 관려하는 컴포넌트이다. react 에서는 index.html이 있는데 여기서는 이 파일에서 html head부분을 관리하더라! 신기해서 써봄
import Document, { Html, Head, Main, NextScript } from "next/document";
export default class CustomDocument extends Document {
render() {
return (
<Html>
<Head>
//넣고싶은 메타태그 넣기 ssap가능
//폰트link도 여기에다가 쓰면 됨
<meta property="custom" content="123123" />
</Head>
<body>
<Main />
</body>
<NextScript />
</Html>
);
}
}
Link 사용하기
보통 페이지간 이동은 a 태그를 사용하나 next에서는 사용하지 않는다. (근데 그냥 리액트에서도 라우터 쓰면 Link 쓰지 않나)
a 태그를 사용하면 처음 페이지에 진입시 번들 파일을 받고, a 태그에 의해 라우팅 되면 다시 번들 파일을 받기 때문이다. 또한 redux을 쓰시는 경우 store의 state 값이 증발되는 현상도 일어난다. 그렇기 때문에 a 태그는 전혀 다른 사이트로 페이지를 이동시켜 다시 돌아오지 않는 경우만 사용하고, 그 이외에는 모두 Link 태그를 사용한다.
import Link from "next/link";
const Index = () => (
<div>
<Link href="/blog">
<a>Blog</a>
</Link>
// 동적 link시 [] 사용
<Link href="/blog/[address]">
<a>Blog</a>
</Link>
</div>
);
동적 url
가변적으로 변하는 url에 대해 동적 url을 지원한다. [] 문법으로 동적 페이지를 생성하는 동적 url을 만들 수 있다.
import { useRouter } from "next/router";
export default () => {
const router = useRouter();
return (
<>
<h1>post</h1>
<p>postid: {router.query.id}</p>
</>
);
};
useRouter훅을 이용해서 동적인 url 설정이 가능하다. 이때 파일의 이름은 pages/[router.query.id].jsx 로 설정해주면 된다.
production 배포
npm run build
npm run start
위의 코드로 localhost:3000 으로 접속시 배포된 버전을 로컬로 확인 가능하다.
4. Next.js 시작하기
npx create-next-app@latest
# or
yarn create next-app
//타입스크립트를 원한다면
npx create-next-app@latest --typescript
# or
yarn create next-app --typescript
위의 명령어로 개발환경 세팅하기
npm install next react react-dom
# or
yarn add next react react-dom
react-dom도 세팅하기
yarn dev
//or
npm run dev
로 개발환경에서의 실행이 가능하다
yarn start
//or
npm start
는 배포환경에서의 실행 명령어이다
5. 내가 겪었던 넥제를 처음시작할 때 이슈들
- 스타일드 컴포넌트를 사용할 때 바벨 설정이 필요하다. (몰랐음 ㅋ ㅋ )
우선 next.js의 첫 화면 렌더링은 pre-rendering으로 자바스크립트 코드가 적용이 되지 않은 페이지가 미리 렌더링되기 때문에 CSS-in-JS로 스타일링을 하면 스타일이 적용되지 않은 html 코드가 먼저 렌더링되는 문제가 발생하게 된다.
그런데 styled-components는 CSS-in-JS 인데...!! 어떡하지 하고 찾아봤는데
__document.jsx에 page폴더 내부에 존재하는 모든 페이지에 global한 설정 값을 줄 수 있는 코드를 삽입한다.
이 코드를 통해 html 파일에 CSS-in-JS 형식으로 작성된 스타일 요소들을 주입시켜서 스타일이 뒤늦게 적용되는 문제를 해결할 수 있다.
import Document, { DocumentContext, DocumentInitialProps } from "next/document";
import { ServerStyleSheet } from "styled-components";
export default class MyDocument extends Document {
static async getInitialProps(
ctx: DocumentContext
): Promise<DocumentInitialProps> {
const sheet = new ServerStyleSheet();
const originalRenderPage = ctx.renderPage;
try {
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) =>
sheet.collectStyles(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
styles: [
<>
{initialProps.styles}
{sheet.getStyleElement()}
</>,
],
};
} finally {
sheet.seal();
}
}
}
⚙️ 그리고 babel 설정을 해주면 된다. 바벨 설정을 해주는 이유는 styled-component로 만들어진 컴포넌트는 내부적으로 태그의 className 을 해시하여 만들어지게 되는데, 이때 server와 client에서 생성하는 class의 해시값이 달라 충돌이 발생할 수 있기 때문이다. ( 첫 렌더링 때 SSR로 페이지를 보여주고, 그 뒤에 CSR로 페이지를 동적으로 바꾸는데 여기서 className의 충돌이 생김)
// yarn add babel-plugin-styled-components로 설치
//.babelrc 파일을 루트경로에 생성 후
{
"presets": ["next/babel"],
"plugins": [
[
"styled-components",
{
"ssr": true,
"displayName": true,
"preprocess": true
}
]
]
}
- #__next
위의 페이지 제일 최상단의 __next라는 id를 가지고있는 div 태그를 내맘대로 스타일링할 수가 없음..
(파란색으로 표시된 부분이 현재 페이지의 최 상단 컴포넌트인데, 페이지 전체를 포함하고 있지 않아서 그냥 찝찝한채로 개발하는중..)
- 이미지 삽입을 도대체 어떻게 하냐?
이문제는 그렇게 어렵지 않았는데, 방법을 찾는데 시간이 오래 걸렸다. 그리고 이미지 스타일링 하는 방식이 너무 구리다 개빡침
Next.js는 Image컴포넌트를 이용해서 자동으로 이미지를 최적화해준다. 그래서 기존에 react + styled-components 에서 사용했던 대로
const ImageTag = styled.img``
방식을 사용하면 이미지 로드가 안된다.🤬
Next.js에서 이미지를 로드하는 코드는 아래와 같다. Next.js의 Image컴포넌트의 특징은 정적이미지 (로컬이미지)에 대해서는 width, height, blurDataURL 정보가 자동으로 생성된다는 점이다.
이 3가지 정보는 페이지가 100% 로드되지 않았을 때 이미지의 위치 가 변하는 현상 즉, Cummulative Layout Shift(CLS) 같은 상황이 발생할 때 아주 유용하다고 한다. 물론 커스텀도 가능하다.
import Image from 'next/image';
const Profile = () => {
return (
<>
<h1>User Profile</h1>
<Image
src={....}
alt="user profile picture"
/>
</>
)
}
// 인터넷의 이미지를 로드하는 것도 가능하다. 하지만 이때에는 무조건 width, height 속성을 입력해줘야한다.
//정적 이미지일 경우 Next.js가 build 할 때 width, height 속성을 계산할 수 있으나 원격 이미지(인터넷상의 이미지) 일 경우 width, height 속성을 계산하지 못하기 때문이다.
import Image from 'next/image';
const Profile = () => {
return (
<>
<h1>User Profile</h1>
<Image
src={https://unsplash.com/photos/...../.jpg}
alt="user profile picture"
width={300}
heifht={300}
/>
</>
)
}
- 폴더구조를 어떻게 하면 좋은건인가? (이건 답이 없음)
Next.js를 공부하면서 다른 프로젝트 폴더구조들을 많이 봤는데, page폴더 안에 각 파일들에 직접 UI와 기능을 구현하는 경우도 있고, components 폴더를 따로 만들어서 그 폴더 안에 컴포넌트들을 페이지 or 기능별로 때려박는 경우도 있더라.
components 폴더로 분리하는 컴포넌트들의 기준이 뭔지 잘 모르겠다. 도와줘 유진 재욱🙇♀️ - src폴더를 자동생성안해줌.
CRA 하면 개발할 때 사용하는 파일들을 src폴더에 자동으로 넣어주는데 CNA는 그딴거 자동으로 생성 안해줘서 나중에 파일 경로 변경하느라 애좀 먹었다. 이건 그낭 기억해두자구~