[Vite+React]타입스크립트 예제 / TypeScript example

👉🏻 타입스크립트는 자바스크립트 + 타입입니다.
TypeScript is JavaScript + types.

👉🏻 자바스크립트는 자동으로 타입을 추론 할 수 있습니다. 하지만 기능이 부족할 경우 오류가 발생할 수 있습니다.
JavaScript can automatically infer types, but if the functionality is lacking, errors can occur.

👉🏻 타입을 사용함으로 자바스크립트의 자동추론을 보완 할 수 있습니다.
Using types can complement JavaScript’s automatic inference.

👉🏻 타입스크립에서 타입을 사용하는 경우는 변수, 함수 파라미터/반환값, state, props, 객체/배열, 제네릭, 타입 별칭/인터페이스 등 거의 모든 값이 들어가는 곳에 타입을 정의할 수 있습니다.
When using types in TypeScript, you can define types for almost any value, including variables, function parameters/return values, state, props, objects/arrays, generics, type aliases/interfaces, etc.

👉🏻 아래의 코드는 타입의 사용빈도가 높은 부분에 대한 예제입니다.
The code below is an example of a frequently used part of the type.

👉🏻 프로젝트 생성 / Create project

% npm create vite@latest ReactExample4 --template react-ts
% cd ReactExample4
% npm install

# 서버 실행(실행 테스트) / Running the server (running test)
# Run the server (run test)
% npm run dev

👉🏻 프로젝트 구조 / Project structure

src/
├─ types.tsx
├─ components/
│ └─ UserCard.tsx
├─ App.tsx
├─ main.tsx

👉🏻1. 전역 타입 정의 (src/types.ts)
Global type definitions (src/types.ts)

// 여러 컴포넌트에서 공유할 타입 정의
export type Todo = {
  id: number;
  text: string;
  completed: boolean;
};

👉🏻2. Props 타입 지정 (src/components/UserCard.tsx)
Specifying Prop types (src/components/UserCard.tsx)

import React from "react";

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

const UserCard: React.FC<UserCardProps> = ({ name, age }) => {
  return (
    <div style={{ border: "1px solid gray", padding: "10px", margin: "5px" }}>
      <h2>{name}</h2>
      {age && <p>Age: {age}</p>}
    </div>
  );
};

export default UserCard;

⭐️ React.FC<UserCardProps>

✔️ React.FC를 사용하면 children porps가 자동 포함됩니다.(타입에 children을 정의하지 않아도 됩니다.)
React.FC automatically includes children types (you don’t need to define children in your types).

✔️ 필요한 경우 꺼내서 사용 할 수 있고 사용하지 않을 수도 있습니다.
You can take it out and use it if you need to, or you can leave it out if you don’t.

const UserCard: React.FC<UserCardProps> = ({ name, age, children }) => {
  return (
    <div style={{ border: "1px solid gray", padding: "10px", margin: "5px" }}>
      <h2>{name}</h2>
      {age && <p>Age: {age}</p>}
      <div>{children}</div> {/* 여기서 children 출력 */}
    </div>
  );
};

✔️ children을 자동으로 포함하지 않을 경우 아래처럼 사용 할 수도 있습니다.
If you don’t want to automatically include children, you can use it like this:

type UserCardProps = {
  name: string;
  age?: number;
};

function UserCard({ name, age }: UserCardProps) {
  return (
    <div>
      <h2>{name}</h2>
      {age && <p>Age: {age}</p>}
    </div>
  );
}

👉🏻3. State 타입 지정 + 함수 타입 지정 (src/App.tsx)
State type specification + function type specification (src/App.tsx)

import React, { useState } from "react";
import UserCard from "./components/UserCard";
import type { Todo } from "./types";

// 함수 타입 지정: string을 받아 string을 반환
// Function type specification: takes a string and returns a string
function greet(name: string): string {
  return `Hello, ${name}!`;
}

function App() {
  // State 타입 지정
// Specify State type
  const [todos, setTodos] = useState<Todo[]>([
    { id: 1, text: "Learn Vite", completed: false },
    { id: 2, text: "Practice TypeScript", completed: true },
  ]);
  // 만약 toggleTodo(3)을 호출하면, id가 3인 todo를 찾아서 completed 값을 반전시킵니다.
  // If you call toggleTodo(3), it will find the todo with id 3 and invert its completed value.
  const toggleTodo = (id: number) => {
    setTodos((prev) =>
      prev.map((todo) =>
        todo.id === id ? { ...todo, completed: !todo.completed } : todo
      )
    );
  };

  return (
    <div style={{ padding: "20px" }}>
      <h3>Vite + React + TypeScript Example</h3>
      <p>{greet("Johnny")}</p>

      <h2>User Cards</h2>
      <UserCard name="Alice" age={25} />
      <UserCard name="Bob" />

      <h2>Todo List</h2>
      <ul>
        {todos.map((todo) => (
          <li
            key={todo.id}
            style={{
              textDecoration: todo.completed ? "line-through" : "none",
              cursor: "pointer",
            }}
            onClick={() => toggleTodo(todo.id)}
          >
            {todo.text}
          </li>
        ))}
      </ul>
    </div>
  );
}

export default App;

⭐️ 기존 상태반전 / Inversion of existing state

✔️prev.map(…)

prev는 이전 todos 배열입니다.
prev is the previous todos array.

map을 사용해서 배열을 순회하면서, id가 일치하는 todocompleted 값을 반전시킵니다.
Using map, we iterate through the array and invert the completed value only for todos with matching id.

{ ...todo, completed: !todo.completed } 이 구문은 기존 todo 객체를 복사한 뒤, completed 속성만 반전시켜서 새로운 객체를 반환하는 방식입니다.
{ …todo, completed: !todo.completed } This syntax copies the existing todo object and returns a new object with only the completed property reversed.

  const toggleTodo = (id: number) => {
    setTodos((prev) =>
      prev.map((todo) =>
        todo.id === id ? { ...todo, completed: !todo.completed } : todo
      )
    );
  };

👉🏻4. 진입 파일 (src/main.tsx)
Entry file (src/main.tsx)

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import "./index.css";

ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

👉🏻5.스크린 샷 / ScreenShot

Leave a Reply