API 라우트를 통해 백엔드 코드 추가하기

·2024년 4월 3일
0

NextJS

목록 보기
12/26
post-thumbnail

🔗 레파지토리에서 보기

📌 API 라우트

📖 API 라우트란 무엇인가?

API 라우트란 특수한 형태의 URL으로 Next.js 어플리케이션에 추가하여 일반적인 브라우저 요청을 받고 사전 렌더링된 HTML 페이지를 돌려보내는 게 아니라 데이터를 수집하고, 사용하고 데이터베이스에 저장한 뒤 원하는 형태의 데이터를 돌려보내는 역할을 한다.
API 라우트는 URL 상에 직접 입력할 필요는 없지만 대신 JavaScript 코드인 Ajax 요청으로 트리거되므로 개발자는 해당 특정 코드를 페이지에 추가하여 백엔드에서 요청을 전송하게끔 하여 API가 데이터를 저장하거나 불러오도록 구성할 수 있다.


📖 첫 번째 API 라우트 작성하기

  • pages 폴더 안에 api 라는 이름의 폴더를 입력한다. 반드시 api라는 이름으로 설정해야 한다. 그래야 Next.js가 처리할 수 있다.
  • api 폴더 내에 있는 파일은 React 컴포넌트를 내보내지 않는다.
// pages/api/feedback.js
export default function handler(req, res) {
  res.status(200).json({ message: "This works" }); // 받은 요청에 포함된 JSON 데이터를 반환
}
  • Next.js는 위의 함수를 실행하여 /api/feedback으로 들어오는 요청을 처리할 것이다. 이 함수 내에서는 GET 요청만을 처리할 수 없으며 HTML 코드를 반환하지 않아도 되고 대신 개발자가 정한 서버 측 코드를 실행할 수 있다.
  • 해당 함수에 추가된 코드는 클라이언트 측에는 도달하지 않는다. 즉, 웹 페이지에 방문한 사람들에게 보이지 않는다.

"http://localhost:3000/api/feedback"으로 요청을 전송하면 다음의 결과가 나온다. → {"message":"This works"}


📖 프론트엔드 양식 준비하기

// pages/index.js
import { useRef } from "react";

function HomePage() {
  const emailInputRef = useRef();
  const feedbackInputRef = useRef();

  function submitFormHandler(event) {
    event.preventDefault();
    const enteredEmail = emailInputRef.current.value;
    const enteredFeedback = feedbackInputRef.current.value;
  }

  return (
    <div>
      <h1>The Home Page</h1>
      <form onSubmit={submitFormHandler}>
        <div>
          <label htmlFor="email">Your Email</label>
          <input type="email" id="email" ref={emailInputRef} />
        </div>
        <div>
          <label htmlFor="feedback">Your Feedback</label>
          <textarea id="feedback" rows="5" ref={feedbackInputRef} />
        </div>
        <button>Feedback</button>
      </form>
    </div>
  );
}

export default HomePage;

📖 입력 요청 구문 분석하기 & 서버 사이드 코드 실행하기

  • root 폴더에 data/feedback.json 생성
// pages/api/feedback.js
import fs from "fs";
import path from "path";

export default function handler(req, res) {
  if (req.method === "POST") {
    const email = req.body.email;
    const feedbackText = req.body.text;

    const newFeedback = {
      id: new Date().toISOString(),
      email: email,
      text: feedbackText,
    };

    // store that in a db or in a file
    const filePath = path.join(process.cwd(), "data", "feedback.json");
    const fileData = fs.readFileSync(filePath);
    const data = JSON.parse(fileData);
    data.push(newFeedback);
    fs.writeFileSync(filePath, JSON.stringify(data));
    res.status(201).json({ message: "Success", feedback: newFeedback });
  } else {
    res.status(200).json({ message: "This works" }); // 받은 요청에 포함된 JSON 데이터를 반환
  }
}

📖 API 라우트로 요청 전송하기

// pages/index.js
import { useRef } from "react";

