개발천재

[ReactJS] JSX와 TSX의 차이 본문

개발 준비/ReactJS

[ReactJS] JSX와 TSX의 차이

세리블리 2025. 5. 12. 13:52
728x90
반응형

JSX와 TSX는 모두 React에서 UI를 만들 때 사용하는 문법이지만, TSX는 타입을 명확히 관리할 수 있는 TypeScript 기반이라는 차이가 있다. 초반에는 JSX로 시작할 수 있지만, 프로젝트가 커지거나 팀과 함께 작업한다면 TSX로 전환하는 것이 더 안정적이다.

구분 .jsx .tsx
언어 JavaScript + JSX TypeScript + JSX
타입 사용 타입 사용 불가 타입 명시 가능
컴파일러 Babel 등 JS 컴파일러 TypeScript 컴파일러
React 문법 JSX 문법 사용 JSX 문법 + 타입 정의 사용

 

 

JSX(JavaScript XML)

JSX는 JavaScript XML의 줄임말로, 자바스크립트 안에 HTML 같은 코드를 쓸 수 있게 해주는 문법이다. JSX는 브라우저가 직접 이해할 수 없기 때문에 Babel 같은 도구가 JavaScript로 변환해준다.

 

App.jsx

jsx는 타입 검사 없이 실행된다. 버그가 런타임에서야 발견될 수도 있다.

function Hello({ name }) {
  return <div>Hello, {name}!</div>;
}

 

 


 

 

TSX(TypeScript XML)

TSX는 TypeScript + JSX를 의미한다. JSX 문법을 쓰면서도 TypeScript의 타입 시스템을 함께 사용할 수 있게 해주는 확장자이다.

TypeScript를 프로젝트에서 사용하는 경우엔 반드시 .tsx를 써야 하고, JSX 문법이 없는 TypeScript 파일은 .ts를 쓰면 된다.

 

App.tsx

tsx에서는 name이 string 타입이어야 한다는 걸 컴파일러가 체크해준다. 컴파일 단게에서 잘못된 타입을 미리 잡아주는게 큰 장점이다.

type HelloProps = {
  name: string;
};

function Hello({ name }: HelloProps) {
  return <div>Hello, {name}!</div>;
}

 

 

TSX를 써야하는 경우

  • 프로젝트에서 TypeScript를 사용하는 경우
  • 팀 작업 중 실수 방지가 중요한 경우
  • 결제 시스템, 회원 정보 등 정확한 데이터 구조가 필요한 경우

특히, 실무 프로젝트나 유지보수가 필요한 서비스, 금융, 결제 같은 프로젝트는에서는 무조건 TSX를 사용하는 것을 권장한다.

 

TSX에서 자주 사용하는 문법

type 또는 interface – Props와 State 타입 정의
React 컴포넌트에 데이터를 전달할 때 props의 타입을 지정해준다.
type이나 interface 모두 사용 가능하지만, interface는 확장이 가능해 재사용에 유리하다. 복잡한 구조나 extends가 필요하면 interface를, 단순한 구조엔 type을 많이 사용한다.

type UserProps = {
  name: string;
  age: number;
};

function User({ name, age }: UserProps) {
  return <p>{name}은 {age}살입니다.</p>;
}




useState<Type>() – 상태에 타입 지정
React의 useState 훅은 기본적으로 타입 추론을 해주지만, 명시적으로 타입을 지정하면 더 안정적이다.

제네릭을 사용하여 useState()처럼 타입을 넣어준다. 

const [count, setCount] = useState<number>(0);
const [username, setUsername] = useState<string>('');
const [items, setItems] = useState<string[]>([]);

 

특히 초기값이 null일 수 있을 때는 string | null 같은 유니언 타입도 자주 사용된다.

const [selectedItem, setSelectedItem] = useState<string | null>(null);

 

 

