diff --git a/src/main/java/fitfit/domain/member/controller/MemberRestController.java b/src/main/java/fitfit/domain/member/controller/MemberRestController.java index ae3ea84..1f04882 100644 --- a/src/main/java/fitfit/domain/member/controller/MemberRestController.java +++ b/src/main/java/fitfit/domain/member/controller/MemberRestController.java @@ -11,6 +11,9 @@ import fitfit.global.enums.Provider; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -30,6 +33,12 @@ public class MemberRestController { @PostMapping("/auth/kko") @Operation(summary = "KAKAO OAuth2 로그인 API", description = "KAKAO OAuth2 로그인 API 입니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "OK, 성공"), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "Bad Request, 잘못된 요청 형식", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "유효하지 않은 ID Token 입니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "카카오 토큰 검증 도중 에러 발생", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + }) public ResponseEntity> kkoOAuth2Login (@Valid @RequestBody MemberRequestDTO.KkoOAuth2LoginRequest request) { // id_token 검증 후 멤버 데이터 추출 MemberDataDTO.MemberData kakaoMemberData = kakaoOidcService.verifyAndParseIdToken(request); @@ -43,6 +52,15 @@ public ResponseEntity> kko @PostMapping("/agreements") @Operation(summary = "약관 동의 API", description = "회원이 약관에 동의하는 API입니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "OK, 성공"), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "Bad Request, 잘못된 요청 형식", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "유효하지 않은 JWT 토큰입니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "만료된 JWT 토큰입니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "존재하지 않는 회원입니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "존재하지 않는 약관입니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "필수 약관에 동의하지 않았습니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + }) public ResponseEntity> termAgreement( @RequestHeader(value = "Authorization", required = false) String authorization, @Valid @RequestBody MemberRequestDTO.TermAgreementRequest request) { @@ -51,9 +69,30 @@ public ResponseEntity> term @PatchMapping("/signup") @Operation(summary = "회원가입 완료 API", description = "회원의 추가 정보(닉네임, 사용자 커스텀 ID, 성별, 생년월일, 프로필 이미지)를 입력하여 회원가입을 완료하는 API입니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "OK, 성공"), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "Bad Request, 잘못된 요청 형식", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "유효하지 않은 JWT 토큰입니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "만료된 JWT 토큰입니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "존재하지 않는 회원입니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "회원 필수 정보가 누락되었습니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + }) public ResponseEntity> signup( @RequestHeader(value = "Authorization", required = false) String authorization, @Valid @RequestBody MemberRequestDTO.MemberSignupRequest request) { return ResponseEntity.ok(ApiResponse.onSuccess(memberCommandUseCase.memberSignup(authorization, request))); } + + @PostMapping("/nickname/check") + @Operation(summary = "닉네임 중복 확인 API", description = "닉네임 중복을 확인하는 API입니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "OK, 성공"), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "Bad Request, 잘못된 요청 형식", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "409", description = "이미 사용 중인 닉네임입니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + }) + public ResponseEntity> checkNickname(@Valid @RequestBody MemberRequestDTO.NicknameCheckRequest request) { + memberCommandUseCase.checkNickname(request.getNickname()); + return ResponseEntity.ok(ApiResponse.onSuccess("사용 가능한 닉네임입니다.")); + } } + diff --git a/src/main/java/fitfit/domain/member/dto/MemberRequestDTO.java b/src/main/java/fitfit/domain/member/dto/MemberRequestDTO.java index b5e0541..3d6f9d3 100644 --- a/src/main/java/fitfit/domain/member/dto/MemberRequestDTO.java +++ b/src/main/java/fitfit/domain/member/dto/MemberRequestDTO.java @@ -42,6 +42,16 @@ public static class TermAgreementRequest { private List disagreeTermIdList; } + @Getter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class NicknameCheckRequest { + @NotNull(message = "닉네임은 필수입니다.") + @Size(max = 25, message = "닉네임은 최대 25자입니다.") + private String nickname; + } + @Getter @Builder @AllArgsConstructor diff --git a/src/main/java/fitfit/domain/member/repository/MemberRepository.java b/src/main/java/fitfit/domain/member/repository/MemberRepository.java index 5bb6935..e2ad2f4 100644 --- a/src/main/java/fitfit/domain/member/repository/MemberRepository.java +++ b/src/main/java/fitfit/domain/member/repository/MemberRepository.java @@ -7,4 +7,6 @@ public interface MemberRepository extends JpaRepository { Optional findByProviderId(String sub); + + boolean existsByName(String name); } diff --git a/src/main/java/fitfit/domain/member/service/MemberCommandService.java b/src/main/java/fitfit/domain/member/service/MemberCommandService.java index 89dfff2..057c4a3 100644 --- a/src/main/java/fitfit/domain/member/service/MemberCommandService.java +++ b/src/main/java/fitfit/domain/member/service/MemberCommandService.java @@ -191,6 +191,14 @@ private boolean validateAdditionalInfo(MemberRequestDTO.MemberSignupRequest requ request.getBirth() != null; } + @Override + @Transactional(readOnly = true) + public void checkNickname(String nickname) { + if (memberRepository.existsByName(nickname)) { + throw new MemberHandler(ErrorStatus.NICKNAME_ALREADY_EXISTS); + } + } + /** * 회원 정보를 업데이트하는 메서드 */ diff --git a/src/main/java/fitfit/domain/member/service/MemberCommandUseCase.java b/src/main/java/fitfit/domain/member/service/MemberCommandUseCase.java index de164bd..fde6796 100644 --- a/src/main/java/fitfit/domain/member/service/MemberCommandUseCase.java +++ b/src/main/java/fitfit/domain/member/service/MemberCommandUseCase.java @@ -10,4 +10,6 @@ public interface MemberCommandUseCase { Member findOrCreateMember(MemberDataDTO.MemberData memberData, Provider provider); MemberResponseDTO.TermAgreementResponse termAgreement(String authorization, MemberRequestDTO.TermAgreementRequest request); MemberResponseDTO.MemberSignupResponse memberSignup(String authorization, MemberRequestDTO.MemberSignupRequest request); + + void checkNickname(String nickname); } diff --git a/src/main/java/fitfit/global/apiPayload/code/status/ErrorStatus.java b/src/main/java/fitfit/global/apiPayload/code/status/ErrorStatus.java index 23106d4..d9fce65 100644 --- a/src/main/java/fitfit/global/apiPayload/code/status/ErrorStatus.java +++ b/src/main/java/fitfit/global/apiPayload/code/status/ErrorStatus.java @@ -33,6 +33,7 @@ public enum ErrorStatus implements BaseErrorCode { MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "MEMBER4001", "존재하지 않는 회원입니다."), INVALID_MEMBER_INFO(HttpStatus.BAD_REQUEST, "MEMBER4002", "회원 필수 정보가 누락되었습니다."), MEMBER_ALREADY_EXISTS(HttpStatus.CONFLICT, "MEMBER4003", "이미 존재하는 회원입니다."), + NICKNAME_ALREADY_EXISTS(HttpStatus.CONFLICT, "MEMBER4004", "이미 사용 중인 닉네임입니다."), INVALID_USER_CUSTOM_ID_FORMAT(HttpStatus.BAD_REQUEST, "MEMBER4005", "사용자 커스텀 ID 형식이 올바르지 않습니다."), DUPLICATE_USER_CUSTOM_ID(HttpStatus.CONFLICT, "MEMBER4006", "이미 사용 중인 사용자 커스텀 ID입니다."), INVALID_BIRTH_DATE(HttpStatus.BAD_REQUEST, "MEMBER4007", "생년월일이 올바르지 않습니다."),