직접 어노테이션을 앤드포인트에 달아주는게 가장 간단하지만 내 프로젝트 코드보다 스웨거 코드가 더 많아지기 떄문에 일단 이해하는 용으로만 보고 스웨거용 코드는 콘트롤러의 인터페이스를 만들어서 거기로 싹 옮겨두면 기존 코드를 해치지 않고 깔끔하게 작성 가능하다 (제일 밑에 링크 달아 둠)
@Tag 이용해서 api들 그룹화하고 그룹 이름, 설명 해주기
일단 아무 태그를 사용하기 전 swagger-ui의 모습이다.

짠! 태그 사용해서 묶어주고 이름과 설명란도 적어주었다. @Tag를 사용하니 member-Controller에서 공개APIs으로 이름이 바뀌었다

@Tag(name = "공개APIs", description = "로그인 필요없는 APIs: 회원가입, 로그인 API- 로그인/가입 후 response header에서 JWT 토큰을(Bearer) 복사해서 인증(Authorize) 해주세요. 이 글 위에 오른쪽에 [Authorize] 버튼누르고 붙여넣기하면 됩니다.")
@Tag를 사용해서 api들의 그룹명을 정해준다.
밑에 코드는 맴버 컨트롤러에 태그를 둬서 맴버 콘트롤러안에 있는 전체 모든 앤드포인트를 그룹으로 묶어서 이름과 설명란을 적었지만 앤트포인트 하나마다 각기 tags를 사용해서 자유롭게 설정 가능하다.
@Tag(name = "공개APIs", description = "로그인 필요없는 APIs: 회원가입, 로그인 API- 로그인/가입 후 response header에서 JWT 토큰을(Bearer) 복사해서 인증(Authorize) 해주세요. 이 글 위에 오른쪽에 [Authorize] 버튼누르고 붙여넣기하면 됩니다.")
@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/api/v1")
public class MemberController {
private final MemberService memberService;
@PostMapping(value = "/join")
public Response<UserJoinResponse> join(@RequestPart("file") MultipartFile file,
@RequestPart("request") UserJoinRequest request, HttpServletResponse response) throws IOException {
UserDTO joinUserDTO = memberService.join(file, request.getNickName(), request.getEmail(), request.getPassword(), response);
return Response.success(UserJoinResponse.fromJoinUserDTO(joinUserDTO));
}
@PostMapping(value = "/login")
public Response<Void> login(@org.springframework.web.bind.annotation.RequestBody UserLoginRequest request) {
memberService.login(request.getEmail(), request.getPassword());
return Response.success(null);
}
}
@Operation을 사용해서 각각의 API를 설명해준다
@Tag는 api들을 그룹으로 묶어서 설명하는거고 @Operation은 api마다 적어서 설명해준다.
summary는 앤트포인트 옆에 뜨고 description은 api를 클릭했을때 밑에 뜨기 때문에 실행하는 법을 더 디테일하게 적는 용으로 사용했다.
@Operation(summary = "로그인 API with 이메일, 비밀번호", description = "(테스트용:Meep@kakao.com, 비밀번호-123) 로그인시 jwt토큰이 발행됩니다.")

저렇게 controller에 있는 api위에 달아주면 된다.
@Operation(summary = "로그인 API with 이메일, 비밀번호", description = "(테스트용:Meep@kakao.com, 비밀번호-123) 로그인시 jwt토큰이 발행됩니다.")
@PostMapping(value = "/login", consumes = {MediaType.APPLICATION_JSON_VALUE})
public Response<Void> login(@org.springframework.web.bind.annotation.RequestBody UserLoginRequest request) {
memberService.login(request.getEmail(), request.getPassword());
return Response.success(null);
}
@RequestBody 미리 예제들 설정해주기.

try it out 버튼을 눌르면 여기 들어가는 값을 설정해줄수 있다.
swagger-ui에 있는 /api/v1/login를 펼쳐주면 밑에 사진처럼 이렇게 설정을 안했을떈 requestBody에 들어가야하는 값들이 기본으로 "String"이라고 채워져있다. 위에 예제처럼 다양한 예제의 인풋을 미리 작성해줘야한다.

