[FE] React-NodeBird 리뉴얼 정리(4)

N·2023년 3월 1일
0

react-nodebird-front

목록 보기
7/8

redux thunk란?

  • redux의 미들웨어(https://github.com/reduxjs/redux-thunk/blob/master/src/index.ts)

  • 설치방법
    npm i redux-thunk

  • 미들웨어란?
    redux의 기능을 향상 시켜주는 역할을 말함, redux에 없는 기능을 추가해주는 역할

  • thunk는 프로그래밍 용어, '지연되는'의 의미

  • redux thunk는 redux가 비동기 action creator를 사용할 수 있게 한다. 따라서 비동기 action 하나에서 여러번의 동기 dispatch를 할 수 있다.

  • redux thunk의 장점과 사용하는 이유
    - dispatch를 여러번 할 수 있다.
    - 로그인을 예로 들면, 로그인과 로그아웃을 실제로 서버에 요청을 보내고 응답을 받아야 되는건데 프론트에서 요청을 보낼 때 state로 관리하면 실제 상황과 일치하지 않을 수 있다. request, success, failure로 구별해서 action을 만들어줘야 정확하다.

  • redux thunk와 redux saga의 차이점
    - thunk는 dispatch를 여러번 할 수 있는 기능만 제공하지만 saga는 더 많은 기능(이펙트)을 제공
    e.g.
    - saga는 delay 기능을 제공하지만 thunk는 setTimeout으로 별도로 만들어줘야 한다
    - 실수로 클릭을 2번하면 thunk는 두번 다 요청이 가지만 saga는 take latest 기능이 동일한 요청이 동시에 들어왔을 때 마지막에 한번만 요청한다
    - saga에는 throttle, debounce 기능이 있다. 스크롤을 내릴 때 스크롤이벤트가 1초에 수백번씩 발생하는데 이벤트 리스너 안에 비동기 요청을 넣어놓으면 스크롤 한번 내리는데 서버에 요청이 수백개가 간다(DOS 공격, 배포하면 디도스 공격이 된다) throttle은 1초에 몇번까지 허용해준다. 정해진 횟수를 초과하면 초과된 요청부분은 차단된다.

  • 강좌에서는 redux thunk 보다 redux saga를 더 많이 쓸 예정

// redux-thunk 예시
//configureStore.js
import thunkMiddleware from "redux-thunk";

const configureStore = () => {
    const middlewares = [thunkMiddleware, loggerMiddleware];
    const enhancer =
        process.env.NODE_ENV === "production"
            ? compose(applyMiddleware(...middlewares)) 
    //배포용
            : composeWithDevTools(applyMiddleware(...middlewares)); 
  //개발용,히스토리쌓는용, 배열은 saga나 thunk를 위해서 제작
    const store = createStore(reducer, enhancer);
    return store;
};

//reducer.js
export const loginAction = (data) => {
   return (dispatch, getState) => {
       const state = getState();
       dispatch(loginRequestAction());
         axios.post("/api/login")
           .then((res) => {
               dispatch(loginSuccessAction(res.data));
         })
           .catch((err) => {
               dispatch(loginFailureAction(err));
            });
   };
};
 export const loginRequestAction = (data) => {
     return {
        type: "LOG_IN_REQUEST",
        data,
    };
 };
 export const loginSuccessAction = (data) => {
    return {
        type: "LOG_IN_SUCCESS",
         data,
     };
};
export const loginFailureAction = (data) => {
    return {
        type: "LOG_IN_FAILURE",
        data,
   };
};

redux-saga 사용하기

  • 설치방법
    npm i redux-saga

  • all은 배열을 인자로 받고 배열안에 있는 것을 모두 실행시킨다

  • fork는 동기(제너레이터)함수를 호출하는 것(블로킹)

  • call은 fork와 비슷한 역할이지만 비동기함수를 호출한다는 차이점이 있다(논블로킹)

  • take 괄호 안에 있는 액션이 실행되기 전까지 기다리겠다는 뜻

  • while take는 동기적으로 동작하지만 takeEvery(요청에 따른 모든 응답), takeLatest(마지막 요청에 대한 응답만), takeLeading(첫번째 요청에 대한 응답만)는 비동기로 동작한다는 차이가 있다.

  • takeLatest와 takeLeading의 단점은 요청은 여전히 여러번 간다는 점이다. 요청 자체를 제한하기 위해서는 throttle('ADD_POST', addPost, 10000); 이런 방법으로 재요청을 할 수 없는 시간을 주는 방법을 사용할 수 있다.

  • 예시

// configureStore
import rootSaga from "../sagas";

const configureStore = () => {
    const sagaMiddleware = createSagaMiddleware();
    const middlewares = [sagaMiddleware, loggerMiddleware];
    const enhancer =
        process.env.NODE_ENV === "production"
            ? compose(applyMiddleware(...middlewares)) 
            //배포용
            : composeWithDevTools(applyMiddleware(...middlewares)); 
            //개발용,히스토리쌓는용, 배열은 saga나 thunk를 위해서 제작
    const store = createStore(reducer, enhancer);
    store.sagaTask = sagaMiddleware.run(rootSaga);
    return store;
};

// sagas/index.js
import { all, fork, take } from "redux-saga/effects";

function* watchLogin() {
    yield take("LOG_IN");
}

function* watchLogout() {
    yield take("LOG_OUT");
}

function* watchAddPost() {
    yield take("ADD_POST");
}

export default function* rootSaga() {
    // 제너레이터
    yield all([fork(watchLogin), fork(watchLogout), fork(watchAddPost)]);
}

제너레이터

  • 중단점(yield)이 있는 함수
  • next()를 붙이면 실행된다
  • 예시
// 함수 뒤에 별을 붙인다
const gen = function* () {
	console.log(1);
  	yield; // 멈춤
    console.log(2);
  	yield; // 멈춤
    console.log(3);
  	yeild 4; // 멈춤 & value = 4
}

// 실행할 때는 next()를 붙인다
const generator = gen();
generator.next();
// 1
//{value: undefined, done: false}
generator.next();
// 2
//{value: undefined, done: false}
generator.next();
//3
//{value: 3, done: false}
generator.next();
//{value: undefined, done: true}
  • 절대 멈추지 않는 함수 while(true) -> saga에서는 다르게 동작한다. 무한의 이벤트 리스너
// 제너레이터의 형식
let i = 0;
const gen = function*() {
	while (true) {
    	yield i++;
    }
}
//redux-saga에서 제너레이터를 사용하는 방법

import { all, fork, call, take, put } from "redux-saga/effects";
import axios from "axios";

unction logInAPI() {
    return axios.post("/api/login");
}

function* logIn() {
    try{
        const result = yield call(logInAPI);  
      //call과 fork의 차이 ->call은 동기 함수 호출, 블로킹된다 fork 비동기 함수 호출, 논블로킹
      //put은 dispatch라고 보면 된다!

        // fork를 사용하면 이렇게
        // const result = yield fork(logInAPI)
        axios.post("/api/login")
        .then((result) => {
            yield put({
                type: "LOG_IN_SUCCESS",
                data: result.data,
            });
        })
      
        // take는 LOG_IN이라는 액션이 실행될 때까지 기다리겠다는 뜻
        function* watchLogIn() {
          // takeLatest는 요청은 여러번 가지만 응답이 여러번 로딩중일 때 가장 마지막 응답만 유효하고 나머지 응답을 취소하는 원리 -> 요청도 못가게 막으려면 쓰로틀링이나 디바운싱을 하자 
            yield takeLatest("LOG_IN_REQUEST", logIn);
        }

쓰로틀링과 디바운싱

출처 : https://www.zerocho.com/category/JavaScript/post/59a8e9cb15ac0000182794fa

  • 쓰로틀링: 마지막 함수가 호출된 후 일정 시간이 지나기 전에 다시 호출되지 않도록 하는 것, 스크롤 이벤트에 사용하면 좋다
  • 디바운싱: 연이어 호출되는 함수들 중 마지막 함수(또는 제일 처음)만 호출하도록 하는 것, 검색창에 단어를 입력할 때 사용하면 좋다
function*watchAddPost() {
	//2초 안에 한번만 요청을 보내도록 한다
    yield throttle('ADD_POST_REQUEST', addPost, 2000);
    
}

로그인 할 때 saga와 reducer와 페이지의 동작 순서(또는 흐름)

  • 로그인 버튼 클릭 - request 액션 실행(dispatch) - saga 실행 - loding: true - saga에서 1초 뒤에 success - reducer 실행 - isLoggedIn: true로 변경 - login form에서 user profile로 화면이 전환된다

  • 어떤 액션이 여러개의 reducer의 데이터를 바꿔야 한다면 saga에서 여러 reducer를 부르면 된다

npm i -D eslint-plugin-jsx-a11y

  • a11y는 접근성. 장애인에게도 서비스를 제공할 수 있어야 하는데 시각 장애인들은 스크린 리더에 의존을 많이 한다. 스크린 리더가 화면을 잘 이해할 수 있는지를 평가하는게 접근성

shortid 라이브러리

  • 아이디를 랜덤하게 만들어준다
  • npm i shortid

불변성의 핵심

  • 바뀌는 부분만 바꾸고 나머지는 참조를 유지한다. 그래야 메모리를 아낄 수 있다

e.g.

       case ADD_COMMENT_SUCCESS: {
            const postIndex = state.mainPosts.findIndex(
                (v) => v.id === action.data.postId
            );
            const post = { ...state.mainPosts[postIndex] };
            post.Comments = [
                dummyComment(action.data.content),
                ...post.Comments
            ];
            const mainPosts = [...state.mainPosts];
            mainPosts[postIndex] = post;
            return {
                ...state,
                mainPosts,
                addPostLoading: false,
                addPostDone: true,
            };
        }
  • 코드의 가독성이 떨어진다
  • 불변성을 유지하면서 코드의 가독성을 좋게 하기 위해서 사용하는 라이브러리가 immer
profile
web

0개의 댓글