[Next] CSS Modules / Styled JSX / Custom App
1. CSS Modules
넥스트 어플리케이션에 CSS를 추가하는 방법 중 하나는 modules를 사용하는 것이다.
NavBar.module.css
.nav {
text-decoration: none;
}
이름은 상관 없고 중요한 건 .module.css이다.
css를 작성하고 이 파일을 사용하고 싶은 곳에 자바스크립트 오브젝트로서 import를 시키면 된다.
import styles from "./NavBar.module.css"
모듈이기 때문에 className="nav"가 아니라 className={styles.nav}처럼 작성해줘야 한다.
이게 작동하는 이유는 .module.css 패턴을 사용했기 때문이다.
이 패턴은 css 모듈이라고 불리는데 이건 개발자로 하여금 평범한 css를 사용할 수 있도록 해준다.
그리고 클래스 이름을 추가해줄 때, 클래스 이름을 텍스트로서 적지 않고 자바스크립트 오브젝트에서의 프로퍼티 형식으로 적는다.
실상 이 클래스의 이름을 살펴본다면 NavBar_nav_무작위 텍스트이다.
클래스 이름이 무작위이기 때문에 충돌이 나지 않는다는 점이 이 접근 방식의 장점이다.
즉, 다른 컴포넌트에서도 nav라는 클래스 이름을 사용할 수 있는 것이다.
두 가지 스타일을 적용하고 싶다면 아래와 같이 작성할 수 있다.
// 방법 1
className={`${styles.link} ${
router.pathname === "/" ? styles.active : ""
}`}
// 방법 2
className={[
styles.link,
router.pathname === "/about" ? styles.active : "",
].join(" ")}
css 모듈 방식은 두 개의 파일을 가져야 하고 클래스 이름을 기억해야 한다는 단점이 있다.
2. Styled JSX
NextJS 어플리케이션에서 스타일을 추가하는 또 다른 방법은 NextJS 고유의 방법인 styled jsx를 사용하는 것이다.
style 태그(HTML 태그)를 열고 jsx prop을 넣어주면 된다.
<style jsx>{`
nav {
background-color: tomato;
}
a {
text-decoration: none;
}
`}</style>
styled jsx를 사용하면 위와 같은 클래스 이름을 지니게 된다.
모듈들이 독립되어 있어 부모 컴포넌트가 그 클래스 이름을 사용하고 있을지라도 상관이 없다.
스타일들은 오직 styled jsx가 사용된 컴포넌트 내부로 범위가 한정된다.
3. Custom App
Global Styles를 추가하는 한 가지 방법으로는 styled jsx에 와서 global이라는 prop을 추가하는 방법이 있다.
하지만 styled jsx가 있는 페이지에만 적용이 된다.
모든 페이지들에 전역적으로 스타일링을 하고 싶다면 모든 페이지의 청사진을 커스텀할 수 있는 장소인 App Component에 대해 알아야 한다.
App Component는 기본으로 프레임워크에 포함되어 있는 NextJS가 모든 페이지를 렌더링할 수 있게 하는 컴포넌트이다.
App Component를 커스터마이즈 하기 위해서는 _app.js라는 파일을 만들어야 한다.
NextJS는 먼저 App을 본 뒤 페이지의 내용물을 렌더링한다.
NextJS가 index를 렌더링하기 전에 _app.js를 먼저 확인할거고, 그 다음에 index.js의 내용물을 _app.js에 기반해서 렌더링하는 것이다.
_app.js는 어떻게 페이지가 있어야 하는지, 어떤 컴포넌트가 어떤 페이지에 있어야만 하는지 알려주는 청사진이다.
NextJS는 _app.js를 불러올거고, App 함수를 두 prop과 함께 부른다.
export default function App({Component, pageProps})
만약 About 페이지를 렌더링하길 원한다면 NextJS는 about.js 파일로 가서 컴포넌트를 가져다가 Component Prop에 전달한다.
export default function App({About, pageProps})
export default function App({ Component, pageProps }) {
return (
<div>
<Component {...pageProps} />
<span>Hello</span>
</div>
);
}
export default function App({ About, pageProps }) {
return (
<div>
<About {...pageProps} />
<span>Hello</span>
</div>
);
}
여러 페이지에서 중복되는 코드를 _app.js에 넣을 수 있다.
import NavBar from "../components/NavBar";
export default function App({ Component, pageProps }) {
return (
<>
<NavBar />
<Component {...pageProps} />
</>
);
}
이렇게 작성한다면 index 페이지에도, about 페이지에도 NavBar가 필요 없다.
왜냐하면 이제 모든 페이지는 여기에 NavBar 밑에 렌더링 될 것이기 때문이다.
원한다면 Global Styles를 추가해줄 수 있다.
import NavBar from "../components/NavBar";
export default function App({ Component, pageProps }) {
return (
<>
<NavBar />
<Component {...pageProps} />
<style jsx global>{`
a {
color: white;
}
`}</style>
</>
);
}
NextJS로 앱을 만들 때는 global css파일을 import할 수 없다.
만일 index.js에 global css 파일을 import하려고 하면 Custom App 이외의 파일들에서는 임포트할 수 없다는 에러가 발생한다.
페이지나 컴포넌트 내에 css를 import하고 싶다면 반드시 module이어야만 한다.
하지만 _app.js는 global css파일을 import할 수 있다.
※ 타입스크립트 사용시 _app.js
import { AppProps } from 'next/dist/shared/lib/router/router'
export default function App({ Component, pageProps }: AppProps) {
return < Component {...pageProps} />
}
정리
NextJS는 App 컴포넌트를 사용하여 page를 초기화하는데 이를 재정의하고 페이지 초기화를 제어할 수 있다.
기본 App을 재정의하려면 pages/_app.js 파일을 만든다.
파일명.module.css 파일 형태를 제외한 모든 나머지 css파일들은 _app.js에서만 import해와서 사용해야 한다.
(global css간의 충돌을 피하기 위함)
Recap
CSR (클라이언트 사이드 렌더링)
브라우저가 리액트 코드를 다운받고 모든걸 그려줘야 하기 때문에 하얀 화면이 보여질 수 있다.
NextJS 어플리케이션에서는 페이지가 사용자가 가기 전에 미리 만들어진다.
컴포넌트의 초기 상태는 자동적으로 HTML로 렌더링된 상태가 된다.
ReHydration = Hydration
NextJS가 백엔드 상에서 리액트를 돌려 HTML 페이지를 사전 생성한다.
사용자가 웹 사이트에 들어갈 때 하얀 화면이나 로딩 단계 없이 이 HTML 페이지를 본다.
모든 자바스크립트가 다 다운로드 된 뒤에는 리액트가 다시 주도권을 가져서 모든 게 일반적인 리액트처럼 동작한다.
그래서 useState같은 평범한 리액트의 모든 것을 사용할 수 있는 것이다.
NextJS를 다룰 때는 하나의 큰 어플리케이션이 아니라 각각의 나뉘어진 페이지를 생각해야 한다.
Custom App 컴포넌트는 NextJS에 의해 페이지를 렌더링할 때마다 사용된다.
About 페이지를 렌더링해야 할 때마다 About 컴포넌트를 가지고 _app.js 내의 App함수(함수명 상관x)를 호출한다.
그리고 NextJS는 그 렌더링하기를 원하는 페이지를 Component Prop에 넣어주고 그리는 작업을 한다.
이건 모두 자동적으로 일어나는 일이다.
커스텀을 원하는 게 아니라면 _app.js 파일을 직접 만들 필요가 없다.
📌 아래 강의의 내용을 정리한 글입니다.