티스토리 뷰
[React] #5 뽀모도로 타이머 + 투두리스트: 투두리스트 (1) - 할 일 목록 추가
무화과(Fig) 2022. 9. 23. 23:59
*피드백은 언제나 환영합니다🙌
저번 글에서는 시계 기능을 개발하면서 useEffect와 new Date()에 대해 알게되었다.
아직은 useEffect 쓰는 게 아직 서툴러서 더 공부해야 할 것 같다고 생각했다.
이번 글에서는 할 일 목록 추가하는 기능을 다뤄보겠다.
우선 각각의 컴포넌트에서 구현하고 싶은 투두리스트 기능은 다음과 같다.
- <Today /> (메인페이지): 할 일 추가/수정/삭제/완료 기능
- <Tomorrow />: 할 일 추가/삭제 기능
- <Someday /> : 할 일 추가/삭제 기능
추가/삭제/수정/완료 기능은 <Today /> 컴포넌트에만 넣었고 나머지 컴포넌트는 추가/삭제만 가능하도록 했다.
1. 기능 별로 컴포넌트 분리하기
컴포넌트는 다음과 같이 세 가지로 분리했다.
- TodoInsert.js
- TodoList.js
- TodoListItem.js
TodoInsert 컴포넌트는 텍스트 입력창과 추가 버튼이 들어갈 부분이다.
TodoList 컴포넌트는 추가된 아이템을 보여주는 부분이다.
TodoListItem 컴포넌트는 추가된 아이템 하나하나를 나타내는 부분이다. (해당 아이템의 완료/삭제/수정 기능을 추가할 것이다.)
1. TodoInsert.js
먼저 TodoInsert 컴포넌트에 input과 button을 만들었다.
return(
<div>
<input placeholder='✔ 할 일 추가' />
<button>버튼</button>
</div>
)
그리고 스타일링을 해주었다.
버튼에 hover 효과를 넣어 커서를 올리면 헤더 색상과 동일하게 되도록 만들었다.
2. TodoList
다음으로 input 칸 아래에 표시될 할 일 리스트를 만들어야 한다. 그 전에 Today 컴포넌트에서 TodoList 컴포넌트를 임포트했다.
<div className='inputButton'>
<TodoInsert/>
<TodoList />
</div>
이후 TodoList 컴포넌트에 TodoListItem 컴포넌트를 임포트해주었다.
return(
<div>
<TodoListItem />
</div>
)
3. TodoListItem
TodoListItem은 완료/수정/삭제 기능이 필요하다.
그래서 react-icons 에서 MdOutlineCheckBox, MdOutlineCheckBoxOutlineBlank 아이콘과 휴지통 아이콘(FaRegTrashAlt)도 가져왔다.
완료 / 삭제 아이콘을 추가한 모습이다.
중간에 수정 아이콘이랑 제출 버튼도 만들어봤었는데 뭔가 난잡한 느낌이라 지웠다.
수정기능이랑 제출버튼을 어떻게 할까🤔
- 검색해보니 제출 버튼은 클릭하는 것 보다 엔터키로 가능하게 하는게 사용자 입장에서 훨씬 편리하다고 한다.
그래서 따로 제출 버튼을 만들지 않고 엔터키로 해결했다.
버튼이 없는 디자인이 나을 것 같아서 다른 방법을 찾아본건데.. 사용자 입장을 고려한 디자인의 중요성도 알게됐다.
- 수정은 더블클릭시 수정할 수 있도록 했다. 원래 휴지통 옆에 연필 아이콘 클릭하면 수정할 수 있게 하는게 원래 계획이었다. 그런데 휴지통과 연필이 붙어있어서 실수로 휴지통을 클릭해 할 일이 지워질 수 있을 것 같아 아예 아이콘을 없앴다.
2. 할 일 목록 추가 기능 만들기
먼저 빈 배열을 인자값으로 넣었다.
여기에는 할 일 목록 객체가 들어가게 된다.
const [todos, setTodos] = useState([]);
그리고 Today.js 에 addTodo 함수를 추가한다. 이 함수는 사용자가 입력한 텍스트를 인자로 받아 새로운 todo 객체를 생성한다.
id: 숫자 랜덤 생성.
textValue: 사용자가 입력한 텍스트.
checked: 체크 되어있는지 알려줌. false를 기본값으로 설정함.
const addTodo = (text) => {
setTodos([
...todos,
{ id: Math.random().toString(), textValue: text, checked: false },
]);
};
그리고 addTodo함수를 props를 이용해 TodoInsert로 전달했다.
<TodoInsert onAddTodo={addTodo} />
다음으로 엔터키를 누르면 input에 입력한 값이 추가되도록 하는 과정을 다뤄보겠다.
useState 훅을 이용하여 사용자가 입력한 텍스트 값의 상태를 관리했다. 텍스트 값은 string(문자열) 이기 때문에 초기 값은 ''으로 정했다.
function TodoInsert({onAddTodo}) {
let [newTodoItem, setNewTodoItem] = useState('');
...
}
newTodoItem은 새로 입력한 텍스트의 상태고 setNewTodoItem은 newTodoItem을 업데이트 해준다.
이제 실시간으로 사용자가 입력한 텍스트 값의 변화를 관리하기 위해 핸들러 함수(todoInputHandler)를 만들었다.
function todoInputHandler(e) {
setNewTodoItem(e.target.value);
};
그리고 input의 onChange 안에 넣어주었다. value속성에는 newTodoItem을 넣어서 실시간으로 최신 텍스트가 업데이트 되게했다.
return(
<div className='inputButton'>
<input
className='insertBox'
onChange={todoInputHandler}
value={newTodoItem}
placeholder='✔ 할 일 추가 (Press Enter)' />
</div>
)
이후 아이템을 추가해주는 핸들러를 만들었다. 이 함수 안에는 onAddTodo와 setNewTodoItem가 있다.
onAddTodo는 사용자가 입력한 텍스트를 전달 받아 목록에 추가하고 setNewTodoItem은 입력창을 공백으로 초기화 시켜준다.
function addTodoHandler(e) {
onAddTodo(newTodoItem);
setNewTodoItem('');
};
마지막으로 엔터키를 누르면 할 일이 추가되도록 input에 onKeyUp 속성을 추가했다.
그리고 &&연산자를 사용하여 엔터키를 누른 경우, e.target.value의 길이가 0보다 크면 아이템이 추가되도록 했다.
따라서 아무것도 입력하지 않았을 경우에는 아이템이 추가되지 않는다.
근데 문제가 하나 있다.
다른 텍스트 없이 스페이스를 여러 번 누르고 제출하면 제출되어버린다. 내가 원하는 건 텍스트가 아무것도 없는 경우에는 제출이 안되게 하는 것이다.
아직 방법을 못 찾아서 그냥 onKeyUp을 사용했다. 우선 다른 것 부터 해결하기로!
return(
<div className='inputButton'>
<input
className='insertBox'
onKeyUp={(e)=> {
e.preventDefault();
if(e.key == 'Enter' && e.target.value.length > 0) {
addTodoHandler()
}
}}
onChange={todoInputHandler}
value={newTodoItem}
placeholder='✔ 할 일 추가 (Press Enter)' />
</div>
)
여기까지 TodoInsert 컴포넌트 끝!
import React, {useState} from 'react';
function TodoInsert({onAddTodo}) {
let [newTodoItem, setNewTodoItem] = useState('');
function todoInputHandler(e) {
setNewTodoItem(e.target.value);
};
function addTodoHandler(e) {
onAddTodo(newTodoItem);
setNewTodoItem('');
};
return(
<div className='inputButton'>
<input
className='insertBox'
onKeyUp={(e)=> {
e.preventDefault();
if(window.event.keyCode == 13 && e.target.value.length > 0) {
addTodoHandler()
}
}}
onChange={todoInputHandler}
value={newTodoItem}
placeholder='✔ 할 일 추가 (Press Enter)' />
</div>
)
}
export default TodoInsert;
이제 추가한 아이템을 리스트에 출력할 일 만 남았다. Today.js 에서 todos를 TodoList 컴포넌트로 전달했다.
<TodoList todos={todos} />
todos는 할 일 목록의 객체가 담긴 배열이다. 따라서 TodoList컴포넌트에서 TodoListItem 컴포넌트로 전달 할 때에는 배열에 담긴 객체 하나하나를 넘겨줘야 한다.
function TodoList({todos}) {
return(
<div>
{
todos.map(todo => (
<TodoListItem
{...todo} />
)
)
}
</div>
)
}
map()함수로 아이템 하나하나를 TodoListItem 컴포넌트로 전달했다. 그리고 자바스크립트의 디스트럭처링 문법을 이용해서 TodoListItem 컴포넌트에서 아이템 객체에 담긴 값들을 받았다.
각각의 아이템에는
- id
- textValue
- checekd
라는 key와 그에 해당하는 value가 담겨있다.
따라서 TodoListItem 컴포넌트는 TodoList 컴포넌트에서 전달한 값들을 받을 수 있다.
function TodoListItem({textValue, id, checked}) {
return (
.
.
<p}>{textValue}</p>
.
.
)
)
이번 문제를 해결하면서 느낀건 대략적인 순서를 정하고 코딩하는 습관이 필요하다는 것이다.
Udemy에서 자바스크립트 강의를 들을 때 강사님이 다이어그램으로 순서도를 짠 뒤 코딩하라고 하셨던게 기억난다.
나는 '일단 해보고 안되면 다른 방법을 생각하자' 주의였는데 이번에 프로젝트를 직접 해보니 무슨 말인지 와닿는다. 앞으로는 계획을 가지고 문제에 뛰어들어야겠다.
오늘은 여기까지!
다음 글에서는 할 일 목록을 삭제하는 기능에 대해 다뤄볼 예정이다.
'Toy Project > Time to focus' 카테고리의 다른 글
- Total
- Today
- Yesterday
- react
- Target
- tanstackquery
- html
- 코드잇스프린트
- 중급 프로젝트
- GitHub
- 배열
- hydrationboundary
- map
- 비동기
- 코드잇 스프린트
- CSS
- 프론트엔드
- js
- 취업까지달린다
- currentTarget
- 동기
- rest parameter
- 유사배열객체
- arguments
- Next.js
- Git
- 제어 컴포넌트
- 스프린트프론트엔드6기
- 객체
- 리액트
- 비제어 컴포넌트
- innerhtml
- javascript
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |