본문 바로가기

Hanghae99

221130 TIL React 입문주차 4

1. 개인과제

  • Goal : My Todo List 만들기
  • features :  
    • UI 구현하기
    • Todo 추가 하기
    • Todo 삭제 하기
    • Todo 완료 상태 변경하기 (완료 ↔ 진행중)
  • Requirement :
    • 제목과 내용을 입력하고, [추가하기] 버튼을 클릭하면 Working에 새로운 Todo가 추가되고 제목 input과 내용 input은 다시 빈 값으로 바뀌도록 구성해주세요.
    • Todo의 isDone 상태가 true이면, 상태 버튼의 라벨을 취소, isDone이 false 이면 라벨을 완료 로 조건부 렌더링 해주세요. 위 영상을 보면 버튼 내 라벨이 다른 걸 볼 수 있죠?
    • Todo의 상태가 Working 이면 위쪽에 위치하고, Done이면 아래쪽에 위치하도록 구현합니다.
    • Layout의 최대 넓이는 1200px, 최소 넓이는 800px로 제한하고, 전체 화면의 가운데로 정렬해주세요.
    • 컴포넌트 구조는 자유롭게 구현해보세요.

나의 풀이

// React를 사요한다. state hook을 사용하기 위해 { useState } 를 불러옴
import React, { useState } from "react";
import "./App.css"; // 🔥 반드시 App.css 파일을 import 해줘야 합니다.
// id 값의 혼선을 방지하기 위해 key값으로 uuid를 설정하였음.
// 자세한 내용은 본문의 uuid를 참고
import uuid from "react-uuid";

const App = () => {
  // todoLists 객체로 state hook을 지정해주었음
  const [todoLists, setTodoLists] = useState([
    {
      // 순서를 지정하기 위해 id값을 지정하였음
      id: 0,
      // id값만 가지고 있을 경우 지칭상의 혼선이 올수 있기 때문에 uuid를 지정하였음
      key: uuid(),
      title: "리액트 공부하기",
      desc: "리액트 기초를 공부해봅시다",
      // 진행중과 완료 상태를 표현하기 위해 isDone 값을 true, false로 지정하였음
      isDone: false,
    },
    {
      id: 1,
      key: uuid(),
      title: "리액트 알아보기",
      desc: "리액트 기초를 알아봅시다",
      isDone: true,
    },
  ]);

  const deleteTodoListHandler = (key) => {
    // '삭제' 버튼을 클릭했을때 실행되는 handler
    // filter를 통해서 받아오는 key값과 다른 key값을 가진 객체들만으로 새로운 배열을 구성해서 state로 넘겨주는 것.
    const newTodoList = todoLists.filter((todoList) => todoList.key !== key);
    setTodoLists(newTodoList);
  };

  const onChangeHandler = (key) => {
    // 완료 혹은 취소 버튼을 클릭했을때 실행되는 handler
    // 받아오는 key값과 같은 key값을 가진 객체를 배열에서 찾아 그 인덱스 값을 리턴하는 함수 findIndex를 선언
    const index = todoLists.findIndex((todoList) => todoList.key === key);

    // spread order로 todoLists 배열의 복사본을 생성하였음
    // ????    복사본은 왜 만들어야 할까? 원본은 어떻게 되는 걸까?
    // !!!!    기존에 있던 배열의 속성을 변경해주면 불변성문제 때문에 react에서 속성이 변한것을 감지하지 못하고 렌더링을 하지 않는다.
    // !!!!    그렇기 때문에 copyTodo를 생성해줘서 주소값을 변경해주어야 한다.
    const copyTodo = [...todoLists];

    // 이 부분이 해석이 안된다.
    // ????   ...copyTodo[index] 이것은 무슨 의미일까?
    // !!!!   ...copyTodo[index] 이것은 copyTodo[index] 내의 모든 속성을 복사해준다는 의미이다.
    // ????   spread syntax의 정확한 의미는 무엇일까?
    // !!!!   spread syntax는 배열 값을 복사해온다.
    if (copyTodo[index].isDone === true) {
      copyTodo[index] = { ...copyTodo[index], isDone: false };
    } else {
      copyTodo[index] = { ...copyTodo[index], isDone: true };
    };

    // setTodoLists 로 새로운 배열을 복사해준다.
    setTodoLists(copyTodo);
  };

  const workingLists = todoLists
    // 객체중에서 isDone 값이 false인 값을 골라 새로운 배열을 만들어줌
    .filter((todoList) => todoList.isDone === false)
    // 각각의 요소에 대해 한번씩 순서대로 불러 그 함수의 반환값으로 새로운 배열을 만들어줌
    .map((todoList) => {
      return (
        // child component인 List를 불러주고 아래와 같이 값을 넣어준다.
        <List
          handleDelete={deleteTodoListHandler}
          handleDone={onChangeHandler}
          todoList={todoList}
          key={todoList.key}
        ></List>
      );
    });

  const doneLists = todoLists
    .filter((todoList) => todoList.isDone === true)
    .map((todoList) => {
      return (
        <List
          handleDelete={deleteTodoListHandler}
          handleDone={onChangeHandler}
          todoList={todoList}
          key={todoList.key}
        ></List>
      );
    });

  // 새로운 title과 desc를 받아 새로운 객체를 만들기 위해 state hook을 선언해줌
  const [title, setTitle] = useState("");
  const [desc, setDesc] = useState("");

  // 추가하기를 클랙했을때 실행할 event handler
  const onSubmitHandler = () => {
    // title 과 desc가 아무것도 입력되지 않으면 실행되지 않도록 if 함수를 사용함.
    if (title !== "" && desc !== "") {
      // 새로운 객체를 생성해줌. isDone의 기본값은 false이다.
      const newTodoLists = {
        id: todoLists.length + 1,
        key: uuid(),
        title: title,
        desc: desc,
        isDone: false,
      };

      // todoLists 배열을 복사해주고 newTodoLists를 추가해줌
      setTodoLists([...todoLists, newTodoLists]);
    } else {
      return null;
    }
  };

  return (
    <div class="layout">
      <div className="container">
        <div>My Todo List</div>
        <div>React</div>
      </div>
      <div className="add-form">
        <div className="input-style">
          <label className="form-label">제목</label>
          <input
            className="add-input"
            // 입력되는 값을 title로 넘겨줌
            value={title}
            // state함수를 이용하여 입력되는 값을 배열로 넘겨줌
            onChange={(e) => setTitle(e.target.value)}
          />
          <label className="form-label">내용</label>
          <input
            className="add-input"
            value={desc}
            onChange={(e) => setDesc(e.target.value)}
          />
        </div>
        <button className="add-button" onClick={onSubmitHandler}>
          추가하기
        </button>
      </div>
      <div className="list-container">
        <div>
          <h2 className="list-title">Working.. 🔥</h2>
          <div
            className="app-style" // 렌더링되도록 {workingLists} 변수를 받아옴
          >
            {workingLists}
          </div>
        </div>
        <div>
          <h2 className="list-title">Done..! 🎉</h2>
          <div className="app-style">{doneLists}</div>
        </div>
      </div>
    </div>
  );
};

