-
1) useCallback 을 사용하여 함수 재사용하기
- useCallback 은 useMemo 와 비슷한 Hook임
- useMemo 는 특정 결과값을 재사용 할 때 사용하고, useCallback 은 특정 함수를 새로 만들지 않고 재사용하고 싶을때 사용함
- 함수들은 컴포넌트가 리렌더링 될 때 마다 새로 만들어짐. 함수를 선언하는 것 자체는 사실 메모리도, CPU 도 리소스를 많이 차지 하는 작업은 아니기 때문에 함수를 새로 선언한다고 해서 그 자체 만으로 큰 부하가 생길일은 없지만, 한번 만든 함수를 필요할때만 새로 만들고 재사용하는 것은 여전히 중요함.
- 리액트 컴포넌트에서는 props 가 바뀌지 않았으면 Virtual DOM 에 새로 렌더링하는 것 조차 하지 않고 컴포넌트의 결과물을 재사용 하는 방식으로 최적화 작업이 이루어지는데, 이 과정이 제대로 이루어지려면 함수를 재사용 하는 과정이 필수적임.
- 주의!) 함수 안에서 사용하는 상태 혹은 props 가 있다면 꼭, deps 배열안에 포함시켜야 함. 만약에 deps 배열 안에 함수에서 사용하는 값을 넣지 않게 된다면, 함수 내에서 해당 값들을 참조할때 가장 최신 값을 참조 할 것이라고 보장 할 수 없음. props 로 받아온 함수가 있다면, 이 또한 deps 에 넣어주어야 함.
- 사실 useCallback 은 useMemo 를 기반으로 만들어졌음. 다만 함수를 위해서 사용 할 때 더욱 편하게 해준 것 뿐임
-
2) useMemo를 통해 특정 결과값 재사용하기
- 리액트 코드를 작성하다 보면 관련 없는 함수인데 호출되는 경우가 있음.
- 컴포넌트가 리렌더링 되면서 불필요하게 함수가 호출되고 자원이 낭비되는 것임.
- useMemo의 Memo 는 "memoized" 를 의미하는데, 이는, 이전에 계산 한 값을 재사용한다는 의미를 가짐
- useMemo 의 첫번째 파라미터에는 어떻게 연산할지 정의하는 함수를 넣어주면 되고, 두번째 파라미터에는 deps 배열을 넣어주면 됨.
- 이 배열 안에 넣은 내용이 바뀌면, 우리가 등록한 함수를 호출해서 값을 연산해주고, 만약에 내용이 바뀌지 않았다면 이전에 연산한 값을 재사용하게 됨.
-
3) useEffect 를 통해 컴포넌트가 마운트 됐을 때 (처음 나타났을 때), 언마운트 됐을 때 (사라질 때), 그리고 업데이트 될 때 (특정 props가 바뀔 때) 특정 작업을 처리하기
- useEffect 를 사용 할 때에는 첫번째 파라미터에는 함수, 두번째 파라미터에는 의존값이 들어있는 배열 (deps)을 넣음. 만약에 deps 배열을 비우게 된다면, 컴포넌트가 처음 나타날때에만 useEffect 에 등록한 함수가 호출됨.
- useEffect 에서는 함수를 반환 할 수 있는데 이를 cleanup 함수라고 부름. cleanup 함수는 useEffect 에 대한 뒷정리를 해주는 역할, deps 가 비어있는 경우에는 컴포넌트가 사라질 때 cleanup 함수가 호출됨.
- 마운트 시에 주로 하는 작업들은 다음과 같음
- props 로 받은 값을 컴포넌트의 로컬 상태로 설정
- 외부 API 요청 (REST API 등)
- 라이브러리 사용 (D3, Video.js 등...)
- setInterval 을 통한 반복작업 혹은 setTimeout 을 통한 작업 예약
- 언마운트 시에 하는 작업들은 다음과 같음
- setInterval, setTimeout 을 사용하여 등록한 작업들 clear 하기 (clearInterval, clearTimeout)
- 라이브러리 인스턴스 제거
- deps 파라미터를 생략한다면, 컴포넌트가 리렌더링 될 때마다 호출 됨.
-
4) hot reloading
- 개발 과정에서 저장되는 코드는 자동으로 새로고침되어 빠르게 확인 가능함.
-
5) automatic routing
- pages 폴더에 있는 파일은 해당 파일 이름으로 라우팅되어 따로 설정할 필요 없음.
- pages/sample.tsx -> localhost:3000/sample
- public 폴더도 pages의 폴더와 동일하게 라우팅 할 수 있지만, 모든 사람이 페이지에 접근할 수 있으므로 주의.
-
6) single file components
- style-jsx를 사용함으로 컴포넌트 내부에 해당 컴포넌트만 스코프를 가지는 css를 만들수 있음.
- style-jsx-global을 사용하면 글로벌로 스타일 정의 가능.
- _app.tsx 에만 정의 가능 → 다른 컴포넌트에 정의한 경우 다른 클래스와 겹쳐 오류를 발생할 수 있음.
-
7) 서버 사이드 렌더링
-
8) Code Splitting
- dynamic import를 이용하면 손쉽게 코드 스플리팅이 가능.
- Code Splitting이란
- 내가 원하는 페이지에서 원하는 자바스크립트와 라이브러리를 렌더링 하는 것.
- 모든 번들(chunk.js)이 하나로 묶이지 않고, 각각 나뉘어 좀 더 효율적으로 자바스크립트 로딩 시간을 개선할 수 있음.
-
9) Typescript
- 타입스크립트 활용을 위해 웹팩을 만지거나 바벨을 만질 필요 없음.
- 타입스크립트를 설치하고 (yarn add typescript @types/node @types/react) → 명령어 (yarn run dev)만 입력하면 → 자동으로 tsconfig, next-end.d.ts가 생성되며 타입스크립트로 코딩이 가능
-
10) _document.tsx 파일
- meta 태그를 정의하거나, 전체 페이지에 관려하는 컴포넌트.
- 여기에서의 console은 서버에서만 보이고 클라이언트에서는 보이지 않음.
- render 요소는 반영하지만 페이지 구성 요소만 반영되고 js는 반영 하지 않기 때문에 console은 보이지 않음.
- componentDidMount 같은 훅도 실행 되지 않음.
-
11) _app.tsx
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />;
}
export default MyApp;
- 이곳에서 렌더링 하는 값은 모든 페이지에 영향을 줌.
- 최초로 실행되는 파일임.
- 공통 레이아웃임으로 최초 실행되며 내부에 컴포넌트들을 실행함
- Component, pageProps를 받음.
- pageProps는 페이지 getInitialProps를 통해 내려 받은 props들임.
- 그 다음 _document.tsx가 실행.
- console.log 실행시 client, server 둘다 확인 가능.
-
12) SASS(Syntactically Awesome StyleSheets) 사용
- 따로 config 파일을 정의 하지 않고, css 파일을 scss로 바꾸고 yarn add sass --dev 명령어를 입력하면 알아서 설정해 줌.
- SASS 관하여
- CSS는 웹 콘텐츠를 위한 스타일시트 형식 언어
- 치명적인 단점이 존재함.
- CSS는 규모가 커질수록 코드가 복잡
- 유지보수가 불편
- CSS 코드 내에서 동일한 코드를 재사용하기 위해서 할 수 있는 유일한 조치는 '복사 & 붙여넣기' 뿐임
- 위의 이유들로 인해 사용하는 과정에서 무척 귀찮고, 짜증나고, 또 많은 실수를 유발함.
- Sass는 앞서 언급한 CSS의 치명적 단점을 보완할 수 있는 몇 가지 방법 중 단연 최선의 방법임
- 별도의 컴파일 과정을 통해 CSS 파일을 생성해 주는 CSS의 확장 언어이자 전처리기(preprocessor)임.
- CSS에는 존재하지 않는 다양한 기능들을 가지고 있음.
- 보유한 기능들이 코드 작성에 드는 시간을 줄여주고, 코드를 유지 관리하는 데 도움을 줌.
- Sass 관련 주의 사항
- Sass는 CSS의 대체 언어가 아니라는 점.
- 그냥 CSS의 확장 언어이고, 이는 결국 CSS 코드를 생산해내기 위해 사용하는 일종의 도구임.
- 스타일시트 생산 절차: Sass가 제공하는 문법을 기반으로 작성된 코드를 별도의 컴파일 과정을 통해 CSS 파일로 빌드함.
- Sass의 특징
- 호환성: 모든 버전의 CSS와 완벽하게 호환된다.
- 기능성: 다양한 기능을 제공하고, 거의 모든 면에서 뛰어나다.
- 안정성: 오랜 기간 적극적인 지원 아래 관리되어 왔다.
- 인지도: 업계에서 인정받고 있으며, 많이 사용되고 있다.
- 신뢰도: 거대 커뮤니티의 지원 아래 개발되고 있다.
- 확장성: Sass 기반의 프레임워크가 다수 존재한다.
- 결국 Sass는 생산성을 위한 것임. 코드 작성을 빠른 시간 안에 간결하게 할 수 있도록 해 주고, 코드 수정 시에 신경 써야 할 부분을 최소화해주는 효과 있음.
- Sass와 SCSS
- SASS 는 SASS 표기법(.sass)과 SCSS(.scss) 표기법이 존재하는데, Sass 3.0 부터 기본 표기법으로 채택된 SCSS (Sassy CSS) 사용을 권장하고 있음.
-
13) Link 사용하기
import Link from "next/link";
const Index = () => (
<div>
<Link href="/blog">
<a>Sample</a>
</Link>
// 동적 link시에는 []를 사용함.
<Link href="/blog/[address]">
<a>Sample</a>
</Link>
</div>
);
- 보통의 경우 페이지간 이동은 a 태그를 사용하나 next에서는 사용하지 않음.
- a 태그를 사용할 경우, 처음 페이지에 진입시 번들 파일을 받고 a 태그에 의해 라우팅 되면 다시 번들 파일을 받기 때문.
- 또한 자바스크립트 상태 관리 라이브러리인 redux을 쓰시는 경우 store의 state 값이 증발되는 현상도 발생.
- 그렇기 때문에 a 태그는 전혀 다른 사이트로 페이지를 이동시켜 다시 돌아오지 않는 경우만 사용 → 그 이외에는 모두 Link 태그를 사용.
-
14) 동적 URL (dynamic route)
// pages/[id].tsx
import { useRouter } from "next/router";
export default () => {
const router = useRouter();
return (
<>
<h1>post</h1>
<p>postid: {router.query.id}</p>
</>
);
};
- 가변적으로 변하는 url에 대해 동적 url을 지원.
- [] 문법으로 동적 페이지를 생성하는 동적 url을 만들 수 있음.
- 위 예시 코드에서 pages/[id].tsx 구조의 id 값은 router.query.id 값과 동일함.
- dynamic route를 사용하고 싶지 않을 경우, dynamic page를 optional하게 주는 문법도 사용 가능 (Optional catch all routes)
-
15) prefetching
- 백그라운드 단에서 페이지를 미리 가져옴.
- 기본값은 true임.
- 뷰포트에있는 모든 항목들이 미리 로드됨.
- 정적 생성을 사용하는 JSON페이지는 더 빠른 페이지 전환을 위해 데이터가 포함 된 파일을 미리 로드함.
- 사실 이건 Link 컴포넌트를 사용해서 이뤄지는 것임
- 링크 컴포넌트를 렌더링할때 형식으로 prefetch 값을 전달해주면 데이터를 먼저 불러온다음에 라우팅을 시작함.
- 프로덕션 레벨에서만 이루어지는 점 개발 과정에서 주의!
-
16) next/router 사용법
import { useEffect } from "react";
import { useRouter } from "next/router";
import posts from "../posts.json";
export default () => {
const router = useRouter();
const post = posts[router.query.id as string];
if (!post) return <p>nothing to post!</p>;
useEffect(() => {
router.prefetch("/test");
}, []);
return (
<>
<h1>{post.title}</h1>
<h1>{post.content}</h1>
<button onClick={() => router.push("test")}>go to Test page!</button>
</>
);
};
- React로 생성된 SPA 내부에서 페이지 이동이 가능하도록 만들어주는 라이브러리인 react-router-dom과 사용 방법은 거의 유사함.
- link에 있는 preferch 기능도 사용 가능.
-
17) 컴포넌트에 데이터 보내주는 getInitialProps() (+ getStaticProps, getStaticPaths, getServerSideProps)
-
서버사이드 렌더링을 하는 nextJs에서 컴포넌트는 각 페이지마다 사전에 불러와야할 데이터가 있음
-
react, vue같은 Client Side Rendering (CSR)의 경우는 data fetching 과정이 useEffect 등을 통해 이루어짐.
-
서버 사이드에서 실행하는 next에서는 getInitialProps를 이용하여 data fetching 작업이 이루어짐.
-
단, next v9 이상에서는 getInitialProps 대신 getStaticProps, getStaticPaths, getServerSideProps을 사용하도록 가이드 함!
-
getStaticProps
function Blog({ posts }) {
return (
<ul>
{posts.map(post => (
<li>{post.title}</li>
))}
</ul>
);
}
export async function getStaticProps() {
const res = await fetch("https://.../posts");
const posts = await res.json();
return {
props: {
posts
}
};
}
export default Blog;
- docs 예제에서는 fetch를 통해 게시물을 가져오고 그 게시물의 title을 보여줌.
- 빌스시 고정되는 값들임 → 빌드 이후 값 변경 불가능.
-
getStaticPaths
export async function getStaticPaths() {
return {
paths: [
{ params: { dynamic: 1 } },
{ params: { dynmic: 2 } }
......
{ params: { dynmic: 동적인값 } }
],
fallback: true,
}
}
- 빌드 시 사전에 렌더링할 경로를 미리 설정함.
- 원래는 이곳에 정의하지 않은 하위 경로는 접근해도 페이지가 뜨지 않음.
- 빌드 타임 때 사전에 정의한 /dyna/1, /dyna/2, ... /dyna/동적인값 경로만 미리 렌더링 됨.
- 만들어지지 않은 경로도 추후 요청이 들어오면 만들어 줄 지 여부를 fallback으로 설정.
-
getServerSideProps
function Page({ data }) {
}
export const getServerSideProps: GetServerSideProps = async context => {
const res = await fetch(`https://.../data`);
const data = await res.json();
return { props: { data } };
};
export default Page;
- 각 요청에 따라 서버로부터 데이터를 가져옴.
- 클라이언트 측에서 데이터를 가져오는 경우에 활용
- 페이지에 자주 업데이트되는 데이터가 포함되어 있고 데이터를 미리 렌더링 할 필요가없는 경우 클라이언트 측에서 데이터를 가져올 수 있음.
- 사용자별 데이터가 이에 해당할 것임.
- 작동 방식: 먼저 데이터가 없는 페이지를 즉시 표시→ 페이지의 일부는 정적 생성을 사용하여 미리 렌더링 할 수 있음 → 누락 된 데이터에 대한 로드 상태를 표시함. → 클라이언트 측에서 데이터를 가져와 준비가 되면 표시
- 이러한 접근 방식은 사용자 대시 보드 페이지에 적합함. 대시 보드는 비공개 사용자 별 페이지이기 때문에 SEO는 관련이 없으며 페이지를 미리 렌더링 할 필요가 없음. 데이터는 자주 업데이트되므로 요청 시간 데이터 가져 오기가 필요함.
-
18) server side lifeCycle
- nextJs 서버가 GET 요청을 받는다.
- GET 요청에 맞는 pages/Component를 찾는다.
- _app.tsx의 getInitialProps가 있다면 실행한다.
- route에 맞는 페이지의 Component의 getInitialProps가 있다면 실행한다. pageProps들을 받아온다.
- _document.tsx의 getInitialProps가 있다면 실행한다. pageProps들을 받아온다.
- 모든 props들을 구성하고, _app.tsx -> page Component 순서로 렌더링
- 모든 Content를 구성하고 _document.tsx를 실행하여 html 형태로 출력한다
-
19) custom 태그
import Head from "next/head";
export default () => (
<div>
<Head>
<title>new title!</title>
</Head>
<div>...</div>
</div>
);
- 페이지 제목이나 메타 태그를 커스텀 및 변경하고 싶을때 커스텀 태그 활용.
- next/head로 부터 Head 컴포넌트를 받아 모든 컴포넌트에서 사용할 수 있음.
- next.js가 해당 컴포넌트를 mount 할때, Head내 태그들을 페이지의 HTML의 Head에 포함 시킴. 마찬가지로 unMount 시에는 해당 태그를 제거함.
-
20) dynamic component import
import React, { useState } from "react";
import dynamic from "next/dynamic";
const DynamicComponent = dynamic<{ nowTab: number }>(() =>
import("./DynamicComponent")
);
const Index = () => {
const [nowTab, setNowTab] = useState(0);
return (
<>
{nowTab === 0 && <div>0 tab</div>}
{nowTab === 1 && <DynamicComponent nowTab={nowTab} />}
</>
);
};
- dynamic component import는 react에서 lazy하게(지연시켰다가) component를 import 해오는 방식과 유사함.
- 처음에 보여주지 않아도 되는 컴포넌트들을 dynamic하게 처리하여 맨 처음에 import 하지 않게 됨 → 초기 화면 렌더링 속도를 상승시킴.
- 주의) 페이지에 진입할때 맨처음 보이는 컴포넌트라면 dynamic 메소드 사용 금지!
-
21) production 배포
- npm run build
- npm run start
-
22) material-ui 또한 어렵지 않게 사용 가능.