function HomePage() {
  const emailInputRef = useRef();
  const feedbackInputRef = useRef();

  function submitFormHandler(event) {
    event.preventDefault();
    const enteredEmail = emailInputRef.current.value;
    const enteredFeedback = feedbackInputRef.current.value;

    const reqBody = { email: enteredEmail, text: enteredFeedback };
    fetch("/api/feedback", {
      method: "POST",
      body: JSON.stringify(reqBody),
      headers: {
        "Content-Type": "application/json",
      },
    })
      .then((res) => res.json())
      .then((data) => console.log(data));
  }

  return (
    <div>
      <h1>The Home Page</h1>
      <form onSubmit={submitFormHandler}>
        <div>
          <label htmlFor="email">Your Email</label>
          <input type="email" id="email" ref={emailInputRef} />
        </div>
        <div>
          <label htmlFor="feedback">Your Feedback</label>
          <textarea id="feedback" rows="5" ref={feedbackInputRef} />
        </div>
        <button>Feedback</button>
      </form>
    </div>
  );
}

export default HomePage;

// {message: 'Success', feedback: {…}}
// data/feedback.json
[
  {
    "id": "2024-04-01T09:32:22.649Z",
    "email": "admin@admin.com",
    "text": "admin data"
  },
  {
    "id": "2024-04-01T09:33:29.198Z",
    "email": "test@test.com",
    "text": "test data"
  }
]
  • 버튼을 눌렀을 때 정상적으로 data/feedback.json에 저장이 된다.

📖 API 라우트를 사용해 데이터 가져오기

💎 feedback.js에서 GET method를 위한 코드 작성하기

import fs from "fs";
import path from "path";

function buildFeedbackPath() {
  return path.join(process.cwd(), "data", "feedback.json");
}

function extractFeedback(filePath) {
  const fileData = fs.readFileSync(filePath);
  const data = JSON.parse(fileData);
  return data;
}

export default function handler(req, res) {
  if (req.method === "POST") {
    const email = req.body.email;
    const feedbackText = req.body.text;

    const newFeedback = {
      id: new Date().toISOString(),
      email: email,
      text: feedbackText,
    };

    // store that in a db or in a file
    const filePath = buildFeedbackPath();
    const data = extractFeedback(filePath);
    data.push(newFeedback);
    fs.writeFileSync(filePath, JSON.stringify(data));
    res.status(201).json({ message: "Success", feedback: newFeedback });
  } else {
    // GET method
    const filePath = buildFeedbackPath();
    const data = extractFeedback(filePath);
    res.status(200).json({ feedback: data }); // 받은 요청에 포함된 JSON 데이터를 반환
  }
}
  • else ~ 부분이 GET method의 동작을 구현한 코드이다.
  • filePath와, data를 받는 코드가 중복되므로 별도의 함수를 작성하여 리팩토링을 하였다.

💎 index.js에서 GET method를 위한 코드 작성하기

import { useRef, useState } from "react";

function HomePage() {
  const [feedbackItems, setFeedbackItems] = useState([]);

  const emailInputRef = useRef();
  const feedbackInputRef = useRef();

  function submitFormHandler(event) {
    event.preventDefault();
    const enteredEmail = emailInputRef.current.value;
    const enteredFeedback = feedbackInputRef.current.value;

    const reqBody = { email: enteredEmail, text: enteredFeedback };
    fetch("/api/feedback", {
      method: "POST",
      body: JSON.stringify(reqBody),
      headers: {
        "Content-Type": "application/json",
      },
    })
      .then((res) => res.json())
      .then((data) => console.log(data));
  }

  function loadFeedbackHandler() {
    fetch("/api/feedback")
      .then((res) => res.json())
      .then((data) => {
        setFeedbackItems(data.feedback);
      });
  }

  return (
    <div>
      <h1>The Home Page</h1>
      <form onSubmit={submitFormHandler}>
        <div>
          <label htmlFor="email">Your Email</label>
          <input type="email" id="email" ref={emailInputRef} />
        </div>
        <div>
          <label htmlFor="feedback">Your Feedback</label>
          <textarea id="feedback" rows="5" ref={feedbackInputRef} />
        </div>
        <button>Feedback</button>
      </form>
      <hr />
      <button onClick={loadFeedbackHandler}>Load Feedback</button>
      <ul>
        {feedbackItems.map((item) => (
          <li key={item.id}>{item.text}</li>
        ))}
      </ul>
    </div>
  );
}

