DTO ์™€ VO ๋ž€?


Data Transfer Object ์™€ Value Object ๋Š” ๊ฐ ๊ณ„์ธต ๊ฐ„ ๋ฐ์ดํ„ฐ ๊ตํ™˜์„ ์œ„ํ•œ ์ž๋ฐ” ๊ฐ์ฒด๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ ๋ ˆ์ด์–ด ๊ฐ„์— ์ „๋‹ฌํ•˜๋Š” ๋ชฉ์ ์„ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฉฐ, ๊ฐ์ฒด์˜ ์†์„ฑ๊ณผ getter, setter ๋งŒ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.

๋‘ ๊ฐ€์ง€ ๋ชจ๋‘ ๋น„์Šทํ•œ ์˜๋ฏธ๋ฅผ ๊ฐ€์กŒ์ง€๋งŒ ์ฐจ์ด์ ์ด ์žˆ๋‹ค.

VO ๋ž€?


VO ๋ž€ ๋„๋ฉ”์ธ์—์„œ ํ•œ ๊ฐœ ๋˜๋Š” ๊ทธ ์ด์ƒ์˜ ์†์„ฑ๋“ค์„ ๋ฌถ์–ด์„œ ํŠน์ • ๊ฐ’์„ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฐ์ฒด๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

์ด๋กœ ์ธํ•ด ๊ฐ์ฒด๋ฅผ ๊ฐ’์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ณ , ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด ์„ค์ •๋œ ์ƒํƒœ ๊ฐ’์€ ์ ˆ๋Œ€ ๋ณ€ํ•˜์ง€ ์•Š๋Š”๋‹ค.

ํ•œ ์˜ˆ๋กœ ๋กœ๋˜ ๋ฒˆํ˜ธ๋ฅผ ์ƒ๊ฐํ•ด๋ณด์ž.

๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” ์ถฉ๋ถ„ํžˆ int ํƒ€์ž…์œผ๋กœ ์‚ฌ์šฉํ•ด๋„ ๋˜์ง€ ์•Š์„๊นŒ?

์‚ฌ์šฉํ•  ์ˆœ ์žˆ์ง€๋งŒ ๋ถ€์ ์ ˆ ํ•˜๋‹ค๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

๋„๋ฉ”์ธ ๊ฐ์ฒด๋ฅผ ๋‚˜ํƒ€๋‚ด๊ธฐ ์œ„ํ•ด ์›์‹œ ๊ฐ’์„ ์‚ฌ์šฉํ•˜๋Š” ๋‚˜์œ ๊ด€์Šต์€ primitive obsession ์ด๋ผ ๋ถˆ๋ฆด ์ •๋„๋‹ค.

๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” 1 ์—์„œ 45 ์˜ ์ˆซ์ž๋กœ ๋ฒ”์œ„๊ฐ€ ์ •ํ•ด์ ธ ์žˆ์œผ๋ฉฐ ์„œ๋กœ์— ๋Œ€ํ•œ ์—ฐ์‚ฐ์ด ๊ฐ€๋Šฅํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— LottoNumber ๋ผ๋Š” VO ๋ฅผ ํ†ตํ•ด ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ํšจ๊ณผ์ ์ด๋‹ค.

VO ์˜ ํŠน์„ฑ


1. Immutable(๋ถˆ๋ณ€์„ฑ) - setter ๊ฐ€ ์—†๋Š” ๋ถˆ๋ณ€ ๊ฐ์ฒด์—ฌ์•ผ ํ•œ๋‹ค.

์†์„ฑ ๊ฐ’ ์ž์ฒด๊ฐ€ ์‹๋ณ„ ๊ฐ’์ธ VO ๋Š” ๊ฐ’์ด ๋ฐ”๋€Œ๋ฉด ๋‹ค๋ฅธ ๊ฐ’์ด ๋˜์–ด ์ถ”์ ์ด ๋ถˆ๊ฐ€ํ•˜๊ณ , ๋ณต์‚ฌ๋  ๋•Œ๋Š” ์˜๋„์น˜ ์•Š์€ ๊ฐ์ฒด๋“ค์ด ํ•จ๊ป˜ ๋ณ€๊ฒฝ๋˜๋Š” ๋ฌธ์ œ๋ฅผ ์œ ๋ฐœํ•œ๋‹ค.

๋”ฐ๋ผ์„œ VO ๋Š” ๋ฐ˜๋“œ์‹œ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋Š” ๋ถˆ๋ณ€ ๊ฐ์ฒด๋กœ ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค.

public class LottoNumber {
 
    private int value;
 
    public LottoNumber(int value) {
        this.value = value;
    }
}
 
public static void main(String[] args) {
 
    LottoNumber lottoNumber1 = new LottoNumber(1);
 
    LottoNumber lottoNumber2 = new LottoNumber(1);
 
    lottoNumber2 = new LottoNumber(2);
}

