728x90

monorepo 환경에서 사용하기 좋다.

polyrepo로 구성할 경우 프로젝트간 공용 컴포넌트에 대한 코드가 중복으로 늘어난다.

UI, 기능이 같은 컴포넌트를 하나의 repository에서 구성하고 싶은 경우 Monorepo로 구성하는 것이 이득이다.

 

Private npm

일반 npm package는 public npm에서 제공하는 package를 install에서 사용하는 구조이다.

만약 package를 private하게 관리해야 한다면 외부로 노출되지 않도록 npm registry를 구성해야한다.

회사 사내에서 재사용할 수 있는 모듈을 패키지화해서 공유하기 위해 private npm을 사용한다.

 

private npm 서비스의 종류로는 Verdaccio, Gihub packages, npm pro 등이 있다.

우리 회사는 node package의 버전 관리 용이성을위해 사용한다.

app1은 private npm의 package@0.1.0을 보는데 app2는 privatge npm의 package@0.1.1을 볼수있도록 설정.

Verdaccio vs Github Packages

Github Packages

  • github action 연동 가능. webhook 지원. => publishing 할때 혹은 publishing 이후 workflow customize 가능하다.
  • microsoft가 지원하기 때문에 유지보수가 계속된다.
  •  

Verdaccio

https://www.devh.kr/2020/Host-Publish-and-Manage-Private-npm-Packages-with/

  1. verdaccio 설치
  2. verdaccio 실행
  3. verdaccio 사용자 등록
  4. package 업데이트 정보 있을 시 npm publish

Best Practice

  • public package와 private package 네이밍 구분
    • 다음의 경우 @my-company/ 와 local- prefix를 가지는 package는 priavate package 이다.
packages:
   '@my-company/*':
     access: $authenticated // import packages할때 authenticated 되어야한다. 
     publish: $authenticated
    'local-*':
     access: $all
     publish: $authenticated
   '@*/*':
     access: $all
     publish: $authenticated
   '**':
     access: $all
     publish: $authenticated
  • private repository -> public repository(uplink) -> cache 순서로 보기때문에 이를 이용한다.
    • 만약 public repository package가 마음에 안든다면 같은 이름으로 override한다.
    • private package의 proxy설정을 해제한다. npmjs.org에서 가져오지 않게하기 위함이다.

Verdaccio + Docker

verdaccio private repository 자체를 docker image로 생성하여 어느 서버에서도 띄울 수 있도록 할 수 있다.

docker pull verdaccio/verdaccio:4
docker run -it --rm --name verdaccio -p 4873:4873 verdaccio/verdaccio

Verdaccio + Environment Variables

verdaccio는 permission이나 port 수정을 위해 환경변수를 사용한다.

728x90

우리의 일과는 50% 이상이 일하는데 소비된다. 삶이 일로써 소비된다면 일을 사랑하는 것이 삶을 사랑하는 가장 중요한 열쇠다. 일에 도전하고 동기를 부여하고 보상을 얻는 것은 아침에 일어나고 싶게 만든다.

잘 하는 사람이 되는 방법은 잘 하는 사람들이 모입 집단에서 한명이 되는 것이다.

 

자신만의 계획을 세워야한다.

어떻게 할지 이 책에서 차근차근 알아보자.

1. 자신의 시장을 선택하라. 집중할 기술과 사업 분야를 신중하게 골라야한다.

2. 자신에게 투자하라. 지식과 기술은 자신이라는 상품의 기반이 된다.

3. 실행하라. 뛰어난 기술만 갖춘 직원만으로는 회사 성과가 나오지 않는다. 직원은 기술을 바탕으로 가치를 만들어야한다. 

4. 마케팅하라. 자신을 전혀 알리지 않고 어떻게 인정받을수 있을지 알아야한다.

 

