외부 라이브러리 API를 사용하며 특정 객체를 생성하는 코드가 반복적으로 나타나기 시작했다.
// 특정 필드를 만들 때마다...
InputField field = new InputField();
field.setAnchorText("user_input :");
field.setOffsetX("10");
field.setOffsetY("-5");
field.setLabel("user_input");
field.setMaxLength("10");
field.setValue("테스트 값");
field.setWidth("200");
field.setRequired("true");
field.setReadOnly("false");
(-) 코드가 길어져 가독성이 떨어짐
(-) 같은 설정을 여러 곳에서 반복해야 함
(-) width, offset과 같은 값을 개발자가 실수로 빠뜨리기 쉬움
기존에는 간단한 헬퍼 메서드로 이를 관리하고 있었다.
public SignatureField makeSignatureField(String anchorText, String documentId, String userId) {
SignatureField field = new SignatureField();
field.setDocumentId(documentId);
field.setUserId(userId);
field.setAnchorText(anchorText);
field.setScale("80");
field.setOffsetX("120");
field.setOffsetY("20");
return field;
}
하지만 위의 방법에도 한계가 존재한다.
(-) 필드 종류마다 메서드를 따로 만들어야 함
(-) 특별한 설정이 필요하면 메서드를 추가로 만들거나 파라미터를 늘려야 함
“더 유연하고 확장 가능한 방법이 없을까?” 고민하다가 Builder 패턴을 적용해보기로 했다.
Builder 패턴이란?
Builder 패턴은 복잡한 객체를 단계별로 생성할 수 있게 해주는 디자인 패턴이다.
- 메서드 체이닝 : 메서드를 연속으로 호출해 값을 설정
- 선택적 파라미터 : 필요한 값만 설정 가능
- 가독성 : 어떤 값을 설정하는지 명확히 드러남
간단 예시
// 일반적인 방식
Person person = new Person();
person.setName("홍길동");
person.setAge(30);
person.setEmail("hong@example.com");
// Builder 패턴
Person person = new PersonBuilder()
.name("홍길동")
.age(30)
.email("hong@example.com")
.build();
이를 기존 입력 필드를 만드는 부분에 적용해보자.
1. FieildBuilder 클래스 만들기
public class FieldBuilder {
// 필드 속성
private String text;
private String id;
...
public FieldBuilder() {}
// 메서드 체이닝을 위해 this 반환
public FieldBuilder text(String text) {
this.text = text;
return this;
}
public FieldBuilder id(String id) {
this.id = id;
return this;
}
...
// 사인 필드
public SignatureField buildSignature() {
this.fieldType = "Signature";
SignatureField field = new SignatureField();
field.text(text);
field.id(id);
...
return field;
}
// 텍스트 필드
public TextField buildTextField() {
this.fieldType = "Text";
TextField field = new TextField();
field.text(text);
field.id(id);
...
return field;
}
public String getFieldType() {
return fieldType;
}
}
2. FormField Factory 클래스 추가
매번 offset이나 scale 같은 값들을 다시 입력해야 하나?
특정 필드는 항상 같은 위치와 크기를 쓰는데, 매번 설정하는 것이 번거로웠다.
그래서 자주 쓰는 패턴을 미리 정의해둔 Factory 클래스를 추가했다.
public class FormField {
/**
* 사용자 서명 필드
*/
public static SignatureField USER_SIGNATURE(String text, String id, ..) {
return new FieldBuilder()
.text(text)
.id(id)
...
.buildSignature();
}
/**
* 사용자 입력 텍스트 필드
*/
public static TextField USER_INPUT(String text, String id, ..) {
return new FieldBuilder()
..text(text)
.id(id)
...
.buildTextField();
}
/**
* Builder 직접 사용 (커스텀 설정 필요할 때)
*/
public static FieldBuilder builder() {
return new FieldBuilder();
}
}
→ 자주 쓰는 패턴은 메서드 이름만으로 의도가 명확해진다. ex) USER_SIGNATURE, USER_INPUT 등
→ 공통 설정(offset, scale 등)을 한 곳에서 관리할 수 있다.
→ 특별한 경우는 builder()로 직접 Builder 사용도 가능하다.
실제 호출 부분
AS-IS
// 서명 필드
public SignatureField makeSignatureField(String text, String id, ..) {
SignatureField field = new SignatureField();
field.text(text);
field.id(id);
...
return field;
}
// 사용
SignatureField signature = makeSignatureField("user_signature_here", id, ..));
TO-BE
SignatureField adminSign = FormField.USER_SIGNATURE(
"user_signature_here",
id,
...
);
빌더 패턴 적용 후 개선된 점
- 코드 라인 수 대폭 감소
- 가독성 향상
- 메서드 체이닝으로 어떤 값을 설정하는지 명확
- 새로운 필드 타입 추가 시, Builder에 build 메서드만 추가하면 됨
but, Builder 패턴이 만능은 아니다.
자주 쓰는 패턴일 경우 Builder가 효과적이나 한 두번만 쓰는 경우에는 오히려 복잡해질 수도 있다.
트레이드오프를 이해하고 상황에 맞게 선택하는 것이 중요하다!
'개발' 카테고리의 다른 글
| @Transactional을 붙이면 어떤 일이 일어나는가? (0) | 2025.12.02 |
|---|---|
| ModulePropertiesUtils로 모듈 별 properties 파일 관리하기 (0) | 2025.11.06 |
| Spring AI에 대해 (0) | 2025.10.10 |
| Kubernetes로 스프링 프로젝트 배포하기 (0) | 2025.10.03 |
| SSG를 사용한 나만의 블로그 만들기 (0) | 2025.09.08 |