본문 바로가기
SPRING

[Spring] Static Class, Custom Builder 소개

by 킹명주 2023. 7. 22.

요즘 날이 너무 더워서 사망 직전까지 가고있다.. 근데,, 비까지 많이 오니 최악이다.. 그러니 블로그라도 활기차게 써야겠다 ^ 3^

 

요번에는 Static Class와 Custom Builder 패턴에 대하여 소개하겠다. 이 부분도 현업에서 가장 많이 사용하고 있는 부분인데, 가독성 좋은 코드를 짜기에 매우 좋다고 판단하여 이번에 소개하고자 한다.

 


Static Class

 

현업에서 Static class는 클래스 내부에서 클래스를 지정할 때 사용하고 있다. 아 !! 그러면 Static Class는 Inner Class를 구현하기 위해 활요하는 것!! 이라고 생각하면 쉽다.

 

그렇다면 어떤 장점이 있을까? 개인적인 경험을 바탕으로 가장 좋았던 장점 두 가지를 간단하게 소개하자면

1. Package 정리

저~~~엉말 회사 DTO 구조를 보면 너무 복잡하다.. 뭐가 뭔지 구별이 안간다.. 예를들어

ContractSendResponse

ContractPushResponse 

ContractSelectResponse

....

이런식으로 계속 있다보니 정말 정신 나갈 것 같다..

이것을 크게 Outer Class로 지정해주고 세부적으로 나눈 예시를 보면

outer class ContractResponse{

   inner class Send

   inner class Push

   inner class Select

} 이런식으로 수도 코드를 작성해볼 수 있을 것이다.

 

2. Namespace 정리

외부 클래스에서 독립적으로 존재하기 때문에 inner class와 outer class가 충돌할 걱정이 적어지며 내부에서 이름을 자유롭게~ 마음대로 작성할 수 있다는 장점이 있다.

 

 

Stataic Class 실습

 

이론보다는 실전을 한번 보고 따라한다면 왜 Static Class를 사용하는지 직관적으로 알 수 있다.

현업에서 마주했던 문제를 바탕으로 재현해보고자 한다. 그래서 크게 두 가지 상황으로 나누어서 진행해보려고 한다.

 

1. DTO의 데이터가 너~~~~무 많을 때

@Builder
public class ContractResponse {
    private String contractId;

    private String contractType;

    private String contractTitle;

    private String contents;

    private LocalDateTime effectiveDate;

    private LocalDateTime ExpirationDate;

    private LocalDateTime updateDate;

    private String signerId;
    
    private String signerName;
    
    private String signerType;
    
}

Inner Class를 사용하기 전에 이렇게 만은 계약 response가 있다고 생각해보자,, 이 경우 열이 너무 많다보니 사용하는 곳이 많을 때는 어떤 열에서 문제가 발생했는지 찾기도 어렵다.. 그리고 프론트에서 저 항목을 그대로 response 받게 되는데 좀 어지러울 수 있다.

 

해당 내용을 Inner Class로 변경하여 코드 가독성을 올려보았고 프론트에도 이쁘게 response를 전달해보자!!

@Builder
public class ContractResponse {

    private Contract contract;
    
    private Signer signer;
    
    @Builder
    public static class Contract{
        private String id;

        private String type;

        private String title;

        private String contents;
        
        private Date date;

    }
    @Builder
    public static class Date{

        private LocalDateTime effectiveDate;

        private LocalDateTime ExpirationDate;

        private LocalDateTime updateDate;

    }

    @Builder
    public static class Signer{

        private String id;

        private String name;

        private String type;

    }

}

어떤가?! ContractResponse에 어떠한 정보들이 반환되는지 명확하지 않은가?!! 또한 프론트 개발자도 signer, contract에 대한 정보를 명확하게 구별하여 사용할 수 있을 것이다.

 

2. DTO package 내부에 response, request 등의  type이 너무 많아 정리가 힘들 때

극단적으로 이러한 예시가 있다고 생각해보자. 계약과 관련된 response만 7개나 있다. 그런데 request 나 다른 dto가 많이 생긴다면 정말 알아보기 힘들다. 그래서 이러한 문제를 Inner Class로 해결해보자.

public class ContractResponse {

    @Builder
    public static class Delete{

    }
    
    @Builder
    public static class Detail{

    }

    @Builder
    public static class Post{

    }

    @Builder
    public static class Save{

    }

    @Builder
    public static class Select{

    }

    @Builder
    public static class Send{

    }

    @Builder
    public static class Update{

    }

}

이런식으로 ContractResponse에 다 넣어주었다!!! 사용법은 CotnractResponse.Save 요런식으로 사용할 수 있다.

 

