본문 바로가기

Front-End/React

React #15 (컴포넌트 실습 - 다시 돌아보는 기초편 + α )

반응형

 

1. 환영 메시지

더보기

src/data/messages.ts

export const welcomeMessages = [
  "환영합니다!",
  "안녕하세요!",
  "오늘도 좋은 하루 되세요!",
];

 

src/components/GreetingList.tsx

import { welcomeMessages } from "../data/messages";

export default function GreetingList() {
  return (
    <ul className="space-y-2">
      {welcomeMessages.map((msg, idx) => {
        return (
          <li key={idx} className="p-3 bg-blue-100 rounded shadow">
            {msg}
          </li>
        );
      })}
    </ul>
  );
}

 

src/pages/GreetingPage.tsx

import GreetingList from "../components/GreetingList";

export default function GreetingPage() {
  return (
    <main className="min-h-screen flex flex-col items-center justify-center p-6">
      <h1 className="text-3xl font-bold mb-4">👋 인사 메시지</h1>
      <GreetingList />
    </main>
  );
}

 

src/App.tsx

import GreetingPage from "./pages/GreetingPage";

export default function App() {
    return (
        <main>
            <GreetingPage />
        </main>
    );
}

 

 

 

2. 과일 상품 리스트

더보기

src/data/products.ts

export const products = [
  { id: 1, name: "딸기", price: 3000 },
  { id: 2, name: "사과", price: 2500 },
  { id: 3, name: "바나나", price: 2000 },
];

 

src/components/ProductItem.tsx

type Product = { name: string; price: number };

export default function ProductItem({ name, price }: Product) {
  return (
    <div className="border p-4 rounded shadow bg-white">
      <h2 className="font-semibold">{name}</h2>
      <p className="text-gray-600">{price.toLocaleString()}원</p>
    </div>
  );
}

 

src/pages/ProductListPage.tsx

import { products } from "../data/products";
import ProductItem from "../components/ProductItem";

export default function ProductListPage() {
  return (
    <main className="p-6 grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3">
      {products.map((item) => (
        <ProductItem key={item.id} name={item.name} price={item.price} />
      ))}
    </main>
  );
}

 

src/App.tsx

import GreetingPage from "./pages/GreetingPage";
import ProductListPage from "./pages/ProductListPage";


export default function App() {
    return (
        <main>
            {/* <GreetingPage /> */}
            <ProductListPage />

        </main>
    );
}

 

 

 

3. 자동차 폐차 신청 폼

더보기

src/data/initialFormData.ts

export const cars = [
  { id: 1, name: "홍길동", carNum: "12가4567" },
  { id: 2, name: "이순신", carNum: "34나6677" },
  { id: 3, name: "세종대왕", carNum: "54다9999" },
];

 

src/components/ScrapForm.tsx

type ScrapInfo = { name: string; carNum: string };

export default function ScrapForm({ name, carNum }: ScrapInfo) {
  return (
    <div className="p-4 bg-white border rounded shadow">
      <label htmlFor="name">이름</label>
      <input id="name" type="text" value={name} />
      <label htmlFor="carNum">차량번호</label>
      <input id="carNum" type="text" value={carNum} />
    </div>
  );
}

 

src/pages/ApplyScrapPage.tsx

import ScrapForm from "../components/ScrapForm";
import { cars } from "../data/initialFormData";

export default function ApplyScrapPage() {
  return (
    <main className="flex flex-col items-center justify-center w-full">
      {cars.map((car) => (
        <ScrapForm name={car.name} carNum={car.carNum} />
      ))}
    </main>
  );
}

 

src/App.tsx

import GreetingPage from "./pages/GreetingPage";
import ApplyScrapPage from "./pages/ApplyScrapPage";

export default function App() {
    return (
        <main>
            {/* <GreetingPage /> */}
            <ApplyScrapPage />
        </main>
    );
}

 

 

 

 

4. 차량 정보 인용

더보기

src/data/quotationMock.ts

export const quotationMock = [
  // 차량명, 연식, 예상금액
  { carName: "아우디", year: 2020, price: 100000000 },
  { carName: "벤츠", year: 2021, price: 110000000 },
  { carName: "BMW", year: 2022, price: 120000000 },
];

 