자신의 시장을 선택하라

  • 우연히 돌아가는 프로그램을 만들어서는 안된다. 어떻게 돌아가는지 이해해야한다.
  • 기술만 알아서는 안된다. 내가 속한(또는 속하려고 하는) 사업에 대해서도 철저한 이해가 필요하다.(ex. 제조, 금융 지식)
  • 내가 제일 못하더라도 잘하는 사람들 사이에 있어라. 그곳에서 보통만 해도 뛰어난 개발자가 될 수 있다.
  • 비주류 기술을 좋아할 수 있다는 것은 당신이 찐 개발자라는 증거가 될 수 있다. 이것 자체가 채용시 장점이 되는 것이고... 비주류 기술을 공부하면 기존에 알고있는 기술에 대해 더 깊게 알 수 있는 기회가 될 수도 있다.(ex Rescript)
  • 단순 테스터나 단순 코더가 되지말자. 코딩, 테스팅, 아키텍트 다재다능한 사람이 되자. 그러면서 깊이 있게 공부하여 전문가가 되자.
  • 작은 프로젝트를 두 번 정도 해보자. 한번은 내가 잘 아는 기술로, 한번은 경쟁 기술로 하되 그 기술의 독특한 방식을 사용해보자.
  • 당신이 IT분야에서 왜 일하나? 우연히 있게된 것인가? 보수가 좋아서? 부모가 권해서? 내 분야에 열정을 쏟을 수 없다면 나쁜 결과를 가져올 수 있다.
    • 2주간 일어나 일을 시작할 때마다 신나는 정도를 1부터 10으로 점수를 매기자. 급상승하는 부분이 있었나? 경향이 있었나? 항상 낮거나 항상 높았나? 평점은 얼마인가? 그 다음 2주간은 매일 아침에 10점으로 만들 계획을 짜라.
  • 나 자신을 기술로 정의하지 말자. 내가 한 일과 하고 싶은 일로 정의하자. 기술은 성공에 이르는 한 방법일 뿐이다.

자신에게 투자하라

경력을 위한 투자 전략 & 기술, 실력, 자신에게 투자하는 방법

  • 하나의 기술에 대해 '어떻게' 와 '왜'라는 질문을 계속 던져라. 이는 스스로 찾아야한다.
  • 무엇인가 정말 배우고 싶다면 내 자신이 멘토가 되어보라. 사람은 가르치면서 배우게된다.
  • 근무 시간을 연습 시간으로 삼지말자. 시간을 별도로 투자해야한다.
    • 몸에 익히기: 개발 환경의 여러 도구를 손에 익혀라. 개발자들이 활용할 수 있는 도구(ex. 정규식, 스트림 라이브러리, 컬렉션이나 리스트를 처리할 수 있는 유틸리티) 전체 를 습득해라.
    • 악보 읽기: 오픈소스를 읽고 기여해보아라. 중요한 것은 내가 보고 있는 것을 빠르게 이해하는 것이다.
    • 즉흥 연주: 사고를 날카롭게하고 즉흥 코딩 솜씨를 향상시킬 멋진 방법으로 스스로 제한 조건을 두고 연습하라. 
  • 특정 문제에 대한 해결 방법을 찾는 것보다 기존 코드를 자신의 스타일과 능력을 점검하는 확대경으로 삼아보자. 코드를 읽으면서 전에 해본적 없는 것들과 결코 생각해 보지도 못했던 것을 발견할 것이다.

