[Tutorial] Create First App

2023. 4. 24. 19:38Next.js/Tutorial

리액트로 온전한 웹 앱을 밑바닥부터 만드려면 고려해야할 세부 사항들이 꽤나 많다.

  • 웹팩같은 번들러로 코드를 번들링하고, 바벨같은 컴파일러로 코드를 변환해야 함
  • 코드 스플리팅같은 프로덕션 최적화 작업을 해줘야 함
  • 앱 성능과 SEO를 위해 일부 페이지를 정적으로 pre-render할 것인지 결정해줘야 함
  • 리액트 앱을 데이터 스토어와 연결하기 위해 일부 서버 측 코드를 작성해햐할 수도 있음

 

물론 프레임워크를 적절히 잘 선택하면 이러한 문제들을 해결할 수 있다. 그러나 실제로 쓸법한 프레임워크가 되려면 적절한 수준의 추상화가 이뤄져있어야 하며, 좋은 개발자 경험도 제공해줘야 하므로 여간 쉬운 일이 아니다.

Next.js는 리액트 프레임워크로써 위에서 언급한 모든 문제들의 솔루션을 제공한다. Next.js는 최고의 개발자 경험과 개발 과정에서 필요한 여러 기능들을 기본으로 제공하는 것을 목표로 하고 있다. 

아래는 Next.js가 제공하는 기능들이다. Next.js의 힘이 느껴지십니까?

  • 직관적인 페이지 기반의 라우팅 시스템(동적 라우팅 지원)
  • 페이지 단위로 SSG, SSR 방식 모두 가능한 Pre-rendering 기능
  • 빠른 페이지 로드를 위한 자동 코드 스플리팅
  • 최적화된 prefetching을 통한 클라이언트측 라우팅
  • Built-In CSS와 Sass 지원, 모든 CSS-in_JS 라이브러리 지원
  • Fast Refresh를 지원하는 개발 환경 제공
  • 서버리스 함수로 API 앤드포인트를 구축하는 API 라우트 지원
  • 완전한 확장성

Next.js는 세계의 온갖 거대 브랜드를 포함해 수만 개의 프로덕션용 웹사이트 및 웹 애플리케이션에 사용되고 있다.

여하튼 이 튜토리얼에선 간단한 블로그 앱을 만들면서 기본 사항들을 배워볼건데, 최종 결과물은 이렇게 생겼다.

 

 

1. Next.js app 생성하기

'Next.js 앱 생성을 위해 디렉토리를 하나 만들고, 터미널에 아래 명령어를 쳐보자.

npx create-next-app@latest nextjs-blog --use-npm --example "https://github.com/vercel/next-learn/tree/master/basics/learn-starter"


내부적으로 Next.js 앱을 부트스트랩하는 craete-next-app이라는 툴을 쓰는데, 이 템플릿은 --example 플래그를 통해 사용된다.

설치가 끝나면 nextjs-blog라는 폴더가 하나 생길텐데, 해당 폴터로 이동해 npm run dev 명령어로 개발서버를 하나 띄워보자. 브라우저에서 아래와 같은 화면을 볼 수 있을것이다.



1.1 페이지 수정해보기
아래 과정을 따라 간단하게 페이지를 수정해보자. 코드를 살짝 바꾸고 세이브하면 Fast Refresh 기능 덕분에 브라우저가 자동으로 페이지가 업데이트될 것이다.

  • 에디터에서 pages/index.js를 열기
  • <h1> 태그의 "Welcome to"를 "Learn"으로 바꾸기

 

1.2 페이지 생성
Next.js에서 각 페이지는 pages 디렉토리에서 export되는 리액트 컴포넌트이다. 페이지들은 파일명에 기반해서 라우트되는데, 예를 들어 

  • pages/index.js / 경로에 연결되고
  • pages/posts/first-page.js는 /posts/first-post 경로에 연결된다


일단 pages/index.js파일이 이미 존재하므로 pages/posts/first-post.js 파일을 생성해서 어떻게 작동하는지 살펴보자. 우선, 1) pages디렉토리에 posts디렉토리를 하나 추가하고, 2)거기에 first-post.js 파일을 하나 생성해 아래와 같이 작성해보자.

