TypeScript 로 React 앱 만들기

서론

JavaScript 는 weakly typed 언어이기에, 숫자가 문자열로 될 수도 있고 그랬다가 또 숫자가 될 수도 있다가 null 인지 아닌지 확인하지 못합니다. 추가적으로 자동완성도 우리가 Java / C# / C++ / Python 등의 언어를 사용 할 때처럼 제대로 되지 않습니다. (VSCode 를 사용하면 어느정도 되고 있다고 착각 할 수도 있는데 사실 TypeScript 관련 기능이 이미 기본적으로 돌아가고있어서 되는 거랍니다.) 만약에 TypeScript 를 사용하면, 이러한 불편함을 해결해주어 개발을 훨씬 편하게 해줍니다.

이 강의에서는, TypeScript 를 맛보기식으로 중요한것들만 조금씩 알아보고, 리액트에서 사용하는 방법을 알아보겠습니다.

TypeScript 를 사용하는 이유

TypeScript 를 프로젝트에서 사용하는 대표적인 이유는 다음과 같습니다.

1. IDE 를 더욱 더 적극적으로 활용 (자동완성, 타입확인)

TypeScript 를 사용하면 자동완성이 굉장히 잘됩니다. 함수를 사용 할 때 해당 함수가 어떤 파라미터를 필요로 하는지, 그리고 어떤 값을 반환하는지 코드를 따로 열어보지 않아도 알 수 있습니다. 추가적으로, 리액트 컴포넌트의 경우 해당 컴포넌트를 사용하게 될 때 props 에는 무엇을 전달해줘야하는지, JSX 를 작성하는 과정에서 바로 알 수 있으며, 컴포넌트 내부에서도 자신의 props 에 어떤 값이 있으며, state 에 어떤 값이 있는지 알 수 있습니다. 또한, 리덕스와 함께 사용하게 되면 connect 로 통하여 props 로 전달해 줄 때에도 자동완성이 되어 굉장히 편리합니다.

2. 실수 방지

함수, 컴포넌트 등의 타입 추론이 되다보니, 만약에 우리가 사소한 오타를 만들면 코드를 실행하지 않더라도 IDE 상에서 바로 알 수 있게 됩니다. 그리고, 예를 들어 null 이나 undefined 일 수도 있는 값의 내부 값 혹은 함수를 호출한다면 (예: 배열의 내장함수) 사전에 null 체킹을 하지 않으면 오류를 띄우므로 null 체킹도 확실하게 할 수 있게 됩니다.

프로젝트 만들기

create-react-app 의 스크립트 기능을 사용하면 TypeScript 가 적용된 프로젝트를 매우 쉽게 만들 수 있습니다.

$ npx create-react-app typescript-react-app --scripts-version=react-scripts-ts

TypeScript 가 적용된 프로젝트를 생성하였습니다. VSCode 마켓플레이스에서 TSLint 확장 프로그램을 설치하시면 코드 스타일과 기본적인 자바스크립트 문법도 에디터상에서 바로 점검 할 수 있습니다. 조금 더 까다로운 규칙을 사용하고 싶다면 tslint-config-airbnb 를 설치하고, tslint.json 에서 다음과 같이 적용하시면 됩니다.

$ yarn add --dev tslint-config-airbnb
{
  "extends": [
    "tslint:recommended",
    "tslint-react",
    "tslint-config-airbnb",
    "tslint-config-prettier"
  ],
  "linterOptions": {
    "exclude": [
      "config/**/*.js",
      "node_modules/**/*.ts",
      "coverage/lcov-report/*.js"
    ]
  },
  "rules": {
    "import-name": false,
    "interface-name": false,
    "semicolon": [true, "always", "ignore-bound-class-methods"],
    "variable-name": false
  }
}

끄고싶은 규칙이 있다면 rules 에서 조정해주시면 됩니다.

airbnb 규칙을 적용하고나면 index.js 와 registerServiceWorker 쪽에서 에러가 발생할텐데요, index.js 에서는 render 가 사용되는 곳을 이렇게 수정해주시고

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import App from './App';
import './index.css';
import registerServiceWorker from './registerServiceWorker';

ReactDOM.render(<App />, document.getElementById('root') as HTMLElement);
registerServiceWorker();

registerServiceWorker.js 에서는 코드 최상단에 다음 주석을 작성하여 TSLint 에서 이 파일을 검사하지 않도록 하세요.

/* tslint:disable */

prettier 를 사용하고 싶으시다면 다음과 같이 .prettierrc.js 파일을 만드세요

.prettierrc.js

module.exports = {
  printWidth: 100,
  parser: 'typescript',
  singleQuote: true,
  useTabs: false, // Indent lines with tabs instead of spaces.
  printWidth: 80, // Specify the length of line that the printer will wrap on.
  tabWidth: 2, // Specify the number of spaces per indentation-level.
  trailingComma: 'es5'
};