실행

  • 파킨슨의 법칙: 끝내는데 필요한 시간에 맞추어 작업이 늘어난다.
    1달 완료 예정인 프로젝트를 1주일 안에 끝내보려고 해보라. 할 수 있다.
  • 상사에게 보고할 수준의 일을 매일 목표로 삼자. 간단하게 일간/주간/월간 목표를 세우고 성과를 추적하자.
  • 나는 회사의 좋은 투자 대상인가? 급여 인상을 당연하게 생각하지 말아라.
  • 회사에서 결코 편해지지 말자. 언제나 겸손해야한다. 매일 일어나면서 현재 지위에서 떨어질 수 있음을 상기하자.
    • 성공하면 성공할 수록 치명적인 실수를 저지를 수 있는데, 자기 결정에 의심을 하지 않는다는 것이다.
    • 나를 대신할 개발자가 언제나 있다고 생각하자. 이는 결국 내가 떠날수도 있다는 얘기가된다.
      나만 이해할 수 있는 코드, 나만이 유일하게 할수 있는 일이라 생각하는 것은 리팩토링과 문서화를 진행한다. 이러한 일들은 목록에서 없어져야 한다.
  • 유지보수를 꾸준히 하자. 버그를 고치고 사소한 기능 요구를 구현하고 계속 동작하게 만들라.
    • 오래된 웹 서비스를 현대적인 웹 브라우저의 기능을 이용할 수 있게 바꾼다.
    • 서비스의 품질을 점점 향상 시킨다.
  • 8시간밖에 없어! 부지런히 일해야지!! 라는 생각으로 일을 한다. 추가시간 없이 일하려하자. 열중하기의 힘은 대단하다.
  • 실수(버그)가 발생했을때 처리하는 법을 배우자.
    • 문제를 알게 되자마자 숨기지말고 드러내자
    • 빠른 해결을 위해 책임을 떠넘기지 말고 책임을 져라.
    • 해결책을 제시하라.
    • 도움을 구하라.
  • 계획을 아주 잘 세웠어도 삶은 위기와 재난의 연속이다. 당황&부정적인 대응으로 얻는 이익은 없다. 당황은 나의 능력을 떨어뜨린다.
    • 당황하지 않는 방법은 문제가 터져도 제3자 관점에서 상황을 분석하는 것이다.
  • 오후에 시간을 내서 다음날 하고 싶은  일을 모두 목록을 만들어둔다.
    • 그 날 하지 못한 일은 다음날 옮기고 과정을 계속한다. 계획하고 착수하는 리듬이 생기기 시작한다.

마케팅은 높으신 분들만 하는게 아니다

  • 내가 한 일을 윗사람이 알게 해라. 표현하지 않으면 모른다
  • 의사소통, 글쓰기를 위한 의사소통 능력을 기르자.
    • 매일 어떤 개발을 했는지 기록하고 설계의 타당성을 증명하고 어려운 기술적 또는 전문적 결정을 자세히 조사하라.
      글쓰기 연습이다
  • 직장 사람들이 나에대해 물을 수 있는 최악의 질문은 "그 사람은 뭘 하나요?" 이다. 영향력을 남기자.
  • 나를 다른 개발자로부터 남달라지게 만들 수 있는 것은 오픈 소스 소프트웨어 발표, 책과 기사쓰기, 컨퍼런스 발표이다.

자신의 강점을 유지보수하라

  • 연구, 투자, 실행, 마케팅의 루프를 돌텐데 너무 한 부분에 시간을 많이 쓰면 시대에 뒤쳐질 수 있음을 명시하자.
  • 나 자신을 측정하기 쉬운 방법은 신뢰할 만한 제3자를 이용하는 것이다. 멘토나 가까운 동료를 이용하자.
  • 내 경력 관리를 폭포수 모델로 관리하지 마라.(경력 사전설계X) 목표는 크게 세우더라도 도중에 꾸준히 수정하라. 목표를 바꿔나가는 것이다.
  • 오늘의 나는 어제의 나보다만 더 잘해지게 성장하면 된다. 조급해하지 말자.

'Books' 카테고리의 다른 글

소프트 스킬 3  (0) 2020.12.13
소프트 스킬 2  (0) 2020.12.13
소프트 스킬 1  (0) 2020.12.13
728x90

createApi

