본문 바로가기
Today I Learned/SeSAC 웹 2기

TypeScript와 React | 13주차(3)

by suyeonnie 2025. 1. 30.

📂 오늘 배운 내용

  • React & TypeScript 프로젝트 세팅
  • Props Type (1) Interface 사용하여 props 타입 정의 (2)선택적 Props
  • useRef와 useState <T>Generic 개념
  • Event 객체의 타입

React & TypeScript 사용하기

// 프로젝트 만들기
npx create-react-app {프로젝트 이름} --template typescript

 

- TypeScript 오류 해결하기👾

추가하기

 

Props Type

- props란?

  • react에서 컴포넌트 간 데이터 전달하기 위해 사용되는 객체
  • 상위 컴포넌트에서 하위 컴포넌트로 데이터 전달할 때 사용

 

- Props와 Interface

Props를 이용해서 데이터를 상위 컴포넌트에게 받을 때는 props가 어떤 형태로 넘어오는 지 type 미리 적어줘야함

JavaScript-React props vs. TypeScript-React props

 

 

-예제

Lecture.tsx 의 PropsType 화면

 

[src/ pages/ Lecture.tsx]

import {
  PropsType1,
  PropsType2,
  PropsType3,
} from '../components/lecture/PropsType';

import Container from '../components/lecture/Container';
import SetText from '../components/lecture/SetText';
import EventObj from '../components/lecture/EventObj';
import TodoList from '../components/lecture/TodoList';

export default function Lecture() {
  return (
    <main>
      <PropsType1 name='suyeon' />
      <PropsType2 width='100px' color='red' height='100px' />
      <PropsType3 width='100px' height='50px' text='hi' />
      <PropsType3 width='100px' height='50px' text='hi' color='red' />

      <Container>
        <SetText />
        <EventObj />
        <TodoList />
      </Container>
    </main>
  );
}

 

[src/components/lecture/Container.tsx]

 

interface Props {
  // React 컴포넌트, JSX 요소, 문자열 등 모든 노드를 허용
  children: React.ReactNode; // <div></div>
}

export default function Container({ children }: Props) {
  return <div style={{ border: '1px dotted green' }}>{children}</div>;
}

 

[src/components/lecture/ PropsType.tsx]

 

1. inteface를 사용하여 props의 타입을 정의

: TypeScript에서는 `props`의 타입을 더 명확하게 지정하기 위해 Interface 사용

// 인터페이스 정의
// - 함수의 매개변수의 타입이니까 밖에서!
interface Props {
  name: string;
}

// PropsType1 컴포넌트
// - 구조분해할당 사용하여 props 객체에서 name만 추출
export function PropsType1({ name }: Props) {
  return (
    <>
      <h2>hello {name}</h2>
    </>
  );
}

 

interface Square {
  // 모든 key는 string이고, value도 string이어야 함
  [key: string]: string;
}

// props 객체를 매개변수로 받아, div 스타일을 동적으로 설정하는 컴포넌트
export function PropsType2(props: Square) {
  const divStyle = {
    width: props.width,
    height: props.height,
    backgroundColor: props.color,
  };

  return <div style={divStyle}></div>;
}

 

2. props를 선택적(optional)으로 만들기

: 물음표(?)를 사용해서 선택적 Props로 만들기

interface Square2 {
  width: string;
  height: string;
  color?: string;
  text: string;
}

// 구조분해 할당을 사용하여 props 객체에서 width, height, color, text 추출
export function PropsType3(props: Square2) {
  const { width, height, color, text } = props;
  const divStyle = {
    width: `${width}`,
    height: `${height}`,
    // ? 처리된 속성에 대해 예외 처리 해주어야함
    backgroundColor: `${color ? color : 'pink'}`,
    lineHeight: `${height}`,
    // textAlign: 'center',
  };

  return <div style={divStyle}>{text}</div>;
}

 

 

useRef와 useState

useState

  • 화면이 변경되어야 하는 값 저장할 때 사용
  • `useState<T>` 는 일반적으로 생략 가능하지만, `null | 타입`이 올 수 있을 때는 generic 사용

useRef

  • 렌더링과 관계 없는 값(변수, DOM 요소 등) 저장할 때 사용
  • `useRef<T>` 는 DOM 요소 접근 시 Generic 활용하여 타입 안정성 높일 수 있음

- `useState<T>` Generic 개념

// 예제
const [count, setCount] = useState<number>(0); // count는 number 타입
const [text, setText] = useState<string>('');  // text는 string 타입

// 예제2: SetText.tsx
const [text, setText] = useState('');

 

  • useState는 기본적으로 초기값의 타입을 추론하므로 일반적으로 Generic 작성할 필요 없음
  • 예제2에서 Generic 생략했지만 타입스크립트가 `''`(빈 문자열)을 보고 자동으로 text를 String 타입으로 추론함
// generic을 명시적으로 쓰는 경우
const [data, setData] = useState<null | Data>(null);

 

  • null을 포함한 유니언 타입 정의할 때는 generic을 활용하는 것이 유용함
  • 위 코드에서 generic은 Null일 수도 있고, Data 타입을 가질 수도 있는 상태

 

- `useRef<T>` Generic 개념

useRef는 2가지 목적으로 사용됨 :

  • 값을 저장하는 변수처럼 사용( 예제 SetText.tsx의 `refVal`변수)
  • DOM 요소에 접근 (예제 SetText.tsx의 `refInput` 변수)

 

- 예제

 

[src/components/lecture/ container.tsx]

import React from 'react';

interface Props {
  children: React.ReactNode; // <div></div>
}

