본문 바로가기
Back-end/Spring

[Spring] SpringBoot 3버전 Swagger 적용 및 활용

by 랑 이 2025. 4. 6.
반응형

Swagger란?

REST API를 설계하고 문서화하여 테스트하는데 사용되는 프레임워크

어노테이션을 통해서 쉽게 API 문서를 HTML으로 자동생성하는 기능을 제공하고 있습니다

 

프론트엔드와 같이 협업하는 과정에서 유용하게 활용할 수 있습니다

 

구현했던 API를 직접 문서로 작성할 필요 없이 Swagger를 사용하여 자동으로 생성한 API 문서로 확인하면 되기 때문에 편리하며

생산성 측면에 있어서 효율이 좋기 때문에 자주 사용되고 있습니다 


Swagger 적용하기

Spring Boot 버전에 따라서 적용하는 방법이 다르게 적용됩니다

2.x 버전 이하springdoc-openapi 1.x 를 사용하여 프레임워크를 사용합니다

 

의존성 추가 (Gradle) 

dependencies {
    implementation 'org.springdoc:springdoc-openapi-ui:1.6.15'
}

 

하지만 요즘은 3 버전을 주로 사용하는 것으로 보이고 진행해 볼 프로젝트 버전이 3.4.4 버전이기 때문에 3.4.4 버전을 기반으로 설명합니다

SpringBoot 3 버전 이상 Swagger 적용

개발환경

-  Java: 17

-  SpringBoot: 3.4.4

-  Gradle: 8.13

-  IDE: IntelliJ IDEA 2024.3.1.1 Ultimate

 

의존성 추가 (Gradle)

implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.6")

인텔리제이의 스타터추가 기능에서 따로 제공하지 않기 때문에 직접 의존성추가해줘야 합니다

우측 상단에 코끼리 모양이 나오면 클릭해서 적용

Swagger UI 접속

Spring Boot 서버 실행하여 http://localhost:8080/swagger-ui/index.html 접속

접속하면 생성된 API 문서를 확인할 수 있습니다

Controller에 구현했던 APImethod 별로 나열되어 있습니다

 

아무 설정 없이도 URL에 접속하면 Swagger UI를 볼 수 있지만 수동으로 커스텀하여 사용할 수도 있습니다


Swagger UI API 테스트

원래는 Postman이나 브라우저 확장 프로그램으로 테스트를 진행했는데 Swagger UI를 통해서도 간단하게 테스트가 가능합니다

 

글 작성 API 테스트 (POST)

각 API가 있는데 테스트를 진행할 API를 누르면 상세정보가 나오게 됩니다

저는 작성을 먼저 테스트하기 위해 글 작성 API를 선택했습니다

 

우측 상단에 있는 Try it out 버튼을 클릭

요청을 보내는데 필요한 Request Body 값을 입력할 수 있는 창이 나오게 됩니다

JSON 형태에 맞게 작성 후 밑에 있는 Execute를 눌러 요청을 보내줍니다

응답값을 확인해 보면 정상적으로 요청이 처리되었습니다

 

Swagger UI 테스트도 Postman처럼 직접 요청을 보내기 때문에 실제로 DB에 데이터가 저장이 됩니다


Swagger 어노테이션

Swagger에서 제공하는 어노테이션을 사용하면 다양하게 API문서를 커스터 마이징 할 수 있습니다

1. Controller

@Operation 어노테이션

  • API의 대한 요약과 상세 설명을 설정
  • 설정한 요약과 상세 설명은 Swagger UI에 적용

1. 요약 설명과 상세 설명 지정

@Operation(summary = "게시글 작성", description = "게시글을 작성합니다")

summary = "[요약 설명]", description = "[상세 설명]" 

@Operation(summary = "게시글 작성", description = "게시글을 작성합니다")
@PostMapping("/posts")
public ResponseEntity<PostResponseDto> addPost(@RequestBody @Valid PostRequestDto dto){
    Post post = postService.save(dto);
    return ResponseEntity.status(HttpStatus.CREATED)
            .body(new PostResponseDto(post));
}

 

@Parameter 어노테이션

  • API의 요청 파라미터 상세 정보를 설정
  • @PathVable,@RequestParam,@RequestHeader에 적용

1. 단순 설명 및 예시 값 지정

