조금조금 REACT, '리엑트' - TSX (1) create-react-app

Edwin·2023년 5월 16일
0

조금조금 REACT

목록 보기
29/31
post-thumbnail

'리엑트' - TSX (1) create-react-app

  • 공식문서에 따르면, 리액트에서 TS를 적용하기 위해서는 기존의 CRA 뒤에 --template typescript 를 붙여주면 된다.

npx create-react-app my-app --template typescript
yarn create react-app my-app --template typescript

JSX와 다른 TSX

타입스크립트를 사용하는 이유

첫째, JavaScript and More
타입스크립트는 편집기(vsCode)와 긴밀한 통합을 지원하기 위해 JS에 구문을 추가하였다. 편집시, 빠르게 에러를 잡아보세요.

둘째, A Result You Can Trust
TS코드는 실행을 위해서 JS로 변환해야 한다. 빌드를 위해서 TS로는 사용할 수 없기 때문이다. JS가 인터프리터 언어라면, TS는 컴파일 언어로 JS를 표현하는 슈퍼셋인데, 이는 런타임에 가서야 에러가 확인되는 JS의 한계를 극복하기 위한 시도이다. 그 결과 빌드된 코드는 신뢰할 수 있게 되는 것이다.

셋째, Safety at Scale
TS는 자바스크립트를 이해하고 타입추론을 통해 추가 코드 없이도 코드를 이해한다.

코드를 작성할 떼 JS와 TS의 가장 큰 차이는 사용되는 모든 리소스에 대해서 "타입"을 설정해야 한다는 점이다. 리액트에서 타입스크립트를 사용한다는 것은 컴포넌트도 타입을 설정해야 한다는 것을 의미한다. 이 부분이 처음에는 어려웠다. 일반적인 TS 강의에서는 리액트의 컴포넌트에도 타입을 설정해야 한다는 내용이 없기 때문이다.

CRA --template typescript

리액트(JSX)는 아래와 같았다.

import React from 'react'

const App = () => {
  return <div>App</div>;
}

export default App

리액트(TSX)에서는 아래와 같이 변경된다.

const App:React.FC = () => {
  return <div>App</div>;
}

export default App;

큰 차이는 컴포넌트의 식별자 부분이다. TSX에서는 App:React.FC 식별자 뒤에 컴포넌트부터 타입을 설정해야 한다.

모든 리소스에 대한 타입설정

let data = {
  name:"누나네 식당",
  category:'western',
  address: {
    city:'incheoi',
    detail:'somewhere',
    zipCode: 234234234
  },
  menu: [
    {name:'rose paste', price:20000, category:'PASTA'},
    {name:'garlic paste', price:25000, category:'PASTA'}
  ]
}

JSX에서 변수 data를 사용하기 위해서는 위와 같이 그냥 기록하고 사용했다. 그러나 TSX에서는 사용될 모든 리소스에 대해서 타입을 설정해야 한다. 모르겠다면 치트키인 any 타입이 있지만, 그러면 타입스크립트를 사용할 이유가 없기 때문에 엄격함이 필요하다. 그러나 타입을 설정하고 빌드된 파일은 에러발생없이 아주 깔끔한 런타임 환경을 경험할 수 있게 된다.

src > model 폴더, 타입파일기록

코알누가 말해주는 실무에서의 TS 사용은 일반적으로 src => model 폴더를 생성하고 그 아래 타입 추출할 하위 ts 파일을 생성함으로 시작된다.

data에 대한, 타입을 선언해보자. 타입을 선언하는 방법은 아래와 같이 두가지의 방법이 있다. 각 방법에 따른 메서드의 치아기 있다. 사용은 취향인 것 같다.

  • 키워드(1) : type
  • 키워드(2) : interface

interface는 extends 를 통해서 확장을 할 수 있다. 인프런 강의에서는 type은 확장할 수 없다고 배웠는데, 코알누는 type 키워드도 확장이 가능할 수 있음을 보여주었다.

type OwnPropsType = Menu & {
  // & 연산자를 통해서 확장할 수 있다. 
} 

type 키워드는 선언된 타입에서 특정 속성을 제외(Omit)하거나, 특정 속성만을 추출(Pick)해서 사용할 수 있다는 점에서 다채롭다.

export type Address = {
  city: string;
  detail: string;
  zipCode: number
}

Address라는 type을 설정했다고 하자. 그렇다면 아래와 같이 사용할 수 있다.

export type AddressWithoutZipCode = Omit<Address, 'zipCode'>
// zipCode를 제외하고 city, detail을 사용하고자 할 때
export type AddressOnlyCity = Pick<Address, 'city'>
//  기존 type에서 city만을 사용하고자 할 때 

부모컴포넌트에서 props 전달

첫째, 부모컴포넌트에서 사용되는 데이터에 대해 타입을 설정한다.
둘째, props를 전달받은 자녀컴포넌트에서 props의 타입을 설정한다.

App.tsx 먼저 살펴보자.

import React, { useState } from 'react';
import './App.css';
import Store from './Store';
import { Address, ResturantsInfo } from './model/resturant';
import BestMenu from './BestMenu';


