[전체 목차]
1. 스프링 부트 프로젝트 생성
2. 프로젝트 열기, 파일 구조 형성
3. 모델이 되는 객체, 객체 생성자 정의
4. Repository 개발
5. Sevice 개발
6. 서버 단위 테스트
7. Controller 개발
8. DB(H2) 연결
9. 통합테스트
10. JPA에서 Spring Data JPA로 전환
1. 스프링 부트 프로젝트 생성
2. 프로젝트 열기, 파일 구조 형성
이 단계에서는 Domain, Controller, Repository, Service의 패키지를 만들어준다.
3. 모델이 되는 객체, 객체 생성자 정의 - Domain
//Domain - 해당 패키지는 객체를 생성하는 곳입니다.
package todolist.todolist_spring.domain;
public class Todo {
private Long id; // 할 일의 고유 ID
private String title; // 할 일의 제목
private String description; // 할 일의 설명 (옵션)
private boolean completed; // 완료 여부
public Todo(Long id, String title, String description) {
this.id = id;
this.title = title;
this.description = description;
this.completed = false; // 처음 생성할 때는 미완료 상태로 설정
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public boolean isCompleted() {
return completed;
}
public void setCompleted(boolean completed) {
this.completed = completed;
}
}
4. Repository 개발
respository는 최종적으로 DB와 소통하는 부분이다.
package todolist.todolist_spring.repository;
import todolist.todolist_spring.domain.Todo;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class MemoryTodoRepository implements TodoRepository {
private List<Todo> todoList = new ArrayList<>(); //
private Long nextId = 0L; // 할 일 ID를 생성할 때 사용
@Override
public Todo save(Todo todo) {
todo.setId(++nextId); //ID 생성해서 객체에 저장
todoList.add(todo); //todo 객체를 리스트에 저장
return todo;
}
@Override
public Optional<Todo> findById(Long id) {
return todoList.stream()
.filter(todo -> todo.getId().equals(id))
.findFirst();
}
@Override
public List<Todo> findAll() {
return new ArrayList<>(todoList);
//todoList 리스트를 복사하여 반환
}
@Override
public void deleteById(Long id) {
todoList.removeIf(todo -> todo.getId().equals(id));
//removeIf 괄호 내부의 값이 true라면 해당 값을 삭제하는 것이다.
//따라서 todo값을 넣고 그 값에서 id가 입력된 id와 같다면 삭제를 하는 구문이다.
}
}
5. Sevice 개발
Controller와 Repository의 중간 다리 역할을 한다.
package todolist.todolist_spring.service;
import todolist.todolist_spring.domain.Todo;
import todolist.todolist_spring.repository.TodoRepository;
import java.util.List;
import java.util.Optional;
public class TodoService {
private final TodoRepository todoRepository;
public TodoService(TodoRepository todoRepository) {
this.todoRepository = todoRepository;
}
public Todo addTodo(String title, String description) {
Todo todo = new Todo(null, title, description);
return todoRepository.save(todo);
}
public List<Todo> getAllTodos() {
return todoRepository.findAll();
}
public Todo completeTodo(Long id) {
Optional<Todo> optionalTodo = todoRepository.findById(id);
if (optionalTodo.isPresent()) { //해당 아이디를 가진 투두가 있다면 완료 조치
Todo todo = optionalTodo.get();
todo.setCompleted(true);
return todoRepository.save(todo);
} else {
return null;
}
}
public void deleteTodoById(Long id) {
todoRepository.deleteById(id);
}
}
6. 서버 단위 테스트
package todolist.todolist_spring.service;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import todolist.todolist_spring.domain.Todo;
import todolist.todolist_spring.repository.MemoryTodoRepository;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
class TodoServiceTest {
TodoService todoService;
MemoryTodoRepository memoryTodoRepository;
@BeforeEach
public void beforeEach() {
memoryTodoRepository = new MemoryTodoRepository();
todoService = new TodoService(memoryTodoRepository);
}
@AfterEach
public void afterEach() {
memoryTodoRepository.clearToDoList();
}
@Test
void addTodo_Test() {
//given - 무엇인가 주어졌고
//when - 그걸 받아서 실행했을 때
Todo savedTodo = todoService.addTodo("스프링 프로젝트", "스프링이용해서 프론트와 연동하기");
//then - 어떤 결과가 나와야한다.
assertEquals("스프링 프로젝트", savedTodo.getTitle());
}
@Test
void getAllTodos_Test() {
//given - 무엇인가 주어졌고
Todo todo1 = new Todo(null, "스프링 프로젝트", "스프링으로 투두리스트 만들기!");
Todo todo2 = new Todo(null, "ios 프로젝트", "ios로 투두리스트 만들기!");
memoryTodoRepository.save(todo1);
memoryTodoRepository.save(todo2);
//when - 그걸 받아서 실행했을 때
//then - 어떤 결과가 나와야한다.
assertThat(todoService.getAllTodos().size()).isEqualTo(2);
}
@Test
void completeTodo_Test() {
//given - 무엇인가 주어졌고
Todo todo = new Todo(null, "스프링 프로젝트", "스프링으로 투두리스트 만들기!");
memoryTodoRepository.save(todo);
//when - 그걸 받아서 실행했을 때
todoService.completeTodo(todo.getId());
//then - 어떤 결과가 나와야한다.
// System.out.println(todo.isCompleted());
assertThat(todo.isCompleted()).isTrue();
}
@Test
void deleteTodoById_Test() {
//given - 무엇인가 주어졌고
Todo todo = new Todo(null, "스프링 프로젝트", "스프링으로 투두리스트 만들기!");
memoryTodoRepository.save(todo);
System.out.println("삭제 전:" + todoService.getAllTodos());
//when - 그걸 받아서 실행했을 때
todoService.deleteTodoById(todo.getId());
System.out.println("삭제 후:" + todoService.getAllTodos());
//then - 어떤 결과가 나와야한다.
assertThat(todoService.getAllTodos().size()).isEqualTo(0);
}
}
7. Controller 개발
iOS앱에서 특정한 요청을 보내면 그 요청의 형태에 따라 데이터를 처리한다.
package todolist.todolist_spring.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import todolist.todolist_spring.domain.Todo;
import todolist.todolist_spring.repository.TodoRepository;
import todolist.todolist_spring.service.TodoService;
import java.util.List;
//@RequestBody: HTTP 요청의 **본문(body)**에 포함된 JSON 데이터를 자바 객체로 변환하여 메서드 파라미터로 전달합니다.
//@PathVariable: URL 경로에 있는 값을 메서드 파라미터로 전달합니다. 예를 들어, /{id}와 같이 경로에 포함된 변수 값을 추출합니다.
//@RequestParam: URL의 쿼리 파라미터를 메서드 파라미터로 전달합니다. 예를 들어, ?key=value와 같은 쿼리 파라미터 값을 가져옵니다.
@RestController //앱과 연동하기에 RESTAPI
@RequestMapping("/todo")
public class TodoController {
private final TodoService todoService;
@Autowired
public TodoController(TodoService todoService) {
this.todoService = todoService;
}
@PostMapping
public Todo crateTodo(@RequestBody Todo todo){
return todoService.addTodo(todo.getTitle(), todo.getDescription());
}
@GetMapping
public List<Todo> getAllTodos(){
return todoService.getAllTodos();
}
@PutMapping("/{id}/complete") //아이디도 변경 됨.
public Todo completeTodo(@PathVariable Long id) {
return todoService.completeTodo(id);
}
@DeleteMapping("/{id}")
public void deleteTodo(@PathVariable Long id) {
todoService.deleteTodoById(id);
}
}
8. DB(H2) 연결
(1) h2 터미널로 연결하고 테이블 생성
CREATE TABLE TODO (
id BIGINT GENERATED BY DEFAULT AS IDENTITY,
title VARCHAR(255),
description VARCHAR(255),
completed BOOLEAN,
PRIMARY KEY(id)
);
(2) 스프링 부트에 JPA설정 추가하기
spring.application.name=todolist-spring
spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
(3) 엔티티 매핑 - 데이터베이스의 정보를 Spring 객체와 연결해주는 것
@Entity
public class Todo {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
// id 값은 데이터 베이스가 할당해준다고 JPA에게 전달하는 코드
private Long id; // 할 일의 고유 ID
private String title; // 할 일의 제목
private String description; // 할 일의 설명 (옵션)
private boolean completed; // 완료 여부
...
}
(4) JPA 레포지토리 만들기
package todolist.todolist_spring.repository;
import jakarta.persistence.EntityManager;
import todolist.todolist_spring.domain.Todo;
import java.util.List;
import java.util.Optional;
public class JpaTodoRepository implements TodoRepository{
private final EntityManager entityManager;
public JpaTodoRepository(EntityManager entityManager){
this.entityManager = entityManager;
}
@Override
public Todo save(Todo todo) {
entityManager.persist(todo);
return todo;
}
@Override
public Optional<Todo> findById(Long id) {
Todo todo = entityManager.find(Todo.class, id);
return Optional.ofNullable(todo);
}
@Override
public List<Todo> findAll() {
return entityManager.createQuery("select t from Todo t", Todo.class).getResultList();
}
@Override
public void deleteById(Long id) {
Todo todo = entityManager.find(Todo.class, id);
if (todo != null) {
entityManager.remove(todo);
} else {
throw new IllegalArgumentException("Todo with id " + id + " not found");
}
}
}
(5) 트랜잭션 추가 - 서비스가 데이터베이스와의 작업을 신뢰할 수 있게 하고, 모든 작업이 올바르게 처리되도록 도와주는 과정
@Transactional
public class TodoService {
private final TodoRepository todoRepository;
...
}
(6) 빈 자동 연결 없애고 빈 수동 연결
config 파일 만들어서 코드 추가
package todolist.todolist_spring;
import jakarta.persistence.EntityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import todolist.todolist_spring.repository.JpaTodoRepository;
import todolist.todolist_spring.repository.TodoRepository;
import todolist.todolist_spring.service.TodoService;
@Configuration
public class SpringConfig {
private EntityManager entityManager;
public SpringConfig(EntityManager entityManager) {
this.entityManager = entityManager;
}
@Bean
public TodoService todoService(){
return new TodoService(todoRepository());
}
@Bean
public TodoRepository todoRepository(){
return new JpaTodoRepository(entityManager);
}
}
9. 통합테스트
package todolist.todolist_spring.service;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Commit;
import org.springframework.transaction.annotation.Transactional;
import todolist.todolist_spring.domain.Todo;
import todolist.todolist_spring.repository.TodoRepository;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
@SpringBootTest //통합 테스트를 위한 어노테이션 (해당 어노테이션을 붙이면 실제 DB와 연결한다.)
@Transactional
public class TodoServiceIntegrationTest {
@Autowired
TodoService todoService;
@Autowired
TodoRepository todoRepository;
@Test
@Commit //실제로 DB에 저장하고 싶을 때 사용
void addTodo_Test() {
//given - 무엇인가 주어졌고
//when - 그걸 받아서 실행했을 때
Todo savedTodo = todoService.addTodo("스프링 프로젝트", "스프링이용해서 프론트와 연동하기");
//then - 어떤 결과가 나와야한다.
assertEquals("스프링 프로젝트", savedTodo.getTitle());
}
@Test
@Commit
void getAllTodos_Test() {
//given - 무엇인가 주어졌고
Todo todo1 = new Todo(null, "스프링 프로젝트", "스프링으로 투두리스트 만들기!");
Todo todo2 = new Todo(null, "ios 프로젝트", "ios로 투두리스트 만들기!");
todoRepository.save(todo1);
todoRepository.save(todo2);
//when - 그걸 받아서 실행했을 때
//then - 어떤 결과가 나와야한다.
assertThat(todoService.getAllTodos().size()).isEqualTo(2);
}
@Test
@Commit
void completeTodo_Test() {
//given - 무엇인가 주어졌고
Todo todo = new Todo(null, "스프링 프로젝트1", "스프링으로 투두리스트 만들기!1");
todoRepository.save(todo);
//when - 그걸 받아서 실행했을 때
todoService.completeTodo(todo.getId());
//then - 어떤 결과가 나와야한다.
// System.out.println(todo.isCompleted());
assertThat(todo.isCompleted()).isTrue();
}
@Test
@Commit
void deleteTodoById_Test() {
//given - 무엇인가 주어졌고
Todo todo = new Todo(null, "스프링 프로젝트", "스프링으로 투두리스트 만들기!");
todoRepository.save(todo);
//when - 그걸 받아서 실행했을 때
todoService.deleteTodoById(todo.getId());
//then - 어떤 결과가 나와야한다.
assertThat(todoService.getAllTodos().size()).isEqualTo(0);
}
}
10. JPA에서 Spring Data JPA로 전환
(1) 스프링 데이터 JPA 레포지토리 만들기
package todolist.todolist_spring.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import todolist.todolist_spring.domain.Todo;
public interface SpringDataJpaTodoRepository extends JpaRepository<Todo, Long>, TodoRepository{
}
(2) 스프링 설정 변경하기
package todolist.todolist_spring;
import jakarta.persistence.EntityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import todolist.todolist_spring.repository.JpaTodoRepository;
import todolist.todolist_spring.repository.TodoRepository;
import todolist.todolist_spring.service.TodoService;
@Configuration
public class SpringConfig {
private final TodoRepository todoRepository;
@Autowired
public SpringConfig(TodoRepository todoRepository){
this.todoRepository = todoRepository;
}
@Bean
public TodoService todoService(){
return new TodoService(todoRepository);
}
}
'Server > Server-Project' 카테고리의 다른 글
Spring을 이용해 DB와 연동된 ToDoList iOS 앱 제작(3) - 앱 제작 (0) | 2024.08.29 |
---|---|
Spring을 이용해 DB와 연동된 ToDoList iOS 앱 제작(1) 전체 구조 (0) | 2024.08.29 |