반응형
해당 글에서는 생성자 패턴 중 Builder() 패턴을 기반으로 상세 속성을 이용하여 심화 내용의 이해를 돕기 위한 목적으로 작성한 글입니다.
💡 해당 글은 이전에 작성한 Builder() 생성자 패턴에 대한 글에 이어지는 내용입니다.
1. @Builder(toBuilder = boolean) : default false
💡 Builder의 속성중 ‘toBuilder’는 값을 true로 지정하는 경우 기존에 구성한 빌더를 기반으로 새로운 객체를 재 구성 할 수 있도록 도와주는 속성입니다.
💡 해당 속성의 사용처는 기존의 Builder로 구성한 객체에 값을 재 구성하고자 할 때 사용하면 좋을 것으로 판단됩니다.
package com.adjh.multiflexapi.model;
import com.adjh.multiflexapi.model.common.Pagination;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class UserDto extends Pagination {
// 사용자 시퀀스
private int userSq;
// 사용자 아이디
private String userId;
// 사용자 패스워드
private String userPw;
// 사용자 이름
private String userNm;
// 사용자 상태
private String userSt = "S";
@Builder(toBuilder = true)
private UserDto(int userSq, String userId, String userPw, String userNm, String userSt) {
this.userSq = userSq;
this.userId = userId;
this.userPw = userPw;
this.userNm = userNm;
this.userSt = userSt;
}
}
/**
* 객체1을 구성합니다.
*/
UserDto userDto1 = UserDto
.userBuilder()
.userSq(1)
.userNm("Lee")
.userId("adjh54")
.build();
log.debug(userDto1.toString()); // UserDto(userSq=1, userId=adjh54, userPw=null, userNm=Lee, userSt=null)
/**
* 구성한 객체1을 기반으로 객체2를 재구성합니다.
* 객체1의 값은 유지하되 변경하고자 하는 값은 객체2로 변경합니다.
*/
UserDto userDto2 = userDto1
.toBuilder()
.userNm("Jong")
.build();
log.debug(userDto2.toString()); // UserDto(userSq=1, userId=adjh54, userPw=null, userNm=Jong, userSt=null)
1. Collection Type을 toBuilder로 추가방법
💡 Collection Type을 toBuilder로 추가방법
- 사전에 구성된 Dto 객체에 tobuilder 메서드를 통해서 재구성합니다.
- toBuilder 내에서도 Collection Type(List, Map, ...) 타입에 대해서 추가하는 방법에 대해 확인합니다,
💡 toBuilder로 재구성할 Dto입니다.
- 해당 부분에 authUserList라는 List 형태의 변수가 있습니다. 해당 부분을 리스트 형태로 수정할 예정입니다.
package com.adjh.multiflexapi.model;
import com.adjh.multiflexapi.model.common.Pagination;
import lombok.*;
import java.util.List;
@Getter
@ToString
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class UserDto extends Pagination {
// 사용자 시퀀스
private int userSq;
private long oauthId;
// 사용자 아이디
private String userId;
// 사용자 패스워드
private String userPw;
// 사용자 이름
private String userNm;
// 사용자 상태
private String userSt = "S";
private String userEmail;
private List<AuthUserDto> authUserList;
@Builder(toBuilder = true)
public UserDto(int userSq, long oauthId, String userId, String userPw, String userNm, String userSt, String userEmail, List<AuthUserDto> authUserList) {
this.userSq = userSq;
this.oauthId = oauthId;
this.userId = userId;
this.userPw = userPw;
this.userNm = userNm;
this.userSt = userSt;
this.userEmail = userEmail;
this.authUserList = authUserList;
}
}
💡 재 구성하려는 DTO입니다.
/**
* Auth User 정보를 관리하는 DTO입니다.
*
* @author : jonghoon
* @fileName : AutoUserDto
* @since : 2023/08/05
*/
@Getter
@ToString
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class AuthUserDto {
private int oauthId;
private int userSq;
private String oauthProvider;
private String userEmail;
private String userNickname;
@Builder(toBuilder = true)
public AuthUserDto(int oauthId, int userSq, String oauthProvider, String userEmail, String userNickname) {
this.oauthId = oauthId;
this.userSq = userSq;
this.oauthProvider = oauthProvider;
this.userEmail = userEmail;
this.userNickname = userNickname;
}
}
💡 테스트 환경의 비즈니스 로직 부분
- 해당 부분에서 파라미터로 받은 userDto 값을 1차로 출력하고 authUserDtoList를 재구성하여서 userDto 내에 tobuilder로 값을 넣어서 Collection Type만 추가하였습니다.
@Override
@Transactional(readOnly = true)
public List<UserDto> selectUserList(UserDto userDto) {
log.debug("userDto 1111111 : " + userDto.toString());
// Builder에 넣을 리스트 선언
List<AuthUserDto> authUserDtoList = new ArrayList<>();
// List 값을 구성
AuthUserDto authUserDto = AuthUserDto
.builder()
.oauthId(1234)
.userSq(userDto.getUserSq())
.oauthProvider("kakao")
.userEmail("adjh54@naver.com")
.userNickname("adjh54")
.build();
// List 내에 요소 추가
authUserDtoList.add(authUserDto);
// toBuilder를 이용하여 List 값을 구성하여 넣습니다.
userDto = UserDto
.builder()
.authUserList(authUserDtoList)
.build();
log.debug("userDto 2222222 : " + userDto.toString());
UserMapper um = sqlSession.getMapper(UserMapper.class);
return um.selectUserList(userDto);
}
💡 결과확인
- Dto 내에 List 값이 추가되었습니다.
2. @Builder(builderMethodName = String)
💡 Builder의 속성중 ‘builderMethodName’은 기본 메서드 명으로 Builder()로 사용이 되는데 이를 다른 이름으로 네이밍을 할 수 있도록 제공하는 속성입니다.
💡 해당 속성의 사용처는 상속 관계에 있는 경우 부모의 Builder와 자식의 Builder 간의 이름이 동일하여 충돌이 발생할 수 있으므로 이에 대해 각각 이름을 지정해주려 할 때 사용하면 좋을 것으로 판단됩니다.
💡 Builder()를 사용하는 도중에 error: builder() in UserDto cannot hide builder() in Pagination 에러를 마주쳤습니다.
💡 이는 부모의 Builder() 메서드와 자식의 Builder() 메서드가 동일한 이름으로 구성이 되어서 발생한 에러였습니다. 이를 위해서 해당 속성을 적용합니다.
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class UserDto extends Pagination {
// 사용자 시퀀스
private int userSq;
// 사용자 아이디
private String userId;
// 사용자 패스워드
private String userPw;
// 사용자 이름
private String userNm;
// 사용자 상태
private String userSt;
@Builder(builderMethodName = "userBuilder")
UserDto(int userSq, String userId, String userPw, String userNm, String userSt) {
this.userSq = userSq;
this.userId = userId;
this.userPw = userPw;
this.userNm = userNm;
this.userSt = userSt;
}
}
/**
* [공통] 페이지 관리
*
* @author : lee
* @fileName : Pagination
* @since : 2022/12/28
*/
@Getter
@ToString
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Pagination {
private int page;
private long perPage;
private String sortColumn;
private Boolean sortAscending;
@Builder(builderMethodName = "paginationBuilder")
Pagination(int page, long perPage, String sortColumn, Boolean sortAscending) {
this.page = page;
this.perPage = perPage;
this.sortColumn = sortColumn;
this.sortAscending = sortAscending;
}
}
3. @Singular(String)
💡 Builder와 관련된 @Singular라는 어노테이션은 @Builder() 내에서 컬렉션 객체(List, Set, Map)에 대해서 값을 전체로 넘기지 않고 하나하나씩 넘길 수 있도록 도와줍니다.
💡해당 속성의 사용처는 콜렉션 객체 내의 조작이 있을 경우 변형을 하여서 Builder로 구성을 하는 경우에 도움이 될 것 같습니다.
💡 DTO 내에서 @Singular("userMapItem")와 같은 형태로 컬렉션 객체의 단수로 Alias를 주어서 각각의 빌더를 구성하였습니다.
package com.adjh.multiflexapi.model;
import lombok.*;
import java.util.List;
import java.util.Map;
@Getter
@ToString
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class UserDto {
// 사용자 시퀀스
private int userSq;
// 사용자 아이디
private String userId;
// 사용자 패스워드
private String userPw;
// 사용자 이름
private String userNm;
// 사용자 상태
private String userSt;
// TEST - Hashmap
private Map<String, Object> userMap;
// TEST - ArrayList
private List<UserDto> userList;
@Builder(builderMethodName = "userBuilder", toBuilder = true)
private UserDto(int userSq, String userId, String userPw, String userNm, String userSt,
@Singular("userMapItem") Map<String, Object> userMap,
@Singular("userListItem") List<UserDto> userList
) {
this.userSq = userSq;
this.userId = userId;
this.userPw = userPw;
this.userNm = userNm;
this.userSt = userSt;
this.userMap = userMap;
this.userList = userList;
}
}
💡 구성한 Builder를 통해서 최종적인 호출을 수행합니다.
/**
* 객체1을 구성합니다.
*/
UserDto userDto1 = UserDto
.userBuilder()
.userSq(1)
.userNm("Lee")
.userId("adjh54")
.build();
/**
* 구성한 객체1을 기반으로 객체2를 재구성합니다.
* 객체1의 값은 유지하되 변경하고자 하는 값은 객체2로 변경합니다.
*/
UserDto userDto2 = userDto1
.toBuilder()
.userNm("Jong")
.build();
UserDto userDto3 = UserDto
.userBuilder()
.userSq(1)
.userNm("Lee")
.userId("adjh54")
.userMapItem("userItem1", "Item1")
.userMapItem("userItem2", "Item2")
.userMapItem("userItem3", "Item3")
.userMapItem("userItem4", "Item4")
.userListItem(userDto1)
.userListItem(userDto2)
.userListItem(userDtoThree)
.build();
💡 Collection 객체에 대해서 각각 지정한 값이 출력됨을 확인하였습니다.
UserDto(userSq = 1, userId = adjh54, userPw = null, userNm = Lee, userSt = null, userMap = {
userItem1 = Item1,
userItem2 = Item2,
userItem3 = Item3,
userItem4 = Item4
}, userList = [
UserDto(userSq = 1, userId = adjh54, userPw = null, userNm = Lee, userSt = null, userMap = {}, userList = []),
UserDto(userSq = 1, userId = adjh54, userPw = null, userNm = Jong, userSt = null, userMap = {}, userList = []),
UserDto(userSq = 0, userId = null, userPw = null, userNm = null, userSt = null, userMap = {}, userList = [])
]);
[ 더 알아보기 ]
💡 Java 9 이상 버전의 경우 각각을 구성할 필요없이 Map.of(), Array.of() 함수를 통해서 한 번에 구성하여 콜렉션 객체를 구성하면 좀 더 간단한 구조로 생성이 가능합니다.
[참고] Lombok 공식사이트
4. @Builder.Default
💡 Builder의 속성 중 ‘Default’는 변수의 앞에 선언이 되며 빌더가 구성이 될 때 초기값을 지정해 줄 수 있도록 도와주는 속성입니다.
💡 아래와 같이 구성을 하였을 경우 ‘userSt’ 컬럼에 “S”라고 초기화를 했습니다. 초기화를 하더라도 Builder로 호출 시 값이 적용되지 않는 점이 있습니다.
@Getter
@ToString
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class UserDto {
// 사용자 시퀀스
private int userSq;
// 사용자 아이디
private String userId;
// 사용자 패스워드
private String userPw;
// 사용자 이름
private String userNm;
// 사용자 상태
private String userSt = "S";
// TEST - Hashmap
private Map<String, Object> userMap;
// TEST - ArrayList
private List<UserDto> userList;
@Builder(builderMethodName = "userBuilder", toBuilder = true)
private UserDto(int userSq, String userId, String userPw, String userNm, String userSt,
@Singular("userMapItem") Map<String, Object> userMap,
@Singular("userListItem") List<UserDto> userList
) {
this.userSq = userSq;
this.userId = userId;
this.userPw = userPw;
this.userNm = userNm;
this.userSt = userSt;
this.userMap = userMap;
this.userList = userList;
}
}
💡 [결과값] useSt를 “S” 값으로 초기화를 하였음에도 userSt 값은 null로 출력이 되었습니다.
UserDto(userSq=0, userId=null, userPw=null, userNm=null, userSt=null, userMap={}, userList=[])
💡 그래서 Builder의 초기값을 지정하기 위해서는 Builder.default를 사용해야 합니다.
단, 해당 값은 @NoArgsConstructor, @RequiredArgsConstructor 인 경우에 에러를 발생합니다. 그러므로@AllArgsConstructor 인 상태일 때만 이를 사용합니다.
💡 해당 속성의 사용처는 Builder로 객체를 구성할 때 초기 값을 지정하는 경우에 도움이 될 것 같습니다.
@Getter
@ToString
@AllArgsConstructor
public class UserDto {
// 사용자 시퀀스
private int userSq;
// 사용자 아이디
private String userId;
// 사용자 패스워드
private String userPw;
// 사용자 이름
private String userNm;
// 사용자 상태
@Builder.Default
private String userSt = "S";
// TEST - Hashmap
private Map<String, Object> userMap;
// TEST - ArrayList
private List<UserDto> userList;
}
💡 [참고] 해당 글은 다음글에서 이어집니다.
오늘도 감사합니다. 😀
반응형
'Java > 아키텍처 & 디자인 패턴' 카테고리의 다른 글
[Java] RESTful API 설계 방법 -2 : 구성하기 (0) | 2023.03.26 |
---|---|
[Java] RESTful API 설계 방법 -1 : 이해하기 (0) | 2023.03.22 |
[Java] 계층화된 아키텍처(Layered Architecture) : N Tier Architecture (0) | 2023.01.25 |
[Java] 프로그래밍 패러다임 이해하기 (0) | 2023.01.07 |
[Java] 생성자 패턴 이해하기 : 점층적 생성자, 자바 빈즈, Builder 패턴) (0) | 2022.11.12 |