이 글은 Next.js의 공식 튜토리얼 문서를 참고하여 작성한 글입니다.
이전글에서 만든 페이지는 아직 스타일링이 되어있지 않다. 이제 이 페이지에 CSS를 통해 스타일을 해보자.
Next.js는 기본적으로 CSS와 Sass를 지원한다. 그리고 여기서는 CSS를 사용하겠다.
이 글에서는 Next.js가 어떻게 이미지와 같은 asset들을 관리하고 <title>태그와 같은 metadata들을 관리하는지 알아볼 것이다.
Assets
Next.js는 이미지와 같은 정적 자산을 최상위인 public디렉토리에서 제공이 가능하다. public안에 있는 파일들은 pages와 유사하게 애플리케이션 루트에서 접근이 가능하다.
또한 이 public디렉토리는 robots.txt, Google Site Verification과 다른 정적 자산에도 유용하다.
먼저, 프로필 사진을 다운로드 해보자.
public디렉토리에 images디렉토리를 만들고 .jpg포맷의 프로필 사진을 그 안에 저장한다. public디렉토리 안에 있는 사용하지 않는 SVG로고 파일은 삭제해도 된다.
그리고 기존의 방식대로, 프로필 사진을 다음과 같이 불러올 수 있다.
<img src="/images/profile.jpg" alt="ginger" />
하지만 이 방법은 다음과 같이 따로 처리해주어야할 것이 있다.
- 달라지는 화면 사이즈에 따라 반응형 처리
- 써드파티 툴이나 라이브러리를 이용한 이미지 최적화
- 사용자가 viewport에 들어왔을 때에만 이미지를 로딩
이 밖에도 더 있을 것이다. 하지만 Next.js는 이러한 문제들을 해결해줄 수 있는 Image컴포넌트를 제공하고 있다.
Image 컴포넌트와 이미지 최적화
next/image는 모던 웹을 위해 좀 더 발전된 HTML <img>요소의 확장이다.
또한 Next.js는 이미지 최적화를 기본적으로 제공한다. 이를 통해 브라우저가 지원하는 경우 WebP와 같은 최신 포맷으로 이미지 사이즈를 조정하고 최적화할 수 있다. 사이즈가 큰 이미지를 작은 화면의 기기에 그대로 사용하는 경우를 피할 수 있으며, 향후 이미지 형식을 자동으로 채택하고 해당 형식을 지원하는 브라우저에 제공 할 수 있습니다.
자동 이미지 최적화는 어떠한 이미지 소스여도 동작이 되며, 심지어 이미지가 CMS와 같은 외부 데이터에서 제공되더라도 가능하다.
Image 컴포넌트
Next.js는 빌드할 때 이미지를 최적화하는 대신에, 사용자가 요청한 것에 대해서 최적화를 진행한다. 정적 사이트 생성기 및 정적 전용 솔루션과 달리 빌드 시간은 이미지 10개를 전송하든 1,000만 개를 전송하든 증가하지 않게 된다.
이미지는 기본적으로 lazy loading된다. 이 말은 페이지 로드 속도가 viewport 외부의 이미지에 의해 느려지지 않는다는 것이다. 즉 이미지는 viewport안에 있는 스크롤에 따라 로드된다.
예를 들어서 next/image를 통해 아까 저장한 프로필 사진을 보여주는 페이지를 만들어보자.
원하는 height와 width값을 주는데, 이때 기존 이미지의 비율과 일치해야 한다.
import Image from "next/image";
const Profile = () => (
<Image
src="/images/profile.jpg" // 프로필 사진 경로
height={200}
width={300}
alt="ginger"
/>
);
export default Profile;
Metadata
만약 <title>태그와 같은 페이지의 메타데이터를 바꾸고 싶을 땐 어떻게 할까?
<title>은 <head>태그에 있기 때문에 Next.js에서 <head>태그를 어떻게 수정하는지 알아야한다.
pages/index.js을 보면 다음과 같은 코드가 작성되어 있다.
<Head>
<title>Create Next App</title>
<link rel="icon" href="/favicon.ico" />
</Head>
위의 코드를 보면 <head>태그 대신에 <Head>로 작성된 React 컴포넌트가 있는 것을 볼 수 있다. 이는 Next.js에서 기본적으로 제공하는 컴포넌트로, 페이지의 <head>태그를 수정할 수 있게 해준다.
Head컴포넌트는 next/head모듈에서 import하여 사용이 가능하다.
/posts/first-post에 <title>이 아직 없기 때문에 추가해보자.
pages/posts/first-post.js파일을 열고 Head를 import시킨다.
// first-post.js
import Head from "next/head";
그리고 FirstPost컴포넌트에 Head를 넣어 다음과 같이 수정한다.
export default function FirstPost() {
return (
<>
<Head>
<title>첫 번째 포스트</title>
</Head>
<h1>첫 포스트!</h1>
<h2>
<Link href="/">
<a>홈으로 돌아가기</a>
</Link>
</h2>
</>
)
}
그리고 이제 서버를 실행시켜 확인해보면 다음과 같이 브라우저 탭에서 첫 번째 포스트라고 나오는 것을 볼 수 있다.
CSS 스타일
이제 CSS얘기를 해보자.
index.js를 보면 알 수 있듯이 이미 어느정도의 스타일이 되어 있다. 스타일 코드는 다음과 같이 생겼을 것이다.
<style jsx>{`
...
`}</style>
이 페이지는 styled-jsx라는 라이브러리를 사용하고 있다. 이는 "CSS-in-JS"라이브러리로, 리액트 컴포넌트에서 CSS를 작성할 수 있으며 각 CSS 스타일은 컴포넌트에 대해 스코프를 가지므로 다른 컴포넌트에 독립적이다.
Next.js는 기본적으로 styled-jsx를 지원하지만 styled-components나 emotion과 같은 다른 유명한 CSS-in-JS라이브러리를 사용하는 것도 물론 가능하다.
Layout 컴포넌트
먼저, 모든 페이지에서 공유가 가능한 Layout컴포넌트를 만들어보자.
프로젝트 최상위 경로에 components라는 디렉토리를 만들고 layout.js파일을 만들어주자.
export default function Layout({ children }) {
return <div>{children}</div>
}
그리고 pages/posts/first-post.js를 열고 방금 만든 Layout컴포넌트를 import해준다. 이때 Layout컴포넌트는 다른 컴포넌트를 감싸는 형태로 해준다.
import Link from "next/link";
import Head from "next/head";
import Layout from "../../components/layout";
export default function FirstPost() {
return (
<Layout>
<Head>
<title>첫 번째 포스트</title>
</Head>
<h1>첫 포스트!</h1>
<h2>
<Link href="/">
<a>홈으로 돌아가기</a>
</Link>
</h2>
</Layout>
);
}
이제 Layout컴포넌트에 스타일을 더해보자. 이를 위해서 React컴포넌트에서 CSS파일을 import하게 해주는 CSS Modules를 사용할 것이다. 그리고 CSS Modules를 사용하기 위해선 CSS파일의 이름은 .module.css로 끝나야 한다.
components/layout.module.css파일을 만들고 다음과 같이 작성한다.
.container {
max-width: 36rem;
padding: 0 1rem;
margin: 3rem auto 6rem;
background-color: yellowgreen;
}
container클래스를 layout.js에서 사용하기 위해선 몇 가지 해주어야 할 것이 있다.
CSS파일을 styles라고 이름을 할당하여 import해온다. 그리고 className를 styles.container로 설정하여 사용한다.
layout.js를 열어 다음과 같이 코드를 수정해보자.
import styles from "./layout.module.css";
export default function Layout({ children }) {
return <div className={styles.container}>{children}</div>;
}
그리고 확인해보면 다음과 같이 스타일이 적용된 것을 볼 수 있다.
또한 개발자 도구를 통해 Layout컴포넌트를 보면 클래스 명이 다음과 같이 나오는 것을 볼 수 있다. layout_container_...
이는 CSS Modules가 자동적으로 고유의 클래스 명을 생성해준 것이다. 이처럼 CSS Modules를 사용하면 고유의 클래스 명을 생각하는데에 힘을 쏟지 않아도 된다.
게다가 Next.js의 코드 스플릿팅은 CSS Modules에서도 동작한다. 각 페이지마다 최소한의 CSS가 로드되는 것을 보장하고 이는 번들 사이즈를 줄여준다.
Global Styles
CSS Modules은 컴포넌트별로 스타일링을 할 때 유용하다. 하지만 모든 페이지에 적용되는 스타일을 원할 때에는 어떻게 할까? Next.js는 이또한 제공해주고 있다.
global CSS를 로드하기 위해 pages/_app.js파일을 만들고 다음과 같이 작성한다.
export default function App({ Component, pageProps }) {
return <Component {...pageProps} />
}
이 App컴포넌트는 모든 다른 페이지에서 공통되는 최상위 구성 요소이다. 이 App를 통해 페이지 간 이동을 할 때 상태를 유지할 수도 있다.
App컴포넌트를 추가했으면 서버를 재실행 할 필요가 있다. Ctrl + C를 누르고 다시 서버를 실행시키자.
npm run dev
Global CSS 추가
Next.js에서는 pages/_app.js에서 global CSS를 import해줌으로써 사용할 수 있다. global CSS는 페이지의 모든 요소에 영향을 끼치기 때문에 _app.js 이외의 경로에서는 global CSS를 import 할 수 없으니 주의한다.
만약 홈에서 /posts/first-post로 이동하게끔 했다면, 홈의 global 스타일은 /posts/first-post에 영향을 끼칠 것이다.
global CSS파일은 어디에나 위치할 수 있으며, 아무런 이름이나 가질 수 있다. 그럼 이제 만들어보자.
프로젝트 최상위에 styles디렉토리를 만들고 그 안에 global.css파일을 만들어준다. 그리고 다음과 같이 작성한다. 이는 몇몇 스타일을 초기화 시키고 a태그의 색상을 변경할 것이다.
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: #cc4137;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
img {
max-width: 100%;
display: block;
}
마지막으로 pages/_app.js에서 CSS파일을 import해준다.
import '../styles/global.css'
export default function App({ Component, pageProps }) {
return <Component {...pageProps} />
}
이제 잘 적용이 되었는지 확인해보자.
혹시 적용이 안된다면 서버를 재실행 했는지 확인하고 다시 서버를 실행시킨다.
'Next.js' 카테고리의 다른 글
[Next.js] Dynamic Routes (0) | 2021.04.10 |
---|---|
[Next.js] Pre-rendering과 Data Fetching (2) | 2021.04.09 |
[Next.js] 페이지 간 이동하기 (0) | 2021.04.06 |
[Next.js] Next.js 프로젝트 시작하기 (0) | 2021.04.06 |