언어 공부/springboot로 백엔드 개발

Springboot에서 swagger 사용하기

카페인곰 2025. 5. 3. 16:22

 

 

 

1. build.gradle에 의존성을 추가합니다

// Swagger/OpenAPI 의존성
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0'

 

이후 gradel reload 해주세요

 

 

2. config 패키지를 만들고 아래와 같이 구성 클래스를 추가합니다.

 

 

config 폴더에

OpenApiConfig 클래스 추가

package restapi.prac.config;

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 OpenApiConfig {

    @Bean
    public OpenAPI openAPI() {
        Info info = new Info()
                .title("Spring Boot 게시판 API")
                .version("v1.0")
                .description("Spring Boot를 이용한 게시판 API 명세서");

        return new OpenAPI()
                .components(new Components())
                .info(info);
    }
}

 

 

3. 모델, 컨트롤러에 어노테이션을 추가합니다.

 

● 원본 Post model 코드

package restapi.prac.model;

import jakarta.persistence.*;

@Entity
public class Post {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String title;

    @Column(nullable = false, length = 5000)
    private String content;

    // 기본 생성자
    public Post() {}

    // 생성자
    public Post(String title, String content) {
        this.title = title;
        this.content = content;
    }

    // getter, setter
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

 

 

  swagger 문서화 적용 코드 

package restapi.prac.model;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.*;

@Entity
@Schema(description = "게시글 정보")
public class Post {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Schema(description = "게시글 고유 ID", example = "1")
    private Long id;

    @Column(nullable = false)
    @Schema(description = "게시글 제목", example = "안녕하세요")
    private String title;

    @Column(nullable = false, length = 5000)
    @Schema(description = "게시글 내용", example = "Spring Boot 학습중입니다.")
    private String content;

    // 기본 생성자
    public Post() {}

    // 생성자
    public Post(String title, String content) {
        this.title = title;
        this.content = content;
    }

    // getter, setter
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

 

 

 

  • @Schema 어노테이션은 Swagger UI에서 모델의 스키마를 문서화합니다.
  • description 속성은 해당 필드에 대한 설명을 추가합니다.
  • example 속성은 해당 필드의 예시 값을 보여줍니다.
  • 이를 통해 API 사용자가 각 필드의 의미와 사용 방법을 쉽게 이해할 수 있습니다.
@Schema(description = "게시글 정보")

 

형태가 클래스와 각 필드 위에 추가됨

 

 

● 원본 PostController 코드

 

package restapi.prac.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import restapi.prac.model.Post;
import restapi.prac.service.PostService;

import java.util.Optional;

@RestController
@RequestMapping("/api/posts")
public class PostController {

    @Autowired
    private PostService postService;

    // 게시글 목록 조회 (페이지네이션 적용: 기본 페이지 번호=0, 페이지 사이즈=10)
    @GetMapping
    public ResponseEntity<Page<Post>> listPosts(@RequestParam(defaultValue = "0") int page,
                                                @RequestParam(defaultValue = "10") int size) {
        Pageable pageable = PageRequest.of(page, size);
        Page<Post> posts = postService.getPosts(pageable);
        return ResponseEntity.ok(posts);
    }

    // 게시글 단건 조회
    @GetMapping("/{id}")
    public ResponseEntity<Post> getPost(@PathVariable Long id) {
        Optional<Post> postOpt = postService.getPost(id);
        return postOpt.map(ResponseEntity::ok)
                .orElseGet(() -> ResponseEntity.notFound().build());
    }

    // 게시글 생성
    @PostMapping
    public ResponseEntity<Post> createPost(@RequestBody Post post) {
        Post createdPost = postService.createPost(post);
        return ResponseEntity.ok(createdPost);
    }

    // 게시글 수정
    @PutMapping("/{id}")
    public ResponseEntity<Post> updatePost(@PathVariable Long id, @RequestBody Post updatedPost) {
        Optional<Post> updated = postService.updatePost(id, updatedPost);
        return updated.map(ResponseEntity::ok)
                .orElseGet(() -> ResponseEntity.notFound().build());
    }

