본문 바로가기
항해99/실전 WIL | TIL

[TIL-018] redux-toolkit, axios - instance와 interceptor, custom Hook

by junvely 2023. 4. 29.

Today목표 : 4/29일 redux-toolkit , axios - instance와 interceptor


유용한 사이트, tools

1. 가변성, 불변성 함수 확인 가능한 사이트 => no mutates 불변성 유지

 

Does it mutate?

The lastIndexOf() method returns the last index at which a given element can be found in the array, or -1 if it is not present. The array is searched backwards, starting at fromIndex.Array.prototype.lastIndexOf ( searchElement [ , fromIndex ] )

doesitmutate.xyz

 

3. uuid 설치 => 고유값 생성

yarn add uuid

 

 

redux - toolkit✅

1. 설치

yarn add react-redux @reduxjs/toolkit

2. store 생성

import { configureStore } from "@reduxjs/toolkit";
import counter from "redux/modules/counter";
import users from "redux/modules/users";

const store = configureStore({
  reducer: { counter, users },
});
});

export default store;

3. reducer, action creator 함수

const counterSlice = createSlice({
  name: "counter",
  initialState,
  reducers: {
    plusCounter: (state, action) => {
      state.number = state.number + action.payload;
    },
    minusCounter: (state, action) => {
      state.number = state.number - action.payload;
    },
  },
});

// conterSlice 안에 reducer, creator 함수가 모두 들어있다.
export default counterSlice.reducer;
export const { plusCounter, minusCounter } = counterSlice.actions;

4. App.js

// src/App.js

import React from "react";
import { useSelector } from "react-redux";

const App = () => {
  // Store에 있는 todos 모듈 state 조회하기
  const todos = useSelector((state) => state.todos);

  // Store에 있는 counter 모듈 state 조회하기
  const counter = useSelector((state) => state.counter);

  return <div>App</div>;
};

export default App;

 

redux toolkit - devtools

redux tookit에서는 devtools가 내장되어 있다. 현재 프로젝트의 state 상태라던가, 어떤 액션이 일어났을 때 그 액션이 무엇이고, 그것으로 인해 state가 어떻게 변경되었는지 등 리덕스를 사용하여 개발할 때 아주 편리하게 사용가능하다.

1. 확장 프로그램 설치

 

Redux DevTools

Redux DevTools for debugging application's state changes.

chrome.google.com

리액트 프로젝트에서 리덕스를 사용하고 있으면 플러그인에 녹색으로 불이 켜진다.

state에 대한 디버깅이 가능하므로 유용하게 사용해 보기

 

참고

1. 파일명은 counter.js, todos.js로 하는 개발자도 있고, counterSlice.js, todosSlice.js로 하는 개발자도 있다.

2. redux toolkit 에는 immer라는 기능이 내장되어있다 = 불변성 유지해 준다. (가변적 함수를 사용해도 불변성 유지해줌)

3.  Redux의 Flux 패턴에 관하여

1. Flux 만화로 이해하기

 

Flux로의 카툰 안내서

원문: https://medium.com/code-cartoons/a-cartoon-guide-to-flux-6157355ab207 Flux…

bestalign.github.io

2. Flux 만화로 이해하기

 

Flux와 Redux

이 글에서는 Facebook에서 React와 함께 소개한 Flux 아키텍처에 대해 알아보고 Flux를 구현한 Redux 라이브러리를 살펴본 후 이를 적용한 간단한 React 애플리케이션을 작성해보겠다. 본문에 사용된 코

taegon.kim

 

 

 

axios - instance와 interceptor

1. axios - interceptor(가로채기) 필요

1) port 번호가 3001 => 4000 등으로 변경될 경우 호출을 일일히 찾아서 변경해 줘야함

axios.get("http://localhost:3001/todos");
axios.post("http://localhost:3001/todos", todo);
axios.delete(`http://localhost:3001/todos/${todoId}`);

2) 요청 시 마다 어떤 로그를 찍어서 확인해야 할 경우 

console.log('요청 시작합니다...!');

 

axios interceptor는 요청과 < - > 응답 사이에서 흐름을 가로채서 여러분이 어떠한 코드 상의 관여를 할 수 있게 한다.

따라서, 요청 및 응답시에 필요한 다음과 같은 작업들을 한꺼번에 처리 할 수 있다.

  • 요청 Header 추가(토큰, 세션 등)
  • 인증 관리(서버와 약속한)
  • 로그 관련 로직 삽입
  • 에러 핸들링

 

2. axios 코드 리팩토링

1) 환경변수 설정 => root 경로에 .env파일 생성

프로젝트 전반에서 환경변수로 URL 사용가능 port가 변경되도 한쪽에서 관리 가능 => 서버 껐다가 켜야 함

환경정보는 github에 올라가지 않도록 gitignore에 추가

 

2) aixos => instance(객체)를 생성해서 api로 사용

