gatsby/redux/hooks 로 카운터 페이지 만들기

21094 단어 ReactReact

gatsby(React) 로 counter 페이지 만들기


어제까지 gatsby , redux , hooks 를 공부해서 이제 뭔지는 알았다. 근데 막상 코드를 짜니까 '이렇게 동작하는건 알겠는데 그래서 어떻게 짜야하는거지?' 라는 생각이 들어서 생각의 흐름을 정리해보려고 한다.

추가적으로 ducks 패턴이라는 것도 정리해볼건데, 우선 어떤 패턴이라는 것은 기존의 방식으로 했더니 어떤 문제점이나 불편함이 있어서 나온 것이기 때문에 기존의 방식대로 한번 작성해 보고 불편함을 직접 느껴보기로 하였다.

먼저 src폴더에 다음과 같은 구조를 추가하였다

src
|
|-components
|	|_Counter.js
|
|-redux
      |- actions
      |       |_counter.js  
      |
      |- constants
      |       |_counter.js
      |
      |- reducers
      |       |_counter.js
      |
      |_ configureStore.js

기존 방식


1. 액션 타입 정의


먼저 어떠한 액션들이 있는지 정의해준다.

< constants / counter.js >

export const INCREMENT = "increment"
export const DECREMENT = "decrement"

2. 액션 생성 함수 정의


액션들을 import하고 액션 형태로 return하는 함수인 액션 생성 함수를 작성한다.

< actions / counter.js >

import { INCREMENT, DECREMENT } from "../constants/counter"

export const increment = () => ({
  type: INCREMENT,
})

export const decrement = () => ({
  type: DECREMENT,
})

3. 리듀서 정의


리듀서를 작성해 준다. 리듀서는 stateaction을 인자로 받아서 state를 변경해주는 역할을 한다.

< reducers / counter.js >


import { INCREMENT, DECREMENT } from "../constants/counter"

const initialState = {
  count: 0,
}


export default (state = initialState, action) => {
  switch (action.type) {
    case INCREMENT:
      return { ...state, count: state.count + 1 }
    case DECREMENT:
      return { ...state, count: state.count - 1 }
    default:
      return state
  }
}

4. 스토어 정의


스토어는 reducer를 인자로 받아서 생성할 수 있다. combineReducers는 리듀서가 여러개일 때, 합쳐서 루트 리듀서로 만들기 위해서 사용하는 함수이다.

< configureStore.js >


import { combineReducers, createStore } from "redux"
import counterReducer from "./reducers/counter"
import { composeWithDevTools } from "redux-devtools-extension"

const reducer = combineReducers({
  counter: counterReducer,
})

const store = createStore(reducer, composeWithDevTools())

export default store

5. 컴포넌트 정의


useSelector 는 이름에서 알 수 있듯 스토어 전체에서 원하는 state에 접근할 수 있게 해준다. (store를 subscribe 하는 구조이다.) dispatch는 리듀서에게 액션을 전달하는 일름보같은 존재인데 인자로 increment()를 가지고 있다. 2번에서 액션함수를 정의할 때 increment 함수의 결과 값으로 INCREMENT 타입을 가지는 액션을 리턴했으므로 액션을 인자로 가지고 있다는 것을 알 수 있다.

< components / Counter.js >

import React from "react"
import { useSelector, useDispatch } from "react-redux"
import { increment, decrement } from "../redux/actions/counter"

const Counter = () => {
  const count = useSelector(state => state.counter.count)
  const dispatch = useDispatch()

  return (
    <div>
      <h2>The current count is : {count}</h2>
      <button onClick={() => dispatch(increment())}>Increment Count</button>
      <button onClick={() => dispatch(decrement())}>Decrement Count</button>
    </div>
  )
}

export default Counter

6. 페이지 작성


configuerStore.js 에서 만든 store 를 먼저 import 해주자. 그리고 react-redux에서는 Provider를 import해주고 사용해야 하는데 Provider는 하나의 컴포넌트로 Provider에 속하는 하위 컴포넌트들이 Provider를 통해 redux store에 접근할 수 있게 해준다.

< page / counter.js >


import React from "react"
import { Link } from "gatsby"
import "./counter.css"
import Layout from "../components/layout"
import SEO from "../components/seo"
import Counter from "../components/Counter"


import store from "../redux/configureStore"
import { Provider } from "react-redux"

const SecondPage = () => (
  <Provider store={store}>
    <Layout>
      <SEO title="Page two" />

      <div>
        <Counter />
      </div>
      <Link to="/">Go back to the homepage</Link>
    </Layout>
  </Provider>
)

export default SecondPage

결과 화면


감사하게도 정상적으로 작동하는 것을 확인 할 수 있다. 페이지를 이동해도 count 값이 유지되는 것을 확인할 수 있다.

ducks 패턴 적용

기존의 방식대로 진행해본 결과, 딱봐도 프로그램이 커지고 복잡해질 수록 파일이 많아지고 관리하기 힘들어 질 것이란 것을 알았다.

ducks 패턴이란 하나의 파일에 액션타입 , 액션 , 리듀서를 전부 작성해서 하나의 모듈로 만드는 것을 말한다.


1. 모듈 작성


ducks / counter.js

//기존방식의 #1
const INCREMENT = "counter/increment"
const DECREMENT = "counter/decrement"

//기존방식의 #2
export const increment = () => ({
  type : INCREMENT,
})

export const decrement = () =>({
  type : DECREMENT,
})

//기존방식의 #3
const initialState = {
  count : 0 ,
}

export default (state = initialState, action)=>{
  switch(action.type){
    case INCREMENT:
      return {...state, count : state.count+1 }
    case DECREMENT:
      return {...state, count : state.count-1 }
    default:
      return state
  }
}

보면 기존 방식의 #1 #2 #3 을 전부 한 파일에 때려박은게 끝이다. (액션 타입 정의시에는 리듀서이름/~ 이렇게 정해주는 불문율? 이 있나보다) 그 외 store , 컴포넌트 , 페이지 부분은 동일하다. 정말 간단하다.

src
|
|-components
|	|_Counter.js
|
|-redux
      |- ducks
      |     |_counter.js  
      |
      |_store.js

(ducks 패턴을 사용했을 시 구조)

코드 : https://github.com/cheal3/gatsby

좋은 웹페이지 즐겨찾기