[TIL] 240516 (React My Todo List 수업 기반 과제 개선 완료)

·2024년 5월 16일

TIL

목록 보기
43/268
post-thumbnail

🥞 오늘 한 일

  • 리액트 입문주차 개인과제
    • 수업 기반 과제 개선
      • 새 Todo 추가할 때 객체 내 요소 단축속성명으로 변경
      • TodoForm에 title, body 상태 및 상태 변경 함수 이동
      • TodoList 반복되는 부분 한 번에 할 수 있도록 변경
      • trim() 사용하여 input에 빈 값만 넣어줬을 때도 실행 안 되도록 변경
    • 해설 기반 과제 개선
      • 1, 2강 수강 완료
      • Layout 컴포넌트 제작
      • placeholder 내용 넣기
      • input 비제어 컴포넌트로 변경
      • 각 todo의 id 값 new Date().getTime() => Date.now() => crypto.randomUUID() 순으로 변경
      • todo => todos (단수 => 복수) 변경
  • 자바스크립트 예제 30제
    • 4주차: 콜백함수, Promise, async/await 완료
  • 알고리즘 코드카타
    • 숫자 짝꿍

리액트 입문주차 개인과제

스탠다드반 수업 기반 과제 개선

아직 해설을 보지 못 했기에 스탠다드반 수업 때 들은 걸 토대로 수정을 해보았다.

새 Todo 추가할 때 객체 내 요소 단축속성명으로 변경

좀 더 간단하게 단축속성명으로 변경했다.

const newTodo = {
  id: new Date().getTime(),
  title,
  body,
  isDone: false,
};

TodoForm에 title, body 상태 및 상태 변경 함수 이동

굳이 부모 컴포넌트에 있을 필요가 없고 TodoForm에서만 쓰이기 때문에 TodoForm 내부로 이동시켜주었다.

const TodoForm = ({ todo, setTodo }) => {
  const addTodoHandler = (e) => {
    e.preventDefault();
    if (!title || !body) {
      alert("제목과 내용을 모두 채워주세요.");
      return;
    }
    const newTodo = {
      id: new Date().getTime(),
      title,
      body,
      isDone: false,
    };
    setTodo([...todo, newTodo]);
    setTitle("");
    setBody("");
  };
  const [title, setTitle] = useState("");
  const [body, setBody] = useState("");
  return (
    <form onSubmit={addTodoHandler} className="todoForm" type="submit">
      <label>제목</label>
      <input
        type="text"
        value={title}
        onChange={(e) => {
          setTitle(e.target.value);
        }}
      />
      <label>내용</label>
      <input
        type="text"
        value={body}
        onChange={(e) => {
          setBody(e.target.value);
        }}
      />
      <Button>추가하기</Button>
    </form>
  );
};

TodoList 반복되는 부분 한 번에 할 수 있도록 변경

본래는 그냥 전체 코드를 한꺼번에 넣어주었는데, h2 내 이름, 필터링 요소, TodoItem 버튼 텍스트 제외하곤 반복되는 코드이기에 하나의 코드로 나타낼 수 있도록 변경해주었다.

const TodoList = ({ isDone, todo, deleteTodoHandler, toggleTodoHandler }) => {
  return (
    <>
      <h2>{isDone ? "Done" : "Working"}</h2>
      <ul>
        {todo
          .filter((todo) => {
            return todo.isDone === isDone;
          })
          .map(function (todo) {
            return (
              <TodoItem
                key={todo.id}
                todo={todo}
                deleteTodoHandler={deleteTodoHandler}
                toggleTodoHandler={toggleTodoHandler}
                text={isDone ? "취소" : "완료"}
              />
            );
          })}
      </ul>
    </>
  );
};

trim() 사용하여 input에 빈 값만 넣어줬을 때도 실행 안 되도록 변경

기존의 코드는 빈 값을 계속 넣어주면 코드가 실행되는 문제점이 있어 trim()을 통해 문제를 해결하였다.

if (!title.trim() || !body.trim()) {
  alert("제목과 내용을 모두 입력해주세요.");
  return;
}

해설 기반 과제 개선

Layout 컴포넌트 제작

해설 영상을 통해 Layout 컴포넌트를 따로 만들어주는 방법도 있다는 점을 알게되어 똑같이 제작해보았다.

const Layout = ({ children }) => {
  return <div className="wrap">{children}</div>;
};

export default Layout;

placeholder 내용 넣기

어찌 보면 기본적인 것인데 놓치고 있었던 부분. placeholder를 넣어주었다.

<label>제목</label>
<input
  type="text"
  placeholder="제목을 입력해주세요."
  name="title"
  />
<label>내용</label>
<input
  type="text"
  placeholder="내용을 입력해주세요."
  name="body"
  />

input 비제어 컴포넌트로 변경

제어/비제어 컴포넌트에 대해 배웠고 비제어 컴포넌트를 만드는 방법을 알게 되어 코드를 비제어 컴포넌트로 바꿔보았다.

const addTodoHandler = (e) => {
  e.preventDefault();
  const formData = new FormData(e.target);
  const title = formData.get("title");
  const body = formData.get("body");

  if (!title.trim() || !body.trim()) {
    alert("제목과 내용을 모두 입력해주세요.");
    return;
  }
  const newTodo = {
    id: crypto.randomUUID(),
    title,
    body,
    isDone: false,
  };
  setTodos((prevTodos) => [...prevTodos, newTodo]);

  e.target.reset();
};

각 todo의 id 값 new Date().getTime() => Date.now() => crypto.randomUUID() 순으로 변경

id를 스탠다드반 수업 때 튜터님이 추천하신 Date.now() 로 바꿨다가, 해설 영상에 나온 crypto.randomUUID()로 바꿨다. 여러가지 방법을 알게 됐는데 상황에 따라 적절히 사용하면 될 것 같다.

const newTodo = {
  id: crypto.randomUUID(),
  title,
  body,
  isDone: false,
};

todo => todos (단수 => 복수) 변경

단수, 복수를 제대로 써줘야한다는 점은 금일 여러 번 강조를 받았다. 안 그래도 코드를 짜면서 이 부분이 명확하지 않아 좋지 못하다고 생각했는데, 복수형으로 바꾸면서 더 필요성을 강하게 느꼈다.

const [todos, setTodos] = useState([...])

🍽️ 문제 해결

알고리즘 코드카타

숫자 짝꿍

문제

두 정수 X, Y의 임의의 자리에서 공통으로 나타나는 정수 k(0 ≤ k ≤ 9)들을 이용하여 만들 수 있는 가장 큰 정수를 두 수의 짝꿍이라 합니다(단, 공통으로 나타나는 정수 중 서로 짝지을 수 있는 숫자만 사용합니다). X, Y의 짝꿍이 존재하지 않으면, 짝꿍은 -1입니다. X, Y의 짝꿍이 0으로만 구성되어 있다면, 짝꿍은 0입니다.

예를 들어, X = 3403이고 Y = 13203이라면, X와 Y의 짝꿍은 X와 Y에서 공통으로 나타나는 3, 0, 3으로 만들 수 있는 가장 큰 정수인 330입니다. 다른 예시로 X = 5525이고 Y = 1255이면 X와 Y의 짝꿍은 X와 Y에서 공통으로 나타나는 2, 5, 5로 만들 수 있는 가장 큰 정수인 552입니다(X에는 5가 3개, Y에는 5가 2개 나타나므로 남는 5 한 개는 짝 지을 수 없습니다.)
두 정수 X, Y가 주어졌을 때, X, Y의 짝꿍을 return하는 solution 함수를 완성해주세요.

문제점

이 문제는 시간 초과가 관건인 문제였다. 코드 자체에 문제가 있는 것은 아니었지만, 테스트 케이스 5곳에서 시간 초과 때문에 실패해서, 시간 복잡도를 줄이는데 초점을 맞춰야 했다.
처음에 제출한 코드는 다음과 같다.

function solution(X, Y) {
    let result = [];
    [...X].forEach((item)=>{
        if (Y.includes(item)){
            result.push(item);
            Y = Y.replace(item, "");
        }
    })
    if (result.length === 0){
        return "-1";
    } else if (result.join("").replaceAll("0","")===""){
        return "0";
    } else {
        return result.sort((a,b)=>b-a).join("");
    }
}

알맞게 풀었지만, X와 Y의 길이가 최대 3,000,000이기에, 그만큼 길 경우 반복을 상당히 많이 해줘야하는 점에서 시간 복잡도가 미친듯이 올라간다. (X*Y 만큼 해줘야 한다.)
그래서 여러가지 방법을 시도해 보았지만, 시간 복잡도가 달라질만한 방법을 찾지는 못 했다. 그렇게 질문하기 페이지도 전전하다가, "그냥 한 번의 순회로 각 숫자들의 개수를 세는게 낫"다는 힌트를 발견하고, 그렇게 할 수 있는 방법을 모색해 보았다.
머리를 싸매다가 결국 구글링으로 힌트를 얻은 후, 구조를 이해하고 내 방식대로 작성했다...