@Operation안에 설정해주는 방법이다. 밑에 코드를 @Operation안에 넣어주면 된다!
requestBody = @RequestBody(content = @Content(
examples = {
@ExampleObject(name = "예시1-성공", value = """
{
"email" : "Meep@kakao.com",
"password" : "123"
}
"""),
@ExampleObject(name = "예시2-틀린 비밀번호", value = """
{
"email" : "Meep@kakao.com",
"password" : "df"
}
"""),
@ExampleObject(name = "예시3-존재하지않는 계정", value = """
{
"email" : "Meepeeeee@kakao.com",
"password" : "123"
}
"""),
}))
밑에는 전체 코드! >_<b
@Operation(summary = "로그인 API with 이메일, 비밀번호", description = "(테스트용:Meep@kakao.com, 비밀번호-123) 로그인시 jwt토큰이 발행됩니다."
, requestBody = @RequestBody(content = @Content(
examples = {
@ExampleObject(name = "예시1-성공", value = """
{
"email" : "Meep@kakao.com",
"password" : "123"
}
"""),
@ExampleObject(name = "예시2-틀린 비밀번호", value = """
{
"email" : "Meep@kakao.com",
"password" : "df"
}
"""),
@ExampleObject(name = "예시3-존재하지않는 계정", value = """
{
"email" : "Meepeeeee@kakao.com",
"password" : "123"
}
"""),
})))
@PostMapping(value = "/login", consumes = {MediaType.APPLICATION_JSON_VALUE})
public Response<Void> login(@org.springframework.web.bind.annotation.RequestBody UserLoginRequest request) {
memberService.login(request.getEmail(), request.getPassword());
return Response.success(null);
}
@Schema사용해서 @RequestPart 미리 예제 적어주기.
(@RequestBody도 @Schema 이용이 가능하지만 바로 최대한 스웨거 코드와 프로젝트 코드를 분리하기 위에서 위에 썼던 방법을 더 추천해요)
@RequestPart은 아직 위처럼 분리하는 법을 못찾아서 이 방법을 사용했다.
이건 DTO에 직접 requestPart일때는 @Scheme 을 직접 requestDto에 직접 해주는 방법인데 더 좋은 방법 있으면 알려줘잉...
@Schema태그를 사용해서 @RequestPart의 인풋 값 미리 설정할 수 있다
설정을 안했을떈 값들이 "String"으로 default되어있다.
회원가입 컨트롤러의 파라미터에 있는 RequestPart인 UserJoinRequest에 인풋값을 미리 설정해준다. @Schema사용하기! *_*b
@AllArgsConstructor
@Getter
@Builder
@NoArgsConstructor
@Schema(description = "회원가입 유저 요청")
public class UserJoinRequest {
@Schema(description = "닉네임을 정해주세요", example = "텔레토비")
private String nickName;
@Schema(description = "로그인 할때 사용할 이메일을 입력해주세요", example = "example@kakao.com")
private String email;
@Schema(description = "비밀번호를 입력해주세요.", example = "password123")
private String password;
}

