코드스쿼드 7 - ORM, 빌더, 테스트 자동화

Alex·2024년 7월 4일
0

리팩토링

목록 보기
8/17
        List<AccoImage> accoImages = request.buildAccoImages(accommodation);
        accommodation.addImages(accoImages);

        Accommodation savedAcco = accoRepository.save(accommodation);

현재 Accommodation 안에서 양방향 맵핑과 Cascade를 사용하여 Amenities와 Images를 한 번에 저장하고 있습니다.
FK를 직접 가지고 있는 Amenity와 Images가 아닌, Accommodation을 통해 한 번에 저장하는 행위는 객체의 관점에서는 자연스러우나(항상 Accommodations를 통해서 amenity나 images가 등록되고 조회되기 때문에)
DB 입장에서는 1:M 관계에서 1인 입장이 모든 것을 컨트롤 하고 있는 것이 부자연스럽다는 생각이 듭니다.
어떤 방법이 어느 상황에서 더 유리하고 불리할까요?

이에 대한 답변으로 리뷰어가

orm 관점에서는
Accommodation 로 접근하여 Amenity 와 Images 를 컨트롤하는 것이 자연스럽습니다.
db 관점에서도 fk 가 있다면 그다지 이상한 관계는 아닙니다.
하지만 실무에서는 fk 를 빼는 경우도 많습니다.
https://news.hada.io/topic?id=878
그렇게 되면 db 관점에서 보면 꽤 이상하긴 할 것 입니다.
이것은 도메인에서 시작하는 orm 의 관점과 db 에서 부터 바라보는 시점이 다르기 때문 입니다.
orm 의 장단점에 대해 찾아보시면 좋습니다.

개발자들이 ORM을 쓰는 이유와 DBA가 ORM을 싫어하는 이유

ORM(Object-Relational Mapping)은 SQL 쿼리를 작성하지 않고도, 객체 지향 프로그래밍(OOP) 개념을 사용하여 데이터베이스와 상호 작용할 수 있도록 하는 프로그래밍 기술이라고 한다.

데이터베이스 테이블을 클래스에 매핑하고 행을 프로그래밍 언어의 개체에 매핑해준다.

ORM을 사용하면 다음과 같은 장점을 얻는다.

1) 추상화: ORM은 더 높은 수준의 추상화를 제공하여 데이터베이스와의 상호 작용을 단순화한다.

2) 보일러 플레이트 코드 감소: ORM은 대부분의 일반적인 데이터베이스 작업을 ORM 시스템이 처리하기 때문에 반복적인 보일러 플레이트 코드를 줄인다.

3) 이식성: ORM 시스템은 일반적으로 여러 데이터베이스 시스템을 지원하므로 개발자는 코드 변경을 최소화하면서 서로 다른 데이터베이스 간에 전환을 쉽게 할 수 있다.

빌더 사용

코드를 작성하면서도 필드 수가 많아지면 많아질수록 빼먹을 가능성이 높겠다는 생각이 들었던 것 같습니다. 혹시 이런 것들을 자동화할 수 있는 방법이 있을까요?

자동화라는 질문이 좀 모호한데
빌더를 사용함에 있어 자동으로 어시스트 해주는 기능은 없습니다.
컴파일 에러도 발생하지 않습니다. java 는 기본적으로 null safe 하지 않기 때문 입니다.
저는 프로덕션 코드에서는 builder 를 사용하지 않습니다. 생성자를 사용합니다.

빌더를 쓰면 주요 인자를 빼먹을 수 있다는 단점이 있는 거 같다. 생성자 사용이 좀더 안전한 방식으로 진행될 듯하다.

테스트 자동화

테스트 자동화는 없지만, Monkey fixture라는 라이브러리를 쓰면 테스트를 좀더 편하게 할 수 있다고 한다.

TestFixture를 쉽게 생성해 주는 라이브러리가 있다?

아무래도 내가 직접 테스트를 위한 객체를 만들다보면
엣지 케이스를 놓칠 수가 있다. 랜덤으로 만들면 이런 일을 방지할 수 있다.

라이브러리를 넣으니 값이 자동으로 들어왔다.

java bean validation으로 설정해놓은 걸 기준으로 값이 들어간다고 한다.
근데, 왜 중국어?가 나오는지 모르겠다...

Product actual = fixtureMonkey.giveMeBuilder(Product.class)
    .set("id", Arbitraries.longs().greaterOrEqual(1000))
    .set("productName", Arbitraries.strings().withCharRange('a', 'z').ofMaxLength(10))
    .set("productType", Arbitraries.of(ProductType.CLOTHING, ProductType.ELECTRONICS))
    .sample();

then(actual.getId()).isGreaterThanOrEqualTo(1000);
then(actual.getProductName()).matches("^[a-z]+$");
then(actual.getProductName().length()).isLessThanOrEqualTo(10);
then(actual.getProductType()).matches(it -> it == ProductType.CLOTHING || it == ProductType.ELECTRONICS);

공식문서를 보니 언어를 설정해줄 수 있다고 한다.


       FixtureMonkey fixtureMonkey = FixtureMonkey.builder()
                .objectIntrospector(ConstructorPropertiesArbitraryIntrospector.INSTANCE)
                .build();

        Arbitrary<String> koreanString = Arbitraries.strings().withChars("가나다라마바사아자차카타파하").ofMinLength(2).ofMaxLength(10);

        AccommodationSave accommodationSave1 = fixtureMonkey.giveMeBuilder(AccommodationSave.class)
                .set("name", koreanString)
                .set("address", koreanString)
                .sample();

이렇게 코드를 변경해주니 한글이 적용됐다!

profile
답을 찾기 위해서 노력하는 사람

0개의 댓글