1. 개요

유효성 검사는 Java 응용 프로그램에서 자주 발생하는 작업이므로 유효성 검사 라이브러리 개발에 많은 노력을 기울였습니다.

Vavr (이전의 Javaslang)은 본격적인 유효성 검사 API를 제공 합니다. 개체 기능 프로그래밍 스타일을 사용하여 간단한 방식으로 데이터의 유효성을 검사할 수 있습니다. 이 라이브러리가 기본적으로 제공하는 기능을 살펴보려면 이 기사 를 확인하십시오 .

이 사용방법(예제)에서는 라이브러리의 유효성 검사 API를 자세히 살펴보고 가장 관련성이 높은 방법을 사용하는 방법을 배웁니다.

2. 검증 인터페이스

Vavr의 유효성 검사 인터페이스는 응용 펑터 로 알려진 기능적 프로그래밍 개념을 기반으로 합니다 . 실행 체인 중에 일부 또는 모든 기능이 실패하더라도 결과를 축적하면서 일련의 기능을 실행합니다.

라이브러리의 적용 펑터는 Validation 인터페이스의 구현자에 기반합니다. 이 인터페이스는 검증 오류 및 검증된 데이터를 누적하는 방법을 제공하므로 두 가지 모두를 일괄 처리할 수 있습니다.

3. 사용자 입력 확인

사용자 입력(예: 웹 레이어에서 수집된 데이터)의 유효성 검사는 유효성 검사 API를 사용하여 원활하게 이루어집니다. 결과 오류가 있는 경우 누적되는 동안 데이터 유효성을 검사하는 사용자 지정 유효성 검사 클래스를 만드는 것으로 귀결되기 때문입니다.

로그인 양식을 통해 제출된 사용자의 이름과 이메일을 검증해 봅시다. 먼저 Vavr의 Maven 아티팩트pom.xml 파일에 포함해야 합니다.

<dependency>
    <groupId>io.vavr</groupId>
    <artifactId>vavr</artifactId>
    <version>0.9.0</version>
</dependency>

다음으로 사용자 개체를 모델링하는 도메인 클래스를 만들어 보겠습니다.

public class User {
    private String name;
    private String email;
    
    // standard constructors, setters and getters, toString
}

마지막으로 사용자 지정 유효성 검사기를 정의해 보겠습니다.

public class UserValidator {
    private static final String NAME_PATTERN = ...
    private static final String NAME_ERROR = ...
    private static final String EMAIL_PATTERN = ...
    private static final String EMAIL_ERROR = ...
	
    public Validation<Seq<String>, User> validateUser(
      String name, String email) {
        return Validation
          .combine(
            validateField(name, NAME_PATTERN, NAME_ERROR),
            validateField(email, EMAIL_PATTERN, EMAIL_ERROR))
          .ap(User::new);
    }
	
    private Validation<String, String> validateField
      (String field, String pattern, String error) {
 
        return CharSeq.of(field)
          .replaceAll(pattern, "")
          .transform(seq -> seq.isEmpty() 
            ? Validation.valid(field) 
            : Validation.invalid(error));		
    }
}

UserValidator 클래스 는 validateField() 메서드 를 사용하여 제공된 이름과 전자 메일을 개별적으로 확인합니다. 이 경우 이 메서드는 일반적인 정규식 기반 패턴 매칭을 수행합니다.

이 예제의 핵심은 valid() , invalid()combine() 메서드를 사용하는 것입니다.

4. valid(), invalid()combine() 메서드

제공된 이름과 이메일이 주어진 정규식과 일치하면, validateField() 메서드는 valid() 를 호출합니다 . 이 메서드는 Validation.Valid 인스턴스를 반환합니다 . 반대로 값이 유효하지 않은 경우 대응하는 invalid() 메서드는 Validation.Invalid 인스턴스를 반환합니다 .

