코드숨 스프링 편 5주차 회고: 유효성 검사

Jake Seo·2022년 4월 27일
0

이번 주에는 무엇을 했는가?

  • 간단히 말하자면, 애플리케이션에 검증 프로세스를 더했다.

자바의 Bean validation 을 배웠다.

  • 자바의 Bean validation 은 스프링 부트에서 spring-boot-starter-validation 의존성으로 쉽게 추가할 수 있다.
  • Bean validation 은 java ee 에서 표준화된 스펙이다.
    • javax.validation 패키지에서는 전체적으로 어떤 기능이 있는지에 대한 명세가 있다.
    • javax.validation.contraints 패키지에서는 우리가 실질적으로 자주 사용하게 되는 애노테이션들에 대한 명세가 있다.

배우며 생각해본 것

실무에서 Controller 를 개발하면서 아래의 로직들이 섞이는 것을 많이 봐왔다.

  • 사용자의 요청에서 받은 데이터에 대한 변환 (Converting)
  • 받은 데이터에 대한 검증 (Validation)
  • 받은 데이터에 대한 처리 (Business Logic)
  • 받은 데이터에 대한 반환 (View)

구조상 Validation 에 대한 완전한 분리가 가능할지는 모르겠지만 어느정도는 분리하여 앱의 복잡성을 제거할 수 있겠다고 생각이 들었다. 검증에 대해 간단하게 표현 가능한 부분을 javax.validation 을 통해 간소화할 수 있었다.

특히 도메인 클래스에 대한 검증을 매우 쉽게 만들어준다. 필드 위에 검증 애노테이션을 하나 얹고, 컨트롤러에서 @Valid 애노테이션을 파라미터 앞에 붙여주면 컨트롤러 단으로 데이터가 넘어올 때 자동으로 검증된다.

경험상 validation 을 구성할 때, 컨트롤러의 파라미터 앞에 @Valid 애노테이션을 깜빡하는 경우가 생각보다 많았다. 꼭 테스트를 꼼꼼히 하자.

스프링과의 연동

아래의 포스팅들을 통해 스프링과의 연동 방식을 알았고, @ControllerAdivce 를 통해 API 에 대한 예외처리를 잘 할 수 있었다.

공부 결과물

Lombok 을 배워보았다.

자바에서 자주 쓰이는 보일러 플레이트 코드인 Getter, Setter, Constructor, Builder 등을 쉽게 만들 수 있도록 도와주는 라이브러리이다.

배우며 생각해본 것

Lombok 은 말도 많고 탈도 많다. 편의성 측면에서는 매우 좋다. 그러나, 설계 측면에서는 글쎄..? 내 코드에 어떤 부작용을 줄 수 있을지 항상 생각하며 사용하자.

개발자는 라이브러리나 프레임워크를 활용하여 쉽게 프로덕션을 만들어낼 수 있다. 그러나 해당 라이브러리나 프레임워크에 대한 이해가 없다면 이로 인해 많은 버그를 만들어내기도 쉽다.

  • 롬복의 애노테이션이 하는 역할을 이해하며 사용하자.
  • 롬복에서 제공하는 access 엘리먼트를 잘 활용하자.
  • lombok.config 파일을 이용하여 롬복의 자유도를 제한하자.

공부 결과물

JPA 와 DTO 에 대한 생각을 해보았다.

JPA 는 ORM 기술 중 하나로, 관계형 DB 와 자바 객체간의 불일치를 해결해주는 데 도움을 준다. JPA 에서 사용하는 엔티티(@Entity)는 관계형 DB 에서 테이블에 있는 한 row 와 매칭된다.

엔티티는 데이터를 매핑하여, 활용하기에는 좋으나 Input, Output 에는 별도의 객체를 구성해 활용하는 편이 좋다.

엔티티를 그대로 요청이나 응답에 활용하기 보다는 DTO (Data Transfer Object)를 활용할 수 있고 이는 컨트롤러 단에서 요청 또는 응답에 사용되는 매개체로 훌륭한 역할을 할 수 있다.

DTO 의 장점은 무엇인가?

간단히 축약해서 말하자면, 엔티티를 그대로 사용했을 때에 발생하는 문제를 해결할 수 있다.

  • 첫째, 원하는 정보만 받거나 보여줄 수 있다.
  • 둘째, 원하는 검증 조건만 걸 수 있다.

첫째는 엔티티를 그대로 반환하거나 받아들일 때의 단점을 해결한다. 회원 정보 조회를 할 때 엔티티를 그대로 반환한다면, 비밀번호 등 모든 정보를 보여주게 될 것이다. 그러나 DTO 를 반환하면 DTO 에 외부로 보여주기 원하는 필드만 세팅하여 보여줄 수 있다.

둘째는 엔티티를 그대로 검증할 때의 단점을 해결한다. 회원 가입 시에는 아이디, 이름, 비밀번호 등 대부분의 정보가 필수인 반면에, 회원 정보 수정 시에는 필수 값이 아닐 수 있다. 이를테면 회원 수정 폼 제출 시 이름에 대한 정보만 받았다면, 이름만 바꿔주는 식이다. 이렇게 특정 작업에 특화된 검증 객체로서 활용될 수 있다.

DTO 는 어디까지 공개해야 하는가?

이 부분은 개발자마다 생각이 다른듯 하다.

