Hi yoahn 개발블로그

Builder 패턴에 대한 이야기 본문

프로젝트 개발 일지

Builder 패턴에 대한 이야기

hi._.0seon 2024. 2. 22. 13:02
반응형

42GG 3기때 리팩토링을 하면서 몇가지 규칙을 정했었는데, 그중 하나인 Builder 패턴에 대한 글이다.

 

먼저 현재 팀에서 정한 규칙은, Builder 호출을 컨트롤러단, 서비스 단에서는 금지하는 규칙이다.

 

@Builder 패턴의 장점

먼저 builder 패턴의 장점을 알아보자

 

내가 생각하는 장점

매개변수 명시적 지정

매개변수가 많은 생성자의 경우, 타입이 같은 매개변수가 여러개가 있다면 각 위치에 어떤 값을 넣느냐가 매우 중요해진다.

 

이때 리팩토링을 하다가 원래 있던 위치의 매개변수가 생성자 내에서 같은 타입이지만 다른 역할로 변경되었다면, 프로그램 실행에는 문제가 없기 때문에 버그를 잡기 어렵다.

 

하지만 builder 패턴을 사용하면 어떤 멤버변수를 초기화 할건지를 명시적으로 지정하게 되어 생성자의 매개변수 순서가 달라지더라도 영향을 받지 않고, 변경이 필요하면 바로 파악할 수 있게 된다.

 

 

하지만 이런 장점에도 불구하고 컨트롤러단, 서비스 단에서는 호출을 금지했다.

리팩터링을 위해 기존 코드를 파악하는 작업이 필요했는데, builder 패턴 사용으로 인해 호출 한번 할때마다 5줄 정도는 기본으로 차지하고 그런 호출이 하나의 함수 내에 여러번 일어나니 메인 로직이 잘 보이지 않는 문제가 있었고, 코드가 지저분하게 느껴졌다.

 

[이전 서버 프로젝트 코드]

@GetMapping(value = "/notifications")
    public NotiResponseDto notiFindByUser(HttpServletRequest request) {
        UserDto user = tokenService.findUserByAccessToken(HeaderUtil.getAccessToken(request));
        NotiFindDto notiFindDto = NotiFindDto.builder()
                .user(user).build();
        List<NotiDto> notis = notiService.findNotiByUser(notiFindDto);
        List<Object> notiDtos = new ArrayList<>();
        notis.forEach(noti -> {
            if (noti.getType().equals(NotiType.ANNOUNCE)) {
                notiDtos.add(NotiAnnounceDto.builder()
                        .id(noti.getId())
                        .type(noti.getType())
                        .isChecked(noti.getIsChecked())
                        .message(noti.getMessage())
                        .createdAt(noti.getCreatdDate())
                        .build());
            } else if (noti.getType().equals(NotiType.MATCHED)) {
                notiDtos.add(NotiMatchedDto.builder()
                        .id(noti.getId())
                        .type(noti.getType())
                        .time(noti.getSlot().getTime())
                        .isChecked(noti.getIsChecked())
                        .createdAt(noti.getCreatdDate())
                        .build());
            } else if (noti.getType().equals(NotiType.IMMINENT)) {
                TeamsUserListDto teamsUserListDto = teamService.findUserListInTeams(noti.getSlot(), user);
                List<String> myTeam = new ArrayList<>();
                List<String> enemyTeam = new ArrayList<>();
                teamsUserListDto.getMyTeam().forEach(userDto -> myTeam.add(userDto.getIntraId()));
                teamsUserListDto.getEnemyTeam().forEach(userDto -> enemyTeam.add(userDto.getIntraId()));
                notiDtos.add(NotiImminentMatchDto.builder()
                        .id(noti.getId())
                        .type(noti.getType())
                        .time(noti.getSlot().getTime())
                        .isChecked(noti.getIsChecked())
                        .myTeam(myTeam)
                        .enemyTeam(enemyTeam)
                        .createdAt(noti.getCreatdDate())
                        .build());
            } else if (noti.getType().equals(NotiType.CANCELEDBYMAN) || noti.getType().equals(NotiType.CANCELEDBYTIME)) {
                notiDtos.add(NotiCanceledDto.builder()
                        .id(noti.getId())
                        .type(noti.getType())
                        .time(noti.getSlot().getTime())
                        .isChecked(noti.getIsChecked())
                        .createdAt(noti.getCreatdDate())
                        .build());
            }
        });
        NotiModifyDto modifyDto = NotiModifyDto.builder()
                .user(user).build();
        notiService.modifyNotiChecked(modifyDto);
        NotiResponseDto responseDto = NotiResponseDto.builder()
                .notifications(notiDtos)
                .build();
        return responseDto;
    }

 

하지만 builder 패턴의 사용 자체를 금지하지는 않았는데, 위에서 말한 장점이 무시할 수 없는 부분이라고 생각하기 때문이다.

현재 프로젝트에서 builder 패턴을 사용하는 경우는 거의 dto 를 entity 객체로 만들어야 하는 경우가 대부분이었다.

 

그래서 처음에는 builder 패턴으로 생성해야 하는 객체의 static method 로 from 메서드를 사용했는데, 이때 여기서도 매개변수가 많아지는 경우가 있었다.

 

서비스 레이어나 컨트롤러 단에서 builder 패턴을 위한 메서드를 분리하는 것이 맞지 않는다고 생각해서 DTO 클래스 내에 toEntity() 메서드를 사용하여 그 안에서 builder 를 호출하는 형태로 변경하고 있는데, 매번 모든 DTO 에서 toEntity 를 만드는게 맞는지에 대한 논란이 있지만, 아직 더 나은 방안을 찾지 못해 이렇게 진행하고 있다.

 

반응형

'프로젝트 개발 일지' 카테고리의 다른 글

GG 프로젝트 회고  (0) 2024.03.20
[42Seoul] 42gg 유지보수 ing  (3) 2024.02.29
42GG 3기 개발 회고  (0) 2023.08.10
[42GG] 3기 개발일지 2  (0) 2023.05.13
[42GG] 3기 개발일지 1  (0) 2023.04.21
Comments