안녕하세요! 이번 포스트에서는 우리의 할 일 관리 앱의 디자인을 개선하고, 컴포넌트 구조를 리팩터링 하여 코드의 가독성
과 유지보수성을 높이는 방법을 알아보겠습니다.
1. CSS 스타일링 개선
먼저, 앱의 외관을 개선하기 위해 CSS를 수정해 보겠습니다. src
폴더에 TodoList.css
파일을 생성하고 다음 내용을 추가합니다:
.todo-app {
max-width: 600px;
margin: 0 auto;
padding: 20px;
font-family: Arial, sans-serif;
}
.todo-app h2 {
text-align: center;
color: #333;
}
.todo-form {
display: flex;
margin-bottom: 20px;
}
.todo-form input[type="text"] {
flex-grow: 1;
padding: 10px;
font-size: 16px;
border: 1px solid #ddd;
border-radius: 4px 0 0 4px;
}
.todo-form button {
padding: 10px 20px;
font-size: 16px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 0 4px 4px 0;
cursor: pointer;
}
.todo-filters {
display: flex;
justify-content: center;
margin-bottom: 20px;
}
.todo-filters button {
margin: 0 5px;
padding: 5px 10px;
font-size: 14px;
background-color: #f1f1f1;
border: none;
border-radius: 4px;
cursor: pointer;
}
.todo-filters button.active {
background-color: #4CAF50;
color: white;
}
.todo-list {
list-style-type: none;
padding: 0;
}
.todo-item {
display: flex;
align-items: center;
padding: 10px;
border-bottom: 1px solid #eee;
}
.todo-item input[type="checkbox"] {
margin-right: 10px;
}
.todo-item span {
flex-grow: 1;
}
.todo-item button {
margin-left: 10px;
padding: 5px 10px;
font-size: 14px;
background-color: #f44336;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.todo-stats {
margin-top: 20px;
text-align: center;
color: #666;
}
그런 다음 TodoList.js
파일에서 이 CSS 파일을 import 합니다:
import React, { useState, useEffect } from 'react';
import './TodoList.css';
// ... 나머지 코드
2. 컴포넌트 분리
이제 TodoList
컴포넌트를 더 작은 컴포넌트로 분리하여 코드의 가독성과 재사용성을 높여보겠습니다.
TodoForm 컴포넌트
src/components
폴더에 TodoForm.js
파일을 생성하고 다음 내용을 추가합니다:
import React, { useState } from 'react';
function TodoForm({ onSubmit }) {
const [inputValue, setInputValue] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
if (inputValue.trim() !== '') {
onSubmit(inputValue);
setInputValue('');
}
};
return (
<form className="todo-form" onSubmit={handleSubmit}>
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder="새로운 할 일을 입력하세요"
/>
<button type="submit">추가</button>
</form>
);
}
export default TodoForm;
TodoItem 컴포넌트
src/components
폴더에 TodoItem.js
파일을 생성하고 다음 내용을 추가합니다:
import React, { useState } from 'react';
function TodoItem({ todo, onToggle, onDelete, onEdit }) {
const [isEditing, setIsEditing] = useState(false);
const [editValue, setEditValue] = useState(todo.text);
const handleEdit = () => {
onEdit(todo.id, editValue);
setIsEditing(false);
};
return (
<li className="todo-item">
<input
type="checkbox"
checked={todo.completed}
onChange={() => onToggle(todo.id)}
/>
{isEditing ? (
<input
type="text"
value={editValue}
onChange={(e) => setEditValue(e.target.value)}
onBlur={handleEdit}
autoFocus
/>
) : (
<span
style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}
onDoubleClick={() => setIsEditing(true)}
>
{todo.text}
</span>
)}
<button onClick={() => onDelete(todo.id)}>삭제</button>
</li>
);
}
export default TodoItem;
TodoFilters 컴포넌트
src/components
폴더에 TodoFilters.js
파일을 생성하고 다음 내용을 추가합니다:
import React from 'react';
function TodoFilters({ filter, setFilter }) {
return (
<div className="todo-filters">
<button
className={filter === 'all' ? 'active' : ''}
onClick={() => setFilter('all')}
>
전체
</button>
<button
className={filter === 'active' ? 'active' : ''}
onClick={() => setFilter('active')}
>
미완료
</button>
<button
className={filter === 'completed' ? 'active' : ''}
onClick={() => setFilter('completed')}
>
완료
</button>
</div>
);
}
export default TodoFilters;
TodoStats 컴포넌트
src/components
폴더에 TodoStats.js
파일을 생성하고 다음 내용을 추가합니다:
import React from 'react';
function TodoStats({ todos }) {
const totalTodos = todos.length;
const completedTodos = todos.filter((todo) => todo.completed).length;
const activeTodos = totalTodos - completedTodos;
return (
<div className="todo-stats">
<p>전체 할 일: {totalTodos}</p>
<p>완료한 할 일: {completedTodos}</p>
<p>남은 할 일: {activeTodos}</p>
</div>
);
}
export default TodoStats;
3. 리팩토링된 TodoList 컴포넌트
이제 TodoList
컴포넌트를 수정하여 새로 만든 컴포넌트들을 사용하도록 합니다:
import React, { useState, useEffect } from 'react';
import TodoForm from './TodoForm';
import TodoItem from './TodoItem';
import TodoFilters from './TodoFilters';
import TodoStats from './TodoStats';
import './TodoList.css';
function TodoList() {
const [todos, setTodos] = useState(() => {
const savedTodos = localStorage.getItem('todos');
return savedTodos ? JSON.parse(savedTodos) : [];
});
const [filter, setFilter] = useState('all');
useEffect(() => {
localStorage.setItem('todos', JSON.stringify(todos));
}, [todos]);
const addTodo = (text) => {
setTodos([...todos, { id: Date.now(), text, completed: false }]);
};
const deleteTodo = (id) => {
setTodos(todos.filter((todo) => todo.id !== id));
};
const toggleTodo = (id) => {
setTodos(
todos.map((todo) =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
)
);
};
const editTodo = (id, newText) => {
setTodos(
todos.map((todo) =>
todo.id === id ? { ...todo, text: newText } : todo
)
);
};
const filteredTodos = todos.filter((todo) => {
if (filter === 'active') return !todo.completed;
if (filter === 'completed') return todo.completed;
return true;
});
return (
<div className="todo-app">
<h2>할 일 관리 앱</h2>
<TodoForm onSubmit={addTodo} />
<TodoFilters filter={filter} setFilter={setFilter} />
<ul className="todo-list">
{filteredTodos.map((todo) => (
<TodoItem
key={todo.id}
todo={todo}
onToggle={toggleTodo}
onDelete={deleteTodo}
onEdit={editTodo}
/>
))}
</ul>
<TodoStats todos={todos} />
</div>
);
}
export default TodoList;
실행 결과
이제 npm start
명령어로 앱을 실행하면, 개선된 디자인과 함께 더 모듈화 된 구조의 앱을 볼 수 있습니다.
마무리
이번 챕터에서는 앱의 디자인을 개선하고 컴포넌트를 더 작은 단위로 분리하여 코드의 가독성과 유지보수성을 높였습니다. 이러한 리팩토링 과정은 앱이 커질수록 더욱 중요해집니다.
컴포넌트를 분리함으로써 얻을 수 있는 장점은 무엇일까요? 각 컴포넌트의 역할이 명확해지고, 재사용성이 높아지며, 테스트하기 쉬워진다는 점 등이 있습니다. 여러분은 어떤 장점을 느끼셨나요?