해결

function solution(X, Y) {
    let arrX = [...X]
    let arrY = [...Y]
    let result = "";
    for (let i = 0; i< 10; i++){
        let lengthX = arrX.filter((num)=>Number(num)===i).length;
        let lengthY = arrY.filter((num)=>Number(num)===i).length;
        if (lengthX > 0 && lengthY > 0 ){
            result += String(i).repeat(Math.min(lengthX,lengthY));
        }
    }
    
    if (result.length === 0){
        return "-1";
    } else if (result.replaceAll("0","")===""){
        return "0";
    } else {
        return result.split("").sort((a,b)=>b-a).join("");
    }
}

반복문을 사용해, 각 숫자가 X 및 Y에 들어있는 숫자만큼을 가져온 후, 둘 중 하나라도 0개 이상이라면 둘 중 작은 숫자만큼 i를 result에 추가한다. (사실 이 조건문은 굳이 필요 없지만, 추가하면 시간 복잡도가 줄어들기에 넣었다.)
이 방식대로 하면 기존의 X\*Y 만큼의 시간 복잡도에서, X\*10 + Y\*10으로 확 줄어들 수 있다.
처음에 잡은 감을 과감하게 다시 생각할 수 있어야한다는 점을 깨달은 문제였다... 오랫동안 풀리지 않는다면 기존 코드에서 어떻게 해보려고 하기 보다는 과감하게 다시, 다르게 해보는 것. 그게 이런 경우에는 중요한 것 같다.

🍪 배운 것

Promise, async/await

자바스크립트 30제를 진행하면서 역시 Promise, async/await에서 어려움을 겪었는데 이번에 푼 문제가 앞으로도 도움이 될 것 같아 여기에 남겨두려고 한다.

Promise

function fetchAndPrintJson(url) {
  return fetch(url)
    .then((response) => {
			return response.json()
		})
    .then((data) => {
	      console.log(data);
    });
}

fetchAndPrintJson('https://jsonplaceholder.typicode.com/posts/1');

async/await

function fetchAndPrintJson(url) {
  return fetch(url)
    .then((response) => {
			return response.json()
		})
    .then((data) => {
	      console.log(data);
    });
}

fetchAndPrintJson('https://jsonplaceholder.typicode.com/posts/1');

스탠다드반 수업

  • id 넣기
    • uuid나 nanoid를 많이 쓰는데, 백엔드에서는 uuid, 프론트엔드에서는 nanoid를 많이 쓰는 편이다.
  • 함수명은 동사 또는 on(이벤트 핸들러 한정)로 시작하는 것이 범용적 코딩 컨벤션이다.
  • sweetalert2 라이브러리를 추천. 1은 6년 전에 업데이트가 끝났고 2는 5일 전에 업데이트 됐기에 2를 쓰는게 더 좋다.
  • 변수명 지을 때 단수, 복수 신경쓰자.
  • 최상위 컴포넌트보다는 가급적 중간 단계의 컨테이너 컴포넌트가 상태를 관리하는 것을 권장

그 외

  • 객체 {key: value}의 key가 변수를 참조하려면 대괄호 안에 넣으면 된다. ex) [key]

🍴 느낀 점

  • 내가 개인적으로 내 코드를 볼 때는 수정할 부분이 많이 안 보이는데, 수업 한 번 듣거나 해설 한 번 들으면 정말 많이 보이는게 신기하다. 이래서 혼자서 하는 것보다는 여러 사람의 도움을 받아 해나가는게 훨씬 성장하기 좋은 것 같다. 앞으로는 주어지는 수업이나 해설 말고도 많은 피드백을 요청해야겠다!
  • ZEP에 늦게까지 남아계신 분들을 보면서 자극을 받는다. 주어진 12시간 외의 시간도 적극적으로 사용해야겠다!

🍳 내일 할 일

  • 리액트 입문주차 개인과제
    • 개인과제 해설 기반 과제 개선
    • 과제 재제출
  • 스탠다드반 2nd 과제 진행
profile
웹 프론트엔드 개발자

0개의 댓글