스택큐힙리스트

Builder 패턴으로 Todo 객체 한방에 완성하기 본문

개발

Builder 패턴으로 Todo 객체 한방에 완성하기

스택큐힙리스트 2025. 7. 16. 00:53
반응형

“선택 옵션이 수두룩해도 메서드 체이닝으로 깔끔!”


1. 왜 Builder 패턴인가?

  • 선택 파라미터 폭발 — 마감일·우선순위·태그·반복 주기… CLI Todo 앱의 Task는 필드가 늘면 생성자 매개변수 순서가 금세 헷갈립니다.
  • 점층적 생성자 vs JavaBeans의 한계 — 파라미터 조합별로 생성자를 늘리면 코드 지옥, setter 방식은 객체가 잠시 불완전해져 스레드 안전성이 깨집니다. Joshua Bloch의 Effective Java Item 2에서도 “선택 매개변수가 많을 때는 Builder가 최선”이라 강조하죠.
  • 인기 블로그 Inpa Dev 역시 “가독성과 일관성을 동시에 잡는 해법”으로 Builder를 꼽습니다.

2. Todo 앱에 적용해 보기

// ① 도메인 클래스
public class Task {
    private final String title;          // 필수
    private final LocalDate dueDate;     // 선택
    private final int        priority;   // 선택
    private final Set<String> tags;      // 선택

    private Task(Builder b) {
        this.title    = b.title;
        this.dueDate  = b.dueDate;
        this.priority = b.priority;
        this.tags     = Set.copyOf(b.tags);
    }

    // ② Builder 내부클래스
    public static class Builder {
        private final String title;          // 필수
        private       LocalDate dueDate;     // 기본값 null
        private       int        priority = 3; // 기본값 보통
        private       Set<String> tags   = new HashSet<>();

        public Builder(String title) {
            this.title = Objects.requireNonNull(title);
        }
        public Builder due(LocalDate d) { this.dueDate = d; return this; }
        public Builder priority(int p)   { this.priority = p; return this; }
        public Builder tag(String t)     { this.tags.add(t); return this; }

        public Task build() {
            return new Task(this);   // **검증**도 여기서!
        }
    }
}

// ③ 사용 예
Task task = new Task.Builder("블로그 글쓰기")
               .due(LocalDate.of(2025, 7, 20))
               .priority(1)
               .tag("writing")
               .tag("deep-work")
               .build();
  • 가독성 ▲: 어떤 필드에 어떤 값이 들어가는지 바로 보입니다.
  • 불변성 ▲: Task는 final 필드만 가지므로 스레드 안전.
  • 검증 포인트 ▲: build() 안에서 필수값·비즈니스 규칙 체크를 일괄 수행할 수 있습니다.

3. Lombok @Builder로 더 간결하게

추천하는 방식은 어노테이션 한 줄입니다.

@Builder
public class Task {
    private String  title;
    private LocalDate dueDate;
    private int     priority;
    private Set<String> tags;
}

 

 

주의
@NoArgsConstructor와 함께 쓰면 기본 생성자와 충돌할 수 있으니 @AllArgsConstructor 또는 필요 파라미터를 받는 생성자 위에 @Builder를 붙이는 식으로 조정하세요. Velog


4. 실전 팁

  1. 필수값 강제: Builder 생성자에 필수 파라미터만 남겨 두면 누락을 컴파일 단계에서 막을 수 있습니다.
  2. 검증 로직: build() 안에서 null 체크나 비즈니스 룰(예: 과거 날짜 금지)을 throw 하면 객체 일관성이 보장됩니다.
  3. 계층적 Builder: Task → RecurringTask 식 계층에도 적용 가능 (covariant return). Effective Java가 소개한 “계층 빌더” 기법을 참고해 보세요.
  4. 성능 민감 시: Builder 인스턴스가 잦다면 재사용(예: TaskBuilder pool)을 고려하거나 필드가 3–4개 이하면 그냥 생성자를 쓰는 편이 낫습니다.

5. 언제 피할까?

  • 필드가 적고, 변형 가능성이 거의 없는 단순 VO(Value Object).
  • 성능이 극도로 중요한 코드(Builder 객체 자체가 한 번은 생성돼야 하므로 미세 오버헤드 존재).

마무리

Builder 패턴은 “읽기 쉬운 코드”“불변 객체”를 동시에 챙기는 훌륭한 무기입니다. CLI Todo 앱처럼 확장성이 예상되는 프로젝트에 처음부터 적용해 두면, 기능이 커져도 생성자 파라미터 지옥에 빠지지 않습니다. 블로그·강의에서 강조하듯 실전에서도 한 번 써 보면 “왜 진작 안 썼지?” 하는 패턴이니, 오늘 바로 Task.Builder를 만들어 보세요!

반응형
Comments