export default function Container({ children }: Props) {
  return <div style={{ border: '1px dotted green' }}>{children}</div>;
}

 

 

[src/components/lecture/ setText.tsx]

import { useRef, useState } from 'react';

export default function SetText() {
  // [1] useState<T> Generic
  //   const [text, setText]= useState<string>('');
  const [text, setText] = useState('');

  // [2] useRef<T> Generic
  // - useRef는 값이 변경되어도 컴포넌트를 리렌더링하지 않는 훅
  //- ref 객체를 이용해서 DOM 접근 시 null 초기값 전달 필수
  const refVal = useRef<number>(0); //변수로 사용할 ref
  const refInput = useRef<HTMLInputElement>(null); // DOM 요소 접근

  // [2-1] refVal 변수를 변경하는 함수
  // - refVal.current 값이 변경되지만, 리렌더링 발생 x
  const changeRef = () => {
    refVal.current += 1;
    console.log('refVal:', refVal.current);
  };

  // [2-2] refInput DOM 요소 접근
  // - 사용자가 입력한 값을 가져와 state 값 변경
  const changeState = () => {
    if (refInput.current) {
      console.log('text state 변경 완료!');
      setText(refInput.current.value);
    }
  };

  // [3] 현재 상태와 input 값 확인 함수
  const checkString = () => {
    console.log('state string', text); //현재 state 값
    console.log('input value ref', refInput.current?.value); //현재 input 값
  };

  return (
    <div>
      <h2>useRef & useState 사용해보기</h2>
      <input type='text' ref={refInput} onChange={checkString} />
      <br></br>

      {/* input 값 -> state 저장 */}
      <button onClick={changeState}>state 변경!</button>

      {/* ref 값 증가 */}
      <button onClick={changeRef}>ref +1</button>

      {/* 현재 state 값 */}
      <p>state: {text}</p>

      {/* refVal 값이 변경되지만 리렌더링 발생 x */}
      <p>refVal: {refVal.current}</p>

      {/* 현재 input 값 */}
      <p>refInput의 value: {refInput.current?.value}</p>
    </div>
  );
}

 

 

Event 객체의 Type

- React에서 이벤트 객체란?

 

  • HTML 요소에서 이벤트가 발생하면, 해당 이벤트에 대한 정보가 이벤트 객체(event object)에 담김
  • 이벤트 객체를 통해 클릭된 요소(e.target), 입력값(e.target.value), 눌린 키(e.key) 등의 정보를 얻을 수 있음

 

이벤트별 주요 속성

이벤트 주요 속성
click e.target, e.currentTarget
keydown e.key, e.code
change e.target.value (input 요소의 값)

 

 

- 이벤트 객체 타입 지정 방법

이벤트마다 타입의 이름이 다름

타입스크립트에서는 이벤트 객체의 타입을 명확히 지정해야 오류 없이 사용 가능

 

이벤트 타입
onClick React.MouseEvent<HTMLElement>
onChange React.ChangeEvent<HTMLInputElement>
onKeyDown React.KeyboardEvent<HTMLINputElement>

 

 

- 예제

[EventObj.txs]

import React from 'react';

export default function EventObj() {
  function handleClick(e: React.MouseEvent<HTMLElement>) {
    console.log(e);
    console.log(e.target);
    // console.log(e.target.value);//에러 changeEvent가 아니라서 value 없음
  }
  function handleChange(event: React.ChangeEvent<HTMLInputElement>) {
    console.log(event);
    console.log(event.target.value);
  }
  function handleKeyDown(a: React.KeyboardEvent<HTMLInputElement>) {
    console.log(a.key);
    console.log(a.code);
    // console.log(a.KeyCode); //legacy keyCode는 더 이상 사용되지 않음
  }

  return (
    <div style={{ backgroundColor: 'yellow' }}>
      {/* onClick 내에서 event 전달받을 때는
        이벤트 타입 작성하지 않아도 됨 */}
      <div onClick={(e) => console.log(e)}>onClick(익명함수)</div>
      <div onClick={handleClick}>onClick(함수 호출)</div>
      <div>
        <span>onChange</span>
        <input type='text' onChange={handleChange}></input>
      </div>
      <div>
        <span>onKeyDown</span>
        <input type='text' onKeyDown={handleKeyDown}></input>
      </div>
      <div onDrag={handleClick}>onDrag</div>
    </div>
  );
}

 

 

💡 클릭 이벤트
- onClick 이벤트 핸들러 정의할 때, 익명함수 안에서 이벤트 객체를 받을 경우 타입 지정 필요 없음
- 타입스크립트가 자동으로 타입 추론 가능

 

 

1. 이벤트 핸들러를 직접 함수로 정의한 경우 (타입 필요)

function handleClick(e: React.MouseEvent<HTMLElement>) {
  console.log(e); // 이벤트 객체 출력
  console.log(e.target); // 클릭된 요소 출력
}

// return
<div onClick={handleClick}>onClick(함수 호출)</div>

 

  • 별도의 이벤트 핸들러 함수로 분리하는 경우 타입스크립트가 `e`의 타입 자동 추론 불가하므로, 타입 직접 명시
  • 함수의 매개변수(e)에 타입 명시적으로 지정해야함

 

2. 이벤트 핸들러를 익명 함수로 전달한 경우 (타입 생략 가능)

<div onClick={(e) => console.log(e)}>onClick(익명함수)</div>

 

  • 익명 함수 안에서 이벤트 객체를 직접 받는 경우 타입스크립트가 e 의 타입 자동 추론

 


✍🏻 회고

 


🌐  참고

서울시 청년취업사관학교(SeSAC) x 코딩온 웹 개발자 풀스택 과정

29.react-typescript