컴포넌트명은 아무렇게나 해도 상관없지만 반드시 export default문으로 작성해줘야 한다. 코드 작성이 끝났으면 http://localhost:3000/posts/first-post에 접속해보자. 아래와 같은 화면을 보게 될 것이다.

너무 간단한 작업이라 특별할 건 없지만, 여기서 중요한건 pages 디렉토리에 JS 파일을 만들어야하며, 파일 경로가 곹 URL 경로가 된다는 점이다.

어찌보면 HTML이나 PHP 파일로 웹사이트를 만드는 방식과 비슷하지만, HTML대신 JSX를 작성하고 리액트 컴포넌트를 쓴다는 점이 다르다.

 

1.3 페이지간 이동(navigation)
보통 웹사이트에서 페이지간 이동을 구현할 때 <a> 태그를 사용한다. 그러나 Next.js에선 next/link 모듈의 Link컴포넌트를 사용하는데, <Link>컴포넌트를 쓰면 클라이언트측 네비게이션 기능뿐만 아니라 추가적인 기능을 위한 프로퍼티를 달아줄 수 있다.

 

실습을 위해 코드를 아래와 같이 변경해보자. 

pages/index.js

   
import Link from 'next/link';
// ... 중략

<h1 className="title">
  Welcome to <a href="https://nextjs.org">Next.js!</a>
</h1>

 

pages/posts/first-post.js

   
import Link from 'next/link';

export default function FirstPost() {
  return (
    <>
      <h1>First Post</h1>
      <h2>
        <Link href="/">Back to home</Link>
      </h2>
    </>
  );
}

 

1.4 Client-Side Navigation
근데 왜 멀쩡한 <a>태그를 두고 <Link>컴포넌트를 쓰는 걸까? 그 이유는 바로 위에서 언급한 바 있는 클라이언트 사이드 네비게이션 때문이다.

클라이언트 사이드 네비게이션이란 JS를 이용한 페이지 전환을 말하는데, 브라우저의 기본 네비게이션 기능대비 더 빠르다는 장점이 있다.

더 빠른 이유는 <a>태그로 페이지 전환 시 페이지 전체를 새로 그리지만(full refresh) <Link>의 경우 필요한 부분만 다시 그리기 때문이다.

1.5 코드 스플리팅과 프리패칭
Next.js는 자동으로 코드 스플리팅을 수행하므로 각 페이지는 필요한 부분만 로드하게 된다. 이러한 방식의 장점이 무엇일까?

1) 홈페이지가 렌더링될 때 다른 페이지와 관련된 코드들은 처음에 제공되지 않는다. 덕분에 앱이 수백개의 페이지를 가지고 있다한들 홈페이지는 빠르게 로드된다.

2) 또한 요청한 페이지의 코드만 로드하면 되므로 페이지간 독립성이 보장되고, 결과적으로 특정 페이지에서 오류가 발생해도 나머지 부분은 정상적으로 작동하게 된다.

3) 게다가 Next.js의 프로덕션 빌드에선 <Link> 컴포넌트가 브라우저의 뷰포트상에 표시될 때마다 링크된 페이지의 코드를 백그라운드에서 자동으로 프리패치한다. 덕분에 해당 링크를 클릭할 때 즈음이면 이미 관련 코드가 백그라운드에서 로드된 상태이므로 사실상 즉시 페이지 전환이 이루어진다고 볼 수 있다.

 

1.6 정리
Next.js는 코드 스플리팅, 클라이언트 사이드 네비게이션, 프리패칭(프로덕션 환경에서)을 통해 애플리케이션을 자동 최적화해 극강의 성능을 제공한다.

pages 디렉토리 내에서 파일이름으로 라우트를 구성하고, 내장된 링크 컴포넌트로 페이지 전환을 구성하면 추가적인 라우팅 라이브러리를 설치할 필요도 없다(ReactRouter 같은)

Link컴포넌트와 관련된 자세한 사항은 next/link API 참조 문서와 라우팅 문서에서 찾아볼 수 있음.

 

 

