스프링

 

 

 

 

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");
    }


}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

about author

PHRASE

Level 60  라이트

Better be the head of a dog than the tail of a lion [horse]. (사자 꼬리가 되느니 개의 머리가 되라.)

댓글 ( 4)

댓글 남기기

작성