728x90
반응형
Level3 단계의 REST API 구현을 위한 HATEOAS 적용
HATEOAS(Hypermedia As the Engine Of Application State)
현재 리소스와 연관된(호출 가능한) 자원 상태 정보를 제공
RESTful API 설계 단계
- Level0: The Swamp of POX
- Level1: Resources
- Level2: HTTP Verbs
- Level3: Hypermedia Controls
// pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
@GetMapping("/users/{id}")
public EntityModel<User> retrieveUser(@PathVariable int id) {
User user = service.findOne(id);
if (user == null) {
throw new UserNotFoundException(String.format("ID[%s] not found", id));
}
EntityModel entityModel = EntityModel.of(user);
// 링크 작업 추가
WebMvcLinkBuilder linTo = linkTo(methodOn(this.getClass()).retrieveAllUsers());
entityModel.add(linTo.withRel("all-users")); // all-users -> http://localhost:8088/users
return entityModel;
}
Swagger Documentation 구현
Swagger
- @Tag: 클래스에 설명, Swagger 리소스
- @Parameter: API에서의 단일 매개 변수
- @Parameters: API에서의 복수 매개 변수
- @Schema: Swagger 모델에 대한 추가 정보
- @Operation(summary="foo", description="bar"): 특정 경로에 대한 작업
(일반적으로 컨트롤러에서의 HTTP 메서드를 설명) - @ApiResponse(responseCode="404", description="foo"): API에서의 작업 처리에 대한 응답코드 설명
http:localhost:8088/swagger-ui/index.html#/
// pom.xml
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.2.0</version>
</dependency>
Config
// config -> NewSwaggerConfig
package com.example.myrestfulservice.config;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.info.Info;
import lombok.RequiredArgsConstructor;
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@OpenAPIDefinition(
info = @Info(title = "My Restful Service API 명세서",
description="Spring Boot로 개발하는 RESTful API 명세서 입니다.",
version="v1.0.0")
)
@Configuration
@RequiredArgsConstructor // 생성자
public class NewSwaggerConfig {
@Bean
public GroupedOpenApi customTestOpenAPI() {
String[] paths = {"/users/**", "/admin/**"};
return GroupedOpenApi.builder()
.group("일반 사용자와 관리자를 위한 User 도메인에 대한 API")
.pathsToMatch(paths)
.build();
}
}
// User
package com.example.myrestfulservice.bean;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Past;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.Date;
@Data
@AllArgsConstructor
@JsonIgnoreProperties(value = {"password", "ssn"})
@Schema(description = "사용자 상세 정보를 위한 도메인 객체")
public class User {
@Schema(title = "사용자 ID", description = "사용자 ID는 자동 생성됩니다.")
private Integer id;
@Schema(title = "사용자 이름", description = "사용자 이름을 입력합니다.")
@Size(min = 2, message = "Name은 2글자 이상 입력해 주세요.")
private String name;
@Schema(title = "사용자 등록자", description = "사용자 등록일을 입력합니다. 입력하지 않으면 현재 날짜가 지정됩니다.")
@Past(message = "등록일은 미래 날짜로 입력하실 수 없습니다.") // 과거 데이터만 들어올 수 있음
private Date joinDate;
// @JsonIgnore
@Schema(title = "사용자 비밀번호", description = "사용자 비밀번호를 입력합니다.")
private String password;
// @JsonIgnore
@Schema(title = "사용자 주민번호", description = "사용자 주민번호를 입력합니다.")
private String ssn;
}
// UserController
package com.example.myrestfulservice.controller;
import com.example.myrestfulservice.bean.User;
import com.example.myrestfulservice.dao.UserDaoService;
import com.example.myrestfulservice.exception.UserNotFoundException;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import org.apache.coyote.Response;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import java.net.URI;
import java.util.List;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;
@RestController
@Tag(name = "user-controller", description = "일반 사용자 서비스를 위한 컨트롤러입니다.")
public class UserController {
// 인스턴스 이미 생성되어 있으니 주입 받아와서 사용
private UserDaoService service;
// Spring context가 호출
public UserController(UserDaoService service) {
this.service = service;
}
// 사용자 목록
@GetMapping("/users")
public List<User> retrieveAllUsers() {
return service.findAll();
}
// 개별적인 1명
@Operation(summary = "사용자 정보 조회 API", description = "사용자 ID를 이용해서 사용자 상세 정보 조회를 합니다.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK !!"),
@ApiResponse(responseCode = "400", description = "BAD REQUEST !!"),
@ApiResponse(responseCode = "404", description = "USER NOT FOUND !!"),
@ApiResponse(responseCode = "500", description = "INTERNAL SERVER ERROR !!"),
})
@GetMapping("/users/{id}")
public EntityModel<User> retrieveUser(
@Parameter(description = "사용자 ID", required = true, example = "1") @PathVariable int id) {
User user = service.findOne(id);
if (user == null) {
throw new UserNotFoundException(String.format("ID[%s] not found", id));
}
EntityModel entityModel = EntityModel.of(user);
// 링크 작업 추가
WebMvcLinkBuilder linTo = linkTo(methodOn(this.getClass()).retrieveAllUsers());
entityModel.add(linTo.withRel("all-users")); // all-users -> http://localhost:8088/users
return entityModel;
}
}
Spring Boot Actuato를 이용한 모니터링 및 Metrics 수집
모니터링, 로깅 정보 등 제어할 수 있게 해주는 서포트 라이브러리
// pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
// application.yml
management:
endpoints:
web:
exposure:
include: "*"
HAL Explorer를 이용한 API 테스트
HAL Browser -> HAL Explorer
- Hypertext Application Language
- Response 메세지에 대한 포맷과 상관 없이 API를 쉽게 사용할 수 있도록
메타 정보 -> Hyperlink형식으로 제공해주는 기능 - API간 검색 쉬움
- API ~> 부가 정보, 메타 정보를 하이퍼링크 텍스트를 통해서 관리할 수 있음
// pom.xml
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-rest-hal-explorer</artifactId>
</dependency>
Spring Security를 이용한 인증 처리
spring 기반의 애플리케이션의 보안(인증, 권한)을 처리하는 프레임워크
// pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>3.0.2</version>
</dependency>
API 사용을 위한 사용자 인증 처리 구현
// application.yml
spring:
security:
user:
name: username
password: passw0rd
// application.yml에 추가한 것 주석 처리
// config -> SecurityConfig
package com.example.myrestfulservice.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@Configuration
public class SecurityConfig {
@Bean
UserDetailsService userDetailsService() {
// 사용자 데이터 -> 메모리 상에서 생성 -> 생성된 메모리 객체 바로 사용
// 메모리 객체 이용 -> 스토리지로 삼아 사용
InMemoryUserDetailsManager userDetailsService = new InMemoryUserDetailsManager();
// 서버 기동될 때마다 user라는 사용자 추가
UserDetails user = User.withUsername("user")
.password(passwordEncoder().encode("passw0rd"))
.authorities("read")
.build();
userDetailsService.createUser(user);
return userDetailsService;
}
@Bean
BCryptPasswordEncoder passwordEncoder() { // 인코딩 처리
return new BCryptPasswordEncoder();
}
}
728x90
반응형