๊ฐœ๋ฐœ์ž HOON
๐Ÿ› HOON DEVLog
๊ฐœ๋ฐœ์ž HOON
์ „์ฒด ๋ฐฉ๋ฌธ์ž
์˜ค๋Š˜
์–ด์ œ
  • ๐Ÿ˜Ž ์ „์ฒด ์นดํ…Œ๊ณ ๋ฆฌ (137)
    • ๐Ÿ“ ์‹ ์ž… ์ธํ„ฐ๋ทฐ ์ค€๋น„ (7)
    • ๐Ÿฆ” ์ทจ์—…์ค€๋น„ ๊ธฐ๋ก (7)
    • โ˜• ์ž๋ฐ” : JAVA (5)
    • ๐Ÿ ์ฝ”๋”ฉํ…Œ์ŠคํŠธ ๋Œ€๋น„ : PS (80)
    • ๐ŸŒฑ ๋ฐฑ์—”๋“œ : Backend (13)
    • ๐Ÿงช ์ปดํ“จํ„ฐ๊ณผํ•™ : CS (11)
    • ๐Ÿ—‚ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค : DB (1)
    • ๐Ÿƒ‍โ™‚๏ธ DEVLOG (8)
    • โš™๏ธ Trouble Shooting (5)

๋ธ”๋กœ๊ทธ ๋ฉ”๋‰ด

  • ํ™ˆ
  • GitHub
  • Resume

๊ณต์ง€์‚ฌํ•ญ

์ธ๊ธฐ ๊ธ€

์ตœ๊ทผ ๊ธ€

ํ‹ฐ์Šคํ† ๋ฆฌ

hELLO ยท Designed By ์ •์ƒ์šฐ.
๊ฐœ๋ฐœ์ž HOON

๐Ÿ› HOON DEVLog

[DEVLOG] @Builder์™€ @Builder.Default
๐Ÿƒ‍โ™‚๏ธ DEVLOG

[DEVLOG] @Builder์™€ @Builder.Default

2023. 4. 12. 22:13

 

๐Ÿค” @Builder๊ฐ€ ๋‹ฌ๋ ค์žˆ๋Š” ํด๋ž˜์Šค์˜ ํ•„๋“œ๋ฅผ ์ดˆ๊ธฐํ™”ํ•ด์คฌ๋Š”๋ฐ, ์™œ ๋นŒ๋” ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜๋ฉด ์ดˆ๊ธฐํ™” ๊ฐ’์ด ์•„๋‹Œ Null์ด ๋‚˜์˜ค์ง€?

 

๐Ÿช„ GTAccountInfo ์—”ํ‹ฐํ‹ฐ์˜ ์ฝ”๋“œ ์ผ๋ถ€

@Slf4j
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity(name = "gt_account_info")
public class GTAccountInfo {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ACCOUNT_ID")
    private long id;

    @Getter
    @Column(name = "account_email")
    private String accountEmail;

    @Getter
    @Column(name = "account_pw")
    private String accountPW;

    @OneToMany(mappedBy = "accountInfo", fetch = FetchType.LAZY)
    private List<GTAccountUserRoleInfo> roleByThisAccountList = new ArrayList<>();

    public List<GTUserRole> getRoles(){
        return roleByThisAccountList.stream()
                .map(GTAccountUserRoleInfo::getUserRole)
                .map(GTUserRoleInfo::getUserRole)
                .collect(Collectors.toList());
    }
    
	// ...
}

 

๐Ÿช„Test ์ฝ”๋“œ์˜ ์ผ๋ถ€ ๋ฐœ์ทŒ

    @Test
    @DisplayName("๐Ÿค” 1. ๊ณ„์ •์ •๋ณด ์ƒ์„ฑ ๋ฐ ์ €์žฅ ํ…Œ์ŠคํŠธ : ์„ฑ๊ณต ์ผ€์ด์Šค")
    @Transactional
    public void testForCreateNewAccountInfoAndSave(){
        GTAccountInfo mockAccountInfo = GTAccountInfo.builder()
                .accountEmail("test@example.com")
                .accountPW("1234")
                .build();

        GTAccountInfo savedAccountInfo = accountInfoRepository.save(mockAccountInfo);

        assertEquals(savedAccountInfo.getAccountEmail(), "test@example.com");
        assertEquals(savedAccountInfo.getAccountPW(), "1234");
        assertEquals(savedAccountInfo.getRoles().size(), 0);

    }

 

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์—์„œ ์œ„์™€ ๊ฐ™์ด, Builder ํŒจํ„ด์„ ์‚ฌ์šฉํ•ด์„œ ์ด๋ฉ”์ผ๊ณผ PW๋ฅผ ์ฑ„์›Œ ๋„ฃ์–ด ์ƒˆ๋กœ์šด ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด๋ƒˆ์Šต๋‹ˆ๋‹ค.

