더보기
폴더 구조는 다음과 같이 설정했다.
pages 폴더 안에 TodoList 폴더를 만들고 components 폴더 안에 CompletedList.tsx, TodoInput.tsx, TodoItem.tsx, TodoList.tsx를 나눠 컴포넌트 분리를 해서 구조를 이뤘다. 그리고 이 컴포넌트를 감싸는 Todo.tsx를 구성했고, Styles.ts 같은 경우는 한꺼번에 관리하도록 스타일 파일을 하나만 뒀다.
📌App.tsx
import React from "react";
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import Todo from "./pages/TodoList/Todo";
const App: React.FC = () => {
return (
<Router>
<Routes>
<Route path="/" element={<Todo />} />
</Routes>
</Router>
);
};
export default App;
- 굳이 라우트를 설정해서 할 필요는 없지만 추가로 설정을 해봤다.
📌CompletedList.tsx
import React from "react";
import TodoItem from "./TodoItem";
import * as S from "../Styles"
interface CompletedListProps {
completedTodos: string[];
onDelete: (index: number) => void;
}
const CompletedList: React.FC<CompletedListProps> = ({ completedTodos, onDelete }) => {
return (
<S.CompletedContainer>
<S.CompleteTitle>완료</S.CompleteTitle>
{completedTodos.map((todo, index) => (
<TodoItem key={index} text={todo} onDelete={() => onDelete(index)} />
))}
</S.CompletedContainer>
);
};
export default CompletedList;
1. CompletedList 컴포넌트
CompletedList는 완료된 할 일 목록을 표시하는 컴포넌트이다. 이 컴포넌트는 completedTodos라는 배열과 onDelete라는 함수 두 가지 props를 받는다.
- completedTodos는 완료된 할 일 항목들의 배열이고, 각 항목은 문자열(string[])로 전달된다.
- onDelete는 항목을 삭제할 때 호출되는 함수로, 삭제할 항목의 인덱스를 파라미터로 받는다.
- S.CompletedContainer: 스타일링된 컨테이너로, 완료된 할 일 목록 전체를 감싸고 있다.
- S.CompleteTitle: "완료"라는 제목을 표시하는 스타일링된 제목이다.
- completedTodos.map()을 사용하여 completedTodos 배열의 각 항목을 순회하며, 각 항목마다 TodoItem 컴포넌트를 렌더링한다. 이때, 각 TodoItem에 key, text, 그리고 onDelete를 props로 전달한다.
- key={index}: React에서는 리스트 항목을 렌더링할 때 각 항목에 고유한 key를 설정해야 하므로, 여기서는 index를 사용한다.
- text={todo}: todo 항목의 내용을 TodoItem에 전달하여 표시한다.
- onDelete={() => onDelete(index)}: onDelete 함수는 해당 항목을 삭제하기 위해 호출되며, 인덱스를 onDelete 함수에 전달한다.
- TodoItem 컴포넌트는 각 할 일 항목을 나타내는 작은 컴포넌트로, 여기서는 text(할 일 내용)와 onDelete(삭제 기능)을 전달받아 사용한다.
2. 컴포넌트의 동작
- 완료된 할 일 목록을 표시: completedTodos 배열에 있는 항목들을 map() 함수로 순차적으로 렌더링하여 각 항목을 TodoItem 컴포넌트로 표시한다.
- 삭제 기능: 각 할 일 항목에는 삭제 버튼이 있으며, 이를 클릭하면 onDelete(index)가 호출되어 해당 항목을 삭제한다. onDelete는 상위 컴포넌트로부터 전달된 콜백 함수로, 완료된 할 일 목록에서 특정 항목을 제거한다.
📌TodoInput.tsx
import React, { useState } from "react";
import * as S from "../Styles";
interface TodoInputProps {
onAddTodo: (text: string) => void;
}
const TodoInput: React.FC<TodoInputProps> = ({ onAddTodo }) => {
const [text, setText] = useState("");
const handleAddTodo = () => {
if (text.trim()) {
onAddTodo(text);
setText("");
}
};
return (
<S.InputContainer>
<S.Input
type="text"
placeholder="할 일 입력"
value={text}
onChange={(e) => setText(e.target.value)}
onKeyPress={(e) => e.key === "Enter" && handleAddTodo()}
/>
<S.AddButton onClick={handleAddTodo}>할 일 추가</S.AddButton>
</S.InputContainer>
);
};
export default TodoInput;
1. TodoInput 컴포넌트
사용자가 할 일을 입력하고, 입력 후 "할 일 추가" 버튼을 클릭하거나 Enter 키를 눌러 할 일을 추가할 수 있게 해주는 기능을 한다.
- Props 정의: TodoInput 컴포넌트는 onAddTodo라는 함수 형태의 props를 받는다. 이 함수는 새로운 할 일 텍스트를 인자로 받아서 상위 컴포넌트에 전달하는 역할을 한다. TodoInput 컴포넌트는 이 props를 사용하여 사용자가 입력한 텍스트를 추가하는 기능을 수행한다.
- 상태 관리 (useState): 컴포넌트 내부에서 text라는 상태 변수를 사용하여 사용자가 입력한 텍스트를 관리한다. text 상태는 useState 훅을 통해 초기값은 빈 문자열("")로 설정되고, 사용자가 입력 필드에 값을 입력할 때마다 상태가 업데이트된다. setText는 상태 업데이트 함수로, 입력 필드의 값이 변경될 때 호출된다.
- 할 일 추가 함수: handleAddTodo 함수는 사용자가 "할 일 추가" 버튼을 클릭하거나 Enter 키를 눌렀을 때 실행된다. 이 함수는 입력된 텍스트(text)가 비어 있지 않은지 확인하고, 비어 있지 않으면 onAddTodo 함수로 텍스트를 전달한다. 텍스트가 전달된 후에는 setText를 호출하여 입력 필드를 비운다.
- 렌더링 부분: JSX에서는 S.InputContainer로 감싼 입력 필드와 버튼을 렌더링한다.
- 입력 필드 (S.Input): 이 필드는 사용자가 할 일을 입력하는 부분. value 속성에 text 상태를 바인딩하여 입력 필드의 값이 상태와 동기화되도록 한다. onChange 이벤트 핸들러는 사용자가 입력할 때마다 setText를 호출하여 상태를 업데이트한다. onKeyPress 이벤트에서는 Enter 키가 눌렸을 때 handleAddTodo를 호출하여 할 일을 추가하도록 한다.
- 할 일 추가 버튼 (S.AddButton): 이 버튼을 클릭하면 handleAddTodo 함수가 호출되어 입력된 텍스트가 할 일 목록에 추가된다.
2. 컴포넌트의 동작
- 사용자가 입력 필드에 할 일 텍스트를 입력한다.
- 텍스트가 변경될 때마다 setText를 호출하여 상태가 업데이트된다.
- 사용자가 Enter 키를 누르거나 할 일 추가 버튼을 클릭하면, handleAddTodo 함수가 호출된다.
- handleAddTodo는 입력된 텍스트가 공백이 아니면 onAddTodo 함수를 호출하여 텍스트를 상위 컴포넌트로 전달하고, 그 후 입력 필드를 비운다.
📌TodoItem.tsx
import React from "react";
import * as S from "../Styles"
interface TodoItemProps {
text: string;
onComplete?: () => void;
onDelete?: () => void;
}
const TodoItem: React.FC<TodoItemProps> = ({ text, onComplete, onDelete }) => {
return (
<S.ItemContainer>
<S.ItemText>{text}</S.ItemText>
{onComplete && <S.CompleteButton onClick={onComplete}>완료</S.CompleteButton>}
{onDelete && <S.DeleteButton onClick={onDelete}>삭제</S.DeleteButton>}
</S.ItemContainer>
);
};
export default TodoItem;
1. TodoItem 컴포넌트
각 할 일 항목을 렌더링하고, 해당 항목에 대해 완료 버튼과 삭제 버튼을 제공한다. 사용자가 버튼을 클릭하면 각각 할 일의 완료 상태를 변경하거나 삭제된다.
2. 컴포넌트의 동작
- text: 할 일 항목의 내용을 나타내는 문자열이다.
- onComplete: 할 일이 완료되었을 때 호출할 선택적 함수. 이 함수는 할 일을 완료 처리하는 역할을 하며, 완료 버튼을 클릭하면 호출된다.
- onDelete: 할 일을 삭제할 때 호출할 선택적 함수. 이 함수는 삭제 버튼을 클릭할 때 실행된다.
- 컴포넌트는 S.ItemContainer로 감싸져서 화면에 표시된다. 이 컨테이너 안에는 할 일 내용이 S.ItemText로 표시된다.
- onComplete와 onDelete가 각각 존재하면, 완료 버튼과 삭제 버튼이 화면에 렌더링.
- 완료 버튼은 onComplete 함수가 전달되었을 때만 나타나며, 클릭 시 onComplete 함수 실행.
- 삭제 버튼은 onDelete 함수가 전달되었을 때만 나타나며, 클릭 시 onDelete 함수 실행.
📌TodoList.tsx
import React from "react";
import TodoItem from "./TodoItem";
import * as S from "../Styles";
interface TodoListProps {
todos: string[];
onComplete: (index: number) => void;
}
const TodoList: React.FC<TodoListProps> = ({ todos, onComplete }) => {
return (
<S.ListContainer>
<S.Title>할 일</S.Title>
{todos.map((todo, index) => (
<TodoItem key={index} text={todo} onComplete={() => onComplete(index)} />
))}
</S.ListContainer>
);
};
export default TodoList;
1. TodoList 컴포넌트
사용자가 입력한 할 일 목록을 표시하고, 각 할 일 항목에 대해 완료 기능을 한다. 상위 컴포넌트에서 받은 todos 배열을 사용해 목록을 생성하고, 각 항목에 대해 완료 기능을 처리하는 onComplete 함수를 전달한다.
2. 컴포넌트의 동작
- todos: 완료되지 않은 할 일 항목들의 배열이다. 각 항목은 문자열로 전달된다.
- onComplete: 각 할 일 항목이 완료되었을 때 호출할 함수이다. 함수는 해당 항목의 인덱스를 인자로 받아, 할 일을 완료로 처리하는 역할을 한다.
- TodoList 컴포넌트는 todos 배열을 map 함수로 순회하며, 각 할 일 항목을 TodoItem 컴포넌트로 변환하여 렌더링한다. 각 TodoItem은 하나의 할 일을 나타내며, 그에 대해 onComplete를 호출할 수 있다.
- TodoItem 컴포넌트는 각 할 일 항목을 표시하고, 완료 버튼을 클릭하면 onComplete 함수가 호출되어 해당 항목을 완료 상태로 처리하게 된다.
- todos 배열에 있는 항목들을 순차적으로 렌더링하며, 각 항목에 대해 TodoItem을 표시이다.
📌Todo.tsx
import React, { useState } from "react";
import TodoInput from "./components/TodoInput";
import TodoList from "./components/TodoList";
import CompletedList from "./components/CompletedList";
import * as S from "./Styles";
const Todo: React.FC = () => {
const [todos, setTodos] = useState<string[]>([]);
const [completedTodos, setCompletedTodos] = useState<string[]>([]);
const addTodo = (text: string) => {
setTodos([...todos, text]);
};
const completeTodo = (index: number) => {
const newTodos = [...todos];
const completedTask = newTodos.splice(index, 1)[0] ?? "";
setTodos(newTodos);
setCompletedTodos([...completedTodos, completedTask]);
};
const deleteTodo = (index: number) => {
setCompletedTodos((prevCompleted) => prevCompleted.filter((_, i) => i !== index));
};
return (
<S.Container>
<S.Title>UMC TODOLIST</S.Title>
<TodoInput onAddTodo={addTodo} />
<S.Wrapper>
<TodoList todos={todos} onComplete={completeTodo} />
<CompletedList completedTodos={completedTodos} onDelete={deleteTodo} />
</S.Wrapper>
</S.Container>
);
};
export default Todo;
1. Todo 컴포넌트
사용자가 할 일을 입력하고, 완료된 할 일을 표시하며, 삭제 기능도 제공하고, 상태 관리와 자식 컴포넌트 간 데이터 전달을 처리한다.
- todos는 완료되지 않은 할 일 목록을 관리하는 상태. useState<string[]>([])로 초기값을 빈 배열로 설정한다.
- completedTodos는 완료된 할 일 목록을 관리하는 상태이다.
- 함수
- addTodo: 사용자가 새로운 할 일을 입력하면 이 함수가 호출된다. 입력받은 텍스트를 todos 배열에 추가하는 함수이다. setTodos([...todos, text])로 새 할 일을 배열에 추가한다.
- completeTodo: 할 일을 완료 상태로 변경하는 함수이다. 인덱스를 받아서 해당 할 일을 todos 배열에서 제거하고, completedTodos 배열에 추가한다. splice를 사용해 해당 항목을 todos에서 삭제하고, 그 항목에 추가한다.
- deleteTodo: 완료된 할 일 중에서 삭제하는 함수이다. completedTodos 배열에서 특정 인덱스를 가진 항목을 삭제하고, filter를 사용하여 해당 인덱스를 제외한 새로운 배열을 만들어 상태를 업데이트한다.
2. 컴포넌트의 동작
- TodoInput 컴포넌트에서 사용자가 입력한 새로운 할 일 텍스트는 addTodo 함수를 통해 todos 상태에 추가된다.
- TodoList 컴포넌트에서 각 할 일 항목에 대해 완료 버튼이 클릭되면 completeTodo 함수가 호출되어 해당 할 일이 todos에서 삭제되고, completedTodos에 추가된다.
- CompletedList 컴포넌트에서 삭제 버튼이 클릭되면 deleteTodo 함수가 호출되어 완료된 할 일이 completedTodos에서 삭제된다.
📌 스타일 컴포넌트를 이용한 스타일 적용
import styled from "styled-components";
export const InputContainer = styled.div`
display: flex;
justify-content: start;
margin: 10px 0;
`;
export const Input = styled.input`
flex: 1;
padding: 8px;
border: 1px solid #ccc;
border-radius: 5px;
`;
export const AddButton = styled.button`
background-color: #28a745;
color: white;
padding: 8px 12px;
margin-left: 5px;
border: none;
border-radius: 5px;
cursor: pointer;
&:hover {
background-color: #218838;
}
`;
export const ItemContainer = styled.div`
display: flex;
background-color: white;
margin: 5px 0;
padding: 10px;
border-radius: 5px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
`;
export const ItemText = styled.span`
text-align: left;
flex: 1;
`;
export const CompleteButton = styled.button`
background-color: #28a745;
color: white;
padding: 5px 10px;
border: none;
border-radius: 5px;
cursor: pointer;
&:hover {
background-color: #218838;
}
`;
export const DeleteButton = styled.button`
background-color: #dc3545;
color: white;
padding: 5px 10px;
border: none;
border-radius: 5px;
cursor: pointer;
&:hover {
background-color: #c82333;
}
`;
export const ListContainer = styled.div`
margin: 5px 0;
width: 240px;
min-height: fit-content;
transition: height 0.3s ease-in-out;
`;
export const Title = styled.h2`
font-size: 20px;
margin-bottom: 15px;
`;
export const CompletedContainer = styled.div`
margin: 5px 0;
width: 240px;
`;
export const CompleteTitle = styled.h2`
font-size: 20px;
margin-bottom: 10px;
`;
export const Container = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding: 15px;
width: fit-content;
max-width: 360px;
min-height: 200px;
background: white;
border-radius: 10px;
box-shadow: 0 4px 10px #ccc;
text-align: center;
transition: height 0.3s ease-in-out;
`;
export const Wrapper = styled.div`
display: flex;
justify-content: space-between;
align-items: flex-start;
width: 100%;
gap: 10px;
transition: height 0.3s ease-in-out;
`;
'UMC 8th Web 워크북' 카테고리의 다른 글
📜 useEffect 공식문서 간단 정리 (0) | 2025.04.05 |
---|---|
🫂왜 useEffect를 쓰는가 (Umc 워크북 3주차 강의.) (0) | 2025.04.04 |
🌐 다크 모드 적용 (UMC 워크북 강의 참고.) (0) | 2025.03.30 |
🌐TodoList - ts + vite + yarn 세팅(tailwind css 적용.) (0) | 2025.03.29 |
🌐Tailwind Css 파헤쳐 보기💿 (0) | 2025.03.25 |