상세 컨텐츠

본문 제목

[NEXT]당근마켓(2-1) - React Hook Form을 이용하여 유저 정보를 백엔드에 넘기기

React/당근마켓 클론

by 래모 2023. 2. 9. 18:13

본문

1️⃣ useForm 사용하여 데이터 생성

pages/enter.tsx에 들어가서 useForm을 사용해주자

//pages/enter.tsx

interface EnterForm {
  email?: string;
  phone?: string;
}
...
const [submitting, setSubmitting] = useState(false);
const {register, handleSubmit,  reset} = useForm<EnterForm>();
...
const onEmailClick = () => {
    reset(); 
    // email을 눌러서 데이터를 전송하고 또 phone을 눌러서 데이터를 전송하면
    // 두 가지 데이터가 동시에 전송되므로 reset사용하여 하나만 전송시켜준다
    setMethod("email")
  };
  const onPhoneClick = () => {
    reset();
    setMethod("phone")
  };
...
const onValid = (validForm:EnterForm) => {
    setSubmitting(true);
    fetch("/api/users/enter", {
      method: "POST",
      body: JSON.stringify(data),
      headers: {
        "Content-Type": "application/json",
      },
    }).then(() => {
      setSubmitting(false);
    });
    // POST 프로토콜로 JSON 인코딩된 데이터를 보내기 위해 fetch() 사용
    // body의 데이터 유형은 반드시 "Content-Type"헤더와 일치해야 한다.
  }
...
 <form onSubmit={handleSubmit(onValid)} className="flex flex-col mt-8 space-y-4">
      {method === "email" ? (
        <Input 
        register = {register("email")} 
        name="email" 
        label="Email address" 
        type="email" required />
      ) : null} // Input은 미리 만드어둔 컴포넌트이다 깃헙 참고
      {method === "phone" ? (
        <Input
          register = {register("phone")}
          name="phone"
          label="Phone number"
          type="number"
          kind="phone"
          required
        />
      ) : null}

코드가 살짝 더럽지만 일단 저 상태로 냅둔다.

위와 같이 작성하면 클릭 버튼을 눌렀을 시 데이터가 url로 전송이된다.

 

// pages/api/users/enter.tsx

import { NextApiRequest, NextApiResponse } from "next";

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  if (req.method !== "POST") {
    res.status(401).end();
  }
  console.log(req.body.email);
  res.status(200).end();
}

해당 api url의 코드는 다음과 같다.

 

더보기

자주 쓰는 HTTP 상태 코드

 

200 OK: 요청이 성공적으로 되었습니다. (성공)

400 Bad Request: 이 응답은 잘못된 문법으로 인하여 서버가 요청을 이해할 수 없음을 의미합니다. (클라이언트에서 Request할 때 발생한 문제)

 

401 Unauthorized : 비록 HTTP 표준에서는 "미승인(unauthorized)"를 명확히 하고 있지만, 의미상 이 응답은 "비인증(unauthenticated)"을 의미합니다. 클라이언트는 요청한 응답을 받기 위해서는 반드시 스스로를 인증해야 합니다.


403 Forbidden: 클라이언트는 콘텐츠에 접근할 권리를 가지고 있지 않습니다. 401과 다른 점은 서버가 클라이언트가 누구인지 알고 있습니다.

405 Method Not Allowed: 요청한 메서드는 서버에서 알고 있지만, 제거되었고 사용할 수 없습니다. (허용되지 않은 메서드 사용)

500 Internal Server Error: 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다.

 

2️⃣ 코드를 정리해보자!! useMutation

useMutaion은 function과 state를 return하며 기존의 onValid함수 내에서 하던 일을 대신 해줄 것이다.

 

libs/client/useMutation.tsx를 생성해주자

import { useState } from "react"

interface UseMutationState {
    loading: boolean;
    data?: object;
    error?: object;
}
type UseMutationResult = [(data: any) => void, UseMutationState];

export default function useMutation (url:string) :UseMutationResult {
    const [state, setState] = useState<UseMutationState>({
    loading: false,
    data: undefined,
    error: undefined,
  });
  function mutation(data: any) {
    setState((prev) => ({ ...prev, loading: true }));
    fetch(url, {
        method: "POST",
        body: JSON.stringify(data),  
        headers: {
        "Content-Type": "application/json",
        },
      
    })
      .then((response) => response.json().catch(() => {}))
      .then((data) => setState((prev) => ({ ...prev, data })))
      .catch((error) => setState((prev) => ({ ...prev, error })))
      .finally(() => setState((prev) => ({ ...prev, loading: false })));
  }
  return [mutation, { ...state }];
}

기존 onValid랑 유사하다.

 

이후에 pages/enter.tsx는 간략하게 수정이 가능하다

const [enter,{loading, data, error}] = useMutation("/api/users/enter");
...
  const onValid = (validForm:EnterForm) => {
    enter(validForm)
  }

 

 

3️⃣ pages/api/enter.tsx의 handler 수정하기

handler는 프로토콜 요청을 return하기만 하고 POST냐 GET이냐 DELETE냐에 대한 것은 다른 컴포넌트에서 저장해줄것이다.

 

libs/server/withHandler.tsx를 만들어주자

import { NextApiRequest, NextApiResponse } from "next";

export default function withHandler(
  method: "GET" | "POST" | "DELETE",
  fn: (req: NextApiRequest, res: NextApiResponse) => void
) {
  return async function (req: NextApiRequest, res: NextApiResponse) {
    if (req.method !== method) {
      return res.status(405).end();
    }
    try {
      await fn(req, res);
    } catch (error) {
      console.log(error);
      return res.status(500).json({ error });
    }
  };
}

이후 handler을 수정하면 다음과 같다.

import withHandler from "@libs/server/withHandler";
import { NextApiRequest, NextApiResponse } from "next";
import client from "../../../libs/client/client";

async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  console.log(req.body);
  return res.status(200).end();
}

export default withHandler("POST", handler);

관련글 더보기