컨트롤러와 퍼시스턴스 사이에서 비즈니스 로직을 수행한다.
컨트롤러는 HTTP와 연관이 깊고, 퍼시스턴스는 DB와 연관되어 있으나 서비스 레이어는 컨트롤러와 퍼시스턴스와 분리되어 있기 때문에 비즈니스 로직에 집중가능
service 패키지 생성하여 TodoService.java를 구현한다.
package com.example.demo.services;
import org.springframework.stereotype.Service;
@Service
public class TodoService {
public String testService(){
return "test service";
}
}
@Service
: 서비스 레이어임을 알려주는 어노테이션
TodoController.java 작성
package com.example.demo.controller;
// import 생략
@RestController
@RequestMapping("todo")
public class TodoController {
@Autowired
private TodoService service;
@GetMapping("/test")
public ResponseEntity<?> testTodo() {
String str = service.testService();
List<String> list = new ArrayList<>();
list.add(str);
ResponseDTO<String> response = ResponseDTO.<String>builder().data(list).build();
return ResponseEntity.ok().body(response);
}
}
@RestController 도 내부에 @Component 어노테이션을 가지고 있기 때문에 @Service, @RestController 모두 자바 빈이다. 자바 빈이기 때문에 스프링의 관리를 받으며, 스프링은 TodoController 오브젝트 생성 시 내부의 @Autowired를 확인한다. @Autowired가 자동으로 해당 빈을 찾아서 그 빈을 인스턴스의 멤버변수에 연결해준다.
테스트를 위해 http://localhost:8080/todo/test
로 요청을 보내면 ResponseDTO를 반환받을 수 있다.
{
"error": null,
"data": [
"test service"
]
}
DB와 애플리케이션을 연결하려면
위 과정을 개별 엔티티마다 반복하고, DAO 클래스를 작성한다.
이러한 반복 작업을 줄이기 위해 ORM 프레임워크(Hibernate etc)를 사용하게 되었고, JPA나 스프링 데이터 JPA와 같은 도구들이 개발되었다.
JPA는 자바에서 DB 접근, 저장, 관리에 필요한 스펙이다. 이러한 스펙 구현자를 JPA Provider라고 하고 대표적으로 Hibernate가 있다.
스프링 데이터 JPA는 JPA를 쉽게 사용할 수 있도록 스프링이 제공하는 인터페이스 (?)
H2는 In-Memory DB로 초기개발 시 유용하다
build.gradle에 h2를 디펜던시로 설정해두었기 때문에 스프링이 알아서 애플리케이션을 H2와 연결해줌
스프링 데이터 JPA 사용을 위한 spring-boot-starter-data-jpa 라이브러리도 포함해두었다
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'com.h2database:h2'
로그 확인하면 실행중임을 확인할 수 있음
JPA를 생성하고 JPA Provider로 Hibernate ORM을 사용한다. 데이터베이스로는 H2를 사용한다.
클래스 그 자체가 테이블을 정의함으로써 ORM이 엔티티를 확인한 후 어떤 테이블의 어떤 필드에 매핑하는지 알 수 식별할 수 있다.
이러한 DB 테이블 스키마 정보는 javax.persistance가 제공하는 JPA 관련 어노테이션 사용
TodoEntity.java (model 패키지 내부)
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
@Entity
@Table(name = "Todo")
public class TodoEntity {
@Id
@GeneratedValue(generator = "system-uuid")
@GenericGenerator(name = "system-uuid", strategy = "uuid")
private String id;
private String userId;
private String title;
private boolean done;
}
@Entity
: 자바 클래스를 엔티티로 지정한다.@Table
: 테이블 이름 지정하려면 name옵션 추가한다.@Id
: 기본키 지정@GeneratedValue
: 자동으로 id 생성@GenericGenerator
: Hibernate 제공 기본 Generator가 아닌 다른 Generator 사용TodoRepository.java
package com.example.demo.persistence;
import com.example.demo.model.TodoEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface TodoRepository extends JpaRepository<TodoEntity, String> {
}
persistence 패키지 생성 후 작성한다.
TodoRepository는 인터페이스이며, 인터페이스 사용을 위해서는 새 인터페이스를 작성하여 JpaRepository를 확장해야 한다. Generic타입 받는다!
첫번째 매개변수는 엔티티 클래스, 두번째 매개변수는 해당 엔티티 기본키 타입
@Repository
또한 @Component
의 종류 중 하나이다. 따라서 스프링이 관리한다.
TodoService에서 TodoRepository 사용해보기
@Service
public class TodoService {
@Autowired
private TodoRepository repository;
public String testService(){
// 엔티티 생성
TodoEntity entity = TodoEntity.builder().title("test todo item").build();
// 저장
repository.save(entity);
// 검색
TodoEntity savedEntity = repository.findById(entity.getId()).get();
return savedEntity.getTitle();
}
}
위 코드 작성 후 애플리케이션 실행하여 로컬 서버에 /todo/test 보내면 data로 title을 받을 수 있다.