그리고 VSCode 환경설정에서 다음과 같이 해주면 저장할때마다 자동 포맷팅 됩니다.

{
  "editor.formatOnSave": true
}

타입 사용하기

우선 src 디렉토리에 tutorial.ts 라는 파일을 만들어서 기본적인 연습을 해봅시다. TypeScript 를 사용 할때는 .ts (리액트 컴포넌트의 경우에는 .tsx) 확장자를 사용합니다.

let, const 사용

let text: string = '';
let number: number = 5;

number = 'asdf';
text = 5;

const array: number[] = [1, 2, 3];
array.push('asdf');

이렇게, 의도치 않은 작업을 하려고 하면 오류 뿜뿜 하는것을 경험해보세요!

이번엔 함수를 작성하보겠습니다.

function squareNumbers(numbers: number[]): number[] {
  return numbers.map(n => n * n);
}

숫자 배열을 파라미터로 받아와서 결과값도 숫자배열로 반환합니다.

이렇게 하고나면 내장함수를 사용 할 때에도 해당 값의 타입이 뭔지 추론을 할 수 있습니다. n, numbers, squareNumber 쪽에 마우스를 올려보세요.

그리고, 이런식으로 숫자 배열이 아닌것을 파라미터로 전달하면, 당연히 오류가 나겠죠?

우리가 방금 사용한 타입외에도 수많은 타입들이 있는데요, 여기 에서 확인 가능하니 참고하세요!

리액트 class 형 컴포넌트 만들기

TypeScript React Code Snippets 를 사용하면 tsrcc, tsrcfull, tsrsfc 등의 단축단어로 컴포넌트를 쉽게 생성 할 수 있습니다. 설치하는것을 적극 권장드립니다.

자~ 그러면 무난한 카운터를 만들어 볼까요? 우선 props 만 받아와서 렌더링 해보겠습니다.

src/Counter.tsx

import * as React from 'react';

interface CounterProps {
  startNumber: number;
}

class Counter extends React.Component<CounterProps> {
  public render() {
    return (
      <div>
        <h1>{this.props.startNumber}</h1>
        <button />
      </div>
    );
  }
}

export default Counter;

이제 App 에서 렌더링하겠습니다. 자동완성을 적극적으로 사용해봅시다! Counter 를 렌더링 할 때 상단에서 import 구문 입력하지 말고 JSX 에서 <Counter 를 입력하고 엔터를 누르면 자동으로 불러와질 것입니다. (TypeScript 를 사용하지 않아도 이 기능이 작동하기는 하죠.. 왜냐하면 위에서도 언급했듯이 TypeScript 의 기능이 VSCode 에 기본 탑재가 되어있기 때문입니다.) 추가적으로! props 를 빠트리면 빨간줄이 그어지고, props 를 설정하게 될 때에 startNumber 이름이 자동완성 됩니다.

src/App.tsx

import * as React from 'react';
import Counter from './Counter';

class App extends React.Component {
  public render() {
    return <Counter startNumber={5} />;
  }
}

export default App;

이번에는 Counter 에서 state 를 사용해볼까요?

src/Counter.tsx

import * as React from 'react';

interface CounterProps {
  startNumber: number;
}

interface CounterState {
  number: number;
}

class Counter extends React.Component<CounterProps, CounterState> {
  public state = {
    number: 0,
  };

  constructor(props: CounterProps) {
    super(props);
    this.state.number = props.startNumber;
  }

  public handleClick = () => {
    this.setState({
      number: this.state.number + 1,
    });
  };

  public render() {
    return (
      <div>
        <h1>{this.state.number}</h1>
        <button onClick={this.handleClick}>Click Me</button>
      </div>
    );
  }
}

export default Counter;

리액트 함수형 컴포넌트 만들기

함수형 컴포넌트를 만들 땐, 이렇게 만듭니다.

src/ShowName.tsx

import * as React from 'react';

interface ShowNameProps {
  name: string;
}

const ShowName: React.SFC<ShowNameProps> = ({ name }) => (
  <div>
    내 이름은 <b>{name}</b>
  </div>
);

export default ShowName;

App 컴포넌트에서 렌더링해보세요!

import * as React from 'react';
import Counter from './Counter';
import ShowName from './ShowName';

class App extends React.Component {
  public render() {
    return (
      <div>
        <ShowName name="velopert" />
        <Counter startNumber={5} />
      </div>
    );
  }
}

export default App;

이제, 여러분들도 리액트 컴포넌트에서의 기본적인 TypeScript 활용을 할 수 있게 되었습니다. Redux 랑 함께 사용하게 되면 정말 편해지는데, 다음 강의에서 진행하겠습니다!

results matching ""

    No results matching ""