Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: #7 Todo 리스트 구현 #13

Merged
merged 5 commits into from
Aug 24, 2023
Merged

feat: #7 Todo 리스트 구현 #13

merged 5 commits into from
Aug 24, 2023

Conversation

salmontaker
Copy link
Contributor

@salmontaker salmontaker commented Aug 24, 2023

Description

Todo 페이지의 기능을 구현하였습니다.

@5unk3n 님과 함께 LiveShare로 페어 프로그래밍을 하였습니다!

Changes

조회

// src/components/TodoList/types.ts

export interface TodoType {
  id: number
  todo: string
  isCompleted: boolean
  userId: number
}

Todo의 type을 위와 같이 정의하였고,

// src/apis/todo.ts

export const getTodosRequest = async (): Promise<TodoType[]> => {
  const { data } = await AuthInstance.get('/todos')
  return data
}
// src/components/TodoList/TodoList.tsx

function TodoList() {
  const [todos, setTodos] = useState<TodoType[]>([])

  ...

  const getTodo = () => {
    getTodosRequest()
      .then((todos) => setTodos(todos))
      .catch((e: AxiosError) => alert(e.message))
  }

  useEffect(() => {
    getTodo()
  }, [])

  ...

}

get 요청 성공시, 서버로부터 받아온 값을 todos에 useState<TodoType[]>([])로 저장하였습니다.

// src/components/TodoList/TodoList.tsx

<ul>
{todos.map((todo) => (
    <TodoItem
      key={todo.id}
      id={todo.id}
      todo={todo.todo}
      isCompleted={todo.isCompleted}
      updateTodo={updateTodo}
      deleteTodo={deleteTodo}
     />
  ))}
</ul>

또한, map()을 사용하여 Todo 리스트의 내용을 렌더링 하였습니다.

  • TODO 추가

추가

// src/apis/todo.ts

export const createTodoRequest = async (todo: string): Promise<TodoType> => {
  const { data } = await AuthInstance.post('/todos', { todo })
  return data
}
// src/components/TodoList/TodoList.tsx

function TodoList() {
  const [todos, setTodos] = useState<TodoType[]>([])
  const [newTodo, setNewTodo] = useState('')

  const createTodo = (todo: string) => {
    createTodoRequest(todo)
      .then((createdTodo) => {
        setTodos((prevTodos) => [...prevTodos, createdTodo])
        setNewTodo('')
      })
      .catch((e: AxiosError) => alert(e))
  }

  ...

}
// src/components/TodoList/TodoList.tsx

<div>
  <label htmlFor="addTodo">
    <input
      id="addTodo"
      value={newTodo}
      onChange={(e) => setNewTodo(e.target.value)}
      data-testid="new-todo-input"
    />
  </label>
  <button onClick={() => createTodo(newTodo)} type="button" data-testid="new-todo-add-button">
    추가
  </button>
</div>

input의 onChange value를 newTodo에 useState('')로 저장하였고, 추가 버튼을 누르면 서버로 post 요청을 보냅니다.

post 요청 성공시, 해당 값을 Spread를 사용하여 todos에 추가 하도록 하였습니다.

  • TODO 수정

수정

// src/components/TodoItem/TodoItem.tsx

function TodoItem({ id, todo, isCompleted, updateTodo, deleteTodo }: TodoItemProps) {

  ...

  const [todoModify, setTodoModify] = useState('')
  const [isEditMode, setIsEditMode] = useState(false)

  ...

  // 수정 버튼을 눌렀을 때
  const onEditButtonClicked = () => {
    setTodoModify(todo)
    setIsEditMode(true)
  }

  ...

  return (
    <li>
      <label>
        <input type="checkbox" checked={todoCheck} onChange={onTodoCheckChanged} />
        {isEditMode ? (
          <input
            data-testid="modify-input"
            type="text"
            onChange={onTodoModifyChanged}
            value={todoModify}
          />
        ) : (
          <span>{todo}</span>
        )}
      </label>
      {isEditMode ? (
        <>
          <button data-testid="submit-button" onClick={onSubmitButtonClicked}>
            제출
          </button>
          <button data-testid="cancel-button" onClick={onCancelButtonClicked}>
            취소
          </button>
        </>
      ) : (
        <>
          <button data-testid="modify-button" onClick={onEditButtonClicked}>
            수정
          </button>
          <button data-testid="delete-button" onClick={onDeleteButtonClicked}>
            삭제
          </button>
        </>
      )}
    </li>
  )
}