Backend API로부터 어떻게 데이터를 반환할지에 대한 endpoint 를 정의.

    • baseQuery(args, {signal, disaptch, getState, extra }, extraOptions ) : endpoint에서 queryFn이 쓰이지 않았다면 모든 endpoint는 baseQuery를 거치게 된다. 
      • args: endpoint에서 query 함수 의 return Value, ex) query: () => '/todos/ -> '/todos' 반환
      • signal: AbortSignal 객체
      • dispatch, getState : redux store의 action을  dispatch하거나 state를 getState할 수 있다.
      • extra: thunk.extraArguments와 유사
        • thunk.withExtraArgument = createThunkMiddleware;
      • extraOptions: endpoint에서 extraOptions으로 넘겨준 값
    • endpoints : 서버에 대응해서 작동하는 operation 집합
    • extractRehydrationInfo : RTK Query는 Next.js에서 next-redux-wrapper를 통해 SSR을 제공하는데 이때 api요청과 함께 hydration을 도와준다.
    • tagTypes: endpoint별로 caching과 invalidation의 기준이 되는 string array
    • reducerPath: store에 저장할때 api별로 구분하기위한 unique key
    • serializeQueryArgs: 여러 이유로 캐 custom function
    • keepUnusedDataFor: default는 60초 이다. 설정 시간동안 api query에 대해 subscriber가 없으면 refetch 되게 만든다.
    • refetchOnMountOrArgChange : refetch 발동조건을 api query subscriber마다 줄지 결정한다. 시간 제한을 줄 수 도 있다. 
      • false(default) : 기본적으로는 항상 cache된 데이터를 반환한다. keepUnusedDataFor 시간만큼 api query에 subscriber가 없어야만 이후 api 요청에대해 refetch가 이뤄진다.
      • true: 새로운 subscriber가  query에 추가될 때마다 refetch가 이뤄진다.
      • number: 마지막 api fulfill시간과 현재시간을 비교해서 넘었으면 refetch한다. 여기서 fullfill은 cache된 데이터를 가져오는 것은 해당하지 않는다.
    • refetchOnFocus: default: false. true이면 application window가 focus되면 refetch 발동조건 성립
    • refetchOnReconnect : default false. true이면 network연결이 다시 되면 refetch 발동조건 성립

endpoint

      • query
      • queryFn(arg,{ signal, dispatch, getState }, extraOptions, baseQuery) : query, Mutation을 대체할 수 있다. query(Mutation)을 Customize할때 활용. createApi.baseQuery, transFormResponse를 거치지 않는다.
        • arg: query요청 시 매개변수
        • signal, dispatch, getState : createApi.baseQuery의 매개변수와 동일
        • extraOptions: 자기 자신한테 받은 extraOptions
        • baseQuery: createApi.baseQuery와 동일
      • transformResponse: Response를 변환한다.
      • extraOptions: baseQuery혹은 queryFn으로 전송할 third argument
      • providesTags, invalidatesTags: Cache 데이터에 Tag를 붙여주고 어떤 Tag를 가진 데이터를 refetch 혹은 cache로부터 삭제해야할지 결정한다.
      • keepUnusedDataFor: subscribe되지 않았는지 특정 초가 지나면 refetch 되게 해준다.
      • onQueryStarted(arg, { dispatch, getState, extra, requestId, getCacheEntry, updateCachedData}): api query를 요청할때 호출된다( cache된 데이터를 끌고올때는 호출되지 않는다)
        • arg: query 요청시 매개변수
        • queryFulfilled: query가 fulfill한 데이터를 반환하는 Promise. 이후의 로직 처리도 가능하다
        • getCacheEntry: cache entry의 값
      • onCacheEntryAdded(arg, { dispatch, getState, extra, requestId, getCacheEntry, updateCachedData}) : 새 cache entry가 추가 되었을때(new endpoint + query parameters combination) 실행된다.
        • cacheDataLoaded, cacheDataRemoved : Promise. 이후 로직 처리하기 위해 사용.
728x90

react 에서 redux-toolkit 을 사용하기 위해서는 '@reduxjs/toolkit' 과 'react-redux' package를 설치해줘야한다.

npm install @reduxjs/toolkit react-redux

 

그리고 이전에 state를 호출하기 위한 getState와 action을 호출하기위한 dispatch를 사용하는 것 대신 'react-redux'의 useSelector와 useDispatch를 사용하면 된다. typescript의 type 미적용시 기본적인 사용법은 다음과 같다.