useRef<Type>() – DOM 또는 값을 참조할 때
useRef는 HTML 요소나 특정 값을 참조할 때 사용하는 훅이다. 이때도 타입을 명확하게 지정해줄 수 있어야 한다.

null 초기값이 들어가므로 | null을 포함하는 것이 일반적이다.

const inputRef = useRef<HTMLInputElement>(null);

// 사용 시:
<input ref={inputRef} />

 

DOM뿐만 아니라 일반 값의 저장소로도 사용한다.

const timerId = useRef<number | null>(null);


React.FC<Props> – 컴포넌트에 기본 타입 적용
React의 함수형 컴포넌트에 타입을 적용할 때 React.FC (또는 React.FunctionComponent)를 사용하면 편리하다.
단, React.FC에는 children이 자동으로 포함되므로, children이 없는 경우는 명시적으로 안 쓰기도 한다.

interface GreetingProps {
  name: string;
}

const Greeting: React.FC<GreetingProps> = ({ name }) => {
  return <h1>Hello, {name}!</h1>;
};



제네릭(Generic) – 재사용 가능한 컴포넌트 작성
제네릭은 여러 타입에 대해 유연하게 대응할 수 있게 해주는 기능이다. 리스트, 폼, 셀렉트박스 등 재사용 가능한 컴포넌트를 만들 때 자주 사용한다.

T는 임의의 타입을 의미하고, 사용하는 쪽에서 실제 타입이 결정된다. 복잡한 UI 구성에서 컴포넌트 재사용성을 극대화할 수 있다.

type ListProps<T> = {
  items: T[];
  renderItem: (item: T) => React.ReactNode;
};

function List<T>({ items, renderItem }: ListProps<T>) {
  return <ul>{items.map(renderItem)}</ul>;
}

// 사용 예
<List items={['apple', 'banana']} renderItem={(item) => <li>{item}</li>} />

 

 

React 프로젝트에서 TypeScript 시작하기

아래의 명령어를 터미널에 입력한다.

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

 

기존 React 프로젝트에 TypeScript를 적용하려면 다음 명령어를 실행한다.

npm install --save typescript @types/react @types/react-dom

 

 


 

TSX 예시

기본 함수형 컴포넌트(Props 타입 지정)

type GreetingProps = {
  name: string;
  age?: number; // 선택적 props
};

const Greeting: React.FC<GreetingProps> = ({ name, age }) => {
  return (
    <div>
      <p>Hello, {name}!</p>
      {age && <p>You are {age} years old.</p>}
    </div>
  );
};

 

 

useState에 타입 지정

import { useState } from "react";

const Counter = () => {
  const [count, setCount] = useState<number>(0);

  return (
    <button onClick={() => setCount(count + 1)}>
      Count: {count}
    </button>
  );
};

 

 

이벤트에 타입 지정

const Form = () => {
  const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    console.log("Form submitted");
  };

  return (
    <form onSubmit={handleSubmit}>
      <button type="submit">Submit</button>
    </form>
  );
};

 

 

컴포넌트에 Children 사용하기

type CardProps = {
  title: string;
  children: React.ReactNode;
};

const Card = ({ title, children }: CardProps) => (
  <div className="card">
    <h2>{title}</h2>
    <div>{children}</div>
  </div>
);

 

 

JSX 안에서 조건부 랜더링 +  map

type Todo = {
  id: number;
  text: string;
  completed: boolean;
};

type TodoListProps = {
  todos: Todo[];
};

const TodoList = ({ todos }: TodoListProps) => (
  <ul>
    {todos.map((todo) => (
      <li key={todo.id}>
        {todo.text} {todo.completed ? "✅" : "❌"}
      </li>
    ))}
  </ul>
);

 

 

컴포넌트에 defaultProps 적용

type ButtonProps = {
  label: string;
  color?: string;
};

const Button = ({ label, color = "blue" }: ButtonProps) => (
  <button style={{ backgroundColor: color }}>{label}</button>
);
반응형