ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Portpolent] 회원가입
    개발/개발일지 2023. 12. 24. 12:41

    유저 기능을 위한 회원가입을 구현한다. 스펙과 파일 구조는 아래와 같다. 데이터베이스는 MariaDB를 사용하고 세션은 나중에 바뀔 수도 있지만 메모리에 저장한다.

     

    가입에 필요한 이메일, 핸들, 닉네임(유저네임), 비밀번호를 POST 요청으로 받아서 validation후 DB에 저장한다. 물론 비밀번호도 암호화 해야 한다.

    DTO

    public record SignUpDto(
            @NotBlank(message = "Email Required")
            @Email(message = "Email pattern mismatch")
            @Size(max = 100)
            String email,
    
            @Size(min = 4, max = 20)
            @Pattern(regexp = "[a-z0-9]{4,20}", message = "Handle pattern mismatched")
            @NotBlank(message = "Handle Required")
            String handle,
    
            @Size(min = 2, max = 10)
            @Pattern(regexp = "[\\w가-힣]{2,10}")
            @NotBlank(message = "Username Required")
            String username,
    
            // Least 8 characters, one lower letter, one upper letter, one special letter, one number
            @Pattern(
                    regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,}$",
                    message = "Password pattern mismatched"
            )
            @NotBlank(message = "Password Required")
            String password
    ) {
    }

    Java 14에 추가된 record class를 통해 DTO를 정의했다. 입력값을 검증하기 위해 Spring validation을 통해 검증한다.

    • email : @Email로 이메일 형식이어야 한다. 참고로 @Email은 null값을 거르지 않는다. 그래서 blank가 아니여야 하고 최대 100자까지 가능하다.
    • handle : 4 ~ 20자의 영어 소문자와 숫자로 구성된다.
    • username : 2~10자의 영어 대소문자, 숫자, 한글로 구성된다.
    • password : 최소 8글자, 하나 이상의 소문자, 하나 이상의 대문자, 하나 이상의 특수문자, 하나 이상의 숫자가 필요하다.

    validation을 만족하지 못할 경우 400 Bad Request가 자동으로 응답되는데 message에 적은 내용이 그대로 응답되면 좋겠다. 그 부분은 아래 글을 참고하라.

     

    [Spring Boot] 에러 메세지 커스터마이징 하기

    스프링 부트 3이전의 구버전에서는 ResponseEntityExceptionHandler을 상속해서 아래의 메소드를 오버라이딩하는 방식으로 접근할 수 있었다. @Override protected ResponseEntity handleMethodArgumentNotValid( MethodArgument

    yeahx4.tistory.com

     

    Service

    @Service
    public class UserService {
        private final UserRepository userRepository;
        private final BCryptPasswordEncoder encoder;
    
        @Autowired
        public UserService(UserRepository userRepository, BCryptPasswordEncoder encoder) {
            this.userRepository = userRepository;
            this.encoder = encoder;
        }
    
        public boolean availableToCreate(String handle, String email) {
            Optional<User> emailSearch = this.userRepository.findByEmail(email);
            Optional<User> handleSearch = this.userRepository.findByHandle(handle);
    
            return emailSearch.isEmpty() && handleSearch.isEmpty();
        }
    
        private String encryptPassword(String password) {
            return this.encoder.encode(password);
        }
    
        public User saveUser(String email, String handle, String username, String password, AccountType type) {
            String encrypted = this.encryptPassword(password);
            User user = new User(-1, email, handle, username, encrypted, type);
            return this.userRepository.save(user);
        }
    }

    생성이 가능한 유저인지(이메일이나 핸들이 중복되지 않았는지) 확인하고 비밀번호를 암호화 한 후 저장한다.

     

    Controller

    @RestController
    @RequestMapping("/user")
    public class UserController {
        private final Logger logger = LoggerFactory.getLogger(this.getClass());
        private final UserService userService;
    
        @Autowired
        public UserController(UserService userService) {
            this.userService = userService;
        }
    
        @PostMapping("/signup")
        public ResponseEntity<RestResponse<User>> signUp(@RequestBody @Valid SignUpDto dto) {
            boolean availableToCreate = this.userService.availableToCreate(dto.handle(), dto.email());
    
            if (!availableToCreate) {
                logger.info(
                        "Sign up request failed due to duplicated email or password ("
                        + dto.email() + ", "
                        + dto.handle() + ")"
                );
    
                return new ResponseEntity<>(
                        RestResponse.fail("Duplicated email or handle."),
                        HttpStatus.BAD_REQUEST
                );
            }
    
            User user = this.userService.saveUser(
                    dto.email(),
                    dto.handle(),
                    dto.username(),
                    dto.password(),
                    AccountType.OWN
            );
    
            logger.info("New user signed up. (" + dto.handle() + ")");
    
            return new ResponseEntity<>(
                    RestResponse.success(user),
                    HttpStatus.OK
            );
        }
    }

     

YEAHx4