import axios from "axios";

// 새로운 instance를 만들어 config,log 등 intercept 처리하여 가공한 axios로 사용할 것
const instance = axios.create({
  baseURL: process.env.REACT_APP_SERVER_URL,
});

export default instance;

api로 import하여 axios 대체하여 사용

import axios from "axios";
const fetchTodos = async () => {
	const { data } = await api.get(`/todos`); //baseURL있기 때문에 생략 가능
   	setTodos(data);
  };

 

3) aixos => instercept를 이용해 요청과 응답 사이에 관여해보기

요청과 응답 시 마다 그 전에 실행됨

import axios from "axios";

// 새로운 instance를 만들어 config,log 등 intercept 처리하여 가공한 axios로 사용할 것
const instance = axios.create({
  baseURL: process.env.REACT_APP_SERVER_URL,
  //  서버 통신을 몇 초까지 기다릴 것인지, 그 이상 기다리게되면 에러를 반환함
  timeout: 1, // 밀리초기 때문에 1은 엄청나게 작은 초 무조건 에러남
});

instance.interceptors.request.use(
  // 요청을 보내기 전 수행되는 함수
  function (config) {
    console.log("인터셉터 요청 성공!");
    return config;
  },

  // 오류 요청을 보내기 전 수행되는 함수
  function (error) {
    console.log("인터셉터 요청 오류!");
    return Promise.reject(error);
  }
);

instance.interceptors.response.use(
  // 응답을 내보내기 전 수행되는 함수
  function (response) {
    console.log("인터셉터 요청을 받았습니다.");
    return response;
  },

  // 오류 응답을 내보내기 전 수행되는 함수
  function (error) {
    console.log("인터셉터 응답 오류발생.");

    return Promise.reject(error);
  }
);

export default instance;

=> 이를 활용하여 어떤 요청을 할 때

1. content type에 대해 지정하거나

2. 토큰의 인증을 삽입하여 보낸다거나

3. 서버 응답에 대한 에러를 핸들링 한다거나

4. 통신의 시작과 종료에 관한 전역상태를 관리하여 로딩 스피너나, 프로그래스 바 등을 구현할 수도 있다.

예시) 

const axiosAuth = axios.create({
    baseURL: 'https://api.urscent.co.kr/auth',
    headers: {
      'content-type': 'application/json',
    },
  });

  export const login = async (payload: { email: string; password: string }) => {
    try {
      const { data } = await axiosAuth.post('login', payload);
      return data;
    } catch (error) {
      if (error instanceof AxiosError) {
        console.error(error.response?.data.detail);
      }
      return false;
    }
  };

 

 

 

custom Hoook✅

cutom Hook

// src/hooks/useInput.js

import React, { useState } from "react";

const useInput = () => {
	// 2. value는 useState로 관리하고, 
  const [value, setValue] = useState("");

	// 3. 핸들러 로직도 구현
  const handler = (e) => {
    setValue(e.target.value);
  };

	// 1. 이 훅은 [ ] 을 반환하는데, 첫번째는 value, 두번째는 핸들러를 반환합니다.
  return [value, handler];
};

export default useInput;

사용부


import useInput from "./hooks/useInput";

const App = () => {
  const [title, onChangeTitleHandler] = useInput();
  const [body, onChangeBodyHandler] = useInput();

  return (
    <div>
      <input
        type="text"
        name="title"
        value={title}
        onChange={onChangeTitleHandler}
      />

      <input
        type="text"
        name="title"
        value={body}
        onChange={onChangeBodyHandler}
      />
    </div>
  );
};

export default App;

 

 

코드 리팩토링 참고사항✅

1. todo-list done / not yet zone

<TodoList isActive={true} />
<TodoList isActive={false} />

<StyledTodoListBox>
        {todos
          .filter((item) => item.isDone === !isActive)
          .map((item) => {
            return <Todo key={item.id} todo={item} isActive={isActive} />;
          })}
</StyledTodoListBox>

2. 유효성 검사

// 제목과 내용이 모두 존재해야만 정상처리(하나라도 없는 경우 오류 발생)
    // "01" : 필수 입력값 검증 실패 안내
    if (!title || !contents) {
      return getErrorMsg("01", { title, contents });
    }

3. confirm

  // [삭제] 버튼 선택 시 호출되는 함수(user의 confirmation 필요)
  const handleRemoveButton = () => {
    if (window.confirm(CONFIRM_MESSAGE)) dispatch(removeTodo(todo.id));
  };

4. HeightBox, marginRightBox

<HeightBox height={10} />
<RightMarginBox margin={10}>

function RightMarginBox({ margin, children }) {
  return <StyledDiv margin={margin}>{children}</StyledDiv>;
}

export default RightMarginBox;

const StyledDiv = styled.div`
  margin-right: ${(props) => props.margin}px;
`;