[TIL] 230705

이세령·2023년 7월 5일
0

TIL

목록 보기
49/118

axios interceptor

interceptor: 중간에 가로채기

  • env 활용하기
    .env
    변수=http://localhost:4000
불러와서 사용하기
    process.env.변수
  • axios.create()
    api.js
import axios from "axios";

const instance = axios.create({
  baseURL: `${process.env.REACT_APP_SERVER_URL}`,
}); // 새로운 인스턴스

export default instance;
한단계 가공할 수 있다.

axios → 생성한 파일 이름(api)
// axios
const { data } = await api.get(
      `${process.env.REACT_APP_SERVER_URL}/todos`
    );

// 가공 
const { data } = await api.get("/todos");
  • intercept
    활용: content type, token인증, server응답 오류 처리, 통신 시작/종료 전역상태(스피너..) 관리 등
    import axios from "axios";
    
    const instance = axios.create({
      baseURL: `${process.env.REACT_APP_SERVER_URL}`,
      timeout: 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;

thunk

비동기 통신 미들웨어

  • 미들웨어?
    중간 작업을 넣기 위해 사용, 보통 통신을 위해 사용함

    thunk, saga 등등

  • thunk?
    redux에서 많이 사용하는 미들웨어

    1) thunk 함수를 만들기 : createAsyncThunk

    → reduxtoolkit 내장 api

    2) createSlice >extraReducer에 thunk 등록

    3) dispatch 에 함수넣기

    4) 테스트

  • createAsyncThunk()

    변수명 : 보통 앞에 언더바(_)를 2개 넣어준다.

    export const __addNumber = createAsyncThunk(
      "ADD_NUMBER_WAIT",
      (payload, thunkAPI) => {
        // 수행하고 싶은 동작 : 3초를 기다리게 할 예정
        setTimeout(() => {
          thunkAPI.dispatch(addNumber(payload)); 
    // 기존의 dispatch를 수행한다.
        }, 3000);
      }
    );

    추가 작업을 넣고 싶은 함수를 추가 작업(setTimeout 등) 내에 넣어주면 된다.

  • 심화

    thunk 함수 구현 → 
    
    리듀서 로직 구현: reducers → extraReducers 
    (서버 통신은 100% 성공 보장 x), 
    지금까지의 redux state(todos, counter) 
    앞으로는 state(isLoading, isError, data)을 통해 화면 제어 → 
    
    기능 확인(network)
  • thunkAPI.fulfillWithValue()
    Promise → resolve 시 dispatch(reducer에게 action, payload를 넘겨준다.)
  • thunkAPI.rejectWithValue()
    Promise → reject시 dispatch
  • todosSlice.js
    import { createSlice } from "@reduxjs/toolkit";
    import { createAsyncThunk } from "@reduxjs/toolkit";
    import axios from "axios";
    
    const initialState = {
      todos: [],
      isLoading: false,
      error: null,
    };
    
    export const __getTodos = createAsyncThunk(
      "getTodos",
      async (payload, thunkAPI) => {
        try {
          const response = await axios.get("http://localhost:4000/todos");
          console.log("res", response);
          // Promise => resolve 요청 성공 시 dispatch
          return thunkAPI.fulfillWithValue(response.data);
        } catch (error) {
          //
          console.log("error", error);
          return thunkAPI.rejectWithValue(error);
        }
      }
    );
    
    const todosSlice = createSlice({
      name: "todos",
      initialState,
      reducers: {},
      extraReducers: {
        [__getTodos.pending]: (state, action) => {
          // 진행중일 때
          state.isLoading = true;
          state.isError = false;
        },
        [__getTodos.fulfilled]: (state, action) => {
          console.log("fulfilled", action);
          state.isLoading = false;
          state.isError = false;
          state.todos = action.payload; // 서버에서 넘어 온 값
        },
        [__getTodos.rejected]: (state, action) => {
          state.isLoading = false;
          state.isError = true;
          state.error = action.payload; // error 처리한 값이 들어갈 것이다.
        },
      },
    });
    
    export const {} = todosSlice.actions;
    export default todosSlice.reducer;
  • App.js
    import { useDispatch, useSelector } from "react-redux";
    import "./App.css";
    import { useEffect } from "react";
    import { __getTodos } from "./redux/modules/todosSlice";
    
    function App() {
      const dispatch = useDispatch();
      const { isLoading, error, todos } = useSelector((state) => {
        return state.todos;
      });
    
      useEffect(() => {
        dispatch(__getTodos());
      }, [dispatch]);
      if (isLoading) {
        return <div>로딩중...</div>;
      }
    
      if (error) {
        return <div>{error.message}</div>;
      }
    
      return (
        <>
          <div>
            {todos.map((todo) => {
              return <div key={todo.id}>{todo.title}</div>;
            })}
          </div>
        </>
      );
    }
    
    export default App;

