정적 생성(Static Generation) 기능을 이용한 페이지의 동적 데이터 업데이트 방식

박찬욱·2023년 7월 12일
0

TIL

목록 보기
1/21
post-thumbnail
import { Fragment } from "react";
import Head from "next/head";

import { getEventById, getFeaturedEvents } from "../../helpers/api-util";
import EventSummary from "../../components/event-detail/event-summary";
import EventLogistics from "../../components/event-detail/event-logistics";
import EventContent from "../../components/event-detail/event-content";
import ErrorAlert from "../../components/ui/error-alert";
import Comments from "../../components/input/comments";
import { buildPath, extractPathData } from "../../helpers/api-pathData";

function EventDetailPage(props) {
  const event = props.selectedEvent;

  if (!event) {
    return (
      <div className='center'>
        <p>Loading...</p>
      </div>
    );
  }

  return (
    <Fragment>
      <Head>
        <title>{event.title}</title>
        <meta name='description' content={event.description} />
      </Head>
      <EventSummary title={event.title} />
      <EventLogistics
        date={event.date}
        address={event.location}
        image={event.image}
        imageAlt={event.title}
      />
      <EventContent>
        <p>{event.description}</p>
      </EventContent>
      <Comments eventId={event.id} data={props.commentsData} />
    </Fragment>
  );
}

export async function getStaticProps(context) {
  const eventId = context.params.eventId;

  const event = await getEventById(eventId);

  const filePath = buildPath("comments.json");
  const commentsData = extractPathData(filePath);

  return {
    props: {
      selectedEvent: event,
      commentsData,
    },
    revalidate: 30,
  };
}

export async function getStaticPaths() {
  const events = await getFeaturedEvents();

  const paths = events.map((event) => ({ params: { eventId: event.id } }));

  return {
    paths: paths,
    fallback: "blocking",
  };
}

export default EventDetailPage;

🤔 궁금했던 점

위의 코드를 간단하게 설명하면 로컬에 있는 comments.json이라는 파일에서 정적 생성방식으로 데이터를 받아와 클라이언트 컴포넌트의 props로 넘겨주는 코드이다.

여기서 궁금했던 점은 정적 생성이란 쉽게 말해 미리 데이터를 만들어놓고 컴포넌트에게 전달해주는 것인데 comments.json파일의 내용이 추가되거나 삭제되면 어떻게 되는지가 궁금했다.

"comment.json" 파일에 데이터가 추가되면 변경된 데이터가 서버 측에서
다시 가져와서 클라이언트 컴포넌트의 props로 전달됩니다. 
이는 Next.js의 정적 생성(Static Generation) 기능을 이용한 
페이지의 동적 데이터 업데이트 방식입니다.

getStaticProps 함수는 페이지를 빌드할 때 서버 측에서 한 번 실행되며,
commentsData 변수에 "comment.json" 파일의 데이터를 가져옵니다. 
이 데이터는 클라이언트 측으로 전달되어 EventDetailPage 컴포넌트의 props로 사용됩니다.

정적 생성된 페이지는 빌드 시점에 생성되므로, 
페이지가 서버에서 클라이언트로 전달되는 동안에는 변경되지 않습니다. 
따라서 "comment.json" 파일에 데이터가 추가되었다면, 
다음 번 빌드 시에 변경된 데이터가 반영된 정적 페이지가 생성됩니다. 
이후 클라이언트가 해당 페이지에 접근할 때 변경된 데이터가 포함된 페이지가 제공됩니다.

즉, "comment.json" 파일에 데이터가 추가되면, 정적 페이지의 콘텐츠가 변경되므로 
클라이언트에게 변경된 데이터를 포함한 새로운 페이지가 전달됩니다.
 

한마디로 말하면 서버가 변경된 데이터를 알아차리고 클라이언트에게 변경된 데이터를 전달한다는 말이다.

하지만 다음 번 빌드 시에 변경된 데이터가 반영된 정적 페이지가 생성됩니다. 라는 문장이 이해가 되지 않았다.


🤔 빌드과정 속 정적 생성의 원리

빌드(Build)는 애플리케이션을 실행 가능한 형태로 변환하는 과정을 의미합니다. 
Next.js에서의 빌드는 정적 생성(Static Generation)이나 
서버 사이드 렌더링(Server-side Rendering)을 통해 페이지를 생성하고, 
이를 미리 준비된 형태로 만드는 과정을 말합니다.

