이 글은 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를 통해 아까 저장한 프로필 사진을 보여주는 페이지를 만들어보자.

원하는 heightwidth값을 주는데, 이때 기존 이미지의 비율과 일치해야 한다.

 

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해온다. 그리고 classNamestyles.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

생강강

,