[TypeScript-08] React + TypeScript + Redux

Comely·2025년 6월 6일

TypeScript

목록 보기
8/9

1. React TypeScript 프로젝트 설정

새 프로젝트 생성

npx create-react-app 프로젝트명 --template typescript

기존 프로젝트에 TypeScript 추가

npm install --save typescript @types/node @types/react @types/react-dom @types/jest

핵심: 컴포넌트 파일은 .tsx 확장자 사용 (JSX 문법 지원)

2. React에서 TypeScript 사용법

JSX 타입 지정

let 박스: JSX.Element = <div></div>;
let 버튼: JSX.Element = <button></button>;

함수 컴포넌트 타입 지정

type AppProps = {
  name: string;
};

function App(props: AppProps): JSX.Element {
  return <div>{props.name}</div>;
}

Props로 JSX 전달하기

type ContainerProps = {
  a: JSX.IntrinsicElements['h4'];
};

function Container(props: ContainerProps) {
  return <div>{props.a}</div>;
}

// 사용법
<Container a={<h4>안녕</h4>} />

State 타입 지정

const [user, setUser] = useState<string | null>('kim');

Type Assertion 주의사항

// React에서는 <> 대신 as 사용
let code: any = 123;
let employeeCode = code as number;  // ✅ 올바름
let employeeCode2 = <number>code;   // ❌ React에서 금지

3. Tuple 타입

기본 문법

배열의 각 위치별로 정확한 타입 지정

let 멍멍이: [string, boolean];
멍멍이 = ['dog', true];  // ✅ 올바름
멍멍이 = [true, 'dog'];  // ❌ 순서 틀림

Rest Parameter와 Tuple

function 함수(...x: [string, number]) {
  console.log(x);
}

함수('kim', 123);        // ✅ 가능
함수('kim', 123, 456);   // ❌ 에러

옵션 요소 (뒤에서만 가능)

type Num = [number, number?, number?];
let 변수1: Num = [10];
let 변수2: Num = [10, 20];
let 변수3: Num = [10, 20, 30];

Spread 연산자와 Tuple

let arr = [1, 2, 3];
let arr2: [number, number, ...number[]] = [4, 5, ...arr];

4. Redux TypeScript 설정

Redux를 사용하는 이유

  1. 중앙 집중식 state 관리 - props 없이도 state 공유 가능
  2. 안전한 state 수정 - reducer 함수로 수정 방법을 미리 정의하여 버그 방지

설치

npm install redux react-redux
npm install @reduxjs/toolkit  # 신규 방식 사용시

5. 전통 방식 Redux + TypeScript

Store 설정

import { Provider } from 'react-redux';
import { createStore } from 'redux';

interface Counter {
  count: number;
}

const 초기값: Counter = { count: 0 };

function reducer(state = 초기값, action: {type: string}) {
  if (action.type === '증가') {
    return { count: state.count + 1 };
  } else if (action.type === '감소') {
    return { count: state.count - 1 };
  } else {
    return state;
  }
}

const store = createStore(reducer);

// Store 타입 export
export type RootState = ReturnType<typeof store.getState>;

컴포넌트에서 사용

import { useDispatch, useSelector } from 'react-redux';
import { Dispatch } from 'redux';
import { RootState } from './index';

function App() {
  const 꺼내온거 = useSelector((state: RootState) => state);
  const dispatch: Dispatch = useDispatch();

  return (
    <div>
      {꺼내온거.count}
      <button onClick={() => dispatch({type: '증가'})}>
        버튼
      </button>
    </div>
  );
}

6. 신규 방식 Redux Toolkit + TypeScript

Store 설정

import { createSlice, configureStore, PayloadAction } from '@reduxjs/toolkit';

const 초기값 = { count: 0, user: 'kim' };

const counterSlice = createSlice({
  name: 'counter',
  initialState: 초기값,
  reducers: {
    increment(state) {
      state.count += 1;
    },
    decrement(state) {
      state.count -= 1;
    },
    incrementByAmount(state, action: PayloadAction<number>) {
      state.count += action.payload;
    }
  }
});

const store = configureStore({
  reducer: {
    counter1: counterSlice.reducer
  }
});

export type RootState = ReturnType<typeof store.getState>;
export const { increment, decrement, incrementByAmount } = counterSlice.actions;

컴포넌트에서 사용

import { useDispatch, useSelector } from 'react-redux';
import { RootState, increment } from './index';

function App() {
  const 꺼내온거 = useSelector((state: RootState) => state);
  const dispatch = useDispatch();

  return (
    <div>
      {꺼내온거.counter1.count}
      <button onClick={() => dispatch(increment())}>
        버튼
      </button>
    </div>
  );
}

7. 실무 예제

숙제 1: 음식 정보 Tuple

let 음식: [string, number, boolean] = ['동서녹차', 4000, true];

숙제 2: 가변 길이 Tuple

let arr: [string, number, ...boolean[]] = ['동서녹차', 4000, true, false, true];

숙제 3: Rest Parameter + Tuple

function 함수(...rest: [string, boolean, ...(number | string)[]]) {
  // 첫째는 문자, 둘째는 boolean, 나머지는 숫자 또는 문자
}

함수('a', true, 6, 3, '1', 4);

숙제 4: 문자/숫자 분류기

function 함수(...rest: (string | number)[]): [string[], number[]] {
  let result: [string[], number[]] = [[], []];
  
  rest.forEach((a) => {
    if (typeof a === 'string') {
      result[0].push(a);
    } else {
      result[1].push(a);
    }
  });
  
  return result;
}

// 사용 예시
함수('b', 5, 6, 8, 'a');  // [['b', 'a'], [5, 6, 8]]

정리

React + TypeScript 핵심

  • 파일 확장자: .tsx 사용
  • JSX 타입: JSX.Element
  • Props/State: 명확한 타입 지정
  • Assertion: as 키워드만 사용

Redux + TypeScript 핵심

  • Store 타입: RootState export로 재사용
  • Action 타입: PayloadAction<T> 사용 (신규 방식)
  • 전통 vs 신규: 코드 길이와 복잡도 차이
profile
App, Web Developer

0개의 댓글