[React에서 TypeScript사용하기] #5 Redux 프로처럼 사용하기

mechaniccoder·2020년 7월 18일
0

React + TypeScript

목록 보기
5/5
post-thumbnail

액션에 const assertion사용하기


액션을 선언할 때 보통은 아래 코드와 같이 쓰죠. 타입추론으로 인해서 각 액션의 타입으로 string이 부여되죠. 그런데 타입스크립트에서는 문자열 자체를 타입으로 만들어 줄 수 있습니다. 바로 const assertion을 통해서 말이죠.

const ADD_TODO = "todos/ADD_TODO"
const TOGGLE_TODO = "todos/TOGGLE_TODO"
const REMOVE_TODO = "todos/REMOVE_TODO"

const assertion 사용

const ADD_TODO = "todos/ADD_TODO" as const;
const TOGGLE_TODO = "todos/TOGGLE_TODO" as const;
const REMOVE_TODO = "todos/REMOVE_TODO" as const;

이렇게 되면 각 변수의 타입으로 string이 아니라 할당한 문자열이 타입이 됩니다.

<const assertion 했을 때>

<const assertion 안했을 때>

만약 const assertion을 하지 않았으면 string으로 나타났을 겁니다. 위 사진과 같이 말이죠. 참고로 액션생성 함수를 만든 뒤에 액션들에 대한 타입을 선언해두면 리듀서에서 인자로 받는 action의 타입으로 설정할 수 있습니다. ReturnType은 함수가 반환하는 타입을 가져오는 유틸 타입입니다. 각 함수가 액션 객체를 반환하기 때문에 이 객체가 가지는 프로퍼티들을 포함하도록 하겠죠.

type TodosAction =
  | ReturnType<typeof addTodo>
  | ReturnType<typeof toggleTodo>
  | ReturnType<typeof removeTodo>;

function todos(state = initialState, action: TodosAction): TodosState {
	(...)
}

Ducks 아키텍처


리듀서를 만들 때 액션타입, 액션 생성함수, 리듀서를 하나의 파일에 모아 작성하는 구조를 말합니다.

이렇게 modules라는 디렉토리 하위에 counter.ts, todos.ts 스토어가 있고 각 리듀서를 모아서 만든 루트 리듀서를 index.ts에 결합합니다.

루트리듀서 타입 선언하기


리덕스에서 리듀서를 결합할 때 일반 리액트에서는 따로 루트리듀서 타입을 선언하지 않았는데, 타입스크립트 리액트에서는 아래 코드와 같이 RootState 타입을 선언해줘야 합니다.

import {combineReducers} from "redux";
import counter from "./counter";
import todos from "./todos";

const rootReducer = combineReducers({
  counter,
  todos,
});

export default rootReducer;
export type RootState = ReturnType<typeof rootReducer>;

이게 나중에 어디서 쓰이냐면 각 컴포넌트에서 리듀서를 부를 때 사용합니다. 아래 코드는 counter 앱을 만들고 컨테이너 컴포넌트에서 리듀서를 부릅니다. 여기서 counter 리듀서를 부를 때 RootState 타입을 선언해줬죠?

const {count} = useSelector(({counter}: RootState) => ({
    count: counter.count,
  }));

리덕스와 컴포넌트 구조


(프레젠테이셔널 + 컨테이너) 컴포넌트

프레젠테이셔널 컴포넌트는 UI를 담당하는 컴포넌트이며, 컨테이너 컴포넌트는 리덕스 상태 모듈과 연결된 컴포넌트입니다. 디렉토리 구조로 containerscontainer로 나눠서 컴포넌트를 작성하게 되는데 이는 필수가 아닙니다.

커스텀 Hooks

굳이 리덕스와 연결된 컨테이너를 만들지 않고 커스텀 Hooks을 만들어서 리덕스와 연결시킬 수도 있습니다. 만들었던 액션 생성 함수가 커스텀 Hooks을 만드는데 토대가 됩니다.

저번에는 프레젠테이셔널과 컨테이너 컴포넌트로 todo앱을 만들었고 이번에는 커스텀 Hooks을 사용했는데요. 제가 생각할 때 커스텀 Hooks 아키텍처가 재사용 측면에서 더 좋다고 생각합니다. 만약 다른 컴포넌트에서 같은 액션을 사용할 시에 useDispatchuseSelector를 이용해 루트리듀서를 import하지 않아도 바로 커스텀 Hook을 사용하면 되기 때문입니다. 이게 맞는 말인지에 대해서는 경험이 쌓여야 될 것 같습니다.

요약

  • 루트 리듀서 export할 때 RootState 타입도 export해주자, 후에 컴포넌트에서 useSelector에서 타입 선언이 필요하기 때문이다.
  • 액션을 만들때 as const로 문자열 그 자체를 타입으로 선언해주자.
  • 액션 타입에서 ReturnType을 통해 액션의 타입을 스키마 할 수 있다.
  • Ducks 아키텍처는 modules 디렉토리 안, 하나의 파일에 액션, 액션생성함수, 리듀서 모두 선언한다.
  • 프레젠테이셔널 컴포넌트 + 컨테이너 컴포넌트 구조는 필수적인게 아니다.
  • 프레젠테이셔널 컴포넌트 + custom Hooks으로 상태관리 할 수 있는데 이 구조도 매우 괜찮다. (velopert님이 후에 자기도 이렇게 만들어본다고 하셨다.)

References


https://velog.io/@velopert/use-typescript-and-redux-like-a-pro#카운터-프리젠테이셔널-컴포넌트-만들기 | TypeScript 환경에서 Redux를 프로처럼 사용하기

profile
세계 최고 수준을 향해 달려가는 개발자입니다.

0개의 댓글