230512.til(next-js-03)

Universe·2023년 5월 12일
0

Dynamic route

라우트의 경로가 동적으로 변하는 방식의 라우팅.
비슷한 패턴의 페이지를 일일히 만들필요 없이 동적으로 컨텐츠를 바인딩 할 수 있다.

리액트에서는 리액트 라우트를 사용해서

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/post/:postId" element={<PostPage />} />
      </Routes>
    </BrowserRouter>
  );
}

function PostPage({ postId }) {
	...
  return <div>{/* 렌더링할 내용 */}</div>;
}

이런식으로 작성해주었다.
넥스트에서는 어떤식으로 작성하는지 알아보자.

[작명]/page.js

아주 간단하게 app 폴더 내부에 [작명]/page.js 로 디렉토리를 구성하면
자동으로 다이나믹 라우트를 지원한다.
굉장히 편리하고 직관적인 기능.

이런식으로.

그리고 해당 페이지에서 props를 객체분할하여 params 객체를 찾으면 바인딩 할 데이터를 가져올 수 있다.
아래의 예시에서는 findOne 메소드를 사용하여 mongo DB의 _id 값으로 게시글의 데이터를 가져온다.

	const client = await connectDB;
  const db = client.db("forum");
  const data = await db
    .collection("post")
    .findOne({ _id: new ObjectId(params.postId) });

Next js의 Link 컴포넌트를 사용해 클릭했을 때 해당 페이지로 이동시켜주면 완성.

{result.map((item) => (
        <Link href={`/detail/${item._id}`} key={item._id}>
          <div className="list-item">
            <h4>{item.title}</h4>
            <p>{item.date}</p>
          </div>
        </Link>
      ))}

또 다른 방법

Link 컴포넌트를 사용하지 않는 다른 방법.
리액트 에서는 useNavigate 같은 리액트 라우터가 지원하는 Hook 들을 사용했다.
예를들면,

import { useNavigate } from 'react-router-dom';

export default function DetailLink({ postId }) {
  const navigate = useNavigate();

  const handleRouterBtn = () => {
    navigate(`/detail/${postId}`);
  };

  return <button onClick={handleRouterBtn}>버튼</button>;
}

이런식으로.

동일한 기능을 Next 에서는 useRouter 라는 Hook 으로 지원한다.
서버 컴포넌트에서는 Hook 을 사용할 수 없으므로 클라이언트 컴포넌트로 만들어야 함에 주의할 것.

"use client";

import { useRouter } from "next/navigation";

export default function DetailLink({ postId }) {
  const router = useRouter();
  const handleRouterBtn = () => {
    router.push(`/detail/${postId}`);
  };
  return <button onClick={handleRouterBtn}>버튼</button>;
}

useNavigate 처럼 뒤로가기, 앞으로 가기, 새로고침 등의 기능뿐 아니라,
prefetch 프롭스로 앞으로 보여질 페이지를 미리 로드해오는 등의 기능도 지원한다.
그밖에는 usePathname 으로 경로명을 가져온다던가,
useSearchParams 로 쿼리스트링을 가져온다던가 하는 기능도 지원한다.

추가적으로 prefetch 기능은 Link 태그를 사용하면 자동으로 지원된다.
게시판이나 블로그 처럼 포스트의 갯수가 무수히 많을 수 있는 사이트에서는 성능저하의 원인이 될 수 있으니 주의.

글 작성기능 (Create)

짧게 넥스트를 공부해보면서 가장 신기했던 기능.
따로 서버를 열어 줄 필요 없이 컴포넌트 자체에서 서버 기능을 만들 수 있다는게 편리했다.

서버 기능을 만드는 방법은 굉장히 단순하다.
어떤 유저어떤 방식 으로 어떤 요청 을 하면 어떻게 해주세요!
를 코드로 풀어내면 되는 식.
넥스트 에서도 똑같다.
단, 현재 app 디렉토리로 서버 기능을 개발했을 때 불완전하고 오류가 많아 pages 디렉토리를 사용한다고 한다.

포스팅 기능을 담당하는 api 를 만들기 전에

import { connectDB } from "./db";

export default async function getPostCollectionData(
  method,
  query = {},
  options = {}
) {
  const client = await connectDB;
  const db = client.db("forum");
  const postCollection = db.collection("post");
  let result;

  switch (method) {
    case "find":
      result = postCollection.find(query, options).toArray();
      break;
    case "findOne":
      result = postCollection.findOne(query, options);
      break;
    case "insertOne":
      postCollection.insertOne(query);
      break;
    default:
      throw new Error("잘못된 요청입니다.");
  }

  return result;
}

위와 같은 함수를 만들었다.
이런 패턴이 효율적인 패턴인지는 백엔드에 대한 지식이 짧아서 잘 모르겠다.

이런 함수를 만든 이유는 단순한데,

const client = await connectDB;
const db = client.db("forum");
const postCollection = db.collection("post");

이러한 코드가 서버를 호출하는 대부분의 컴포넌트에서 중복으로 사용되고 있었기 때문이다.
그래서 switch 문으로 파라미터로 받은 메소드, 쿼리, 옵션들을 처리하는 식으로 함수를 작성했다.

지금 생각해보니 함수의 작명도 getPostCollectionData 라는 이름 보다는 서버 api 를 담당하는 함수니까
postCollectionAPI 같은 식으로 변경하는 것도 좋아보인다.

넥스트에서는 서버 기능을 개발하는 것도 굉장히 간단한데,
pages 폴더 내부에 api 라는 폴더를 만들고 그 안에 사용할 메소드 이름으로 자바스크립트 파일을 만들면 된다.

그리고 아래와 같이 서버 기능을 담당할 함수를 만들면,

import getPostCollectionData from "@/util/getPostCollectionData";

export default function post(req, res) {
  if (req.method === "POST") {
    const postData = req.body;
    getPostCollectionData("insertOne", postData);
  }
  return res.status(200).json("처리완료");
}

/api/post 에 POST 메소드로 api 요청을 보내면 구현한 기능대로 수행한다.
위의 코드는 postData 를 insertOne, Create 하는 기능을 수행하게 된다.

"use client";

import getCurrentTime from "@/util/getCurrentTime";

export default function Post() {
  const handlePostSubmit = (event) => {
    event.preventDefault();
    const formData = new FormData(event.target);
    formData.append("date", getCurrentTime());
    const postData = Object.fromEntries(formData.entries());

    fetch("/api/post", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(postData),
    });
  };
  return (
    <div>
      <h2>글 작성</h2>
      <form onSubmit={handlePostSubmit}>
        <label>
          <h4>글 제목</h4>
          <input name="title" />
        </label>
        <label>
          <h4>내용</h4>
          <input name="content" />
        </label>
        <button type="submit">전송하기</button>
      </form>
    </div>
  );
}

그리고 Post 컴포넌트에서 form의 onSubmit 이벤트 핸들러에
formData 를 application/json 형식으로 보내주었다.
강의에서는 form action 으로 처리하는 방법을 알려주었는데
리액트스럽게 처리하는 방식도 가능한지 궁금해서 작성해보았다.

formData 자체를 보내서 처리하는 방법도 찾아봤으나,
넥스트에서 multer 같은 미들웨어를 사용할 수 없어 제한된다고 한다.
분명 다른 방법이 있겠지만 아직은 배우지 않았으므로 차근차근 나아가려고 한다.

profile
Always, we are friend 🧡

0개의 댓글