๋”ฐ๋ผ์„œ ๊ฐ์ฒด ์ƒ์„ฑ ์ดํ›„ ์†์„ฑ ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ์—ฌ์ง€๊ฐ€ ์žˆ๋Š” setter ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ ๋ณด๋‹ค ์ƒ์„ฑ๊ณผ ๋™์‹œ์— ์†์„ฑ ๊ฐ’์„ ํ• ๋‹นํ•˜๋Š” ์ƒ์„ฑ์ž๋ฅผ ํ™œ์šฉํ•˜๋Š” ๊ฒƒ์ด VO ์˜ ์ •์ฒด์„ฑ์„ ์ง€ํ‚ค๋ฉด์„œ ์˜๋„์น˜ ์•Š์€ ๋ณ€๊ฒฝ์„ ๋ง‰์„ ์ˆ˜ ์žˆ๊ณ  ์œ ์ง€๋ณด์ˆ˜์—๋„ ํšจ๊ณผ์ ์ด๋‹ค.

์ด๋กœ์จ VO ๋Š” GC ์— ์˜ํ•ด ํ๊ธฐ ๋  ๋•Œ๊นŒ์ง€ ๋™์ผํ•จ์„ ๋ณด์žฅํ•œ๋‹ค.

1.1 Hassle-free Sharing(๋ฒˆ๊ฑฐ๋กœ์›€ ์—†๋Š” ๊ณต์œ )

VO ๋Š” ์ฝ”๋“œ์˜ ๋‹ค๋ฅธ ๋ถ€๋ถ„์— ์˜ํ•ด ์ˆ˜์ •๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ฐธ์กฐ๋กœ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด๋Š” side effect ๋ฅผ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋˜๋Š” ์ฝ”๋“œ์˜ ๋ณต์žก์„ฑ๊ณผ ๋ถ€ํ•˜๋ฅผ ๊ทน์ ์œผ๋กœ ๊ฐ์†Œ์‹œํ‚จ๋‹ค.

1.2 Improved Semantics(ํ–ฅ์ƒ๋œ ์˜๋ฏธ)

์ดˆ๊ธฐ ํด๋ž˜์Šค์—๋Š” ์ƒ์„ฑ์ž์™€ private ์†์„ฑ๋งŒ ์กด์žฌํ•ด์•ผํ•œ๋‹ค.

์ด๋ฅผ ํ†ตํ•ด ๋ฌด์˜๋ฏธํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ”ผํ•˜๊ณ  VO ์— ๋Œ€ํ•œ ์˜๋ฏธ์žˆ๋Š” ์ด๋ฆ„๊ณผ ๋™์ž‘์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

2. Value Equality(๊ฐ’ ๋™๋“ฑ์„ฑ) - equals ์™€ hashCode ๋ฉ”์„œ๋“œ๋ฅผ ์žฌ์ •์˜ํ•ด์•ผ ํ•œ๋‹ค.

ํƒ€์ž…๋„ ๊ฐ™๊ณ , ๋‚ด๋ถ€ ์†์„ฑ๊ฐ’๋„ ๊ฐ™์€ ๋‘ ๊ฐ์ฒด๊ฐ€ ์žˆ๋‹ค๋ฉด ์‹ค์ œ๋กœ๋„ ๊ฐ™์€ ๊ฐ์ฒด๋กœ ์ทจ๊ธ‰ํ•˜๋Š” ๊ฒƒ์ด ์ด์ƒ์ ์ด๋‹ค.

public class LottoNumber {
 
    private int value;
 
    public LottoNumber(int value) {
        this.value = value;
    }
}
 
@Test
void equals() {
    LottoNumber lottoNumber1 = new LottoNumber(5);
    LottoNumber lottoNumber2 = new LottoNumber(5);
 
    // lottoNumber1 != lottoNumber2
    assertThat(lottoNumber1 == lottoNumber2).isFalse(); // ๋™์ผ์„ฑ ๋น„๊ต
}

ํ•˜์ง€๋งŒ ๊ฐ’์ด ๊ฐ™์€ ๋‘ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๋™์ผ์„ฑ ๋น„๊ต๋ฅผ ํ•ด๋ณด๋ฉด ์„œ๋กœ ๋‹ค๋ฅธ ๊ฐ์ฒด๋กœ ๊ตฌ๋ณ„๋œ๋‹ค.

๋‘ ๊ฐ์ฒด๊ฐ€ ์ฐธ์กฐํ•˜๊ณ  ์žˆ๋Š” ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ๊ฐ’์ด ์„œ๋กœ ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๋”ฐ๋ผ์„œ ๊ฐ์ฒด๊ฐ€ ํฌํ•จํ•˜๊ณ  ์žˆ๋Š” ์†์„ฑ๊ฐ’๋“ค์„ ๊ธฐ์ค€์œผ๋กœ ๊ฐ์ฒด๋ฅผ ๋น„๊ตํ•˜๋Š” ๋™๋“ฑ์„ฑ ๋น„๊ต๋ฅผ ํ†ตํ•ด ๊ฐ์ฒด๋ฅผ ๋น„๊ตํ•ด์•ผ ํ•œ๋‹ค.