isEditMode 상태에 따라 렌더링 요소를 결정하고, 수정 버튼을 통해 토글합니다.

// src/components/TodoItem/TodoItem.tsx

// 제출 버튼을 눌렀을 때
const onSubmitButtonClicked = () => {
  updateTodo(id, todoModify, isCompleted)
  setIsEditMode(false)
}

제출 버튼 클릭 시 TodoList 컴포넌트에서 prop으로 받은 updateTodo함수를 실행합니다.

// src/components/TodoList/TodoList.tsx

const updateTodo = (id: number, todo: string, isCompleted: boolean) => {
  updateTodoRequest(id, todo, isCompleted)
    .then((updatedTodo) =>
      setTodos((prevTodos) =>
        prevTodos.map((prevTodo) => (prevTodo.id === id ? updatedTodo : prevTodo))
      )
    )
    .catch((e: AxiosError) => alert(e.message))
}
// src/apis/todo.ts

export const updateTodoRequest = async (
  id: number,
  todo: string,
  isCompleted: boolean
): Promise<TodoType> => {
  const { data } = await AuthInstance.put(`/todos/${id}`, { todo, isCompleted })
  return data
}

put 요청 성공시 todos에서 같은 id를 가진 todo를 변경하도록 했습니다.

  • TODO 삭제

삭제

// src/components/TodoItem/TodoItem.tsx

function TodoItem({ id, todo, isCompleted, updateTodo, deleteTodo }: TodoItemProps) {

  ...

  // 삭제 버튼을 눌렀을 때
  const onDeleteButtonClicked = () => {
    deleteTodo(id)
  }

  ...

  <button data-testid="delete-button" onClick={onDeleteButtonClicked}>
  삭제
  </button>

  ...

}

삭제 버튼 클릭 시 TodoList에서 prop으로 받은 deleteTodo 함수를 실행합니다.

// src/components/TodoList/TodoList.tsx

const deleteTodo = (id: number) => {
  deleteTodoRequest(id)
    .then(() => setTodos((prevTodos) => prevTodos.filter((prevTodo) => prevTodo.id !== id)))
    .catch((e: AxiosError) => alert(e.message))
}
// src/apis/todo.ts

export const deleteTodoRequest = async (id: number) => {
  return AuthInstance.delete(`/todos/${id}`)
}

delete 요청 성공 시 todos에서 .filter() 메서드를 사용해 같은 id를 가진 todo를 삭제하도록 했습니다.

  • TODO 체크

체크

// src/components/TodoList/TodoList.tsx

function TodoItem({ id, todo, isCompleted, updateTodo, deleteTodo }: TodoItemProps) {
  const [todoCheck, setTodoCheck] = useState(isCompleted)

  ...

  // TODO의 체크박스가 변경 되었을 때
  const onTodoCheckChanged = () => {
    updateTodo(id, todo, !todoCheck)
    setTodoCheck(!todoCheck)
  }

  ...

  <label>
    <input type="checkbox" checked={todoCheck} onChange={onTodoCheckChanged} />
    {isEditMode ? (
      <input
        data-testid="modify-input"
        type="text"
        onChange={onTodoModifyChanged}
        value={todoModify}
      />
    ) : (
      <span>{todo}</span>
    )}
  </label>
}

todo의 완료 여부는 todoCheck 상태로 결정됩니다.

TODO 수정 기능과 같이 updateTodo함수를 사용하며 todoCheck를 not 연산자로 변경해 요청합니다.

Copy link
Contributor

@SeungrokYoon SeungrokYoon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 함께 화면공유를 통해 개발에 간접적으로 참여한 컴포넌트라 로직이 잘 이해가 되었습니다.

고생하셨습니다!

src/components/TodoItem/TodoItem.tsx Show resolved Hide resolved
src/components/TodoItem/types.ts Show resolved Hide resolved
src/components/TodoList/TodoList.tsx Show resolved Hide resolved
src/components/TodoItem/TodoItem.tsx Show resolved Hide resolved
@5unk3n 5unk3n merged commit 008af7b into main Aug 24, 2023
@5unk3n 5unk3n deleted the feat/#7-todo branch August 24, 2023 11:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feat 기능 관련
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Todo 리스트 구현
4 participants