    // 게시글 삭제
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deletePost(@PathVariable Long id) {
        boolean deleted = postService.deletePost(id);
        if (deleted) {
            return ResponseEntity.ok().build();
        } else {
            return ResponseEntity.notFound().build();
        }
    }
}

 

 

● swagger 적용 PostController 코드

 

package restapi.prac.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import restapi.prac.model.Post;
import restapi.prac.service.PostService;

import java.util.Optional;

@RestController
@RequestMapping("/api/posts")
@Tag(name = "게시글 API", description = "게시글 CRUD API")
public class PostController {

    @Autowired
    private PostService postService;

    @Operation(summary = "게시글 목록 조회", description = "페이지네이션을 적용하여 게시글 목록을 조회합니다.")
    @GetMapping
    public ResponseEntity<Page<Post>> listPosts(
            @Parameter(description = "페이지 번호 (0부터 시작)") @RequestParam(defaultValue = "0") int page,
            @Parameter(description = "페이지 크기") @RequestParam(defaultValue = "10") int size) {
        Pageable pageable = PageRequest.of(page, size);
        Page<Post> posts = postService.getPosts(pageable);
        return ResponseEntity.ok(posts);
    }

    @Operation(summary = "게시글 조회", description = "ID로 특정 게시글을 조회합니다.")
    @GetMapping("/{id}")
    public ResponseEntity<Post> getPost(
            @Parameter(description = "게시글 ID") @PathVariable Long id) {
        Optional<Post> postOpt = postService.getPost(id);
        return postOpt.map(ResponseEntity::ok)
                .orElseGet(() -> ResponseEntity.notFound().build());
    }

    @Operation(summary = "게시글 생성", description = "새로운 게시글을 생성합니다.")
    @PostMapping
    public ResponseEntity<Post> createPost(
            @Parameter(description = "게시글 정보") @RequestBody Post post) {
        Post createdPost = postService.createPost(post);
        return ResponseEntity.ok(createdPost);
    }

    @Operation(summary = "게시글 수정", description = "기존 게시글을 수정합니다.")
    @PutMapping("/{id}")
    public ResponseEntity<Post> updatePost(
            @Parameter(description = "게시글 ID") @PathVariable Long id,
            @Parameter(description = "수정할 게시글 정보") @RequestBody Post updatedPost) {
        Optional<Post> updated = postService.updatePost(id, updatedPost);
        return updated.map(ResponseEntity::ok)
                .orElseGet(() -> ResponseEntity.notFound().build());
    }

    @Operation(summary = "게시글 삭제", description = "게시글을 삭제합니다.")
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deletePost(
            @Parameter(description = "게시글 ID") @PathVariable Long id) {
        boolean deleted = postService.deletePost(id);
        if (deleted) {
            return ResponseEntity.ok().build();
        } else {
            return ResponseEntity.notFound().build();
        }
    }
}

 

 

 

 

  • @Tag: 컨트롤러를 Swagger UI에서 하나의 그룹으로 분류합니다.
  • @Operation: 각 API 엔드포인트의 목적과 기능을 설명합니다.
  • @Parameter: API 요청 파라미터의 의미와 용도를 문서화합니다.
  • 이러한 어노테이션을 통해 API의 사용 방법과 목적이 명확하게 문서화됩니다.

각 클래스와 메서드 위에 추가

// 클래스 위에 추가
@Tag(name = "게시글 API", description = "게시글 CRUD API")

// 각 메서드 위에 추가
@Operation(summary = "게시글 목록 조회", description = "페이지네이션을 적용하여 게시글 목록을 조회합니다.")
// listPosts 메서드 위에

@Parameter(description = "페이지 번호 (0부터 시작)")
// @RequestParam(defaultValue = "0") int page 파라미터 앞에

@Parameter(description = "페이지 크기")
// @RequestParam(defaultValue = "10") int size 파라미터 앞에

 

 

 

테스트

 

http://localhost:8080/swagger-ui.html

 

로 접속합니다.

 

 

 

 

200으로 성공 확인

 

실제 mysql workbench에 들어간 것을 확인