From 86e3432db3c737e779a9ed575874f7e2b7376708 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=92=80=EA=B7=B8/=EC=9D=B4=EC=A4=80=ED=91=9C?= <85255237+wnsvy607@users.noreply.github.com> Date: Tue, 10 Sep 2024 16:06:30 +0900 Subject: [PATCH] =?UTF-8?q?Feat:=20=EB=8B=89=EB=84=A4=EC=9E=84=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20API=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../avatar/controller/AvatarController.java | 31 +++++++++++++++++++ .../core/avatar/domain/AvatarCommand.java | 17 ++++++++++ .../join/core/avatar/domain/AvatarInfo.java | 22 +++++++++++++ .../join/core/avatar/domain/AvatarReader.java | 4 ++- .../core/avatar/domain/AvatarService.java | 7 +++++ .../core/avatar/domain/AvatarServiceImpl.java | 29 +++++++++++++++++ .../domain/NicknameDuplicationValidator.java | 23 ++++++++++++++ .../domain/NicknameFormatValidator.java | 24 ++++++++++++++ .../core/avatar/domain/NicknameValidator.java | 7 +++++ .../avatar/repository/AvatarReaderImpl.java | 6 ++++ .../avatar/repository/AvatarRepository.java | 12 +++++-- 11 files changed, 178 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/join/core/avatar/controller/AvatarController.java create mode 100644 src/main/java/com/join/core/avatar/domain/AvatarCommand.java create mode 100644 src/main/java/com/join/core/avatar/domain/AvatarInfo.java create mode 100644 src/main/java/com/join/core/avatar/domain/AvatarService.java create mode 100644 src/main/java/com/join/core/avatar/domain/AvatarServiceImpl.java create mode 100644 src/main/java/com/join/core/avatar/domain/NicknameDuplicationValidator.java create mode 100644 src/main/java/com/join/core/avatar/domain/NicknameFormatValidator.java create mode 100644 src/main/java/com/join/core/avatar/domain/NicknameValidator.java diff --git a/src/main/java/com/join/core/avatar/controller/AvatarController.java b/src/main/java/com/join/core/avatar/controller/AvatarController.java new file mode 100644 index 0000000..1a46d50 --- /dev/null +++ b/src/main/java/com/join/core/avatar/controller/AvatarController.java @@ -0,0 +1,31 @@ +package com.join.core.avatar.controller; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.join.core.avatar.domain.AvatarCommand; +import com.join.core.avatar.domain.AvatarInfo; +import com.join.core.avatar.domain.AvatarService; +import com.join.core.common.response.ApiResponse; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@RestController +@RequestMapping("${api.prefix}/avatars") +public class AvatarController { + + private final AvatarService avatarService; + + @Tag(name = "${swagger.tag.sign-up}") + @Operation(summary = "닉네임 유효성 검사 API", + description = "닉네임 유효성 검사 API") + @GetMapping("/nickname/valid") + public ApiResponse isValidNickname(AvatarCommand.ChangeNickname command) { + return ApiResponse.ok(avatarService.isValid(command)); + } + +} diff --git a/src/main/java/com/join/core/avatar/domain/AvatarCommand.java b/src/main/java/com/join/core/avatar/domain/AvatarCommand.java new file mode 100644 index 0000000..6a7b895 --- /dev/null +++ b/src/main/java/com/join/core/avatar/domain/AvatarCommand.java @@ -0,0 +1,17 @@ +package com.join.core.avatar.domain; + +import lombok.Builder; +import lombok.Getter; + +public class AvatarCommand { + + private AvatarCommand() { + } + + @Builder + @Getter + public static class ChangeNickname { + private final String nickname; + } + +} diff --git a/src/main/java/com/join/core/avatar/domain/AvatarInfo.java b/src/main/java/com/join/core/avatar/domain/AvatarInfo.java new file mode 100644 index 0000000..e1e8868 --- /dev/null +++ b/src/main/java/com/join/core/avatar/domain/AvatarInfo.java @@ -0,0 +1,22 @@ +package com.join.core.avatar.domain; + +import lombok.Builder; +import lombok.Getter; + +public class AvatarInfo { + + private AvatarInfo() { + } + + @Builder + @Getter + public static class ValidNickname { + private final boolean isValid; + private final String message; + + public static ValidNickname valid() { + return new ValidNickname(true, "사용 가능한 닉네임이에요."); + } + } + +} diff --git a/src/main/java/com/join/core/avatar/domain/AvatarReader.java b/src/main/java/com/join/core/avatar/domain/AvatarReader.java index ed8a9fd..ba38e13 100644 --- a/src/main/java/com/join/core/avatar/domain/AvatarReader.java +++ b/src/main/java/com/join/core/avatar/domain/AvatarReader.java @@ -1,6 +1,8 @@ package com.join.core.avatar.domain; public interface AvatarReader { - Avatar getAvatarByToken(String avatarToken); + Avatar getAvatarByToken(String avatarToken); + + boolean existsByNickname(String nickname); } diff --git a/src/main/java/com/join/core/avatar/domain/AvatarService.java b/src/main/java/com/join/core/avatar/domain/AvatarService.java new file mode 100644 index 0000000..29ead7e --- /dev/null +++ b/src/main/java/com/join/core/avatar/domain/AvatarService.java @@ -0,0 +1,7 @@ +package com.join.core.avatar.domain; + +public interface AvatarService { + + AvatarInfo.ValidNickname isValid(AvatarCommand.ChangeNickname command); + +} diff --git a/src/main/java/com/join/core/avatar/domain/AvatarServiceImpl.java b/src/main/java/com/join/core/avatar/domain/AvatarServiceImpl.java new file mode 100644 index 0000000..e1fa365 --- /dev/null +++ b/src/main/java/com/join/core/avatar/domain/AvatarServiceImpl.java @@ -0,0 +1,29 @@ +package com.join.core.avatar.domain; + +import java.util.List; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Transactional(readOnly = true) +@Service +public class AvatarServiceImpl implements AvatarService { + + private final List nicknameValidatorList; + + @Override + public AvatarInfo.ValidNickname isValid(AvatarCommand.ChangeNickname command) { + + for (NicknameValidator validator : nicknameValidatorList) { + AvatarInfo.ValidNickname validationResult = validator.validate(command); + if (!validationResult.isValid()) + return validationResult; + } + + return AvatarInfo.ValidNickname.valid(); + } + +} diff --git a/src/main/java/com/join/core/avatar/domain/NicknameDuplicationValidator.java b/src/main/java/com/join/core/avatar/domain/NicknameDuplicationValidator.java new file mode 100644 index 0000000..bc8bf83 --- /dev/null +++ b/src/main/java/com/join/core/avatar/domain/NicknameDuplicationValidator.java @@ -0,0 +1,23 @@ +package com.join.core.avatar.domain; + +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Order(2) +@Component +public class NicknameDuplicationValidator implements NicknameValidator { + + private final AvatarReader avatarReader; + + @Override + public AvatarInfo.ValidNickname validate(AvatarCommand.ChangeNickname command) { + if (avatarReader.existsByNickname(command.getNickname())) + return new AvatarInfo.ValidNickname(false, "이미 사용 중인 닉네임이에요."); + + return AvatarInfo.ValidNickname.valid(); + } + +} diff --git a/src/main/java/com/join/core/avatar/domain/NicknameFormatValidator.java b/src/main/java/com/join/core/avatar/domain/NicknameFormatValidator.java new file mode 100644 index 0000000..78df75a --- /dev/null +++ b/src/main/java/com/join/core/avatar/domain/NicknameFormatValidator.java @@ -0,0 +1,24 @@ +package com.join.core.avatar.domain; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +@Order(1) +@Component +public class NicknameFormatValidator implements NicknameValidator { + + private static final Pattern pattern = Pattern.compile("^[가-힣a-zA-Z0-9]{2,7}$"); + + @Override + public AvatarInfo.ValidNickname validate(AvatarCommand.ChangeNickname command) { + Matcher matcher = pattern.matcher(command.getNickname()); + if (!matcher.matches()) + return new AvatarInfo.ValidNickname(false, "2-7자리 한글/영문/숫자로 입력해주세요."); + + return AvatarInfo.ValidNickname.valid(); + } + +} diff --git a/src/main/java/com/join/core/avatar/domain/NicknameValidator.java b/src/main/java/com/join/core/avatar/domain/NicknameValidator.java new file mode 100644 index 0000000..3c6ce24 --- /dev/null +++ b/src/main/java/com/join/core/avatar/domain/NicknameValidator.java @@ -0,0 +1,7 @@ +package com.join.core.avatar.domain; + +public interface NicknameValidator { + + AvatarInfo.ValidNickname validate(AvatarCommand.ChangeNickname command); + +} diff --git a/src/main/java/com/join/core/avatar/repository/AvatarReaderImpl.java b/src/main/java/com/join/core/avatar/repository/AvatarReaderImpl.java index 7197b1f..1ba91e7 100644 --- a/src/main/java/com/join/core/avatar/repository/AvatarReaderImpl.java +++ b/src/main/java/com/join/core/avatar/repository/AvatarReaderImpl.java @@ -21,4 +21,10 @@ public Avatar getAvatarByToken(String avatarToken) { .orElseThrow(() -> new EntityNotFoundException(ErrorCode.AVATAR_NOT_FOUND)); } + @Transactional(readOnly = true) + @Override + public boolean existsByNickname(String nickname) { + return avatarRepository.existsByNickname(nickname); + } + } diff --git a/src/main/java/com/join/core/avatar/repository/AvatarRepository.java b/src/main/java/com/join/core/avatar/repository/AvatarRepository.java index 9e7f719..f27d1d8 100644 --- a/src/main/java/com/join/core/avatar/repository/AvatarRepository.java +++ b/src/main/java/com/join/core/avatar/repository/AvatarRepository.java @@ -1,11 +1,17 @@ package com.join.core.avatar.repository; -import com.join.core.avatar.domain.Avatar; +import java.util.Optional; + import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; -import java.util.Optional; +import com.join.core.avatar.domain.Avatar; public interface AvatarRepository extends JpaRepository { - Optional findByAvatarToken(String avatarToken); + Optional findByAvatarToken(String avatarToken); + + @Query("select (count(a) > 0) from Avatar a where a.nickname = :nickname") + boolean existsByNickname(@Param("nickname") String nickname); }