Lucian Log
Blog
Computer Science
Algorithm
DB
Network
OS
General
AI
Blockchain
Concurrency
ETC
Git
Infrastructure
AWS
Docker
Java-Ecosystem
JPA
Java
Spring
JavaScript-Ecosystem
JavaScript
Next.js
React
TypeScript
Python-Ecosystem
Django
FastAPI
Python
SQLAlchemy
Software Engineering
Architecture
Culture
Test
Home
Contact
Copyright © 2024 |
Yankos
Home
>
JavaScript-Ecosystem
> Next.js
Now Loading ...
Next.js
Next.js basic - 개념 조각 모음
Next.js 주요한 특징들 Static Generation VS Sever-side Rendering VS Client-side Rendering Static Generation HTML이 build time에 pre-rendering되는 방식입니다. 즉, 외부에서 가져오는 데이터들도 build time에 요청하기 때문에 최신 데이터보다는 잘 변하지 않는 데이터들을 처리하기에 적합합니다. 미리 HTML을 생성하기 때문에 SEO에 강점이 있습니다. 또한 미리 한 번 생성된 HTML을 재사용하는 것과 더불어 Sever-side Rendering과 달리 CDN에 캐시되는 덕분에 셋 중에 속도가 가장 빠르며, Next.js에서 가장 권장되는 방법입니다. Sever-side Rendering HTML이 유저로부터 request가 있을 때마다 pre-rendering되는 방식입니다. 즉, 외부에서 가져오는 데이터들이 request 시점에 요청된 데이터들이기 때문에, 최신 데이터들을 사용하기 용이하다는 장점이 있습니다. HTML이 pre-rendering되기 때문에, SEO에 강점이 있으며, Static Generation보다는 느리지만 Client-side Rendering 보다는 빠릅니다. 다만, 사용자 측면에서는 페이지 이동마다 화면이 깜빡거리며 새로고침이 발생하게 됩니다. Client-side Rendering HTML의 pre-rendering 및 외부 데이터 API 요청을 하지 않고, 클라이언트 측에서 자바스크립트 코드로 모든 것을 처리하는 방식입니다. 사용자가 요청한 페이지만 불러온 후, 사용자의 행동에 따라 필요한 부분만 다시 읽어 들이는 single page application 방식으로 동작하게 됩니다. 따라서, 사용자 측면에서 리로딩없이 필요한 부분만 빠르게 인터랙션할 수 있습니다. 다만, 초기 구동 속도가 느리고 SEO가 어렵다는 단점이 있습니다. (구글에서는 Client-side Rendering도 SEO를 잘 할 수 있다고 이야기하지만, Client-side Rendering은 SEO가 잘 안된다는 것이 정설입니다.) 위의 렌더링 방식들은 페이지마다 다르게 적용할 수 있고, 한 페이지 안에서도 부분마다 다르게 적용할 수 있습니다. 예를 들어, 보통 SEO가 가장 잘되어야 하는 부분은 상품 정보 페이지이므로 해당 페이지는 Static Generation이나 Sever-side Rendering으로 처리하는 것이 좋습니다. 또한, 상품 정보 페이지 내에서 title 같은 정보는 잘 변하지 않으므로 Static Generation을 사용하는 것이 좋습니다. 반면에, description이나 keyword 같은 부분들은 A/B Test 등으로 자주 변화를 시도해 볼 수 있기 때문에, Sever-side Rendering을 사용하는 것이 적합합니다. 이외의 데이터와 상관없는 navigation bar나 메뉴 같은 부분들은 Client-side Rendering을 적용해 보다 나은 인터랙션을 제공할 수 있습니다. Static file serving Next.js는 static 파일을 public 디렉토리에서 처리합니다. 그리고 public 폴더 안에 있는 static file들은 base URL을 /로 사용할 수 있습니다. 예를 들어, /public/me.jpg는 /me.jpg로 사용하면 됩니다. Public 디렉토리에 있는 파일들은 빌드 타임에만 서빙되므로, 런타임에 저장되는 파일들은 AWS S3 같은 다른 서드 파티 서비스를 사용해 처리하길 권장합니다. Reference Next.js Document
JavaScript-Ecosystem
· 2021-10-14
Next.js basic - Pre-rendering
Pre-rendering Pre-rendering은 Next.js의 중요한 특징 중 하나입니다. Next.js는 클라이언트에서 HTML 생성을 모두 처리하기 보다는, 처음에 모든 페이지에 대한 HTML을 미리 생성하는데, 이것을 pre-rendering이라고 합니다. Pre-rendering 덕분에 Next.js는 SEO와 더불어 좋은 성능을 보입니다. Pre-rendering 이후에는 hydration이라는 과정을 거칩니다. Hydration이란 브라우저가 페이지를 로딩할 때, 해당 페이지를 로딩하기 위해 필요한 최소한의 자바스크립트 코드만을 가져와 실행시켜서 미리 생성되어 있는 HTML을 interactive하게 만드는 과정을 말합니다. 만일 순수 리액트 코드로 작성된 애플리케이션의 경우, pre-rendering이 없기 때문에 페이지들의 HTML을 미리 생성하지 않습니다. 그래서 만일 순수 리액트 코드로 이루어진 애플리케이션의 자바스크립트 코드를 disabled 상태로 만든다면, 페이지 자체가 보이지 않게 됩니다. 반면, Next.js가 적용된 애플리케이션은 static HTML이 미리 생성된 덕분에 자바스크립트 기능을 제외한 페이지 자체는 보이게 됩니다. Two forms of pre-rendering Next.js의 pre-rendering은 Static Generation과 Server-side Rendering이라는 두 가지 형태가 존재합니다. 두 형태의 차이점은 언제 페이지에 대한 HTML이 생성되는가에 있습니다. 먼저, Static Generation은 build-time에 HTML을 생성하는 pre-rendering method입니다. 즉, 클라이언트의 request 이전에 HTML이 생성됩니다. 이렇게 pre-rendering된 HTML은 각각의 request에 요청될 때마다 재사용됩니다. Static Generation은 request에 상관없이 내용이 자주 바뀌지 않는 marketing page, blog post, E-commerce product listing, documentation 등에 유용합니다 이와 달리, Server-side Rendering은 각각의 request가 올 때마다 HTML을 생성하는 pre-rendering method입니다. 즉, 클라이언트의 request 후에 HTML이 생성되며, 생성된 HTML은 재사용되지 않습니다. Server-side Rendering은 빈번히 update되는 데이터 혹은 request마다 content가 바뀜으로 인해, request 이전에 pre-render하기 어려운 상황에서 유용합니다. 참고로, 개발자 모드로 서버를 실행했을 때는 모든 페이지가 Server-side Rendering으로 pre-rendering됩니다. 심지어 Static Generation을 사용하는 페이지라고 하더라도 마찬가지입니다. Next.js는 각각의 페이지마다 위의 두 가지 형태 중 어떤 pre-rendering을 사용할지 선택할 수 있습니다. 따라서, Static Generation과 Server-side Rendering 방식이 혼합된 Next.js 애플리케이션을 만들 수 있습니다. 다만, 대부분의 경우에서는 Static Generation이 권장됩니다. 매 request 마다 HTML을 생성해야 하는 Server-side rendering에 비해, 한 번 HTML을 생성하고 재사용하는 Static Generation이 훨씬 빠르기 때문입니다. 만일 항상 최신 상태를 유지해야 하는 데이터를 처리할 경우, 느림을 감안하고서라도 Server-side Rendering을 사용하거나 pre-rendering을 생략하고 Client-side Rendering을 사용하는 것이 적합합니다. Static Generation with and without data Static Generation은 외부 데이터가 필요할 때 혹은 필요하지 않을 때 모두 사용할 수 있습니다. 외부적으로 데이터를 가져오지 않아도 되는 페이지들은 자동으로 Static Generation될 것입니다. 이와 달리, 처음에 반드시 데이터를 가져와야 하는 페이지의 경우, 빌드 시간에 파일 시스템에 접근하거나 외부 API 혹은 데이터베이스 등에 request를 해야만 합니다. 이러한 요청은 getStaticProps을 사용해 진행합니다. export default function Home(props) { ... } export async function getStaticProps() { // Get external data from the file system, API, DB, etc. const data = ... // The value of the `props` key will be // passed to the `Home` component return { props: ... } } 비동기 함수 getStaticProps 안에서 실행하는 모든 것들은 빌드 타임에 진행되고, 요청을 통해 응답받은 데이터는 props의 형태로 데이터가 필요한 페이지 컴포넌트에 전달할 수 있습니다. 또한, getStaticProps 함수 내에 원하는 로직을 완성했다면, 페이지 컴포넌트를 export한 것과 마찬가지로 getStaticProps 함수도 export해주는 것을 유의해야 합니다. getStaticProps에 대한 몇 가지 유의할 점 getStaticProps 함수는 항상 server-side에서 실행됩니다. 즉, 브라우저에서 실행될 염려가 없기 때문에, 필요한 데이터를 가져오기 위해 데이터베이스에 쿼리를 날리는 것 역시 문제가 되지 않습니다. 개발자 모드로 서버를 실행했다면, getStaticProps는 request가 있을 때마다 실행되는 Sever-side Rendering 방식으로 동작합니다. (npm run dev, yarn dev) 반면에, production용으로 서버가 실행되었다면, getStaticProps 함수는 원래 의도대로 빌드 시간에 실행됩니다. getStaticProps 함수는 항상 page 파일에서 export되어야 합니다. Non-page 파일에서 export 되어서는 안됩니다. Server-side Rendering 만일 Server-side Rendering을 하고 싶다면, getServerProps를 사용합니다. CDN에 캐시되지 않아 getStaticProps보다는 느리겠지만, 최신의 정보를 request 때마다 가져올 수 있습니다. export async function getServerSideProps(context) { return { props: { // props for your component } } } context 매개변수를 사용하면, request와 관련된 매개변수들을 다룰 수 있습니다. Client-side Rendering 만일 데이터 pre-rendering을 생략하고 싶다면, Client-side Rendering을 사용합니다. 페이지에서 외부적으로 데이터를 필요로하지 않는 부분만 Static Generation하고, 페이지의 나머지 부분은 client에서 자바스크립트를 사용해 데이터를 가져오면서 로딩할 수 있습니다. (Static Generation without data + Fetch data on the Client-Side) 만일 client-side에서 데이터를 fetching하고 싶다면, Next.js에서 제공하는 리액트 훅 SWR을 사용할 것을 권장합니다. 다음은 SWR의 예시입니다. import useSWR from 'swr' function Profile() { const { data, error } = useSWR('/api/user', fetch) if (error) return <div>failed to load</div> if (!data) return <div>loading...</div> return <div>hello {data.name}!</div> } Reference Next.js Document
JavaScript-Ecosystem
· 2021-10-13
Next.js basic - Asset, Metadata and CSS
CSS, assets and metadata Next.js에서는 CSS를 어떻게 적용하여 스타일링할 수 있을까요? 그리고 이미지와 같은 정적 파일들과 <title>과 같은 페이지 내 메타 데이터들은 Next.js에서 어떻게 다뤄야 할까요? Asset with <Image> and image optimization 이미지와 같은 정적 파일들은 public 디렉토리에 위치시킵니다. Next.js는 public에 있는 파일들을 자동으로 참조합니다. 이미지를 저장하기 위해 public 디렉토리에 images 디렉토리를 생성하고, 그 안에 원하는 이미지를 저장하세요. (예를 들어, 프로필 사진을 사용하기 위해 profile.jpg를 저장해보세요.) import Image from 'next/image' const YourComponent = () => ( <Image src="/images/profile.jpg" // Route of the image file height={144} // Desired size with correct aspect ratio width={144} // Desired size with correct aspect ratio alt="Your Name" /> ) 저장한 이미지는 Image 컴포넌트를 next/image에서 임포트해 사용합니다. height와 width 속성을 사용해 이미지의 렌더링 사이즈를 지정해주고, src로 이미지의 위치를 설정해줍니다. 기존 HTML <img> 태그는 브라우저의 화면 크기가 바뀔 때마다 변화에 대한 이미지의 resizing을 지원하지 않습니다. 반면에, Next.js의 <Image> 컴포넌트를 사용하면, 해당 이미지의 resizing을 자동으로 지원해줍니다. 또한, <Image> 컴포넌트는 이미지의 포멧도 브라우저에서 WepP와 더 나은 이미지 포멧을 지원한다면, 자동으로 포멧을 변환해서 이미지 파일을 optimization해줍니다. 뿐만 아니라, 애플리케이션의 빌드 타임에서 이미지를 로딩하는 대신, 이미지가 viewport에 나올 때 비로소 lazy-loading하여, 페이지 전체 로딩 시간을 원활히 합니다. Metadata 페이지의 메타 데이터를 변경하고 싶다면, Next.js의 <Head> 컴포넌트를 사용합니다. HTML <head>와는 달리, <Head>는 리액트 컴포넌트입니다. import Head from 'next/head' <Head> 컴포넌트는 'next/head'에서 임포트합니다. export default function FirstPost() { return ( <> <Head> <title>First Post</title> </Head> <h1>First Post</h1> <h2> <Link href="/"> <a>Back to home</a> </Link> </h2> </> ) } 그리고 원하는 메타 데이터를 <Head> 컴포넌트 안에서 설정해줍니다. 위 코드는 페이지의 <title> 속성을 변경했습니다. 개발자 도구에서 해당 페이지의 HTML 문서를 확인해보면, 실제로 <head>에 <title> 태그가 추가되어 있는 것을 볼 수 있습니다. CSS styling <style jsx>{` … `}</style> Next.js에서 CSS는 <style jsx> 태그에 작성하면 됩니다. <style jsx>는 styled-jsx 라이브러리를 사용해 지원되는 것이며, Next.js는 built-in으로 제공됩니다. CSS와 Sass 역시 마찬가지로 built-in으로 지원됩니다. Layout component & CSS module CSS 스타일을 적용하기 위해, Layout 컴포넌트와 CSS module을 사용해봅시다. 먼저 최상위 디렉토리에 components 디렉토리를 하나 생성합니다. export default function Layout({ children }) { return <div>{children}</div> } 그리고 components/layout.js를 생성하여 위와 같은 Layout 컴포넌트를 작성합니다. 이 Layout 컴포넌트는 모든 페이지에 걸쳐 사용될 것입니다. import Head from 'next/head' import Link from 'next/link' import Layout from '../../components/layout' export default function FirstPost() { return ( <Layout> <Head> <title>First Post</title> </Head> <h1>First Post</h1> <h2> <Link href="/"> <a>Back to home</a> </Link> </h2> </Layout> ) } 그리고 CSS를 추가하고 싶은 페이지에 <Layout> 컴포넌트를 감싸서 적용해줍니다. .container { max-width: 36rem; padding: 0 1rem; margin: 3rem auto 6rem; } <Layout>에 적용해줄 CSS는 CSS Module을 사용해 생성합니다. CSS Module은 CSS 파일을 임포트해 리액트 컴포넌트에서 사용하는 것을 도와줄 것입니다. components/layout.module.css 파일을 생성하여, 위와 같이 원하는 CSS 코드를 작성합니다. 특히, CSS Modules를 사용하기 위해서는 생성한 CSS 파일의 이름이 반드시 .module.css로 끝나야함을 유의합니다. import styles from './layout.module.css' export default function Layout({ children }) { return <div className={styles.container}>{children}</div> } 끝으로, Layout 컴포넌트에 CSS를 적용합니다. layout.module.css 파일을 임의의 이름에 임포트해 사용합니다. 여기서는 styles를 사용합니다. 그리고 Layout 내에서 className 속성을 사용해 styles.container를 적용합니다. 이 후, http://localhost:3000/posts/first-post 페이지에 들어가보면, CSS가 잘 적용된 것을 확인할 수 있습니다. Unique class name의 자동 생성 CSS가 적용된 해당 페이지에서 개발자 도구를 열어 HTML 문서를 확인해보면, Layout 컴포넌트로 인해 렌더링된 다음과 같은 class name으로 새로운 <div>가 생성되어 있는 것을 볼 수 있습니다. <div class="layout_container__2t4v2"> 이는 CSS Module이 자동으로 생성한 고유한 class name입니다. 뒷 부분의 고유 문자열 덕분에 class name이 충돌할 여지는 없습니다. 또한, Next.js의 code splitting은 CSS Module에서도 적용되어, 현재 페이지가 로딩될 때 필요한 최소한의 CSS만 함께 로딩되게 됩니다. Global CSS 만일 모든 페이지에서 항상 적용 및 로딩되는 CSS를 원한다면, pages/_app.js 파일을 생성하고 _app.js 파일 내부에서 해당 CSS 파일을 임포트하면 됩니다. export default function App({ Component, pageProps }) { return <Component {...pageProps} /> } 먼저, pages/_app.js 파일을 생성하고 파일 내부에 위 컴포넌트를 작성합니다. App 컴포넌트는 가장 최상위 컴포넌트로서 모든 페이지에 영향을 줍니다. 특히, 페이지들 간의 이동이 있을 때, App 컴포넌트에 state을 저장해두면 유용합니다. 그리고 npm run dev로 서버를 다시 실행해줍니다. _app.js를 추가했을 때는 항상 서버를 다시 실행해줘야 변경사항이 저장됨을 유의합니다! html, body { padding: 0; margin: 0; font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; line-height: 1.6; font-size: 18px; } * { box-sizing: border-box; } a { color: #0070f3; text-decoration: none; } a:hover { text-decoration: underline; } img { max-width: 100%; display: block; } 그리고 최상위 디렉토리 밑에 styles 디렉토리를 하나 만들어, 위와 같이 원하는 CSS 코드를 styles/global.css로 파일을 생성해 저장합니다. import '../styles/global.css' export default function App({ Component, pageProps }) { return <Component {...pageProps} /> } 그리고 pages/_app.js에서 global.css를 임포트해주면, 페이지를 이동해도 global.css의 내용이 항상 적용되는 것을 확인할 수 있습니다. 여기서 주의할 점은 global.css는 항상 _app.js 내에서 임포트해줘야 한다는 것입니다. global.css는 항상 모든 페이지에 영향을 주어야 하기 때문입니다. Reference Next.js Document
JavaScript-Ecosystem
· 2021-10-12
Next.js basic - Pages
페이지 이동 구현 방법 Next.js를 사용해 여러 개의 페이지를 만들고 이동하는 방법을 소개합니다. Next.js는 code splitting, client-side navigation, prefetching 등을 통해, 자동으로 애플리케이션의 성능을 best performance로 최적화합니다. 페이지 만들기 먼저 새로운 페이지를 만들어봅시다. Next.js에는 pages 디렉토리가 존재합니다. 해당 디렉토리에 원하는 URL로 js 파일을 생성하면, 쉽게 새로운 페이지를 만들 수 있습니다. 예를 들어, pages/posts/first-post.js라는 경로로 새로운 페이지를 만들었다면, 해당 페이지의 URL은 /posts/first-post이 됩니다. export default function FirstPost() { return <h1>First Post</h1> } 그리고 위와 같이 컴포넌트를 만들고 서버를 실행하면, http://localhost:3000/posts/first-post에 해당 페이지가 뜨게 됩니다. 이 때, 컴포넌트는 항상 default export가 되어야 함을 유의합니다. 이러한 방식은 HTML과 PHP를 사용하여 웹사이트를 구축하는 방식과 비슷하지만, HTML 대신에 JSX와 React component를 사용했다는 점이 다릅니다. 이제 남은 것은 홈페이지에 새로운 페이지로 가는 링크만 걸어주는 것입니다! Link component Next.js에서 페이지의 링크를 걸어주는 것은 <Link> 컴포넌트를 사용해서 수행합니다. import Link from 'next/link' 이를 위해, 먼저 'next/link'로부터 Link 컴포넌트를 import합니다. <h1 className="title"> Read{' '} <Link href="/posts/first-post"> <a>this page!</a> </Link> </h1> 그리고 index.js에서 위와 같이 코드를 작성하면, 새로 만든 페이지의 URL /posts/first-post로 이동하는 링크를 만들 수 있습니다. 여기서 <Link>가 <a> 태그를 감쌌다는 점, href 속성은 <Link> 태그에 주었다는 점을 유의합니다. 참고로, {' '}은 multiple line text를 나누기 위해 사용됩니다. import Link from 'next/link' export default function FirstPost() { return ( <> <h1>First Post</h1> <h2> <Link href="/"> <a>Back to home</a> </Link> </h2> </> ) } 앞선 pages/posts/first-post.js에도 위와 같이 홈으로 돌아가는 링크를 만들면, 페이지끼리 서로 이동할 수 있게 됩니다. Link and client-side navigation <Link>의 사용은 client-side navigation을 가능하게 합니다. 즉, 페이지 전환이 클라이언트 측에서 자바스크립트를 이용해 일어나기 때문에, 페이지의 모든 부분을 서버에서부터 새로 가져와 로딩하는 브라우저 기본 navigation 방식보다 훨씬 빠르게 동작합니다. 만일 <Link>가 아닌 <a> 태그를 사용했다면, 브라우저는 해당 링크에 접근할 때마다 페이지 전체를 refresh할 것입니다. Code splitting Next.js에서는 code splitting이 자동적으로 일어나므로, 페이지의 로딩도 해당 페이지에 반드시 필요한 것들만 로딩됩니다. 예를 들어, 홈페이지가 렌더링될 때는 다른 페이지들은 로딩되지 않습니다. 특히, 애플리케이션에 수 많은 페이지가 있을 때, 유저는 자신이 요청한 페이지를 보다 빠르게 볼 수 있게 됩니다. 즉, 페이지들의 코드는 각각 분리되어 있고, 어떤 특정 페이지가 오류를 일으켜도 애플리케이션의 나머지 부분은 문제없이 동작합니다. Prefetching 브라우저의 viewport(메뉴 바, 탭 영역을 제외한 브라우저의 순수 화면 영역)에 <Link> 컴포넌트가 있을 때마다, Next.js는 <Link>에 연결된 페이지들을 자동으로 미리 로딩해둡니다. 이를 prefetching이라고 하며, 이러한 페이지들은 유저가 링크를 누를 때 background에서 이미 로딩되어 있어서 매우 빠르게 페이지가 전환됩니다. Reference Next.js Document
JavaScript-Ecosystem
· 2021-10-11
<
>
Touch background to close