본문 바로가기

항해 99/Spring

Swagger 사용하기

Swagger 개요

API를 설계, 빌드, 문서화하고 테스트하는 데 사용되는 오픈 소스 프레임워크, 주로 RESTful API를 문서화하고 클라이언트 개발을 용이하게 하는 데 활용됨

 

Swagger는 API 명세를 정의하기 위한 OpneAPI Specification(OAS)를 사용함.

이 명세는 API 엔드포인트, 매개변수, 응답 형식 등과 같은 API의 구조와 동작을 정의함, Swagger를 사용하면 개발자는 API의 동자 방식을 이해하고 API를 통합하는 데 필요한 정보를 쉽게 찾을 수 잇음

 

주요 기능

  1. API 문서화 : Swagger를 사용하여 API의 명세를 문서화할 수 있음, 이를 통해 API 사용자들은 API의 기능, 엔드포인트, 매개변수 등에 대한 정보를 쉽게 얻을 수 있음
  2. API 테스트 : Swagger UI를 통해 API 엔드포인트를 직접 호출하고 테스트할 수 있음, 이를 통해 API의 동작을 빠르게 확인하고 디버깅할 수 있음
  3. 코드 생성 : Swagger 명세를 기반으로 클라이언트 코드를 자동으로 생성할 수 있음, 이를 통해 개발자는 클라이언트와 서버 간의 통신을 위한 코드를 더 쉽게할 수 있음

Swagger는 API 개발 생명주기를 보다 투명하고 효율적으로 만들어주는 강력한 도구

 

 

Spring Doc

Spring 프레임워크에서 API 문서를 생성하는 도구, Swagger와 유사한 목적을 가지고 있지만, Spring 프레임워크와의 통합을 강조하여 Spring 애플리케이션에서 API 문서를 생성하고 유지하는 데 특화되어 있음

 

Spring Doc은 Spring 애플리케이션의 코드와 주석을 기반으로 API 명세를 생성함, 주로 OpneAPI Specification(OAS)를 사용하여 API를 정의하며, 이를 통해 API의 구조와 동작을 문서화함.

Spring Doc는 Swagger UI와 함께 사용될 수 있어 API 사용자가 API를 쉽게 탐색하고 테스트할 수 있도록 도와줌

 

Spring Doc 장점

  1. Spring 프레임워크 통합 : Spring 애플리케이션과의 원활한 통합을 제공하여 Spring의 기능과 주요 구성 요소를 활용하여 API를 문서화 할 수 있음
  2. 주석 기반 문서 생성 : 코드에 주석을 추가함으로써 API 문서를 생성할 수 있음, 이를 통해 개발자는 코드와 동기화된 문서를 유지할 수 있음
  3. 코드 생성 : Spring Doc를 사용하여 API 명세를 기반으로 클라이언트 코드를 자동으로 생성할 수 있음

Spring Doc를 통해 Spring 기반의 애플리케이션에서 API 문서를 쉽게 생성하고 유지할 수 있으며, API 사용자들에게 API를 쉽게 이해하고 사용할 수 있는 문서를 제공할 수 있음

 

 

 

Spring Boot 3에서 Swagger 사용하기

Programming Language : Java 17

Framework : Spring Boot 3

Build Tool : Gradle 7.6

 

순서

1. gradle 의존성 추가

implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0'

 

  • SpringDoc 공식 문서에 따르면 Spring Boot v3(Java 17 & Jakarta EE9)을 사용하기 위해선 dependency를 사용해야 함(Spring Boot 3 지원을 위해 springdoc-openapi v2를 사용해야 하기 때문)

2. Swagger 설정 파일 추가

config - swagger

package com.sparta.office.config;

import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.info.Info;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.security.SecurityScheme;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.context.SecurityContext;

import java.util.Arrays;

@OpenAPIDefinition(
        info = @Info(title = "📚 스파르타 백오피스 서버 만들기 API 명세서",
                description = "백오피스 서버",
                version = "v1"))
@RequiredArgsConstructor
@Configuration
public class SwaggerConfig {

}

 

3. Spring Security에 Swagger Endpoint 활성화

confing - security

@Configuration
@EnableWebSecurity // Spring Security 지원을 가능하게 함
@RequiredArgsConstructor
public class WebSecurityConfig {

    private final JwtUtil jwtUtil;
    private final UserDetailsServiceImpl userDetailsService;
    private final AuthenticationConfiguration authenticationConfiguration;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
        return configuration.getAuthenticationManager();
    }

    @Bean
    public JwtAuthenticationFilter jwtAuthenticationFilter() throws Exception {
        JwtAuthenticationFilter filter = new JwtAuthenticationFilter(jwtUtil);
        filter.setAuthenticationManager(authenticationManager(authenticationConfiguration));
        return filter;
    }

    @Bean
    public JwtAuthorizationFilter jwtAuthorizationFilter() {
        return new JwtAuthorizationFilter(jwtUtil, userDetailsService);
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        // CSRF 설정
        http.csrf((csrf) -> csrf.disable());

        // 기본 설정인 Session 방식은 사용하지 않고 JWT 방식을 사용하기 위한 설정
        http.sessionManagement((sessionManagement) ->
                sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        );

        http.authorizeHttpRequests((authorizeHttpRequests) ->
                authorizeHttpRequests
                        .requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll() // resources 접근 허용 설정
                        .requestMatchers("/").permitAll() // 메인 페이지 요청 허가
                        .requestMatchers("/api/admin/**").permitAll() // '/api/admin/'로 시작하는 요청 모두 접근 허가
                        .requestMatchers("/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html").permitAll() // Swagger 요청 접근 허가
                        .anyRequest().authenticated() // 그 외 모든 요청 인증처리
        );

        // 필터 관리
        http.addFilterBefore(jwtAuthorizationFilter(), JwtAuthenticationFilter.class);
        http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }
}

 