Entity ํด๋ž˜์Šค์—์„œ, roleByThisAccountList์˜ ๊ฒฝ์šฐ, new ArrayList<>()๋กœ ํ• ๋‹น๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— null์ผ ๊ฒฝ์šฐ๋Š” ์—†๋‹ค๋ผ๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค.

 

๊ทธ๋Ÿฌ๋‚˜, ํ…Œ์ŠคํŠธ์˜ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•ด๋ณด๋‹ˆ,

warning : @Builder will ignore the initializing expression entirely. If you want the initializing expression to serve as default, add @Builder.Default. If it is not supposed to be settable during building, make the field final. private List<GTAccountUserRoleInfo> roleByThisAccountList = new ArrayList<>();
java.lang.NullPointerException at com.example.gtmvcserverside.member.domain.GTAccountInfo.getRoles(GTAccountInfo.java:41) ...

์™€ ๊ฐ™์ด roleByThisAccountList๊ฐ€ null์ธ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. 

 

๊ฒฝ๊ณ  ํ‘œํ˜„์„ ์ฝ์–ด๋ณด๋ฉด, @Builder๋Š” Initializing expression์„ ์ „์ฒด ๋ฌด์‹œํ•˜๊ณ , default๋กœ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, @Builder.Default๋ฅผ ์‚ฌ์šฉํ•ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. 

 

class Example<T> {
   	private T foo;
   	private final String bar;
   	
   	private Example(T foo, String bar) {
   		this.foo = foo;
   		this.bar = bar;
   	}
   	
   	public static <T> ExampleBuilder<T> builder() {
   		return new ExampleBuilder<T>();
   	}
   	
   	public static class ExampleBuilder<T> {
   		private T foo;
   		private String bar;
   		
   		private ExampleBuilder() {}
   		
   		public ExampleBuilder foo(T foo) {
   			this.foo = foo;
   			return this;
   		}
   		
   		public ExampleBuilder bar(String bar) {
   			this.bar = bar;
   			return this;
   		}
   		
   		@java.lang.Override public String toString() {
   			return "ExampleBuilder(foo = " + foo + ", bar = " + bar + ")";
   		}
   		
   		public Example build() {
   			return new Example(foo, bar);
   		}
   	}
   }

์œ„ ์ฝ”๋“œ๋Š”, @Builder๋ฅผ ํ†ตํ•ด ๋‚ด๋ถ€์ ์œผ๋กœ ๋งŒ๋“ค์–ด์ง„ ์ฝ”๋“œ์— ๋Œ€ํ•œ ๋‚ด์šฉ์„ ์ฃผ์„์—์„œ ๊ฐ€์ ธ์˜จ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

builder ํŒจํ„ด์„ ํ™œ์šฉํ•ด์„œ foo์™€ bar๋ฅผ ์„ธํŒ…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์ด inner class์ธ ExampleBuilder<T>๋Š”, ๋‚ด๋ถ€์—์„œ ์ดˆ๊ธฐํ™” ํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ์–ด๋””์—๋„ ์—†์Šต๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ @Builder๊ฐ€ ๋‹ฌ๋ ค์žˆ๋Š” ํด๋ž˜์Šค์— ๋Œ€ํ•ด ๋นŒ๋” ํŒจํ„ด์œผ๋กœ ์ƒˆ๋กœ์šด ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋ ค๊ณ  ํ•˜๋ฉด, ํ•„๋“œ๊ฐ€ initialized ๋˜์–ด์žˆ๋”๋ผ๋„, ๋นŒ๋”ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜๋ฉด์„œ ๊ฐ’์„ ์‚ฝ์ž…ํ•˜์ง€ ์•Š์œผ๋ฉด null์ด ๋“ค์–ด๊ฐˆ ์ˆ˜ ๋ฐ–์— ์—†์Šต๋‹ˆ๋‹ค.

 

๋”ฐ๋ผ์„œ ์ดˆ๊ธฐํ™”๋œ ์ƒํƒœ๋ฅผ @Builder๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด์„œ ์œ ์ง€ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, ํ•„๋“œ์— @Builder.Default๋ฅผ ์ ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

 