뭔가 예시들을 보고 너무 극단적이라 이런 경우가 있을까?! 라는 생각을 하겠지만,, 회사에 들어가면 많이 마주칠 수 있다 ㅎ,, 리펙토링할 엄두가 안난다.

 

 


Inner & Custom Builder

 

Builder 패턴은 블로그에 정리를 해두었는데

https://mjoo1106.tistory.com/entry/Spring-Setter-vs-Builder

 

[Spring] @Setter vs @Builder

Entity, Dto를 개발하다보면 @Setter를 써야할지 @Builder를 써야할지 고민된적이 있을 것이다. 그리고 강의나 블로그에 보면 Setter 사용을 지양하라고 되어있다. "그래 Setter 사용 안할게,, 근데 왜 사용

mjoo1106.tistory.com

여기서는 간단하게 Builder 패턴에 대해서만 알아보았고, 이번에는 Builder 응용에 대해 소개하겠다.

 

Inner Builder라고 네이밍을 지은 이유는 말 그대로 클래스 내부에서 Builder를 지정해주는 것이다.

public class Test{

    private String id;
    
    @Builder
    public Test(String id){
    	this.id = id;
    }

}

Custom Builder의 경우 builder의 이름을 커스텀하는 것인데,

public class ContractResponse {

    private String temp;
    @Builder(builderMethodName = "test")
    public ContractResponse(String temp){
        this.temp = temp;
    }

}

해당 예시는 원래라면  Test test = Test.builder().build();  이렇게 사용해야하지만 Test test = Test.test().build(); 로 custom하여 사용할 수 있다.

buildMethodName build() method 이름을 지정

이 옵션은 빌더 패턴을 사용하여 객체를 생성할 때
build() 메서드의 이름을 지정합니다. 기본값은 "build"입니다.
builderMethodName 빌더 객체를 생성하는 메서드의 이름을 지정합니다. 기본값은 "builder"입니다. 이를 원하는 이름으로 변경하려면
builderClassName 기본적으로 빌더 클래스는 원래 클래스의 이름에 "Builder"를 붙인 형태로 생성됩니다. 이 옵션을 사용하여 원하는 빌더 클래스의 이름을 지정할 수 있습니다.
  • builderMethodName : builder()  method 이름을 지정 (기본 값 : builder)
  • buildMethodName : build() method 이름을 지정 (기본 값 : build)
  • builderClassName : builder class 이름을 지정 (기본 값 : Builder)

어째든 Inner & Custom builder를 사용하는 이유는 무엇이 있을까??!!

바로 부분적으로 파라미터를 사용하는 경우에 중복 코드 없이 빌터 패턴으로 분기 처리가 가능하여 간편하다는 장점이 있다.

우리 부서는 마이바티스를 활용하고 있는데 많은 열 중 부분적으로 사용해야 하는 경우가 있다. 이럴 경우 builder패턴을 다양하게 사용한다면 부분적으로 중복 코드 없이 활용 가능하다.

 

실습

 

회사 상황을 가정해보고 실습을 진행해보자.

 

우선 많은 열을 가진 Contract 라는 class가 있다고 가정해보자.

public class Contract {

    private String id;

    private String title;

    private String contents;

    private String type;
    
}

그리고 전체 열을 사용하는 경우인 Insert와 id만 사용하는 Select가 있다고 가정하고 코드를 작성한다면

public class Contract {

    private String id;

    private String title;

    private String contents;

    private String type;

    @Builder(builderMethodName = "ofInsertContract")
    public Contract(String id, String title, String contents, String type) {
        this.id = id;
        this.title = title;
        this.contents = contents;
        this.type = type;
    }
    
    @Builder(builderMethodName = "ofSelectContract")
    public Contract(String id) {
        this.id = id;
    }

}

요런 형태가 될 것이다. 그렇다면 사용 방법은

public class TestService {

    public void test() {
        Contract selectContract = Contract.ofSelectContract()
                                          .id("test")
                                          .build();


        Contract insertContract = Contract.ofInsertContract()
                                          .id("test")
                                          .title("test")
                                          .contents("test")
                                          .type("test")
                                          .build();
    }
}

요런식으로 사용할 수 있을 것이다.

 

즉, 필자가 해당 패턴을 사용하는 이유는 선택적 파라미터 처리가 가능하기 때문이다!! 이게 가장 큰 장점이라고 생각한다.

 


결론

 

오늘은 필자의 코딩 스타일 중 현업에 유용했던 부분에 대해 소개했다~~ 다들 신규 프로젝트에 적용하여 사용해보면서 피드백을 남겨준다면 정말 정말 행복할 것 같다.