아무튼 인풋 예제가 잘 적용되어 있다 이제 RequestPart 안에 채울필요없이 그냥 execute누르면 실행 된다 :)
여기는 예제는 하나였지만 PathVariable일 경우는 여러개의 예제를 넣는 방법을 알아냈당..
혹시 모르니 이 코드도 첨부해본다...
@Operation(summary = "회원가입 with 프로필 사진", description = "프로필 사진과 함께 회원가입 해주세요. 사진 올리지 않으면 실행되지않아요!")
@PostMapping(value = "/join", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
public Response<UserJoinResponse> join(@RequestPart("file") MultipartFile file,
@RequestPart("request") UserJoinRequest request, HttpServletResponse response) throws IOException {
UserDTO joinUserDTO = memberService.join(file, request.getNickName(), request.getEmail(), request.getPassword(), response);
return Response.success(UserJoinResponse.fromJoinUserDTO(joinUserDTO));
}
PathVariable 예제들 미리 만들어 주기
@Parameter를 사용하면 여러 예제를 PathVariable에 설정해줄수 있다.
@PathVariable Long alarmId 앞에 두면 된다.
@Parameter(
description = "해당 게시글 아이디 입력. *성공예제를 먼저 실행해 주세요.*",
examples = {
@ExampleObject(name = "예시1-성공(3,5,7,9~14)", value = "3"),
@ExampleObject(name = "예시2-내 알림 ID가 아닐 경우", value = "1"),
@ExampleObject(name = "예시3-존재하지 않는 알림일 경우", value = "0"),
}
)
요걸 밑에 적용해보았다. @PathVariable Long alarmId요기 앞에 두었다.
Response<Void> deleteAlarm(@Parameter(
description = "해당 게시글 아이디 입력. *성공예제를 먼저 실행해 주세요.*",
examples = {
@ExampleObject(name = "예시1-성공(3,5,7,9~14)", value = "3"),
@ExampleObject(name = "예시2-내 알림 ID가 아닐 경우", value = "1"),
@ExampleObject(name = "예시3-존재하지 않는 알림일 경우", value = "0"),
}
) @PathVariable Long alarmId, Authentication authentication)

히힛 예시에 저건 실행누르면 삭제되서 또 못쓰는 번호라서 삭제가능한 다른 번호들도 알려주는거다.. 그래서 이건 delete기능 보단 조회기능인 get사용시 더 유용하다.
Response 예제 설정하기

이번에는 api를 실행했을때 받을수 있는 response들을 적어주자 사용자가 execute버튼 눌러서 알아보기 귀찮을수 있으니! *-*b
다양한 exception을 만들었으니 그것들이 터진다고 생각하고 꼼꼼히 작성해 봅시당.. 상태코드가 같을떄는 저렇게 설정 가능해서 참 좋당

위 사진은 아무 설정하지 않았을 때인데 사진의 제일 밑을 보면 200코드로 스웨거가 대충 노력해서 어림 짐작해서 보내주긴 하지만 더 디테일하게 작성해보자.
@APIResponses(value={ })
이 안에 @APIResponse들을 넣어준다. 단수 복수 주의하기!! APIResponses 안에 여러 APIResponse넣어준다!!!
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "회원가입이 완료되었습니다.",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = UserJoinResponse.class),
examples = @ExampleObject(
name = "회원가입 성공 예제",
value = """
{
"resultCode": "SUCCESS",
"result": {
"id": 13,
"email": "examp2le@kakao.com",
"role": "USER",
"profileImageUrl": "https://s3.ap-northeast-2.amazonaws.com/pizzakoala/50daeb35-17b0-4bae-a3fe-2d0a34132bfe.jpeg"
}
}
"""
))),
@ApiResponse(responseCode = "409", description = "중복 이메일, 닉네임일때 에러 반환",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = ErrorResponse.class),
examples = {
@ExampleObject(
name = "1_인증 실패 예제 - Duplicated Email",
value = """
{
"resultCode": "DUPLICATED_EMAIL_ADDRESS",
"result": null
}
"""
),
@ExampleObject(
name = "2_인증 실패 예제 - Duplicated Nickname",
value = """
{
"resultCode": "DUPLICATED_NICKNAME",
"result": null
}
"""
)
}))})
@Operation(summary = "회원가입 with 프로필 사진", description = "프로필 사진과 함께 회원가입 해주세요. 사진 올리지 않으면 실행되지않아요!")
*위에 코드 설명하기*
이렇게 @Operation위에 ApiResponses를선언해주면 된다. 원래 에러코드 하나당 에러가 하나만 터진다면 위에 밑에 처럼 @APIResponses들을 여러개 선언해주면 된다.
@ApiResponse(responseCode = "200", description = "회원가입이 완료되었습니다.",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = UserJoinResponse.class),
examples = @ExampleObject(
name = "회원가입 성공 예제",
value = """
{
"resultCode": "SUCCESS",
"result": {
"id": 13,
"email": "examp2le@kakao.com",
"role": "USER",
"profileImageUrl": "https://s3.ap-northeast-2.amazonaws.com/pizzakoala/50daeb35-17b0-4bae-a3fe-2d0a34132bfe.jpeg"
}
}
"""
)))
하지만 가끔 에러코드가 같은 여러 예외들이 터질떄는 이렇게 선언해주면 된다.
이번 코드는 닉네임이나 이메일이 중복일때 서로 다른 exception을 반환하지만 에러코드는 동일한 409일 떄이다.
examples = 과 @ExampleObject사이에 {}를 넣어줘서 여러개의 exception을 추가해준다
examples = { @ExampleObject(),
@ExampleObject(),
@ExampleObject()
}
@ApiResponse(responseCode = "409", description = "중복 이메일, 닉네임일때 에러 반환",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = ErrorResponse.class),
examples = {
@ExampleObject(
name = "1_인증 실패 예제 - Duplicated Email",
value = """
{
"resultCode": "DUPLICATED_EMAIL_ADDRESS",
"result": null
}
"""
),
@ExampleObject(
name = "2_인증 실패 예제 - Duplicated Nickname",
value = """
{
"resultCode": "DUPLICATED_NICKNAME",
"result": null
}
"""
)
}))
이렇게 하면 내 컨트롤러의 원래 코드양보다 스웨거 코드가 많아지기 떄문에 막상해보면 어렵지 않으니 힘든길 택하지말구 밑에 블로그 읽고 그 방법으로 해보자 :(
(그냥 콘트롤러마다 인터페이스 만들어서 거기에 스웨거 코드 넣거 콘트롤러에 implement 해주면 된다.)
스웨거는 막상 알면 쉬운데 내가 원하는 자료들을 찾기가 쉽지않아서 요 몇일 찾은 정보들을 블로그에 남겨봅니당 다른 분들은 시간 아끼시길...
https://what-is-coding.tistory.com/46
스웨거 코드를 컨트롤러 인터페이스로 분리하기
스웨거 코드가 최대한 프로젝트 코드를 해치지 않는 방법으로 각 컨트롤러들의 인터페이스를 만들어 준다. 인터페이스 안에 스웨거 코드를 적어준다.FollowController(현재 존재하는 컨트롤러) -> Fol
what-is-coding.tistory.com