Swagger UI 와 Spring RestDocs 의 장 단점의 비교
Swagger UI 의 장점
- 직관적인 UI
- 스웨거는 API 에 대한 요청과 응답 등을 시각적으로 표현하여 사용자가 쉽게 이해할 수 있습니다
- 실시간 테스트
- API 엔드포인트에 대한 실시간 테스트를 제공합니다.
Swagger UI 의 단점
- 어노테이션 수동 기입 기반 API
- 어노테이션 등을 수기로 기입하여 문서를 생성하기에, 코드와 문서간의 불일치가 발생할 수 있다.
- 유지 보수의 문제성
- API 변경시마다 스웨거 어노테이션을 수정해야한다.
- 복잡
- 코드가 진짜~ 너무 더럽다
- 어노테이션때문에 불필요하게 컨트롤러단과 DTO 단에서 피로함이 가중된다.
Spring Rest Docs 의 장점
- 정확성
- 테스트 코드를 기반으로 문서를 생성하기에 코드와 문서간 일관성 유지가 가능하다
- 가볍다
- 어노테이션을 사용하지 않기에, 코드 런타임 단계에서 오버헤드가 존재하지 않는다.
Spring Rest Docs 의 단점
- API 테스트 UI 의 부재
- 스웨거 처럼 API 를 실시간으로 테스트할 수 있는 기능이 존재하지 않는다
- 자료의 부재
- 스웨거에 비해 자료가 조금 적은 것 같다..
분명 사람들이 스웨거의 문제점과 RestDocs 의 문제점에 대해 불편해하고, 이에 대해 대안이 있지 않을까? 라는 생각을 하였다.
- 역시나, 이미 존재했다.
기반 코드 설정관련 링크
- 대략적으로 해당 방식은
- Spring REST Docs 를 사용하여 API 문서를 작성한다.
- restdocs-api-spec 오픈소스로 작성된 문서를 OAS 파일로 변환
- 변환된 OAS 파일을 스웨거 UI 에 뿌려준다.
적용
- Swagger-UI standalone, Static Routing 셋팅
- 에서
- 최신 버전을 받아서
/dist
디렉터리의 파일을 static > swagger-ui 디렉터리 생성 후- 복붙
- 해당 파일들은 스웨거 UI 를 구성할 때 필요한 html, js, css 파일들이다.
Static Routing 설정
@Configuration public class StaticRoutingConfiguration implements WebMvcConfigurer { @Override public void addResourceHandlers(@NotNull ResourceHandlerRegistry registry) { registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/"); registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/static/swagger-ui/"); } }
@Configuration
- 해당 클래스가 Spring 의 설정 정보를 담고 있음을 나타냄
- Bean 등록으로 Spring Container 에서 관리되도록 설정
WebMvcConfigurer
- Spring MVC 의 기본 설정을 오버라이딩하고 커스텀 하기 위한 인터페이스 구현 설정
addResourceHandlers(ResourceHandlerRegistry registry)
- 정적 리소스를 처리하기 위한 핸들러 오버라이딩
- ResourceHandlerRegistry 를 사용하여 URI 패턴과 해당 패턴에 맞는 리소스 위치 매핑
- 일단 /static 은 기본적으로 존재하는 URI 임
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/static/swagger-ui/");
- 는 swagger-ui.html 경로를 설정해주기 위해 핸들러 설정을 해준 것 같다.
- 정적 리소스를 처리하기 위한 핸들러 오버라이딩
Swagger file 수정
- 그대로 수행함.
- 에서
2.2.2. ePages-de/restdocs-api/spec을 이용한 OAS 출력
- 그대로 수행
2.2.3. 생성된 OAS 파일을 swagger 디렉토리로 복사하는 스크립트 작성(copyOasToSwagger)
- 그대로 수행
- 하지만,
tasks.named('test') { useJUnitPlatform() finalizedBy('copyOasToSwagger') // test 태스크 실행 후에 copyOasToSwagger 태스크 실행 } openapi3 { server = 'https://localhost:8080' // list로 넣을 수 있어 각종 환경의 URL들을 넣을 수 있음! title = 'ITEM-BROWSER API' description = 'ITEM-BROWSER API' version = '0.0.1' format = 'yaml' // or json } tasks.register('copyOasToSwagger', Copy) { // 기존 yaml 파일 삭제 delete 'src/main/resources/static/swagger-ui/openapi3.yaml' // 복제할 yaml 파일 타겟팅 from "$buildDir/api-spec/openapi3.yaml" // 타겟 디렉토리로 파일 복제 into 'src/main/resources/static/swagger-ui' // openapi3 task가 먼저 실행되도록 설정 dependsOn tasks.named('openapi3') }
문제 상황:
- OpenAPI 3 태스크를 설정할 때,
copyOasToSwagger
태스크가 실행되지 않는 문제가 있었습니다.
발생 원인:
openApi3
태스크를 설정하면,test
태스크가 자동으로 실행됩니다.
- 테스트 코드를 실행할 때도
test
태스크가 함께 실행되는데, 이로 인해copyOasToSwagger
태스크의 실행 타이밍이 불분명해졌습니다.
해결 방법:
copyOasToSwagger
태스크가 항상test
태스크가 완료된 후에 실행되도록 설정을 변경했습니다.finalizedBy('copyOasToSwagger') // test 태스크 실행 후에 copyOasToSwagger 태스크 실행
- 위의 요 부분이 test 태스크 이후에 로컬에 OAS 파일을 복사붙여넣기하는 과정임.
- OpenAPI 3 태스크를 설정할 때,
결과
- openapi3 task 수행
- 어플리케이션 실행
- 아래 결과
2024.01.07 추가
그래들 설정 변경
- openApi3 스프링 프로파일에 따라 기본 server 도메인 설정이 변경되어야 할 필요성이 생겼다.
과정
- gradle 스프링 프로파일 현재 설정값을 들고와야함
// properties file load... def loadProperties() { Properties props = new Properties() file("src/main/resources/application.properties").withInputStream { props.load(it) } return props }
- Gradle Api 라이브러리와 File 자바 내장 라이브러리를 활용
-
application.properties
의 파일을 withInputStream 으로 읽어온다- withInputStream 메서드
public static Object withInputStream(File file, @ClosureParams(value = SimpleType.class,options = {"java.io.InputStream"}) Closure closure) throws IOException { return IOGroovyMethods.withStream(newInputStream(file), closure); }
- 등등 의 내부 소스를 타고 들어가보면 newInputStream 의 경우 단순한 BufferedInputStream (File …) 의 형태를 띄고 있다.
- 8192 의 바이트 크기만큼 단순히 파일을 읽는 형태이다.
- closure 를 매개변수로 받는데
- 클로저 내에서 사용자가 InputStream 을 활용해서 필요한 작업을 수행하게 되는데, 해당 메서드 수행이 끝나면 try-with-resource 와 유사한 패턴으로 리소스 누수 방지를 위해 스트림을 닫는 역할을 할 뿐만 아니라
file("src/main/resources/application.properties").withInputStream { props.load(it) }
withInputStream { stream → … (하나의 스트림 단위의 로직 ) } 을 수행할 수 있게 해준다.
이는 내부에 중요 로직을 작성하고, 외부에서 별도 수행해야하는 사용자 정의 로직이 있는 경우 주입이 가능하도록 한다.
- 재사용성에 장점이 있다고 생각한다.
- 암튼, 위 예제에서는 props 객체안에 load 를 수행하여
public synchronized void load(InputStream inStream) throws IOException { Objects.requireNonNull(inStream, "inStream parameter is null"); load0(new LineReader(inStream)); }
- 입력 스트림을 매개변수로 전달
load0
메서드에 LineReader 객체로 전달하여- Properties 타입의 객체안에 key - value 형태로 속성을 저장하는 로직이다.
- 입력 스트림을 매개변수로 전달
- 위와 같은 과정을 수행하여
- Properties 타입의 객체를 반환값으로 전달하여
openapi3 { def props = loadProperties() def profile = props.hasProperty('spring.profiles.active') ? props.get('spring.profiles.active') : 'local' def domainKey = "app.domain.${profile}" def domain = props[domainKey] println("domainKey: ${domainKey}") server = domain title = 'ITEM-BROWSER API' description = 'ITEM-BROWSER API' version = '0.0.1' format = 'yaml' // or json }
- 에서 props 안에 ‘spring.profiles.active’ 키 값이 있는지 확인 후
- 없으면 local 값으로, 있으면 해당 프로파일값을 설정
- 해당 키값의 프로파일에 맞는 domainKey 를 불러와서 server 에 세팅해준다.
- 에서 props 안에 ‘spring.profiles.active’ 키 값이 있는지 확인 후
- Properties 타입의 객체를 반환값으로 전달하여
- 등등 의 내부 소스를 타고 들어가보면 newInputStream 의 경우 단순한 BufferedInputStream (File …) 의 형태를 띄고 있다.
- withInputStream 메서드
-
- Gradle Api 라이브러리와 File 자바 내장 라이브러리를 활용
2024.01.29
- API 인증시 JWT 토큰관련 문제점 발생으로
- 아래 풀리퀘스트를 던졌다.
- JWT 토큰 인증을 위해서는 별도의 openapi3 방식의 인증 헤더를 던질 수 있어야 한다.
- 스웨거 파일이 만들어지기 전에 별도의 JWT 인증을 위한 securitySchemas 와 Security 요소를 추가한다.
Uploaded by N2T
'자바 > 리팩토링' 카테고리의 다른 글
[프로젝트] RestDocs 커스텀 유저 모킹 `@MockMember` 적용기 (0) | 2024.02.02 |
---|---|
[리팩토링] properties 파일을.. yaml 로 변경하면서 생긴, gradle 문제 해결 (0) | 2024.01.19 |
스웨거와 RestDocs 의 장점을 뽑아내기 (0) | 2024.01.03 |
[프로젝트] 스웨거 example 설정시 String.format 등의 동적 값 삽입이 불가능함 (0) | 2023.12.19 |
[리팩토링] 스웨거 DTO 가 schema 에 보이지 않는 이유와 DTO 어노테이션 설정법 (0) | 2023.12.17 |