[React]리액트를 다루는 기술-10. 일정 관리 웹 애플리케이션 만들기

chaaerim·2021년 12월 2일
1

📖 일정 관리 웹 애플리케이션 만들기

오늘은 지금까지 배운 지식을 활용하여 일정 관리 애플리케이션을 만들어보겠습니다!

10.1. 프로젝트 준비하기

✍ example

$ cd todo-app
$ yarn add sass classnames react-icons
  • 프로젝트에서 Sass를 사용할 것이므로 sass 설치
  • classnames는 조건부 스타일링을 더 편하게 하기 위해 설치

✍ example

body{
  margin: 0;
  padding: 0;
  background: #e9ecef;
}
  • 배경 회색으로 설정

✔ 결과 확인


10.2. UI 구성하기

앞으로 만들 컴포넌트
1. TodoTemplate : 앱 타이틀 보여주기
2. TodoInsert : 새로운 항목을 입력, 추가
3. TodoListItem : 각 항목에 대한 정보를 보여주기
4. TodoList : 여러 개의 TodoListItem 컴포넌트로 변환하여 보여주기

10.2.1. TodoTemplate 만들기

✍ example

//TodoTemplate.js
import './TodoTemplate.scss';

const TodoTemplate=({children})=>{
    return(
        <div className="TodoTemplate">
            <div className="app-title">일정 관리</div>
            <div className="content">{children}</div>
        </div>
    );
};

export default TodoTemplate;
//App.js
import TodoTemplate from './components/TodoTemplate';

const App=()=>{
  return <TodoTemplate>Todo App을 만들자! </TodoTemplate>
};

export default App;
  • TodoTemplate.js를 만들고 App.js에서 렌더링

✍ example

//jsconfig.json
{
    "compilerOptions": {
        "target": "es6"
    }
}
  • 만약 import문을 직접 입력하는 것이 귀찮다면 위와 같은 jsconfig.json 파일을 최상위 디렉토리에 만들면 됨.
  • 파일을 저장하면 자동완성을 통해 컴포넌트를 불러와서 사용 가능.

✔ 결과 확인


✍ example

//TodoTemplate.scss
.TodoTemplate {
  width: 512px; //width가 주어진 상태에서 좌우 중앙 정렬
  margin-left: auto;
  margin-right: auto;
  margin-top: 6rem;
  border-radius: 4px;
  overflow: hidden;
}

.app-title {
  background: #22b8cf;
  color: white;
  height: 4rem;
  font-size: 1.5rem;
  display: flex;
  align-items: center;
  justify-content: center;
}

.content {
  background: white;
}
  • TodoTemplate.js를 만든 components 디렉토리에 TodoTemplate.scss 작성

✔ 결과 확인

10.2.2. TodoInsert 만들기

✍ example

//TodoInsert.js
import{MdAdd} from 'react-icons/md';
import './TodoInsert.scss';

const TodoInsert=()=>{
    return(
        <form className="TodoInsert">
        <input placeholder="할 일을 입력하세요"/>
        <button type="submit">
            <MdAdd/>
        </button>
        </form>

    );
};

export default TodoInsert;

  • import{아이콘 이름} from 'react-icons/md';위에서 사용하고 싶은 아이콘을 선택하여 import구문을 이용해 컴포넌트 처럼 사용가능
  • TodoInsert.js를 App.js에서 렌더링

✔ 결과 확인

10.2.3. TodoListItem과 TodoList 만들기

일정 관리 항목이 보일 TodoListItem과 TodoList를 만들어보자.
✍ example

//TodoListItem.js
import{
    MdCheckBoxOutlineBlank,
    MdCheckBox,
    MdRemoveCircleOutline,
} from 'react-icons/md';
import './TodoListItem.scss';
  • 다양한 아이콘을 불러와 사용.
//TodoList.js
import TodoListItem from "./TodoListItem";
import'./TodoList.scss';

const TodoList=()=>{
    return(
        <div className="TodoList">
            <TodoListItem/>
            <TodoListItem/>
            <TodoListItem/>
        </div>
    );
};

export default TodoList;
  • TodoList.js에 TodoListItem을 불러와서 별도의 props 전달 없이 보여줌.

✔ 결과 확인


10.3. 기능 구현하기

10.3.1. App에서 todos 상태 사용하기

✍ example

//App.js
import TodoTemplate from './components/TodoTemplate';
import TodoInsert from './components/TodoInsert';
import TodoList from './components/TodoList';
import { useState } from 'react';
const App=()=>{
  const[todos, setTodos]=useState([{
    id:1,
    text:'리액트 기초 알아보기',
    checked: true,

  },
  {
    id:2,
    text:'컴포넌트 스타일링 보기',
    checked: true,
  }, 
  {
    id:3,
    text:'일정 관리 앱 만들어 보기',
    checked:false,


  },
]);
  return (<TodoTemplate>
             <TodoInsert/> 
            <TodoList todos={todos}/>
          </TodoTemplate>
      )
};

export default App;
  • todos 배열 안에 들어있는 객체에 각 항목의 id, 내용, 완료 여부를 알려주는 값 포함.
  • todos배열은 TodoList에 props로 전달.

✍ example

//TodoList.js
import TodoListItem from "./TodoListItem";
import'./TodoList.scss';

const TodoList=({todos})=>{
    return(
        <div className="TodoList">
           {todos.map(todo=>(
               <TodoListItem todo={todo} key={todo.id}/>
           ))}
        </div>
    );
};

export default TodoList;
  • props로 받아온 todos를 내장 함수 map()을 통해 TodoListItem으로 이뤄진 배열로 변환하여 렌더링.
  • map을 사용할 때에는 key props도 함께 전달해주어야 함.

