본문 바로가기

개발 공부/React

Next.js 에서 모달 띄우기와 서버작업을 통한 유저 정보 받기 및 등록 (4)

코드 출처 : https://github.com/Cluster-Taek/next14-boilerplate

 

GitHub - Cluster-Taek/next14-boilerplate

Contribute to Cluster-Taek/next14-boilerplate development by creating an account on GitHub.

github.com

 

 

useCreateUserMutation 훅

import { fetchApi } from './base';
import { IPageable } from '@/types/pageable';
import { IUser } from '@/types/user';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';

...

export const useCreateUserMutation = () => {
  const queryClient = useQueryClient();
  return useMutation({
    mutationFn: async (data: IUserCreateFormValue) => await fetchApi.post(`/api/users`, data),
    onSuccess: async () => {
      await queryClient.invalidateQueries({
        queryKey: ['/api/users'],
        refetchType: 'all',
      });
    },
  });
};

 

useCreateUserMutation 훅

  • queryClient:
    • React Query에서 캐시를 조작하는 클라이언트를 가져옵니다.
    • 이는 useMutation 내에서 성공적인 요청 후 관련 데이터를 새로 고침하거나 무효화하는 데 사용됩니다.
  • mutationFn:
    • useMutation의 핵심으로, 실제로 실행할 함수입니다.
    • 여기서는 사용자 생성 API를 호출하는 함수가 포함됩니다.
    • fetchApi.post를 사용하여 data를 서버로 전송하고, /api/users 엔드포인트에 POST 요청을 보냅니다.
  • onSuccess:
    • 요청이 성공적으로 완료된 후 호출되는 콜백 함수입니다.
    • 여기서는 queryClient.invalidateQueries를 사용하여 /api/users 쿼리 키와 관련된 데이터를 무효화하여 최신 사용자 목록을 다시 가져오도록 설정합니다.

fetchApi.post

  • fetchApi.post(/api/users, data):
    • useMutation의 mutationFn으로 호출되며, data (사용자 생성 폼 데이터)를 포함하여 /api/users API 엔드포인트로 POST 요청을 보냅니다.
  • 이 API 요청은 fetchApi 유틸리티로 작성된 인증된 요청입니다.
  • 즉, 액세스 토큰이 포함되어 보내지며, 서버 측에서는 해당 토큰을 통해 요청을 인증합니다.

queryClient.invalidateQueries

  • invalidateQueries는 지정된 쿼리 키와 관련된 데이터를 무효화하여 서버에서 다시 데이터를 가져오도록 하는 방법입니다.
  • onSuccess 콜백 내에서 이 메서드를 호출하여, /api/users 쿼리 키에 해당하는 캐시된 데이터를 무효화하고 최신 사용자 목록을 가져오도록 합니다.
  • refetchType: 'all': 이는 invalidateQueries 메서드의 옵션으로, 관련된 모든 쿼리 데이터를 새로 고침하도록 지시합니다.

useMutation 훅의 반환

  • useMutation 훅은 API 호출 상태와 메서드를 포함하는 객체를 반환합니다.
  • 여기서 반환되는 객체에는 mutate 함수와 요청 상태를 추적할 수 있는 값들이 포함됩니다.
  • 예를 들어, 로딩 상태(isLoading), 성공 여부(isSuccess), 에러 여부(isError) 등을 추적할 수 있습니다.

base.ts 

