React에 TypeScript 사용하기

학습 목표

  • What & Why?
  • TypeScript Basics
  • Combining React & TypeScript

React + TypeScript: Adding Type Safety To React Apps

What & Why?

TypeScript는 JavaScript의 superset이다. 즉, JavaScript의 문법보다 더 확장된 프로그래밍 언어라는 뜻이다. TS는 정적 타입을 추가했다. JS는 동적 타입이기에 함수의 매개변수가 어떤 타입을 받을 지 정할 수 없다. 이는 버그를 만들기 때문에 TS를 이용해 정적 타입을 사용하면 버그를 줄일 수 있다. 참고로 함수의 매개변수 뿐만 아니라 여러 곳에서 타입을 지정할 수 있다.

TypeScript Basics

  • 설치: npm install typescript

TypeScript는 브라우저에서 사용할 수 없어서 JS로 컴파일 해줘야 한다. 타입 표기는 사라지겠지만 오류는 잡아준다.

  • 컴파일러 실행:
    • npx tsc [파일명].ts

오류가 생겨도 일단 js 파일을 준다.

Types

  • Primitives: number, string, boolean
    • 타입은 소문자, 대문자는 JS 객체이다.
      let age: number = 23;
      let userName: string;
      let isInstructor: boolean;
      
  • arrays, objects
    • any 타입을 사용할 수 있지만 그럴거면 그냥,,, js를 쓰는게 나으니 사용을 비추한다.
      let hobbies: string[]; // number[]
      // 객체를 생성하는 게 아니다.
      let person: {
      name: string;
      age: number;
      };
      // object 배열
      let people: {
      name: string;
      age: number;
      }[];
      
  • Function Types, parameters

    // 반환값도 ts는 타입 추론하므로 필요한 경우 아니면 굳이 안해도 된다.
    function add(a: number, b: number): number | string {
      return a + b;
    }
    // 반환을 하지 않는 경우 void 타입을 사용한다.
    function printOutput(value: any) {
      console.log(value);
    }
    
  • Generics

    • helper 함수 같은 것을 만들 때 유용하게 쓰이는 기능이다. 보통 상황에 따라 타입이 다르지만 input과 output의 type이 같은데 ts는 이해하지 못한다. 이럴 때 generics을 사용해 유연성과 타입 안정성을 높일 수 있다.
    function insertAtBeginning(array: any[], value: any) {
      const newArray = [value, ...array];
      return newArray;
    }
    const demoArray = [1, 2, 3];
    
    const updatedArray = insertAtBeginning(demoArray, -1); // [-1, 1, 2, 3]
    const stringArray = insertAtBeginning(["a", "b", "c"], "d");
    
    // 이렇게 사용하면 어떠한 정보도 얻을 수 없기에 오류가 발생하지 않는다.
    updatedArray[0].split("");
    
    // array와 value는 같은 타입임을 알려줌.
    // T 대신 구체적인 type을 지정해줄 수 있다. Array<number> === number[]
    function insertAtBeginning<T>(array: T[], value: T) {
      const newArray = [value, ...array];
      return newArray;
    }
    
    const demoArray = [1, 2, 3];
    
    const updatedArray = insertAtBeginning(demoArray, -1); // [-1, 1, 2, 3]
    const stringArray = insertAtBeginning(["a", "b", "c"], "d");
    
    // 이렇게 사용하면 첫번째는 number 타입으로, 두번째는 string 타입으로 추론되어 오류를 잡아낼 수 있다.
    updatedArray[0].split("");
    
  • type 추론 기능이 있어서 변수를 만들고 바로 초기화한 그 값의 자료형을 type으로 지정해서 재할당할 때 다른 자료형을 사용하면 오류가 생긴다. 이를 사용하면 명시적으로 타입을 지정할 필요가 없어서 편하다.

    let age = 23;
    
    age = "23"; // error
    
  • 유니온 타입이라는 기능도 제공하는데 이는 한 가지 이상의 타입을 지정하고 싶을 때 사용하면 된다.

    let userName: string | string[] | number;
    
  • 동일한 타입을 계속 지정해줄 일이 생긴다. 이는 귀찮으니 type aliases 기능을 사용할 수 있다. 관리하기 쉽다는 장점이 있다.

    // not using type aliases
    let person: {
      name: string;
      age: number;
    };
    let people: {
      name: string;
      age: number;
    }[];
    
    // using type aliases
    type Person = {
      name: string;
      age: number;
    };
    
    let person: Person;
    let people: Person[];
    
  • TypeScript 공식 사이트

