Skip to content

Commit

Permalink
Merge pull request #1326 from Aqudi/taejung
Browse files Browse the repository at this point in the history
  • Loading branch information
github-actions[bot] authored Feb 12, 2024
2 parents ee940c6 + 5674b38 commit 2675ffd
Showing 1 changed file with 107 additions and 0 deletions.
107 changes: 107 additions & 0 deletions taejung/2024-02-03-why-value-object.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
์›๋ณธ: https://cloud-whale.hashnode.dev/why-value-object

# Value Object๋ฅผ ํ†ตํ•œ ์•ˆ์ „ํ•œ ๋ฐ์ดํ„ฐ ์‚ฌ์šฉ

## ๊ฐœ์š”

์ตœ๊ทผ BLE ํ”„๋กœํ† ์ฝœ์„ ํ†ตํ•ด์„œ IOT๊ธฐ๊ธฐ์™€ ํ†ต์‹ ์„ ํ•˜๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ฐœ๋ฐœํ•  ์ผ์ด ์žˆ์—ˆ๋Š”๋ฐ ํŽŒ์›จ์–ด ์—…๋ฐ์ดํŠธ ์ดํ›„์— ๊ฐ‘์ž๊ธฐ ํ†ต์‹ ์ด ์•ˆ ๋˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒ์„ ํ–ˆ๋‹ค. ์›์ธ์„ ์ฐพ์•„๋ณด๋‹ˆ ์ผ๋ถ€ ๊ธฐ๊ธฐ์˜ unique key๋ฅผ advertising ํ•˜๋Š” ๋กœ์ง์ด ๋ฐ”๋€Œ์–ด ํฌ๋งท์€ ๋™์ผํ•œ๋ฐ ์ผ๋ถ€ ์ž๋ฆฟ์ˆ˜๊ฐ€ 16์ง„์ˆ˜๋กœ ํ‘œ๊ธฐ๋  ๋•Œ ์•ž์˜ 0์„ ๋นผ๋จน๊ณ  ์ „๋‹ฌ์ด ๋œ ๊ฒƒ๊ณผ ๋Œ€์†Œ๋ฌธ์ž์˜ ์ฐจ์ด๋กœ ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒ์„ ํ–ˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ฒŒ ๋๋‹ค.

์ด๋•Œ ๋‹จ์ˆœํ•˜๊ฒŒ IoT device๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ํด๋ž˜์Šค์˜ getter๋ฅผ ํ†ตํ•ด์„œ ์ˆ˜์ •์„ ํ• ๊นŒ ํ•˜๋‹ค๊ฐ€ ์ฝ”๋“œ๊ฐ€ ๋„ˆ๋ฌด ๊ธธ์–ด์ ธ์„œ ๊ฐ€๋…์„ฑ, ์œ ์ง€๋ณด์ˆ˜์„ฑ์ด ๋–จ์–ด์ง€๊ธฐ๋„ ํ•ด์„œ value object๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ validation์˜ ์ฑ…์ž„์„ ๋ถ„๋ฆฌ์‹œํ‚ค๊ธฐ๋กœ ํ–ˆ๋‹ค. ์˜ค๋Š˜์€ ์ด๋•Œ ๊ณต๋ถ€ํ–ˆ๋˜ ๋‚ด์šฉ์„ ์˜ˆ์‹œ์™€ ํ•จ๊ป˜ ์ •๋ฆฌ๋ฅผ ํ•ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

## Primitive Obsession

Primitive Obsession, ์›์‹œํƒ€์ž… ์ง‘์ฐฉ์€ ๋ณต์žกํ•œ ๊ฐœ๋…์„ ํ‘œํ˜„ํ•  ๋•Œ ๊ธฐ๋ณธ ์ž๋ฃŒํ˜•์„ ๊ณผ๋„ํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๋งํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, 2์ฐจ์› ์ขŒํ‘œ, ๊ธฐ๊ฐ„, ์ „ํ™”๋ฒˆํ˜ธ์™€ ๊ฐ™์€ ๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ ํƒ€์ž…์„ ํŠน์ • ํด๋ž˜์Šค๋‚˜ ๊ตฌ์กฐ์ฒด๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๊ณ  ํ”„๋ฆฌ๋ฏธํ‹ฐ๋ธŒ๋กœ ํ‘œํ˜„ํ•˜๋Š” ๊ฒƒ์ด ์—ฌ๊ธฐ์— ํฌํ•จ๋œ๋‹ค.

