Spring Boot・React・MysqlでTodoアプリを作ってみた
エンジニアになって半年くらい経つのですが、
案件先でプログラミングを全くやっておらず、先行きが見えないので少しでもアピールポイントを増やせるようにコーディングスキルを磨きたいと思い、アウトプットを兼ねてSpring BootとReactを使ってTodoアプリを作ってみました。
※デザインは付けておらずSpring BootとReactとの連携を試してみただけなのでレベル感が底辺なので予めご了承ください。
現在のスキル感
- エンジニア歴半年
- shellコマンドを叩くだけのお仕事
- 研修でJava・SpringMVCを学んだ
- 独学でJavaScript・Reactの勉強中
使用したもの
- Spring Boot 2.7.8(Java 11)
- maven
- Mybatis
- React 18.2.0
- react-router-dom
- axios
- Mysql
主な機能
- Todo
- 一覧表示
- 編集
- 削除
- 登録
完成品
テーブルを定義
Spring Boot側
- Model
- データの定義
@Getter
@Setter
public class Todo {
private int id;
private String name;
private String description;
private boolean deleteFlag;
}
- Controller
- React側から来たリクエストに応じてServiceへ処理を渡す
@RestController
@CrossOrigin("http://localhost:3000")
@RequestMapping("/todo")
public class TodoController {
@Autowired
TodoService todoService;
@GetMapping("/list")
List<Todo> getTodos() {
return todoService.findAll();
}
@PostMapping("/create")
int createTodo(@RequestBody Todo newTodo) {
return todoService.createTodo(newTodo);
}
@GetMapping("/edit/{id}")
Todo editTodo(@PathVariable Long id) {
return todoService.findById(id);
}
@PutMapping("/edit/{id}")
int saveTodo(@RequestBody Todo updateTodo, @PathVariable Long id) {
return todoService.saveTodo(updateTodo);
}
@DeleteMapping("delete/{id}")
int deleteTodo(@PathVariable Long id) {
return todoService.deleteById(id);
}
}
- Service
- Mapperに処理を渡す
@Service
@Transactional
public class TodoService {
@Autowired
TodoMapper todoMapper;
public List<Todo> findAll() {
return todoMapper.findAll();
}
public int createTodo(Todo newTodo) {
return todoMapper.createTodo(newTodo);
}
public Todo findById(Long id) {
return todoMapper.findById(id);
}
public int saveTodo(Todo updateTodo) {
return todoMapper.saveTodo(updateTodo);
}
public int deleteById(Long id) {
return todoMapper.deleteTodo(id);
}
}
- DB操作
- Serviceから渡された情報をもとにDB操作を行う
Mybatisを使っているので、「TodoMapper.xml」にSQLを記述します
@Mapper
public interface TodoMapper {
List<Todo> findAll();
int createTodo(Todo newTodo);
Todo findById(Long id);
int saveTodo(Todo updateTodo);
int deleteTodo(Long id);
}
※「application.properties」でmodelへのパスを指定してMybatisに認識させないとエラーになる
mybatis.type-aliases-package=com.example.apiapp.model
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTDMapper3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.apiapp.repository.TodoMapper">
<select id="findAll" resultType="Todo">
SELECT
*
FROM
todo
</select>
<insert id="createTodo" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
INSERT INTO todo
(name, description)
VALUES
(#{name}, #{description})
</insert>
<select id="findById" resultType="Todo">
SELECT
*
FROM
todo
WHERE
id = #{id}
</select>
<update id="saveTodo">
UPDATE
todo
SET
name = #{name},
description = #{description}
WHERE
id = #{id}
</update>
<delete id="deleteTodo">
DELETE FROM
todo
WHERE
id = #{id}
</delete>
</mapper>
React側
基本的にaxiosでhttpメソッドを投げて処理を行う
- メインのページ
const Home = () => {
const [todos, setTodos] = useState([]);
const { id } = useParams();
useEffect(() => {
loadTodos()
}, [])
const loadTodos = async () => {
const result = await axios.get("http://localhost:8080/todo/list")
setTodos(result.data)
}
const deleteTodo = async (id) => {
const res = window.confirm('本当に削除しますか?')
if (res) {
await axios.delete(`http://localhost:8080/todo/delete/${id}`)
loadTodos()
}
}
return (
<div>
<table border="1">
<thead>
<tr>
<th>id</th>
<th>名前</th>
<th>説明</th>
<th>アクション</th>
</tr>
</thead>
<tbody>
{
todos.map((todo, index) => (
<tr>
<th key={index}>{index + 1}</th>
<td>{todo.name}</td>
<td>{todo.description}</td>
<td>
<Link to={`edittodo/${todo.id}`}>編集</Link>
<button onClick={() => deleteTodo(todo.id)}>削除</button>
</td>
</tr>
))
}
</tbody>
</table>
<Link to={"/addtodo"}>追加</Link>
</div>
)
}
export default Home
- 追加ページ
const AddTodo = () => {
let navigate = useNavigate();
const [todo, setTodo] = useState({
name: "",
description: ""
})
const { name, description } = todo
const onInputChange = (e) => {
setTodo({...todo, [e.target.name]: e.target.value})
}
const onSubmit = async (e) => {
e.preventDefault()
await axios.post("http://localhost:8080/todo/create", todo)
navigate("/")
}
return (
<div>
<h1>Todo追加</h1>
<from onSubmit={(e) => onSubmit(e)}>
<div>
<label htmlFor="Name">名前</label>
<input
type="text"
name='name'
value={name}
onChange={(e) => onInputChange(e)}
/>
</div>
<br />
<div>
<label htmlFor="Name">説明</label>
<textarea
type="text"
name='description'
value={description}
onChange={(e) => onInputChange(e)}
/>
</div>
<br />
<div>
<button type='submit' onClick={onSubmit}>登録</button>
<Link to="/">キャンセル</Link>
</div>
</from>
</div>
)
}
export default AddTodo
- 編集ページ
const EditTodo = () => {
let navigate = useNavigate();
const { id } = useParams();
const [todo, setTodo] = useState({
name: "",
description: ""
})
const { name, description } = todo
const onInputChange = (e) => {
setTodo({...todo, [e.target.name]: e.target.value})
}
useEffect(() => {
loadTodo()
}, [])
const onSubmit = async (e) => {
e.preventDefault();
await axios.put(`http://localhost:8080/todo/edit/${id}`, todo);
navigate("/");
}
const loadTodo = async () => {
const result = await axios.get(`http://localhost:8080/todo/edit/${id}`);
setTodo(result.data);
}
return (
<div>
<h1>Todo編集</h1>
<from onSubmit={(e) => onSubmit(e)}>
<div>
<label htmlFor="Name">名前</label>
<input
type="text"
name='name'
value={name}
onChange={(e) => onInputChange(e)}
/>
</div>
<br />
<div>
<label htmlFor="Name">説明</label>
<textarea
type="text"
name='description'
value={description}
onChange={(e) => onInputChange(e)}
/>
</div>
<br />
<div>
<button type='submit' onClick={onSubmit}>更新</button>
<Link to="/">キャンセル</Link>
</div>
</from>
</div>
)
}
export default EditTodo
- ルーティング
function App() {
return (
<div className='App'>
<Router>
<Routes>
<Route exact path='/' element={<Home />} />
<Route exact path='/addtodo' element={<AddTodo />} />
<Route exact path='/edittodo/:id' element={<EditTodo />} />
</Routes>
</Router>
</div>
);
}
export default App;
これから
簡単ではありますがSpring Boot・React・Mysqlの連携を学ぶことができました。
udemyでtailwindcssを学んだのでReactに組み込んでスタイルを整えて、もう少し規模の大きいアプリケーションも作ってみたいと思います。
今回Spring Bootを使いましたが実務だと違う構成の方が多いんですかね?
技術や作文にまだ慣れていない部分が多く、至らぬ点が多かったとは思いますが、最後まで読んでいただき、ありがとうございました!