유효성 검사 결과에 따라 다른 유효성 검사 인스턴스 를 생성하는 것을 기반으로 하는 이 간단한 메커니즘 은 결과를 처리하는 방법에 대한 최소한의 기본 아이디어를 제공해야 합니다(자세한 내용은 섹션 5 참조).

유효성 검사 프로세스의 가장 관련성이 높은 측면은 combine() 메서드입니다. 내부적으로 이 메서드는 서로 다른 메서드로 계산할 수 있는 최대 8개의 서로 다른 Validation 인스턴스를 결합할 수 있는 Validation.Builder 클래스를 사용합니다.

static <E, T1, T2> Builder<E, T1, T2> combine(
  Validation<E, T1> validation1, Validation<E, T2> validation2) {
    Objects.requireNonNull(validation1, "validation1 is null");
    Objects.requireNonNull(validation2, "validation2 is null");
    return new Builder<>(validation1, validation2);
}

가장 간단한 Validation.Builder 클래스는 두 가지 유효성 검사 인스턴스를 사용합니다.

final class Builder<E, T1, T2> {

    private Validation<E, T1> v1;
    private Validation<E, T2> v2;

    // standard constructors

    public <R> Validation<Seq<E>, R> ap(Function2<T1, T2, R> f) {
        return v2.ap(v1.ap(Validation.valid(f.curried())));
    }

    public <T3> Builder3<E, T1, T2, T3> combine(
      Validation<E, T3> v3) {
        return new Builder3<>(v1, v2, v3);
    }
}

Validation.Builderap(Function) 메서드와 함께 유효성 검사 결과와 함께 단일 결과를 반환합니다. 모든 결과가 유효한 경우 ap(Function) 메서드는 결과를 단일 값에 매핑합니다. 이 값은 서명에 지정된 함수를 사용하여 유효한 인스턴스에 저장됩니다.

이 예에서 제공된 이름과 이메일이 유효하면 새 사용자 개체가 생성됩니다. 물론 유효한 결과로 완전히 다른 작업을 수행할 수 있습니다. 예를 들어 결과를 데이터베이스에 보관하거나 이메일로 보내는 등의 작업이 가능합니다.

5. 유효성 검사 결과 처리

유효성 검사 결과를 처리하기 위한 다양한 메커니즘을 구현하는 것은 매우 쉽습니다. 그러나 처음에 데이터를 어떻게 검증합니까? 이를 위해 UserValidator 클래스를 사용합니다.

UserValidator userValidator = new UserValidator(); 
Validation<Seq<String>, User> validation = userValidator
  .validateUser("John", "john@domain.com");

유효성 검사 인스턴스를 얻은 후에는 유효성 검사 API의 유연성을 활용하고 여러 가지 방법으로 결과를 처리할 수 있습니다.

가장 일반적으로 발생하는 접근 방식에 대해 자세히 설명하겠습니다.

5.1. 유효한 인스턴스 잘못된 인스턴스

이 방법은 지금까지 가장 간단한 방법입니다. ValidInvalid 인스턴스 를 사용하여 유효성 검사 결과를 확인하는 것으로 구성됩니다 .

@Test
public void 
  givenInvalidUserParams_whenValidated_thenInvalidInstance() {
    assertThat(
      userValidator.validateUser(" ", "no-email"), 
      instanceOf(Invalid.class));
}
	
@Test
public void 
  givenValidUserParams_whenValidated_thenValidInstance() {
    assertThat(
      userValidator.validateUser("John", "john@domain.com"), 
      instanceOf(Valid.class));
}

ValidInvalid 인스턴스 로 결과의 유효성을 확인하는 대신 한 단계 더 나아가 isValid()isInvalid() 메서드를 사용해야 합니다.

5.2. isValid()isInvalid () API

isValid() / isInvalid() 탠덤을 사용하는 것은 유효성 검사 결과에 따라 이러한 메서드가 true 또는 false 를 반환한다는 차이점이 있는 이전 접근 방식과 유사합니다 .