2. Assets, Metatdata

이번 단원에선 Next.js에 정적 파일(이미지 등)을 추가하는 법과 메타데이터 설정법, 서드 파티 모듈을 추가하는 방법에 대해 알아보자.

 

2.1 Assets
Next.js는 최상위 public 디렉토리에 이미지같은 정적 에셋static assets을 저장해 제공할 수 있다. 바로 위에서 pages 디렉토리의 파일들이 파일명에 따라 참조되는 모습을 봤었는데, public 디렉토리에서도 이와 유사한 방식으로 내부 파일들을 참조할 수 있다.

실제로 어떻게 돌아가는지 확인을 위해 이미지를 한 장 준비해서 아래 과정을 따라해보자.

  • .jpg 포맷으로 이미지 한장을 준비한다(이미지 크기는 400 x 400px 정도로)
  • public 디렉토리內 images 디렉토리를 생성한다
  • 이미지 파일을 profile.jpg란 이름으로 저장한다
  • public 디렉토리에서 안쓰는 SVG 로고 파일같은 것들은 삭제해도 좋다.

 

아마 통상의 HTML이라면 <img>태그를 써서 아래와 같이 이미지를 붙였겠지만

 

<img src="/images/profile.jpg" alt="흐흥" />


이 방식은 최적화 측면에서 수작업 해줘야하는 부분이 많다

  • 스크린 사이즈에 따라 이미지 사이즈 조절(반응형 만들기)
  • 서드파티 라이브러리나 툴로 이미지 최적화
  • 이미지가 뷰포트에 들어올 때만 로드하기

 

2.1.1 이미지 최적화
next/image 모듈은 HTML <img> 태그의 익스텐션으로 모던 웹을 위한 패키지이다. Next.js는 기본적으로 이미지 최적화를 지원하는데, 여기엔 이미지 리사이징 및 최적화, 최신 포맷의 이미지 제공(WebP같은) 등이 포함된다. 물론 WebP같은 최신 형식을 브라우저가 지원하는 경우에만.

여하튼 이렇게 함으로써 작은 뷰포트에 큰 이미지를 전송하는 불상사를 막을 수 있고, Next.js가 향후 이미지 형식을 자동으로 채택하여 브라우저에 제공할 수 있게끔 해준다.

 

2.1.2 이미지 컴포넌트

Next.js는 빌드 타임중 이미지를 최적화하는게 아니라 온디맨드 방식으로 유저가 request를 날리는 시점에 이미지를 최적화한다. 즉, 이미지를 기본적으로 지연 로드lazy load한다는 소리인데, 스크롤을 내려 이미지가 뷰포트 내에 들어오는 시점에 이미지를 로드한다는 뜻이다. 

 

이렇게 하면 뷰포트 바깥의 이미지를 로드하느라 페이지 속도가 느려지는 일이 없고, 정적 사이트와 달리 전송 이미지가 백만개가 된다한들 빌드 타임이 늘어나지 않는다. 또한, 이 렌더링 방식 덕분에 레이아웃 시프트를 피할 수도 있다 (레이아웃 시프트는 구글이 검색 순위를 매길때 사용하는 Core Web Vital이므로 매우 중요함)

 

아래 코드는 next/image 모듈의 Image컴포넌트 사용 예제이다. heightwidth 프로퍼티는 원하는 렌더링 사이즈로 하되, 원본 이미지와 동일한 비율로 설정해줘야 한다.

import Image from 'next/image';

const YourComponent = () => (
  <Image
    src="/images/profile.jpg" // 이미지 파일 경로
    height={144} // Desired size with correct aspect ratio
    width={144} // Desired size with correct aspect ratio
    alt="Your Name"
  />
);

 

NOTE
To learn more about Automatic Image Optimization, check out the documentation.
To learn more about the Image component, check out the API reference for next/image.

 

2.2 Metadata

HTML의 <title>태그 같은 페이지 메타데이터를 수정하려면 어떻게 해야할까? <title> 태그는 <head> 태그의 일부이므로 Next.js에선 <head>태그를 어떻게 다룰 수 있는지 알아보자.

