스프링 버전이 3.x대로 넘어오면서 기존에 사용하던 Springfox가 호환이 되지않는 문제가 있습니다.
해결을 위한 SpringDocs 사용법에 대한 가이드 포스팅
예전 스프링 2.x대에서 사용하던 Springfox 라이브러리가 메인테이너되지 않는 문제가 발생하여 레퍼런스를 참조하던 중 3.x에서 Swagger를 적용하기 위해서는 SpringDocs 라이브러리를 활용하여 구축하여야 한다는 솔루션을 찾게 되었습니다.
이번 포스팅은 처음 써보는 SpringDocs에 대한 기본 작성 가이드를 구축하기 위한 포스팅이며, 오타/오역/오류가 있다면 언제든지 말씀하여 주시면 감사하겠습니다.
Swagger 구축 환경
SpringBoot : 3.2.2
JDK : 17
build Tools : gradle
Editor : InteliJ
build.gradle 의존성 추가
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2' //Swagger
Springboot 3.x 버전에서 스웨거 적용을 위한 의존성을 한 줄 추가해줍니다.
Config 클래스 파일 작성
Swagger 사용을 위한 설정 파일을 작성해줍니다.
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 // 스프링 실행시 설정파일 읽어드리기 위한 어노테이션
public class SwaggerConfig {
@Bean
public OpenAPI openAPI() {
return new OpenAPI()
.components(new Components())
.info(apiInfo());
}
private Info apiInfo() {
return new Info()
.title("CodeArena Swagger")
.description("CodeArena 유저 및 인증 , ps, 알림에 관한 REST API")
.version("1.0.0");
}
}
@Configuration으로 빈 등록과 동시에 설정파일임을 알립니다.
apiInfo 메서드의 경우 아래 그림과 같이 제목, 설명, 버전과 같이 Swagger에서 보여줄 API 정보를 설정하는 부분입니다.
혹은 application.yml이나 application.properties 설정으로 대신할 수 있습니다.
위는 기본적인 설정이며, 자세한 설정은 다음 공식문서를 참조하시면 좋을 것 같습니다.
https://springdoc.org/#properties
GUI Swagger 접속
http://서버주소:포트번호/swagger-ui/index.html#/
해당 URL로 접속을 하면 상단 그림처럼 접속 후 설정된 Swagger를 확인할 수 있습니다.
혹시라도 접속이 안된다면 application 설정에서 아래와 같은 context path를 변경하는 구문이 없는지 확인합시다.
server.servlet.context-path=/api
만약 context Path가 설정되어 있다면 URL에 추가하여 접속하시면 됩니다.
혹은 임의의 경로로 Swagger 접속 URL을 변경하고 싶다면 아래와 같은 속성을 추가하면 됩니다.
springdoc.swagger-ui.path=/swagger-ui.html
위 설정 적용 시 최종 URL : 서버주소:포트/swagger-ui.html
Annotation
직접적인 적용에 앞서 자주 사용하는 각 태그에 대해 설명하겠습니다.
@Tag
API Endpoint가 어떤 그룹에 속하는지 알려주는 그룹핑 어노테이션입니다.
> Controller단에 설정하면 해당 Controller의 정보를 나타낼 수 있습니다.
다음으로는 @Tag를 Controller에 적용했을 경우의 과정입니다.
@RestController
@RequiredArgsConstructor
@Tag(name = "알림 API", description = "컨트롤러에 대한 설명입니다.")
@RequestMapping("/alarm")
public class AlarmController implements AlarmControllerDocs{
...
...
}
아래는 Controller 상단 어노테이션으로 위와 같이 @Tag를 설정해주었을 때의 Swagger 문서 모습입니다.
@Operation
현재 REST API Endpoint에 대한 요약, 설명 등을 지정하는 용도로 사용됩니다.
아래는 사용 가능한 옵션 목록입니다.
✴︎ operationId: Endpoint 아이디
✴︎ summary: 간단한 설명으로 Swagger-ui의 Endpoint 상단에 노출됩니다.
✴︎ description: 엔드포인트 상세 설명입니다.
✴︎ tags: 현재 Endpoint가 어떠한 tag 그룹에 속한지 알려주는 속성입니다.
✴︎ response: 응답코드, 응답 타입 등을 지정합니다.
✴︎ security: 보안방법에 대한 설정을 지정합니다.
다음으로는 @Operation를 /alarm/receive이라는 Endpoint에 적용했을 경우의 과정입니다.
@Operation(summary = "수신함 리스트", description = "파라미터로 받은 유저가 수신한 알림 목록을 최신순으로 정렬하여 전달")
@GetMapping("/receive")
public ResponseEntity<AlarmResultDto> receive(@RequestParam String userId) {
...
}
아래는 어노테이션 적용 시 모습입니다.
@Parameter
해당하는 Endpoint의 파라미터의 타입과 입력에 대해 설명을 지정합니다.
아래는 사용 가능한 옵션 목록입니다.
✴︎ name: 파리미터 이름
✴︎ description: 파리미터 설명
✴︎ required: 필수/선택 여부 (true이면 필수, false이면 선택입니다.)
✴︎ in: 파리미터의 타입을 지정합니다.
✴︎ ParameterIn.QUERY: 요청 쿼리 파라미터입니다.
✴︎ ParameterIn.HEADER: 요청 헤더에 전달되는 파라미터입니다.
✴︎ ParameterIn.PATH: PathVariable 에 속하는 파라미터입니다.
✴︎ 값없음: RequestBody에 해당하는 객체 타입의 파라미터를 나타냅니다.
다음으로는 @Parameter를 코드에 적용했을 경우의 과정입니다.
@Operation(summary = "알림 송신", description = "필요 파라미터 : 알림타입, 수신자ID, 송신자ID, 알림내용 || 알림타입이 1 또는 2라면 status=요청 대기, 3,4라면 status=처리 완료")
@Parameter(name = "alarmType", description = "보내는 알림의 타입")
@Parameter(name = "toId", description = "받는 유저의 아이디")
@Parameter(name = "fromId", description = "보내는 유저의 아이디")
@Parameter(name = "alarmMsg", description = "알림의 내용")
@Parameter(name = "alarmStatus", description = "알림의 처리상태, Required = false || 요청 시 보내지 않아도 자동 처리됨.")
@PostMapping("/send")
public ResponseEntity<?> send(@RequestBody AlarmSendDto alarmSendDto) {
...
}
메서드 상단 어노테이션도 가능하지만 매개변수 단에도 @Parameter가 적용됩니다.
아래는 어노테이션 적용 시 모습입니다.
@Response
해당하는 Endpoint의 파라미터의 응답 구조를 나타냅니다.
아래는 사용 가능한 옵션 목록입니다.
✴︎ responseCode : HTTP 상태 코드입니다.
✴︎ description : 응답 결과 구조에 대한 설명입니다.
✴︎ content : 응답 페이로드 구조입니다.
✴︎ schema : 페이로드에서 사용하는 객체 스키마입니다.
다음으로는 @Response를 코드에 적용했을 경우의 과정입니다.
@Operation(summary = "알림 송신", description = "필요 파라미터 : 알림타입, 수신자ID, 송신자ID, 알림내용 || 알림타입이 1 또는 2라면 status=요청 대기, 3,4라면 status=처리 완료")
@ApiResponse(responseCode = "200", description = "수신함 조회 성공", content = @Content(schema = @Schema(implementation = AlarmReceiveDto.class)))
@PostMapping("/send")
public ResponseEntity<?> send(@RequestBody AlarmSendDto alarmSendDto) {
...
}
responseCode, description 옵션 외에도 schema를 content로 활용도 한번 해보았습니다.
아래는 어노테이션 적용 시 모습입니다.
@Schema
DTO 객체를 설명을 추가하는 어노테이션입니다.
아래는 사용 가능한 옵션 목록입니다.
✴︎ description : 멤버변수에 대한 설명입니다.
✴︎ defaultValue : 기본값을 설정하는 옵션입니다.
다음으로는 @Schema를 코드에 적용했을 경우의 과정입니다.
첫번째로 DTO에 대한 @Schema 설명만 추가했을 경우의 모습입니다.
@Data
@Schema(description = "알림 송신 DTO")
public class AlarmSendDto {
private int alarmType;
private int toId;
private int fromId;
private String alarmMsg;
private String alarmStatus;
}
이제 각 멤버변수에도 @Schema를 달아준 뒤의 모습을 보겠습니다.
@Data
@Schema(description = "알림 송신 DTO")
public class AlarmSendDto {
@Schema(description = "알림 타입 >> 1 : 문제 생성 요청, 2 : 문제 수정 요청, 3 : 게임 초대, 4 : 공지사항")
private int alarmType;
@Schema(description = "수신자 ID")
private int toId;
@Schema(description = "송신자 ID")
private int fromId;
@Schema(description = "보내는 내용")
private String alarmMsg;
@Schema(description = "알림 상태", defaultValue = "요청 대기")
private String alarmStatus;
}
확실히 이전 보다 더 알아보기 쉬운 문서로 바뀐걸 확인할 수 있습니다.
최적화
위에서 알아본 어노테이션들을 활용하면 좀 더 알아보기 쉬운 Swagger 문서를 작성할 수 있습니다.
하지만 Controller단에 덕지덕지 어노테이션을 활용하여 작성하게 된다면 아무래도 코드의 가독성을 떨어뜨릴 수 있다는 단점이 있습니다.
해당 부분을 해결할 수 있도록 추가적인 Swagger 전용 인터페이스를 추가하여 Controller는 해당 인터페이스를 상속받게만 한다면 Controller의 가독성을 헤치지 않으면서 수많은 어노테이션을 포함한 읽기 쉬운 Swagger 문서를 작성할 수 있습니다.
다음은 기존 Controller에서 어노테이션 매핑을 한 상태입니다.
@Operation(summary = "알림 송신", description = "필요 파라미터 : 알림타입, 수신자ID, 송신자ID, 알림내용 || 알림타입이 1 또는 2라면 status=요청 대기, 3,4라면 status=처리 완료")
@Parameter(name = "alarmType", description = "보내는 알림의 타입")
@Parameter(name = "toId", description = "받는 유저의 아이디")
@Parameter(name = "fromId", description = "보내는 유저의 아이디")
@Parameter(name = "alarmMsg", description = "알림의 내용")
@Parameter(name = "alarmStatus", description = "알림의 처리상태, Required = false || 요청 시 보내지 않아도 자동 처리됨.")
@PostMapping("/send")
public ResponseEntity<?> send(@RequestBody AlarmSendDto alarmSendDto) {
return new ResponseEntity<AlarmResultDto>(alarmService.send(alarmSendDto), HttpStatus.OK);
}
메서드 내부로직은 단 한줄인데 어노테이션만 한가득 있는 모습이 충분히 Controller의 가독성을 방해하고 있다는 걸 알 수 있습니다.
이제는 인터페이스를 활용하여 Swagger와 가독성을 모두 챙겨 보도록 하겠습니다.
AlarmControllerDocs.Interface
@Tag(name = "알림 API", description = "알림에 관한 Controller **알림 타입 = 1 : 문제 생성 요청, 2 : 문제 수정 요청, 3 : 게임 초대, 4 : 공지사항 **")
public interface AlarmControllerDocs {
@Parameters(value = {
@Parameter(name = "alarmType", description = "보내는 알림의 타입"),
@Parameter(name = "toId", description = "받는 유저의 아이디"),
@Parameter(name = "fromId", description = "보내는 유저의 아이디"),
@Parameter(name = "alarmMsg", description = "알림의 내용"),
@Parameter(name = "alarmStatus", description = "알림의 처리상태, Required = false || 요청 시 보내지 않아도 자동 처리됨.")
})
@Operation(summary = "알림 송신", description = "필요 파라미터 : 알림타입, 수신자ID, 송신자ID, 알림내용 || 알림타입이 1 또는 2라면 status=요청 대기, 3,4라면 status=처리 완료")
@ApiResponses(value = {
@ApiResponse(responseCode = "201", description = "알림 송신 완료", content = @Content(schema = @Schema(implementation = AlarmSendDto.class)))
})
public ResponseEntity<?> send(AlarmSendDto alarmSendDto);
}
인터페이스에서 send 메서드에 대한 Swagger 설정을 똑같이 완료했습니다.
이제 Controller로 돌아가서 해당 인터페이스를 상속 후 Swagger-ui를 확인해보겠습니다.
AlarmController
@RestController
@RequiredArgsConstructor
@RequestMapping("/alarm")
public class AlarmController implements AlarmControllerDocs{
@PostMapping("/send")
public ResponseEntity<?> send(@RequestBody AlarmSendDto alarmSendDto) {
...
}
}
Swagger-ui.html
정상적으로 Swagger 문서가 작성된 걸 확인할 수 있습니다.
이런 방식으로 인터페이스를 활용하여 Controller 가독성을 지키며, 깔끔한 Swagger문서를 작성할 수 있습니다.
추가로 위 인터페이스에서 사용한 것처럼 @Parameters 및 @ApiResponses 어노테이션을 활용하면 N개의 @Parameter, @ApiResponse를 묶어서 표현할 수 있으니 참고하면 좋을 것 같습니다.
'Develop > SpringBoot' 카테고리의 다른 글
[JWT] JSON Web Token - With JWT.io (0) | 2024.03.11 |
---|---|
세션(Session) & 쿠키(Cookie) (0) | 2024.02.23 |
[SpringBoot] 웹소켓(WebSocket) (0) | 2024.01.31 |
[Jasypt] application.properties 설정 암호화 (0) | 2024.01.29 |
[채팅 서버] Springboot + STOMP를 활용한 채팅 구현 (1) | 2024.01.26 |
개발 기술 블로그, Dev
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!