1. Level3 단계의 REST API 구현을 위한 HATEOAS 적용
https://www.inflearn.com/questions/203512/entitymodel-deprecated-어떻게-바꾸면-될까요
https://github.dev/braverokmc79/rest-api-width-spring
https://github.com/braverokmc79/restapi-spring-boot-study
hateoas 라이브러리 추가
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> <version>2.14.2</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-hateoas</artifactId> </dependency>
package com.example.restfullwebservice.user; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j; import lombok.extern.slf4j.Slf4j; import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import java.net.URI; import java.nio.file.attribute.UserPrincipalNotFoundException; import java.util.List; import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo; import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn; @RestController @RequiredArgsConstructor @Slf4j public class UserController { private final UserDaoService service; // public UserController(UserDaoService service){ // this.service=service; // } // @GetMapping("/users") public List retrieveAllUsers(){ return service.findAll(); } //https://www.inflearn.com/questions/203512/entitymodel-deprecated-어떻게-바꾸면-될까요 //GET /users/1 or /users/10 -> String /** * 다음을 참조 * https://github.dev/braverokmc79/rest-api-width-spring * @param id * @return */ @GetMapping("/users/{id}") public ResponseEntity retrieveUser(@PathVariable int id){ User user =service.findOne(id); if(user==null){ throw new UserNotFoundException(String.format("ID[%s] not found ", id) ); } /** * 링크 생성하기 * EntityModel.of(newEvent); Resource 객체를 가져와서 사용 */ // WebMvcLinkBuilder selfLinkBuilder=linkTo(UserController.class).slash(id); // URI createdUri=selfLinkBuilder.toUri(); // log.info("* createdUri {} " , createdUri); //1.HATEOAS EntityModel entityModel = EntityModel.of(user); //2. 링크 추가 모든 유저보기 링크추가 WebMvcLinkBuilder linkTo=linkTo(methodOn(this.getClass()).retrieveAllUsers()); entityModel.add(linkTo.withRel("all-users")); //간소화 한줄 처리 - 셀프 링크 추가 entityModel.add( linkTo(methodOn(this.getClass()).retrieveUser(id)).withSelfRel() ); //3.반환처리 //return ResponseEntity.status(HttpStatus.OK).body(entityModel); return ResponseEntity.ok(entityModel ); } @PostMapping("/users") public ResponseEntity createUser(@Valid @RequestBody User user){ User savedUser =service.save(user); URI location = ServletUriComponentsBuilder.fromCurrentRequest() .path("/{id}") .buildAndExpand(savedUser.getId()) .toUri(); // log.info(" ***** uri {} ", uri); //ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(savedUser.getId()).toUri(); return ResponseEntity.created(location).build(); } @DeleteMapping("/users/{id}") public void deleteUser(@PathVariable int id){ User user =service.deleteById(id); if(user==null){ throw new UserNotFoundException(String.format("ID[%s] not found ", id)); } } }
2. REST API Documentation을 위한 springdoc-openapi 사용
2018 Swagger 업데이트 중단 sprinddoc 는 지속적인 업데이트
출처 : https://colabear754.tistory.com/99
공식문서 : https://springdoc.org/
Gradle
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2")
Maven
<!-- https://mvnrepository.com/artifact/org.springdoc/springdoc-openapi-starter-webmvc-ui --> <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <version>2.2.0</version> </dependency>
위 라이브러만 추가후 구동만 시켜도 실행이 된다.
http://localhost:8080/swagger-ui/index.html
Swagger 사용을 위한 기본 설정
Springdoc은 Swagger UI 설정을 하는 방법이 Springfox와 다소 차이가 있다.
Springfox는 별도의 config 클래스에서 대부분의 설정을 하였지만 Springdoc는 config 클래스에서 API 문서페이지의 기본 설명만 작성하고 나머지 설정은
모두 application.properties 혹은 application.yml에서 설정한다.
또한 config 클래스에 @EnableWebMvc 어노테이션을 붙이지도 않는다.
SwaggerConfig
라이브러리 추가
// Kotlin import io.swagger.v3.oas.models.Components import io.swagger.v3.oas.models.OpenAPI import io.swagger.v3.oas.models.info.Info import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration @Configuration class SwaggerConfig { @Bean fun openAPI(): OpenAPI = OpenAPI() .components(Components()) .info(apiInfo()) private fun apiInfo() = Info() .title("Springdoc 테스트") .description("Springdoc을 사용한 Swagger UI 테스트") .version("1.0.0") }
// Java import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; import org.springframework.context.annotation.Bean; import org.springfrapackage com.example.restfullwebservice.config; //** //https://velog.io/@kjgi73k/Springboot3에-Swagger3적용하기 import io.swagger.v3.core.model.ApiDescription; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Contact; import io.swagger.v3.oas.models.info.Info; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * Spring boot Swagger 3.0 적용하기 * * https://dev-youngjun.tistory.com/258 * */ //@OpenAPIDefinition( // info=@Info(title="API 명세서", // description = "Spring boot API", // version = "v1" // ) //) @Configuration public class SwaggerConfig { @Bean public OpenAPI openAPI() { return new OpenAPI() .components(new Components()) .info(apiInfo()); } private Info apiInfo() { Contact contact=new Contact(); contact.setName("Hong Gil Dong"); contact.setUrl("https://macaronics.net"); contact.setUrl("honggil@gmail.com"); return new Info() .title("Springdoc 테스트") .description("Springdoc을 사용한 Swagger UI 테스트") .contact(contact) .version("1.0.0"); } }mework.context.annotation.Configuration; @Configuration public class SwaggerConfig { @Bean public OpenAPI openAPI() { return new OpenAPI() .components(new Components()) .info(apiInfo()); } private Info apiInfo() { return new Info() .title("Springdoc 테스트") .description("Springdoc을 사용한 Swagger UI 테스트") .version("1.0.0"); } }
application.yml
springdoc: packages-to-scan: com.colabear754.springdoc_example.controllers default-consumes-media-type: application/json;charset=UTF-8 default-produces-media-type: application/json;charset=UTF-8 swagger-ui: path: / disable-swagger-default-url: true display-request-duration: true operations-sorter: alpha
Swagger 문서에 API 등록
Springdoc도 Springfox와 마찬가지로 application 속성의 springdoc.packages-to-scan에 설정해놓은 패키지 내부의 클래스는 모두 자동으로 Swagger 문서에 등록된다.
만약 등록하고 싶지 않은 컨트롤러가 있다면 @Hidden 어노테이션을 이용하여 숨길 수 있다.
Springdoc에서는 Swagger UI를 위한 어노테이션이 변경되었다. 어노테이션 외에는 변경할 점이 없기 때문에 컨트롤러에서 어노테이션만 변경해주면 된다
// Kotlin import io.swagger.v3.oas.annotations.Hidden import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.Parameter import io.swagger.v3.oas.annotations.tags.Tag import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController @Tag(name = "예제 API", description = "Swagger 테스트용 API") @RestController @RequestMapping("/") class ExampleController { @Operation(summary = "문자열 반복", description = "파라미터로 받은 문자열을 2번 반복합니다.") @Parameter(name = "str", description = "2번 반복할 문자열") @GetMapping("/returnStr") fun returnStr(@RequestParam str: String) = "$str\n$str" @GetMapping("/example") fun example() = "예시 API" @Hidden @GetMapping("/ignore") fun ignore() = "무시되는 API" }
// Java import io.swagger.v3.oas.annotations.Hidden; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @Tag(name = "예제 API", description = "Swagger 테스트용 API") @RestController @RequestMapping("/") public class ExampleController { @Operation(summary = "문자열 반복", description = "파라미터로 받은 문자열을 2번 반복합니다.") @Parameter(name = "str", description = "2번 반복할 문자열") @GetMapping("/returnStr") public String returnStr(@RequestParam String str) { return str + "\n" + str; } @GetMapping("/example") public String example() { return "예시 API"; } @Hidden @GetMapping("/ignore") public String ignore() { return "무시되는 API"; } }
Springdoc 공식 가이드에서 설명하는 어노테이션의 변화는 다음과 같다.
- @Api → @Tag
- @ApiIgnore → @Parameter(hidden = true) or @Operation(hidden = true) or @Hidden
- @ApiImplicitParam → @Parameter
- @ApiImplicitParams → @Parameters
- @ApiModel → @Schema
- @ApiModelProperty(hidden = true) → @Schema(accessMode = READ_ONLY)
- @ApiModelProperty → @Schema
- @ApiOperation(value = "foo", notes = "bar") → @Operation(summary = "foo", description = "bar")
- @ApiParam → @Parameter
- @ApiResponse(code = 404, message = "foo") → @ApiResponse(responseCode = "404", description = "foo")
Spring Boot 프로젝트를 실행한 후 application 속성에서 설정한 포트와 경로로 이동하면 API가 등록되어 있는 Swagger UI 문서를 확인할 수 있다.
실행
http://localhost:8080/swagger-ui/index.html
http://localhost:8080/v3/api-docs
Swagger에 등록된 API 테스트
Springfox의 Swagger와 마찬가지로 Try it out 버튼을 클릭하면 API를 테스트할 수 있다.
API가 정상적으로 동작하는 것을 확인할 수 있다.
3. Springdo Documentation 구현 방법
@ApiModel → @Schema
package com.example.restfullwebservice.user; import com.fasterxml.jackson.annotation.JsonFilter; 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.*; import java.util.Date; @Getter @Setter @JsonIgnoreProperties(value = {"password" }) //@JsonFilter("UserInfo") @NoArgsConstructor(access = AccessLevel.PROTECTED) @ToString @EqualsAndHashCode(of="id") @Schema(description = "사용자 상세 정보를 위한 도메인 객체") public class User { private Integer id; @Size(min=2, message = "Name 은 2글자 이상 입력해 주세요.") @Schema(description = "사용자 이름을 입력해 주세요.") private String name; //과거데이터만 올수 있는 제약 조건 @Past @Schema(description = "사용자 등록일을 입력해 주세요.") private Date joinDate; // @JsonIgnore @Schema(description = "사용자 패스워드를 입력해 주세요.") private String password; // @JsonIgnore @Schema(description = "사용자 주민번호를 입력해 주세요.") private String ssn; @Builder public User(Integer id, String name, Date joinDate, String password, String ssn){ this.id=id; this.name=name; this.joinDate=joinDate; this.password=password; this.ssn=ssn; } }
4. Spring boot Actuator를 이용하여 스프링 애플리케이션 정보 모니터링 하기
Actuator를 사용하는 법은 아주 간단하다.
- maven dependency에 아래의 내용을 추가하기만 하면 된다.
maven
<!--- actuator --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
gradle
implementation 'org.springframework.boot:spring-boot-starter-actuator'
actuator 라이브러리 추가후 구동후 actuator 페이지에 접속하면
http://localhost:8080/actuator
더 많은 정보를 보기위해 application.yml 에 다음과 같이 설정하면 된다.
management: endpoints: web: exposure: include: "*"
5. HAL Explorer이용한 HATEOAS 기능 구현
HAL Browser Hypertext Application Language
REST API 설계 시 Response message의 포맷과는 상관없이
API를 쉽게 사용할 수 있는 메타정보를 하이퍼링크 형식으로 제공한다.
- API 리소스 사이에서 일관적인 하이퍼링크를 제공
- API 설계에서 HAL을 도입하게 되면 API 간 쉬운 검색이 가능해진다. → 더 나은 개발환경 제공
dependency 추가
maven
<dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-rest-hal-explorer</artifactId> </dependency>
gradle
implementation 'org.springframework.data:spring-data-rest-hal-explorer'
explorer/html 접속후 url 입력 하면 된다.
http://localhost:8088/explorer/index.html
6. POST MAN Spring Security를 이용한 인증 처리
라이브러리 추가
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency>
또는
application.yml
spring: security: user: name: username password: 1111
또는
SecurityConfig
package com.example.restfullwebservice.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.stereotype.Component; @EnableMethodSecurity(securedEnabled = true, prePostEnabled =true) @EnableWebSecurity @Component public class SecurityConfig { @Autowired public void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("test") .password("{noop}1111") .roles("USER"); } }
댓글 ( 4)
댓글 남기기