src/components/QuotationCard.tsx

// itcr (import type component return)

type Quotation = {
  carName: string;
  year: number;
  price: number;
};

export default function QuotationCard({ carName, year, price }: Quotation) {
  return (
    <div className="w-1/4 h-1/4 bg-gray-200 rounded-md shadow-lg p-4 m-8">
      <h2>{carName}</h2>
      <p>{year}</p>
      <p>{price}</p>
    </div>
  );
}

 

src/pages/QuotationPage.tsx

// itcr (import, type, component, return)

import QuotationCard from "../components/QuotationCard";
import { quotationMock } from "../data/quotationMock";

export default function QuotationPage() {
  return (
    <main>
      {quotationMock.map((item) => (
        <QuotationCard
          carName={item.carName}
          year={item.year}
          price={item.price}
        />
      ))}
    </main>
  );
}

 

src/App.tsx

import GreetingPage from "./pages/GreetingPage";
import ApplyScrapPage from "./pages/ApplyScrapPage";
import QuotationPage from "./pages/QuotationPage";


export default function App() {
    return (
        <main>
            {/* <GreetingPage /> */}
            {/* <ApplyScrapPage /> */}
            <QuotationPage />
        </main>
    );
}

 

 

 

5. 스크랩이 끝난 결과

더보기

src/data/completedData.ts

export const completedData = [
  { user: "아무개", car: "현대", date: "2025-07-16" },
  { user: "아무개2", car: "현대2", date: "2025-07-17" },
  { user: "아무개3", car: "현대3", date: "2025-07-18" },
];

 

src/components/SummaryTable.tsx

type Complete = {
  user: string;
  car: string;
  date: string;
};

export default function SummaryTable({ user, car, date }: Complete) {
  return (
    <div className="m-8">
      <h2>{user}</h2>
      <p>{car}</p>
      <p>{date}</p>
    </div>
  );
}

 

src/pages/ScrapCompletePage.tsx

import SummaryTable from "../components/SummaryTable";
import { completedData } from "../data/completedData";

export default function ScrapCompletePage() {
  return (
    <div>
      {completedData.map((item) => (
        <SummaryTable user={item.user} car={item.car} date={item.date} />
      ))}
    </div>
  );
}

 

src/App.tsx

import GreetingPage from "./pages/GreetingPage";
import ApplyScrapPage from "./pages/ApplyScrapPage";
import QuotationPage from "./pages/QuotationPage";
import ScrapCompletePage from "./pages/ScrapCompletePage";


export default function App() {
    return (
        <main>
            {/* <GreetingPage /> */}
            {/* <ApplyScrapPage /> */}
            {/* <QuotationPage /> */}
            <ScrapCompletePage />
        </main>
    );
}

 

 

 

 

 

+ α (useState 추가해서 페이지 기능 추가)

더보기

src/App.tsx

import GreetingPage from "./pages/GreetingPage";
import ApplyScrapPage from "./pages/ApplyScrapPage";
import QuotationPage from "./pages/QuotationPage";
import ScrapCompletePage from "./pages/ScrapCompletePage";
import { useState } from "react";



function App() {
  const [stage, setStage] = useState(1);
  const [count, setCount] = useState(0);

  const prevStage = () => {
    setStage(stage - 1);
  };

  const nextStage = () => {
    setStage(stage + 1);
  };

  const countUp = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <button className="p-4 m-4 bg-blue-500" onClick={countUp}>
        카운트 증가{count}
      </button>
      <button className="p-4 m-4 bg-blue-500" onClick={prevStage}>
        이전
      </button>
      <span>{stage}</span>
      <button className="p-4 m-4 bg-blue-500" onClick={nextStage}>
        다음
      </button>
      {stage === 1 && <GreetingPage />}
      {stage === 2 && <ApplyScrapPage />}
      {stage === 3 && <QuotationPage />}
      {stage === 4 && <ScrapCompletePage />}
    </div>
  );
}

export default App;

 

 

※ 추가로 넣은 페이지 기능은 1~4까지만 출력되며, 범위를 넘어가면 출력되지 않음.

 

 

 

 

 

 

반응형