// src/features/icecream/IcecreamView.tsx
// type 미지정

import React from 'react';
import { useSelector, useDispatch } from 'react-redux'
import { ordered } from './icecreamSlice'

export const IcecreamView = () => {
  const numOfIcecream = useSelector(state => state.icecream.numOfIcecream
  const dispatch = useDispatch()
  return(
    <div>
      <h2> Num of icecream - {numOfIcecream} </h2>
      <button onClick={()=>dispatch(ordered())}>Order icecream</button>
    </div>
  )
}

typescript의 type을 지정해야한다면, store로부터 store states type과 dispatch type을 반환하도록 설정하고 이를 custom hook으로 custom useSlector와 useDispatch를 만들도록 한다.

그리고 위의 IcecreamView.tsx 에서 react-redux 의 useSelector, useDispatch를 사용하는 대신 customhook의 useAppSelector, useAppDispatch를 사용하도록 하면된다.

import { configureStore } from '@reduxjs/toolkit'
import cakeReducer from '../features/cake/cakeSlice'
import icecreamReducer from '../features/icecream/icecreamSlice'


const store = createStore({
  reducer:{
    cake:cakeReducer,
    icecream: icecreamReducer
  }
})

export default store
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
//src/app/hooks.ts
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
import type { RootState, AppDispatch } from './store'

export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
export const useAppDispatch= () => useDispatch<AppDispatch>()

 

728x90
//asyncActions.js

const { createStore, applyMiddleware } = require('redux');
const thunkMiddleware = require('redux-thunk').default;
const axios = require('axios');

const FETCH_USERS_REQUEST = 'FETCH_USERS_REQUEST';
const fetchUsersRequest = () => ({
  type: FETCH_USERS_REQUEST,
});

const FETCH_USERS_SUCCESS = 'FETCH_USERS_SUCCESS';
const fetchUsersSuccess = (users) => ({
  type: FETCH_USERS_SUCCESS,
  users,
});

const FETCH_USERS_ERROR = 'FETCH_USERS_ERROR';
const fetchUsersError = (error) => ({
  type: FETCH_USERS_ERROR,
  error,
});

const initialState = {
  loading: false,
  users: [],
  error: '',
};

const fetchUsers = () => {
  return function (dispatch) {
    dispatch(fetchUsersRequest());
    axios
      .get('https://jsonplaceholder.typicode.com/users')
      .then((res) => {
        const users = res.data.map((user) => user.id);
        dispatch(fetchUsersSuccess(users));
      })
      .catch((error) => {
        dispatch(fetchUsersError(error.message));
      });
  };
};

const reducer = (state = initialState, action) => {
  switch (action.type) {
    case FETCH_USERS_REQUEST:
      return {
        ...state,
        loading: true,
        users: [],
        error: '',
      };
    case FETCH_USERS_SUCCESS:
      return {
        ...state,
        loading: false,
        users: action.users,
        error: '',
      };
    case FETCH_USERS_ERROR:
      return {
        ...state,
        loading: false,
        users: [],
        error: action.error,
      };
    default:
      return state;
  }
};

const store = createStore(reducer, applyMiddleware(thunkMiddleware));
console.log('intial state : ' + store.getState());
const unsubscribe = store.subscribe(() => {
  console.log(store.getState());
});

store.dispatch(fetchUsers());

기존 redux에서 async action처리를 위한 redux thunk middleware를 접목한 구현은 다음과 같다.

action을 직접 호출하는 대신 thunk를 호출하면 thunk 내에서 action을 호출하여 reducer에서 그에 맞는 state를 반영하는 개념이었다.

 

 

redux-toolkit도 크게 다르지 않다. redux와 크게 다르게 크게 명심해야할 점은 두가지가 있다.

  • thunk 내 action dispatch 불필요 및 action type 이름 자동 지정 (pending, fulfilled, rejected)
  • action에 대한 reducer를 extraReducers에 정의
// userSlice.js
const { createSlice } = require('@reduxjs/toolkit');
const { createAsyncThunk } = require('@reduxjs/toolkit');
const axios = require('axios');

//initial state
const initialState = {
  loading: false,
  users: [],
  error: '',
};

//thunk
const fetchUsers = createAsyncThunk('users/fetchUsers', () => {
  return axios.get('https://jsonplaceholder.typicode.com/users').then((res) => {
    return res.data.map((user) => user.id);
  });
});

//reducer
// redux thunk 내에서 지정된 type 이름의 action이 자동 dispatch =>  pending, fulfilled, rejected
const userSlice = createSlice({
  name: 'user',
  initialState,
  extraReducers: (builder) => {
    builder.addCase(fetchUsers.pending, (state) => {
      state.loading = true;
    });
    builder.addCase(fetchUsers.fulfilled, (state, action) => {
      state.loading = false;
      state.users = action.payload;
      state.error = '';
    });
    builder.addCase(fetchUsers.rejected, (state, action) => {
      state.loading = false;
      state.users = [];
      state.error = action.error.message;
    });
  },
});

module.exports = userSlice.reducer;
module.exports.fetchUsers = fetchUsers;
//index.js
const { configureStore } = require('@reduxjs/toolkit');
const userReducer = require('./userSlice');
const { fetchUsers } = require('./userSlice');

const store = configureStore({
  reducer: {
    user: userReducer,
  },
});

store.subscribe(() => {
  console.log(store.getState());
});

store.dispatch(fetchUsers());
728x90

redux와 redux toolkit은 얼핏보면 사용에 있어 다른 점이 없어보인다.

 

하지만 특정 module의 action이 호출 되었을때 다른 module의 state값을 바꿔야 할때  redux와 redux toolkit의 동작방식은 달라진다.

 

다음의 예시를 보자

CAKE_ORDERED action이 발생했을때 numOfIcecreams도 1씩 줄어들게 reducer를 수정하도록하자.

 

 기존 redux의 경우 아래 처럼 특정 store module의 initial state와 관련없는 state를 변화시켜도 문제 없이 작동한다.

...

const icecreamReducer = (state = icecreamInitialState, action) => {
  switch (action.type) {
    case 'ADD_ICECREAM':
      return {
        ...state,
        numOfIcecreams: state.numOfIcecreams - action.quantity,
      };
    case 'ADD_CAKE':
      return {
        ...state,
        numOfIcecreams: state.numOfIcecreams - 1,
      };
    default:
      return state;
  }
};

...


redux-toolkit의 경우 action을 현재 slice의 name + action name으로 자동 지정해주기 때문에 어떻게 만들어야할지 감이 오질 않는다.

위의 경우 ordered의 정확한 action이름은 `icecream/ordered` 가 된다.

 

위의 상황을 해결할 수 있는 것이 바로 extraReducers 이다.

//icecreamSlice.js

const { createSlice } = require('@reduxjs/toolkit');

const icecreamSlice = createSlice({
  name: 'icecream',
  initialState: {
    numOfIcecreams: 10,
  },
  reducers: {
    ordered: (state, action) => {
      state.numOfIcecreams -= action.payload;
    },
  },
  extraReducers: {
    ['cake/ordered']: (state) => {
      state.numOfIcecreams -= 1;
    },
  },
});

//export reducer && actions
module.exports = icecreamSlice.reducer;
module.exports.icecreamActions = icecreamSlice.actions;

위 처럼 'cake/ordered' action이 호출되었을 때 numOfIcecream도 1씩 줄어들게 설정할 수 있다

 

728x90

이전에서 보았던 redux는 기본설정(boilerplate)가 복잡하였다.

 

기존 redux에서 달라진 점은 다음과 같다.

  • intialState와 reducer를 createSlice 내에서 묶어서 관리
  • action,action creator가 reducer내에서 자동설정
  • nested state object에 대해 immer package 없이 간편하게 처리
  • combineReducer 함수 import 불필요

 

redux-toolkit에 추가되는 함수는 다음과 같다.

  • createSlice : action, action creator, initialstate, reducer 관리
  • configureStore : 기존 createStore 대체

위의 사항들을 기반으로 구현된 기본 redux toolkit 구성은 다음과 같다.

 

// cakeSlice.js
const { createSlice } = require('@reduxjs/toolkit');

const cakeSlice = createSlice({
  name: 'cake',
  initialState: {
    nested: {
      numOfCakes: 10,
    },
  },
  reducers: {
    ordered: (state) => {
      state.nested.numOfCakes--;
    },
  },
});

// export reducer && actions
module.exports = cakeSlice.reducer;
module.exports.cakeActions = cakeSlice.actions;
//icecreamSlice.js
const { createSlice } = require('@reduxjs/toolkit');

const icecreamSlice = createSlice({
  name: 'icecream',
  initialState: {
    numOfIcecreams: 10,
  },
  reducers: {
    ordered: (state, action) => {
      state.numOfIcecreams -= action.payload;
    },
  },
});

//export reducer && actions
module.exports = icecreamSlice.reducer;
module.exports.icecreamActions = icecreamSlice.actions;
//index.js
const { configureStore } = require('@reduxjs/toolkit');
const cakeReducer = require('./cakeSlice');
const icecreamReducer = require('./icecreamSlice');
const cakeActions = cakeReducer.cakeActions;
const icecreamActions = icecreamReducer.icecreamActions;

const store = configureStore({
  reducer: {
    cake: cakeReducer,
    icecream: icecreamReducer,
  },
});

console.log('initialState: ', store.getState());

const unsubscribe = store.subscribe(() => {
  console.log(store.getState());
});

store.dispatch(cakeActions.ordered());
store.dispatch(icecreamActions.ordered(3));

unsubscribe();

728x90

redux의 구성

  • Action : string
    • action type 지정
  • Action Creator : function(return object)
    • Action Type과 payload 를 반환
  • Reducer : function(return state mutation)
    • state(initialstate) 와 action은 parameter로 받음
  • Store
    • createStore 로 생성 
    • reducer 와 middleware를 매개 변수로 받음
  • Middleware
    • action과 reducer 사이에 중간 처리해주는 함수
    • logger, thunk등

 

다음은 위의 redux 기본구조를 구현한 모습니다. (middleware는 생략)

 

const { createStore, combineReducers } = require('redux');
const produce = require('immer').produce;

//action && action creator
const ADD_CAKE = 'ADD_CAKE';
const orderCake = () => ({
  type: ADD_CAKE,
  quantity: 1,
});

const ADD_ICECREAM = 'ADD_ICECREAM';
const orderIceCream = (q) => ({
  type: ADD_ICECREAM,
  quantity: q,
});

//initialState
const cakeInitialState = {
  //immer 시현용으로 일부러 nested
  nested: {
    numOfCakes: 10,
  },
};

const icecreamInitialState = {
  numOfIcecreams: 10,
};

//reducer
const cakeReducer = (state = cakeInitialState, action) => {
  switch (action.type) {
    case 'ADD_CAKE':
      //굳이 produce 쓸필요 없지만 nested object case 라 가정
      return produce(state, (draft) => {
        draft.nested.numOfCakes -= action.quantity;
      });
    default:
      return state;
  }
};

const icecreamReducer = (state = icecreamInitialState, action) => {
  switch (action.type) {
    case 'ADD_ICECREAM':
      return {
        ...state,
        numOfIcecreams: state.numOfIcecreams - action.quantity,
      };
    default:
      return state;
  }
};

//store && logging
const store = createStore(combineReducers({ cake: cakeReducer, icecream: icecreamReducer }));
console.log('initial state', store.getState());

const unsubscribe = store.subscribe(() => {
  console.log(store.getState());
});

store.dispatch(orderCake());
store.dispatch(orderIceCream(2));

unsubscribe();

+ Recent posts