ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • RESTful API 설계하기
    이커머스 devops/국비교육 2025. 11. 16. 12:30

    "RESTful하다 = HTTP 규칙(메서드, 상태코드, URI)을 잘 지켜서 자원 중심의 일관된 인터페이스를 제공하는 것"

     

     

    Lombok @Builder : 클래스 vs 생성자

    - @Builder는 빌더 패턴(Builder Pattern)을 자동으로 생성하여 복잡한 객체 생성을 명확하고 안전하게 만들어 준다

    - @Builder는 클래스 전체 또는 특정 생성자에 붙일 수 있다

     

     

    1. 클래스에 @Builder 붙이는 경우

    • 가장 일반적이고 간단한 사용법
    • 클래스 레벨에 @Builder를 선언하면 해당 클래스의 모든 필드를 초기화할 수 있는 빌더가 생성된다
    • 동작 방식 : Lombok은 모든 필드를 파라미터로 받는 생성자(@AllArgsConstructor)를 자동으로 만들고, 이 생성자를 기반으로 빌더를 생성한다
    // 코드 예제
    @Getter
    @Builder
    @AllArgsConstructor // 클래스 레벨 빌더는 모든 필드 생성자를 필요로 함
    @NoArgsConstructor  // JSON 변환 등에서 필요할 수 있음
    public class Product {
        private String name;
        private double price;
        private String description;
    
        @Builder.Default // 필드 초기값을 빌더에 반영하려면 @Builder.Default 사용
        private boolean onSale = false; 
    }
    
    // 사용법
    Product product = Product.builder()
            .name("Laptop")
            .price(1500.00)
            .description("A high-performance laptop")
            .onSale(true) // onSale 필드도 설정 가능
            .build();
    • 장점
      • 간편함과 완전성
      • 높은 유연성
    • 단점 및 주의사항
      • 필수 값 강제 불가 : 어떤 필드가 필수인지 빌더 수준에서 강제할 수 없다
      • 필드 기본값 무시 : 필드에 직접 초기값을 주면 @Builder는 무시한다 필드 초기값을 빌더 생성 시에도 유지하려면 반드시 @Builder. Default 어노테이션을 함께 사용해야 한다 

     

     

    2. 생성자에 @Builder 붙이는 경우

    • 특정 생성자 위에 @Builder를 선언하면 해당 생성자의 파라미터로 받은 필드들만으로 빌더 생성
    • 객체 생성을 더 엄격하게 제어하고 싶을 때 사용
    • 동작 방식 : Lombok은 @Builder가 붙은 생성자만을 사용하여 빌더 코드를 생성한다
    // 코드 예제
    @Getter
    public class Product {
        private final String name; // 필수 값
        private final double price;  // 필수 값
        private String description;  // 선택 값
    
        @Builder // 이 생성자에 대해서만 빌더를 만듦
        public Product(String name, double price) {
            // 생성자 내부에서 유효성 검증 로직 추가 가능
            if (name == null || name.isBlank()) {
                throw new IllegalArgumentException("상품 이름은 필수입니다.");
            }
            this.name = name;
            this.price = price;
            // 특정 필드를 내부적으로 초기화할 수도 있음
            this.description = "기본 설명"; 
        }
    }
    
    // 사용법
    Product product = Product.builder()
            .name("Laptop")
            .price(1500.00)
            // .description("...") -> 빌더에 포함되지 않아 컴파일 에러 발생
            .build();

     

    • 장점 
      • 필수값 강제
      • 초기화 로직 추가 : 생성자 내부에 유효성 검증, 다른 필드를 조합하여 새로운 값을 만드는 등 복잡한 초기화 로직을 추가할 수 있다
    • 단점
      • 빌더를 통해 초기화할 수 있는 필드가 제한된다
      • 다른 조합의 객체를 생성하려면 별도의 생성자와 빌더를 또 만들어야 할 수 있다
    사용 사례 추천 방식 이유
    단순한 DTO, Response 객체 클래스에 @Builder 모든 필드를 유연하게 채워야 하고, 복잡한 생성 로직이 필요 없는 경우가 대부분이기 때문이다
    엔티티, 도메인 객체 생성자에 @Builder "이름은 필수, 가격은 0이상"과 같이 필수값을 강제하고 객체가 생성될 때부터 일관된 상태를 보장해야하기 때문이다
    필드에 기본값을 설정하고 싶을 때 클래스에 @Builder + @Builder.Default @Builder.Default를 사용하면 필드 초기화와 빌더의 유연함을 동시에 가져갈 수 있기 때문이다

     

     

    Builder 패턴

    - 복잡한 객체의 생성 과정과 최종 표현을 분리하여 객체를 단계별로 유연하고 안전하게 만들 수 있도록 한다

    - 높은 가독성 : 어떤 필드에 어떤 값이 들어가는지 명확하게 알 수 있다

    - 유연한 객체 생성 : 필요한 필드만 선택적으로 설정할 수 있고, 순서에 상관없이 값을 지정할 수 있다

    - 불변성 확보 : setter 필요 없이 build()메서드가 호출되는 시점에 완전한 상태의 불변 객체를 생성할 수 있다

     

     

    서비스 메서드 네이밍 규칙 : get vs find

    - 메서드 이름은 해당 메서드의 동작 제약을 나타내는 중요한 규칙이다

    • get
      • 데이터가 반드시 존재함을 보장한다
      • orElseThrow()를 사용하여 데이터가 없을 경우 예외를 던지는 방식
      • 로직상 데이터가 반드시 존재해야만 다음 단계 진행이 가능한 경우 사용
      • 예) 로그인한 사용자의 정보 조회
    • find 
      • 데이터가 없을 수도 있음을 인정한다
      • Optional<T>를 그대로 반환하여 호출한 쪽에서 데이터 존재 여부를 판단하고 처리하도록 위임
      • 데이터의 존재 여부 자체가 비즈니스 로직의 중요한 분기점이 될 때 사용
      • 예) 이메일 중복 확인
    728x90
Designed by Tistory.