Hello-LSY / Baenang

여행과 관련된 전자문서를 관리하는 여행 어플리케이션, Beanang
0 stars 3 forks source link

프론트엔드 redux 가이드 #30

Open Hello-LSY opened 1 month ago

Hello-LSY commented 1 month ago
          프론트엔드 redux 가이드

1. 새로운 Redux Slice 생성

  1. Slice 파일 생성: redux 디렉터리 안에 새로운 slice 파일을 생성합니다. 예: redux/profileSlice.js.

  2. Slice 구성: 아래의 기본 구조를 사용해 새로운 slice를 구성합니다.

    // redux/profileSlice.js
    import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
    import { getApiClient } from './apiClient';
    
    // 예시 비동기 작업 (프로필 데이터 가져오기)
    export const fetchProfile = createAsyncThunk(
      'profile/fetchProfile',
      async (memberId, { getState, rejectWithValue }) => {
        const token = getState().auth.token;
        try {
          const apiClient = getApiClient(token);
          const response = await apiClient.get(`/api/profiles/${memberId}`);
          return response.data;
        } catch (error) {
          return rejectWithValue(error.response ? error.response.data : 'Network error');
        }
      }
    );
    
    const profileSlice = createSlice({
      name: 'profile',
      initialState: {
        profileData: null,
        loading: false,
        error: null,
      },
      reducers: {
        clearProfile: (state) => {
          state.profileData = null;
          state.error = null;
        },
      },
      extraReducers: (builder) => {
        builder
          .addCase(fetchProfile.pending, (state) => {
            state.loading = true;
            state.error = null;
          })
          .addCase(fetchProfile.fulfilled, (state, action) => {
            state.loading = false;
            state.profileData = action.payload;
          })
          .addCase(fetchProfile.rejected, (state, action) => {
            state.loading = false;
            state.error = action.payload || 'Failed to fetch profile data';
          });
      },
    });
    
    export const { clearProfile } = profileSlice.actions;
    export default profileSlice.reducer;
    
  3. storeConfig.js에 리듀서 추가:

    redux/storeConfig.js 파일에 새로 생성한 slice 리듀서를 추가합니다.

    import { configureStore } from '@reduxjs/toolkit';
    import authReducer from './authSlice';
    import businessCardReducer from './businessCardSlice';
    import profileReducer from './profileSlice'; // 새 slice 추가
    
    const storeConfig = configureStore({
      reducer: {
        auth: authReducer,
        businessCard: businessCardReducer,
        profile: profileReducer, // 새 리듀서 등록
      },
    });
    
    export default storeConfig;
    

2. 사용자 정의 Hook 생성

  1. Custom Hook 파일 생성: redux 디렉터리 안에 Custom Hook 파일을 생성합니다. 예: redux/profileState.js.

  2. Hook 구성:

    // redux/profileState.js
    import { useSelector, useDispatch } from 'react-redux';
    import { fetchProfile, clearProfile } from './profileSlice';
    
    export const useProfile = () => {
      const profile = useSelector((state) => state.profile);
      const dispatch = useDispatch();
    
      const fetchUserProfile = (memberId) => {
        return dispatch(fetchProfile(memberId)); // return 추가
      };
    
      const clearUserProfile = () => {
        dispatch(clearProfile());
      };
    
      return {
        profile,
        fetchUserProfile,
        clearUserProfile,
      };
    };
    
  3. 컴포넌트에서 Custom Hook 사용:

    컴포넌트에서 Custom Hook을 사용하여 상태를 관리합니다.

    // components/ProfileComponent.js
    import React, { useEffect } from 'react';
    import { View, Text, ActivityIndicator } from 'react-native';
    import { useProfile } from '../redux/profileState';
    
    const ProfileComponent = ({ memberId }) => {
      const { profile, fetchUserProfile } = useProfile();
    
      useEffect(() => {
        fetchUserProfile(memberId);
      }, [memberId]);
    
      if (profile.loading) {
        return <ActivityIndicator size="large" color="#0000ff" />;
      }
    
      if (profile.error) {
        return <Text>Error: {profile.error}</Text>;
      }
    
      return (
        <View>
          {profile.profileData ? (
            <Text>Profile Name: {profile.profileData.name}</Text>
          ) : (
            <Text>No Profile Data</Text>
          )}
        </View>
      );
    };
    
    export default ProfileComponent;
    

3. API 클라이언트 설정 (옵션)

API 요청이 필요한 경우, redux/apiClient.js 파일에 API 클라이언트를 추가하고 사용합니다.

// redux/apiClient.js
import axios from 'axios';

const API_BASE_URL = '<http://10.0.2.2:8080>'; // API URL 설정

export const getApiClient = (token = '') => {
  const headers = {
    'Content-Type': 'application/json',
  };
  if (token) {
    headers.Authorization = `Bearer ${token}`;
  }

  const instance = axios.create({
    baseURL: API_BASE_URL,
    headers,
    timeout: 5000,
  });

  instance.interceptors.request.use(
    (config) => {
      console.log('Request Config:', config);
      return config;
    },
    (error) => {
      console.error('Request Error:', error);
      return Promise.reject(error);
    }
  );

  instance.interceptors.response.use(
    (response) => {
      console.log('Response Data:', response.data);
      return response;
    },
    (error) => {
      if (error.response) {
        console.error('Server Error:', error.response.data);
      } else if (error.request) {
        console.error('No Response from Server:', error.request);
      } else {
        console.error('Request Setup Error:', error.message);
      }
      return Promise.reject(error);
    }
  );

  return instance;
};

새로운 기능 추가 시 체크리스트:

Originally posted by @Hello-LSY in https://github.com/Hello-LSY/Baenang/issues/28#issuecomment-2385675831