Trouble
스프링부트 2.x대에서 사용하던 Springfox 라이브러리가 메인테이너되지 않는 이슈...
진짜 별의별 레퍼런스를 찾아봐도 내 기존 Springfox콜랍 코드를 낫게해줄 처방전은 없었다ㅠ
Solution
스프링부트 3.x에서 Swagger를 적용하기 위해서는 SpringDocs 라이브러리를 활용해야함.
과정이 궁금한 사람들도 있을테지만, 이 블로그는 나의 동의보감 이니까 결론적인것만 담아보겠다.ㅎ
Result(결론적인 해결과정)
Step 1 : build.gradle
많고 많은 의존성들이 있지만,
내 개발 환경과, Swagger를 위한 의존성만 남겨보자면,
plugins {
id 'java'
id 'org.springframework.boot' version '3.2.3'
id 'io.spring.dependency-management' version '1.1.7'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
// Swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0'
}
tasks.named('test') {
useJUnitPlatform()
}
코끼리 빙글뱅글 돌려주시고,
Step 2 : SwaggerConfig
@Configuration 어노테이션으로 설정파일임을 알리고,
@Bean 빈 등록 해주기.
package com.example.backend.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 SwaggerConfig {
@Bean
public OpenAPI openAPI() {
return new OpenAPI()
.components(new Components())
.info(apiInfo());
}
private Info apiInfo() {
return new Info()
.title("SpringBoot Swagger")
.description("유저, 결제, 토큰 관리 및 fastAPI 연결")
.version("1.0.0");
}
}
apiInfo 메서드는, 사진 보면 감이 올텐데,
'제목, 설명, 버전'같이, API 정보를 설정하는 부분이다.
( Step 3 : WebSecurityConfig )
SpringSecurity 를 사용하고있으므로, permitAll에 swagger관련된 부분도 추가해주자.
Step 4 : Docs, 꾸미기💄
Controller, DTO의 Docs를 예쁘게 작성하는 방법은 여러가지가 있는데, 예시를 작성해두겠다.
아마 이 예시 긁어다가 우리 모두의 친구 GPT에게 "이거 참고해서 내 컨트롤러의 Docs를 작성해줘"라고 부탁하면,
기깔난 XxxControllerDocs 인터페이스를 작성해줄것이다.
ex 1. Controller
// 패키지 구조를 어찌 잡았나 궁금한 사람들을 위해~
// package com.example.backend.swagger;
@Tag(name = "User", description = "회원 관련 API")
public interface UserControllerDocs {
@Operation(
summary = "회원가입",
description = "사용자가 회원가입을 진행합니다.",
requestBody = @RequestBody(
required = true,
content = @Content(schema = @Schema(implementation = SignUpRequestDTO.class))
),
responses = {
@ApiResponse(
responseCode = "200",
description = "회원가입 성공",
content = @Content(schema = @Schema(implementation = UrlResponseDTO.class))
),
@ApiResponse(responseCode = "400", description = "요청 형식 오류"),
@ApiResponse(responseCode = "500", description = "서버 에러")
}
)
ResponseEntity<UrlResponseDTO> signup(@RequestBody SignUpRequestDTO signUpRequestDTO);
@Operation(
summary = "튜토리얼 상태 업데이트",
description = "인증된 사용자의 튜토리얼 상태를 업데이트합니다. (토큰 기반 인증 필요)",
responses = {
@ApiResponse(responseCode = "200", description = "튜토리얼 상태가 업데이트되었습니다.",
content = @Content(schema = @Schema(implementation = String.class))),
@ApiResponse(responseCode = "401", description = "인증 정보가 없습니다.",
content = @Content(schema = @Schema(implementation = String.class))),
@ApiResponse(responseCode = "404", description = "사용자를 찾을 수 없습니다.",
content = @Content(schema = @Schema(implementation = String.class))),
@ApiResponse(responseCode = "409", description = "튜토리얼 상태 업데이트 중 충돌 발생",
content = @Content(schema = @Schema(implementation = String.class))),
@ApiResponse(responseCode = "500", description = "서버 에러가 발생하였습니다.",
content = @Content(schema = @Schema(implementation = String.class)))
}
)
ResponseEntity<?> updateTutorialStatus(@AuthenticationPrincipal SecurityUserDto authenticatedUser);
// 더 있지만, 일단 얘만 남겨둘래용ㅎ
}
이걸 작성해두고,
다음과 같이, 컨트롤러 가셔서 implements 해주세요~
public class UserController implements UserControllerDocs {
private final UserService userService;
@PostMapping("/signup")
public ResponseEntity<UrlResponseDTO> signup(@RequestBody SignUpRequestDTO signUpRequestDTO) {
userService.saveUser(signUpRequestDTO); // 회원가입 진행 (DB 저장)
return ResponseEntity.ok(
UrlResponseDTO.builder()
.message("회원가입을 성공했습니다.")
.build()
);
}
@PostMapping("/tutorial")
public ResponseEntity<?> updateTutorialStatus(@AuthenticationPrincipal SecurityUserDto authenticatedUser) {
// 인증 정보 확인
if (authenticatedUser == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("인증 정보가 없습니다.");
}
try {
// 튜토리얼 상태 업데이트 로직 실행
userService.tutorialComplete(authenticatedUser);
return ResponseEntity.ok("튜토리얼 상태가 업데이트되었습니다.");
} catch (UsernameNotFoundException e) {
// 예: 사용자 정보가 DB에 없을 경우
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("사용자를 찾을 수 없습니다.");
} catch (IllegalStateException e) {
// 예: 이미 튜토리얼이 완료된 상태라면
return ResponseEntity.status(HttpStatus.CONFLICT).body("튜토리얼 상태 업데이트 중 충돌 발생: " + e.getMessage());
} catch (Exception e) {
// 그 외의 예상치 못한 오류 처리
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("서버 에러가 발생하였습니다: " + e.getMessage());
}
}
}
ex 2. DTO
이건 Docs 만들어서 할 필요 없이 Schema 어노테이션 사용하셔요~
@Getter
@Setter
@Schema(description = "회원가입 요청 DTO")
public class SignUpRequestDTO {
@Schema(description = "사용자 이메일", example = "user@example.com")
private String email;
@Schema(description = "비밀번호", example = "securePassword123")
private String password;
@Schema(description = "비밀번호 확인", example = "securePassword123")
private String confirmPassword;
@Schema(description = "이름", example = "홍길동")
private String name;
@Schema(description = "나이", example = "28")
private int age;
@Schema(description = "전화번호", example = "01012345678")
private String phone;
@Schema(description = "회원 구분 (APPLICANT or HR)", example = "APPLICANT")
private String role;
@Schema(description = "회사명 (HR 전용)", example = "ABC Corp")
private String companyName;
@Schema(description = "사업자등록번호 (HR 전용)", example = "123-45-67890")
private String businessNumber;
}
접속, 확인
// 예시) http://localhost:8080/swagger-ui/index.html
http://서버주소:포트번호/swagger-ui/index.html
이런식으로 주소창에 입력해서 확인 가능하다.
적용 결과 예시
로컬이라면, 인텔리제이(사용하시는 IDE) 실행시키고,
위의 URL 접속해서,
궁금한 엔드포인트 펼쳐보면 짜자잔~
다음 사진에서 Request body에 Example Value 옆의 Schema를 눌러보셔도 되지만,
아래 쭉 내려보면 Schemas 모아둔곳도 있답니다!
'Web > SrpingBoot' 카테고리의 다른 글
스프링과 객체 지향 프로그래밍 - SOLID(객체 지향 설계의 5가지 원칙) 관점 (0) | 2024.05.05 |
---|---|
스프링과 객체 지향 프로그래밍 - 다형성 관점 (0) | 2024.04.24 |
스프링부트3 백엔드 - 3. 스프링 부트 3 구조 이해하기 (2) | 2023.10.08 |
스프링부트3 백엔드 - 2. 스프링 부트 3 시작하기(스프링과 스프링부트 차이, IoC, DI, AOP, PSA ) (0) | 2023.08.26 |
스프링부트3 백엔드 - 1. 자바 백엔드 개발자가 알아두면 좋은 지식 (0) | 2023.08.17 |