React로 애플리케이션을 만드는 것은 단순히 코드를 작성하는 것을 넘어, UI를 어떻게 컴포넌트 단위로 분해하고, 데이터가 어떻게 흐를지를 설계하는 과정입니다.
"Todo List" 애플리케이션을 만든다고 가정해봅시다.
UI를 컴포넌트 계층 구조로 나누기:
App (전체를 감싸는 루트 컴포넌트)TodoForm (새로운 할 일을 입력하고 추가하는 폼)TodoList (할 일 목록을 보여주는 리스트)TodoItem (개별 할 일 항목)State가 어디에 있어야 할지 결정하기:
todos 배열)은 TodoForm에서 새로 추가되고, TodoList에서 보여져야 합니다.App 컴포넌트가 todos 상태를 관리하는 것이 가장 적합합니다.App 컴포넌트는 todos 상태 배열을 TodoList 컴포넌트에 items라는 prop으로 전달합니다.TodoList는 전달받은 items 배열을 map()으로 순회하며, 각 todo 객체를 TodoItem 컴포넌트에 prop으로 전달합니다.App 컴포넌트는 todos 상태를 변경하는 함수(addTodoHandler)를 정의합니다.TodoForm 컴포넌트에 onAddTodo라는 prop으로 전달합니다.TodoForm은 폼이 제출될 때, props.onAddTodo()를 호출하여 입력된 데이터를 App 컴포넌트로 올려보냅니다.App 컴포넌트는 특정 id의 항목을 삭제하는 함수(deleteTodoHandler)를 정의합니다.TodoList를 거쳐 TodoItem까지 prop으로 전달됩니다.TodoItem의 삭제 버튼이 클릭되면, props.onDelete()를 자신의 id와 함께 호출하여 App 컴포넌트의 상태를 변경합니다.map & key)TodoList 컴포넌트는 props로 받은 배열을 map() 메서드를 사용하여 TodoItem 컴포넌트의 배열로 변환합니다.TodoItem에는 React가 각 항목을 효율적으로 식별할 수 있도록 고유하고 안정적인 key prop (e.g., key={todo.id})을 반드시 지정해야 합니다.TodoList 컴포넌트에서, 만약 props.items 배열의 길이가 0이라면 "할 일이 없습니다."와 같은 메시지를 보여주고, 0보다 크면 목록을 렌더링하도록 조건부 렌더링을 적용합니다.{props.items.length === 0 ? (
<p>할 일이 없습니다.</p>
) : (
<ul>...</ul>
)}useEffect)todos 목록이 변경될 때마다 브라우저의 localStorage에 자동으로 저장하고 싶다면, 이는 Side Effect에 해당합니다.useEffect를 사용하여 todos 상태를 의존성 배열에 넣으면, todos가 변경될 때마다 localStorage.setItem()을 실행하는 로직을 구현할 수 있습니다.useEffect(() => {
localStorage.setItem('todos', JSON.stringify(todos));
}, [todos]); // todos 배열이 변경될 때마다 이 효과가 실행됨useState)가 변경되면 컴포넌트가 자동으로 리렌더링됩니다.map()과 key를 사용하여 배열 데이터를 리스트로 렌더링합니다.localStorage 접근 등 렌더링과 직접 관련 없는 작업은 useEffect를 사용하여 처리합니다.