// child component인 List를 설정
function List(props) {
  return (
    // 컴포넌트로 이러한 사각형 형태의 카드를 만들어준다.
    <div className="square-style">
      <h2 // props를 이용해 부모 conponent에서 title을 받아와서 이부분에 렌더링한다.
      >
        {props.todoList.title}
      </h2>
      <div>{props.todoList.desc}</div>
      <div className="button-set">
        <button
          className="todo-delete-button button"
          // props로 handleDelete값을 받아오면서 key를 함수로 넘겨준다.
          onClick={() => {
            props.handleDelete(props.todoList.key);
          }}
        >
          삭제
        </button>
        <button
          className="todo-complete-button button"
          onClick={() => {
            props.handleDone(props.todoList.key);
          }}
          // isDone 값에 따라 버튼의 글자가 바뀌도록 삼항연산자를 설정하고 렌더링해준다.
          // {조건문 ? true인 경우 : false인 경우}
        >
          {props.todoList.isDone === true ? "취소" : "완료"}
        </button>
      </div>
    </div>
  );
}

export default App;

 

주요한 기능 구현

1. deleteTodoListHandler

 - 개요
     각 객체를 식별해주는 key 값을 비교하여 해당되지 않는 객체만으로 새로운 배열을 복사하는 식으로 구현하였다.

 - point

  • 객체를 식별하는 문제:
     - id값이 아닌 key값을 새로 설정하여서 혼선이 없도록하였다. "삭제하기"를 클릭하는 경우에는 key값이 함수로 전달되도록 'props.todoList.key' 를 통해 props로 받아온 key를 전달해준다.
  • filter의 사용 :
     - filter는 callback 함수 조건에 맞는 배열 요소로 새로운 배열을 반환해준다.
 todoLists.filter((todoList) => todoList.key !== key)

         - 위의 구문을 해석하자면 todoLists라는 배열의 filter메소드를 적용한다. 배열 내 요소를 todoList로 설정해주고 todoList의 key 값(todoList.key)이 함수에서 받아온 key값과 같지 않은 요소들로 새로운 배열을 만들어준다.

 

