개발/프로젝트

RTK Query를 사용하여 회원가입 API 구현하기

xuwon 2025. 1. 6. 17:31
RTK Query

Redux Toolkit의 강력한 데이터 패칭 및 캐싱 라이브러리


1. 데이터 패칭 및 캐싱

2. 자동 상태 관리
- API 요청의 상태(isLoading, isSuccess, isError, isFetching 등)를 자동으로 관리
- 수동으로 로딩 상태를 설정하거나 에러를 처리할 필요 X

3. 데이터 변조 지원
- POST, PUT, DELETE 요청과 같은 데이터 변조 작업도 간단히 구현 가능

4. React Hooks 자동 생성
- API 엔드포인트 정의만 하면 React Hooks가 자동으로 생성됨.
- 이를 통해 컴포넌트에서 쉽게 API를 호출할 수 있음.

 

RTK Query의 주요 구성 요소

1. createApi
API 서비스 로직을 정의하는 데 사용

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

export const api = createApi({
  reducerPath: 'api', // Redux store에 저장될 리듀서의 이름
  baseQuery: fetchBaseQuery({ baseUrl: '/api' }), // 기본 요청 설정
  endpoints: (builder) => ({
    // 엔드포인트 정의
  }),
});


2. baseQuery

모든 API 요청에 공통적으로 적용될 설정 정의
- 기본 URL, 인증 헤더 추가, 에러 처리 등 포함

const baseQuery = fetchBaseQuery({
  baseUrl: 'https://api.example.com',
  prepareHeaders: (headers, { getState }) => {
    const token = getState().auth.token;
    if (token) {
      headers.set('Authorization', `Bearer ${token}`);
    }
    return headers;
  },
});


3. endpoints

API 요청 로직을 정의하는 곳.
builder.querybuilder.mutation을 사용해 GET 요청과 POST/PUT/DELETE 같은 데이터 변조 요청 설정

export const api = createApi({
  reducerPath: 'api',
  baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
  endpoints: (builder) => ({
    getUser: builder.query({
      query: (id) => `/user/${id}`, // GET 요청
    }),
    updateUser: builder.mutation({
      query: (user) => ({
        url: `/user/${user.id}`,
        method: 'PUT',
        body: user, // PUT 요청에 전송할 데이터
      }),
    }),
  }),
});


이렇게 createApiAPI를 정의하면, 자동으로 React Hooks를 생성한다.

export const { useGetUserQuery, useUpdateUserMutation } = api;

 

이렇게 만들어진 리액트 훅들은 이렇게 사용할 수 있다.

const { data, error, isLoading } = useGetUserQuery(userId);

if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;

return <div>{data.name}</div>;

 

RTK Query의 캐싱

RTK Query는 동일한 요청 데이터에 대해 자동으로 캐싱한다.
데이터가 이미 캐싱되어 있다면 서버 요청 없이 캐시 데이터 반환!

const { data, refetch } = useGetUserQuery(userId, { pollingInterval: 10000 }); // 10초마다 데이터 갱신

 

RTK Query의 상태 관리

RTK Query는 API 요청의 상태를 자동으로 관리하며, 다음과 같은 상태 값 제공!

- isLoading : 요청이 진행 중인지 여부
- isSuccess : 요청이 성공적으로 완료되었는지 여부
- isError : 요청이 실패했는지 여부
- isFetching : 새로운 데이터를 가져오는 중인지 여부

 


우선 createApi를 사용해서 API 요청 로직을 작성해보자.

import { createApi } from '@reduxjs/toolkit/query/react';
import baseQuery from './baseQuery';

export const userApi = createApi({
  reducerPath: 'userApi', // 리듀서 경로 이름
  baseQuery,
  endpoints: builder => ({
    signUp: builder.mutation({
      query: newUser => ({
        url: '/user/signup', // 회원가입 엔드포인트
        method: 'POST',
        body: newUser, // 요청 데이터
        headers: {
          'Content-Type': 'application/json',
        },
      }),
    }),
  }),
});

