[REACT] 리액트로 TODO 리스트 구현하기 :: (2) 기능 구현하기

2020. 8. 23. 23:33React

 

https://codecrafting.tistory.com/11 에서 이어집니다!

 

[REACT] 리액트로 TODO 리스트 구현하기 :: (1) UI 구성하기

서론 요즘 리액트를 다루는 기술이라는 책으로 코딩을 시작하고 처음으로! 리액트를 접하고 있습니다. 요즘 트렌드가 기존의 MVC구조에서 SPA위주로 추세가 전환되고 있다는 소식이 자주 들려옵

codecrafting.tistory.com

 

 

이제 일정 관리 애플리케이션이 실제로 동작할 수 있도록 기능을 구현해봅시다. 

 

App 에서 todos 상태 사용하기

나중에 추가할 일정 항목에 대한 상태들은 모두 App 컴포넌트에서 관리합니다. App에서 useState를 사용하여 todos라는 상태를 정의하고, todos를 TodoList의 prop으로 전달합니다. 

 

App.js

todos 배열 안에 들어있는 객체에는 각 항목의 Id, 내용, 완료 여부를 알려주는 값이 포함되어 있습니다. 이 배열은 TodoList에 props에 전달됩니다. TodoList에서 이 값을 받아온 후, TodoItem으로 변환하여 랜더링 하도록 설정할 것입니다.

 

TodoList.js

props로 받아 온 todos 배열을 배열 내장 함수 maps을 통해 TodoListItem으로 이루어진 배열로 변환하여 랜더링해 주었습니다. map을 이용하여 컴포넌트로 변환할 때는 key props를 전달해 주어야 하는데 이때 각 todo 객체의 고윳값이 id를 전달해줍니다. 그리고 TodoItem에서 다룰 데이터는 통째로 props 로 전달하면 됩니다.

 

자바스크립트 배열의 map() 함수는 반복되는 컴포넌트를 랜더링 할 때 필수적으로 사용되는 메소드입니다. map 함수는 파라미터로 전달된 함수를 사용하여 배열 내 각 요소를 원하는 규칙에 따라 변환한 후 그 결과로 새로운 배열을 생성합니다. 

 

예를 들어, const name = ['눈사람' ,'얼음', '눈', '바람'] 이라는 배열이 있을 때, 이 배열의 값을 사용하여 JSX 코드로 구성된 배열을 새로 만들고 싶다면 아래 예제처럼 하면 됩니다.

import React, { Component } from 'react';

// props로 names = ['눈사람', '얼음', '눈', '바람'] 라는 데이터가 들어온다고 했을 때
const SampleComponent = ({ names }) -> {
	{names.map(name -> (
    	<NameItem name={name} />
    ))}
}

위 예제의 결과는 NewItem 이라는 컴포넌트가 네 번 생성되면서 prop의 값으로 각 컴포넌트에 {name: '눈사람'} {name: '눈'} {name: '얼음'}, {name: '바람'}의 데이터가 들어가게 됩니다.

 

아무튼 <TodoListItem todo={todo} key={todo.id} /> 형식으로 JSX 구문이 생성되었으므로 todo 값에 따라 적절한 UI를 보여줄 수 있도록 컴포넌트를 수정해보겠습니다.

 

TodoLIstItem.js

classnames 를 사용하여 특정 div 를 제어할 수 있습니다. 예제에서 checkbox 클래스의 값을 props로 넘어오는 checked으로 결정하고 있는데, 이때 checked 의 값이 True이면 <MdCheckBox/> 를, False이면 <MdCheckBoxOutlineBlank/>를 나타냅니다.

text 역시 마찬가지로 props로 넘어오는 데이터이기 때문에 그대로 {text} 로 출력하면 됩니다.

 

항목 추가 기능 구현하기

이번에는 일정 항목을 추가하는 기능을 구현합니다. 이 기능을 구현하려면, TodoInsert 컴포넌트에서 인풋 상태를 관리하고 App 컴포넌트에는 todos 배열에 새로운 객체를 추가하는 함수를 만들어야 합니다.

 

먼저 TodoInsert 컴포넌트부터 수정하겠습니다.

 

TodoInsert.js



input 태그 안에서 값을 입력할 때마다 컴포넌트 state인 value에 값이 들어가게 정의했습니다. 이렇게 정의된 새로운 값을 todos 배열에 추가하도록 onInsert 함수를 만들어 보겠습니다. 

App.js

useRef 함수를 이용해 nextId 라는 변수에 4를 초기화 시키고 이를 새로 등록된 할 일 객체의 id로 활용합니다.