@Slf4j
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity(name = "gt_account_info")
public class GTAccountInfo {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ACCOUNT_ID")
    private long id;

    @Getter
    @Column(name = "account_email")
    private String accountEmail;

    @Getter
    @Column(name = "account_pw")
    private String accountPW;

	@Builder.Default
    @OneToMany(mappedBy = "accountInfo", fetch = FetchType.LAZY)
    private List<GTAccountUserRoleInfo> roleByThisAccountList = new ArrayList<>();

    public List<GTUserRole> getRoles(){
        return roleByThisAccountList.stream()
                .map(GTAccountUserRoleInfo::getUserRole)
                .map(GTUserRoleInfo::getUserRole)
                .collect(Collectors.toList());
    }
    
	// ...
}

 

์ด์ œ๋Š”, ์›ํ•˜๋Š”๋Œ€๋กœ ์ดˆ๊ธฐํ™”๊ฐ€ ๋˜๊ณ  ์ •์ƒ์ ์œผ๋กœ ํ…Œ์ŠคํŠธ๊ฐ€ ํ†ต๊ณผํ•˜๋Š” ๋ชจ์Šต์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

 

 

 

๐Ÿค” @Builder์˜ ์ถ”๊ฐ€ ์ง€์‹

 

์ถ”๊ฐ€์ ์œผ๋กœ @Builder์˜ ์ฃผ์„์„ ํ•œ ๋ฒˆ ์‚ดํŽด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

If a member is annotated, it must be either a constructor or a method. If a class is annotated, then a package-private constructor is generated with all fields as arguments (as if @AllArgsConstructor(access = AccessLevel.PACKAGE) is present on the class), and it is as if this constructor has been annotated with @Builder instead. Note that this constructor is only generated if you haven't written any constructors and also haven't added any explicit @XArgsConstructor annotations. In those cases, lombok will assume an all-args constructor is present and generate code that uses it; this means you'd get a compiler error if this constructor is not present.

 

@Builder ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฝ์ž…ํ•˜๋ฉด, ๋ชจ๋“  ํ•„๋“œ๋ฅผ ์ธ์ž๋กœ ์š”๊ตฌํ•˜๋Š” package-privateํ•œ ์ƒ์„ฑ์ž๋ฅผ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•˜๊ณ , ๋‚ด๋ถ€์ ์œผ๋กœ ์ด๋Ÿฌํ•œ ์ƒ์„ฑ์ž๊ฐ€ ์žˆ๋‹ค๋Š” ๊ฐ€์ •ํ•˜์— ์ฝ”๋“œ๋ฅผ generate ํ•ฉ๋‹ˆ๋‹ค.

 

์ด๋Ÿฌํ•œ ๋ชจ๋“  ํ•„๋“œ๋ฅผ ์ธ์ž๋กœ ์š”๊ตฌํ•˜๋Š” package-privateํ•œ ์ƒ์„ฑ์ž๋Š” ์–ด๋– ํ•œ ์ƒ์„ฑ์ž๋ฅผ ์ž‘์„ฑํ•˜์ง€ ์•Š์•„์•ผํ•˜๊ณ , ๋‹ค๋ฅธ @XArgsConstructor(No, Required..) ์—ญ์‹œ ๋ช…์‹œํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ์— ์ƒ์„ฑ๋œ๋‹ค๊ณ  ๋‚˜์™€์žˆ์Šต๋‹ˆ๋‹ค.

 

์ฆ‰, JPA๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด์„œ @NoArgsConstructor๋ฅผ ๋ฐ˜๋“œ์‹œ ํฌํ•จํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ๋ชจ๋“  ํ•„๋“œ๋ฅผ ์ธ์ž๋กœ ์š”๊ตฌํ•˜๋Š” package-privateํ•œ ์ƒ์„ฑ์ž๊ฐ€ ์ƒ์„ฑ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

 

๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— Entity ํด๋ž˜์Šค์—์„œ @Builder๋ฅผ ์‚ฌ์šฉํ•˜๊ณ ์ž ํ•œ๋‹ค๋ฉด,

@NoArgsConstructor๊ณผ @AllArgsConstructor๋ฅผ ๊ฐ™์ด ์‚ฌ์šฉํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

 

์ €์ž‘์žํ‘œ์‹œ ๋น„์˜๋ฆฌ ๋™์ผ์กฐ๊ฑด (์ƒˆ์ฐฝ์—ด๋ฆผ)