import axios from 'axios';
import { getSession, signOut } from 'next-auth/react';
import { redirect } from 'next/navigation';

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const _fetchApi = async <T = object>(method: string, url: string, body?: Record<string, unknown>): Promise<T> => {
  const session = await getSession();
  const response = await axios({
    method,
    url: url,
    data: method !== 'GET' ? body : undefined,
    params: method === 'GET' ? body : undefined,
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${session?.user?.accessToken}`,
    },
    withCredentials: true,
  }).catch(async (error) => {
    if (response.status === 401 && window.location.pathname !== '/login') {
      await signOut();
      redirect('/login');
    }
    throw error;
  });

  return response?.data;
};
type FetchApi = {
  post: <T = object>(url: string, body?: Record<string, unknown>) => Promise<T>;
  get: <T = object>(url: string, params?: Record<string, unknown>) => Promise<T>;
  patch: <T = object>(url: string, body?: Record<string, unknown>) => Promise<T>;
  put: <T = object>(url: string, body?: Record<string, unknown>) => Promise<T>;
  delete: <T = object>(url: string) => Promise<T>;
};

export const fetchApi: FetchApi = {
  post: (url, body) => _fetchApi('POST', url, body),
  get: (url, params) => _fetchApi('GET', url, params),
  patch: (url, body) => _fetchApi('PATCH', url, body),
  put: (url, body) => _fetchApi('PUT', url, body),
  delete: (url) => _fetchApi('DELETE', url),
};
  • 이 코드는 Axios와 Next.js의 next-auth를 사용하여 인증된 API 요청을 보내는 fetchApi 유틸리티를 정의한 것입니다.
  • 요청은 POST, GET, PATCH, PUT, DELETE 방식에 맞게 처리되며, 인증이 필요한 요청에는 Authorization 헤더에 액세스 토큰을 추가합니다.
  • 만약 401 상태 코드(인증 실패)를 받을 경우, 사용자는 로그아웃되고 /login 페이지로 리디렉션됩니다.
  1. _fetchApi 함수:
    • method: HTTP 요청 메서드(예: POST, GET 등)를 받습니다.
    • url: API 엔드포인트 URL.
    • body: 요청에 포함될 데이터(주로 POST, PATCH, PUT 요청에서 사용).
    • session: next-auth를 통해 현재 사용자의 세션을 가져옵니다.
    • response: axios를 사용하여 실제 API 요청을 보냅니다. 요청에 대한 응답을 처리하고, 401 오류가 발생하면 사용자를 로그아웃시키고 로그인 페이지로 리디렉션합니다.
    • catch 블록에서는 에러 처리 후 401 상태 코드인 경우 사용자를 로그아웃하고 로그인 페이지로 이동합니다.
  2. fetchApi 객체:
    • fetchApi 객체는 post, get, patch, put, delete 메서드를 제공하여 각 HTTP 요청 방식에 맞는 API 호출을 간편하게 사용할 수 있도록 합니다.
    • 각 메서드는 _fetchApi 함수에 HTTP 메서드와 URL, 데이터를 전달하여 API 호출을 수행합니다.
const _fetchApi = async <T = object>(method: string, url: string, body?: Record<string, unknown>): Promise<T> => {
  const session = await getSession();
  const response = await axios({
    method,
    url: url,
    data: method !== 'GET' ? body : undefined,
    params: method === 'GET' ? body : undefined,
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${session?.user?.accessToken}`,
    },
    withCredentials: true,
  }).catch(async (error) => {
    if (response.status === 401 && window.location.pathname !== '/login') {
      await signOut();
      redirect('/login');
    }
    throw error;
  });

  return response?.data;
};

 

  • 이 함수는 제네릭 타입 T를 사용하여 반환되는 데이터를 유연하게 처리합니다.
  • method는 HTTP 메서드(GET, POST, PUT 등), url은 요청할 API의 URL, body는 요청 본문 데이터를 나타냅니다.
  • 반환 타입은 Promise<T>로 지정되어 있으며, 이는 Axios 응답 데이터(response.data)를 반환하는 비동기 함수임을 나타냅니다.
  • getSession()은 next-auth에서 제공하는 함수로, 현재 로그인한 사용자의 세션 정보를 가져옵니다.
  • 이 함수는 비동기적으로 동작하며, 세션 정보가 없으면 null을 반환할 수 있습니다.
  • 세션에 user 객체가 포함되어 있고, 그 안에 accessToken이 있어야 API 요청에 인증 헤더를 추가할 수 있습니다.
  • method: 요청할 HTTP 메서드(GET, POST, PATCH, PUT, DELETE 등)입니다.
    • method 변수는 함수 매개변수로 전달됩니다.
  • url: 요청할 URL입니다.
  • data: HTTP 요청의 본문(body)을 전달합니다.
    • GET 요청의 경우 본문을 사용하지 않으므로, GET이 아닌 경우에만 data로 전달합니다.
  • params: GET 요청에서는 URL 쿼리 문자열로 데이터를 전달하므로, GET 메서드일 때 params를 사용합니다.
    • 이때 body가 params로 전달됩니다.
  • headers: 요청 헤더입니다.
    • Authorization 헤더는 Bearer ${session?.user?.accessToken}으로 세션에서 가져온 액세스 토큰을 사용하여 인증된 요청을 보냅니다.
    • Content-Type은 application/json으로 설정되어, 요청 데이터가 JSON 형식임을 나타냅니다.
  • withCredentials:
    • true로 설정하면 크로스 도메인 요청에서 쿠키를 포함하여 보낼 수 있습니다.
    • 이는 인증된 요청을 위한 쿠키를 처리하는 데 사용됩니다.

 

예시

유저 등록을 하면 db.json에 이런식으로 쌓이게 됩니다.

{
  "users": [
    {
      "id": "1",
      "name": "John Doe"
    },
    {
      "id": "2",
      "name": "Jane Doe"
    },
    {
      "id": "dd2f",
      "name": "jimin"
    },
    {
      "id": "f15f",
      "name": "zz"
    }
  ]
}