다음 번 빌드 시에 변경된 데이터가 반영된 정적 페이지가 생성된다는 의미는 다음과 같습니다

초기 빌드: 애플리케이션의 코드와 데이터를 기반으로 초기 빌드가 수행됩니다. 
이 과정에서 getStaticProps 함수를 사용하여 데이터를 가져와 정적 페이지를 생성합니다. 
이때 "comment.json" 파일의 데이터를 가져와 commentsData로 사용합니다.

데이터 변경: "comment.json" 파일에 새로운 데이터가 추가되거나 기존 데이터가 수정되었다면, 
서버 측에서는 이를 감지할 수 있습니다.

다음 번 빌드: Next.js는 빌드 시에 변경된 데이터를 감지하여 페이지를 다시 생성합니다. 
이때 변경된 데이터가 commentsData로 사용되어 
클라이언트에게 반영된 새로운 정적 페이지를 제공합니다.

요약하면, Next.js는 변경된 데이터를 감지하고 
다음 번 빌드 시에 해당 데이터가 포함된 정적 페이지를 새로 생성합니다. 
이를 통해 클라이언트는 변경된 데이터를 가진 새로운 페이지를 받아볼 수 있습니다.

빌드는 애플리케이션을 미리 준비된 형태로 만들기 때문에, 
클라이언트가 페이지에 접근할 때 서버 측에서 동적으로 데이터를 가져오거나 
페이지를 렌더링하는 과정이 필요하지 않습니다. 
이는 빠른 초기 로딩 속도와 SEO에 이점을 제공하는데 도움을 줍니다.

데이터가 변경되었으면 서버는 이를 인지하고 새로운 데이터를 포함한 새로운 정적 페이지를 생성하기 위해 다시 빌드작업을 한다는 것이다.


문제해결

import { useEffect, useState } from "react";
import classes from "./comment-list.module.css";
import axios from "axios";

export default function CommentList({ data }) {
  const [commentsData, setCommentsData] = useState(data);

  useEffect(() => {
    axios
      .get("/api/new-comment")
      .then((res) => setCommentsData(res.data.commentsData));
  }, [commentsData]);

  return (
    <ul className={classes.comments}>
      {/* Render list of comments - fetched from API */}
      {commentsData.map((comment) => (
        <li key={comment.id}>
          <p>{comment.text}</p>
          <div>
            By <address>{comment.name}</address>
          </div>
        </li>
      ))}
    </ul>
  );
}

위 코드는 서버로부터 받은 정적 데이터 즉, comments.json파일의 데이터를 props로 받아서 브라우저에 출력하는 컴포넌트 코드이다.

나는 comments.json파일이 변경될 때마다 브라우저를 새로고침하지 않아도 동적으로 변경사항을 표시하고 싶었다. 그래서 데이터를 받아오는 것은 서버에서 정적 생성을 통해 받아오고 변경사항은 클라이언트 측에서 처리하기로 했다.

동작 원리를 정리해보겠다.

  • commentList 컴포넌트는 초기 렌더링 시, commentsData의 상태는 props의 값을 갖고 초기화된다.
  • props가 변동되면 컴포넌트가 리랜더링되고 commentsData의 상태는 변경된 props의 값을 갖게 된다.
  • commentsData의 상태 변경을 useEffect가 감지하게 되고, 비동기 처리가 진행된다.
  • 비동기 요청의 응답이 도착하면 commentsData상태가 업데이트되고 컴포넌트가 리랜더링된다.
  • 업데이트된 commentsData의 값을 통해 사용자는 새로고침하지 않아도 변경된 컴포넌트를 확인할 수 있다.

느낀점

사용자가 인풋값을 주면 데이터가 추가되고 추가된 데이터를 즉각적으로 브라우저에 랜더링하는 기술은 클라이언트 측에서 구현할 수 있다.

서버 데이터는 정적 생성방식을 통해 미리 받아와서 사전 랜더링할 수 있으므로 사용자 입장에서는 데이터를 받아오는 시간을 기다리지 않을 수 있다.

변경된 서버데이터는 서버가 눈치를 챈다. 변경되었으면 다음 빌드 때 변경된 데이터로 다시 사전 랜더링을 진행한다.

이를 통해 서버 측에서의 처리와 클라이언트 측에서의 처리에 적절한 조화가 필요하다는 것을 느낄 수 있었다.

profile
대체불가능한 사람이다

0개의 댓글

관련 채용 정보