'๐Ÿƒโ€โ™‚๏ธ DEVLOG' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[DEVLOG] JUnit5๋ฅผ ํ™œ์šฉํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ๊ณผ @DataJpaTest ์‚ฌ์šฉ์‹œ ๋ฐœ์ƒํ•œ ๋ฌธ์ œ ํ•ด๊ฒฐ  (1) 2023.04.12
[DEVLOG] if(kakao) dev 2022 ๊ฐœ๋ฐœ์ž ์ปจํผ๋Ÿฐ์Šค ํ›„๊ธฐ - 1. ISFP์˜ ์ฝ”๋“œ ๊ฐ€๋…์„ฑ ๊ฐœ์„  ๊ฒฝํ—˜  (1) 2023.01.01
[DEVLOG] ์šฐ๋‹นํƒ•ํƒ• ์ œ๋ฐœ๋ชจ๋ฐœ v2 ๊ฐœ๋ฐœ๊ธฐ - ํƒˆ๋ชจ ์ง„๋‹จ API ์ˆ˜์ •ํ•˜๊ธฐ (4) createdAt๊ณผ updatedAt, JPA AttributeConverter  (1) 2022.12.27
[DEVLOG] ์šฐ๋‹นํƒ•ํƒ• ์ œ๋ฐœ๋ชจ๋ฐœ v2 ๊ฐœ๋ฐœ๊ธฐ - ํƒˆ๋ชจ ์ง„๋‹จ API ์ˆ˜์ •ํ•˜๊ธฐ (3) DTO์— ๋”ฐ๋ฅธ ์‘๋‹ต ์ƒ์„ฑ ํด๋ž˜์Šค ๋งŒ๋“ค๊ธฐ  (0) 2022.12.16
[DEVLOG] ์šฐ๋‹นํƒ•ํƒ• ์ œ๋ฐœ๋ชจ๋ฐœ v2 ๊ฐœ๋ฐœ๊ธฐ - ํƒˆ๋ชจ ์ง„๋‹จ API ์ˆ˜์ •ํ•˜๊ธฐ (2) ๊ณตํ†ตํ•„๋“œ๊ฐ€ ๋งŽ์€ DTO ๋งŒ๋“ค๊ธฐ  (0) 2022.12.16
    '๐Ÿƒ‍โ™‚๏ธ DEVLOG' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€
    • [DEVLOG] JUnit5๋ฅผ ํ™œ์šฉํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ๊ณผ @DataJpaTest ์‚ฌ์šฉ์‹œ ๋ฐœ์ƒํ•œ ๋ฌธ์ œ ํ•ด๊ฒฐ
    • [DEVLOG] if(kakao) dev 2022 ๊ฐœ๋ฐœ์ž ์ปจํผ๋Ÿฐ์Šค ํ›„๊ธฐ - 1. ISFP์˜ ์ฝ”๋“œ ๊ฐ€๋…์„ฑ ๊ฐœ์„  ๊ฒฝํ—˜
    • [DEVLOG] ์šฐ๋‹นํƒ•ํƒ• ์ œ๋ฐœ๋ชจ๋ฐœ v2 ๊ฐœ๋ฐœ๊ธฐ - ํƒˆ๋ชจ ์ง„๋‹จ API ์ˆ˜์ •ํ•˜๊ธฐ (4) createdAt๊ณผ updatedAt, JPA AttributeConverter
    • [DEVLOG] ์šฐ๋‹นํƒ•ํƒ• ์ œ๋ฐœ๋ชจ๋ฐœ v2 ๊ฐœ๋ฐœ๊ธฐ - ํƒˆ๋ชจ ์ง„๋‹จ API ์ˆ˜์ •ํ•˜๊ธฐ (3) DTO์— ๋”ฐ๋ฅธ ์‘๋‹ต ์ƒ์„ฑ ํด๋ž˜์Šค ๋งŒ๋“ค๊ธฐ
    ๊ฐœ๋ฐœ์ž HOON
    ๊ฐœ๋ฐœ์ž HOON
    ์ข‹์€ ๋ฐฑ์—”๋“œ ์—”์ง€๋‹ˆ์–ด๊ฐ€ ๋˜๊ธฐ ์œ„ํ•œ ๊ธฐ๋ก์„ ๋ชจ์•˜์Šต๋‹ˆ๋‹ค. # ์ฃผ๋‹ˆ์–ด # ๋ฐฑ์—”๋“œ # ๊ฐœ๋ฐœ์ž

    ํ‹ฐ์Šคํ† ๋ฆฌํˆด๋ฐ”