[TIL] 240523 (React 가계부 수정, 삭제 기능 구현)

·2024년 5월 23일

TIL

목록 보기
50/268
post-thumbnail

🥞 오늘 한 일

  • 리액트 숙련 개인과제
    • 지출 수정화면 UI 구현 (수정, 삭제 기능 구현)
    • 추가 및 수정 시 유효성 검사 구현

🍽️ 문제 해결

리액트 숙련 개인과제

지출 수정화면 삭제 기능

STEP 1

수정한 데이터를 다시 홈에 넣는 방법을 고민했다. 현재는 context API도, Redux도 쓰면 안 되는데 어떻게 값을 넘겨줄 수 있을까... detail 페이지로 넘겨준 방식대로 navigate를 사용해 넘겨주면 되지 않을까? 하는 가설을 세웠다. 테스트해본 결과, 잘 넘어가는 것을 알 수 있었다.

// Detail.jsx

navigate(`/`, { state: { deletedExpenses: { deletedExpenses } } });

STEP 2

일단 Detail.jsx에서 필터링을 통해 미리 삭제된 배열을 Home.jsx에 넘기고, 아래 코드처럼 적어주니 삭제가 잘 되었다.

// Home.jsx

useEffect(() => {
  if (location.state.deletedExpenses) {
    const { updatedDeletedExpenses } = location.state.deletedExpenses;
    setExpenses(updatedDeletedExpenses);
  }
}, []);

그러나 새로고침을 했을 때 삭제된 그대로라는 문제가 있었다. updatedDeleteExpenses를 초기화해줄 필요가 있을 것 같다는 점을 느끼고는 있었다. (이게 사실 어찌보면 답이었다. 방법을 몰랐을 뿐...)
튜터님께서도 react-router-dom 5버전을 사용해 이 문제를 내셨는데, 6버전을 사용할 때의 방법에 대해서는 어렵게 느낄 수 있다고 하셨다. (다행이다...)
튜터님이 보셨을 때 내가 전에 짠 로직, 그러니까 Datail.jsx에서 삭제할 expense의 id만 Home.jsx에 넘겨주는 방식으로 변경해보았다.

STEP 3

// Home.jsx

useEffect(() => {
  if (location.state.deleteExpenseId) {
    const { deleteExpenseId } = location.state.deleteExpenseId;
    const deletedExpenses = expenses.filter((expense) => {
      return deleteExpenseId !== expense.id;
    });
    setExpenses(deletedExpenses);
    location.state.deleteExpenseId = "";
  }
}, []);

여기서 두 가지의 문제가 또 발생했다.

  • 처음에 yarn dev로 재시작했을 때 deletedExpenseId가 없어서 오류가 난다.
    => if (location.state !== null) 조건문으로 감싸서 해결했다.
  • 계속 한개씩만 삭제되고, 이미 삭제한 expense가 돌아온다.
    => 리렌더링할 때마다 기존 expenses가 재선언되기 때문.

결국 튜터님과 오랜 시간 고민하다가, 로직을 다시 STEP2에서 사용한 로직과 유사하게 바꾼 다음 코드 한 줄을 추가해 해결했다.

STEP 4

// Home.jsx

navigate(location.pathname, { state: null, replace: true });

이 코드에 대해 설명을 하자면, navigate를 사용해 다시 현재 페이지로 가는데, state, replace를 사용해 히스토리를 없애주는 것이다. 그러면 새로고침을 했을 때 기존의 expenses 파일들이 다시 나타난다.
아래는 각 파일의 삭제 기능에 해당하는 부분의 코드이다.

// Detail.jsx

const deleteExpenseHandler = (deleteExpenseId) => {
  const deletedExpenses = expenses.filter((expense) => {
    return deleteExpenseId !== expense.id;
  });
  return deletedExpenses;
};

const handleDelete = () => {
  confirm("정말로 삭제하시겠습니까?");
  const deleteExpenseId = expense.id;
  const deletedExpenses = deleteExpenseHandler(deleteExpenseId);
  navigate(`/`, { state: { deletedExpenses: { deletedExpenses } } });
};
// Home.jsx

useEffect(() => {
  if (location.state !== null) {
    if (location.state.deletedExpenses) {
      const { deletedExpenses } = location.state.deletedExpenses;

      setExpenses(deletedExpenses);
      navigate(location.pathname, { state: null, replace: true });
    }
  }
}, []);

(수정 코드는 임의로 삭제했다.)

지출 수정화면 수정 기능

삭제 기능과 비슷한 점이 많아서 크게 어려운 점은 없었는데, 삭제에서 쓰던 filter()를 그대로 수정에서도 쓰는 바람에 오랫동안 애먹었다. map()을 써야한다는 점은 투두 리스트 때도 배웠던 점인데... 그래서 해당 사실을 깨닫고 기쁨과 허무함의 감정이 동시에 들었다. 아래는 해당 코드가 들어있는 함수이다.

// Detail.jsx

const updateExpenseHandler = (updateExpense) => {
  const updatedExpenses = expenses.map((expense) => {
    if (updateExpense.id !== expense.id) {
      return expense;
    } else if (updateExpense.id === expense.id) {
      return updateExpense;
    }
  });
  return updatedExpenses;
};

간단한 문제들

  • 동기분이 가계부 과제에서 월 별로 필터링하는 방법을 물어보셔서 코드를 보니 비교할 데이터 타입이 달라 number 타입으로 변경하면 된다는 점을 알려드렸다.

🍪 배운 것

  • react router dom의 5 버전과 6 버전은 확연히 다르다.

🍴 돌아보기

  • 확실히 튜터님의 도움을 받는 것, 그리고 정리를 하면서 차근차근 해나가는 것이 도움이 많이 되는 것 같다. 조급한 마음에 막 그냥 하다가 정신적으로 힘들었던 어제의 나와는 다른 하루를 보낸 것 같아서 기쁘다.
  • props-drilling의 단점에 대해 (이 과제를 내주신 튜터님의 의도 그 이상으로) 아주 뼈저리게 느끼고 있다... context API나 Redux를 사용하지 않으니 수정 삭제 기능 하나를 위해 정말 많은 번거로움이 생긴다. 내일부터는 context API와 Redux로 리팩토링을 시작하게 되기 때문에, 그 편리함을 깨달으면서 조금 더 즐겁게 할 수 있지 않을까 싶다!

🍳 내일 할 일

  • 리액트 숙련 개인과제
    • “context” 로 브랜치 생성 및 이동
    • props drilling으로 불편하게 관리하던 state를 context api 로 리팩터링
profile
웹 프론트엔드 개발자

4개의 댓글

comment-user-thumbnail
2024년 5월 23일

솔님 최고 👏👏👏
저는... 로컬스토리지에 데이터를 저장한 순간부터 뭔가 잘못되어가고 있는 것 같습니다 ^.^
수정 기능이 정말 문제네요 미치겠다.
오늘도 정말 수고 많으셨고 내일도 화이팅입니다 !!

1개의 답글
comment-user-thumbnail
2024년 5월 24일

무슨 싸움을 하셨던 겁니까 T.T

1개의 답글