[Front & Back] TodoList 심화 작업

뽕칠이·2024년 7월 20일

TodoList

이전에 작업했던 todoList는 react만 이용해서 새로고침을 하면 데이터가 초기화됐다.

기존에 학습했던 react와 최근 스터디에서 spring boot를 통한 CRUD를 학습했다.

이를 활용해서 todoList와 DB를 spring boot를 통해 연결해서 데이터가 계속 저장되게 할 계획이다.


Create & Read

DB에 table을 생성하고 table의 어트리뷰트는 todoId, checkBox, todoContent, todoDate로 할 것이다.

DB에 저장된 데이터를 불러와 화면에 보여주는 작업을 수행할 것이다.

Repository 생성

  • TodoRepository.java
package com.example.demo.Repository;

import com.example.demo.Entity.Todo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface TodoRepository extends JpaRepository<Todo, Long> {
}

Table 생성

  • TodoEntity.java
package com.example.demo.Entity;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;

import java.util.Date;

@Entity
@Table(name = "Todo")
@Data
public class Todo {
    @Id
    @Column(name = "TodoId")
    int Todoid;

    @Column(name = "checkBox")
    Boolean checkBox;

    @Column(name = "TodoContent")
    String TodoContent;

    @Column(name = "TodoDate")
    Date TodoDate;
}

Dto 생성

  • TodoDto.java
package com.example.demo.Dto;

import lombok.Getter;
import lombok.Setter;

import java.util.Date;

@Getter
@Setter
public class TodoDto {
    int Todoid;
    Boolean checkBox;
    String TodoContent;
    Date TodoDate;
}

Dto 사용 이유

계층 간 데이터를 이동시키기 위한 객체로, DB와 직접적으로 연결된 Entity 객체의 변경을 피하기 위해서 사용한다. Entity를 파라미터로 받게 되면 엔티티에서 정해진 형태로 사용해야 하는데 Dto를 통해서 필요한 것만 가져와서 파라미터로 전달할 수 있다.

Service 생성

  • TodoService.java
package com.example.demo.service;

import com.example.demo.Dto.TodoDto;
import java.util.ArrayList;
import java.util.List;
import com.example.demo.Entity.Todo;
import com.example.demo.Repository.TodoRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.awt.*;

@Service
@RequiredArgsConstructor
public class TodoService {
    private final TodoRepository todoRepository;
    // 추가
    public void saveTodo(TodoDto todoDto){
        Todo t = new Todo();
        t.setTodoid(todoDto.getTodoid());
        t.setCheckBox(todoDto.getCheckBox());
        t.setTodoContent(todoDto.getTodoContent());
        t.setTodoDate(todoDto.getTodoDate());
        todoRepository.save(t);
    }
    
    // 읽기
    public List<Todo> loadTodo() {
        List<Todo> tl = todoRepository.findAll();
        return tl;
    }
}

Controller 생성

  • TodoController.java
package com.example.demo.controller;

import com.example.demo.Dto.TodoDto;
import com.example.demo.Entity.Todo;
import com.example.demo.service.TodoService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequiredArgsConstructor
@CrossOrigin(origins = "http://localhost:5173")
@RequestMapping("/todo")
public class TodoContorller {
    private final TodoService todoService;

    @PostMapping("/add")
    public void addTodo(@RequestBody TodoDto todoDto){
        todoService.saveTodo(todoDto);
    }

    @GetMapping("/read")
    public List<Todo> readTodoId(){
        return todoService.loadTodo();
    }
}

Test

Front와 연결하기 전에 api가 정상적으로 동작하는지 Talend API를 사용해서 확인한다.

성공적으로 동작한다.


DB로 데이터 전송하기

  • App.jsx
/* eslint-disable no-undef */
/* eslint-disable no-useless-catch */
import { useEffect, useRef, useState } from 'react'
import TodoDate from './TodoDate'
import TodoList from './TodoList'
import TodoWrite from './TodoWrite'
import axios from 'axios';
import './App.css'

function App() {
  const [todo, setTodo] = useState([{
    "todoid": "",
    "checkBox": "",
    "todoContent": "",
    "todoDate": ""
  }]);
  const idRef = useRef(todo.length);

  const onCreate = (todoContent) => {
    const newData = {
      todoid: idRef.current,
      checkBox: false,
      todoContent,
      todoDate: new Date().getTime()
    };
  
    const addData = async () => {
      try {
        await axios.post("http://localhost:8080/todo/add", newData);
        setTodo([...todo, newData]);

      }catch(error){
        console.log("Error");
      }
    }
    addData();
  };

  return (
    <div className='App'>
      <TodoDate />
      <TodoWrite onCreate={onCreate}/>
      <TodoList todo={todo} onUpdate={onUpdate} onDelete={onDelete}/>
      <button onClick={() => {console.log(todo);
      }}>button</button>
    </div>
  )
}

export default App
  • TodoWrite.jsx
import { useRef, useState } from "react";
import "./TodoWrite.css"

const TodoWrite = ({ onCreate }) => {
    const [todoContent, setContent] = useState("");
    const inputRef = useRef();
    const onChangeContent = (event) => {
        setContent(event.target.value);
    }
    const onSubmit = () => {
        if (!todoContent) {
            inputRef.current.focus();
            return;
        }
        onCreate(todoContent);
        setContent("");
    }

    return(
        <div className="TodoWrite">
            <h3>새로운 Todo 작성하기 🖋️</h3>
            <div className="WriteContent">
                <input
                    ref={inputRef}
                    value={todoContent}
                    onChange={onChangeContent}
                    placeholder="새로운 Todo..."/>
                <button onClick={onSubmit}>추가</button>
            </div>
        </div>
    )
}

export default TodoWrite;
  1. TodoWrite에서 todoContent 내용을 담아 onCreate 함수에 전달한다.
  2. onCreatenewData 객체에 담는다.
  3. addData 함수는 비동기 처리한 axios.post를 통해 newData 객체를 전달한다.
  4. setTodo를 통해 기존 todo 뒤에 추가한다.
    -> 여기서 내가 기존에 배운 ... 연산자 문법은 setTodo([newData, ...todo])형태였는데 이 형태로 작성하게 되면 todolist 상단에 추가되고 리렌더링 되면 기본키인 todoid 순서로 정렬되어 하단으로 내려간다. 이를 해결하기 위해서 찾아보던 중, ... 연산자를 setTodo([...todo, newData])와 같이 앞에 작성하고 추가할 변수를 뒤에 작성하게 되면 하단에 추가된다.
    -> 여기 또 든 생각이 api를 통해 newData를 전달해서 추가하고 리렌더링만 해주면 되는 것 아닌가? 왜 setTodo를 통해 리렌더링을 해주는 것인가? useEffect를 통해 todo를 의존성 배열에 넣어주면 되지 않을까?

기존 setTodo([newData, ...todo])

수정한 setTodo([...todo, newData])

DB에서 데이터 불러오기

  • App.jsx
function App() {
    useEffect(() => {
      const fetchData = async () => {
        try {
          const response = await axios.get("http://localhost:8080/todo/read");
          setTodo(response.data);
          idRef.current = response.data.length;
        } catch (error) {
          console.error("Error fetching data:", error);
        }
      };
      fetchData();
    }, []);
  
  	return (...);
}
  1. useEffect를 통해 화면이 최초 렌더링 될 때 불러온다.
  2. axios.get을 통해 api를 호출하고 api를 통해 받은 데이터를 setTodo를 통해 todo를 불러온다.

0개의 댓글