등록버튼을 눌렀을 때 호출되는 함수를 onInsert() 함수로 정의하는데, 당연히 기존 객체의 key가 모두 포함되어 있는 데이터 형태를 띄고 있어야 합니다. id에는 nextId의 값을 넣고 text에는 전달된 text값을 그대로 넣습니다. checked 에는 기본값으로 false를 넣으면 되겠네요.

 

이제 만들어진 onInsert() 함수를 항목 추가버튼이 있는 컴포턴트인 TodoInsert 의 props로 전달합니다.

 

TodoInsert.js

TodoInsert의 submit버튼을 눌렀을 때 동작할 함수 onSubmit를 만듭니다. 이 함수가 호출되면 Props로 받아온 onInsert() 함수에 현재의 value 값을 파라미터로 넣어서 호출하고, 현재 value 값을 초기화 합니다.

 

이것으로 항목의 추가기능이 구현됐습니다!

 

잘 되나 한번 볼까요? 🤔

 

 

 

 

정상적으로 잘 작동하네요! 🤗

 

추가기능을 했으면 이번에는 삭제기능을 구현해볼 차례입니다.

리액트 컴포넌트에서 배열의 불변성을 지키면서 배열 원소를 제거해야 할 경우, 배열 내장 함수인 filter를 사용하면 매우 간편합니다.

 

아래 예시는 1부터 10까지 이루어진 배열을 5 이상인 수 만 담겨있는 배열로 필터링 합니다.

const array [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
const biggerThanFive = array.filter(number => number > 5);
// 결과 : [ 6, 7, 8, 9, 10 ]

filter 함수에는 조건을 확인해 주는 함수를 파라미터로 넣어야 합니다 (조건문 필수). 따라서 반드시 boolean 값을 리턴해야만 하며, 배열의 값이 조건문에서 true 인 경우에만 새로운 배열에 포함됩니다.

 

방금 사용한 filter 함수를 사용하여 onRemove 함수를 App 컴포넌트에 작성해보겠습니다.

App 컴포넌트에 Id를 파라미터로 받아와서 같은 Id를 가진 항목을 todos 배열에서 지우는 함수입니다. (만약에 id 3을 지우고 싶으면 3이 미포함된 배열을 리턴)

 

App.js

todos 배열객체를 filter 함수를 통해서 새로운 배열로 리턴하는게 보이시나요? todos를 수정하는 과정이니까 당연히 useState를 이용해 setTodos를 호출하면 됩니다.

 

이렇게 만들어진 onRemove 함수를 TodoList로 전달해 TodoListItem 컴포넌트의 props 로 추가해보도록 하겠습니다.

 

TodoList.js

이제 TodoListItem.js 에서 onRemove함수를 불러내 인자로 아이디 값을 담으면 되겠습니다.

 

 

TodoListItem.js

 

 

이렇게 삭제 기능까지 모두 구현이 되었습니다!

 

 

 

바로 이어서 토글 기능을 추가해보도록 하겠습니다.

 

토글기능도 삭제 기능과 꽤 비슷합니다. App에서 수정 함수를 만들어서 TodoList에게 전달하고 이것을 TodoListItem 의 props으로 넣어주면 됩니다.

 

App.js

onToggle() 함수는 클릭 시 해당 todo의 id를 인자로 받아 기존의 todo객체를 얕은 복사하고 (불변성 유지), checked의 값을 반대로 바꿉니다. 나머지 todo는 그냥 todo로 내버려둡니다.

 

TodoList.js

삭제와 마찬가지로 TodoListItem 컴포넌트로 onToggle 함수를 전달해줍니다.

 

 

TodoListItem.js

엔드포인트인 TodoListItem 입니다. 이제 이 checkbox클래스의 div를 클릭할 때 마다 onToggle 함수를 해당 todo의 id를 인자로 주면서 호출하게 됩니다. 그러면 App 컴포넌트에서 함수를 정의해준 대로 checkbox의 값이 바뀌게 되면서 re-rendering되고TodoListItem에서도 checked에 값에 따라 checkbox 클래스의 값이 바뀌게 되니 이에 해당하는 UI로 바뀌게 될 것입니다.

 

 

 

 

전부 잘 되네요! 

 

 

이렇게 일정 관리 웹 어플리케이션을 실습해보는데요, 지금 만든 어플리케이션의 경우 많은 데이터를 처리할 때 필연적으로 많은 시간이 소요됩니다. 최적화가 되어있지 않기 때문이죠! 

 

그래서 다음 포스팅에서는 실습한 일정 관리 어플리케이션을 최적화 시키는 작업을 진행하도록 하겠습니다