React/개념

투두리스트 구현 1

spring_sunshine 2022. 9. 23. 10:49

 

 

[React] 간단한 투두리스트 만들기

리액트를 다루는 기술 10장 일정 관리 웹 애플리케이션 만들기를 보면서 만든 투두리스트 앱입니다. 할 일 추가, 체크, 삭제 기능에 수정하기 기능을 덧붙였습니다.

velog.io

TodoTemplate, TodoInsert, TodoListItem, TodoList, TodoEdit 로 이루어져 있는 투두리스트를 만들 것이다.

컴포넌트 구조
TodoEdit 팝업창 추가

 


기능 구현

우선 TodoTemplate 안에 입력창, 할일 목록들을 넣었다. JSX 형식으로 표현하면 이런 느낌이다.

<TodoTemplate> //앱을 이루는 컨테이너 박스
   <ToDoInsert /> //할 일 입력창
   <ToDoList> //할 일 목록(ul)
      <ToDoListItem /> //할 일 (li)
   </ToDoList>
   <ToDoEdit /> //수정하기 창(팝업창이라서 대충 빼놓음)
</TodoTemplate>

 

일정 항목에서 필요한 것은 텍스트(내용), 체크박스, 삭제버튼, 수정버튼이다.

// id, text, checked 세가지의 key를 가진 객체로 구성하자
{
	id: 1,
    text: '리액트 기초',
    checked: true,
}

  


TodoInsert

- 사용자의 할일을 입력받는 란, 추가버튼

 

form 태그

  • 하나의 입력란과 제출버튼으로 구성된 간단한 양식은 <form> <input /> <button /> </ form> 으로 구성된다.
  • 사용자의 입력을 React가 인지하고 통제할 수 있어야 한다. 이때 React의 useState()을 사용하여 상태를 추가해주고, 해당 상태값을 <input>의 value 속성에 설정해준다.
  • 입력값이 바뀔때마다 발생하는 변경(Change) 이벤트를 처리하기 위한 handleChange() 함수를 <input>의 onChange 속성에 설정해준다.
  • 제출버튼을 클릭하면 form에서 submit 이벤트가 발생한다. 해당 이벤트를 처리하기 위한 handleSubmit() 함수를 <form>의 onSubmit 속성에 설정해준다.
  • 양식을 제출할 때는 브라우저에서 새로 고침이 일어나는데, SPA에서는 필요없는 동작이므로 handleSubmit() 함수 안에서 event.preventDefault() 로 해당 동작을 방지한다. 
  • 중복 제출 방지를 위해 사용자가 제출버튼을 클릭하자마자 제출버튼을 비활성화 시켰다가, 이벤트 처리가 완료됐을 때 제출버튼을 다시 활성화시키는 게 안전하다.
    • 버튼 활성화 여부를 관리하는 disabled 상태를 추가하고, <button>의 disabled 속성에 설정해준다.
    • 그리고 handleSubmit() 함수에서 disabled 값을 맨 처음에는 true로 변경했다가 맨 마지막에는 false로 변경한다.
    • 마지막으로 제출 이벤트 처리시 발생하는 지연을 시뮬레이션 하기 위해 handleSubmit() 내에서 1초 지연을 발생시킨다.
const [disabled, setDisabled] = useState(false);
...
const handleSubmit = async (event) => {
	setDisabled(true);
    event.preventDefault();
    await new Promise((r) => setTimeOut(r,1000));
    alert(`변경된 패스워드: ${password}`);
    setDisabled(false);
}
...
return (
	...
    <button type='submit' disabled={disabled}> 비번 변경 </button>
    ...
)

 

 

React로 양식(form) UI 구현하기

Engineering Blog by Dale Seo

www.daleseo.com


Promise

  • new Promise(...)로 Promise 객체를 새로 만든다. 생성자는 특별한 함수(화살표 함수 형태) 하나를 인자로 받는다. 이 특별한 함수를 공식 문서에서는 executor라고 부른다. new Promise((resolve, reject) => { 비동기 작업 })
  • executor의 첫번째 인수는 resolve, 두번째 인수는 reject 이다. resolve를 호출하게 되면 비동기 작업이 성공했다는 뜻이고, reject를 호출하게 되면 비동기 작업이 실패했다는 뜻이다.
  • Promise의 특징은 new Promise(...) 하는 순간 여기에 할당된 비동기 작업이 바로 시작된다는 것이다. 작업이 끝난 이후의 동작은 then 메소드와 catch 메소드로 지정해준다.
function startAsync(age) {
  return new Promise((resolve, reject) => {
    if (age > 20) resolve(`${age} success`);    
    else reject(new Error(`${age} is not over 20`));
  });
}

setTimeout(() => {
  const promise1 = startAsync(25);
  promise1
    .then((value) => {
      console.log(value);
    })
    .catch((error) => {
      console.error(error);
    });
  const promise2 = startAsync(15);
  promise2
    .then((value) => {
      console.log(value);
    })
    .catch((error) => {
      console.error(error);
    });
}, 1000);

async / await

  • async 함수는 Promise와 굉장히 밀접한 연관을 갖고 있다. 기존에 작성한 executor로부터 몇 가지 규칙만 적용하면 new Promise(...)를 리턴하는 함수를 쉽게 async 함수로 변환할 수 있다.
  • 함수에 async 키워드를 붙이고, new Promise... 부분을 없애고 executor 본문 내용만 남긴다.
  • resolve(value); 를 return value; 로 변경한다.
  • reject(new Error(..)); 를 throw new Error(...); 로 수정한다.
async function startAsync(age) {
  if (age > 20) return `${age} success`;
  else throw new Error(`${age} is not over 20`);
}

setTimeout(() => {
  const promise1 = startAsync(25);
  promise1
    .then((value) => {
      console.log(value);
    })
    .catch((error) => {
      console.error(error);
    });
  const promise2 = startAsync(15);
  promise2
    .then((value) => {
      console.log(value);
    })
    .catch((error) => {
      console.error(error);
    });
}, 1000);

→ async 함수의 리턴 값은 무조건 Promise 이다!

  • await는 Promise가 끝날 때까지 기다리는 함수이고, 반드시 async 함수에서만 사용할 수 있다.
  • await를 활용하여 기존 함수 작업의 느낌을 살릴 수 있다.
  • await는 Promise가 완료될 때까지 기다리므로, setTimeoutPromise의 executor에서 resolve 함수가 호출될 때까지 기다린다. 그동안 startAsyncJobs()의 진행은 멈춰있다.
  • await는 Promise가 resolve한 값을 내놓는다. 
function setTimeoutPromise(ms) {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve(), ms);
  });
}

async function startAsync(age) {
  if (age > 20) return `${age} success`;
  else throw new Error(`${age} is not over 20`);
}

async function startAsyncJobs() {
  await setTimeoutPromise(1000);
  const promise1 = startAsync(25);
  try {
    const value = await promise1;
    console.log(value);
  } catch (e) {
    console.error(e);
  }
  const promise2 = startAsync(15);
  try {
    const value = await promise2;
    console.log(value);
  } catch (e) {
    console.error(e);
  }
}

startAsyncJobs();