본문 바로가기

웹공부/React

React 공부 9일차

 

TypeScript

JavaScript를 기반으로 한 프로그래밍 언어, strongly-typed language

JS 거의 복붙인데 거기에 새로운 기능만 살짝 추가됐다고 생각하면 된다.(Type에 관해서...)

const plus = (a, b)=>a + b;
JS는 이대로 코드를 실행시켜도 문제 없이 결과를출력한다. a, b의 자료형이 달라도 상관없이 말이다.
하지만 저대로 코드를 실행하면, TypeScript는 error message를 출력한다.
TypeScript 코드가 발동하기 전에 protection이 일어나고, 별 문제가 없으면 JS 코드를 return한다.

 

const plus = (a: number,b: number)=>a + b;
TypeScript는 strongly-typed한 특성을 가지기 때문에 위 코드처럼 a, b의 자료형이 무엇인지 (:) 뒤에 작성해줘야 한다.
코드 작성 시 의도치 않은 실수를 예방하는 데 도움이 된다.
 

1) 환경 setup

https://create-react-app.dev/docs/adding-typescript/

 

Adding TypeScript | Create React App

Note: this feature is available with react-scripts@2.1.0 and higher.

create-react-app.dev

나는 index.js, App.js 파일 코드를 복사한 후 지금까지 한 모든걸 지우고 

create-react-app을 tyepscript가 추가된 버전으로 다시 생성했다.

이게 기존 내용 save하면서 이것저것 설정하는것보단 훨씬 간단했기 때문이다.

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

새 프로젝트를 생성 한 후,

index.tsx, App.tsx 파일에 각각 복사해놨던 코드를 복붙하고 typescript에서 쓸 수 있는 버전의 "styled-components" 환경

또한 설치했다.

npm install @types/styled-components

우리가 TypeScript를 사용하는 이유는 코드 실행 전 오류를 확인하기 위해서다.

그래서 이제 propTypes를 사용하지 않고, prop들을 TypeScript로 보호해 줄 것이다.

 

component에게 type을 줌으로써, TypeScript에게 해당 component가 가져야 하는 prop을 설명해 줄 수 있다.

어떻게 우리 자신과 props를 interafce를 사용하여 보호하는지 코드로 살펴보자.

*interface: object가 어떤 식으로 보일지 설명해주는 것

 

interface PlayerShape {
    name: string;
    age: number;
}

const sayHello = (playerObj:PlayerShape) => `
    Hello ${playerObj.name} You are ${playerObj.age}.
`

sayHello({name: "Panda", age: 8});

기본적으로 전달되는 props는 required이다. (interface에 정의된 props중 하나라도 빠져있으면, error가 난다는 뜻이다.)

 

 

<App.tsx>

import styled from "styled-components";
import Circle from "./Circle";
function App() {
  return (
  <div>
    <Circle bgColor="teal"/>
    <Circle bgColor="tomato"/>
  </div>
  );
}

export default App;

<Circle.tsx>

import styled from "styled-components";

//(2) styled-component 뒤에 추가하면 Circle 컴포넌트에 있던 에러가 사라짐.
const Container=styled.div<ContainerProps>`
    width: 200px;
    height: 200px;
    background-color: ${props=> props.bgColor};
    border-radius: 100px;
`;

//bgColor를 styled-components에도 보내기 위해서(1)interface 만들고
interface ContainerProps{
    bgColor:string;
}  

interface CircleProps{
    bgColor:string;
}



//이제 TS은 CircleProps 안에 bgColor가 있다는 걸 알게 됨
function Circle(props:CircleProps){
    //그럼 이제 bgColor를 styled-components로 보내줄 수 있음.
    return <Container bgColor={props.bgColor} />;
}

export default Circle;

//interface 안에다가 TS에게 object shape를 설명해 줌.

//1. 어떻게 우리 자신과 props를 interface를 사용해 보호하는지
//2.

<index.tsx>

import React from "react";
import ReactDOM from "react-dom";
import {ThemeProvider} from "styled-components";
import App from "./App";




ReactDOM.render(
  <React.StrictMode>
        <App />
  </React.StrictMode>,
  //should include the target HTML element in 'ReactDOM.render()'
  document.getElementById('root')
)

위의 코드 실행 화면

 

 

 

어떻게 props를 required 되지 않는 상태로 변경할 수 있을까?(전달이 안돼도 문제 없는 상태)

Optional Props

Circle.tsx

import styled from "styled-components";

//(2) styled-component 뒤에 추가하면 Circle 컴포넌트에 있던 에러가 사라짐.
const Container=styled.div<ContainerProps>`
    width: 200px;
    height: 200px;
    background-color: ${props=> props.bgColor};
    border: 1px solid ${props=>props.borderColor};
    border-radius: 100px;
`;

