Redux를 사용하는 이유

kyeongboo·2023년 5월 22일

Redux란 무엇이고 왜 사용하는가?

Redux에 대해서 알아보기 이전에 React 상태관리를 할 때 데이터의 흐름이 어떤식으로 진행되는지 알 필요가 있습니다.

React에서 부모 컴포넌트 레이아웃 안에 존재하는 자식 컴포넌트들은 부모 컴포넌트들의 상태를 props로 전달받게 되는데요. 그러다 부모 컴포넌트의 상태 값이 변하게 되면 자식 컴포넌트들은 변화된 상태에 맞게 리렌더링을 하게 됩니다.

그렇기 때문에 부모 컴포넌트의 state를 전달받을 자식 컴포넌트들의 깊이가 깊어질수록 부모 컴포넌트에서 최하위의 자식 컴포넌트까지 거쳐야하는 컴포넌트가 그만큼 많아지기 때문에 효율적인 상태 관리가 쉽지 않을 것입니다.

이런 이유로 우리는 상태를 관리하는데 있어서 상태 구성 요소를 최소화하기 위한 노력이 필요한데요!

바로 이럴 때 상태관리를 효율적으로 할 수 있도록 도와주는 도구가 Redux입니다.

위 이미지처럼 Redux를 사용하면 store를 사용하여 상태 값을 컴포넌트 안에 종속시키지 않고 컴포넌트 구조 바깥에서 상태관리를 할 수 있게 해줍니다.

Redux를 통해서 다음 구글 폰트 화면을 구현해보고 Redux Flow에 대해서 자세히 살펴보자.

<!DOCTYPE html>
<html>

<head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.1/redux.js"></script>
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link
        href="https://fonts.googleapis.com/css2?family=Anek+Malayalam:wght@300&family=Fascinate&family=Kdam+Thmor+Pro&family=Noto+Sans+KR:wght@300&family=Open+Sans&family=Roboto:wght@300&display=swap"
        rel="stylesheet">
    <style>
        .container {
            border: 5px solid black;
            padding: 10px;
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            gap: 10px;
        }

        .item {
            border: 5px solid #f6866a;
            height: 100px;
            padding: 20px;
            font-size: 32px;
        }

        .item:nth-child(1) {
            font-family: 'Anek Malayalam', sans-serif;
        }

        .item:nth-child(2) {
            font-family: 'Fascinate', cursive;
        }

        .item:nth-child(3) {
            font-family: 'Kdam Thmor Pro', sans-serif;
        }

        .item:nth-child(4) {
            font-family: 'Noto Sans KR', sans-serif;
        }

        .item:nth-child(5) {
            font-family: 'Open Sans', sans-serif;
        }

        .item:nth-child(6) {
            font-family: 'Roboto', sans-serif;
        }
    </style>
</head>

<body>
    <input type="text" id="inputText">
    <input type="button" value="눌러버렷!" onclick="
        const value = document.getElementById('inputText').value
        store.dispatch({type:'TEXT', text:value})
    " />
    <input type="button" value="Rounded!" onclick="
        store.dispatch({type:'STYLE_BORDER', borderRadius:'10px'})
    " />
    <input type="button" value="Circle!" onclick="
        store.dispatch({type:'STYLE_BORDER', borderRadius:'20px'})
    " />
    <input type="button" value="Rect!" onclick="
        store.dispatch({type:'STYLE_BORDER', borderRadius:'0px'})
    " />
    <input type="button" value="Red!" onclick="
        store.dispatch({type:'STYLE_BORDER_COLOR', border:'5px solid red'})
    " />
    <input type="button" value="Green!" onclick="
        store.dispatch({type:'STYLE_BORDER_COLOR', border:'5px solid green'})
    " />
    <input type="button" value="Blue!" onclick="
        store.dispatch({type:'STYLE_BORDER_COLOR', border:'5px solid blue'})
    " />
    <br>
    <br>
    <div class="container">
        <div id="item1" class="item"></div>
        <div id="item2" class="item"></div>
        <div id="item3" class="item"></div>
        <div id="item4" class="item"></div>
        <div id="item5" class="item"></div>
        <div id="item6" class="item"></div>
    </div>

    <script>
        const store = Redux.createStore(reducer)
        function reducer(state, action) {
            console.log(state, action)
            if (state === undefined) {
                return {
                    text: 'hello',
                    color: 'black',
                    border: '5px solid #f6866a',
                    borderRadius: '0px'
                }
            } else if (action.type === 'TEXT') {
                return {
                    ...state,
                    text: action.text
                }
            } else if (action.type === 'STYLE_BORDER') {
                return {
                    ...state,
                    borderRadius: action.borderRadius
                }
            } else if (action.type === 'STYLE_BORDER_COLOR') {
                return {
                    ...state,
                    border: action.border
                }
            }
        }
        function item1() {
            const state = store.getState()
            const $item = document.querySelector('#item1')
            $item.innerText = `${state.text}`
            $item.style.borderRadius = `${state.borderRadius}`
            $item.style.border = `${state.border}`
        }
        store.subscribe(item1)
        item1()

        function item2() {
            const state = store.getState()
            const $item = document.querySelector('#item2')
            $item.innerText = `${state.text}`
            $item.style.borderRadius = `${state.borderRadius}`
        }
        store.subscribe(item2)
        item2()

        function item3() {
            const state = store.getState()
            const $item = document.querySelector('#item3')
            $item.innerText = `${state.text}`
            $item.style.borderRadius = `${state.borderRadius}`
        }
        store.subscribe(item3)
        item3()

        function item4() {
            const state = store.getState()
            const $item = document.querySelector('#item4')
            $item.innerText = `${state.text}`
            $item.style.borderRadius = `${state.borderRadius}`
        }
        store.subscribe(item4)
        item4()

        function item5() {
            const state = store.getState()
            const $item = document.querySelector('#item5')
            $item.innerText = `${state.text}`
            $item.style.borderRadius = `${state.borderRadius}`
        }
        store.subscribe(item5)
        item5()

        function item6() {
            const state = store.getState()
            const $item = document.querySelector('#item6')
            $item.innerText = `${state.text}`
            $item.style.borderRadius = `${state.borderRadius}`
        }
        store.subscribe(item6)
        item6()
    </script>