let data:ResturantsInfo = {
  name:"누나네 식당",
  category:'western',
  address: {
    city:'incheoi',
    detail:'somewhere',
    zipCode: 234234234
  },
  menu: [
    {name:'rose paste', price:20000, category:'PASTA'},
    {name:'garlic paste', price:25000, category:'PASTA'}
  ]
}

const App:React.FC = () => {
  return (
    <div className="App">
      <Store info={myRestaurant}/>
    </div>
  );
}

export default App;

resturant.ts 타입설정파일을 보자

export type Address = {
  city: string;
  detail: string;
  zipCode: number
}

export interface Menu {
  name: string;
  price: number;
  category: string
}

export interface ResturantsInfo {
  name: string;
  category: string;
  address: Address;
  menu: Menu[]
}

resturant.ts에서 설정한 타입을 App.tsx에 주입하여 타입을 설정한다.

Store.ts 자녀컴포넌트를 살펴보자

import React from 'react'
import { Address, ResturantsInfo } from './model/resturant'


interface OwnProps {
  info: ResturantsInfo
}

const Store:React.FC<OwnProps> = ({info}) => {
  return (
    <div>{JSON.stringify(info)}</div>
  )
}

export default Store

함수를 props로 내려준다면?

// App.tsx
 const changeAddress = (address: Address): void => {
    setMyRestaurant({...myRestaurant,address:address})
  }


 // Store.tsx
import React from 'react'
import { Address, ResturantsInfo } from './model/resturant'


interface OwnProps {
  info: ResturantsInfo
  changeAddress(address: Address): void; 
}

const Store:React.FC<OwnProps> = ({info, changeAddress}) => {
  return (
    <div>{JSON.stringify(info)}</div>
  )
}

export default Store

부모컴포넌트에서 함수를 선언하며 설정한 타입이 있다면, 자녀컴포넌트에서도 프롭스를 설정해주면되는데, Store:React.FC<OwnProps>와 같이 컴포넌트 다음에 props의 타입을 <generic>으로 선언해주면 된다. 이는 부모컴포넌트에서 자녀컴포넌트로 올 props의 타입에 대해서 정의할 수 없기 때문이다. 자녀컴포넌트에서 OwnProps를 타입선언 키워드로 선언하고, 해당 객체 안에 props의 타입을 기록해주면 된다.

타입스크립트에서 <generic>이란?

ChptGPT에 따르면, 일반적으로 제네릭을 이용하면 여러 종류의 타입을 수용하도록 만들 수 있다. 타입을 전달받아, 사용한다. 제네릭을 사용하는 일반적인 형태는 타입 매개변수를, 인자로 전달받아 반환값의 타입으로 사용하는 것이다. 리액트 컴포넌트에서의 <generic>은 위와 같이 사용된다.

해당 자녀컴포넌트에서 사용될 리소스에 대한 타입을 객체 안에 담는다. 이를 통해서 리소스를 규정한다.

useState와 타입설정

const [myRestaurant, setMyRestaurant] = useState<ResturantsInfo>(data)

위에서 선언한 data를 useState의 초기값으로 설정한다고 하면, useState<ResturantsInfo>과 같이 제너릭으로 설정해주면 된다. 이는 useState가 어떤 타입을 받을지 사전에 알 수 없기에, 제너릭으로 처리해주는 것이다.

props에서 type을 주입받을 때

// App.tsx  
<BestMenu name="불고기피자" category="피자" price={20000} showBestMenuName={showBestMenuName}/>

위에서 언급한 것처럼 부모컴포넌트에서 props로 내려줄 때 에러가 발생되지 않으르면, 자녀컴포넌트에서 props에 대한 타입을 설정해야 한다.

import React from 'react'
import { Menu } from './model/resturant'

interface OwnProps extends Menu {
 }

const BestMenu: React.FC<OwnPropsType> = ({name, category, price}) =>  {
  return (
    <div>{`${name}, ${category}, ${price}`}</div>
  )
}

export default BestMenu

interface의 확장(extends)을 통해서, 사전에 정립해 놓은 타입이 있다면 가져와서 사용할 수 있다. 그리고 이는 type의 & 연산자를 통해서 표현하는 것도 가능한데 아래와 같다.

interface OwnProps extends Menu {}
type OwnPropsType = Menu & {}

위에서 선언된 interface와 type은 동일한 기능을 수행한다. 여기까지가 코알누의 TS 스트립트 2번째 강의내용이다. TS는 공부했지만, 이를 리액트에서 사용하는 것은 또다른 문제였다.

마지막으로 비동기 데이터의 타입설정

export type ResponstDate<T> = {
  data:T[]; 
  totalPate:number;
  page:number
}

export type RestaurantResponse = ResponstDate<ResturantsInfo>
export type MenuResponse = ResponstDate<Menu>

비동기로 받아오는 리소스에는 다양한 내용이 있을 수 있다. 배열이 있을 수 있고, 페이지네이션 또는 무한스크롤을 구현한다고 했을 때 해당 정보가 전달될 수 있다. 그러나 정확히 어떤 데이터가 담겨올지 알 수가 없기에, 여기에 또 제너릭이 등장한다. 기본 타입을 설정하고

해당 API에 대한 type을 지정하여, 내보내는 것으로 비동기 처리로 전달되는 data에 대한 타입을 설정할 수도 있다.

edited. EDWIN

profile
신학전공자의 개발자 도전기!!

0개의 댓글