WIL

또 투두리스트

resolve() 2023. 7. 28. 15:24

요즘 리액트로 또 투두리스트를 만들고있다. 대신 이번에는 체크박스를 사용해서.. 
다시 만드는 김에 투두리스트의 기본적인 구현방법을 CRUD에 빗대서 간단하게 정리해두려고 한다.
소위 말하는 CRUD라는건  Create, Read, Update, Delete의 앞글자만 떼온건데 게시판의 가장 기본적인 구성이라고 생각하면 될것같다. 투두리스트는 그 축소판으로 만들기 좋은 예제라서 맨날맨날 만드는 것 같다. 

1. Read

CRUD지만 R부터 해야 쉽다. 먼저 예시로 쓸 객체의 배열을 초기값으로 설정한 useState 하나를 만들어준다. 
const [todo, setTodo] = useState([{id:식별자 uuid()같은거, title:'제목', content:'내용', isDone:false}]) 이런식으로. 
이 todo라는 state는 배열이니까 map으로 뿌려줄 수 있다. JSX에서 {todo.map~}으로 뿌려준다. 짜잔 끝.

2. Create

input을 하나 만든다. onchange를 달아서 입력한 값을 추적하게 만든다. 이 입력한 값은 새로운 useState (write)로 (관리해준다. 추가 버튼에 click 이벤트와 submit 타입을 달아둔다.

click이벤트와 연결된 함수는 이런 내용을 가진다 :
- 클릭시 새로고침 방지
- 입력한 값이 들어갈 새로운 객체를 만들고, 변수(newTodo)로 선언해주기
- 기존에 만들어뒀던 상태 업데이트 setState (setTodo)로 newTodo를 업데이트 시켜줄건데, 기존에 있던 객체의 데이터도 유지해야 하니까 setTodo([...todos, newTodo])로 로 불변성 지켜주기
- 입력했던 값이 input창에 남아있으면 지저분하니까 setWrite('')로 초기화 시켜주기
- input창에 그냥 공백만 입력하면 추가가 안되게 만들고 싶다? 
if (write.trim() === '') { return } 을 사용하면 된다. trim()은 앞뒤공백을 없애주는 매서드인데, 이렇게 없앤 뒤의 값이 '' 면 빈 값이라는 소리니까 아무것도 반환하지 않는다. 즉, 추가되지 않는다.  

 

 3. Update

투두리스트의 글을 직접 수정하는 대신에 이번에는 체크박스를 토글하는 걸로 U의 기능을 구현해봤다. 체크박스도 글 수정이랑 다를 게 없다. 기존 데이터의 내용을 변경해서 사용자가 보는 상태 정보를 업데이트(수정)해주는 게 핵심이다.

이대로는 심심할것 같아서 커스텀 체크박스를 만들었다.
아래처럼 원래는 흰색 체크박스처럼 보이다가, 클릭하면 오렌지색 배경에 하얀 체크가 표시된다. 

// JSX


  <input
   type="checkbox"
   id={`checkbox-${id}`}
   checked={isDone === 'completed'}
   onChange={statusChangeHandler}
  />

  <label htmlFor={`checkbox-${id}`}></label>
   {text}

// CSS


 
input[type="checkbox"]+label {
    display: inline-block;
    width: 15px;
    height: 15px;
    border: 3px solid white;
    background-color: white;
    position: relative;
    border-radius: 3px;
    cursor: pointer;

  }


  /* 체크박스가 선택되었을 때 스타일 변경 */

  input[type='checkbox']:checked+label::after {
    content: '';
    font-size: 15px;
    width: 15px;
    height: 15px;
    text-align: center;
    position: absolute;
    border: 3px solid orange;
    background-color: orange;
    right: -3px;
    bottom: -3px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 3px;
    cursor: pointer;
  }

JSX에서 input에 checkbox type을 줘서 체크박스 원형을 만든 다음에 label에 htmlFor을 달아 input의 id로 연결해준다. 
그런 다음 CSS로 클릭하기 전의 체크박스 input[type='checkbox']+label{} / 클릭한 후의 체크박스 input[type='checkbox'] : checked+label :: after {} 디자인을 커스텀해준다.

content안에 체크가 아니라 다른걸 넣거나, 체크박스 모양을 둥근 스위치로 변경하는 등 더 다양하게 디자인 할 수 있어서 좋다. 

아무튼 id에 `checkbox-${id}`를 넣어서 새로운 id가 생길때마다 새롭게 연결해준건 좋았는데, 아직 제대로 작동하지 않는다. 내가 원하는건 체크박스를 누를 때 마다 todo라는 state의 배열에 있는 isDone:'active' 라는 key-value가 active-completed로 토글되는 것이다. 

위에 보면 이걸 위해서 넣어둔게 checked={isDone === 'completed'} 이 부분이다. onChange에 isDoneChangeHandler라는 함수를 달아놓자. 이 함수는 삼항연산자 e.target.checked ? 'completed' : 'active' 와, 이 삼항연산자 값을 저장해둔 변수 newStatus가 있고 이 status를 업데이트해준다.

업데이트는 어떻게 해주냐, 새로운 update라는 useState를 만들수도 있겠지만 나는 statusUpdateHandler라는 함수를 만들어서 사용했다. 이 함수는 todos라는 초기 배열을 다시 map으로 뿌려주는데, 이번에는 todos의 item.id와 id가 일치하면 status를 newStatus로 바꿔주고, 일치하지 않으면 item(todos) 그대로 반환해준다.  

 

 3. Delete

이제 CRUD의 마지막인 삭제. 삭제 버튼에 onClick으로 연결한 함수를 작성해주면 된다.
어떤 함수냐면 filter로 내가 선택한 id와 id가 일치하지 않는 배열만 새로 담아서 setState로 보여주는 내용이다.

const delBtnClickHandler = id => {
const deletedTodos = todos.filter(item => item.id !== id)
setTodos(deletedTodos) 
}

이런 느낌. 여기서 id는 todos라는 배열에 들어있는 id:uuid() 라는 key를 말한다.