//bgColor를 styled-components에도 보내기 위해서(1)interface 만들고
interface ContainerProps{
    bgColor:string;
    borderColor: string;
}  

interface CircleProps{
    bgColor:string;
    //optional props(?)
    borderColor?:string;
    text?:string;
}

//이제 TS은 CircleProps 안에 bgColor가 있다는 걸 알게 됨
function Circle({bgColor, borderColor, text="default text"}:CircleProps){
    //그럼 이제 bgColor를 styled-components로 보내줄 수 있음.
    //borderColor가 undefined된 상태에서의 default값을 줘야 에러가 안남.
    return <Container bgColor={bgColor} borderColor={borderColor?? "yellow"}>{text}</Container>;
}

export default Circle;

해당 코드는 borderColor(테두리 색)과 text를 optional한 prop으로 지정한 코드이다.

optional prop을 지정할 때는, 사용자 지정 색이 없을 때 사용할 default값을 주어야 에러가 나지 않는다.

 

1) CircleProps 인터페이스에서 borderColor?:string;     text?:string;부분처럼 {속성명} 뒤에 물음표를 붙이면

optional prop으로 지정하는 것이다.

2) function Circle부분에 사용자 지정 입력이 없을 때의 default값은 다음과 같이 써주면 된다.

함수의 매개변수로 Object를 받는 곳에 {bgColor, borderColor, text="default text"}

바로 써주거나, 함수 return값 Container 컴포넌트 안에서 <Container  borderColor={borderColor?? "yellow"} >

{속성명} 뒤에 물음표 2개 찍고 default 값을 적어주면 된다.

function App(){
  return (
    <div>
      <Circle bgColor="teal" borderColor="red" text="optional text"/>
      <Circle bgColor="tomato"/>
    </div>
  )
}

bgColor는 required props이므로 지정해주지 않으면 에러가 나지만, borderColor, text는 지정해주지 않아도

default값으로 출력되는 걸 확인할 수 있었다.

코드의 결과물

 

State

보통 state는 초깃값으로 지정한 자료형 타입 한가지로만 계속 사용되는 경우가 대다수다.

const [value, setValue] = useState(1);

원래 TypeScript에서는 속성의 type을 명시해주는게 원칙이지만, useState를 사용할 때는, 지정해준 초깃값을 바탕으로

state의 자료형을 자동으로 인식해준다.

위의 코드는 초깃값 Type이 number이므로, setValue("string"); 코드는 Error가 난다.

 

간혹, state의 자료형이 하나로 통일되지 않고, 나중에 바뀌어야 하는 경우도 생길 수 있다. 이런 경우엔

const [value, setValue] = useState<number | string >(1);
    setValue("hi");
    setValue(true);

useState<{type1 | type2 | ...}> 형태로 허용하고자 하는 state의 type을 여러개 지정할 수 있다.

setValue("hi")는 string이므로 error가 나지 않지만

setValue(true)는 boolean 타입 허용에 대한 언급이 없었으므로 error처리 된다.

 

Forms

▶여담: TypeScript를 사용하다 보면, 모르는 많은 것들이 있다. 어디에 타입이 있는지, 타입을 넣는 방법들이 무궁무진하기 때문에 항상 구글링과 함께 해야한다. 

https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/forms_and_events/

 

Forms and Events | React TypeScript Cheatsheets

If performance is not an issue (and it usually isn't!), inlining handlers is easiest as you can just use type inference and contextual typing:

react-typescript-cheatsheet.netlify.app

javascript에서는 form의 event의 타입을 지정해주지 않아도 진행이 되지만, 타입스크립트는 event가 어디서 발생하는지에 대한 타입을 지정하지 않으면 에러가 발생한다.

어떤식으로 Form 태그를 사용해야 할까? form event의 type을 어떻게 설정해줘야 할까?

App.tsx

import styled from "styled-components";
import {useEffect, useState} from "react";
import Circle from "./Circle";
function App() {
  const [value, setValue]=useState("");
  const onChange=(event: React.FormEvent<HTMLInputElement>)=>{
    const {
      currentTarget: {value},
        //event.currentTarget: 이벤트가 발생한 요소를 나타낸다. 여기서는 <input> 요소를 나타낸다.
        //currentTarget의 value 속성을 추출한다. 
    }=event;
    setValue(value);
    //console.log(event.currentTarget.value);
    //target vs currentTarget: 같은 것?
  }

  //event는 form으로부터 왔고, HTMLForm Element가 event를 발생시킨다.
  const onSubmit=(event: React.FormEvent<HTMLFormElement>)=>{
    event.preventDefault();
    console.log("hello", value);
  }
  return (
  <div>
    <form onSubmit={onSubmit}>
      <input
      value={value}
      onChange={onChange}
      type="text"
      placeholder="username"
      />
      <button>LogIn</button>
    </form>
  </div>
  );
}

export default App;

기본적으로 JSX 문법과 거의 동일하다.

 const onChange=(event: React.FormEvent<HTMLInputElement>)=>{
    const {
      currentTarget: {value},
    }=event;
 
이 부분과

 

  const onSubmit=(event: React.FormEvent<HTMLFormElement>)=>{
    event.preventDefault();
    console.log("hello", value);
  }
이 부분에서 차이점을 살펴보자.
onChange 함수의 매개변수 부분에는 React.FormEvent<HTMLInputElement>,
onSubmit 함수의 매개변수 부분에는 React.FormEvent<HTMLFormElement>라는 타입 주석이 추가되었는데,
얘는 event가 HTMLForm에서 발생한 이벤트라는 걸 나타낸다.
 
Themes

1) 환경 setting

src 폴더 밑에 styled.d.ts 파일을 만들고, 해당 링크로 들어가서 declaration file을 복붙한다.

https://styled-components.com/docs/api#create-a-declarations-file

 

styled-components: API Reference

API Reference of styled-components

styled-components.com

그 후 테마가 어떻게 보일지에 대한 요소들을 작성한다. DefaultTheme 안의 코드를 수정한다.

styled.d.ts
// and extend them!
declare module 'styled-components' {
  export interface DefaultTheme {
    textColor:string;
    bgColor:string;
    btnColor: string;
  }
}

테마에 대한 interface를 지정해 export한다.

그리고 theme.ts 파일에 import 한다.

2) 테마 만들기

src 폴더 밑에 theme.ts 파일 생성

theme.ts
import {DefaultTheme} from "styled-components";

export const lightTheme: DefaultTheme = {
    bgColor: "skyblue",
    textColor:"black",
    btnColor: "tomato",
};

export const darkTheme: DefaultTheme = {
    bgColor: "tomato",
    textColor:"white",
    btnColor: "teal",
};

원하는 테마를 생성하는 코드를 작성한다. 테마 export 하는 것 잊지 말고, 테마를 사용할 App 컴포넌트에 import 해준다.

테마를 생성할 때 해당 테마의 interface가 DefaultTheme인 것을 : 뒤에 표기해준다.

 

 

3) index.tsx 파일로 가서 <ThemeProvider> 컴포넌트 추가하기

ThemeProvider는 styled-components로부터 오는 하나의 컴포넌트이다.

그리고 지정할 theme을 적어준다.

index.tsx
import React from "react";
import ReactDOM from "react-dom";
import {ThemeProvider} from "styled-components";
import App from "./App";
import {lightTheme, darkTheme} from "./theme";

ReactDOM.render(
  <React.StrictMode>
    <ThemeProvider theme={darkTheme}>
        <App />
    </ThemeProvider>
  </React.StrictMode>,
  //should include the target HTML element in 'ReactDOM.render()'
  document.getElementById('root')
)

 

4) index.tsx 파일에서 테마 지정 후 App.tsx 파일에 테마(theme) 적용되는 것 확인

App.tsx
import styled from "styled-components";

function App(){
  const Container = styled.div`
  background-color:${props=>props.theme.bgColor};
  `
  const H1=styled.h1`
    color:${props=>props.theme.textColor};
  `
  return (
    <div>
      <Container>
        <H1>Hello</H1>
      </Container>
     
    </div>
  )
}

export default App;

 <ThemeProvider theme={darkTheme}>

darkTheme 모드 적용

 <ThemeProvider theme={lightTheme}>

lightTheme 모드적용

 

보통 어떤 라이브러리를 import 하려고 할 때, 아래 링크에서 찾아서 환경설정 해주면 된다.

간혹가다가 때때로 타입을 갖지 않는 뭔가를 다운로드 할 수도 있다.

아래 깃허브 레포지토리에 모든 걸 다 보여주지는 않기때문에 아래 링크 가서 무작정 찾아보지말고 일단

npm i --save-dev @types/{라이브러리 이름} 설치를 시도해보자.

설치가 된다면, ok고 타입이 없는 거면, 타입을 갖지 않는 ~~ 라고 뜰 것이다.

https://github.com/DefinitelyTyped/DefinitelyTyped

 

GitHub - DefinitelyTyped/DefinitelyTyped: The repository for high quality TypeScript type definitions.

The repository for high quality TypeScript type definitions. - GitHub - DefinitelyTyped/DefinitelyTyped: The repository for high quality TypeScript type definitions.

github.com

 

'웹공부 > React' 카테고리의 다른 글

React에서 typescript 사용하기  (0) 2024.01.16
React에서 styled components 사용하기  (1) 2024.01.08
React 공부 8일차  (0) 2023.08.21
React 공부 7일차  (0) 2023.08.18
React 공부 6일차  (0) 2023.08.17