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

코딩하는 감자·2023년 12월 8일
0
post-custom-banner

@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 상수만 수정하면 된다!

post-custom-banner

0개의 댓글