์ „ํ™”๋ฒˆํ˜ธ๋Š” ๋ฌธ์ž์—ด๋งŒ์„ ์ด์šฉํ•ด์„œ ์ถฉ๋ถ„ํžˆ ๋‚˜ํƒ€๋‚ผ ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ๋ฌธ์ž์—ด์ด ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๋ชจ๋“  ์†์„ฑ์„ ์ „ํ™”๋ฒˆํ˜ธ๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ์ง€๋Š” ์•Š๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, "๋ฌธ์ž์—ด์ด ์ง€์›ํ•˜๋Š” ๋”ํ•˜๊ธฐ ์—ฐ์‚ฐ์„ ์ „ํ™”๋ฒˆํ˜ธ๊ฐ€ ์ง€์›์„ ํ•ด์•ผ ํ• ๊นŒ?"๋ผ๊ณ  ์ƒ๊ฐํ•œ๋‹ค๋ฉด ๊ทธ ๋Œ€๋‹ต์€ "์•„๋‹ˆ์š”"๊ฐ€ ๋  ๊ฒƒ์ด๋‹ค.

Primitive Obsession๋Š” ์ด์ฒ˜๋Ÿผ ๋„๋ฉ”์ธ์ ์œผ๋กœ ํ•ด๋‹น ๊ฐ’์ด ๋‚˜ํƒ€๋‚ด๋Š” ์˜๋ฏธ๋ฅผ ์ง๊ด€์ ์œผ๋กœ ํŒ๋‹จํ•  ์ˆ˜ ์—†๊ฒŒ ๋งŒ๋“ ๋‹ค. ๊ทธ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋ฐ์ดํ„ฐ์™€ ๊ด€๋ จ๋œ ๊ฒ€์ฆ ๋ฐ ์กฐ์ž‘ ๋กœ์ง์„ ์ฝ”๋“œ ์ „์ฒด์— ๋ถ„์‚ฐ์‹œ์ผœ ์œ ์ง€ ๊ด€๋ฆฌ๋ฅผ ์–ด๋ ต๊ฒŒ ๋งŒ๋“ค๊ณ  IDE์˜ ๋„์›€์„ ๋ฐ›๊ธฐ ์–ด๋ ต๊ธฐ ๋•Œ๋ฌธ์— ์‹ค์ˆ˜๊ฐ€ ์ผ์–ด๋‚˜๊ธฐ๋„ ์‰ฝ๋‹ค.

Value Object๊ฐ€ ๋ฐ”๋กœ ์ด๋Ÿฌํ•œ Primitive Obsession์„ ํ”ผํ•˜๋ฉด์„œ ๊ฐ’์ด ๋„๋ฉ”์ธ ๋‚ด์—์„œ ๊ฐ€์ง€๋Š” ์˜๋ฏธ๋ฅผ ๋ช…ํ™•ํ•˜๊ฒŒ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ์žฅ์น˜์ด๋‹ค.

## Value Object(VO)๋ž€?

Value Object๋Š” ๋„๋ฉ”์ธ ์ฃผ๋„ ์„ค๊ณ„(domain-driven development, DDD)์˜ ํ•ต์‹ฌ์ ์ธ ๊ฐœ๋… ์ค‘ ํ•˜๋‚˜๋กœ ๋„๋ฉ”์ธ์˜ ํ•œ ์ธก๋ฉด์„ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฐ์ฒด์ด๋‹ค. ๋ณดํ†ต ํ•œ ๊ฐœ ๋˜๋Š” ๊ทธ ์ด์ƒ์˜ ๊ฐ’๋“ค์„ ๋ฌถ์–ด์„œ ํŠน์ • ๊ฐ’์„ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฐ์ฒด๋กœ ๋Œ€ํ‘œ์ ์ธ ์˜ˆ์‹œ๋กœ๋Š” x, y ์ขŒํ‘œ๋กœ ๊ตฌ์„ฑ๋˜๋Š” 2์ฐจ์› ์ขŒํ‘œ๋‚˜ ์‹œ์ž‘ ๋‚ ์งœ์™€ ๋ ๋‚ ์งœ๋กœ ๊ตฌ์„ฑ๋˜๋Š” ๊ธฐ๊ฐ„ ๋“ฑ์ด ์žˆ๋‹ค.