윤석님: 최소한의 레이어에만 공개해야 하는 것이 바람직하다.
종립님: DTO 클래스를 사용하는 것보다는 차라리 인터페이스를 사용하고, DTO 가 해당 인터페이스를 구현하게 하자.
아샬님: DTO 를 Infrastructure Layer 까지 연결하면 오히려 CQRS 에서까지 유용하게 쓸 수 있다.

핵심은 프로그램을 복잡하게 만드는 요인을 파악해서 제거하자는 것이다.

결국 정답은 없는 것 같다.

공부 결과물

객체를 매핑하는 라이브러리들에 대해서 알아보았다.

대표적으로 ModelMapper, Dozer 등이 있었다.

나의 시간을 낭비시켰던 것

이 라이브러리들은 모두 @Getter, @Setterpublic 으로 공개되어 있어야 제 역할을 한다. 없다면, 로그 메세지라도 줬으면 좋겠는데 어떠한 말도 없이 동작을 하지 않아서 약간의 혼란을 주었다.

코드 리뷰 Discussion

@Configuration@Bean 에 대해 이해해보자

롬복 사용의 주의점을 알아보자.

@GeneratedValue 의 전략들을 알아보자.

import * 의 사용을 지양하자.


  • Intellij 위 설정에서 패키지 하나에서 몇 개의 import 를 할 때 * 이 되는지를 설정할 수 있다. 저걸 늘려서 import * 이 나오는 것을 최대한 방지하자.

메서드 테스트에서 메서드는 행동하는 대상으로 생각하자.

  • 메서드를 마치 장소처럼 생각하여 ... 메서드에서 라는 표현을 썼는데, 메서드는 행동하는 대상으로 보고 ... 메서드는, ... 메서드가 등의 표현으로 고쳐야겠다.

계층형 테스트를 작성할 때는 합쳤을 때 문장이 잘 어우러지는지 확인하자.

Bad Request 는 에러가 아니라 응답이다.

'없는' 은 애매한 표현이 될 수 있다.

  • 없는은 "" 과 같이 공백인 경우와 null 같이 null 인 경우로 구분될 수 있어서 어떤 게 '없는' 인지 알기 어려우니 더 명확한 표현을 사용하자.

@Data 가 어떤 역할을 하는지 설명해보자.

  • @ToString, @EqualsAndHashCode, @Getter 를 모든 필드에 설정한다.
  • @Setter 필드를 final 이 아닌 필드에 설정한다.
  • @RequiredArgsConstructor 를 설정한다.

기타 혼자서 생각해본 것들

테스트에서의 생성자 사용

  • 테스트 시 한번만 실행했으면 좋겠는 것은 생성자 로 처리하자.
  • 매 테스트 메서드마다 실행할 것만 @BeforeEach 를 사용하자.
  • @BeforeAllstatic 메서드로 작성해야 해서 별로이다.

@Valid 잊지 않기

Bean Validation 을 적용하고도 컨트롤러 파라미터 부분에 @Valid 애노테이션이 없으면 Bean Validation 은 작동하지 않는다.

jacoco 테스트 커버리지 제외 기능

테스트 커버리지가 필요 없는 부분들을 제거할 수 있다.

jacocoTestCoverageVerification {
    violationRules {
        rule {
            element = "CLASS"

            limit {
                counter = 'LINE'
                minimum = 1
            }

            limit {
                counter = 'BRANCH'
                minimum = 1
            }

            excludes = [
                    "com.codesoom.assignment.App",
                    "com.codesoom.assignment.dto.*",
                    "com.codesoom.assignment.domain.*"
            ]
        }
    }
}

망할 JavaBeans 에러

좋은 설계를 위해 Setter, Constructor 등을 제한하면 자꾸 JavaBeans 가 내 앞길을 막는다.

Dozer 의 경우에는 Setter 가 없으면 제대로 매핑이 되지 않았다. 앞으로 공식문서에서 이러한 부분들을 잘 찾아봐야겠다. JavaBeans Mapper 라는 이름부터 Setter 가 필요하다는 의미였을 수도 있겠다.

공식문서의 중요성

모르는 키워드가 나올 때마다 공식문서에서 찾아서 하나씩 정리하다보면, 언젠가는 인간 스펙이 될 것 같다. 이제 공식문서 정리하는 게 좀 더 익숙해지고 있는데, 이렇게 공식문서 내용을 이해하는 게 익숙해지면 어떤 언어, 어떤 라이브러리, 어떤 프레임웍을 사용하게 되더라도 해낼 수 있다는 자신감이 생길 것 같다.

공식문서의 정리 방법은 처음부터 목차대로 하나하나씩 정리하기보다는 모르는 게 나올 때마다 하나하나 확실히 알아가는 게 좋을 것 같다. 그리고, 보통 공식문서를 검색하면 스펙이 나오는데, 구현체를 구현한 웹사이트에 세부적인 내용이 많다. 이를테면 이번에는 Java ee persistence 에 있는 @GeneratedValue, GenerationType 등에 대해 알아보았는데, 하이버네이트 공식문서에 세부적인 구현을 아주 자세히 설명해놓았었다.

그것도 모르고 java ee 공식문서만 뒤졌더니 굉장히 추상적이고 이해하기 힘든 내용만 있었다.

profile
풀스택 웹개발자로 일하고 있는 Jake Seo입니다. 주로 Jake Seo라는 닉네임을 많이 씁니다. 프론트엔드: Javascript, React 백엔드: Spring Framework에 관심이 있습니다.

0개의 댓글