스웨거 코드가 최대한 프로젝트 코드를 해치지 않는 방법으로 각 컨트롤러들의 인터페이스를 만들어 준다. 인터페이스 안에 스웨거 코드를 적어준다.
FollowController(현재 존재하는 컨트롤러) -> FollowControllerDoc (새로 추가하는 인터페이스)

위에 사진처럼 controller 패캐지 안에 swaggerInterface 패캐지를 만들어서 저 안에 컨트롤러 인터페이스들을 보관하면 깔끔히 분리된다. 이 방법은 이렇게 컨트롤러에는 스웨거에 관한거는 implements FollowControllerDoc 이부분만 추가되기 때문에 프로젝트 코드가 스웨거코드에 의해 최소한으로 침혜된다.
public class FollowController implements FollowControllerDoc {
FollowController 안에 있는 api들을 복사해서 FollowControllerDoc에 가져온다.
단 인터페이스니까 구현부가 없어야 한다 바디{}와 어노테이션들을 다 정리 해주고 이제 스웨거 어노테이션을 잔뜩 올려주면 된다.
public interface FollowControllerDoc {
Response<Void> followAMember(Authentication authentication, @PathVariable Long following);
Response<Void> unfollowAMember(Authentication authentication, @PathVariable Long followingId);
Response<Void> deleteAFollower(Authentication authentication, @PathVariable Long followerId);
Response<Page<FollowListResponse>> followList(Authentication authentication, Pageable pageable, @PathVariable int or);
}
@Tag(name="") 그룹화
swagger-UI에서는 콘트롤러만다 기본으로 api들이 그룹으로 묶인다.
@Tag 를 인터페이스 맨 위에 달아주면 그 그룹 이름을 정할수 있다.
@Tag(name = "팔로우APIs")
public interface FollowControllerDoc {
Tag는 description 까지 설정할수있지만 컨트롤러나 api의 @Tag는 그냥 name만 선언해주고 나중에 SwaggerConfig에서 순서 정해줄때 거기서 description까지 한번에 설정하는게 좋다.
예)@Tag(name = "팔로우APIs",description = "meep") 할수 있지만 SwaggerConfig에서 한번에 정의하기.

이렇게 Tag로 그룹 이름을 정할수있다.
콘트롤러 위에 태그를 붙이면 그 안에 API들이 다 묶이지만 조금 더 섬세한 작업을 하고 싶다면 그 안에 API들마다 Tags()를 사용해서 묶어줄수 있다. 콘트롤러 위에랑 api위에 둘다 했다면 두 그룹에 똑같은 api가 존재할 것이다.
또 api에서도 tags에 {}를 사용하면 @Tags{"로그인 필요없는 APIs","팔로우 APIs"}를 사용하면 여러개의 그룹에 속하게 할수있다.
@Operation(summary = "게시글 검색 기능-좋아요 순", description = "로그인 없이 요청 가능한 기능입니다."
,tags = "공개APIs")
Response<Page<PostListResponse>> LikedList();
//한개의 그룹에 속한다
@Operation(summary = "게시글 검색 기능-좋아요 순", description = "로그인 없이 요청 가능한 기능입니다."
,tags = {"공개APIs","팔로우APIs"})
Response<Page<PostListResponse>> LikedList();
//두개의 그룹에 속한다
OpenAPiCustomizer 사용해서 Tag 태그, Api 순서 정렬해주기
여러쉬운 길을 찾았지만 안되더라 SwaggerConfig에 설정해주면 간단하게 된다. Tag 이름이랑 Description까지 적어주면 완벽!
@Bean
public OpenApiCustomizer customTagsOrder() {
return openApi -> {
openApi.setTags(Arrays.asList(
new Tag().name("공개APIs").description("로그인 필요없는 APIs: 회원가입, 로그인 API- 로그인/가입 후 response header에서 JWT 토큰을(Bearer) 복사해서 인증(Authorize) 해주세요. 이 글 위에 오른쪽에 [Authorize] 버튼누르고 붙여넣기하면 됩니다."),
new Tag().name("메인페이지APIs").description("게시글 좋아요순, 팔로우순 다건 조회"),
new Tag().name("게시글쓰기APIs").description("게시글 쓰기, 수정, 삭제 기능"),
new Tag().name("게시글조회APIs").description("게시글 단건조회, 다건조회(맴버 아이디로 특정맴버 조회, 내 게시글 조회)기능"),
new Tag().name("댓글쓰기APIs").description("게시글 댓글 쓰기, 수정, 삭제 기능"),
new Tag().name("좋아요APIs").description("특정 게시글 좋아요하기, 좋아요 취소하기 기능"),
new Tag().name("팔로우APIs").description("팔로우 기능+ 팔로우,팔로워 다건 조회 기능"),
new Tag().name("검색APIs").description("키워드 검색기능(게시글:최신순,추천순),(맴버:팔로워순,최근게시글순)"),
new Tag().name("토큰재발급APIs").description("토큰 재발급 요청을 합니다.")
));
};
}
위에 코드처럼 원하는 API tag 순서대로 적어주면 된다.
@Configuration
public class SwaggerConfig {
// JWT 인증 스키마 생성
private SecurityScheme createAPIKeyScheme() {
return new SecurityScheme()
.type(SecurityScheme.Type.HTTP) // HTTP 기반 인증
.bearerFormat("JWT") // Bearer 포맷 사용
.scheme("bearer"); // Bearer 스키마
}
// OpenAPI 설정
@Bean
public OpenAPI OpenAPIConfig() {
return new OpenAPI()
// 인증 요구 사항 추가
.addSecurityItem(new SecurityRequirement().addList("JWT")) // JWT 인증 추가
.components(new Components()
.addSecuritySchemes("JWT", createAPIKeyScheme()))// JWT 인증 스키마
.info(new Info()
.title("PIZZA KOALA API")
.description("PizzaKoala OpenAPI documentation. \n\n[JWT 인증 방법 영상 보기]X가림X 로그인api에서 jwt토큰을 발급받아서 수동으로 인증해주세요.") // 마크다운 형식으로 영상 링크 추가
.version("1.0"));
}
@Bean
public OpenApiCustomizer customTagsOrder() {
return openApi -> {
openApi.setTags(Arrays.asList(
new Tag().name("공개APIs").description("로그인 필요없는 APIs: 회원가입, 로그인 API- 로그인/가입 후 response header에서 JWT 토큰을(Bearer) 복사해서 인증(Authorize) 해주세요. 이 글 위에 오른쪽에 [Authorize] 버튼누르고 붙여넣기하면 됩니다."),
new Tag().name("메인페이지APIs").description("게시글 좋아요순, 팔로우순 다건 조회"),
new Tag().name("게시글쓰기APIs").description("게시글 쓰기, 수정, 삭제 기능"),
new Tag().name("게시글조회APIs").description("게시글 단건조회, 다건조회(맴버 아이디로 특정맴버 조회, 내 게시글 조회)기능"),
new Tag().name("댓글쓰기APIs").description("게시글 댓글 쓰기, 수정, 삭제 기능"),
new Tag().name("좋아요APIs").description("특정 게시글 좋아요하기, 좋아요 취소하기 기능"),
new Tag().name("팔로우APIs").description("팔로우 기능+ 팔로우,팔로워 다건 조회 기능"),
new Tag().name("검색APIs").description("키워드 검색기능(게시글:최신순,추천순),(맴버:팔로워순,최근게시글순)"),
new Tag().name("토큰재발급APIs").description("토큰 재발급 요청을 합니다.")
));
};
}
}
안되면 왜떄문인지 나도 모르지만 혹시 모르니 yml파일 확인해보자
springdoc:
api-docs:
tags-sorter:
swagger-ui:
disable-swagger-default-url: true
이렇게만 해도 되던뎀...
@Operation 사용해서 api들 마다 설명 적어주기.

@Tag(name = "팔로우APIs")
public interface FollowControllerDoc {
@Operation(summary = "맴버 팔로우 기능", description = "다른 유저를 팔로우하는 기능")
Response<Void> followAMember(Authentication authentication, @PathVariable Long following);
}
FollowControllerDoc에 이렇게 적어주면 api들 마다 기능 설명을 적을수 있다.
그리구 아까 위에 @Tag에서 설명한것 처럼 api에 그룹으로 개별로 묶어줄려고 한다면 tags를 @Operation안에 사용하면 된다.
@Tag(name = "팔로우APIs")
public interface FollowControllerDoc {
@Operation(summary = "맴버 팔로우 기능", description = "다른 유저를 팔로우하는 기능",tags = {"기능APIs","맴버APIs"})
Response<Void> followAMember(Authentication authentication, @PathVariable Long following);
}
한개만 tag할떈 {}대신 ()을 사용한다. tags=("맴버APIs")
@Parameter 사용해서 PathVariable에 들어갈 예제 미리 넣어주기

@PathVariable 앞에 @Paramemter를 적용해준다.
@Parameter(
description = "해당 게시글 아이디 입력. *성공예제를 먼저 실행해 주세요.*",
examples = {
@ExampleObject(name = "예시1-성공", value = "4"),
@ExampleObject(name = "예시2-자신을 팔로우할 경우", value = "1"),
@ExampleObject(name = "예시3-이미 팔로우한 게시글일 경우", value = "2"),
@ExampleObject(name = "예시4-존재하지않는 계정일 경우", value = "10004")
}
@Tag(name = "팔로우APIs")
public interface FollowControllerDoc {
/**
* 유저 팔로우 하기
*/
@Operation(summary = "맴버 팔로우 기능", description = "다른 유저를 팔로우하는 기능",tags = {"팔로우APIs","맴버APIs"})
Response<Void> followAMember(Authentication authentication,
@Parameter(
description = "해당 게시글 아이디 입력. *성공예제를 먼저 실행해 주세요.*",
examples = {
@ExampleObject(name = "예시1-성공", value = "4"),
@ExampleObject(name = "예시2-자신을 팔로우할 경우", value = "1"),
@ExampleObject(name = "예시3-이미 팔로우한 게시글일 경우", value = "2"),
@ExampleObject(name = "예시4-존재하지않는 계정일 경우", value = "10004")
}
) @PathVariable Long following);
}
@APIResponses, APIResponse 사용해서 반환하는 코드 미리 작성해주기

response가 반환하는 형식과 다양한 에러코드 반환을 한눈에 알수있게 작성해준다.
햇갈리면 안되는데 복수인 @APIResponses(value={}) 안에 여러 response예제들을 작성해야한다 ->@APIResponse()
@ApiResponses(value = {
@ApiResponse(),
@ApiResponse(),
@ApiResponse()
})
@APIResponse안에는 이렇게 하나씩 작성해주면 된다.
@ApiResponse(responseCode = "200", description = "유저를 팔로우합니다.",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = PostCreateRequest.class),
examples = @ExampleObject(
name = "맴버 팔로우 성공 예제",
value = """
{
"resultCode": "SUCCESS"
}
"""
)))
근데 어떤 경우에는 에러코드가 같지만 다른 exception을 반환해야할떄가 있다. 그럴떄는 examples=에 {}이걸 붙여주고 @ExampleObject들을 넣어주면 된다.
@ApiResponse(responseCode = "401", description = "자신의 계정을 팔로우하려 한 경우, 로그인을 안한 경우",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorCode.class),
examples = {@ExampleObject(
name = "1_로그인해야 사용할수있는 기능입니다",
value = """
{
"resultCode": "INVALID_TOKEN",
"message": "Full authentication is required to access this resource"
}
"""
), @ExampleObject(
name = "2_자신의 계정을 팔로우하려 한 경우",
value = """
{
"resultCode": "INVALID_PERMISSION",
"message": "Permission is invalid., You cannot follow your own account."
}
"""
)}))
최종 코드
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "유저를 팔로우합니다.",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = PostCreateRequest.class),
examples = @ExampleObject(
name = "맴버 팔로우 성공 예제",
value = """
{
"resultCode": "SUCCESS"
}
"""
))),
@ApiResponse(responseCode = "401", description = "자신의 계정을 팔로우하려 한 경우, 로그인을 안한 경우",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorCode.class),
examples = {@ExampleObject(
name = "1_로그인해야 사용할수있는 기능입니다",
value = """
{
"resultCode": "INVALID_TOKEN",
"message": "Full authentication is required to access this resource"
}
"""
), @ExampleObject(
name = "2_자신의 계정을 팔로우하려 한 경우",
value = """
{
"resultCode": "INVALID_PERMISSION",
"message": "Permission is invalid., You cannot follow your own account."
}
"""
)}))
})
@Operation(summary = "맴버 팔로우 기능", description = "다른 유저를 팔로우하는 기능")
Response<Void> followAMember(Authentication authentication, @Parameter(
description = "해당 게시글 아이디 입력. *성공예제를 먼저 실행해 주세요.*",
examples = {
@ExampleObject(name = "예시1-성공", value = "4"),
@ExampleObject(name = "예시2-자신을 팔로우할 경우", value = "1"),
@ExampleObject(name = "예시3-이미 팔로우한 게시글일 경우", value = "2"),
@ExampleObject(name = "예시4-존재하지않는 계정일 경우", value = "10004")
}
) @PathVariable Long following);
이정도면 된거 같당..
RequestPart이랑 RequestBody에 예제 미리 담아 두는 방법은 이 블로그에 적어뒀으니 궁금하시면 보세효.. 이건 그냥 스웨거용 어노테이션을 콘트롤러에 적용하는 법인데 우린 위에 인터페이스에 적용하는법 내내 알아봤으니 잘할수있을거다요..
https://what-is-coding.tistory.com/44
간단한 스웨거 사용 법
직접 어노테이션을 앤드포인트에 달아주는게 가장 간단하지만 내 프로젝트 코드보다 스웨거 코드가 더 많아지기 떄문에 일단 이해하는 용으로만 보고 스웨거용 코드는 콘트롤러의 인터페이스
what-is-coding.tistory.com
요기 밑에 추가로 스웨거로 사진 올리는 법이랑 토큰 인증 하는법 올릴거당 토큰 인증은 다행이 포스트맨보다 간편하다! *-*b
https://what-is-coding.tistory.com/49
스웨거 사진 업로드 하는 법
일단 consumes = {MediaType.MULTIPART_FORM_DATA_VALUE} 을 컨트롤러 api에 선언해줘야지 스웨거에서 파일을 올릴수있는 버튼이 생긴다.@PostMapping(value = "/join", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})그렇게 실행
what-is-coding.tistory.com
//토큰
//달력?! 시간