Value object๊ฐ€ ๊ฐ€์ ธ์•ผ ํ•˜๋Š” ์ฃผ์š” ํŠน์ง•์€ ๊ฐ’ ๊ธฐ๋ฐ˜ ๋™๋“ฑ์„ฑ ๊ฒ€์‚ฌ์™€ ๋ถˆ๋ณ€์„ฑ, ์ž๊ธฐ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ฐ€ ์žˆ๋‹ค.

## Value Object์˜ ํŠน์ง•

### ๊ฐ’ ๊ธฐ๋ฐ˜ ๋™๋“ฑ์„ฑ ๊ฒ€์‚ฌ

[๋™์ผ์„ฑ(identity)๊ณผ ๋™๋“ฑ์„ฑ(equality)์˜ ์ฐจ์ด](https://hudi.blog/identity-vs-equality/)๋ฅผ ๊ฐ„๋žตํ•˜๊ฒŒ ์„ค๋ช…ํ•˜๋ฉด ๋™์ผ์„ฑ์€ ๋น„๊ต ๋Œ€์ƒ์ธ ๋‘ ๊ฐ์ฒด์˜ ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ๊ฐ€ ๊ฐ™์Œ์„ ์˜๋ฏธํ•˜๊ณ  ๋™๋“ฑ์„ฑ์€ ๋‘ ๊ฐ์ฒด๊ฐ€ ๋…ผ๋ฆฌ์ ์œผ๋กœ ๋™์ผํ•œ ๊ฐ’์„ ๋‚˜ํƒ€๋‚ด๊ณ  ์žˆ์Œ์„ ์˜๋ฏธํ•œ๋‹ค.

```javascript
const point1 = { x: 1, y: 10 };
const point2 = { x: 1, y: 10 };
```

์˜ˆ๋ฅผ ๋“ค์–ด, 2์ฐจ์› ์ขŒํ‘œ๋ฅผ ํ‘œํ˜„ํ•˜๋Š” ๊ฐ’์„ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๋‘ ๋ณ€์ˆ˜ `point1`, `point2`๊ฐ€ ์žˆ์„๋•Œ ๋‘ ๊ฐ’์€ ๋‚ด๋ถ€์˜ x, y ์ขŒํ‘œ๊ฐ’์ด ๊ฐ™๊ธฐ ๋•Œ๋ฌธ์— ๋™๋“ฑํ•˜์ง€๋งŒ ์„œ๋กœ ๋‹ค๋ฅธ ๋ณ€์ˆ˜์ด๊ธฐ ๋•Œ๋ฌธ์— ๋™์ผํ•˜์ง€๋Š” ์•Š๋‹ค๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

๋™๋“ฑ์„ฑ ๊ฒ€์‚ฌ๋Š” ์–ธ์–ด๋งˆ๋‹ค ๋‹ค๋ฅด๊ฒŒ ์ด๋ฃจ์–ด์ง„๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด Java์—์„œ๋Š” `equals`, `hashcode`๋ฅผ ์žฌ์ •์˜ํ•ด์•ผ ํ•˜๋Š” ๋ฐ˜๋ฉด์— functional programming ์–ธ์–ด์ธ Haskell์ด๋‚˜ Clojure์—์„œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์†์„ฑ์„ ๊ธฐ์ค€์œผ๋กœ ํ•œ ๋™๋“ฑ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์ง€์›ํ•œ๋‹ค.

### ๋ถˆ๋ณ€์„ฑ (Immutability)

Value Object๋Š” ๊ทธ ์ž์ฒด๋กœ ๊ฐ’์ธ ๊ฐ์ฒด์ด๊ณ  ์ด ๊ฐ’์ด ๋ถˆ๋ณ€์ด์–ด์•ผ ํ•œ๋‹ค๋Š” ์›์น™์ด ์žˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ์ˆ˜์ •์ž๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์ง€ ์•Š์•„์•ผ ํ•˜๋ฉฐ ๋งŒ์•ฝ์— ๊ฐ’์„ ๋ฐ”๊พธ๊ณ  ์‹ถ๋‹ค๋ฉด ์ƒˆ๋กœ์šด ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด์„œ ๊ฐ’์„ ํ• ๋‹นํ•˜๋Š” ๋ฐฉ๋ฒ• ๋ฟ์ด๋‹ค.

์ด๋Ÿฌํ•œ ํŠน์ง• ๋•๋ถ„์— side effect ๋ฐœ์ƒ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์–ด ์˜๋„ํ•˜์ง€ ์•Š์€ ๊ณณ์—์„œ ์ด ๊ฐ’์ด ์ˆ˜์ •๋˜์–ด ๋ฐœ์ƒํ•˜๋Š” [Aliasing Bug](https://martinfowler.com/bliki/AliasingBug.html) ๊ฑฑ์ • ์—†์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

### ์ž๊ธฐ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ (Self-validation)

๋งŒ์•ฝ์— ์›์‹œํƒ€์ž…์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ณต์žกํ•œ ๊ฐ’์„ ํ‘œํ˜„ํ•œ๋‹ค๋ฉด ๊ฐ’์˜ ์œ ํšจ์„ฑ์„ ์‚ฌ์šฉํ•˜๋Š” ์ธก์—์„œ ๊ฒ€์‚ฌ๋ฅผ ํ•ด์•ผ ํ•œ๋‹ค. Value Object์˜ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋Š” ์ƒ์„ฑ ์‹œ๊ฐ„์— ์ด๋ฃจ์–ด์ ธ์„œ ํ•ญ์ƒ ์œ ํšจํ•œ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์•ผ ํ•˜๋ฉฐ ์œ ํšจํ•˜์ง€ ์•Š๋Š” ๊ฐ’์ด ๋“ค์–ด์™”์„ ๋•Œ๋Š” Value Object๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์—†์–ด์•ผํ•œ๋‹ค.

Value Object์˜ ๋ถˆ๋ณ€์„ฑ๊ณผ ์ž๊ธฐ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ํ†ตํ•ด ํ•ญ์ƒ ์œ ํšจํ•œ ์ƒํƒœ ์œ ์ง€๋ฅผ ๋ณด์žฅํ•  ์ˆ˜ ์žˆ์–ด ํด๋ผ์ด์–ธํŠธ ์ชฝ์—์„œ ๋„๋ฉ”์ธ ๊ทœ์น™์ด ๊นจ์งˆ ์—ผ๋ ค๋ฅผ ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋‹ค.

## Device์˜ unique key๋ฅผ Value Object๋กœ ํ‘œํ˜„

```dart
class DeviceUniqueKey {
final String _uniqueKey;
DeviceUniqueKey(String uniqueKey)
: assert(uniqueKey.isNotEmpty),
assert(
uniqueKey
.split(":")
.map((e) => int.tryParse(e, radix: 16) != null)
.every((element) => element),
),
_uniqueKey = uniqueKey.toUpperCase();
@override
bool operator ==(Object other) {
return other.hashCode == hashCode;
}
@override
int get hashCode => _uniqueKey.hashCode;
}
```

์œ„์˜ dart๋กœ ์ž‘์„ฑ๋œ unique key๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” value object์„ ์‚ดํŽด๋ณด๋ฉด ์•ž์„œ ๋ณธ Value Object์˜ ํŠน์ง•์ฒ˜๋Ÿผ ์ƒ์„ฑ ์‹œ์ ์— validation์„ ์ˆ˜ํ–‰ํ•˜์—ฌ ํŠน์ •ํ•œ ํฌ๋งท์„ ๋งŒ์กฑํ•˜๋„๋ก ๊ฐ•์ œํ•˜๋ฉฐ ์ž๊ธฐ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋ถˆ๋ณ€์„ฑ์„ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•ด ๋‚ด๋ถ€์˜ ๊ฐ’์„ ์ˆ˜์ •ํ•  ์ˆ˜ ์—†๊ฒŒ ๋งŒ๋“ค์—ˆ์œผ๋ฉฐ `==` ์—ฐ์‚ฐ์ž์™€ `hashCode`๋ฅผ ์žฌ์ •์˜ํ•˜์—ฌ ๊ฐ’์„ ํ†ตํ•œ ๋™๋“ฑ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์ง€์›ํ•˜๊ณ  ์žˆ๋‹ค.

## ๊ฒฐ๋ก 

Value Object๊ฐ€ ๊ฐ€์ง€๋Š” ๋ถˆ๋ณ€์„ฑ, ์ž๊ธฐ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๋“ฑ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์•ˆ์‹ฌํ•˜๊ณ  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ํŠน์„ฑ๋“ค์€ ํ˜‘์—… ์ƒํ™ฉ์—์„œ ๋”์šฑ ๋น›์„ ๋ฐœํ•˜๋Š” ๊ฒƒ ๊ฐ™๋‹ค. ํŠนํžˆ ๋‚ด๊ฐ€ ๋งŒ๋“ค์–ด๋‘” ๊ฐ์ฒด๊ฐ€ ์˜๋„ํ•œ ์ƒํƒœ๋Œ€๋กœ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค๋Š” ๊ฒƒ์€ ์ปค๋ฎค๋‹ˆ์ผ€์ด์…˜ ๊ณผ์ •์—์„œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์˜ค๋ฅ˜๋“ค์„ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด ๊ต‰์žฅํžˆ ํฐ ๋งค๋ ฅ์œผ๋กœ ๋Š๊ปด์กŒ๋‹ค. ๋„๋ฉ”์ธ์ด ๋” ๋ณต์žกํ•ด์งˆ์ˆ˜๋ก Value Object๊ฐ€ ๊ฐ€์ ธ๋‹ค์ฃผ๋Š” ์ฝ”๋“œ์˜ ๋ช…ํ™•์„ฑ, ์œ ์ง€๋ณด์ˆ˜์„ฑ์ด ๋” ํฐ ์žฅ์ ์œผ๋กœ ๋‹ค๊ฐ€์˜ฌ ๊ฒƒ ๊ฐ™๋‹ค.

์•ž์œผ๋กœ๋Š” ํ˜ผ์ž์„œ ๊ฐœ๋ฐœ์„ ํ•  ๋•Œ๋„ ๊ฝค ์˜ค๋ž˜ ์ „์— ๋งŒ๋“ค์–ด ๋‘” ๋กœ์ง๋“ค์€ ๊นŒ๋จน์„ ์ˆ˜๋„ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์•ž์œผ๋กœ validation ์ด ํ•„์š”ํ•œ ๊ฐ’์˜ ๊ฒฝ์šฐ์—๋Š” Value Object๋ฅผ ํ†ตํ•ด์„œ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋Š” ์Šต๊ด€์„ ๋“ค์—ฌ์•ผ๊ฒ ๋‹ค.

๋‹ค์Œ์—๋Š” Value Object๋ฅผ ๊ณต๋ถ€ํ•˜๋ฉด์„œ ์ •๋ฆฌ๋ฅผ ํ•˜๊ณ  ์žˆ์—ˆ๋˜ ์ผ๊ธ‰ ์ปฌ๋ ‰์…˜์— ๋Œ€ํ•œ ํฌ์ŠคํŒ…์„ ํ•  ๊ฒƒ์ด๋‹ค.

## References

* https://martinfowler.com/bliki/ValueObject.html

* https://en.wikipedia.org/wiki/Value\_object

* https://hudi.blog/value-object/

* https://hudi.blog/identity-vs-equality/

* https://ksh-coding.tistory.com/83

* https://velog.io/@livenow/Java-VOValue-Object%EB%9E%80

* https://medium.com/@nicolopigna/value-objects-like-a-pro-f1bfc1548c72

* https://medium.com/fistkim101/ddd-%EC%84%B8%EB%A0%88%EB%82%98%EB%8D%B0-3-%EB%8F%84%EB%A9%94%EC%9D%B8-%EC%A3%BC%EB%8F%84-%EC%84%A4%EA%B3%84-%EA%B8%B0%EB%B3%B8-%EC%9A%94%EC%86%8C-99eead8e96f3

0 comments on commit 2675ffd

Please sign in to comment.