비밀번호 형식 @Valid 대신 도메인에서 검증하기

코딩하는 감자·2023년 12월 8일
0

@Valid 사용 시 문제점

public class SignupRequest {
    
	// ...
    
    @Pattern(regexp = "^.{8,}$", message = "비밀번호는 8글자 이상이어야 합니다")
    private String password;
    
    // ...
    
}

@RestController
public class MemberController {

 	// ...
    
    @PostMapping
    public ResponseEntity<MemberSignupResponse> signup(@Valid @RequestBody MemberSignupRequest request) {
        MemberSignupResponse response = memberService.register(request);
        return ResponseEntity.ok().body(response);
    }
    
 }

위와 같은 코드가 있을 때,

public class ChangePasswordRequest {

    private String oldPassword;
    // NOTE 중복 코드
    @Pattern(regexp = "^.{8,}$", message = "비밀번호는 8글자 이상이어야 합니다")
    private String newPassword;

    public ChangePasswordRequest(String oldPassword, String newPassword) {
        this.oldPassword = oldPassword;
        this.newPassword = newPassword;
    }
    
}

@RestController
public class MemberController {

 	// ...
    
	@PutMapping("/{id}/password")
    public ResponseEntity<ChangePasswordRequest> changePassword(@MemberId String loginId,
  	          @PathVariable String id, @Valid @RequestBody ChangePasswordRequest request) {
        memberService.changePassword(loginId, id, request);
        return ResponseEntity.noContent().build();
    }
    
}

이렇게 비밀번호를 변경하는 api를 추가하려고 하니,
@Pattern(regexp = "^.{8,}$", message = "비밀번호는 8글자 이상이어야 합니다")
라는 중복 코드가 발생하게 되었다.

도메인에서 검증

@Embeddable
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Password {

    private static final String REGEX = "^.{8,}$";

    private String value;

    public Password(String value) {
        validate(value);
        this.value = value;
    }

    private void validate(String value) {
        if (!Pattern.matches(REGEX, value)) {
            throw new PasswordPatternNotMatchException();
        }
    }
}

@Entity
public class Member {

    // ...
    private Password password;
    // ...

    public Member(/*...,*/ String password /*,...*/) {
        // ...
        this.password = new Password(password);
        // ...
    }
    
    public void changePassword(String oldPassword, String newPassword) {
        checkPasswordMatch(oldPassword);
        this.password = new Password(newPassword);
    }
    
 }

@Service
public class MemberService {

	    public void changePassword(String loginId, String id, ChangePasswordRequest request) {
        if (!Objects.equals(loginId, id)) {
            throw new UnauthorizedException();
        }
        Member member = findById(IdFactory.createMemberId(id));
        member.changePassword(request.getOldPassword(), request.getNewPassword());
    }
    
}

Password 는 Value이므로 setter가 없어서 member.password를 변경하려면 무조건 Password 인스턴스를 생성해야 하고, 생성자에서 검증이 이뤄지므로 모든 비밀번호 변경 로직은 검증을 거치게 된다.

이로써 비밀번호 형식이 변경되면 도메인의 REGEX 상수만 수정하면 된다!

0개의 댓글

관련 채용 정보