ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 10. 리덕스
    React 2023. 6. 20. 17:44

    리덕스란?

    리덕스 (Redux) 란?

    : 자바스크립트 애플리케이션을 위한 상태 관리 라이브러리

     

    -> 리덕스는 State 을 관리한다.

     

    -> 리덕스의 데이터 관리 과정은 위와 같다.

    => 좀 더 효율적으로 애플리케이션을 관리할 수 있다.

     

    Action 이란?

    : 간단한 JavaScript 객체이다.

     

    -> 우리가 수행하는 작업의 유형을 지정하는 'type' 속성이 있으며, 선택적으로 리덕스 저장소에 (Redux Store) 일부 데이터를 보내는데 사용되는 'payload' 속성을 가질 수 있다.

     

    Reducer 란?

    : 애플리케이션 상태의 변경 사항을 결정하고 업데이트된 상태를 반환하는 함수.

     

    -> 인수로 조치를 취하고 Store 내부의 상태를 업데이트한다.

    => State 과 action object 를 받은 후에 next state 을 return 한다.

     

    Redux Store 란?

    : 하나로 모으는 객체 저장소 (애플리케이션의 전체 상태 트리를 보유한다.)

     

    -> 내부 상태를 변경하는 유일한 방법은 해당 상태에 대한 Action을 전달하는 것.

    #Redux Store는 클래스가 아님. 몇 가지 Methods가 있는 객체일 뿐이다.

     


    미들웨어 없이 리덕스 카운터 앱 만들기

    리액트 앱 설치

    npx create-react-app my-app --template typescript

     

    리액트 라이브러리 설치

    npm install redux --save

     

    Counter UI 및 함수 생성

     

    Reducer 생성 

    -> reducers 폴더 안에 index.tsx

     

     

    Store 생성 및 Action 전달

    -> CreateStore()

     

    : 앱의 전체 상태 트리를 보유하는 Redux 저장소를 만든다.

    #앱에는 하나의 스토어만 있어야 한다.

     

    -> getState()

    Store | Redux

     

    Store | Redux

    API > Store: the core Redux store methods

    redux.js.org

     

    : 애플리케이션의 현재 상태 트리를 반환한다.

     

    -> subscribe()

    : change listner 를 추가한다.

     

    -> 작업이 전달될 때마다 호출되며 상태 트리의 일부가 잠재적으로 변경되었을 수 있다.

    => 다음 getState() 를 호출하여 콜백 내부의 현재 상태 트리를 읽을 수 있다.

     


    combineReducers / Todo 기능 추가

    root reducer와 sub reducer

    : 현재까지 counter 리듀서만 있는데 하나를 더 추가해주려면 Root 리듀서를 만들어서 그 아래 counter와 todos라는 서브(sub) 리듀서를 넣어준다.
    #Root 리듀서를 만들 때 사용하는 것이 combineReducers
     
    => reducers 폴더 안에 counter.tsx , index.tsx , todo.tsx 생성
     

     

    createStore 에 루트 리듀서로 대체

     


    Redux Provider

    : Redux Store 저장소에 액세스해야 하는 모든 중첩 구성 요소는 Redux Store 저장소를 사용할 수 있도록 한다.

    -> React Redux 앱의 모든 React 구성 요소는 저장소에 연결할 수 있으 므로 대부분의 응용 프로그램은 전체 앱의 구성 요소 트리가 내부에 있는 최상위 수준에서 <Provider> 를 렌더링한다. 그런 다음 Hooks 및 연결 API는 React 의 컨텍스트 메커니즘을 통해 제공된 저장소 인스턴스에 액세스할 수 있다.

     

    Provider 를 렌더링

    -> React Redux 앱의 모든 React 구성 요소는 저장소에 연결할 수 있으므로 대부분의 응용 프로그램은 전체 앱의 구성 요소 트리가 내부에 있는 최상위 수준에서 <Provider> 를 렌더링한다.

     

    Todo UI

     


    useSelector & useDispatch

    : provider 로 둘러 쌓인 컴포넌트에서 store 접근 리액트에 Hooks 가 있듯이 리덕스에도 Hooks 가 있는데, 그게 바로 useSelector와 useDispatch이다.

     

    useSelector

     
    -> useSelector Hooks 를 이용해서 스토어의 값을 가져올 수 있다.
     

    1. Root Reducer 에 RootState 타입을 생성

     

    2. 생성한 RootState 을 State 객체에 제공

     

    useDispatch

    : Store 에 있는 dispatch 함수에 접근하는 hooks

      

    리덕스 미들웨어

    : Action 을 dispatch 전달하고 리듀서에 도달하는 순간 사이에 사전에 지정된 작업을 실행할 수 있게 해주는 중간자.

    -> 로깅, 충돌 보고, 비동기 API와 통신, 라우팅 등을 위해 Redux 미들웨어를 사용한다.

     

     

    리덕스 로깅 미들웨어 생성하기

    https://www.freecodecamp.org/news/what-is-redux-middleware-and-how-to-create-one-from-scratch/

     

    1. 로깅 미들웨어 함수 생성

     

    2. 미들웨어 함수를 applyMiddleware 함수에 넣어주기

     

    3. createStore 에서 미들웨어 넣어주기

     


    Redux Thunk

    리덕스 Thunk란?

    : 리덕스를 사용하는 앱에서 비동기 작업을 할 때 많이 사용하는 방법이 redux-thunk

    -> 이것도 앞서 만들어본 logger 미들웨어 처럼 리덕스 미들웨어이다.

     

    비동기 작업을 해야 할 때는 ?

    -> 여러 경우가 있지만 서버에 요청을 보내서 데이터를 가져올 때 주로 비동기 요청을 보낸다.

     

    Thunk ?

    : "thunk"라는 단어는 "일부 지연된 작업을 수행하는 코드 조각"을 의미하는 프로그래 밍 용어이다.

    -> 지금 일부 논리(logic) 를 실행하는 대신 나중에 작업을 수행하는 데 사 용할 수 있는 함수 본문이나 코드를 작성할 수 있다.

     

    Axios 모듈 설치

    : Api request를 위한 모듈

    npm install axios --save posts

     

    리듀서 생성

     

    posts 데이터를 위한 요청 보내기

     -> 에러 발생

     

    에러 발생 이유는 ? 

    -> 원래 Actions은 객체여야 하는데 현재는 함수를 Dispatch 하고 있다.

    => 그래서 함수를 dispatch 할 수 있게 해주는 Redux-Thunk 미들웨어를 설치해서 사용해보자!

     

     

    redux-thunk 설치

    npm install redux-thunk --save

     

    redux-thunk 적용

     

    posts 데이터 화면에 표출

     

    actions 들은 actions 폴더로 따로 분리

     

    modern ES2015 형태로 변경

     

    => 이렇게 리덕스 던크를 사용함으로써 액션 생성자가 그저 하나의 액션 객체를 생성할 뿐 아니라, 그 내부 안에서 여러 가지 작업도 할 수 있게 만들어준다.

     


    Redux Toolkit

    리덕스 익스텐션이란 ?

    : Redux-Devtools는 Redux 앱을 위한 디버깅 플랫폼을 제공한다.

    -> 시간 여 행 디버깅 및 라이브 편집을 수행할 수 있다.

    -> 공식 문서의 일부 기능은 다음과 같다.

    - 모든 상태 및 작업 페이로드를 검사할 수 있다.

    - 작업을 "취소"하여 시간을 되돌릴 수 있다.

     

    설치 방법

    https://chrome.google.com/webstore/detail/redux

     

    패키지 설치하기

    npm install redux-devtools-extension --save

     

    #리덕스 툴킷에서는 devTools를 위한 파라미터가 있는데 기본적으로 사용을 할수있게 설정되어있다

    https://redux-toolkit.js.org/api/configureStore#parameters

     

    리덕스 툴킷

    : Redux 툴킷은 Redux 로직을 작성하기 위한 공식 권장 접근 방식.

    -> Redux 코어 를 둘러싸고 있으며 Redux 앱을 빌드하는 데 필수적이라고 생각하는 패키지와 기능이 포함되어 있다.

    -> Redux 툴킷은 제안된 모범 사례를 기반으로 하여 대부분의 Redux 작업을 단순화하고 일반적인 실수를 방지하고 Redux 애플리케이션을 더 쉽 게 작성할 수 있도록 한다.

     

    리덕스 툴킷으로 만들어진 카운터 앱 살펴보기 

    https://redux-toolkit.js.org/tutorials/quick-star

     

    Quick Start | Redux Toolkit

     

    redux-toolkit.js.org

     

    React에 Redux 스토어 제공

    : app/store.ts 저장소가 생성되면 src/index.js에서 애플리케이션 주위에 React-Redux <Provider> 를 배치하여 React 구성 요소에서 사용할 수 있도록 할 수 있다.

     

    -> 방금 만든 Redux 저 장소를 가져오고 주위에 <Provider> 를 배치하고 저장소를 prop으로 전달한다.

     

    Redux State Slice 생성

    : src/features/counter/counterSlice.js라는 새 파일을 추가한다.

     

    -> 해당 파일에서 Redux Toolkit의 createSlice API를 가져온다.

     

    스토어에 Slice Reducer 추가

    : 리듀서 매개변수 내부에 필드를 정의함으로써 스토어에 이 슬라이스 리듀서 함수를 사용 하여 해당 상태에 대한 모든 업데이트를 처리하도록 지시한다.

     

     

    React 컴포넌트에서 Redux State 및 Actions 사용 

     


    Redux Toolkit APIs

    컴포넌트 생성후 thunk 호출하기

    App.tsx

    function App() {
      const [isTestOpen, setISTestOpen] = useState(true);
    
      return (
        <div className="App">
          <header className="App-header">
            <img src={logo} className="App-logo" alt="logo" />
    
            <button onClick={() => setISTestOpen(prev => !prev)}>Toggle</button> 
            {isTestOpen && <Test />} 
    
            <Counter />
          </header>
        </div>
      );
    }

     

    features/counter/Test.tsx

    import React, { useEffect } from 'react'
    import { useAppDispatch } from '../../app/hooks'
    import { incrementAsync } from './counterSlice';
    
    const Test = () => {
    
      const dispatch = useAppDispatch();
      useEffect(() => {
        dispatch(incrementAsync(10));
    
        return () => {
        }
      }, [])
    
        return (
            <div>test</div>
        )
    }
    export default Test

     

    => 맨 처음에 앱을 실행하면 state변수가 true이다.

    => Test 컴포넌트는 마운트된다. (비동기 요청을 보낸다)

    => 때문에 pending, fulfilled가 보인다.

    => counter 앱에도 10이 증가한다.

     

    비동기 요청 도중에 Test 컴포넌트가 unmount 된다면?

    (toggle 버튼을 두 번 빠르게 눌러 생성하자마자 없애보면)

    -> 그럼에도 불구하고 잘 작동된다!

    => useEffect() clean up 함수를 통해서 이를 해결할 수 있다!

     

    clean up

    1. dispatch()함수는 promise 객체를 반환한다.

    2. 그 객체를 받아, clean up 함수에 promise.abort() 를 추가한다.

     

    Test.tsx

    import React, { useEffect } from 'react'
    import { useAppDispatch } from '../../app/hooks'
    import { incrementAsync } from './counterSlice';
    
    const Test = () => {
    
      const dispatch = useAppDispatch();
      useEffect(() => {
        const promise = dispatch(incrementAsync(10));
    
        return () => {
          promise.abort()
        }
      }, [])
    
        return (
            <div>test</div>
        )
    }
    
    export default Test

     

    => 컴포넌트 마운트 후 빠르게 바로 언마운트 시키면 비동기 요청이 reject 된다.

     

    abort() 할 때 request 취소하기

    counterSlice.tsx

    export const fetchUsesAsync = createAsyncThunk(
      'counter/fetchUsers',
      async() => {
        await axios.get("https://jsonplaceholder.typicode.com/users")
      }
    )

     

    Test.tsx

    const Test = () => {
    
      const dispatch = useAppDispatch();
      useEffect(() => {
        const promise = dispatch(fetchUsesAsync());
    
        return () => {
          promise.abort()
        }
      }, [])
    
        return (
            <div>test</div>
        )
    }

    -> rejected가 되었는데, 요청이 보내지고 그에 대한 답이 돌아온다.

    => request 자체를 취소하겠다.

     

    async thunk 함수의 두번째 인자인 thunkAPI 객체를 받는다. 그 후에,

    1. controller 를 생성
    2. thunkAPI.signal에 abort에 대한 eventListener를 등록
    3. axios 요청에 추가
    export const fetchUsesAsync = createAsyncThunk(
      'counter/fetchUsers',
      async(_, thunkAPI) => {
        const controller = new AbortController();
        thunkAPI.signal.addEventListener('abort', () => {
          controller.abort();
        });
    
        await axios.get("https://jsonplaceholder.typicode.com/users", {
          signal: controller.signal
        })
      }
    )

     

     

    'React' 카테고리의 다른 글

    11. 도커를 이용한 리액트 실행  (0) 2023.06.27
    09. 리액트 Version 18  (0) 2023.06.19
    8. Next.js와 TypeScript  (0) 2023.05.29
    7. React TDD 를 이용한 간단한 앱 생성 및 배포  (0) 2023.05.22
    6. React TDD 기본  (0) 2023.05.22
Designed by Tistory.