pages/posts/first-post.js

   
import Head from 'next/head';
import Link from 'next/link';

export default function FirstPost() {
  return (
    <>
      <Head>
        <title>First Post</title>
      </Head>
      <h1>First Post</h1>
      <h2>
        <Link href="/">Back to home</Link>
      </h2>
    </>
  );
}

 

Next.js에선 <head>태그 대신 next/head 모듈의 Head 컴포넌트를 사용한다. 위 코드는 해당 컴포넌트를 이용해 First Post란 이름으로 타이틀을 추가했는데, 실제 페이지를 보면 아래와 같이 타이틀이 변경된걸 확인할 수 있다.

제목 바뀐거 보임?

NOTE
To learn more about the Head component, check out the API reference for next/head.
If you want to customize the <html> tag, for example to add the lang attribute, you can do so by creating a pages/_document.js file. Learn more in the custom Document documentation.

 

2.3 Thired-Party JavaScript

서드 파티에서 추가되는 스크립트를 말하는데, 통상 분석, 광고, 고객 지원 위젯 등 처음부터 새로 작성할 필요가 없는 최신 기능을 도입하기 위해 추가되곤 한다.

pages/posts/first-post.js

   
export default function FirstPost() {
  return (
    <>
      <Head>
        <title>First Post</title>
        <script src="https://connect.facebook.net/en_US/sdk.js"/>
      </Head>
      // 중략
    </>
  );
}

 

위 코드는 메타데이터와 더불어 서드 파티 JS를 추가한 예제이다. 보통 페이지 로드와 함께 빨리 실행돼야하는 스크립트의 경우 페이지의 head부분에 넣게 되는데, 통상의 <script> 태그를 사용하면 된다. 그리고 코드에서 추가된 스크립트는 Facebook SDK인데 보통 페이스북 소셜 플러그인 및 기타 기능을 도입하는데 사용된다.

 

위 방식으로 외부 스크립트를 포함시킬 수 있긴 하지만 동일 페이지내 다른 JS코드와 비교해서 언제 로드될지 명확하게 알 수 없다는 단점이 있다. 특히, 특정 스크립트가 렌더링을 차단해 페이지의 컨텐츠 로딩을 방해할 경우 심각한 퍼포먼스 저하로 이어질 수 있다.

 

따라서 Next.js에선 위 문제를 피하고 최적화를 위해 next/script 모듈 내 Script컴포넌트를 사용한다. 

pages/posts/first-post.js

   
import Head from 'next/head';
import Link from 'next/link';
import Script from 'next/script';

export default function FirstPost() {
  return (
    <>
      <Head>
        <title>First Post</title>
      </Head>
      <Script
        src='https://connect.facebook.net/en_US/sdk.js'
        strategy='lazyOnload'
        onLoad={() => console.log('스크립트 로드됨')}
      />
        
      <h1>First Post</h1>
      <h2>
        <Link href="/">Back to home</Link>
      </h2>
    </>
  );
}

 

위 코드는 Script 컴포넌트를 사용한 예제인데, 몇 가지 프로퍼티를 설정해 스크립트 로드를 제어하고 있다. Script 컴포넌트에 대한 자세한 사항은 여기에서 확인하기로 하고, 몇 가지 중요한 부분만 추려내자면

  • strategy : 서드파티 스크립트의 로드 시점을 제어한다. 위에서 쓴 lazyOnload는 브라우저의 아이들 타임에 로드하도록 설정
  • onLoad : 스크립트 로드가 끝난 후 동작시킬 JS코드를 설정한다. 위 예시에선 간단하게 콘솔창에 '로드됨' 이라는 텍스트를 띄우도록 함

 

실제로 위 코드를 저장하고 콘솔창을 열어보면 '스크립트 로드됨' 이라는 텍스트가 뜨고, 해당 서드파티 툴에서 가져온 기능을 window.FB라는 전역 변수로 사용할 수 있게된다.

 

 

3. CSS

