RESTful API 서비스를 개발하고 문서화하는 데 도움을 주는 오픈소스로 간단하게 설명하면 Swagger는 API Spec 문서이다. API를 엑셀이나 가이드 문서를 통해 관리하는 방법은 주기적인 업데이트가 필요하기 때문에 관리가 쉽지 않고 시간이 오래 걸린다.
그래서 Swagger를 사용해 API Spec 문서를 자동화해 주어 간편하게 API문서를 관리한다.
Springfox와 Springdoc의 특징을 확인하고 환경과 상황에 맞는 라이브러리를 사용하는 것을 추천한다.
Springdoc은 업데이트가 지속적으로 되고 있고 Springfox는 2020년 이후로 업데이트가 되지 않기에 Springdoc을 사용하기로 결정했다.
특징
단점

특징

<!-- Swagger -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.7.0</version>
</dependency>
1.7.0 버전이 가장 많이 사용되고 있기에 1.7.0 선택
springdoc:
api-docs:
path: /api-docs # OpenAPI 문서의 기본 경로 설정 / defualt는 /v3/api-docs
swagger-ui:
#default path는 /swagger-ui/index.html
operations-sorter: alpha # alpha(알파벳 오름차순), method(HTTP메소드순)
tags-sorter: alpha # 태그 정렬 순서. alpha: 알파벳 순 정렬,
disable-swagger-default-url: true # swagger-ui default url인 petstore html의 비활성화 설정
display-request-duration: true # 요청 소요 시간 표시
default-consumes-media-type: application/json # request media type 의 기본 값
default-produces-media-type: application/json # response media type 의 기본 값
paths-to-match: # 해당 패턴에 매칭되는 Restcontroller만 swagger-ui에 노출한다.
- /api/v1/**
- /devlog/**
permit:
url:
...
/swagger-ui/**,
/api-docs/**
Spring Security에서 제외되어야 할 URL경로에도 포함시켜 준다.
swagger-ui.path 설정을 하지 않으면 http://localhost:{port}/swagger-ui/index.html로 접속하면된다.
@Configuration
@RequiredArgsConstructor
@EnableWebSecurity
public class SecurityConfiguration {
@Value("${permit.url}")
private final String[] PERMIT_ALL_RESOURCES;
@Bean
protected SecurityFilterChain configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
...
.authorizeHttpRequests(authorize -> authorize
.requestMatchers(this.getPermitAllResources()).permitAll()
.requestMatchers(new AntPathRequestMatcher("/admin/**")).hasAuthority("ADMIN")
.requestMatchers(new AntPathRequestMatcher("/api/v1/**")).hasAnyAuthority("USER", "ADMIN")
.anyRequest().authenticated())
...
return httpSecurity.build();
}
private AntPathRequestMatcher[] getPermitAllResources() {
AntPathRequestMatcher antPathRequestMatcherArr[] = new AntPathRequestMatcher[PERMIT_ALL_RESOURCES.length];
for(int nIdx = 0 ; nIdx < PERMIT_ALL_RESOURCES.length ; nIdx++) {
antPathRequestMatcherArr[nIdx] = new AntPathRequestMatcher(PERMIT_ALL_RESOURCES[nIdx]);
}
return antPathRequestMatcherArr;
}
}
Swagger관련 URL은 Spring Security에서 접근허용설정
Spring Security 자세한 내용은 아래 포스팅을 참조하기 바람.
Spring Security + JWT 인증 (2/2) - Spring Security 설정
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.devlog.common.jwt.JwtProvider;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import lombok.RequiredArgsConstructor;
@Configuration
@RequiredArgsConstructor
public class SwaggerConfiguration {
private final JwtProvider jwtProvider;
@Bean
public OpenAPI openAPI() {
String jwtSchemeName = jwtProvider.getJwtHeader();
SecurityRequirement securityRequirement = new SecurityRequirement().addList(jwtSchemeName);
SecurityScheme securityScheme = new SecurityScheme();
securityScheme.name(jwtSchemeName);
securityScheme.description("JWT 입력");
securityScheme.type(SecurityScheme.Type.HTTP);
securityScheme.scheme(jwtProvider.getJwtType());
securityScheme.bearerFormat(jwtProvider.getJwtType());
Components components = new Components().addSecuritySchemes(jwtSchemeName, securityScheme);
return new OpenAPI()
.addSecurityItem(securityRequirement)
.components(components)
.info(apiInfo());
}
private Info apiInfo() {
return new Info()
.title("DevLog Swagger")
.description("DevLog Swagger Description ... ")
.version("1.0.0");
}
}
JWT 인증을 사용하고 있기 때문에 Swagger로 API를 테스트할 때 JWT 토큰이 필요하다.
위에 설정으로 Swagger에서 JWT 토큰값을 입력한 후 API 테스트를 할 수 있다.



@Tag
@Slf4j
@RequestMapping("/devlog/quartz")
@RestController
@RequiredArgsConstructor
@Tag(name = "Quartz", description = "Quartz 테스트 - JWT 인증 필요없음")
public class QuartzTestController {
...
}
@Operation
@Operation(summary = "Quartz Scheduler 등록", description = "Quartz Scheduler 등록 - JWT 인증 필요없음")
@GetMapping("/addJob")
public ResponseEntity<Void> addJob( ... ) {}
@Parameter
@Parameters({
@Parameter(name = "jobName", description = "Quartz Schduler Job Name")
,@Parameter(name = "jobGroup", description = "Quartz Schduler Job Group")
,@Parameter(name = "jobDesc", description = "Quartz Schduler Job Description")
,@Parameter(name = "cronExpr", description = "Quartz Schduler Cron Expression - Ex) 0 * * * * ?")
})
@Operation(summary = "Quartz Scheduler 등록", description = "Quartz Scheduler 등록 - JWT 인증 필요없음")
@GetMapping("/addJob")
public ResponseEntity<Void> addJob(String jobName, String jobGroup, String jobDesc, String cronExpr) {
...
}
@Operation(summary = "Quartz Scheduler 삭제", description = "Quartz Scheduler 삭제 - JWT 인증 필요없음")
@GetMapping("/deleteJob")
public ResponseEntity<Void> deleteJob(
@Parameter(name = "jobName", description = "Job Name") @RequestParam(required = true) String jobName
,@Parameter(name = "jobGroup", description = "Job Group") @RequestParam(required = true) String jobGroup) {
...
}
변수에 직접 설정할 수 있고 메서드에 설정할 수 있다. 개인적으로 메서드에 설정하는 게 가독성 있다고 생각된다.

@Schema
@Setter
@Getter
@Entity
@Schema(description = "User Entity")
@Table(name = "DEV_USER")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(description = "사용자ID")
private long userId;
@Column(unique = true, nullable = false)
@Schema(description = "E-Mail")
private String email;
@Column(nullable = false)
@Schema(description = "패스워드")
private String password;
@Column(nullable = false)
@Schema(description = "사용자명")
private String username;
@Column(nullable = false)
@Schema(description = "권한", example = "ADMIN or USER")
private String authority;
}

@ApiResponse
@ApiResponses({
@ApiResponse(responseCode = "200", description = "성공"),
@ApiResponse(responseCode = "400", description = "실패")
})
댓글 영역