하위 태스크 1
React 프로젝트 실행
npm install , npm run dev 로 앱 실행
프로젝트의 의존성을 설치한다.
npm install프로젝트를 개발 모드로 실행한다.
npm run devhttp://localhost:5173에 접속해 실행 결과를 확인한다.

하위 태스크 2
App 구조 분석
main.jsx , App.jsx , components 구조 이해
main.jsx: React 애플리케이션의 진입점이다.index.html의#root요소를 루트 요소로 지정하고<App />컴포넌트를 렌더링 한다.App.jsx:<App />컴포넌트의 구현이다.main.jsx에서 사용되며 내부적으로 하나의<h1>요소를 포함한다.components/: 개발자가 작성한 모듈화된 컴포넌트의 모음이다.
하위 태스크 3 ~ 5
ProfileCard 컴포넌트 생성
Props 기반 프로필 카드 구현
기본 Props 설정
defaultProps 또는 기본값으로 안전한 Props 처리
ProfileCard 여러 개 렌더링
서로 다른 데이터로 카드 반복 렌더링
components/ProfileCard.jsx에 ProfileCard 컴포넌트를 작성한다.
ProfileCard 컴포넌트:
export default function ProfileCard({
name = "무명",
role = "없음",
description = "없음",
profileImageUrl = "https://picsum.photos/100",
}) {
return (
<article style={{ margin: 10, border: "1px solid lightgray", }}>
<strong>{name}</strong>
<ul>
<li>이름: {name}</li>
<li>역할: {role}</li>
<li>설명: {description}</li>
<li><img src={profileImageUrl} width={100} /></li>
</ul>
</article>
);
}App 컴포넌트가 여러 개의 ProfileCard 컴포넌트를 포함하도록 수정한다.
App 컴포넌트:
import { useState } from "react";
import ProfileCard from "./components/ProfileCard";
import reactLogo from "./assets/react.svg";
import viteLogo from "/vite.svg";
import "./App.css";
const PROFILES = [
{
name: "캐터피",
role: "벌레",
description: "빨간 더듬이로부터 냄새를 내어 상대를 쫓아 버린다. 탈피를 반복하여 자라난다.",
profileImageUrl: "https://data1.pokemonkorea.co.kr/newdata/pokedex/full/001001.png",
isNew: false,
},
{
name: "피카츄",
role: "전기",
description: "뺨의 양쪽에 작은 전기 주머니가 있다. 위기 상황일 때 방전한다.",
profileImageUrl: "https://data1.pokemonkorea.co.kr/newdata/pokedex/full/002501.png",
isNew: true,
},
{
name: "삐삐",
role: "페어리",
description: "등의 날개에 달빛을 모으면 공중에 떠오를 수 있다는 듯하다.",
profileImageUrl: "https://data1.pokemonkorea.co.kr/newdata/pokedex/full/003501.png",
isNew: false,
},
];
function App() {
return (
<main>
<ProfileCard />
{PROFILES.map((profile) => <ProfileCard key={profile.name} {...profile} />)}
</main>
);
}
export default App;웹브라우저에서 확인한 결과는 다음과 같다.

하위 태스크 6 ~ 9
조건부 렌더링 구현
특정 조건에서만 뱃지/텍스트 표시
리스트 렌더링 구현
배열 데이터를 map 으로 렌더링
key Props 설정
리스트 렌더링 시 고유 key 지정
CardLayout + children 구현
children을 받는 래퍼 컴포넌트 생성
ProfileCard 컴포넌트에 isNew Prop을 추가한다. ProfileCard 컴포넌트가 반환하는 JSX에 뱃지를 조건부로 렌더링하는 코드를 작성한다.
ProfileCard 컴포넌트:
export default function ProfileCard({
name = "무명",
role = "없음",
description = "없음",
profileImageUrl = "https://picsum.photos/100",
isNew,
}) {
return (
<article style={{ margin: 10, border: "1px solid lightgray", }}>
<div style={{ display: "flex", gap: 4, }}>
<strong>{name}</strong>
{isNew && <span style={{ background: "black", color: "white", }}>NEW</span>}
</div>
<ul>
<li>이름: {name}</li>
<li>역할: {role}</li>
<li>설명: {description}</li>
<li><img src={profileImageUrl} width={100} /></li>
</ul>
</article>
);
}복수의 ProfileCard 컴포넌트를 감쌀 CardLayout 컴포넌트를 정의한다.
CardLayout 컴포넌트:
export default function CardLayout({ children }) {
return (
<div style={{ padding: 4, border: "2px solid orange", }}>
{children}
</div>
);
}App 컴포넌트에서 ProfileCard 컴포넌트 배열을 CardLayout 컴포넌트가 감싸도록 수정한다.
App 컴포넌트:
import CardLayout from "./components/CardLayout";
import ProfileCard from "./components/ProfileCard";
// ...
function App() {
return (
<main>
<CardLayout>
{PROFILES.map((profile) => <ProfileCard key={profile.name} {...profile} />)}
</CardLayout>
</main>
);
}
export default App;웹브라우저에서 확인한 결과는 다음과 같다.