ํ•˜์ง€๋งŒ ์–ด๋–ค ์†์„ฑ๊ฐ’๋“ค์„ ๊ธฐ์ค€์œผ๋กœ ๋™๋“ฑ์„ฑ ๋น„๊ต๋ฅผ ํ•  ๊ฒƒ์ธ์ง€๋Š” ์ง์ ‘ equals() ๋ฉ”์„œ๋“œ๋ฅผ ์žฌ์ •์˜ํ•˜์—ฌ ์ •ํ•ด์•ผ ํ•œ๋‹ค.

// equals & hashcode ์žฌ์ •์˜
...
    @Override
    public boolean equals(final Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        final LottoNumber lottoNumber = (LottoNumber) o;
        return value == lottoNumber.value;
    }
 
    @Override
    public int hashCode() {
        return Objects.hash(value);
    }
...

hashCode() ๋ฅผ ์žฌ์ •์˜ํ•˜์ง€ ์•Š์œผ๋ฉด ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Ÿ๊ฐ’์„ ์‚ฌ์šฉํ•ด์„œ ํ•ด์‹œ๊ฐ’์„ ๋งŒ๋“ ๋‹ค.

hashCode() ๋ฅผ ์žฌ์ •์˜ ํ•ด์คŒ์œผ๋กœ์จ ๊ฐ์ฒด์˜ ํŠน์ • ๊ฐ’์„ ๊ธฐ์ค€์œผ๋กœ ๊ฐ™์€ ํ•ด์‹œ์ฝ”๋“œ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๊ณ , ์ด๋Š” ํ•ด์‹œ๊ฐ’์„ ์‚ฌ์šฉํ•˜๋Š” ์ปฌ๋ ‰์…˜ ๋“ฑ์—์„œ ๊ฐ์ฒด๋ฅผ ๋น„๊ตํ•˜๋Š” ์šฉ๋„๋กœ ์‚ฌ์šฉ๋˜๊ธฐ ๋•Œ๋ฌธ์— equals() ๋ฉ”์„œ๋“œ ์žฌ์ •์˜ ์‹œ hashCode() ๋„ ํ•จ๊ป˜ ์žฌ์ •์˜ ํ•ด์ฃผ๋Š” ๊ฒƒ์ด ํšจ๊ณผ์ ์ด๋‹ค.

๋‹จ, ๋ฌด์˜๋ฏธํ•œ equals() ์™€ hashCode() ๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„์•ผ ํ•œ๋‹ค.

3. Self Validation(์ž๊ฐ€ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ) - ์ƒ์„ฑ์ž์—์„œ validate

VO ๋Š” context ์—์„œ ์œ ํšจํ•œ ๊ฐ’๋งŒ ํ—ˆ์šฉํ•œ๋‹ค. ์ด๋Š” ์œ ํšจํ•˜์ง€ ์•Š๋Š” ๊ฐ’์œผ๋กœ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์—†์Œ์„ ์˜๋ฏธํ•˜๊ธฐ๋„ ํ•œ๋‹ค.

์ƒ์„ฑ์ž ๋‚ด๋ถ€์—์„œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์ˆ˜ํ–‰ํ•จ์œผ๋กœ์จ ๋ชจ๋“  ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์ƒ์„ฑ ์‹œ๊ฐ„์— ์ด๋ฃจ์–ด์ง€๊ฒŒ๋” ํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด๋Ÿฌํ•œ ๊ฐ•์ œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋Š” ์˜๋ฏธ์žˆ๊ณ  ๋ช…์‹œ์ ์ธ ๋ฐฉ๋ฒ•์œผ๋กœ ๋„๋ฉ”์ธ ์ œ์•ฝ ์กฐ๊ฑด์„ ํ‘œํ˜„ํ•˜๋Š”๋ฐ๋„ ์œ ์šฉํ•˜๋‹ค.

public class LottoNumbers {
 
    private final List<LottoNumber> lottoNumbers;
 
    private LottoNumbers(List<LottoNumber> lottoNumbers) {
	      // validateLottoNumbers(lottoNumbers); 1๋ฒˆ ์“ฐ๋ ˆ๋“œ์—์„œ ๊ฒ€์ฆ
        // 2๋ฒˆ ์“ฐ๋ ˆ๋“œ์˜ ์™ธ๋ถ€ ๋ฉ”์„œ๋“œ์—์„œ lottoNumbers ๋ฅผ ๋ณ€๊ฒฝ
        // this.lottoNumbers = new ArrayList<>(lottoNumbers); ๋ณ€๊ฒฝ๋œ ๊ฐ’์ด ํ• ๋‹น
        this.lottoNumbers = new ArrayList<>(lottoNumbers);
        validateLottoNumbers();
    }
 
