목표
Swagger에 대해 알아보고, JWT Security가 적용된 Springfox 3.0을 적용해보겠습니다.
목차
개요
팀 프로젝트를 진행하면서 가장 고된 사항은 바로 API 정보를 프론트와 공유하는 것이었습니다.
노션에 직접 URL, 파라미터, 결과 등을 적어줬습니다. 프로그램이 커지니까 실수도 잦아지고, 찾기도 힘들었습니다.
이에 대한 해결책인 Swagger에 대해 알아보고, 적용을 해보도록 하겠습니다.
Swagger란?
Swagger는 OAS(Open Api Specification)를 위한 오픈소스 프레임워크입니다. 즉, API의 문서를 자동으로 정리해주는 것 입니다.
해당 Swagger를 협업하는 개발자에게 전달하면 Path, Request, Response, 제약 조건 등을 한 번에 알 수 있습니다. API 문서 자동화 뿐만 아니라, Swagger를 통해 파라미터를 넣어보고 테스트를 진행할 수 있습니다.
프론트 개발자는 API에서 요구하는 Request를 작성하고, Swagger에서 직접 테스트 후 코드에 반영하곤 합니다.
API 문서를 작성하는 시간을 절약할 수 있고, API 정보를 실시간으로 유지할 수 있다는 장점이 존재합니다.
특히, 개인적으로 휴먼에러를 최소화할 수 있다는 점이 상당한 매리트라고 생각합니다.
이러한 Swagger는 다양한 Tool이 존재합니다.다음 Swagger.io 홈페이지를 보면 3가지 Tool을 제공합니다.
1. Swagger Codegen
Codegen은 server stubs client SDKs를 생성할 수 있게 해줍니다. 즉, Swagger에서 정의한 설계대로 개발을 진행할 수 있도록 합니다. 모든 언어를 지원하고 있지는 않습니다.
2. Swagger Editor
Swagger Editor는 Swagger를 온라인에서 작성할 수 있는 것이 특징입니다. Cloud 환경에서 작업할 수도 있고, Editor를 다운받아서 사용할 수도 있습니다. Docker를 통해 이미지로 제공하고 있습니다.
3. Swagger UI
Swagger의 표준에 맞춰 UI를 작성해주는 에디터입니다.
Springfox와 Springdoc
이러한 Sagger를 Spring에서 쉽게 사용할 수 있도록 도와주는 라이브러리가 2개가 존재합니다.
바로, Springdoc와 Springfox 입니다. Springfox가 잘 사용되다 개발을 멈춘 적이 있다고 합니다. 그래서 SpringDoc이 개발되어 사용되고 있었습니다. 하지만, Springfox가 다시 개발을 시작하면서 많이 사용되기 시작했습니다.
우리는 무엇을 사용해야 할까요? 사람들은 Springfox를 더 많이 사용하고 있습니다. (출처:MVNRepsoitory)
Springfox 순위: 364 (https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui)
Springdoc 순위: 1484 (https://mvnrepository.com/artifact/org.springdoc/springdoc-openapi-ui)
문서화 및 테스트 등 기능에 큰 차이가 없고, 로직에 영향을 주지 않으므로, 많이 사용되는 Springfox를 적용시켜보도록 하겠습니다.
Springfox 3.0.0 적용
Springfox를 통해 기본적인 문서화 및 테스트 기능을 제공하는 Swagger를 작성할 것입니다.
제 프로젝트는 JWT 기반의 인증을 사용하고 있기 때문에, 테스트 할 때 JWT 기반의 인증을 제공할 수 있도록 설정할 것입니다.
환경: Spring boot 2.6.4, Gradle
1. 의존성 추가
Swagger를 생성하고 작업하는데 있어서 필요한 의존성을 추가합니다.
implementation 'io.springfox:springfox-boot-starter:3.0.0'
Spring boot 환경에선 자동으로 설정이 되지만, Spring 환경에서는 리소스 핸들러를 추가로 설정해야 합니다.
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
2. Swagger 설정
아래와 같이, @Configuration을 사용해서 설정 파일임을 명시하고, Docket 빈을 중심으로 Swagger 설정을 진행합니다.
참고로, 3.0으로 넘어오면서 @EnableSwagger2 는 사용하지 않아도 됩니다.
@Configuration
public class SwaggerConfig {
private String version = "V0.1";
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.apiInfo(apiInfo())
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("제목")
.description("설명")
.version(version)
.contact(new Contact("이름", "홈페이지 URL", "e-mail"))
.build();
}
}
select() 메소드는 ApiSelectorBuilder 클래스의 인스턴스를 반환합니다. 해당 인스턴스를 통해 Swagger의 end-point를 제어할 수 있습니다.
또한, RequestHandlerSelectors를 any()로 설정한다면, 전체 API에 대한 문서를 Swagger를 통해 나타낼 수 있습니다.
ApiInfoBuilder()를 통해 API에 대한 정보를 설정할 수 있습니다. Swagger 상단에 표시될 제목, 설명, 버전, 작성자 정보 등을 표시합니다.
3. API 적용
REST API를 위한 Controller에 Swagger에 작성할 명세(Specification)을 작성합니다.
//Controller
@Api(tags = "컨트롤러이름")
@RestController
public class SwaggerExample {
@Operation(summary="요약", description="설명")
@ApiResponse(code = 200, message="ok")
@PostMapping("/ex/")
public ResponseDto exampleMethod() {
return new ResponseDto();
}
}
//DTO
@Data
public class ResponseDto {
@ApiModelProperty(value="필드 값", example="예시", required=true)
private String a1;
@ApiModelProperty(value="필드 값", example="예시")
private String a2;
}
컨트롤러 클래스 단에, @Api 어노테이션을 사용하면, Swagger에 표시되는 컨트롤러 이름을 설정할 수 있습니다. 클래스 명을 그대로 띄우는 것보다, 컨트롤러의 역할이나 기능같이 한 번에 이해하기 좋은 표현을 권장합니다. 왜냐하면, API 명세는 협업을 위해 다른 사람들과 함께 공유하는 자료이기 때문입니다.
컨트롤러 내부에 있는 메소드에 @Operation 어노테이션을 통해 해당 URL에 대한 명세를 작성할 수 있습니다.
다양한 옵션이 존재하지만, 크게 summary와 description이 있습니다. summary는 Swagger의 메인에서 바로 확인할 수 있고, description은 해당 URL을 선택했을 때 보이는 자세한 설명입니다. 이 외에도, 응답에 대한 정보를 나타내는 @ApiResponse 등이 존재합니다.
DTO도 @ApiModelProperty 어노테이션을 활용해 필요한 명세를 작성할 수 있습니다.
해당 어노테이션의 옵션은 다음과 같습니다.
- value : 해당 값을 파라미터 예시로 보여준다
- example : 모델에 대한 부연 설명을 확인할 때 나타난다.
- required : 꼭 필요한 인자를 표시한다.
4. Swagger 확인
Swagger는 서버를 실행시킨 뒤, http://localhost:8080/swagger-ui/ 혹은 http://localhost:8080//swagger-ui/index.html에서 확인할 수 있습니다.
해당 URL이 번거롭다면, redirect를 활용해서 URL을 연결해 쉽게 접근할 수 있습니다.
@Controller
public class ExampleController {
@Getmapping("/api/doc/")
public String redirectSwagger() {
return "redirect:/swagger-ui/index.html";
}
}
명세 자동화, 테스트 등 기본적인 Swagger는 완성이 되었습니다.
JWT를 적용해 테스트할 수 있도록 Security를 추가해보도록 하겠습니다.
5. JWT Security 적용
아까 작성했던 Swagger 설정 파일을 일부 수정해야 합니다.
@Configuration
public class SwaggerConfig {
private String version = "V0.1";
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.apiInfo(apiInfo())
.securityContexts(Arrays.asList(securityContext())) // *
.securitySchemes(Arrays.asList(apiKey())); // **
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("제목")
.description("설명")
.version(version)
.contact(new Contact("이름", "홈페이지 URL", "e-mail"))
.build();
}
//*** 하단 추가
//ApiKey 정의
private ApiKey apiKey() {
return new ApiKey("JWT", "Authorization", "header");
}
//JWT SecurityContext 구성
private SecurityContext securityContext() {
return SecurityContext.builder().securityReferences(defaultAuth()).build();
}
private List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEveryThing");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
return Arrays.asList(new SecurityReference("JWT", authorizationScopes));
}
}
JWT를 인증 헤더로 사용하기 위해 ApiKey를 정의해야 합니다.
이후, 전역 AuthorizationScope를 사용해 JWT SecurityContext를 구성합니다. 이후, ApiKey와 보안 컨텍스트 및 체계를 Docket 빈에 추가합니다.
그럼 다음과 같이 자물쇠가 생긴 것을 확인할 수 있습니다.
자물쇠를 클릭하면 다음과 같이 JWT 값을 입력할 수 있습니다.
값을 입력하고 테스트를 직접 해보도록 하겠습니다.
보시는 것과 같이, 헤더에 토큰이 같이 날라가는 것을 확인할 수 있습니다.
정리
이상으로 Swagger를 위한 springfox에 대한 게시글을 마무리하겠습니다.API 문서를 작성하는 것은 생각보다 고된 일이었습니다. Swagger를 통해 원활한 협업에 한층 더 가까워진 것 같습니다.
아래 문서를 참고하여 작성된 글입니다.
1. Springfox 공식 문서
https://springfox.github.io/springfox/docs/current/#getting-started
2. Springfox 기본 설정
https://www.baeldung.com/swagger-2-documentation-for-spring-rest-api
3. SpringfoxSecurity 적용
https://www.baeldung.com/swagger-2-documentation-for-spring-rest-api
'개발 > Spring & Spring boot' 카테고리의 다른 글
[Spring] 내가 ~Service, ServiceImpl로 분리하는 이유 (0) | 2023.03.01 |
---|---|
[Spring] 전역 예외 처리를 위한 @ControllerAdvice와 @RestControllerAdvice (0) | 2022.07.06 |
[Spring] JPA, RDS, MySQL 연동 시 연결 안됨-CommunicationsException (0) | 2022.04.04 |