{
"id": 123,
"name": "John Doe",
"email": "john@example.com"
}
인터페이스 일관성 (Uniform Interface)
: 리소스에서 수행 가능한 작업의 균일한 인터페이스를 정의한다.
GET, POST, PUT, DELETE와 같은 HTTP Method를 사용해 구현된다.
무상태성 (Stateless)
: 작업을 위한 상태 정보를 따로 저장,관리하지 않는다.
세션이나 쿠키 정보를 별도로 저장하고 관리하지 않기 때문에, API서버는 단순 요청만 처리한다.
서비스의 자유도가 증가하고, 불필요한 정보 관리를 하지 않기 때문에 구현이 단순해진다.
캐싱 (Cacheable)
: REST는 HTTP 웹 표준을 그대로 사용하기 때문에, 웹에서 사용하는 기존 인프라를 그대로 활용할 수 있다.
HTTP 프로토콜에서 사용하는 Last-Modified 태그나 E-Tag를 이용하면 캐싱 구현도 가능하다.
서버-클라이언트 구조 (Server-Client)
: 영역을 클라이언트와 서버로 분리한다.
서버는 처리할 부분이 줄어들고, 여러 플랫폼과 UI 형태로 확장하기 쉬워진다.
계층형 구조 (Layered System)
: REST 서버는 다중 계층으로 구성될 수 있고,
로드 밸런싱, 암호화, Proxy 등의 계층을 추가해서 구조상 유연성을 둘 수 있다.
: REST의 특징을 기반으로 서비스 API를 구현한 것
각 요청이 어떤 동작이나 정보를 위한 것인지를 '요청 자체'로 추론이 가능하다.
API (Application Programming Interface)
: 응용 프로그램에서 사용할 수 있도록 OS나 프로그래밍 언어가 제공하는 기술을 제어할 수 있게 만든 인터페이스
잘못된 예 : http://www.example.com/sendEmail
올바른 예 : http://www.example.com/emails
http://www.example.com/emails/:id
잘못된 예 : http://www.example.com/pages/
올바른 예 : http://www.example.com/pages
잘못된 예 : http://www.example.com/user_profiles
올바른 예 : http://www.example.com/user-profiles
잘못된 예 : http://www.example.com/users/user-profiles
올바른 예 : http://www.example.com/user/:id/user-profiles
잘못된 예 : http://www.example.com/documents.pdf
올바른 예 : http://www.example.com/documents
잘못된 예 : http://www.example.com/createNewUsers
http://www.example.com/users/new
올바른 예 : POST http://www.example.com/users