export default HomePage;
  • Load Feedback 버튼을 눌렀을 때 loadFeedbackHandler 함수가 동작하여 unordered list에 표현되도록 하였다.

🔗 레파지토리에서 변경된 부분 확인하기


📖 사전 렌더링 페이지에 API 라우트 설정하기

  • Load Feedback 버튼이 아니라 /feedback에 피드백을 나열
  • 불필요한 fetch를 진행하지 않고 getStaticProps를 이용해 데이터를 받아올 것이다.
// pages/api/feedback.js
import fs from "fs";
import path from "path";

export function buildFeedbackPath() {
  return path.join(process.cwd(), "data", "feedback.json");
}

export function extractFeedback(filePath) {
  const fileData = fs.readFileSync(filePath);
  const data = JSON.parse(fileData);
  return data;
}
  • 우선 feedback.js에서 /data/feedback.json으로부터 경로를 받는 함수와 데이터를 추출하는 함수를 export 한다.

//pages/feedback/index.js
import { buildFeedbackPath, extractFeedback } from "../api/feedback.js";

export default function FeedbackPage({ feedbackItems }) {
  return (
    <ul>
      {feedbackItems.map((item) => (
        <li key={item.id}>{item.text}</li>
      ))}
    </ul>
  );
}

export async function getStaticProps() {
  const filePath = buildFeedbackPath();
  const data = extractFeedback(filePath);

  return {
    props: {
      feedbackItems: data,
    },
  };
}
  • export 한 두 함수를 불러와 getStaticProps에서 실행. props.feedbackItems로 리턴하여 표현하였다.

📖 동적 API 라우트 생성 & 사용하기

  • api/some-feedback-id를 통해서 동적으로 데이터를 받아올 경우
// pages/api/[feedbackId].js
import { buildFeedbackPath, extractFeedback } from "./feedback.js";

export default function handler(req, res) {
  if (req.method === "DELETE") {
    //
  }
  const feedbackId = req.query.feedbackId;
  const filePath = buildFeedbackPath();
  const feedbackData = extractFeedback(filePath);

  const selectedFeedback = feedbackData.find(
    (feedback) => feedback.id === feedbackId
  );

  res.status(200).json({ feedback: selectedFeedback });
}
// pages/feedback/index.js
import { buildFeedbackPath, extractFeedback } from "../api/feedback.js";
import { useState } from "react";

export default function FeedbackPage({ feedbackItems }) {
  const [feedbackData, setFeedbackData] = useState();

  function loadFeedbackHandler(feedbackId) {
    fetch(`/api/${feedbackId}`)
      .then((response) => response.json())
      .then((data) => {
        setFeedbackData(data.feedback);
      });
  }

  return (
    <>
      {feedbackData && <p>{feedbackData.email}</p>}
      <ul>
        {feedbackItems.map((item) => (
          <li key={item.id}>
            {item.text}{" "}
            <button onClick={loadFeedbackHandler.bind(null, item.id)}>
              Show Details...
            </button>
          </li>
        ))}
      </ul>
    </>
  );
}

export async function getStaticProps() {
  const filePath = buildFeedbackPath();
  const data = extractFeedback(filePath);

  return {
    props: {
      feedbackItems: data,
    },
  };
}
  • loadFeedbackHandler와 상태를 추가하여 만약 feedbackData가 존재한다면 해당 데이터의 이메일을 출력하고자 한다.
  • loadFeedbackHandler는 버튼이 클릭되었을 때 동작하고, 해당 함수에 feedback의 id를 전달해야하므로 bind(null,item.id)를 이용하여 id를 전달한다.

0개의 댓글

관련 채용 정보