    public static LottoNumbers of(List<Integer> rawLottoNumbers) {
        ...
        return new LottoNumber(lottoNumbers);
    }
 
    private void validateLottoNumber() {
        ...
    }
}

๊ฐ’ ์ฃผ์ž… ํ›„ validate ๋ฅผ ์ง„ํ–‰ํ•˜๋Š” ๊ฒƒ์„ ๋ฐฉ์–ด์  ๋ณต์‚ฌ ๊ธฐ๋ฒ•์ด๋ผ ํ•˜๋Š”๋ฐ, ์ด๋Š” Multi-Thread ํ™˜๊ฒฝ์— ์•ˆ์ „ํ•˜๋‹ค.

Multi-Thread ํ™˜๊ฒฝ์—์„œ๋Š” validate ๋ฅผ ๋งˆ์นœ lottoNumber ๋ฅผ ๋‹ค๋ฅธ ์“ฐ๋ ˆ๋“œ์—์„œ ๋ณ€๊ฒฝํ•˜๊ฒŒ๋˜๋ฉด ๋ณ€๊ฒฝ๋œ ์ƒํƒœ๋กœ ๊ฐ์ฒด์— ํ• ๋‹น๋˜๊ธฐ ๋•Œ๋ฌธ์— validate ๋ฅผ ํ›„์ˆœ์œ„๋กœ ๋ฏธ๋ฃจ๋Š” ๊ฒƒ์ด๋‹ค.

VO ๋Š” ์›์‹œ๊ฐ’ ํฌ์žฅ๊ณผ ๊ฐ™์€๊ฐ€?


๊ฒฐ๋ก ๋ถ€ํ„ฐ ๋งํ•˜์ž๋ฉด ๋‹ค๋ฅด๋‹ค. ์›์‹œ๊ฐ’ ํฌ์žฅ๊ณผ ๋™์‹œ์— VO ๋กœ ๋งŒ๋“ค ์ˆœ ์žˆ์ง€๋งŒ, ์›์‹œ๊ฐ’ ํฌ์žฅ์ด VO ๋Š” ์•„๋‹ˆ๋‹ค.

์›์‹œ๊ฐ’ ํฌ์žฅ

์›์‹œ ์œ ํ˜•์˜ ๊ฐ’์„ ์ด์šฉํ•ด ์˜๋ฏธ๋ฅผ ๋‚˜ํƒ€๋‚ด์ง€ ์•Š๊ณ , ์˜๋ฏธ์žˆ๋Š” ๊ฐ์ฒด๋กœ ํฌ์žฅํ•œ๋‹ค๋Š” ๊ฐœ๋…

VO

  • Immutability(๋ถˆ๋ณ€์„ฑ) - ์ˆ˜์ •์ž(setter)๊ฐ€ ์—†๋‹ค.
  • Value Equality(๊ฐ’ ๋™๋“ฑ์„ฑ) - ๋‚ด๋ถ€ ๋™๋“ฑ์„ฑ ๊ฒ€์‚ฌ.
  • Self Validation(์ž๊ธฐ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ) - ์ƒ์„ฑ์ž์—์„œ validate.

DTO ๋ž€?


DTO ๋Š” ๊ณ„์ธต ๊ฐ„ ๋ฐ์ดํ„ฐ ๊ตํ™˜์„ ์œ„ํ•ด ์‚ฌ์šฉ๋˜๋Š” ๊ฐ์ฒด๋กœ, ๋กœ์ง์„ ๊ฐ€์ง€์ง€ ์•Š๊ณ  getter ์™€ setter ๋งŒ ๊ฐ–๊ณ  ์žˆ๋Š” ์ˆœ์ˆ˜ํ•œ ๋ฐ์ดํ„ฐ ๊ฐ์ฒด๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด ์œ ์ €๊ฐ€ ์ž…๋ ฅํ•œ ๋ฐ์ดํ„ฐ๋ฅผ DB ์— ๋„ฃ๋Š” ๊ณผ์ •์„ ๋ณด์ž.

์œ ์ €๊ฐ€ ์ž์‹ ์˜ ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ž…๋ ฅํ•˜์—ฌ form ์— ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ DTO ์— ๋„ฃ์–ด์„œ ์ „์†กํ•œ๋‹ค.

ํ•ด๋‹น DTO ๋ฅผ ๋ฐ›์€ ์„œ๋ฒ„๊ฐ€ DAO ๋ฅผ ์ด์šฉํ•˜์—ฌ DB ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ง‘์–ด๋„ฃ๋Š”๋‹ค.

References