@Test
public void 
  givenInvalidUserParams_whenValidated_thenIsInvalidIsTrue() {
    assertTrue(userValidator
      .validateUser("John", "no-email")
      .isInvalid());
}

@Test
public void 
  givenValidUserParams_whenValidated_thenIsValidMethodIsTrue() {
    assertTrue(userValidator
      .validateUser("John", "john@domain.com")
      .isValid());
}

잘못된 인스턴스에는 모든 유효성 검사 오류가 포함됩니다 . getError() 메서드 를 사용하여 가져올 수 있습니다 .

@Test
public void 
  givenInValidUserParams_withGetErrorMethod_thenGetErrorMessages() {
    assertEquals(
      "Name contains invalid characters, Email must be a well-formed email address", 
      userValidator.validateUser("John", "no-email")
        .getError()
        .intersperse(", ")
        .fold("", String::concat));
 }

반대로 결과가 유효 하면 get() 메서드 를 사용하여 User 인스턴스를 가져올 수 있습니다 .

@Test
public void 
  givenValidUserParams_withGetMethod_thenGetUserInstance() {
    assertThat(userValidator.validateUser("John", "john@domain.com")
      .get(), instanceOf(User.class));
 }

이 접근 방식은 예상대로 작동하지만 코드는 여전히 매우 장황하고 길어 보입니다. toEither() 메서드 를 사용하여 더 압축할 수 있습니다 .

5.3. toEither () API

toEither() 메서드 Each 인터페이스의 LeftRight 인스턴스를 구성합니다 . 이 보완 인터페이스에는 유효성 검사 결과 처리 시간을 단축하는 데 사용할 수 있는 몇 가지 편리한 방법이 있습니다.

결과가 유효하면 결과는 Right 인스턴스에 저장됩니다. 이 예에서 이것은 유효한 사용자 개체에 해당합니다. 반대로 결과가 유효하지 않으면 오류가 Left 인스턴스에 저장됩니다.

@Test
public void 
  givenValidUserParams_withtoEitherMethod_thenRightInstance() {
    assertThat(userValidator.validateUser("John", "john@domain.com")
      .toEither(), instanceOf(Right.class));
}

이제 코드가 훨씬 더 간결하고 능률적으로 보입니다. 하지만 아직 끝나지 않았습니다. 유효성 검사 인터페이스는 유효한 결과에 적용되는 사용자 정의 함수와 유효하지 않은 결과에 다른 함수를 적용하는 fold() 메서드를 제공합니다.

5.4. 접기( ) API

유효성 검사 결과를 처리하기 위해 fold() 메서드 를 사용하는 방법을 살펴보겠습니다 .

@Test
public void 
  givenValidUserParams_withFoldMethod_thenEqualstoParamsLength() {
    assertEquals(2, (int) userValidator.validateUser(" ", " ")
      .fold(Seq::length, User::hashCode));
}

fold() 를 사용 하면 유효성 검사 결과 처리가 한 줄로 줄어듭니다.

메서드에 대한 인수로 전달되는 함수의 반환 유형이 동일해야 한다는 점을 강조할 가치가 있습니다. 또한 함수는 검증 클래스, 즉 Seq<String>User 에 정의된 유형 매개변수에 의해 지원되어야 합니다 .

6. 결론

이 기사에서 우리는 Vavr의 유효성 검사 API를 심층적으로 살펴보고 가장 관련성이 높은 몇 가지 방법을 사용하는 방법을 배웠습니다. 전체 List은 공식 문서 API 를 확인하세요 .

Vavr의 유효성 검사 제어는 Hibernate Validator 와 같은 Java Beans 유효성 검사 의 보다 전통적인 구현에 대한 매우 매력적인 대안을 제공합니다 .

늘 그렇듯이 기사에 표시된 모든 예제는 GitHub 에서 사용할 수 있습니다 .

Generic footer banner