노마드코더 - Next.js 시작하기 강의를 참고로 하여 정리했다.
강의와는 달리 next.js 13버전 + 타입스크립트로 작성하며 공부했다.
(버전 차이, 타입스크립트 코드 등 강의 내용과 다른 부분은 추가적으로 공식문서를 참고해 코드 변경함)
npx create-next-app@latest
npx create-next-app@latest --typescript
프로젝트 명 입력해주고 설치 기다리면..
위와같이 프로젝트 생성 완료!
vscode에서 해당 폴더를 열어보자
npm run dev
ex) react는 개발자가 원하는대로 폴더구조 세팅하고, 코드 작성할 수 있음.
ex) 다 지어져있는 집, 개발자와 코드는 게스트와 같은 존재
ex) next.js는 특정 규칙을 따라야 정상동작함. ReactDOM.render 없음. -> Pages 내에서 무언가 만드는 것만 할 수 있다.
export default function Home() {
return "hi";
}
http://localhost:3000로 접속해보면, 어떤 설정이나 router 설정 없이도 자동적으로 hi를 확인할 수 있음.
about.ts 만들고 안에 코드 작성하면, /about 으로 이동하면 작성한 내용 확인할 수 있음.
export default function Home() {
return "about";
}
http://localhost:3000/about로 접속하면, about을 확인할 수 있음.
react 컴포넌트를 export하는 파일들을 pages 폴더 안에 넣어주면, 파일 이름으로 url을 만들어 자동으로 라우팅 해준다.
파일 이름이 url이 됨.
export default function ~ 형태로 컴포넌트 작성해야함.
404 페이지도 자동으로 만들어져 있음.(커스터마이징 가능)
index.ts는 예외적으로 홈페이지로 연결됨.(앱의 Home, url 그냥 /)
import react from "react"; 할 필요 없음!
CSR(Client-Side Rendering)
CRA(create-react-app)로 생성한 프로젝트는 모두 클라이언트 사이드 렌더링이다.
브라우저가 자바스크립트 코드 가져와서 모든 UI를 빌드해서 보여줌.(유저가 볼 수 있는 상태)
사용자는 처음에 비어있는 HTML을 보게됨. 이후 브라우저가 JS 파일 로드-실행해서 화면을 그려줌.
- 만약 브라우저에서 자바스크립트를 비활성화 했을때, noscript 태그가 있었다면 <noscript>자바스크립트가 활성화 되지 않았습니다.</noscript>
라고 보여질것.
브라우저가 JS 파일을 통해 UI를 그려내는 작업은 비용이 크고 오래걸리는 작업이다.
- Networt 탭에서 Slow 3G 연결로 설정하고 새로고침하면 사용자는 처음에 빈 화면 보게됨, 시간이 지나야 페이지 출력됨
Server Side 단에서 Pre-Rendering된 HTML 페이지와 번들링된 JS파일을 클라이언트에게 보낸 뒤, 클라이언트 단에서 HTML 코드와 React인 JS코드를 서로 매칭 시키는 과정
자바스크립트 코드들이 DOM 요소 위에 물을 채우듯 필요로 하던 요소들을 채운다 하여 Hydrate(수화)라는 용어를 쓴다고 한다.
브라우저가 자바스크립트를 비활성화하면 초기 html은 그려져 있지만, 동적으로 동작하지는 않음
import { useState } from "react";
export default function Home() {
const [counter, setCounter] = useState(0);
return (
<div>
<h1>Hello {counter}</h1>
<button onClick={() => setCounter((prev) => prev + 1)}>+</button>
</div>
);
}
작성한 HTML 코드들이 초기에 존재하는 것을 확인할 수 있음.
next.js는 react.js를 백엔드에서 동작시켜 첫 페이지를 미리 렌더링함. -> HTML (초기 상태 pre-rendering)
유저는 js코드가 존재하지 않아도 HTML 볼 수 있음. 이후 js코드가 로딩되었을때, 일반적으로 동작하는 react 앱이 되는 것!
유저는 웹사이트 접속했을때 미리 생성된 초기 HTML 페이지를 먼저 보게된다.
자바스크립트 비활성화 하면 코드 동작하지 않음, 하지만 유저가 무엇인가는 볼수 있게 됨!
(사용자가 빈페이지 보는것 보다는 훨씬 낫다!)
SEO에 매우 좋음
보통 페이지간 이동은 a 태그를 사용하지만, next.js에서는 사용하지 않는다.
a 태그를 사용하면 처음 페이지 진입시 번들파일을 받고, a태그에 의해 라우팅 되면서 다시 번들파일을 받기 때문이다. 또 redux를 사용하는 경우에는 store의 state 값이 증발되는 현상도 발생한다 한다.
그렇기 때문에 next에서 페이지간 이동에는 모두 Link
태그를 사용한다.
<Link href="/">
<a>Home</a>
</Link>
노마드코더 강의에서는 <Link>
태그 안에 자식태그로 <a>
를 넣어 위와 같이 사용했는데, 아래와 같은 에러가 떴다.
<Link>
renders as <a>
Invalid <Link> with <a> child
https://nextjs.org/docs/messages/invalid-new-link-with-extra-anchorStarting with Next.js 13,
<Link>
renders as<a>
, so attempting to use<a>
as a child is invalid.
next.js 13 버전에서는 더이상 a 태그를 자식으로 포함하지 않아도 된다.(기본으로 포함)
import Link from "next/link";
import { useRouter } from "next/router";
export default function NavBar() {
const router = useRouter();
console.log(router);
return (
<nav>
<Link
href="/"
style={{ color: router.pathname === "/" ? "red" : "blue" }}
>
Home
</Link>
<Link
href="/about"
style={{ color: router.pathname === "/about" ? "red" : "blue" }}
>
About
</Link>
</nav>
);
}
import NavBar from "../components/NavBar";
export default function Home() {
return (
<div>
<NavBar />
<h1>Hello</h1>
</div>
);
}
import NavBar from "../components/NavBar";
export default function Home() {
return (
<div>
<NavBar />
<h1>About</h1>
</div>
);
}
next.js 앱에 css 추가하는 방법은 여러가지가 있음.
.nav {
display: flex;
justify-content: space-between;
background-color: skyblue;
}
import styles from "./NavBar.module.css";
<nav className={styles.nav}>
</nav>
styles.nav
와 같이 클래스 이름을 프로퍼티 형식으로 작성해 사용한다.
.link {
text-decoration: none;
}
.active {
color: tomato;
}
...
return (
<nav>
<Link
href="/"
className={`${styles.link} ${
router.pathname === "/" ? styles.active : ""
}`}
>
Home
</Link>
<Link
href="/about"
className={[
styles.link,
router.pathname === "/about" ? styles.active : "",
].join(" ")}
>
About
</Link>
</nav>
);
Styled jsx는 Next.js 고유의 CSS 사용법.
export default function NavBar() {
const router = useRouter();
return (
<nav>
<Link href="/">Home</Link>
<Link href="/about">About</Link>
<style jsx>{`
nav {
background-color: tomato;
}
`}</style>
</nav>
);
}
nav 태그 배경이 토마토색으로 변경된 것을 확인할 수 있다.
클래스 이름을 확인해보면, jsx-{해쉬값} 형태로 클래스 이름이 무작위로 생성되어 있다.
(컴포넌트 단위로 네이밍 스페이스가 지정, 스타일이 컴포넌트 내로 한정됨!)
파일 import도 필요없고, 다른 컴포넌트라면 같은 클래스 이름 사용할 수 있음.
``으로 묶인 문자열이기에, props로 받은 변수도 사용 가능하다. color: ${props.color}
next.js 13 버전에서는 Link 태그를 사용할때 더이상 a태그를 자식으로 포함하지 않아도 된다.
하지만, a태그에 클래스 지정해줘야 하는 상황에서는?
Link태그에 legacyBehavior를 추가해주어야 오류 없이 실행된다.
...
<nav>
<Link href="/" legacyBehavior>
<a className={router.pathname === "/" ? "active" : ""}>Home</a>
</Link>
<Link href="/about" legacyBehavior>
<a className={router.pathname === "/about" ? "active" : ""}>About</a>
</Link>
<style jsx>{`
nav {
background-color: tomato;
}
a {
text-decoration: none;
}
.active {
color: yellow;
}
`}</style>
</nav>
...
Link태그의 자식에 a태그를 작성해도 오류가 뜨지 않았고, a태그에도 클래스이름이 잘 적용되었다!
<styled jsx global>
하지만 index.tsx 파일에만 global 스타일 적용해주면, about 페이지로 이동했을때는 적용 안되는 문제!
import type { AppProps } from "next/app";
import NavBar from "../components/NavBar";
export default function App({ Component, pageProps }: AppProps) {
return (
<>
<NavBar />
<Component {...pageProps} />
<style jsx global>
{`
a {
color: white;
}
`}
</style>
</>
);
}
}
about페이지를 렌더링할때, about 컴포넌트를 App 컴포넌트의 props로({Component, pageProps})로 보내준 다음 합쳐진 것을 그려준다.
지금 예제에서는 NavBar가 어느페이지에서든 보여야하는 공통 컴포넌트 이기에, NavBar를 App 컴포넌트에 넣어주면 된다!
(더이상 index.tsx, about.tsx 모두에<NavBar />
넣어줄 필요 없음.)
또한 글로벌 css도 _app.tsx 파일에 작성해주면 된다.
원래 next.js에서는 예를들어, about.tsx에 globals.css 파일 import 할 수 없음.
하지만, _app.tsx에서는 임포트 가능!
위 에러메시지만 보아도, 글로벌 css는 App 컴포넌트에서 import 하라고 되어있다.
_app.tsx에서 import "../styles/globals.css";
임포트 해주면 아래와 같이 글로벌 스타일이 적용된다.
? 라이브러리 vs 프레임워크
? /pages/about.tsx 생성 -> /about으로 라우팅
? /pages/index.tsx -> / 으로 라우팅
? create-next-app vs create-react-app(CSR vs SSR)
? Rehydration
? css modules -> className={styles.nav}
? styled-jsx -> <style jsx>
{a { ~~~ }
} </style>
? <style jsx global>
? custom App(_app.tsx)