이전에 작업했던 todoList는 react만 이용해서 새로고침을 하면 데이터가 초기화됐다.
기존에 학습했던 react와 최근 스터디에서 spring boot를 통한 CRUD를 학습했다.
이를 활용해서 todoList와 DB를 spring boot를 통해 연결해서 데이터가 계속 저장되게 할 계획이다.
DB에 table을 생성하고 table의 어트리뷰트는 todoId, checkBox, todoContent, todoDate로 할 것이다.
DB에 저장된 데이터를 불러와 화면에 보여주는 작업을 수행할 것이다.
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> {
}
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;
}
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;
}
계층 간 데이터를 이동시키기 위한 객체로, DB와 직접적으로 연결된 Entity 객체의 변경을 피하기 위해서 사용한다. Entity를 파라미터로 받게 되면 엔티티에서 정해진 형태로 사용해야 하는데 Dto를 통해서 필요한 것만 가져와서 파라미터로 전달할 수 있다.
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;
}
}
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();
}
}
Front와 연결하기 전에 api가 정상적으로 동작하는지 Talend API를 사용해서 확인한다.

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

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 (...);
}
useEffect를 통해 화면이 최초 렌더링 될 때 불러온다.axios.get을 통해 api를 호출하고 api를 통해 받은 데이터를 setTodo를 통해 todo를 불러온다.