2. onChangeHandler

 - 개요

    객체를 식별해주는 key값을 확인하고 그 값을 가지고 있는 객체의 속성 중 isDone 값을 변경하도록 만들어야한다.
 - point

  • key값을 가진 객체를 식별하는 문제
    • key값을 가지고 있는 배열 요소의 index를 구한다.
      findIndex를 사용하여 key값을 가지고 있는 요소의 index를 구할수 있다.
    • copyTodo[index]와 같이 값을 입력해줌으로써 배열 요소에 접근할 수 있다.
  • 기존의 있는 배열이 아닌 함수에서 내려주는 key값을 가진 객체의 속성 중 isDone값을 변경해주는 문제
const copyTodo = [...todoLists];
  • 기존에 있던 배열의 속성을 변경해주면 불변성문제 때문에 react에서 속성이 변한것을 감지하지 못하고 렌더링을 하지 않는다.
  • 그렇기때문에 copyTodo를 생성해줌으로써 주소값을 변경해주는 것이다.
  • if 함수를 사용하여 copyTodo[index].isDone의 값이 true 혹은 false인 경우에 따라 변경되는 값이 다르도록 설정하였다.
copyTodo[index] = { ...copyTodo[index], isDone: false };
  • copyTodo[index]을 새로 정의해준다. 
  • { ...copyTodo[index], isDone: false } 
    copyTodo[index]의 모든 속성을 복사해주되, isDone의 값은 false로 변경해준다.
setTodoLists(copyTodo);
  • state를 통해 새로 정의된 배열을 update해준다.

3. id가 아닌 객체를 식별해주는 uuid 설정

  • length로 id값을 주고 식별하는 경우에 순서가 같으면 식별되지 않는 문제가 발생한다.
  • 그렇기 때문에 length가 아닌 다른 고유값을 부여하는 식별자가 필요하다.
  • 국제기구에서 표준으로 정한 범용고유식별자(UUID)를 사용하였다.
  • 16 옥텟(128비트)의 수로 32개의 16진수로 표현 - 36개의 문자로 이루어져있으며 중복가능성이 거의 없다.
  • 사용법은 블로그를 통해 확인

리액트 id에 고유값 주기

https://velog.io/@xpmxf4/%EC%95%84%EC%9D%B4%EB%94%94%EC%97%90-%EA%B3%A0%EC%9C%A0-%EA%B0%92-%EC%A3%BC%EA%B8%B0

 

아이디에 고유 값 주기

이 주제로 블로깅을 하게 된 이유

velog.io

 

추가 공부

전개구문은 무엇인가?

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Spread_syntax

 

전개 구문 - JavaScript | MDN

전개 구문을 사용하면 배열이나 문자열과 같이 반복 가능한 문자를 0개 이상의 인수 (함수로 호출할 경우) 또는 요소 (배열 리터럴의 경우)로 확장하여, 0개 이상의 키-값의 쌍으로 객체로 확장시

developer.mozilla.org

삼항연산자 사용방법

https://velog.io/@zwon111/react-%EC%A1%B0%EA%B1%B4%EB%B6%80-%EC%97%B0%EC%82%B0%EC%9E%90%EC%82%BC%ED%95%AD%EC%97%B0%EC%82%B0%EC%9E%90

 

react 조건부 연산자_삼항연산자

JSX 내부의 자바스크립트 표현식에서 if 문을 사용할 수는 없다.하지만, 조건에 따라 다른 내용을 렌더링해야 할때는JSX 밖에서 if 문을 사용하여 사전에 값을 설정한다.{ } 안에서 조건부 연산자\_

velog.io

 

객체 배열에서 값변경해주기

https://developer-talk.tistory.com/246

 

[React]객체 배열에서 값 변경

이번 포스팅에서는 React에서 객체의 배열을 가지는 state 값을 변경하는 방법을 소개합니다. 다음과 같이 객체의 배열(objArray)을 가지는 state가 있다고 가정합니다. state = { objArray: [ { code: "", name: ""

developer-talk.tistory.com

 

'Hanghae99' 카테고리의 다른 글

221129 TIL 07 문서 객체 모델 1  (0) 2022.12.01
221130 TIL 팀별 과제 정리  (0) 2022.12.01
221128 TIL React 입문주차 3  (0) 2022.11.29
221126 TIL React 입문 주차 2  (0) 2022.11.28
221125 TIL React 입문 주차 1  (0) 2022.11.25