Next.js Crash Course for Beginners 2021 강의노트(4): General Layout & Programmatic Navigation

9rganizedChaos·2021년 11월 22일
0
post-thumbnail

(🛑 해당 포스팅은 Youtube에 업로드된 Academind의 NextJS 튜토리얼 강의 콘텐츠의 강의노트입니다. 링크를 클릭하시면 강의를 들으실 수 있습니다.)

Onwards To A Bigger Project!

NextJS는 페이지 세팅 외에도, 데이터 페칭을 비롯한 여러 기능을 제공한다.
지금까지 다루었던 뉴스 프로젝트를 잠시 접어두고, 새로운 프로젝트-meetup 리스트 앱-로 학습을 시작한다.

Preparing Our Project Pages

우선 아래와 같이 page 폴더를 구성해준다.

1) starting page: 모든 밋업 리스트를 보여줄 페이지
2) new meetup page: 새로운 밋업을 작성하는 페이지
3) meetup detail page: 밋업 디테일(각각의 밋업에 대한 상세 정보)을 확인할 수 있는 페이지

Rendering A List Of (Dummy) Meetups

현재 폴더 구조를 살펴보면 pages 폴더와 components 폴더가 있다. (components 폴더와 같은 경우, 임의로 이름을 변경해주어도 전혀 상관이 없다.) pagescomponents의 결정적인 차이는 components 폴더에 저장된 컴포넌트들은 페이지로서 자동적으로 로드되지 않는다는 점이다. 기존 리액트의 방식과 마찬가지로, 다른 컴포넌트나 페이지에서 재사용이 가능한 컴포넌트들에 해당한다.

위와 같이 코드를 작성해주고 컴포넌츠 폴더에 작성한 컴포넌트를 HomePage에서 임포트해주었다. 여기까지는 기존 리액트와 크게 다를 것은 없다!

Adding A Form For Adding Meetups

위와 같은 원리로 meetup폼(기본 컴포넌트! not 페이지컴포넌트)도 추가해준다.
이로써 우리는 어떻게 기존 react 컴포넌트와 nextJS페이지컴포넌트를 블렌딩해서 사용하는지 배워보았다.
그러나 이러한 방식은 functionality를 상실하는 결과를 낳는다. 페이지들 간에 어떻게 네비게이팅을 하는지도 모르고, 페이지들 사이의 제너럴한 레이아웃도 아직 없다. 모든 페이지는 전체 넓이를 차지하는데, 헤더도 없다.

The "_app.js" File & Wrapper Components

제너럴 레이아웃을 적용해주기 위해 해당 강의가 제공하는 starting 프로젝트에는 layout 폴더가 준비되어 있다. 만일 우리가 HomePage에 헤더를 추가해주기 원한다면 아래와 같이 코드를 작성해볼 수 있다.

function HomePage() {
  return (
    <Layout>
      <MeetUpList meetups={DUMMY_MEETUPS}></MeetUpList>
    </Layout>
  );
}

이렇게 하면 홈페이지에는 적용이 되지만, 다른 페이지로 이동하면 레이아웃이 사라진다.
물론, 여타 페이지에도 동일한 작업을 해줄 수도 있지만 더 좋은 방법이 있다.

이때 필요한 것이 바로 _app.js 파일이다!
_app.js는 조금 특별한 파일이다. 해당 파일은 NestJS의 root 컴포넌트라고 이해해볼 수 있다. _app.js는 프롭스를 받아서 디스트럭처링하고 있다.

import '../styles/globals.css'

function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}

export default MyApp

디스트럭처링 하고 있는 두 프롭들을 살펴보자. 두 프롭들은 myApp 컴포넌트에 자동으로 전달되는 프롭들이다. pageProps란 우리의 페이지들이 전달받을 prop들을 의미한다. 그리고 Component는 페이지 이동에 따라 그때그때 렌더될 컴포넌트들이다. 여기서 우리가 생각해낼 수 있는 건, _app.js를 layout으로 감싸버리는 것이다.

이제 모든 페이지에 일일이 레이아웃을 적용해주지 않아도, 모든 페이지에서 동일한 레이아웃을 확인할 수 있다.
수많은 페이지가 존재할 경우에도 코드 작성에 어려움이 없고, 유지보수도 쉽다.

Programmatic Navigation

데이터페칭과 데이터를 백엔드로 보내는 작업을 하기 전에, 디테일 페이지에 대해 다뤄볼 것이다.
우선 programmatic한 방식으로 네비게이팅을 해줄 것이다.

function MeetupItem(props) {
  const router = useRouter();

  function showDetailsHandler() {
    router.push("/" + props.id);
  }

  return (
    <li className={classes.item}>
      <Card>
        <div className={classes.image}>
          <img src={props.image} alt={props.title} />
        </div>
        <div className={classes.content}>
          <h3>{props.title}</h3>
          <address>{props.address}</address>
        </div>
        <div className={classes.actions}>
          <button onClick={showDetailsHandler}>Show Details</button>
        </div>
      </Card>
    </li>
  );
}

물론 기존에 사용하던 Link 컴포넌트를 활용해줄 수도 있다. 그러나 더 좋은 방법이 있다!
useRouter 훅이다. 해당 훅을 활용해 router를 선언하고, router.push 메서드를 활용하면 전달인자에 해당하는 페이지로 유저를 이동시킬 수 있다!

Adding Custom Components & Styling With CSS

마지막으로, 아직은 프로젝트를 백엔드 서버에 연결하지 않았기 때문에 하드코딩을 통해 디테일 페이지를 완성해준다.

위와 같이 코드를 작성하고 나면 이미지의 우측과 같이 출력되는 화면을 볼 수 있다.
여기서 다루어볼 부분은 NextJS에서 스타일링을 적용하는 방법이다.

물론 pages > [meetupId] 에 바로 css 파일을 만들어줄수도 있다.
그러나 우리의 목표는 pages 폴더를 가능한 lean하게 유지하는 것이다. 우선 MeetupDetails 내부에 작성해두었던 코드를 페이지 컴포넌트 밖으로 끄집어내줄 수 있다.

import { Fragment } from "react";
import MeetupDetail from "../../components/meetups/MeetupDetail";

function MeetupDetails() {
  return (
    <MeetupDetail
      image="https://upload.wikimedia.org/wikipedia/commons/d/d3/Stadtbild_M%C3%BCnchen.jpg"
      title="A First Meetup"
      address="Some Street 5, Some City"
      description="This is a First Meetup"
    />
  );
}

export default MeetupDetails;

위와 같은 내용으로 컴포넌트 폴더에 파일을 따로 생성해주고, 프롭만 내려준다.
그리고 컴포넌트 폴더에서 MeetupDetail.module.css를 따로 작성해준다.

NextJS에서 module.css가 특별한 점은 css class를 스코핑할 수 있게 해준다는 것이다.
해당 파일을 만들고 해당파일을 특정 js 파일에서 임포트하면 해당 컴포넌트에 스코핑된다.

import classes from "./MeetupDetail.module.css";

function MeetupDetail(props) {
  return (
    <section className={classes.detail}>
      <img src={props.image} alt={props.title} />
      <h1>{props.title}</h1>
      <address>{props.address}</address>
      <p>{props.description}</p>
    </section>
  );
}

export default MeetupDetail;

CSS모듈을 사용할 때는 위와 같이 적용해주면 된다!

profile
부정확한 정보나 잘못된 정보는 댓글로 알려주시면 빠르게 수정토록 하겠습니다, 감사합니다!

0개의 댓글