현재까지 index.js first-post.js 페이지가 있는데, index.js의 경우 이미 스타일이 입혀진 것을 볼 수 있다. 파일 구조를 까보면 styles폴더에 글로벌 스타일시트(globals.css)와 CSS 모듈을 볼 수 있을텐데(Home.module.css) 이처럼 Next.js는 기본적으로 CSS Module과 Sass를 지원하고 있다.

 

CSS Module이 생소할 수도 있는데, 이 모듈은 고유한 클래스 이름을 자동으로 생성해 컴포넌트 레벨에서 local scope의 CSS를 작성할 수 있게 도와준다. 이를 통해 클래스명의 충돌 걱정 없이 편하게 CSS를 짤 수 있다.

 

물론 CSS Module외에도 여러 방법으로 스타일을 입힐 수 있으며, 아래는 대표적인 스타일링 방법들이다.

  • Sass
  • TailwindCSS같은 PostCSS 라이브러리
  • styled-jsx, styled-components, emotion같은 CSS-in-JS 라이브러리

 

여하튼 이런저런 방법들이 많은데 이번 장에선 CSS Module과 Sass를 사용해보자.

 

3.1 Layout Component (feat. CSS Module)

가장 먼저 CSS Module을 사용해 Layout 컴포넌트를 만들어보자. 이 컴포넌트는 모든 페이지에서 공유되는 일종의 탬플릿 역할을 할 예정이다.

components/layout.js

   
export default function Layout({ children }) {
  return <div>{children}</div>;
}

 

우선 최상위 디렉토리에 components 디렉토리를 만들고, 그 안에 layout.js파일을 위와 같이 만들어주자. 그 다음 first-post.js파일을 열어 Fragment대신(<> 이거) Layout 컴포넌트를 넣어주자.

pages/posts/first-post.js

   
import Layout from '../../components/layout';

export default function FirstPost() {
  return (
    <Layout>
      // ... 중략
    </Layout>
  );
}

 

이제 CSS Module을 사용해 Layout 컴포넌트에 스타일을 입힐건데, 여기서 가장 중요한 것은 모든 CSS Module파일은 파일명이 .module.css형태로 끝나야 한다는 점이다.

components/layout.module.css

   
.container {
  max-width: 36rem;
  padding: 0 1rem;
  margin: 3rem auto 6rem;
}

 

위와 같이 스타일시트를 만들었으면 다음은 이 스타일을 컴포넌트에 입힐 차례인데, CSS Module의 경우 다음과 같은 방식으로 적용시킬 수 있다.

  • CSS파일을 import하고 styles같은 이름으로 명명해준다
  • classNamestyles.container 같은 형식으로 해당 스타일을 참조한다

components/layout.js

   
import styles from './layout.module.css';

export default function Layout({ children }) {
  return <div className={styles.container}>{children}</div>
}

 

여기까지 잘 따라왔으면 화면상의 글자가 좀더 밀린 모습을 볼 수 있을텐데, 지금까지의 결과물을 정리하면 아래와 같다.

 

CSS Module의 특별한 점은 유니크한 클래스 네임을 자동으로 생성해준다는 것인데, 실제로 위 페이지에서 개발자 도구를 켜서 클래스명을 확인해보면 layout_container_fbLkO같은 이상한 이름이 붙은걸 볼 수 있다.

 

또한, Next.js의 코드 스플리팅 기능이 CSS Modules에서도 동일하게 동작하는데, 덕분에 각 페이지마다 필요한 최소한의 CSS만 로드하므로 번들 사이즈를 줄일 수 있다. 구체적으로는 CSS Module은 빌드 시 JS 번들에서 추출되어 Next.js가 자동으로 로드하는 .css파일을 생성한다.

 

 

3.2 Global Styles

CSS Module은 컴포넌트 레벨에서 스타일링하기 좋은 도구이다. 그러나 만약 모든 페이지에 동일하게 적용하고싶은 CSS가 있다면 global CSS를 사용해야 한다. 이 역시 Next.js에서 지원하는 기능인데, 아래와 같은 과정으로 적용할 수 있다.

 

[1] 최상위 컴포넌트 만들기 ( _app.js )

