스프링 디렉토리 구조

김다혜·2020년 2월 9일
3

spring-guide

목록 보기
1/2

2019년에 했던 '스프링 가이드' (스터디 자료로 공유된 내용은 비공개이므로 해당 repository를 공유) 스터디 내용을 바탕으로 내용을 정리하고자 한다. 모든 것을 100%로 이해했다고 생각하진 않지만, 이해한 내용을 바탕으로 정리하려고 한다.😊

1회차 내용은 디렉토리구조 라는 주제였다. 디렉토리 구조와 더불어 객체&도메인에 대한 내용도 다루었는데, 그 이후에 사내에서 진행한 프로젝트에 많은 영향을 주었던 내용이다.

📚디렉토리 구조

계층형 vs 도메인 형

  • 계층형 : spring 디렉토리를 예로 들면, controller/service/dao.. 와 같은 디렉토리들이 상위 개념이 되는 것이다.
    👍장점
    • 작성하고자 하는 계층이 명확한 경우에는 빠르게 개발 가능하다.
    👎단점
    - 각 레이어 (controller, service 등)별로 수십개의 클래스들이 존재하기 때문에 코드 파악이 어려움
    • Layer 기준으로 분리했기 때문에 코드 응집력이 떨어짐
  • 도메인 형 : 구현하고자하는 도메인 디렉토리가 상위 개념이 되는 것이다. (예를 들면, 회원 / 어드민 / 상품과 같은 개념들)
    👍장점
    • 관련된 코드들이 응집해 있음 (새로 입사한 신입 사원도 빠르게 이해가능하지 않을까? ㅎㅎ)
    • 디렉토리 구조로 도메인을 이해할 수 있음
    👎단점
    - 도메인 지식이 필요함
    • 각 계층을 구분하기 위한 논의가 필요

도메인 끼리는 응집력이 높아야 한다. (즉, 유사한 성격의 도메인끼리는 함께 있는 것이 좋다는 의미이다.) 따라서, 한 곳에 때려박기 식 디렉토리 구조는 좋지 않다. (도메인 응집력이 높아질수록 직관적이며, 해당 도메인을 이해하기 쉬워진다.) 이러한 관점에서 '도메인 형' 디렉토리 구조를 더 좋은 구조라고 생각한다.

😮객체와 도메인

아래의 예제는 우리가 쇼핑 사이트를 개발한다고 상상하고 보자.

완벽한 객체를 생성하자

🙋‍♀️ 요구사항 : 주문상품이 있어야 주문이 가능하다.
👩‍💻 개발자 관점 : 상품 기반으로 주문을 만들어야 한다.

@Getter
public class Order {

    private Long id;

    private Address address;

    private Orderer orderer;

    private List<OrderItem> orderItems = new ArrayList<>();

    public Order(Address address, Orderer orderer, List<ProductItem> productItems) {
        Assert.notNull(address, "address must not be null");
        Assert.notNull(orderer, "orderer must not be null");
        Assert.notEmpty(orderItems, "orderItems must not be empty");

        this.address = address;
        this.orderer = orderer;
        for(ProductItem productItem : productItems) {
            orderItems.add(new OrderItem(productItem));
        }
    }
}

🕵️‍♀️ 👆코드 설명

  • 객체 생성에서 강제하자.
    👉 예제 코드의 Order의 생성자를 보자. Order 객체를 생성할 때에, 상품 (productItems)을 파라미터로 넘겨야 생성 가능하다.
  • 객체 생성할 때에 꼭 있어야 할 값이라면 exception 처리하자
    👉 객체 생성에서 강제하지 않는다면 null 체크 비용이 뒷 단(DB나 서비스 로직)으로 넘어가게된다.
    👉 예제로 보면, 주문은 상품이 있어야 주문이 가능하다고 했다. 즉, 상품이 없다면 주문이 불가능하다는 의미이다. 즉, 주문이 null 이라면 상품 생성은 불가능 하다.

정적 팩토리 메서드 패턴을 사용하자

🙋‍♀️ 요구사항 : 회원 주문 / 비회원 주문이 있다.
👩‍💻 개발자 관점 : 해당 요구사항을 최대한 코드로 해석 가능하게 작성하자.

@Getter
@NoArgsConstructor
public class Orderer {

    private Long id;

    private String name;

    private Orderer(Long id, String name) {
        Assert.notNull(name, "name must not be null");
        this.id = id;
        this.name = name;
    }

    public static Orderer memberOrderer(Long id, String name) {
        Assert.notNull(id, "id must not be null");
        return new Orderer(id, name);
    }

    public static Orderer nonMemberOrderer(String name) {
        return new Orderer(name);
    }
}

🕵️‍♀️ 👆코드 설명

  • 회원 주문(memberOrderer) / 비회원 주문(nonMemberOrderer)의 객체 생성을 분리 (보다 직관적인 코드를 만들 수 있다.)
  • 생성자는 private 하게
    👉 public으로 열어두면 null 체크가 까다로워짐
  • static method 패턴 이용
    👉 naming을 통해 보다 직관적으로 생성할 수 있음 (코드 의도가 보다 명확해짐)

0개의 댓글