티스토리 뷰

2025.01.21 - [웹 개발 공부하기] - [01.20] 리액트를 이용한 Task 생성 앱 만들기

2025.01.27 - [웹 개발 공부하기] - [01.21] 리액트를 이용한 Task 생성 앱 만들기 2

💥 Modal Edit 컴포넌트 생성, 기능 생성, 스타일하기

const EditModal = () => {
    const dispatch = useTypedDispatch()
    const editingState = useTypedSelector(state => state.modal)
    const [data, setData] = useState(editingState)

    const handleCloseButton = () => {
        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 handleAuthorChange = (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(),
                logMessage: `일 삭제하기: ${editingState.task.taskName}`,
                logAuthor: "User",
                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={handleCloseButton}/>
                </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={handleAuthorChange}
                />
                <div className={buttons}>
                    <button className={updateButton} onClick={handleUpdate}>
                        일 수정하기
                    </button>
                    <button className={deleteButton} onClick={handleDelete}>
                        일 삭제하기
                    </button>
                </div>
            </div>
        </div>
    )
}

export default EditModal

 - EditModal.tsx

 

다른 컴포넌트와 크게 다를 것 없이 생성했기 때문에 큰 어려움은 없었다.

수정or삭제 하고자 하는 Task를 누르면 해당 Modal창이 뜨게끔 만들었다.

제목, 설명, 생성한 사람을 수정할 수 있었고, 제목에 적혀있는 Task 1 이 Modal창 가장 맨 위의 타이틀과 동일하게 나오게 만들었다.

그래서 Task1 을 Task 13으로 만들면 위의 Title도 Task 13으로 나타나게 된다.

💥 LoggerModal 컴포넌트 생성, LogItem 컴포넌트 생성&스타일 하기

type TLoggerModalProps = {
    setIsLoggerOpen: React.Dispatch<React.SetStateAction<boolean>>
}

const LoggerModal: FC<TLoggerModalProps> = ({
    setIsLoggerOpen
}) => {

    const logs = useTypedSelector(state => state.logger.logArray)
    
    return (
        <div className={wrapper}>
            <div className={moadlWindow}>
                <div className={header}>
                    <div className={title}>활동 기록</div>
                    <FiX className={closeButton} onClick={() => setIsLoggerOpen(false)} />
                </div>
                <div className={body}>
                    {logs.map((log) => (
                        <LogItem key={log.logId} logItem={log}/>
                    ))}
                </div>
            </div>
        </div>
    )
}

export default LoggerModal

 - LoggerModal.tsx

 

LoggerModal 컴포넌트 역시 다른 파일과 크게 다르진 않다.

단, LogItem 부분을 보면 logitem을 배열의 객체 수만큼 렌더링 해주고있고, props로 내려주는 건 log데이터를 내려주고 있다.

type TLogItemProps = {
    logItem: ILogItem
}

const LogItem: FC<TLogItemProps> = ({
    logItem
}) => {
    const timeOffset = new Date(Date.now() - Number(logItem.logTimestamp))
    console.log('timeOffset : ', timeOffset)
    console.log('timeOffset : ', timeOffset.getMinutes())
    console.log('timeOffset : ', timeOffset.getSeconds())

    const showOffsetTime = `
    ${timeOffset.getMinutes() > 0 ? `${timeOffset.getMinutes()}m` : ""}
    ${timeOffset.getSeconds() > 0 ? `${timeOffset.getSeconds()}s ago` : ""}
    ${timeOffset.getSeconds() === 0 ? `just now` : ""}
    `

    return (
        <div className={logItemWrap}>
            <div className={author}>
                <BsFillPersonFill />
                {logItem.logAuthor}
            </div>
            <div className={message}>{logItem.logMessage}</div>
            <div className={date}>{showOffsetTime}</div>
        </div>
    )
}

export default LogItem

 - LogItem.tsx

 

LogItem의 코드를 보면 timeOffset 이라는 변수를 사용하는 걸 볼 수 있는데, 

보통 활동 로그를 보면 몇 초 전, 몇 분 전, 이렇게 시간 계산을 해주는 걸 볼 수 있다. 그걸 위한 변수라고 생각하면 된다.

console.log를 찍어놨기 때문에 간단히 보면서 설명하겠다.

첫 번째 log의 분, 초를 보면 된다.

두 번째 log는 timeOffset에서 나온 값의 분만 나타나게 만들었다.

그래서 0분의 0 을 나타내고 있고, 세 번째 log는 timeOffset의 초만 나타나게 하였다.

그래서 22초의 22를 나타내고 있다.

이걸 사용해서 showOffsetTime 변수를 만들었다.

즉, 분은 m을 붙여 나타내게 하고, 초는 s ago를 붙여 나타내게 했다.

하지만, s는 0초일 경우 0s가 아닌 지금! 이라는 뜻을 가진 just now 라는 글자가 나타나게 만들었다.

이렇게 0초를 나타내는 just now와 2초 전인 2s ago가 나타는 걸 볼 수 있다.

💥 게시판 삭제 기능 생성하기

게시판 삭제하기 버튼을 누르면, Active한 게시판을 삭제할 수 있게 할 것이다.

단, 게시판이 1개일 경우에 삭제버튼을 누르면 최소 게시판 개수는 한 개입니다. 라는 알림창이 뜨게 만들려고 한다.

deleteBoard: (state, {payload}: PayloadAction<TDeleteBoardAction>) => {
            state.boardArray = state.boardArray.filter(
                board => board.boardId !== payload.boardId
            )
}

 - boardsSlice.tsx

 

먼저, boardsSlice 파일에 deleteBoardAction을 만들어준다.

const handleDeleteBoard = () => {
    if(boards.length > 1) {
      dispatch(
        deleteBoard({boardId: getActiveBoard.boardId})
      )

      dispatch(
        addLog({
          logId: v4(),
          logMessage: `게시판 지우기: ${getActiveBoard.boardName}`,
          logAuthor: "User",
          logTimestamp: String(Date.now())
        })
      )

      const newIndexToSet = () => {
        const indexToBeDelete = boards.findIndex(
          board => board.boardId === activeBoardId
        )

        return indexToBeDelete === 0
        ? indexToBeDelete + 1
        : indexToBeDelete - 1
      }

      setActiveBoardId(boards[newIndexToSet()].boardId)
      
    } else {
      alert('최소 게시판 개수는 한 개입니다.')
    }
  }

 - App.tsx

 

그 다음, App.tsx 파일에 "이 게시판 삭제하기" 버튼을 눌렀을 때 일어날 이벤트를 만들어준다.

변수이름은 handleDeleteBoard 라고 만들어주었다.

 

게시판이 1개일 땐, 삭제가 되지 않고 알림창을 띄워주어야 한다.

그래서 boards의 length가 1보다 클 때만 삭제할 수 있게 유효성 체크 부분을 넣어주었다.

그럼 이렇게 게시판이 1개일 땐, "최소 게시판 개수는 한 개입니다." 라는 알림창이 뜨게 되고,

게시판이 2개 이상일 땐, 성공적으로 지울 수 있게 되고, dispatch를 통해 log를 남기게 만들어놨으니,

log도 정상적으로 "게시판 지우기" 라고 뜨는 걸 볼 수 있다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
글 보관함