새 프로젝트 세팅

  1. 설치
yarn add redux react-redux
yarn add @reduxjs/toolkit 
yarn add json-server
yarn add axios
  1. 세팅할 폴더 및 파일
src -> redux -> config, modules
root -> db.js
  1. 실행
yarn json-server --watch db.json --port 포트번호
yarn start

custom hooks

중복되는 코드를 줄이기 위해서 나만의 hook을 만드는 것

ex) input에 들어가는 value, onchange마다 e.target.value ~~

  • 세팅
src -> hooks -> customhook이름.js
import { useState } from "react";

const useInput = () => {
  // state
  const [value, setValue] = useState("");

  // handler
  const handler = (e) => {
    setValue(e.target.value);
  };
  return [value, handler];
};

export default useInput;

React query

요즘에 많이 사용
Query: 데이터 요청
Mutation: 데이터 변경
Query Invalidation: 무효화, 최신 상태가 아닌 것을 무효화 하고 최신화한다.

  • 설치
yarn add react-query
  • App.jsx 세팅
    **import { QueryClient, QueryClientProvider } from "react-query";**
    
    **const queryClient = new QueryClient();**
    const App = () => {
    
      return (
        **<QueryClientProvider client={queryClient}>**
          <Router />;
        **</QueryClientProvider>**
      );
    };
  • 통신 관련 로직들
    api/todos.js
    import axios from "axios";
    
    const getTodos = async () => {
    	const response = await axios.get(`${process.env.REACT_APP_SERVER_URL}/todos`);
      console.log(response);
    	return response.data;
    };
    
    export { getTodos };
  • 가져와서 사용하기
    react query가 편한이유!! isLoading, isError, data 처리를 따로 하지 않아도 된다.

TodoList.jsx
useQuery('쿼리의 이름', 가져온 api 메서드)

    import React from "react";
    import { StyledDiv, StyledTodoListHeader, StyledTodoListBox } from "./styles";
    import { getTodos } from "../../../api/todos";
    import { useQuery } from "react-query";
    
    function TodoList({ isActive }) {
      // const todos = useSelector((state) => state.todos);
      const { isLoading, isError, data } = useQuery("todos", getTodos);
    	
      if (isLoading) {
        return <h1>로딩중입니다~~~~</h1>;
      }
    
      if (isError) {
        return <h1>오류가 발생하였습니다....!!!</h1>;
      }
      
      return (
        <StyledDiv>
          <StyledTodoListHeader>
            {isActive ? "해야 할 일 ⛱" : "완료한 일 ✅"}
          </StyledTodoListHeader>
          <StyledTodoListBox></StyledTodoListBox>
        </StyledDiv>
      );
    }
    
    export default TodoList;
  • mutation
    todos.js
    // 추가
    const addTodo = async (newTodo) => {
      await axios.post(`${process.env.REACT_APP_SERVER_URL}/todos`, newTodo);
    };
    
    export { getTodos, addTodo };

해당 함수를 추가해서 query로 불러와준다!

