LinkedIn

Inner Class에 대한 유효성 검사(javax.validation)

2022. 7. 2. 11:53 | 자바 개발자되기

API를 작성하다 보면 입력 데이터에 대한 유효성 검사가 필요한 상황이 많다.

 

예를 들어 아래와 같은 코드가 있다고 해보자.

 

입력 데이터(CreatePersonRequest)를 검증한 후 요건에 맞는 처리 후 응답하는 간단한 코드이다.

 

이름(name)과 나이(age)를 요청 값으로 받고 이름은 올바른 문자열인지, 나이는 0보다 큰 값인지 판단하는 검증을 진행한다.

 

{
    "name" : "Steve",
    "age" : 15
}
// 201 Success Return

위와 같이 올바른 값을 전달하여 호출하면 정상 응답 반환된다.

 

{
    "name" : "",
    "age" : 15
}
// 올바른 이름을 입력해주세요.

그런데 유효하지 않은 요청 값을 전달하면 유효성 검사에 실패하여 에러 메시지가 반환된다.

 

여기까지는 큰 문제없이 사용할 수 있었다.

그런데 요청 데이터 클래스 내부에 또 다른 클래스(Inner Class)가 존재하고, 그 클래스 필드에도 유효성 검사가 필요하다면 어떨까?

 

 

추가로 직업 정보(직업명, 설명)를 받아야 한다고 가정해보자.

대충 봤을 때 잘 돌아갈 것 같은 코드처럼 보인다.

 

그럼 한번 요청을 해보자.

{
    "name" : "Steve",
    "age" : 15,
    "job" : {
        "name" : "",
        "description" : "middle school"
    }
}
// Internal Server Error

에러가 발생하였다. 그런데 유효성 검사에 따른 에러로 보이지 않는다.

왜냐하면 유효성 검사에 따른 Exception Handler를 별도로 추가해두었기 때문이다.

 

error log를 봐보면 다음과 같은 내용이 출력되어 있다.

org.springframework.beans.NotReadablePropertyException: Invalid property 'job' of bean class [com.example.demo_databinder.controller.CreatePersonRequest]: Bean property 'job' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?

job 프로퍼티를 읽을 수 없다는 내용으로 보인다.

 

당황스럽다. Inner Class가 추가되었을 뿐인데 왜 에러가 발생하는 것 일까?

 

has an invalid getter method

private field라서 접근을 못하는 것인가? 그렇다면 해당 필드의 Getter를 추가하고 다시 호출해보자.

 

{
    "name" : "Steve",
    "age" : 15,
    "job" : {
        "name" : "",
        "description" : "middle school"
    }
}
// Internal Server Error

그런데 결과는 똑같다. 다시 에러 로그를 봐보자.

 

Invalid property 'job.name' of bean class [com.example.demo_databinder.controller.CreatePersonRequest]: Bean property 'job.name' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?

이번에는 Job 내부의 name 프로퍼티에 대한 내용이 출력되었다.

CreatePersonRequest.Job에 대한 접근을 위해 job 프로퍼티에 대한 Getter만 추가해주면 될 것이라 생각했지만 Inner Class의 경우 검증이 필요한 내부 필드에도 Getter 추가가 필요하다.

 

그럼 name 필드에 Getter를 추가 후 다시 테스트해보자.

{
    "name" : "Steve",
    "age" : 15,
    "job" : {
        "name" : "",
        "description" : "middle school"
    }
}
// 올바른 직업명을 입력해주세요.

드디어 의도한대로 유효성 검사에 따른 에러 메시지가 반환되었다.

 

정리하자면 Inner Class 유효성 검증을 진행하려면 상위 클래스와 해당 클래스(Inner Class)에서 검증을 진행할 필드의 Getter가 필요하다.

 

그렇다면 만약 요청 객체의 모든 필드를 public으로 지정하여 외부에서 자유롭게 접근할 수 있도록 한다면 어떨까?

 

단순 데이터 전달 목적(로직을 포함하지 않은)으로만 사용한다면 위와 같이 작성하여 사용하는 것도 크게 이상하지 않다.

이제 모든 필드에 대한 접근 지정자를 public으로 허용해두었으니 외부에서 자유롭게 접근할 수 있을 것이고 그럼 에러가 발생하지 않을까?

 

{
    "name" : "Steve",
    "age" : 15,
    "job" : {
        "name" : "",
        "description" : "middle school"
    }
}
// Internal Server Error
org.springframework.beans.NotReadablePropertyException: Invalid property 'job' of bean class [com.example.demo_databinder.controller.CreatePersonRequest]: Bean property 'job' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?

안타깝게도 동일한 에러가 발생한다.

 

왜 그럴까?

이쯤에서 드는 생각은 유효성 검사를 진행할 때 검사가 필요한 대상이 필드라면 직접 접근하여 값을 얻어오는 것이 아니라 Getter를 통해 값을 얻어온다고 추측할 수 있다.

 

그럼 반대로 필드에 직접 접근하도록 할 수 없을까 찾아보다가 아래의 글을 발견하였다.

https://stackoverflow.com/questions/39164077/java-spring-rest-validation-configure-property-access

 

Java Spring Rest validation configure property access

I have a Spring-JSON/RestAPI that use annotation driven input validation. @Valid I get the following error when I try to validate an object inside another object. java.lang.IllegalStateException:...

stackoverflow.com

    @InitBinder
    private void activateDirectFieldAccess(DataBinder dataBinder) {
        dataBinder.initDirectFieldAccess();
    }

위와 같은 코드를 ControllerAdvice에 추가하여 설정한다면 DataBinder가 필드에 직접 접근하여 사용한다고 한다.

 

그럼 검사를 위한 목적으로만 추가된 불필요한 public getter를 제거하고 저 설정을 켜 두는 게 맞을까?

 

https://stackoverflow.com/questions/1568091/why-use-getters-and-setters-accessors

 

Why use getters and setters/accessors?

What's the advantage of using getters and setters - that only get and set - instead of simply using public fields for those variables? If getters and setters are ever doing more than just the simp...

stackoverflow.com

위 글은 필드에 직접 접근하지 않고 getter/setter를 사용하였을 때 장점을 이야기하고 있다.

 

객체지향적인 관점에서 필드를 숨기는 행위(private)는 여러모로 의미가 있다.

비록 유효성 검증만을 위한 public getter이지만 필드에 직접 접근하는 것을 허용함으로써 여러 설계적 단점이 발생하는 것보다 낫다고 생각한다.

 

마지막으로 필드에 있는 유효성 검사 어노테이션을 메서드로 옮겨 public getter method의 역할을 분명히 하였다.