✍ example

//TodoListItem.js
import{
    MdCheckBoxOutlineBlank,
    MdCheckBox,
    MdRemoveCircleOutline,
} from 'react-icons/md';
import './TodoListItem.scss';
import cn from 'classnames';
import './TodoListItem.scss';

const TodoListItem=({todo})=>{
    const{text, checked}=todo;
    return(
        <div className="TodoListItem">
            <div className={cn("checkbox", {checked})}>
               {checked ? <MdCheckBox/> :<MdCheckBoxOutlineBlank/>}
                <div className="text">{text}</div>
            </div>
        
        <div className="remove">
            <MdRemoveCircleOutline/>
        </div>
    </div>
    );
};

export default TodoListItem;
  • 받아온 todo 값에 따라 렌더링할 수 있도록 컴포넌트 수정.

✔ 결과 확인

10.3.2. 항목 추가 기능 구현하기

10.3.2.1. TodoInsert value 상태 관리하기

✍ example

//TodoInsert.js
import { useState, useCallback } from 'react';
import{MdAdd} from 'react-icons/md';
import './TodoInsert.scss';

const TodoInsert=()=>{
    const[value, setValue]=useState('');
    const onChange=useCallback(e=>{
        setValue(e.target.value);
    }, []);
    return(
        <form className="TodoInsert">
        <input 
        placeholder="할 일을 입력하세요"
        value={value}
        onChange={onChange}/>
        <button type="submit">
            <MdAdd/>
        </button>
        </form>

    );
};

export default TodoInsert;
  • TodoInsert 컴포넌트에 인풋 값을 관리할 수 있도록 useState을 이용하여 value를 정의.
  • 인풋에 넣어주는 onChange함수 추가.
  • 함수를 재사용할 수 있도록 useCallback Hook 사용.

#### 10.3.2.2. 리액트 개발자 도구

현재 state가 잘 업데이트되고 있는지 console.log를 찍어보지 않고도 확인하는 방법이 있다.
=> 리액트 개발자 도구를 사용하면 됨.

  • 리액트 개발자 도구를 설치하고 크롬 개발자 도구를 열면 도구 탭에 Components이 나타남.
  • 그리고 좌측에서 TodoInsert를 선택하면, 다음과 같이 인풋을 수정했을 때 Hooks의 state부분에도 같은 값이 들어감을 확인.

10.3.2.3 todos 배열에 새 객체 추가하기

✍ example

//App.js
import TodoTemplate from './components/TodoTemplate';
import TodoInsert from './components/TodoInsert';
import TodoList from './components/TodoList';
import { useState, useRef, useCallback } from 'react';
(...)
const nextId=useRef(4);
const onInsert=useCallback(
  text=>{
    const todo={
      id: nextId.current,
      text,
      checked: false,};
      setTodos(todos.concat(todo));
      nextId.current+=1;
    },
  [todos],
)
  return (<TodoTemplate>
             <TodoInsert onInsert={onInsert}/> 
             <TodoList todos={todos}/>
          </TodoTemplate>
      )
};

export default App;
  • todos 배열에 새 객체를 추가하는 onInsert 함수 생성.
  • id값은 렌더링되는 정보가 아니므로 useRef를 이용하여 관리.

10.3.2.4. TodoInsert에서 onSubmit 이벤트 설정하기

✍ example

//TodoInsert.js
(...)
const TodoInsert=({onInsert})=>{
    const[value, setValue]=useState('');
    const onChange=useCallback(e=>{
        setValue(e.target.value);
    }, []);
    const onSubmit=useCallback(
        e=>{
            onInsert(value);
            setValue('');

            e.preventDefault();
        },
        [onInsert, value],
    )
    (...)
  • onInsert 함수에 value값을 파라미터로 넣어 호출.
  • onSubmit는 props로 받아 온 onInsert함수에 현재 value값을 넣어서 호출하고, 현재 value값을 초기화.
  • e.preventDefault()함수는 submit이 될 때 새로고침을 방지.

✔ 결과 확인

10.3.3. 지우기 기능 구현하기

10.3.3.1. 배열 내장 함수 filter

✍ example

const array=[1, 2, 3, 4, 5, 6, 7];
const biggerThanFive=array.filter(number=>number>5);
//결과: [6. 7]
  • filter는 기존의 배열을 그대로 둔 상태에서 특정 조건을 만족하는 원소만 추출하여 새로운 배열을 만듦.
  • 조건을 확인해 주는 함수를 파라미터로 넣어야함.

10.3.3.2 todos배열에서 id로 항목 지우기

✍ example

//App.js
(...)
const onRemove=useCallback(
  id=>{
    setTodos(todos.filter(todo=>todo.id!==id));
  },
  [todos],
);
(...)
  • id를 파라미터로 받아와서 같은 id를 가진 항목을 todos에서 지우는 onRemove함수를 작성.

10.3.4. 수정 기능

✍ example

//App.js
(...)
const onToggle = useCallback(
    id => {
      setTodos(
        todos.map(todo =>
          todo.id === id ? { ...todo, checked: !todo.checked } : todo,
        ),
      );
    },
    [todos],
  );
(...)
  • onToggle함수 App.js에 추가.
  • map함수는 배열을 전체적으로 새로운 형태로 변환해서 새로운 배열을 생성할 때 사용하는데 왜 원소 하나만 수정하는 onToggle 함수에서 사용?
    => todo.id === id ? ... : ... 삼항연산자 사용-todo.id와 현재 파라미너 id값이 같을 때에는 새로운 객체를 생성하지만 id값이 다르면 변화를 주지 않으므로 map을 사용해서 변화가 필요한 원소만 업데이트!!

✔ 결과 확인

0개의 댓글