@Parameter(description = "조회할 게시글 ID",example = "1",required = true)
@PathVariable Long id

description = "[파라미터 설명]", example = "[예시값]", required = "[필수 여부(false, true)]"

@GetMapping("/posts/{id}")
public ResponseEntity<PostResponseDto> findPost(@Parameter(description = "조회할 게시글 ID",example = "1",required = true) @PathVariable Long id){ // URL에서 값을 가져옴
    Post post = postService.findById(id);
    return ResponseEntity.ok().body(new PostResponseDto(post));
}

 

2. Swagger UI에서 숨기기

@Parameter(hidden = true)
@PathVariable Long id

hidden = "[숨길 여부]"

 

@ApiResponse 어노테이션

  • API의 응답 정보(HTTP 상태코드,설명,타입)을 설정

1. HTTP Status code 및 설명 지정

@ApiResponse(
    responseCode = "201",
    description = "등록 성공"
)

responseCode="[응답코드]", description="[응답 설명]"

@ApiResponse(
        responseCode = "201",
        description = "등록 성공"
)
@PostMapping("/posts")
public ResponseEntity<PostResponseDto> addPost(@RequestBody @Valid PostRequestDto dto){
    Post post = postService.save(dto);
    return ResponseEntity.status(HttpStatus.CREATED)
            .body(new PostResponseDto(post));
}

@Hidden 어노테이션

  • API를 Swagger UI에 표시하지 않도록 숨기기
  • 테스트 API,외부로 유출되지 않아야 하는 API에 사용됩니다
@Hidden
@GetMapping("/posts/{id}")
public ResponseEntity<PostResponseDto> findPost(@Parameter(description = "조회할 게시글 ID",example = "1",required = true) @PathVariable Long id){ // URL에서 값을 가져옴
    Post post = postService.findById(id);
    return ResponseEntity.ok().body(new PostResponseDto(post));
}

숨길 API에 @Hidden 어노테이션 명시

PostController 최종 적용

import com.domain.openboard.domain.Post;
import com.domain.openboard.dto.*;
import com.domain.openboard.service.PostService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;

@RequiredArgsConstructor
@RequestMapping("/api") // 전체 기본 경로
@RestController // HTTP Response Body에 객체 데이터를 JSON 형식으로 변환
public class PostController {

    private final PostService postService;

    // 게시글 작성 API
    @Operation(summary = "게시글 작성", description = "게시글을 작성합니다")
    @ApiResponse(responseCode = "201",description = "등록 성공")
    @PostMapping("/posts")
    public ResponseEntity<PostResponseDto> addPost(@RequestBody @Valid PostRequestDto dto){
        Post post = postService.save(dto);
        return ResponseEntity.status(HttpStatus.CREATED)
                .body(new PostResponseDto(post));
    }

    // 게시글 목록 조회 API
    @Operation(summary = "게시글 목록 조회",description = "게시글 전체 목록을 조회합니다")
    @ApiResponse(responseCode = "200",description = "조회 성공")
    @GetMapping("/posts")
    public ResponseEntity<List<PostListResponseDto>> findAllPosts() {
        List<PostListResponseDto> postListDto = postService.findAll()
                .stream()                               // 컬렉션(List)를 Stream 객체로 변환
                .map(PostListResponseDto::new)          // 각 Post 객체를 PostListResponseDto로 변환
                .toList();                              // 변환된 요소들을 새로운 리스트(List<PostListResponseDto>)로 수집
                                                        // List<Post> -> Stream -> PostListResponseDto -> List<PostListResponseDto>
        return ResponseEntity.ok().body(postListDto);
    }

    // 게시글 단건 조회 API
    @Operation(summary = "게시글 조회",description = "게시글 id로 단건 조회합니다")
    @ApiResponses({
            @ApiResponse(responseCode = "200",description = "조회 성공"),
            @ApiResponse(responseCode = "404",description = "해당 게시글을 찾을 수 없음")
    })
    @GetMapping("/posts/{id}")
    public ResponseEntity<PostResponseDto> findPost(@Parameter(description = "조회할 게시글 ID",example = "1",required = true) @PathVariable Long id){ // URL에서 값을 가져옴
        Post post = postService.findById(id);
        return ResponseEntity.ok().body(new PostResponseDto(post));
    }

