- 레이어드 아키텍처 패턴
- REST 아키텍처 패턴
- 스프링 어노테이션
- JPA와 스프링 Data JPA
스프링 프로젝트 내부에서 코드를 분배하고 관리하는 방식. 애플리케이션 구성요소들을 수평적으로 분리한다
보통 자바 클래스는 기능 수행 클래스와 데이터 저장 클래스로 구분할 수 있다.
기능 수행 클래스는 컨트롤러, 서비스, 퍼시스턴스처럼 로직을 수행한다.
기능 없이 DB에서 반환된 데이터를 담기 위한 클래스들은 기능에 따라 Entity, Model, DTO 등으로 부른다.
이 프로젝트는 모델과 엔티티를 한 클래스에 구현한다.
즉 모델은 비즈니스 데이터 담는 역할 + DB의 테이블과 스키마 표현 역할
package com.example.demo.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class TodoEntity {
private String id;
private String userId;
private String title;
private boolean done;
}
@Builder
디자인 패턴 중 빌더 패턴 (생성 패턴), 롬복 사용시 빌더 클래스를 따로 개발하지 않아도 된다. 빌더 패턴 사용은 생성자를 이용해 오브젝트를 생성하는 것과 비슷하다.
// 롬복이 이러한 코드를 자동 생성해준다
ToodEntity todo = TodoEntity.builder()
.id("id값")
.userId("userID값")
.title("제목")
.build()
@NoArgsConstructor
매개변수가 없는 생성자를 구현해준다.
public TodoEntity() {
}
@AllArgsConstructor
클래스의 모든 멤버 변수를 매개변수로 받는 생성자 구현
public TodoEntity(String id, String userId, String title, boolean done){
super();
this.id = id;
this.userId = userId;
this.title = title;
this.done = done;
}
@Data
클래스 멤버 변수의 Getter / Setter 메서드 구현
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
// 생략
public void setDone(boolean done) {
this.done = done;
}
서비스가 요청을 처리하고 클라이언트로 반환 시 Model 그 자체로 보내기보단 DTO로 변환해서 리턴한다. (Data Transfer Object, 데이터 전달에 사용하는 오브젝트)
package com.example.demo.dto;
import com.example.demo.model.TodoEntity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class TodoDTO {
private String id;
private String title;
private boolean done;
public TodoDTO(final TodoEntity entity) {
this.id = entity.getId();
this.title = entity.getTitle();
this.done = entity.isDone();
}
}
REST 아키텍처 스타일
클라이언트가 서비스를 이용하기 위해 어떠한 형식으로 요청을 보내고 응답을 받는지에 대한 것. REST아키텍처를 따라 구현된 서비스를 REST 서비스라고 한다. REST 아키텍처 스타일은 6가지 제약조건이 있고, 이 제약조건을 따르는 API를 RESTful API라고한다.
- 클라이언트 - 서버
- Stateless
- Cacheable 데이터
- 레이어 시스템
- 서버가 인증서버, 캐싱서버, 로드 밸런서를 거쳐 최종적으로 애플리케이션에 도착한다고 가정할 때, 이 사이의 레이어는 응답에 영향을 미치지 않으며 클라이언트는 레이어의 존재 인식하지 못한다.- code-on-demand (optional)
- 클라이언트는 서버에 코드를 요청할 수 있고, 서버가 리턴한 코드를 실행할 수 있다.- 일관적 인터페이스
스프링 부트 스타터 웹 디펜던시 설정을 해두었다. 이 패키지가 제공하는 어노테이션을 사용하면 REST 구현이 쉽다.
@RestController
/ @RequestMapping
package com.example.demo.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("test") // 리소스
public class TestController {
}
@GetMapping
메서드를 추가하고 @GetMapping
설정해준다
public class TestController {
@GetMapping
public String testController() {
return "hello world";
}
}
@GetMapping
은 HTTP에 매핑@RequestMapping(/test)
는 URI에 매핑됨앱 실행 후 http://localhost:8080/test
로 접근하면 Hello World 출력됨
@GetMapping에 HTTP 메서드 지정 말고도 경로를 지정하는 것 또한 가능하다!
@GetMapping("/testGetMapping")
public String testControllerWithPath() {
return "hello world & testGetMapping";
}
위의 코드는 아래와 같이 매핑된다.
@PathVariable
/test/{id}
와 같은 URI경로로 넘어오는 파라미터를 변수로 받을 수 있다.
@GetMapping("/{id}")
public String testControllerPathVar(@PathVariable(required = false) int id) {
return "Hello world " + id;
}
@RequestParams
/test?id=123
과 같은 요청 파라미터를 받을 수 있다. ?
뒤의 파라미터에 매핑된다.
@RequestBody
반환하고자 하는 리소스가 복잡할 때 사용한다.
DTO 추가 후 DTO를 요청 바디로 받는 메서드 추가하여 확인해보기
package com.example.demo.dto;
import lombok.Data;
// DTO
@Data
public class TestRequestBodyDTO {
private int id;
private String message;
}
// 컨트롤러 클래스에 메서드 추가
// /test경로는 이미 존재하므로 /test/testRequestBody로 지정
@GetMapping("/testRequestBody")
public String testControllerRequestBody(@RequestBody TestRequestBodyDTO testRequestBodyDTO) {
return "Hello World! ID " + testRequestBodyDTO.getId() + " Message : " + testRequestBodyDTO.getMessage();
}
@RequestBody TestRequestBodyDTO testRequestBodyDTO
는 Request Body로 보내오는 JSON을 TestRequestBodyDTO
오브젝트로 변환하여 가져온다. 따라서 클라이언트가 전송하는 요청 바디의 JSON 문자열 내부는 TestRequestBodyDTO
와 의미적으로 동일해야 한다.
@ResponseBody
와 @ResponseEntity
문자열 리턴말고 복잡한 오브젝트 리턴하기
@RestController
는 @ResponseBody
와 @Controller
의 조합으로 이루어져 있다. @Controller는 @Component로서 스프링이 이 클래스의 오브젝트를 자동으로 생성, 의존성 연결을 수행한다. @ResponseBody는 이 클래스의 반환값이 웹의 ResponseBody라는 의미이다. 즉, 스프링은 메서드가 리턴할 때 리턴된 오브젝트를 JSON 형태로 변환 후 HttpResponse에 담아 반환한다는 것이다.
@GetMapping("/testResponseBody")
public ResponseDTO<String> testControllerResponseBody() {
List<String> list = new ArrayList<>();
list.add("Hello World! ResponseDTO");
ResponseDTO<String> response = ResponseDTO.<String>builder().data(list).build();
return response;
}
@GetMapping("/testResponseEntity")
public ResponseEntity<?> testControllerResponseEntity() {
List<String> list = new ArrayList<>();
list.add("Hello World! ResponseEntity. Error 400!");
ResponseDTO<String> response = ResponseDTO.<String>builder().data(list).build();
// http status를 400로 설정.
return ResponseEntity.badRequest().body(response);
}
ResponseEntity는 HTTP 응답의 바디 외에도 status나 header 등 조작 가능