To do Application 아키텍처
Repository
ORM부터 간단하게 알아보자
Object Relational Mapping은 객체가 테이블이 되도록 매핑시켜주는 프레임워크이다. ORM을 사용하면 SQL 쿼리를 메서드 호출로 대체할 수 있으며 데이터베이스 중심 설계의 단점을 개선하여 효율적으로 개발할 수 있다.
또한 DBMS에 대한 종속성이 줄어들어 프로그래머는 객체에만 집중하면 되고, DBMS를 교체하는 작업에도 적은 시간이 소요된다. ex) SELECT * FROM todo -> todo.findAll()
package org.example.repository;
import org.example.model.TodoEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
// JpaRepository<대상으로 지정할 엔티티, 해당 엔티티의 기본키 타입>
public interface TodoRepository extends JpaRepository<TodoEntity, Long> {
}
@Entity 선언으로 생성된 데이터베이스에 접근하는 메서드를 사용하기 위한 인터페이스이다. JPA repositoty를 상속받아 CRUD 등의 기본적인 메서드를 사용할 수 있다. 메서드 종류는 Service 구현 부분에서 설명하겠다.
다시 정리해보면, client에서 요청 데이터를 DB에 직접 넘기기 쉽지 않다. 서로 사용하는 언어가 다르기 때문이다. 이를 위한 라이브러리가 JPA이고 JPA는 DB와의 소통을 쉽게 한다. 그 핵심이 되는 인터페이스를 repository라 한다.
JpaRepository 인터페이스 계층 구조
TodoService
서비스는 컨트롤러와 레파지토리 중간에서 로직을 수행하는 부분이다. 컨트롤러는 리퀘스트를 받아서 서비스에 넘겨주고 서비스에서 리퀘스트를 처리하고 리스폰스를 리턴하는 역할을 한다. 그러면 컨트롤러가 다시 그걸 받아서 모델에 담고 view로 전달한다.
package org.example.service;
import lombok.AllArgsConstructor;
import org.example.model.TodoEntity;
import org.example.model.TodoRequest;
import org.example.repository.TodoRepository;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;
import java.util.List;
@Service // 해당 클래스가 서비스 레이어 클래스라는것을 명시
@AllArgsConstructor
public class TodoService {
private final TodoRepository todoRepository;
public TodoEntity add(TodoRequest request){
TodoEntity todoEntity = new TodoEntity();
todoEntity.setTitile(request.getTitle());
todoEntity.setOrder(request.getOrder());
todoEntity.setCompleted(request.getCompleted());
return this.todoRepository.save(todoEntity); // 레파지토리에 데이터베이스 값 입력 save() 제네릭으로 받은 타입을 반환, 반환값은 todoentity
}
public TodoEntity searchById(Long id){
return this.todoRepository.findById(id).orElseThrow(()-> new ResponseStatusException(HttpStatus.NOT_FOUND));
}
public List<TodoEntity> searchAll() {
return this.todoRepository.findAll();
}
//id를 받고 request로받은 값으로 변경
public TodoEntity updateById(Long id, TodoRequest request){
TodoEntity todoEntity = this.searchById(id);
if(request.getTitle() != null){
todoEntity.setTitile(request.getTitle());
}
if(request.getCompleted() != null) {
todoEntity.setCompleted(request.getCompleted());
}
if(request.getOrder() != null){
todoEntity.setOrder(request.getOrder());
}
return this.todoRepository.save(todoEntity);
}
public void deleteById(Long id){
this.todoRepository.deleteById(id);
}
public void deleteAll(){
this.todoRepository.deleteAll();
}
}
각 함수들의 기능들에 대해 간단히 알아보자.
- public TodoEntity add(TodoRequest request) - 데이터 저장
컨트롤러에서 전달받은 request의 요청을 파라미터로 받아서 model 클래스인 TodoEntity에 데이터를 넣는다. 그리고 리턴할 때 레파지토리에 데이터베이스 값을 save()로 insert 한다.
- public TodoEntity searchById(Long id) - 데이터 검색
레파지토리에서 findByid()를 사용해서 엔티티 1개를 id값으로 찾는다. 만약 id 정보가 없을 경우 ResponseStatusException 클래스는 사용하여 HTTP 상태 코드를 리턴한다. NOT_FOUND값은 코드가 404를 전달한다.
- public List<TodoEntity> searchAll() - 모든 데이터 검색
레파지토리에서 findAll()을 사용해서 모든 데이터를 조회한다. 모든 데이터를 조회함으로 id값을 파라미터로 받을 필요는 없다. 하지만 모든 데이터를 조회함으로 반환 값을 List <>로 설정한다.
- public TodoEntity updateById(Long id, TodorRequest request) - 데이터 수정
수정해야 되는 데이터를 찾기 위해 id를 파라미터로 받고, request로 받은 값이 null 이 아닌 경우만 값을 수정한다. 그리고 값을 수정했으면 다시 레파지토리에 값을 insert 한다.
- public deleteById(Long id) - 데이터 삭제
id를 파라미터로 받아서 특정 데이터를 삭제한다.
- public void deleteAll()
delete 문을 날려서 모든 데이터를 삭제한다.
간단히 service구현에서 사용한 JPA 함수를 정리해보자면 아래와 같다. 좀 더 다양한 함수의 기능을 알아보고 싶다면 링크를 참고하면 된다. JpaRepository (Spring Data JPA 2.6.1 API)
- findAll() - 모든 엔티티 조회
- findById(id) - 엔티티 1개를 id 값으로 조회
- save(todoEntity) - 새로운 엔티티 저장
- deleteById(id) - 엔티티 1개를 id 값으로 삭제
- deleteAll() - 모든 엔티티 테이블에서 삭제
TodoController
스프링 부트에서 컨트롤러가 하는 역할은 다양하며 주로 client에 요청을 처리하고 응답해주는 역할을 한다. 간단하게 RESTful API를 알아보자.
RESTful API(Respresentational State Trasfer)는 HTTP 리퀘스트를 이용해 데이터를 GET, POST, PUT, DELETE 할 수 있도록 하는 API를 의미한다. GET, POST, PUT, DELETE를 리퀘스트 메서드라고 부른다. 예를 들어서 HTTP 리퀘스트를 보낼 때, 애는 GET메서드에요 하고 메서드를 포함시켜서 리퀘스트를 보내면 서버 애플리케이션이 이를 인지하고 해석할 수 있어야 한다.
GET - 데이터를 가져온다.
POST - 데이터를 생성하거나 업데이트한다.
PUT - 데이터를 업데이트한다.
DELETE - 데이터를 삭제한다.
create()
@RestController //해당 클래스는 REST API 처리하는 Controller (@Controller + @ResponseBody)
@CrossOrigin // CORS이슈 방지
@AllArgsConstructor
@RequestMapping("/") // base url 설정, url을 컨트롤러의 메서드와 매핑할 때 사용
public class TodoController {
private final TodoService service;
@PostMapping
//ResponseEntity는 HttpEntity 클래스를 상속받아 구현한 클래스이며 HttpStatus, HttpHeaders, HttpBody를 포함
//list에 todoitem을 추가하는 api
public ResponseEntity<TodoResponse> create(@RequestBody TodoRequest request){
log.info("CREATE");
if(ObjectUtils.isEmpty(request.getTitle()))
return ResponseEntity.badRequest().build();
if(ObjectUtils.isEmpty(request.getOrder()))
request.setOrder(0L);
if(ObjectUtils.isEmpty(request.getCompleted()))
request.setCompleted(false);
TodoEntity result = this.service.add(request);
return ResponseEntity.ok(new TodoResponse(result));
}
create() 메서드는 클라이언트로부터 요청받은 정보를 todolist에 todoitem을 추가한다. @RestController을 사용하면 편리하고 쉽게 REST API를 구현하여 데이터를 처리할 수 있다. 그렇지만 더 나아가 응답에 헤더 정보와 HTTP 상태 코드 등을 담을 수 있는 ResponseEntity가 제공된다. return 타입을 ResponseEntity로 설정하고@RequestBody를 사용하여 HTTP 요청 본문(body)을 자바 객체로 받는다.
ObjectUtils.isEmpty() 메서드를 사용하여 객체가 비어 있거나 null인지 확인한다. 그리고 서비스에 request를 처리하고 결과를 리턴한다.
API 테스트
readOne()
@GetMapping("{id}")
//todolist 하나씩 조회 하는 api
//GetMapping에 경로로받은 id값을 쓰기위해서 @PathVariable
public ResponseEntity<TodoResponse> readOne(@PathVariable Long id){
log.info("Read One");
TodoEntity result = this.service.searchById(id);
return ResponseEntity.ok(new TodoResponse(result));
}
readOne() 메서드는 todolist에서 요청한 id값 경로로 하나씩 조회한다. @PathVariable을 사용하여 HTTP 요청에 대한 요청 url을 파라미터 형태로 사용할 수 있다. 쉽게 말하면 url endpoint에 @GetMapping에 명시한 {id}가 변수로 추가된다. @PathVariable을 사용하면 특정 resource id를 식별할 수 있다.
그리고 id값을 인자로 서비스에 전달해서 처리한 결과를 리턴한다.
API 테스트
readAll()
@GetMapping
public ResponseEntity<List<TodoResponse>> readAll(){
log.info("Read ALL");
List<TodoEntity> list = this.service.searchAll();
List<TodoResponse> responses = list.stream().map(TodoResponse::new).collect(Collectors.toList());
return ResponseEntity.ok(responses);
}
readAll() 메서드는 전체 todolist를 조회한다. stream은 리스트에 저장되어 있는 요소를 람다 함수 형식과 map(데이터 변환), collect(데이터 수집) 함수를 같이 사용하여 배열의 원소 가공이 간결하고 깔끔해진다. 자세한 java Stream API 따로 찾아보길 바란다. 즉, stream의 요소들을 TodoResponse의 형태로 변환하여 그 결과를 List로 반환받는다고 생각하면 된다.
API 테스트
update()
@PatchMapping("{id}")
public ResponseEntity<TodoResponse> update(@PathVariable Long id, @RequestBody TodoRequest request) {
log.info("UPDATE");
TodoEntity result = this.service.updateById(id,request);
return ResponseEntity.ok(new TodoResponse(result));
}
update() 메서드는 todoltem을 수정한다. @PatchMapping어노테이션은 Put을 위한 HTTP 요청 처리할 때 사용된다. 요청을 수정을 하려면 수정되어야 하는 todoitem을 찾아야 하는데 그러기 위해서는 id정보와 변경할 내용 정보가 필요하다.
API 테스트
delete()
@DeleteMapping("{id}")
// <?> 타입 변수에 모든 타입을 사용할 수 있음
public ResponseEntity<?> deleteOne(@PathVariable Long id){
log.info("DELETE");
this.service.deleteById(id);
return ResponseEntity.ok().build();
}
delete() 메서드는 특정 todoItem을 삭제한다. 특정 id 값으로 todoItem을 삭제한다. 요청이 성공했다는 HTTP 상태 코드 200을 응답한다.
API 테스트
deleteAll()
@DeleteMapping
public ResponseEntity<?> deleteAll(){
log.info("DELETE ALL");
this.service.deleteAll();
return ResponseEntity.ok().build();
}
deleteAll() 함수는 전체 todoItem을 삭제한다. delete와 마찬가지로 HTTP 상태 코드 200을 응답한다.
API 테스트
다음 포스팅에서는 vue.js를 이용해 Fronted웹 앱을 만들고 Application server와 연동을 설명하겠다.
'Spring Boot' 카테고리의 다른 글
[Spring boot] (1) Spring boot를 활용한 To do Application (0) | 2022.02.08 |
---|