DAY-31]240111 NewsFeed Project ( validation 설정하기 )

JUNHYUK CHANG·2024년 1월 12일
1

TIL

목록 보기
4/33

주요 기능의 구현을 마치고, 안정성을 높이기 위해 예외처리의 방법에 대해 더 공부하는 시간을 가져보았다.

현재 우리 팀의 프로젝트에서는 Service에서 request의 값이 유효한 값인지 확인하고 예외 발생시키는 방법으로 예외처리를 하고 있었다.

override fun createReply(feedId: Long, request: ReplyCreateRequest ): ReplyResponse {

        if(request.userName.length !in 1 .. PASSWORD_MAX)
            throw InputLengthException("UserName",request.userName.length,1,USERNAME_MAX)

        else if(request.password.length !in PASSWORD_MIN .. PASSWORD_MAX)
            throw InputLengthException("Password",request.password.length,PASSWORD_MIN,USERNAME_MAX)

        else if(request.contents.length !in 1 .. CONTENTS_MAX)
            throw InputLengthException("Contents",request.password.length,1,CONTENTS_MAX)

단순하게 보면 제대로 예외처리는 되었으니 문제 없다고 볼 수 있지만, 어느 계층에서 어떤 에러를 처리할 것인지에 대한 정책도 제대로 설정하지 않으면 프로젝트가 커질 수록 예외처리에 대한 내용이 중구난방이 되어 예외를 놓쳐버리거나 중복으로 체크하여 비효율적이 될 수 있다고 한다.

이에 대한 내용은 확실한 정답이 정해져 있는 것은 아니고 아직 이슈가 되고 있는 내용인듯 했다.

Validation(입력값 검증)의 최적의 장소는 어디일까?
https://starkying.tistory.com/entry/Model-객체에서만큼은-Validation을-필수로-하자

[우아한 테크 코스 5기] 가장 강력한 예외 처리 방법은 무엇이 있을까요?
https://heesangstudynote.tistory.com/109

아직은 이해하기 어려운 내용들이 많았지만 우리 프로젝트에서 적용시킨다면 크게 두 가지로 나눌 수 있다고 생각했다. 입력에 대한 검증과 비지니스 요구사항에 대한 검증.

이중 입력에 대한 검증은 Request 클래스에서 @field:NotBlank 와 @Min(1) @Max(5) 어노테이션을 설정해두고 Controller 에서 @RequestBody 앞에 @Valid 를 붙여 유효성을 검사할 수 있도록 하였다. 이것으로 1차적인 Request 입력 오류를 방지할 수 있었다.

비즈니스 요구사항에 대한 검증은 도메인에서 실시해보기로 하였다.

처음 시도한 방법은 아래와 같았다.

 @Column(name="user_name")
    var userName = _userName
        .also{if(it.isEmpty() || it.length > USERNAME_MAX)
            throw InputLengthException("UserName",_userName.length,1,USERNAME_MAX)
        }

    @JsonIgnore
    @Column(name = "password")
    var password = _password
        .also{if(it.length < 4 || it.length > PASSWORD_MAX)
            throw InputLengthException("Password",_password.length,PASSWORD_MIN,PASSWORD_MAX)
        }

    @Column(name = "content")
    var content = _content
        .also{if(it.isEmpty() || it.length > CONTENT_MAX)
            throw InputLengthException("Content",_content.length,1,CONTENT_MAX)
        }

클래스의 생성자로 _userName, _Password 등을 입력받은 뒤, 클래스의 내용에서 값의 유효성을 체크하는 방법이다.

하지만 이 방법은 문제가 있었다. 객체를 생성할 때는 제대로 처리되지만, 이미 생성된 객체를 업데이트할 때는 아래 과정이 수행되지 않는 것이다..

두번째 방법은 아예 validate() 메서드를 생성하여 검증이 필요할 때 마다 호출하도록 하는 것이었다. 이 방법이라면 create, update 두 상황에서 모두 한 메서드를 호출하여 확인할 수 있고, 이후에도 검증이 필요할 때 언제든 불러올 수 있어 적합한 방법으로 판단되었다.


    init{
        validate()
    }
    fun validate(){
        if(userName.isEmpty() || userName.length > USERNAME_MAX)
            throw InputLengthException("UserName",userName.length,1,USERNAME_MAX)

        if(password.length < 4 || password.length > PASSWORD_MAX)
            throw InputLengthException("Password",password.length,PASSWORD_MIN,PASSWORD_MAX)

        if(content.isEmpty() || content.length > CONTENT_MAX)
            throw InputLengthException("Content",content.length,1,CONTENT_MAX)
    }

각 도메인에 알맞는 내용 값을 넣어 검증할 수 있고, 에러를 검증할 때는 어디에서 어떤 이유인지 메세지로 전달하도록 하였다.

다행히 모든 요청과 응답에 정상 작동 확인 후 PR 완료!


처음 예외처리에 대해 배울땐 그저 예외메세지를 발생시키는 것만 되면 끝이라고 생각했는데 항상 더 큰 규모의 개발을 염두해두고 진행해야 함을 다시 느꼈다. 어디서 어떻게 처리되느냐에 따라 의미가 달라질 수 있고, 안정성과 효율성뿐만 아니라 유지보수에서도 큰 차이를 가져올 내용이기 때문이다. 앞으로도 코드 한줄 한줄의 의미를 고민해보는 노력을 더해야 겠다.

0개의 댓글