GET /users HTTP/1.1Host: api.example.com
User-agent: Mozilla/5.0 (Windows NT 10.0 ...
Authorization: Bearer eyJhbGciOiJIUzl1N...
Content-Type: application/json
Cookie: sessionId=abc123; preferences=dart-mode
```
HTTP/1.1 200 OKContent-Type: application/json
Content-Length: 1024
Cache-Control: max-age=3600
Set-Cookie: sessionId=abc123; Expires=Wed, 21 Oct 2023 07:28:00 GMT; Path=/
```
: 계층 간 데이터 교환을 위해 사용하는 객체
로직을 갖지 않는 순수한 데이터 객체로, Getter/Setter만 가진 클래스이다.
DTO는 서버로의 요청(Request)과 서버로부터의 응답(Response) 전송을 위한 용도로 사용한다.
DTO가 필요한 이유
public class BookDTO {
// 생성(POST)
@Getter @Setter
@NoArgsConstructor
@AllArgsConstructor
public static class Post {
private String title;
private String subTitle;
private String author;
private String publisher;
}
// 전체 수정 (PUT)
@Getter @Setter
@NoArgsConstructor
@AllArgsConstructor
public static class Put {
private String title;
private String subTitle;
private String author;
private String publisher;
private Book.Status status;
}
// 일부 수정(PATCH)
@Getter @Setter
@NoArgsConstructor
@AllArgsConstructor
public static class Patch {
private Book.Status status;
}
@Getter @Setter
@NoArgsConstructor
@AllArgsConstructor
public static class Put {
@NotBlank(message = "제목은 필수 입력 값입니다.")
@NotNull(message = "제목은 반드시 입력하셔야 합니다.")
@Size(min = 1, max = 45, message = "제목은 45자 이하여야 합니다.")
private String title;
@NotBlank
@NotNull
@Size(max = 45)
private String subTitle;
@Size(max = 45)
private String author;
@Size(max = 45)
private String publisher;
private Book.Status status;
}
@PostMapping
public Book insertBook(@Valid @RequestBody BookDTO.Post dto) {
return bookService.insertBook(dto);
}
: DTO와 엔티티 간 매핑을 담당하는 클래스
public class BookMapper {
// 1. Entity -> DTO
public BookDTO.Response toDto(Book book) {
BookDTO.Response bookDto = new
BookDTO.Response();
// Entity 필드 값 하나씩 복사
bookDto.setId(book.getId());
bookDto.setTitle(book.getTitle());
bookDto.setAuthor(book.getAuthor());
bookDto.setPublisher((book.getPublisher()));
bookDto.setStatus((book.getStatus()));
return bookDto;
}
// 2. DTO -> Entity
public Book ToEntity(BookDTO.Post postDto) {
Book book = new Book();
// DTO 필드 값 하나씩 복사
book.setTitle(postDto.getTitle());
book.setSubtitle(postDto.getSubtitle());
book.setAuthor(postDto.getAuthor());
book.setPublisher(postDto. getPublisher());
return book;
}
}
: Java 기반의 매퍼 라이브러리 중 하나
컴파일 타임에 매퍼 구현체를 생성해주는 코드 생성기이다.
@Mapper
public interface BookMapper {
BookDto.Response toDto(Book book);
Book toEntity(BootDto.Post bookDto);
}
@Mapper(componentModel = "spring")
public interface BookControlMapper {
@Mapping(target = "id", ignore = true)
@Mapping(target = "status", ignore = true)
Book PostDTOToEntity(BookDTO.Post post);
@Mapping(target = "id", ignore = true)
void PutDTOToEntity(BookDTO.Put put, @MappingTarget Book book);
@Mapping(target = "id", ignore = true)
@Mapping(target = "title", ignore = true)
@Mapping(target = "subTitle", ignore = true)
@Mapping(target = "author", ignore = true)
@Mapping(target = "publisher", ignore = true)
void PatchDTOToEntity(BookDTO.Patch patch, @MappingTarget
Book book);
}
@Mapper(componentModel = "spring")
public interface BookResponseMapper {
BookDTO.Response entityToResponse(Book book);
List<BookDTO.Response> booksToResponses(List<Book> books);
@AfterMapping
default void titleAndSubTitle(Book book, @MappingTarget BookDTO.Response response) {
response.setTitle(book.getTitle() + " - " + book.getSubTitle());
}
}
: 컨트롤러와 리포지토리 사이에 위치하는 계층으로, 서버의 핵심 기능(비즈니스 로직)을 처리하는 순서를 총괄한다.
: DB와 관련된, 트랜잭션이 필요한 서비스 클래스 또는 메서드에 사용하는 Annotation
Propagation
: 기본적으로 존재하는 트랜잭션에 참여하거나, 새로운 트랜잭션을 시작한다.
Isolation
: DB의 기본 격리 수준을 따르거나, 특정 격리 수준을 지정할 수 있다.
ReadOnly
: 트랜잭션을 읽기 전용으로 설정해, 데이터 변경을 방지하고 성능을 최적화
rollbackFor / noRollbackFor
: 특정 예외 발생 시, 트랜잭션을 롤백하거나 롤백 않도록 설정
Timeout
: 트랜잭션의 최대 실행시간을 초 단위로 설정해, 시간초과 시 자동 롤백
@RestController = @Controller + @ResponseBody
@RestController의 모든 메서드에서 리턴되는 값은 "MessageConverter"에서 변환되어, HTTP Response Body에 쓰여진다.
응답할 때, 적절한 상태 코드를 반환하기 위해 ResponseEntity 클래스를 사용한다.
💡ResponseEntity와 HttpStatus
: ResponseEntity는 REST 컨트롤러의 응답을 위해 사용하는 클래스,
HttpStatus는 HTTP 상태 코드를 관리하는 클래스
ResponseEntity 클래스에 HTTP 상태 코드, 헤더, 바디를 실어 보낼 수 있다.
@PostMapping("/users")
publiuc ResponseEntity<User> createUser(@RequestBody User user) {
User savedUser = userService.createUser(user);
return ResponseEntity.status(HttpStatus.CREATED).body(savedUser);
}