Input.jsx

    const queryClient = useQueryClient();
      const mutation = useMutation(addTodo, { 
    // 공식문서 처럼 비동기 함수를 첫 인자에 넣어줘도 되지만, 따로 만들어서 불러오는 것이 보기 좋다.
        onSuccess: () => {
          queryClient.invalidateQueries(**"todos"**); // 해당 key를 무효화 하고 다시 불러와줘
          console.log("성공하였습니다!");
        },
      });
    .
    .
    .
    const newTodo = {
          title,
          contents,
          isDone: false,
          id: uuidv4(),
        };
    
        // todo를 추가하는 reducer 호출
        // 인자 : payload
        // dispatch(addTodo(newTodo));
        mutation.mutate(newTodo); // 추가하는 기능이 react query를 통해 실행된다.
    
        // state 두 개를 초기화
        setTitle("");
        setContents("");

입력이 성공하면 queryClient의 api인 invalidateQueries를 사용하여 기존에 TodoList.jsx에서 선언한 const { isLoading, isError, data } = useQuery("todos", getTodos); 여기서 사용한 todos key를 넣어주어 무효화 하고 다시 불러와야 데이터가 변경되어 화면에 렌더링 된다.

성공하면 queryClient.invalidateQueries("key"); 사용하는 것 숙지

  • 정리
    1) useQuery
    const { isLoading, isError, data } = useQuery("key", 비동기함수);

인자 1 key
key는 어떤 컴포넌트에 있던지 같은 쿼리, 데이터를 보장하게 해준다. key는 unique 해야한다.

배열 형태로 해석된다. 
    useQuery('todos',..) 
    querykey === ['todos']

인자 2 쿼리함수
promise 객체 return → 비동기 함수일 것이다.
resolve, error 오류처리 필요

결과물
isLoading + isSuccess + isError가 담긴 object

thorottling & debouncing

ex) 좋아요 버튼 같은 경우 연속 event가 발생하여 과부하가 발생할 수 있다.

thorottling

많은 event , 실행은 1번 → 일정 구간마다 실행되도록 조절해주는 것

let timerId = null;
const throttle = (delay) => {
    if (timerId) {
      return;
    }

    console.log(`API 요청 실행 ${delay}ms 동안 추가요청은 안받습니다!`);
    timerId = setTimeout(() => {
      console.log(`${delay}ms 지남 추가요청 받습니다!`);
      timerId = null;
    }, delay);
  };

쓰로틀링 → 페이지 이동 했을 경우 이대로 함수를 사용하면 쓰로틀링이 계속 실행되고 있기 때문에 메모리 누수가 발생한다.

useEffect(() => {
    return () => {
      // 언마운트 시
      if (timerId) {
        clearTimeout(timerId);
      }
    };
  });

렌더링되면 지우는 것으로 메모리 누수를 방지할 수 있다.

debouncing

연속해서 event 발생시 → 마지막으로부터 delay이 경과 후 한번만 호출

let timerId = null;
// 반복적인 이벤트 이후, delay가 지나면 function 실행
  const debounce = (delay) => {
    if (timerId) {
      // null이 아닐 때
      // 할당되어 있는 timerId에 해당하는 타이머 제거
      clearTimeout(timerId);
    }
    timerId = setTimeout(() => {
      console.log(`마지막 요청으로부터 ${delay}ms 지났으므로 API 요청 실행`);
      timerId = null;
    }, delay);
  };

Lodash 라이브러리

설치

yarn add lodash

밑줄처럼 씀

import _ from "lodash";
const 변수 = _.debounce(함수,)

제대로 사용하기 위해서는 useCallback을 사용해야 한번에 바뀐다.

const handleSearchText = useCallback(
    _.debounce((text) => {
      setSearchText(text);
    }, 2000),
    []
  );
  • 직접 debounce 구현
    const debounce = (callback, delay) => {
        let timerId = null;
        return (...args) => {
          if (timerId) clearTimeout(timerId);
          timerId = setTimeout(() => {
            callback(...args);
          }, [delay]);
        };
      };

내부에 있는 close함수를 반환받는다.

기존에 사용하던 라이브러리를 다른 라이브러리를 통해 간편하게 사용하는 법을 배울 수 있었다.
사용법만 익숙해지면 이해를 더 잘 할 수 있기 때문에 lv4 과제를 설계하고 작업하는 과정에서 어디에 적용할지 생각할 필요가 있어보인다.

profile
https://github.com/Hediar?tab=repositories

0개의 댓글