4. Spring 프로젝트 실행

3번(application.yml)에서 지정한 springdoc.swagger-ui.path 경로로 접속

  • 위 코드 기준 http://localhost:8080/swagger-ui/index.html

 

 

예제 코드

1. Controller 전체에 Tag 추가

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/members")
@Validated
@Tag(name = "Member", description = "Member API")
public class MemberController {
 
  private final MemberService memberService;
  private final MemberMapper memberMapper;
 
}

 

 

@Tag(name = "Member", description = "Member API")
  • Controller 단위에 Tag를 지정

  • API들이 Swagger UI에서 Tag 단위로 그룹핑됨, 가독성을 높여주는 설정임

 

 

2. 각 Controller에 API 메타 데이터 정보 명세

@GetMapping("/{memberId}")
@Operation(summary = "Get member profile", description = "특정 멤버의 상세 정보를 조회한다.")
@ApiResponses(value = {
    @ApiResponse(responseCode = "200", description = "성공",
        content = {@Content(schema = @Schema(implementation = MemberProfileRes.class))}),
    @ApiResponse(responseCode = "404", description = "해당 ID의 유저가 존재하지 않습니다."),
})
public MemberProfileRes getMemberProfile(
    @PathVariable
    @Positive(message = "유저 ID는 양수입니다.")
    @Schema(description = "Member ID", example = "1")
    Long memberId,
 
    // TODO: Replace with member ID from JWT or that from any other authentication method
    @Parameter(name = "loginId", description = "로그인 유저 ID 값", example = "3", required = true)
    @Positive(message = "유저 ID는 양수입니다.") @RequestParam final Long loginId,
    
    @RequestBody @Valid MemberProfileUpdateReq request
) {
  return memberMapper.toResponse(
      memberService.findProfileByMemberId(memberId, loginId)
  );
}

 

 

@Operation(summary = "Get member profile", description = "특정 멤버의 상세 정보를 조회한다.")
  • @Operation를 이용해 해당 API가 어떤 리소스를 나타내는지 간략한 설명을 추가할 수 있음

 

@ApiResponses(value = {
      @ApiResponse(responseCode = "200", description = "성공",
          content = {@Content(schema = @Schema(implementation = MemberProfileRes.class))}),
      @ApiResponse(responseCode = "404", description = "해당 ID의 유저가 존재하지 않습니다."),
  })
  • @ApiResponse를 이용해 해당 API의 Response 정보들을 나타낼 수 있다
  • 위 코드의 경우 성공, 실패 경우의 Reponse가 분리되어 @ApiResponses로 묶어줌
  • reponseCode : HTTP Status Code
  • description : 이 Response의 의미
  • content : Response 데이터 포맷, void이거나 response field에 대한 설명을 추가하지 않을 경우 생략

 

@ApiResponses(value = {
	@ApiResponse(responseCode = "200", description = "성공",
		content = {
			@Content(mediaType = "application/json", array = @ArraySchema(schema = @Schema(implementation = MemberRes.class)))
		})
})
  • Response가 List<> 형태일 때는 array 옵션을 사용

 

      @PathVariable
      @Positive(message = "유저 ID는 양수입니다.")
      @Schema(description = "Member ID", example = "1")
      Long memberId,
 
      // TODO: Replace with member ID from JWT or that from any other authentication method
      @Parameter(name = "loginId", description = "로그인 유저 ID 값", example = "3", required = true)
      @Positive(message = "유저 ID는 양수입니다.") @RequestParam final Long loginId,
 
      @RequestBody @Valid MemberProfileUpdateReq request
  • 기본적으로 Spring validation 어노테이션을 인식하기 때문에 크게 설정이 필요하지 않음
  • Swagger 제공 @Schema와 @Parameter 어노테이션을 사용하면 각 필드에 대한 설명, 예시, 필수 값 여부를 추가할 수 있음

 

 

3. Request Body, Response 각 Schema 정보 명세

Swagger Schemas 기능을 이용해 request body, response value를 확인/ 입력할 수 있음

 

@Getter
@AllArgsConstructor
@Schema(description = "Member profile update request")
public class MemberProfileUpdateReq {
 
  @NotBlank(message = "사용자 이름을 입력해주세요.")
  @Length(max = 20, message = "사용자 이름은 20글자 이하로 입력해야 합니다.")
  @Schema(description = "member name", example = "John Doe")
  private String name;
 
  @NotBlank(message = "사용자 닉네임을 입력해주세요.")
  @Length(max = 20, message = "사용자 닉네임은 20글자 이하로 입력해야 합니다.")
  @Schema(description = "member nickname", example = "johndoe")
  private String nickname;
 
  @NotBlank
  @Schema(description = "member profile emoji", example = "👨🏻‍💻")
  private String profileEmoji;
}

 

 

@Schema(description = "Member profile update request")
  • 클래스 상단에 @Schema 어노테이션을 이용해, 해당 클래스가 어떤 클래스인지 설명을 적어줄 수 있음

 

  @NotBlank(message = "사용자 이름을 입력해주세요.")
  @Length(max = 20, message = "사용자 이름은 20글자 이하로 입력해야 합니다.")
  @Schema(description = "member name", example = "John Doe")
  private String name;
  • request param, path variable과 마찬가지로 Spring validation을 인식함
  • Swagger 제공 @Schema 어노테이션을 이용해 각 필드에 대한 설명, 예시, 필수값 여부를 추가할 수 있음