본문으로 건너뛰기
프론트엔드 기초집LESSON 21

상태 끌어올리기와 컴포넌트 설계

난이도초급
예상 시간35분
선수지식이전 강의

21강. 상태 끌어올리기와 컴포넌트 설계

1. 이번 강의에서 해결할 문제

FilterBar의 검색어를 GameList가 알아야 하고 관심 게임 수는 Header에도 보여야 합니다. 서로 떨어진 컴포넌트가 같은 데이터를 따로 저장하면 값이 어긋납니다.

2. 학습 목표

  • state의 단일 진실 공급원을 정한다.
  • 공유 state를 가장 가까운 공통 부모로 끌어올린다.
  • 표현 컴포넌트와 상태 관리 컴포넌트의 책임을 나눈다.

3. 핵심 개념

둘 이상의 컴포넌트가 같은 값을 읽거나 바꾸면 state를 가장 가까운 공통 부모에 둡니다. 부모는 값을 props로 내리고 변경 의도를 콜백으로 받습니다. 이를 상태 끌어올리기라고 합니다.

모든 state를 App까지 올릴 필요는 없습니다. 한 카드에만 필요한 펼침 여부처럼 지역적인 값은 카드에 둡니다. 공유 범위만큼만 올리는 것이 핵심입니다.

4. 단계별 코드 예시

파일 경로: game-release-dashboard/src/pages/HomePage.tsx

import { useState } from 'react';
import { FilterBar } from '../components/FilterBar';
import { GameList } from '../components/GameList';
import type { Game, Platform } from '../types/game';

type HomePageProps = { games: Game[] };

export function HomePage({ games }: HomePageProps) {
const [query, setQuery] = useState('');
const [platform, setPlatform] = useState<Platform | 'all'>('all');
const [favoriteIds, setFavoriteIds] = useState<string[]>([]);

const visibleGames = games.filter((game) => {
const matchesTitle = game.title.toLowerCase().includes(query.toLowerCase());
const matchesPlatform = platform === 'all' || game.platform === platform;
return matchesTitle && matchesPlatform;
});

function toggleFavorite(id: string) {
setFavoriteIds((current) =>
current.includes(id)
? current.filter((item) => item !== id)
: [...current, id],
);
}

return (
<main>
<p>관심 게임 {favoriteIds.length}</p>
<FilterBar
query={query}
platform={platform}
resultCount={visibleGames.length}
onQueryChange={setQuery}
onPlatformChange={setPlatform}
/>
<GameList
games={visibleGames}
favoriteIds={favoriteIds}
onFavorite={toggleFavorite}
/>
</main>
);
}

5. 코드가 동작하는 이유

HomePage가 검색 조건과 관심 id의 유일한 원본입니다. FilterBar는 값을 바꾸겠다는 의도를 콜백으로 알리고 GameList는 같은 최신 값을 받습니다. 배열 갱신은 기존 값을 변경하지 않고 새 배열을 만듭니다.

6. 자주 하는 실수와 해결법

  • 같은 query를 두 컴포넌트에 각각 저장: 공통 부모에 한 번만 둡니다.
  • 모든 state를 전역으로 이동: 실제 공유하는 가장 가까운 부모를 먼저 선택합니다.
  • 자식에 setter를 무의미하게 그대로 전달: onFavorite, onQueryChange처럼 도메인 의도를 이름으로 드러냅니다.
  • 배열 push로 직접 수정: filter와 펼침 문법으로 새 배열을 만듭니다.

7. 직접 실습

Header 컴포넌트를 만들고 favoriteCount만 prop으로 받게 하세요. GameCard의 “설명 펼치기” state는 카드 내부에 남겨 두고 두 state의 위치가 다른 이유를 메모하세요.

8. 이해 점검 질문 3개

  1. 공유 state는 어느 컴포넌트에 두어야 하나요?
  2. 모든 state를 App에 두면 어떤 문제가 생기나요?
  3. setter 대신 의도가 드러나는 콜백 prop을 쓰면 무엇이 좋아지나요?

9. 핵심 요약

공유 데이터는 가장 가까운 공통 부모가 소유하고 props와 콜백으로 흐르게 합니다. 지역 state는 지역에 남겨 데이터 소유권을 단순하게 유지합니다.

MINI QUIZ

상태 끌어올리기와 컴포넌트 설계 미니 퀴즈

선택 즉시 정답과 해설을 확인할 수 있습니다. 결과는 이 브라우저에만 저장됩니다.

0 / 2
  1. 문제 1“상태 끌어올리기와 컴포넌트 설계” 구현을 설계할 때 책임과 구조를 올바르게 나눈 선택은 무엇인가요?
  2. 문제 2“상태 끌어올리기와 컴포넌트 설계”에서 ‘배열 push로 직접 수정’ 문제가 생겼습니다. 가장 알맞은 진단 또는 대응은 무엇인가요?
LESSON STATUS

학습을 마쳤나요?

직접 실습과 점검 질문까지 확인한 뒤 완료로 표시하세요.

21강. 상태 끌어올리기와 컴포넌트 설계 미완료 상태