</body>

</html>

1.Store 만들기

       const store = Redux.createStore(reducer)

store에 저장하고 싶은 사용자의 상태를 저장합니다. createStore 함수를 사용하여 만들 수 있으며 한 개의 프로젝트 당 하나의 store만 가질 수 있습니다.
변화가 일어날 데이터는 모두 하나의 스토어에 넣습니다. 데이터를 한 곳에 모아놓기 때문에 에러가 발생했을 때 각각의 컴포넌트에 가서 값을 확인할 필요가 없습니다.

2.reducer 만들기

       function reducer(state, action) {
            console.log(state, action)
            if (state === undefined) {
                return {
                    text: 'hello',
                    color: 'black',
                    border: '5px solid #f6866a',
                    borderRadius: '0px'
                }
            } else if (action.type === 'TEXT') {
                return {
                    ...state,
                    text: action.text
                }
            } else if (action.type === 'STYLE_BORDER') {
                return {
                    ...state,
                    borderRadius: action.borderRadius
                }
            } else if (action.type === 'STYLE_BORDER_COLOR') {
                return {
                    ...state,
                    border: action.border
                }
            }
        }

state는 reducer을 통해서만 값을 처리할 수 있습니다. reducer는 전달된 액션(action)과 이전 state값을 가지고 어떻게 값을 처리해줘야할지 결정합니다. 실제 값의 변경이 일어나서 reducer가 호출되면 액션(action)에 따라서 값이 바뀌게 되고 새로운 state값을 반환합니다.

3.Render 만들기와 Subscribe

      function item1() {
            const state = store.getState()
            const $item = document.querySelector('#item1')
            $item.innerText = `${state.text}`
            $item.style.borderRadius = `${state.borderRadius}`
            $item.style.border = `${state.border}`
        }
        store.subscribe(item1)
        item1()

실제 화면에 뿌려주는 역할을 합니다. 이 때 데이터를 store.getState(); 통해 받아야 합니다.

subscribe는 새로운 데이터가 생성될때마다 화면을 갱신하는 것입니다.

4.action과 dispatch

const addNumber = () => {
  return { type: "ADD" };
};
<input type="button" value="눌러버렷!" onclick="
        const value = document.getElementById('inputText').value
        store.dispatch({type:'TEXT', text:value})
    " />

상태변화가 필요할 때 action을 발생시킵니다. 이 액션은 하나의 객체로 표현이 됩니다. action은 Type이라는 필드를 필수적으로 가지고 있어야 하고 추가적으로 필요한 객체의 요소들은 필요에 의해 추가될 수 있습니다.

Redux Flow

action → dispatch → reducer → state변경 → subscribe → render → getState → state

1. dispatch가 일어납니다.
2. subscribe으로 해당 action이 들어옵니다.
3. state를 수정합니다.
4. subscribe을 통하여 값이 subscribe에 등록된 모든 요소에 state가 수정되었음을 전파합니다.
5. render에서 getState를 통해 값을 새로 받아옵니다.
6. 다시 render합니다.

profile
적극성과 커뮤니케이션 능력이 겸비된 개발자로 성장하자!

0개의 댓글