    // 게시글 삭제 API
    @Operation(summary = "게시글 삭제",description = "게시글을 삭제합니다 작성한 게시글의 비밀번호,글 번호를 받습니다")
    @ApiResponses({
            @ApiResponse(responseCode = "200",description = "삭제 성공"),
            @ApiResponse(responseCode = "404",description = "해당 게시글을 찾을 수 없음")
    })
    @DeleteMapping("/posts/{id}")
    public ResponseEntity<Void> deletePost(@Parameter(description = "삭제할 게시글 ID",example = "1",required = true)@PathVariable Long id, @RequestBody PostPasswordDto dto){
        postService.delete(id,dto.getPassword());
        return ResponseEntity.ok().build();
    }

    // 게시글 수정 API
    @Operation(summary = "게시글 수정",description = "게시글을 수정합니다 작성한 게시글의 비밀번호,글 번호를 받습니다")
    @ApiResponses({
            @ApiResponse(responseCode = "200",description = "수정 성공"),
            @ApiResponse(responseCode = "404",description = "해당 게시글을 찾을 수 없음")
    })
    @PutMapping("/posts/{id}")
    public ResponseEntity<PostResponseDto> updatePost(@Parameter(description = "수정할 게시글 ID",example = "1",required = true)@PathVariable Long id, @RequestBody PostUpdateRequestDto dto){
        Post post = postService.update(id,dto);
        return ResponseEntity.ok().body(new PostResponseDto(post));
    }
}

Swagger UI 확인

@Operation 어노테이션

각 API를 보면 작성한 요약으로 설명한 값이 적용된 걸 볼 수 있습니다

 

@Parameter 어노테이션

상세 보기를 눌러보면 상세 내용과 파라미터 설명이 적용완료

 

@ApiResponse 어노테이션

밑에 Response도 보면 HTTP 상태 코드도 적용이 완료되었습니다

2. DTO

@Schema 어노테이션

  • DTO 클래스의 필드의 상세 정보를 설정
  • 필드의 설명,예시 값 등을 설정할 수 있음
@Schema(description = "게시글 제목",example = "제목")

description = "[필드 설명]",example = "예시값"

 

PostListResponseDto 최종 적용

import com.domain.openboard.domain.Post;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;

@Getter
public class PostListResponseDto {

    @Schema(description = "게시글 제목",example = "제목")
    private final String title;

    @Schema(description = "게시글 내용",example = "내용")
    private final String content;

    @Schema(description = "게시글 작성자",example = "이름")
    private final String name;

    public PostListResponseDto(Post post) {
        this.title = post.getTitle();
        this.content = post.getContent();
        this.name = post.getName();
    }
}

@Schema 어노테이션

DTO는 하단에 있는  Schemas를 보면 확인할 수 있습니다 적용이 잘된 걸로 보입니다

3. 보안 문서 전역 설

@Tag 어노테이션

  • API 및 Controller 그룹화하여 상세설명 설정
  • 설정한 상세설명은 Swagger UI 문서 상단에 표시
@Tag(name = "Post API",description = "작성글 관련 기능 API")

name = "[태그 이름]", description = "[상세 내용]"

 

@SecurityRequirement 어노테이션

  • 인증이 필요한 API임을 명시
  • Swagger UI에서 "Authorize" 버튼 활성화 및 토큰 입력 기능 추가
@SecurityRequirement(name = "bearerAuth")

보안 관련 설정을 위해서 SwaggerConfig 클래스를 생성하고 다음과 같이 작성해야 사용이 가능

SwaggerConfig

package com.domain.openboard.config;

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 org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SwaggerConfig {

    @Bean
    public OpenAPI customOpenAPI() {
        return new OpenAPI()
                .info(new Info().title("Open Board API 문서")
                        .description("커뮤니티 게시판 서비스 API")
                        .version("1.0"))
                .addSecurityItem(new SecurityRequirement())
                .components(new Components()
                        .addSecuritySchemes("bearerAuth", new SecurityScheme()
                        .type(SecurityScheme.Type.HTTP)
                        .scheme("bearer")
                        .bearerFormat("JWT")));
    }
}

SwaggerConfig 클래스를 통해서 API 문서의 제목,내용,버전을 명시할 수 있습니다

작성한 내용 그대로 API 문서에 적용된 걸 볼 수 있습니다

우측에 있는 Authorize를 누르면 인증 API 사용이 가능합니다

 

반응형