Builder 패턴이 항상 최선의 선택인가??

한잔해·2025년 4월 16일

TroubleShooting

목록 보기
2/4

💡 Builder 패턴이 항상 최선의 선택은 아니다. Builder 패턴이 유용한 경우만 쓰는 것이 좋다. 그렇지 않은 경우에는 오히려 코드르 복잡하게 할 수 있다.

팀 프로젝트 중에 Builder Pattern 관련해서 피드백을 얻었다. 무슨 차이가 있을까? 무슨 장점이 있을까? 어떤 상황에 쓰는 것이 좋을까? 라는 생각이 들었다.

✨ Builder 패턴이란?

Builder 패턴은 객체 생성 패턴 중 하나로, 복잡한 객체를 단계별로 생성할 수 있도록 도와주는 디자인 패턴이다.
생성자와 Setter 메서드를 직접 사용하는 대신, Builder라는 별도의 클래스를 통해 객체의 각 속성을 설정하고 최종적으로 객체를 생성합니다.

ℹ️ Builder 패턴의 장점

가독성 향상

  • 객체 생성 코드를 더 명확하고 이해하기 쉽게 만들어준다.
  • 어떤 필드에 어떤 값을 설정하는지 명확하게 드러난다.

유연성 증가

  • 필수 필드만 설정하거나, 선택적으로 특정 필드만 설정할 수 있다.
  • 매개변수의 순서에 의존하지 않고 원하는 속성만 설정할 수 있다.

안정성 확보

  • 매개변수 순서에 따른 실수를 방지할 수 있다.
  • 불완전한 상태의 객체 생성을 방지할 수 있다.

불변 객체 생성 용이

  • Builder를 통해 객체를 생성한 후에는 필드 값을 변경할 수 없도록 만들 수 있다.
  • 멀티스레드 환경에서 안전하게 객체를 사용할 수 있다.

유지보수 용이

  • 클래스에 새로운 필드가 추가되어도 Builder 클래스만 수정하면 된다.
  • 객체 생성 로직이 변경되어도 Builder 클래스만 수정하면 된다.

ℹ️ Builder 패턴이 유용한 경우

선택적 매개변수가 많은 경우

객체를 생성할 때 많은 선택적 매개변수를 설정해야 하는 경우, Builder 패턴은 각 매개변수를 명확하게 지정하고 순서에 상관없이 설정할 수 있게 해준다.

💡 선택적 매개변수란?
함수 또는 메서드 호출 시 필수로 제공하지 않아도 되는 매개변수.

가변적인 객체 생성 로직이 필요한 경우

객체 생성 과정이 복잡하고, 조건에 따라 다른 방식으로 객체를 생성해야 하는 경우, Builder 패턴은 객체 생성 로직을 캡슐화하고 유연하게 변경 할 수 있게 해준다.

불변 객체를 생성해야 하는 경우

Builder 패턴은 객체 생성 후에는 상태를 변경할 수 없는 불변 객체를 만드는 데 적합하다.

가독성이 중요한 경우

객체 생성 코드를 더 읽기 쉽고 이해하기 쉽게 만들어준다.

ℹ️ Builder 패턴을 사용하지 않아도 되는 경우

생성자에 필수 매개변수만 있는 경우

객체 생성에 필요한 매개변수가 적고 필수적인 경우, 간단하게 생성자를 사용하는 것이 더 간결하고 효율적이다.

객체 생성 로직이 단순한 경우

객체 생성 과정이 간단하고 특별한 로직이 필요하지 않은 경우, Builder 패턴을 사용하는 것은 불필요한 복잡성을 더할 수 있다.

성능이 중요한 경우

Builder 패턴은 객체 생성 과정에서 추가적인 객체를 생성하고 메서드를 호출하므로, 성능에 민간함 경우에는 생성자를 직접 사용하는 것이 더 효율적일 수 있다.

🔨 내 코드의 Builder 패턴을 적용해보자!

기존 코드

기존 코드(CommentCreateResponseDto)

//import lombok.Builder; 추가 X

기존 코드 (CommentService - createComment)

    @Transactional
    public CommentCreateResponseDto createComment(Long userId, Long postId, CommentCreateRequestDto requestDto) {
        // 1. 사용자 인증 및 게시글 존재 확인
        User user = userRepository.findById(userId)
                .orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));

        Post post = postRepository.findById(postId)
                .orElseThrow(() -> new CustomException(ErrorCode.POST_NOT_FOUND));

        // 2. 댓글 생성
        Comment comment = new Comment(user, post, requestDto.getCommentText());
        comment.setCommentLikes(0); // 좋아요 개수 0 초기화

        // 3. 댓글 저장
        Comment savedComment = commentRepository.save(comment);

        // 4. CommentCreateResponseDto 생성 및 반환
        return new CommentCreateResponseDto(
                savedComment.getId(),
                savedComment.getUser().getUserId(),
                savedComment.getPost().getId(),
                savedComment.getCommentText(),
                savedComment.getCommentLikes()
        );
    }

변경 코드

변경 코드(CommentCreateResponseDto)

import lombok.Builder; 추가 O

private final 타입 변수;

변경 코드 (CommentService - createComment)

    @Transactional
    public CommentCreateResponseDto createComment(Long userId, Long postId, CommentCreateRequestDto requestDto) {
        // 1. 사용자 인증 및 게시글 존재 확인
        User user = userRepository.findById(userId)
                .orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));

        Post post = postRepository.findById(postId)
                .orElseThrow(() -> new CustomException(ErrorCode.POST_NOT_FOUND));

        // 2. 댓글 생성
        Comment comment = new Comment(user, post, requestDto.getCommentText());
        comment.setCommentLikes(0); // 좋아요 개수 0 초기화

        // 3. 댓글 저장
        Comment savedComment = commentRepository.save(comment);

        // 4. CommentCreateResponseDto 생성 및 반환
        return CommentCreateResponseDto.builder()
                .commentId(savedComment.getId())
                .userId(savedComment.getUser().getUserId())
                .postId(savedComment.getPost().getId())
                .commentText(savedComment.getCommentText())
                .commentLikes(savedComment.getCommentLikes())
                .build();
    }

CommentCreateResponseDto.builder()...build();:
Builder 패턴을 사용하여 CommentCreateResponseDto 객체를 생성합니다.
각 필드에 해당하는 값을 savedComment 객체에서 가져와 설정합니다.

수정 코드의 장점

  • DTO 생성에 Builder 패턴 적용 : CommentCreateResponseDto는 Builder 패턴을 사용하여 생성되므로, 가독성이 높고 유지보수가 용이합니다.
  • 엔티티 생성은 직접 : Comment 엔티티new 키워드를 사용하여 직접 생성되므로, 코드가 간결하고 직관적입니다. 엔티티 생성 시 필요한 로직이 복잡하지 않다면, 직접 생성하는 방식이 더 효율적

📝 결론

Builder 패턴은 객체 생성 코드를 더 읽기 쉽고 유지보수하기 쉽게 만들어주는 도구이다. 하지만 모든 경우 Builder 패턴을 적용해야하는 것은 아니며, 객체의 복잡성, 매개변수의 개수, 불변셩 여부 등을 고려하여 적절하게 선택하는 것이 좋다.

profile
항상 배우고 탐구하는 노력하는 개발자

0개의 댓글