4 간단하게 페이지 띄워보기 - TypeScript로 컴포넌트 작성하기
source: categories/study/gatsby/gatsby_4.md
지난 챕터에서 간단하게 텍스트를 띄워보며 메인 페이지를 띄워보았습니다.
여기에서는 저번에 만든 컴포넌트를 통해 어떻게 타입스크립트가 가진 장점을 리액트 컴포넌트에 적용할 수 있는지 알아봅시다.
TypeScript의 핵심 기능, Types
- Types는 자바스크립트의 동적 타입으로 인해 야기될 수 있는 문제를 해결할 수 있는 타입스크립트의 강력한 기능입니다.
- 보통 아래와 같이 사용할 수 있으며, 설정한 타입에 맞지 않은 값을 지정했을 때에는 에러를 표시해줍니다.
const str: string = "This is String"; // Good
const num: number = "This is String"; // error TS2322: Type 'string' is not assignable to type 'number'.
저번 챕터에서 이미 한 번 접했지만, 이 기능을 리액트 컴포넌트를 작성할 때에는 어떻게 사용할 수 있을까요?
저희가 타입을 표시해줄 수 있는 것은 컴포넌트를 저장한 변수 뿐입니다.
리액트에서는 기본적으로 함수형 컴포넌트를 위한 타입을 제공해주는데요, 아래와 같이 사용할 수 있습니다.
import React, { FunctionComponent } from 'react';
import Text from 'components/Text';
const IndexPage: FunctionComponent = function () {
return <Text text="Home" />
}
export default IndexPage
FunctionComponent
가 리액트에서 기본적으로 제공해주는 함수형 컴포넌트 타입입니다.- 이를 통해 컴포넌트를 담는 변수에 타입을 지정해줄 수 있습니다.
- 하지만 이렇게만 보고 나서는 리액트에 타입스크립트를 적용하여 얻는 이점이 잘 보이지 않아 대체 왜 사용하는지 모를 수도 있을 것입니다.
- 그러나 타입스크립트의 또 다른 기능인 Generic을 추가하면 왜 타입스크립트를 사용하는지 알 수 있습니다.
React에서 Types를 잘 활용할 수 있게 해주는 Generic
자바 개발자라면 Generic이라는 단어가 되게 익숙할 수 있습니다.
타입스크립트에서 제공하는 Generic 기능은 자바에서의 Generic 기능과 거의 동일합니다.
하지만 Genenric에 대해 처음 들어보는 사람이 대부분일테니 짚고 넘어가겠습니다.
Generic이란 어떠한 클래스나 함수에서 사용할 타입을 그 함수나 클래스를 사용할 때 결정할 수 있게 만들어주는 기능입니다.
설명만 들어서는 잘 와닿지 않을테니 예시를 통해 알아봅시다.
아래 예시는 타입스크립트를 통해 만든 Stack 자료구조입니다.
class Stack {
private data: any[] = []
constructor() {}
push(item: any): void {
this.data.push(item)
}
pop(): any {
return this.data.pop()
}
}
이렇게 만든 Stack에서는 어떤 타입의 데이터가 추가될 지 모르기 때문에 데이터를 저장하는 변수나 함수에 any 타입을 지정했습니다.
따라서 다음과 같은 상황이 발생할 수 있습니다.
const stack = new Stack()
stack.push(10)
stack.push('10')
console.log(stack.pop().toFixed()) // 10
console.log(stack.pop().toFixed()) // Error
Number.prototype.toFixed
메서드는 숫자 자료형에서만 사용할 수 있는 함수입니다.- 하지만 위에서 정의한 Stack 자료구조는 숫자 자료형이 아닌 데이터도 추가할 수 있기 때문에 숫자 데이터만 있을 것으로 확신하여 코드를 작성하다가는 다음과 같이 에러가 발생할 수 있습니다.
- 여기서 확실하게 숫자 데이터만 받을 것이라고 명시해줄 수 있는 것이 바로 Generic입니다.
class Stack<DataType> {
private data: DataType[] = []
constructor() {}
push(item: DataType): void {
this.data.push(item)
}
pop(): DataType {
return this.data.pop()
}
}
Generic을 사용하여 작성한 Stack 자료구조 코드입니다.
클래스명 바로 옆에 추가된 것이 타입 변수인데요, 이를 통해 클래스를 정의할 시점이 아닌 인스턴스를 생성할 시점에 타입을 정의해줄 수 있습니다.
const stack = new Stack<number>()
stack.push('10') // error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
위와 같이 다른 타입의 데이터를 추가할 때 에러를 발생시킵니다.
이렇게 저 자료구조는 숫자 타입의 데이터만 받는다고 확신할 수 있는 것이죠.
그럼 이제 Generic을 사용해 컴포넌트를 수정해봅시다.
아래는 Types와 Generic을 사용해 변경한 Text.tsx
파일의 코드입니다.
import React, { FunctionComponent } from 'react'
interface TextProps {
text: string
}
const Text: FunctionComponent<TextProps> = function ({ text }) {
return <div>{text}</div>
}
export default Text
FunctionComponent
타입에 TextProps
Generic을 추가했습니다.
리액트에서는 함수형 컴포넌트 타입에 Generic을 추가함으로써 컴포넌트가 받는 props가 어떤 것이 있고, 타입은 무엇인지 지정해줄 수 있습니다.
따라서 해당 컴포넌트에 props를 넘길 때에 정해진 타입에 맞는 데이터를 넘길 수 있도록 할 수 있습니다.
현재 index.tsx
파일의 IndexPage 컴포넌트에서는 string 타입의 데이터를 props로 넘기고 있습니다.
이를 숫자 타입으로 바꾸면 어떻게 될까요?
import React, { FunctionComponent } from 'react'
import Text from 'components/Text'
const IndexPage: FunctionComponent = function () {
return <Text text={10} /> // error TS2322: Type 'number' is not assignable to type 'string'.
}
export default IndexPage
이렇게 Types와 Generic을 통해 Props의 Type Checking이 가능합니다.
리액트에서 타입스크립트를 사용하는 가장 큰 이유 중 하나입니다.
위와 같은 예시 정도에서도 장점이 잘 드러나는 Type Checking은 컴포넌트의 규모가 커질수록 효과가 크게 나타납니다.