export const { useSignUpMutation } = userApi;

reducerPathRedux Store에 저장될 리듀서의 키 이름이다.

그리고 baseQuery에는 기본 URL과 인증 헤더가 들어가 있다!

endpoints에는 builder를 통해 query(GET 요청) mutation(POST, PUT, DELETE 요청)을 설정할 수 있다.
회원가입 시에는 데이터를 보내야 하기 때문에, mutation을 설정해 주었다. (newUser가 함수 호출 시 전달되는 사용자 데이터)


이렇게 정의하고 나면 useSignUpMutation이라는 훅이 완성된다!

 


이렇게 createApi로 정의된 API는 Redux Store에 연결해야 사용할 수 있다.

// store.ts

import { configureStore } from '@reduxjs/toolkit';
import { userApi } from './userApi'; // createApi로 정의한 API

export const store = configureStore({
  reducer: {
    // RTK Query 리듀서를 등록
    [userApi.reducerPath]: userApi.reducer,
  },
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().concat(userApi.middleware), // RTK Query 미들웨어 추가
});

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

아까 정의한 리듀서 경로 이름으로 리듀서를 등록해준다.


Redux에서 미들웨어는 액션이 리듀서로 전달되기 전에 가로채서
추가적인 작업(ex. 로깅, 비동기 처리, API 호출 등)을 수행할 수 있는 기능이다.

RTK Query에서는 미들웨어가 데이터 패칭과 캐싱, 상태 관리를 자동으로 처리하는 데 사용된다!


그리고 Redux Store를 또 React 컴포넌트 트리에 연결해 주어야 한다.

import { Provider } from 'react-redux';
import { store } from './store'; // Redux Store

function MyApp({ Component, pageProps }) {
  return (
    <Provider store={store}>
      <Component {...pageProps} />
    </Provider>
  );
}

export default MyApp;


이렇게 하면 RTK Query에서 상태 관리와 API 호출을 사용할 수 있게 된다!


const [signUpUser, { isLoading }] = useSignUpMutation(); // isError, isSuccess

사용하려는 컴포넌트에서 useSignUpMutation 훅을 불러온다.

isLoading은 버튼의 disabled 속성에 넣어 주었고, isError, isSuccess는 회원가입 실패/성공 시 토스트 띄울 때 사용할 예정!

const onSubmit = async (data: SignUpSchema) => {
  try {
    // 필수 항목 + 선택 항목 데이터 합치기
    const userData = {
      ...omit(data, 'confirmPassword'), // confirmPassword 제거
      birthday: selectedDate || '1925-01-01',
      ...(selectedGender !== '선택안함'
        ? { gender: selectedGender === '여성' ? 'female' : 'male' }
        : {}),
    };

    // API 호출
    const result = await signUpUser(JSON.stringify(userData)).unwrap();
    console.log('회원가입 성공:', result);
  } catch (err) {
    console.error('회원가입 실패:', err);
  }
};

회원가입 폼 제출 시 실행되는 함수에 API 호출 로직을 추가했다.

API 호출은 비동기 작업이니까 async, await도 추가해주고, signUpUser에 데이터를 담아서 API를 호출한다.
데이터는 json 형식으로 보내야 하기 때문에 JSON.stringify() 메서드도 사용해 주었다.
try/catch로 에러도 잡아준다!

unwrap()는 API 호출 결과에서 성공과 실패를 Promise 기반으로 처리할 수 있도록 도와주는 메서드이다.
성공 시 data를 반환하고, 실패 시 error를 반환하도록!

이렇게 하면 회원가입 성공 시 resultdata가 반환되고, 실패 시 error가 반환된다.

 


RTK Query는 처음 사용해 보는데, 간편하고 좋은 것 같다.
로그인이랑 유저 정보 받아오는 API 호출 로직도 다 함께 작성이 가능하다.

하지만 어렵다.....