TodoList - 할일 수정 및 삭제하기

개발냥이·2025년 4월 14일

데브코스

목록 보기
41/75
post-thumbnail

이번엔 Redux를 이용한 할일 수정 및 삭제를 해보고자 한다.

  1. 특정 데이터 가져오기 (useSelector)
  2. 기능 구현 (useDispatch)

순으로 진행해보겠다.


1. 특정 데이터 가져오기 (useSelector)

우선 모든 할일이 아닌 특정 할일을 수정 및 삭제하는 것이기 때문에
useSelector()를 사용해 특정 데이터를 가져와야 한다.

// EditModal.tsx
const editingState = useTypedSelector(v => v.modal);

//참고로 useTypedSelector는 미리 정의해둔 커스텀 훅을 사용

위의 코드는 특정 할일을 나타내는 modal을 가져온 로직이다.

// EditModal.tsx
const [data, setData] = useState(editingState);

그 이후 useState()훅을 사용해서 특정 할일에 이벤트가 발생했을 시에만
상태가 업데이트 되도록 한다.


2. 기능 구현 (useDispatch)

기능할 구현은 크게 2가지이다.
1. 제목 , 설명 , 사람부분 각각 수정
2. 할일 삭제하기

우선 특정 기능을 동작시켜주는 slice부분을 수정한다.

// BoardSlice.ts
    setModalActive: (state, {payload}: PayloadAction<boolean>) => {
      state.modalActive = payload;
    },

사용자가 특정 할일을 클릭했을 때, 모달창을 열거나 닫기 위한 상태를 업데이트한다.

// BoardSlice.ts
    updateTask: (state, {payload}: PayloadAction<TAddTaskAction>) => {
      state.boardArray = state.boardArray.map(v =>
        v.boardId === payload.boardId
          ? {
              ...v,
              lists: v.lists.map(v =>
                v.listId === payload.listId
                  ? {
                      ...v,
                      tasks: v.tasks.map(v =>
                        v.taskId === payload.task.taskId ? payload.task : v,
                      ),
                    }
                  : v,
              ),
            }
          : v,
      );
    },

이 후 boardArray -> lists -> tasks 를 순회하면서 수정된 것이 있으면
새로운 payload를 전달 그렇지 않다면 기존 배열을 얇은 복사로 반환해준다.

// BoardSlice.ts
    deleteTask: (state, {payload}: PayloadAction<TDeleteTaskAction>) => {
      state.boardArray = state.boardArray.map(board =>
        board.boardId === payload.boardId
          ? {
              ...board,
              lists: board.lists.map(list =>
                list.listId === payload.listId
                  ? {
                      ...list,
                      tasks: list.tasks.filter(
                        task => task.taskId !== payload.taskId,
                      ),
                    }
                  : list,
              ),
            }
          : board,
      );
    },

삭제부분도 사실상 같지만 살짝 다른 부분이 있다면 task(할일)부분에 filter 메서드를 사용해서
삭제할 부분만 삭제하고 나머진 남겨두도록 하였다.

이제 해당 부분을 화면에 나타내는 EditModal 컴포넌트에 해당 부분을 useDispatch()로 받아와 사용해주면 된다.

전체 코드 보기
import {FiX} from 'react-icons/fi';
import {useTypedDispatch, useTypedSelector} from '../../hooks/redux';
import {ChangeEvent, useState} from 'react';
import {
  deleteTask,
  setModalActive,
  updateTask,
} from '../../store/slices/boardSlice';
import {addLog} from '../../store/slices/loggerSlice';
import {v4} from 'uuid';
import {
  buttons,
  closeButton,
  deleteButton,
  header,
  input,
  moadlWindow,
  title,
  updateButton,
  wrapper,
} from './EditModal.css';

const EditModal = () => {
  const dispatch = useTypedDispatch();
  const editingState = useTypedSelector(v => v.modal);
  const [data, setData] = useState(editingState);
  const handelCloseBtn = () => {
    dispatch(setModalActive(false));
  };

  const handleNameChange = (e: ChangeEvent<HTMLInputElement>) => {
    setData({
      ...data,
      task: {
        ...data.task,
        taskName: e.target.value,
      },
    });
  };

  const handleDescriptionChange = (e: ChangeEvent<HTMLInputElement>) => {
    setData({
      ...data,
      task: {
        ...data.task,
        taskDescription: e.target.value,
      },
    });
  };

  const handleOwnerChange = (e: ChangeEvent<HTMLInputElement>) => {
    setData({
      ...data,
      task: {
        ...data.task,
        taskOwner: e.target.value,
      },
    });
  };

  const handleUpdate = () => {
    dispatch(
      updateTask({
        boardId: editingState.boardId,
        listId: editingState.listId,
        task: data.task,
      }),
    );

    dispatch(
      addLog({
        logId: v4(),
        logMessage: `일 수정하기: ${editingState.task.taskName}`,
        logAuthor: 'User',
        logTimestamp: String(Date.now()),
      }),
    );

    dispatch(setModalActive(false));
  };

  const handleDelete = () => {
    dispatch(
      deleteTask({
        boardId: editingState.boardId,
        listId: editingState.listId,
        taskId: editingState.task.taskId,
      }),
    );

    dispatch(
      addLog({
        logId: v4(),
        logAuthor: 'user',
        logMessage: `일 삭제하기 ${editingState.task.taskName}`,
        logTimestamp: String(Date.now()),
      }),
    );

    dispatch(setModalActive(false));
  };

  return (
    <div className={wrapper}>
      <div className={moadlWindow}>
        <div className={header}>
          <div className={title}>{editingState.task.taskName}</div>
          <FiX className={closeButton} onClick={handelCloseBtn} />
        </div>
        <div className={title}>제목</div>
        <input
          className={input}
          type="text"
          value={data.task.taskName}
          onChange={handleNameChange}
        />
        <div className={title}>설명</div>
        <input
          className={input}
          type="text"
          value={data.task.taskDescription}
          onChange={handleDescriptionChange}
        />
        <div className={title}>생성한 사람</div>
        <input
          className={input}
          type="text"
          value={data.task.taskOwner}
          onChange={handleOwnerChange}
        />

        <div className={buttons}>
          <button onClick={handleUpdate} className={updateButton}>
            일 수정하기
          </button>
          <button onClick={handleDelete} className={deleteButton}>
            일 삭제하기
          </button>
        </div>
      </div>
    </div>
  );
};

export default EditModal;

중요 부분만 보자면

  const dispatch = useTypedDispatch();

이걸로 가져와서 해당하는 부분을 업데이트 해주면 된다.

만약 설명을 업데이트 해준다고 한다면

  const handleDescriptionChange = (e: ChangeEvent<HTMLInputElement>) => {
  setData({
    ...data,
    task: {
      ...data.task,
      taskDescription: e.target.value,
    },
  });
};

이런식으로 해당하는 부분을 업데이트 해주면 된다.


후기

Redux를 사용하기 전에는 특정 컴포넌트에 화면 및 기능을 구현했지만 Redux를 사용하면서 useSelector()와 useDispatch()를 이용해 컴포넌트를 관리해주다 보니 헷갈리고 어려운 거 같다 ㅠㅠ

profile
웹 개발자가 되고픈

0개의 댓글