우선 글로벌 CSS를 로드하려면 애플리케이션내 모든 페이지를 래핑하는 최상위 리액트 컴포넌트가 하나 필요하다.

pages/_app.js

   
export default function App({ Component, pageProps }) {
  return <Component {...pageProps} />;
}

 

pages 디렉토리 내 _app.js라는 이름으로 파일을 만들어줘야 하며, 이 컴포넌트를 통해 글로벌 스타일을 적용하고 페이지 이동간 state를 유지시킬 수 있다. 자세한건 여기 참조.

 

참고로 이 파일에 한해선 Fast Refresh가 적용되지 않으므로 터미널에서 개발 서버를 한번 껐다가 다시 켜줘야한다

 

[2] 글로벌 CSS 추가하기

글로벌 CSS를 적용시켜줄 최상위 컴포넌트를 만들었으니, 실제로 글로벌 CSS 코드를 짜기만 하면 된다. 다만, 글로벌 CSS 주의사항은 pages/_app.js파일을 제외하고선 import시킬 수 없다는 점인데, 그 이유는 이름 그대로 모든 페이지內 모든 컴포넌트에 적용시킬 스타일이므로 최상위 컴포넌트에서만 쓸 수 있도록 제한되어있기 때문이다.

 

그럼 이제 아래와 같이 글로벌 CSS코드를 작성하고 _app.js파일에 import해 실제로 적용시켜보자.

pages/_app.js

   
export default function App({ Component, pageProps }) {
  return <Component {...pageProps} />;
}

styles/global.css

   
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: #0070f3;
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

img {
  max-width: 100%;
  display: block;
}

 

이후 개발 서버를 켜서 localhost:3000/posts/first-post로 접속해보면 스타일이 새롭게 입혀진 모습을 확인할 수 있다. first-post.js파일의 스타일은 전혀 건드리지도 않았는데도 말이다. 말 그대로 global CSS라 모든 페이지에 적용된 것.

뭔가 더 예뻐짐

 

3.3 스타일 다듬기

지금까진 CSS Module같은 개념 설명을 위해 아주 간단한 리액트 코드와 CSS만 짜봤는데, 다음 장 내용인 data fetching에 대해 알아보기전에 조금만 스타일을 다듬고 넘어가보자.

 

[1] 업데이트 : components/layout.module.css

전체적인 레이아웃과 프로필 사진을 좀더 세련되게 바꾸기 위해 코드를 아래와 같이 수정해주자.

더보기

components/layout.module.css

   
.container {
  max-width: 36rem;
  padding: 0 1rem;
  margin: 3rem auto 6rem;
}

