하위 태스크 1

React 프로젝트 실행

npm install , npm run dev 로 앱 실행

프로젝트의 의존성을 설치한다.

npm install

프로젝트를 개발 모드로 실행한다.

npm run dev

http://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.jsxProfileCard 컴포넌트를 작성한다.

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;

웹브라우저에서 확인한 결과는 다음과 같다.