Combining React & TypeScript

파일 확장명이 .ts가 아닌 .tsx로 되어 있는데 그 이유는 jsx 문법을 사용하기 때문이다. 불필요한 경고창을 없애기 위함이다. 참고로 react가 알아서 자동으로 해주기 때문에 컴파일할 필요가 없다. package.json을 보면 @types로 시작하는 파일이 보이는데 이는 ts를 js로 번역해주는 번역기이다.

함수형 컴포넌트에 사용해보기

React.FC로 타입을 지정해줘야 하는데 이는 props.children을 기본으로 가진다. 뒤에 추가하고 싶은 속성을 Generics으로 추가해주면 된다.

const Component: React.FC<{ text: string }> = (props) => {
  return <li>{props.text}</li>;
};

모델 데이터를 사용하여 타입을 명시해줄 수 있다.

// models/Todo.ts
class Todo {
  id: string;
  text: string;

  constructor(todoText: string) {
    this.text = todoText;
    this.id = new Date().toISOString();
  }
}
// Todos.tsx
const Todos: React.FC<{ items: Todo[] }> = (props) => {
  return (
    <ul>
      {props.items.map((item) => (
        <TodoItem key={item.id} text={item.text} />
      ))}
    </ul>
  );
};
  • props.children 오류 발생
    • React18이상에서는 children을 기본으로 가지지 않는다고 한다. 그래서 타입을 명시해줘야 한다. React.FC<React.PropsWithChildren>

useState, useRef에 사용해보기

// 만약 타입을 명시해주지 않으면 초깃값에서 추론된 타입밖에 못 쓴다.
const [todos, setTodos] = useState<Todo[]>([]);

// 참조하는 DOM의 타입을 지정하면 된다.
const todoTextInputRef = useRef<HTMLInputElement>(null);

// event도 타입 지정해줘야 한다. ts는 모른다.
const submitHandler = (event: React.FormEvent) => {
  event.preventDefault();

  // 확실히 값을 들고 온다면 !.로 사용해주면 된다. null일 수도 있다면 ?.로 들고 온다.
  const enteredText = todoTextInputRef.current!.value;
};

Context API에 사용해보기

// createContext에 명시해줘야 한다.
export const TodosContext = createContext<TodosContextObj>({
  items: [],
  addTodo: (todoText: string) => {},
  removeTodo: (todoId: string) => {},
});

// provider도 컴포넌트이기 때문에 React.FC로 명시, props.children을 위해 타입 명시
const TodosContextProvider: React.FC<React.PropsWithChildren> = (props) => {};

// 사용은 기존과 똑같이 해주면 된다.

tsconfig.json은 컴파일하는데 필요한 루트 파일과 옵션을 지정한다. 자세한 건 아래 참조사이트에서 확인해보자. 중요한 것만 주석을 달아놨다.

{
  "compilerOptions": {
    "target": "es5", // js 버전에 따라 변경한다. ex) ex5이하인 경우, arrow func을 function 표현식으로 변환
    "lib": ["dom", "dom.iterable", "esnext"], // 타입 라이브러리
    "allowJs": true, // js 파일 허용 여부, ts 파일에 js 파일도 같이 사용할  있다.
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true, // 엄격함! true 타입을  명시할  있도록 타입 추론인 경우 제외하고 에러가 생긴다.
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx" // JSX 구문 내보내는 방법 제어
  },
  "include": ["src"]
}

수강 완료!

certificate


강의명

  • Udemy : React 완벽 가이드

카테고리:

업데이트:

댓글남기기