.header {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.backToHome {
  margin: 3rem 0 0;
}

 

 

[2] 생성 : styles/utils.module.css

CSS 유틸리티 클래스를 하나 추가할건데, 여러 컴포넌트에서 재사용 하기 위함이다. 여기서 만드는 유틸리티 클래스는 애플리케이션 전반에 걸쳐 재사용이 가능한데, 심지어 global.css파일에도 쓸 수 있다.

 

이렇게 말해놓고 나면 유틸리티 클래스가 뭔가 대단하고 특별한 놈인가 싶을텐데, '유틸리티 클래스'라는 말은 그저 CSS 셀렉터를 작성하는 방법론중 하나를 가리킬 뿐이다. 즉, 어떤 특별한 메서드나 대단한 의미를 담고있는 무언가는 아니라는 뜻.

 

더보기

styles/utils.module.css

   
.heading2Xl {
  font-size: 2.5rem;
  line-height: 1.2;
  font-weight: 800;
  letter-spacing: -0.05rem;
  margin: 1rem 0;
}

.headingXl {
  font-size: 2rem;
  line-height: 1.3;
  font-weight: 800;
  letter-spacing: -0.05rem;
  margin: 1rem 0;
}

.headingLg {
  font-size: 1.5rem;
  line-height: 1.4;
  margin: 1rem 0;
}

.headingMd {
  font-size: 1.2rem;
  line-height: 1.5;
}

.borderCircle {
  border-radius: 9999px;
}

.colorInherit {
  color: inherit;
}

.padding1px {
  padding-top: 1px;
}

.list {
  list-style: none;
  padding: 0;
  margin: 0;
}

.listItem {
  margin: 0 0 1.25rem;
}

.lightText {
  color: #666;
}

 

[3] 업데이트 : components/layout.js

여기가 뜯어고칠 부분이 많은데, 요약하면 다음과 같다.

  • meta tag 관련 : 페이지 컨텐츠 설명을 위한 태그
  • home 프로퍼티
     - 타이틀과 이미지 사이즈 조절을 위한 prop. 
     - 현재 페이지가 home이 아니라면(home = false이면) "Back to home" 링크가 표시될 예정
  • Image 컴포넌트 : next/image 모듈을 쓰며, preload를 위해 priority 속성을 추가해줌
더보기

components/layout.js

   
import Head from 'next/head';
import Image from 'next/image';
import styles from './layout.module.css';
import utilStyles from '../styles/utils.module.css';
import Link from 'next/link';

const name = 'Atmosg';
export const siteTitle = 'Next.js 샘플페이지';

export default function Layout({ children, home }) {
  return (
    <div className={styles.container}>
      <Head>
        <link rel='icon' href='/favicon.ico'/>
        <meta 
          name='으아아앙'
          content='Next.js로 웹사이트 만드는 법 배우는 중'
        />
        <meta
          property="og:image"
          content={`https://og-image.vercel.app/${encodeURI(
            siteTitle,
          )}.png?theme=light&md=0&fontSize=75px&images=https%3A%2F%2Fassets.vercel.com%2Fimage%2Fupload%2Ffront%2Fassets%2Fdesign%2Fnextjs-black-logo.svg`}
        />
        <meta name='og:title' content={siteTitle} />
        <meta name='twitter:card' content='summary_large_image' />
      </Head>
      <header>
        <Profile home={home}/>
      </header>
      <main>{children}</main>
      {!home && (
        <div className={styles.backToHome}>
          <Link href='/'>← Back to home</Link>
        </div>
      )}
    </div>
  )
}

function Profile({ home }) {
  if (home) {
    return (
      <>
        <Image
          priority // lazy loading 비활성화. 이미지가 사전에 로드됨
          src='/images/profile.jpg'
          className={utilStyles.borderCircle}
          height={144}
          width={144}
          alt=''
        />
        <h1>{name}</h1>
      </>
    )
  }
  
  return (
    <>
      <Link href='/'>
        <Image />
        <h2 className={utilStyles.headingLg}>
          <Link href='/' className={utilStyles.colorInherit}>
            {name}
          </Link>
        </h2>
      </Link>
    </>
  )
}

 

[4] 업데이트 : pages/index.js

홈페이지 역할을 해줄 index.js의 내용을 고치면 끝.

pages/index.js

   
import Head from 'next/head';
import utilStyles from '../styles/utils.module.css';
import Layout, { siteTitle } from '../components/layout';

export default function Home() {
  return (
    // 현재 화면이 home인지 아닌지 전달해줌
    <Layout home> 
      <Head>
        <title>{siteTitle}</title>
      </Head>
      <section className={utilStyles.headingMd}>
        <p>여어 안녕하신가</p>
        <p>
          (This is a sample website - you’ll be building a site like this on{' '}
          <a href='https://nextjs.org/learn'>Next.js 튜토리얼</a>.)
        </p>
      </section>
    </Layout>
  )
}

 

지금까지의 결과물은 아래와 같으며, 보고있는 페이지가 home인 경우와 아닌 경우 각각 다르게 표시된다.

home = true
home = false

 

이 외에도 clsx 라이브러리나 tailwindCSS, Sass등을 활용해 스타일링할 수 있으며, 각각의 공식문서를 참조해 각자의 상황에 맞게 CSS를 작성해보길 추천한다. 간단한 사용법은 여기를 참조.

 

 

'Next.js > Tutorial' 카테고리의 다른 글

[Tutorial] Create First App(3)  (0) 2023.04.29
[Tutorial] Create First App(2)  (0) 2023.04.26