본문으둜 κ±΄λ„ˆλ›°κΈ°

🐀 Chapter 5. λͺ©κ³Ό ν…ŒμŠ€νŠΈ μ·¨μ•½μ„±

πŸ₯• λͺ©κ³Ό μŠ€ν… ꡬ뢄​

🎈 ν…ŒμŠ€νŠΈ λŒ€μ—­ μœ ν˜•β€‹

ν…ŒμŠ€νŠΈ λŒ€μ—­μ€ λͺ¨λ“  μœ ν˜•μ˜ λΉ„μš΄μ˜μš© κ°€μ§œ μ˜μ‘΄μ„±μ„ μ„€λͺ…ν•˜λŠ” 포괄적인 μš©μ–΄λ‹€. ν…ŒμŠ€νŠΈ λŒ€μ—­μ˜ μ£Ό μš©λ„λŠ” ν…ŒμŠ€νŠΈλ₯Ό νŽΈλ¦¬ν•˜κ²Œ ν•˜λŠ” 것이닀.
ν…ŒμŠ€νŠΈ λŒ€μ—­μ€ λͺ©κ³Ό μŠ€ν…μ˜ 두 가지 μœ ν˜•μœΌλ‘œ λ‚˜λˆŒ 수 μžˆλ‹€.

이 두 μœ ν˜•μ˜ 차이점은 λ‹€μŒκ³Ό κ°™λ‹€.

  • λͺ©μ€ μ™ΈλΆ€λ‘œ λ‚˜κ°€λŠ” μƒν˜Έ μž‘μš©μ„ λͺ¨λ°©ν•˜κ³  κ²€μ‚¬ν•˜λŠ” 데 도움이 λœλ‹€. μ΄λŸ¬ν•œ μƒν˜Έ μž‘μš©μ€ SUTκ°€ μƒνƒœλ₯Ό λ³€κ²½ν•˜κΈ° μœ„ν•œ μ˜μ‘΄μ„±μ„ ν˜ΈμΆœν•˜λŠ” 것에 ν•΄λ‹Ήν•œλ‹€.
  • μŠ€ν…μ€ λ‚΄λΆ€λ‘œ λ“€μ–΄μ˜€λŠ” μƒν˜Έ μž‘μš©μ„ λͺ¨λ°©ν•˜λŠ” 데 도움이 λœλ‹€. μ΄λŸ¬ν•œ μƒν˜Έ μž‘μš©μ€ SUTκ°€ μž…λ ₯ 데이터λ₯Ό μ–»κΈ° μœ„ν•œ μ˜μ‘΄μ„±μ„ ν˜ΈμΆœν•˜λŠ” 것에 ν•΄λ‹Ήν•œλ‹€.

μŠ€νŒŒμ΄λŠ” λͺ©κ³Ό 같은 역할을 ν•œλ‹€. μŠ€νŒŒμ΄λŠ” μˆ˜λ™μœΌλ‘œ μž‘μ„±ν•˜λŠ” 반면, λͺ©μ€ λͺ© ν”„λ ˆμž„μ›Œν¬μ˜ 도움을 λ°›μ•„ μƒμ„±λœλ‹€.
μŠ€ν…, 더미, 페이크의 μ°¨μ΄λŠ” μ–Όλ§ˆλ‚˜ λ˜‘λ˜‘ν•œμ§€μ— μž‡λ‹€. λ”λ―ΈλŠ” 널 κ°’μ΄λ‚˜ κ°€μ§œ λ¬Έμžμ—΄κ³Ό 같이 λ‹¨μˆœν•˜κ³  ν•˜λ“œμ½”λ”©λœ 값이닀. SUT의 λ©”μ„œλ“œ μ‹œκ·Έλ‹ˆμ²˜λ₯Ό λ§Œμ‘±μ‹œν‚€κΈ° μœ„ν•΄ μ‚¬μš©ν•˜κ³  μ΅œμ’… κ²°κ³Όλ₯Ό λ§Œλ“œλŠ” 데 영ν–₯을 주지 μ•ŠλŠ”λ‹€. μŠ€ν…μ€ 더 μ •κ΅ν•˜λ‹€. μ‹œλ‚˜λ¦¬μ˜€λ§ˆλ‹€ λ‹€λ₯Έ 값을 λ°˜ν™˜ν•˜κ²Œλ” ꡬ성할 수 μžˆλ„λ‘ ν•„μš”ν•œ 것을 λ‹€ κ°–μΆ˜ μ™„μ „ν•œ μ˜μ‘΄μ„±μ΄λ‹€. λ§ˆμ§€λ§‰μœΌλ‘œ νŽ˜μ΄ν¬λŠ” λŒ€λ‹€μˆ˜μ˜ λͺ©μ μ— λΆ€ν•©ν•˜λŠ” μŠ€ν…κ³Ό κ°™λ‹€. 차이점은 생성에 μžˆλ‹€. νŽ˜μ΄ν¬λŠ” 보톡 아직 μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” μ˜μ‘΄μ„±μ„ λŒ€μ²΄ν•˜κ³ μž κ΅¬ν˜„ν•œλ‹€.

λͺ©κ³Ό μŠ€ν…μ˜ 차이점에도 μœ μ˜ν•˜λΌ. λͺ©μ€ SUT와 κ΄€λ ¨ μ˜μ‘΄μ„± κ°„μ˜ μƒν˜Έ μž‘μš©μ„ λͺ¨λ°©ν•˜κ³  κ²€μ‚¬ν•˜λŠ” 반면, μŠ€ν…μ€ λͺ¨λ°©λ§Œ ν•œλ‹€. μ΄λŠ” μ€‘μš”ν•œ 차이점이닀.

λͺ© λΌμ΄λΈŒλŸ¬λ¦¬μ—μ„œ Mock 클래슀λ₯Ό μ‚¬μš©ν•΄ λͺ©μ„ 생성
[Fact]
public void Sending_a_greetings_email() {
var mock = new Mock<IEmailGateway>();
var sut = new Controller(mock.Object);

sut.GreetUser("user@email.com");

mock.Verify(
x => x.SendGreetingsEmail("user@email.com"),
Times.Once
);
}

이 ν΄λž˜μŠ€λŠ” ν…ŒμŠ€νŠΈ λŒ€μ—­(λͺ©)을 λ§Œλ“€ 수 μžˆλŠ” 도ꡬ닀. λ„κ΅¬λ‘œμ„œμ˜ λͺ©μ„ μ‚¬μš©ν•΄ λͺ©κ³Ό μŠ€ν…, 이 두가지 μœ ν˜•μ˜ ν…ŒμŠ€νŠΈ λŒ€μ—­μ„ 생성할 수 있기 λ•Œλ¬Έμ— λ„κ΅¬λ‘œμ„œμ˜ λͺ©κ³Ό ν…ŒμŠ€νŠΈ λŒ€μ—­μœΌλ‘œμ„œ λͺ©μ„ ν˜Όλ™ν•˜μ§€ μ•ŠλŠ” 것이 μ€‘μš”ν•˜λ‹€.

Mock 클래슀λ₯Ό μ‚¬μš©ν•΄ μŠ€ν…μ„ 생성
[Fact]
public void Creating_a_report() {
var stub = new Mock<IDatabase>();
stub.Setup(x => x.GetNumberOfUsers()).Returns(10);
var sut = new Controller(stub.Object);

Report report = sut.CreateReport();

Assert.Equal(10, report.NumberOfUsers);
}

이 ν…ŒμŠ€νŠΈ λŒ€μ—­μ€ λ‚΄λΆ€λ‘œ λ“€μ–΄μ˜€λŠ” μƒν˜Έ μž‘μš©, 즉 SUT에 μž…λ ₯ 데이터λ₯Ό μ œκ³΅ν•˜λŠ” ν˜ΈμΆœμ„ λͺ¨λ°©ν•œλ‹€. 반면 이전 μ˜ˆμ œμ—μ„œ SendGreetingsEmail에 λŒ€ν•œ ν˜ΈμΆœμ€ μ™ΈλΆ€λ‘œ λ‚˜κ°€λŠ” μƒν˜Έ μž‘μš©μ΄κ³  κ·Έ λͺ©μ μ€ λΆ€μž‘μš©μ„ μΌμœΌν‚€λŠ” 것(이메일 λ°œμ†‘)뿐이닀.

🎈 μŠ€ν…μœΌλ‘œ μƒν˜Έ μž‘μš©μ„ κ²€μ¦ν•˜μ§€ 말라​

λͺ©κ³Ό μŠ€ν…μ˜ μ°¨μ΄λŠ” μŠ€ν…κ³Όμ˜ μƒν˜Έ μž‘μš©μ„ κ²€μ¦ν•˜μ§€ λ§λΌλŠ” μ§€μΉ¨μ—μ„œ λΉ„λ‘―λœλ‹€. SUTμ—μ„œ μŠ€ν…μœΌλ‘œμ˜ ν˜ΈμΆœμ€ SUTκ°€ μƒμ„±ν•˜λŠ” μ΅œμ’… κ²°κ³Όκ°€ μ•„λ‹ˆλ‹€. μ΄λŸ¬ν•œ ν˜ΈμΆœμ€ μ΅œμ’… κ²°κ³Όλ₯Ό μ‚°μΆœν•˜κΈ° μœ„ν•œ μˆ˜λ‹¨μΌ 뿐이닀. 즉, μŠ€ν…μ€ SUTκ°€ 좜λ ₯을 μƒμ„±ν•˜λ„λ‘ μž…λ ₯을 μ œκ³΅ν•œλ‹€.

4μž₯μ—μ„œ μ‚΄νŽ΄λ΄£λ“―μ΄, ν…ŒμŠ€νŠΈμ—μ„œ 거짓 양성을 ν”Όν•˜κ³  λ¦¬νŒ©ν„°λ§ 내성을 ν–₯μƒμ‹œν‚€λŠ” 방법은 κ΅¬ν˜„ μ„ΈλΆ€ 사항이 μ•„λ‹ˆλΌ μ΅œμ’… κ²°κ³Όλ₯Ό κ²€μ¦ν•˜λŠ” 것뿐이닀.

mock.Verify(x => x.SendGreetingsEmail("user@email.com"));

μœ„ 예제의 μœ„ ꡬ문은 μ‹€μ œ 결과에 λΆ€ν•©ν•˜λ©°, ν•΄λ‹Ή κ²°κ³ΌλŠ” 도메인 μ „λ¬Έκ°€μ—κ²Œ μ˜λ―Έκ°€ μžˆλ‹€.

λ‹€μŒ μ˜ˆμ œλŠ” 깨지기 μ‰¬μš΄ ν…ŒμŠ€νŠΈμ˜ μ˜ˆμ— ν•΄λ‹Ήν•œλ‹€.

μŠ€ν…μœΌλ‘œ μƒν˜Έ μž‘μš© 검증
[Fact]
public void Creating_a_report() {
var stub = new Mock<IDatabase>();
stub.Setup(x => x.GetNumberOfUsers()).Returns(10);
var sut = new Controller(stub.Object);

Report report = sut.CreateReport();

Assert.Equal(10, report.NumberOfUsers);
// μŠ€ν…μœΌλ‘œ μƒν˜Έ μž‘μš© 검증
stub.Verify(
x => x.GetNumberOfUsers(),
Times.Once
);
}

μ΅œμ’… κ²°κ³Όκ°€ μ•„λ‹Œ 사항을 κ²€μ¦ν•˜λŠ” μ΄λŸ¬ν•œ 관행은 κ³Όμž‰ λͺ…세라고 λΆ€λ₯Έλ‹€.
κ³Όμž‰ λͺ…μ„ΈλŠ” μƒν˜Έ μž‘μš©μ„ 검사할 λ•Œ κ°€μž₯ ν”ν•˜κ²Œ λ°œμƒν•œλ‹€. μŠ€ν…κ³Όμ˜ μƒν˜Έ μž‘μš©μ„ ν™•μΈν•˜λŠ” 것은 μ‰½κ²Œ λ°œκ²¬ν•  수 μžˆλŠ” 결함이닀. ν…ŒμŠ€νŠΈμ™€ μŠ€ν…κ³Όμ˜ μƒν˜Έ μž‘μš©μ„ ν™•μΈν•΄μ„œλŠ” μ•ˆ 되기 λ•Œλ¬Έμ΄λ‹€. λͺ©μ€ 더 λ³΅μž‘ν•˜λ‹€. λͺ©μ„ μ“°λ©΄ 무쑰건 ν…ŒμŠ€νŠΈ 취약성을 μ΄ˆλž˜ν•˜λŠ” 것은 μ•„λ‹ˆμ§€λ§Œ, λŒ€λ‹€μˆ˜κ°€ κ·Έλ ‡λ‹€.

🎈 λͺ©κ³Ό μŠ€ν… ν•¨κ»˜ 쓰기​

λͺ©μ΄μž μŠ€ν…μΈ storeMock
[Fact]
public void Purchase_fails_when_not_enough_inventory() {
var storeMock = new Mock<IStore>();
storeMock.Setup(x => x.HasEnoughInventory(Product.Shampoo, 5)).Returns(false);
var sut = new Customer();

bool success = sut.Purchase(
storeMock.Object, Product.Shampoo, 5
);

Assert.False(success);
storeMock.Verify(
x => x.RemoveInventory(Product.Shampoo, 5),
Times.Never
);
}

이 ν…ŒμŠ€νŠΈλŠ” 두 가지 λͺ©μ μœΌλ‘œ storeMock을 μ‚¬μš©ν•œλ‹€. μ€€λΉ„λœ 응닡을 λ°˜ν™˜ν•˜κ³  SUTμ—μ„œ μˆ˜ν–‰ν•œ λ©”μ„œλ“œ ν˜ΈμΆœμ„ κ²€μ¦ν•œλ‹€. κ·ΈλŸ¬λ‚˜ μ΄λŠ” 두 κ°€μ§€μ˜ μ„œλ‘œ λ‹€λ₯Έ λ©”μ„œλ“œλ‹€. ν…ŒμŠ€νŠΈλŠ” HasEnoughInventory()μ—μ„œ 응닡을 μ„€μ •ν•œ λ‹€μŒ RemoveInventory()에 λŒ€ν•œ ν˜ΈμΆœμ„ κ²€μ¦ν•œλ‹€. λ”°λΌμ„œ μŠ€ν…κ³Όμ˜ μƒν˜Έ μž‘μš©μ„ κ²€μ¦ν•˜μ§€ λ§λΌλŠ” κ·œμΉ™μ„ μ—¬κΈ°μ„œλ„ μœ„λ°°λ˜μ§€ μ•ŠλŠ”λ‹€.

ν…ŒμŠ€νŠΈ λŒ€μ—­μ€ λͺ©μ΄λ©΄μ„œ μŠ€ν…μ΄μ§€λ§Œ, μ—¬μ „νžˆ λͺ©μ΄λΌκ³  λΆ€λ₯΄μ§€ μŠ€ν…μ΄λΌκ³  λΆˆλ¦¬μ§€λŠ” μ•ŠλŠ”λ‹€.

🎈 λͺ©κ³Ό μŠ€ν…μ€ λͺ…λ Ήκ³Ό μ‘°νšŒμ— μ–΄λ–»κ²Œ 관련돼 μžˆλŠ”κ°€?​

λͺ©κ³Ό μŠ€ν…μ˜ κ°œλ…μ€ λͺ…λ Ή 쑰회 뢄리(CQS) 원칙과 관련이 μžˆλ‹€. CQS 원칙에 λ”°λ₯΄λ©΄ λͺ¨λ“  λ©”μ„œλ“œλŠ” λͺ…λ Ήμ΄λ‚˜ μ‘°νšŒμ—¬μ•Ό ν•˜λ©°, 이 λ‘˜μ„ ν˜Όμš©ν•΄μ„œλŠ” μ•ˆ λœλ‹€. λͺ…령은 λΆ€μž‘μš©μ„ μΌμœΌν‚€κ³  μ–΄λ–€ 값도 λ°˜ν™˜ν•˜μ§€ μ•ŠλŠ” λ©”μ„œλ“œλ‹€. λΆ€μž‘μš©μ˜ 예둜 객체 μƒνƒœ λ³€κ²½, 파일 μ‹œμŠ€ν…œ λ‚΄ 파일 λ³€κ²½ 등이 μžˆλ‹€. μ‘°νšŒλŠ” κ·Έ λ°˜λŒ€λ‘œ, λΆ€μž‘μš©μ΄ μ—†κ³  값을 λ°˜ν™˜ν•œλ‹€.

이 원칙을 λ”°λ₯΄κ³ μž ν•  경우, λ©”μ„œλ“œκ°€ λΆ€μž‘μš©μ„ μΌμœΌν‚€λ©΄ ν•΄λ‹Ή λ©”μ„œλ“œμ˜ λ°˜ν™˜ νƒ€μž…μ΄ void인지 ν™•μΈν•˜λΌ. 그리고 λ©”μ„œλ“œκ°€ 값을 λ°˜ν™˜ν•˜λ©΄ λΆ€μž‘μš©μ΄ μ—†μ–΄μ•Ό ν•œλ‹€. λ‹€μ‹œ 말해, μ§ˆλ¬Έμ„ ν•  λ•Œ 닡이 λ‹¬λΌμ Έμ„œλŠ” μ•ˆ λœλ‹€.

λͺ…령을 λŒ€μ²΄ν•˜λŠ” ν…ŒμŠ€νŠΈ λŒ€μ—­μ€ λͺ©μ΄λ‹€. λ§ˆμ°¬κ°€μ§€λ‘œ 쑰회릘 λŒ€μ²΄ν•˜λŠ” ν…ŒμŠ€νŠΈ λŒ€μ—­μ€ μŠ€ν…μ΄λ‹€.

μœ„ 예제의 두 가지 ν…ŒμŠ€νŠΈλ₯Ό λ‹€μ‹œ μ‚΄νŽ΄λ³΄μž

var mock = new Mock<IEmailGateway>();
mock.Verify(x => x.SendGreetingsEmail("user@email.com"));

var stub = new Mock<IDatabase>();
stub.Setup(x => x.GetNumberOfUsers()).Returns(10);

SendGreetingsEmail()은 이메일을 λ³΄λ‚΄λŠ” λΆ€μž‘μš©μ΄ μžˆλŠ” λͺ…령이닀. 이 λͺ…령을 λŒ€μ²΄ν•˜λŠ” ν…ŒμŠ€νŠΈ λŒ€μ—­μ΄ λͺ©μ΄λ‹€. 반면 GetNumberOfUsers()λŠ” 값을 λ°˜ν™˜ν•˜κ³  λ°μ΄ν„°λ² μ΄μŠ€ μƒνƒœλ₯Ό λ³€κ²½ν•˜μ§€ μ•ŠλŠ” μ‘°νšŒλ‹€. ν•΄λ‹Ή ν…ŒμŠ€νŠΈμ˜ γ„·ν…ŒμŠ€νŠΈ λŒ€μ—­μ€ μŠ€ν…μ΄λ‹€.

πŸ₯• 식별할 수 μžˆλŠ” λ™μž‘κ³Ό κ΅¬ν˜„ μ„ΈλΆ€ 사항​

🎈 식별할 수 μžˆλŠ” λ™μž‘μ€ 곡개 API와 λ‹€λ₯΄λ‹€β€‹

λͺ¨λ“  μ œν’ˆ μ½”λ“œλŠ” 2μ°¨μ›μœΌλ‘œ λΆ„λ₯˜ν•  수 μžˆλ‹€.

  • 곡개 API λ˜λŠ” λΉ„κ³΅κ°œ API
  • 식별할 수 μžˆλŠ” λ™μž‘ λ˜λŠ” κ΅¬ν˜„ μ„ΈλΆ€ 사항

식별할 수 μžˆλŠ” λ™μž‘κ³Ό λ‚΄λΆ€ κ΅¬ν˜„ μ„ΈλΆ€ μ‚¬ν•­μ—λŠ” λ―Έλ¬˜ν•œ 차이가 μžˆλ‹€. μ½”λ“œκ°€ μ‹œμŠ€ν…œμ˜ 식별할 수 μžˆλŠ” λ™μž‘μ΄λΌλ©΄ λ‹€μŒ 쀑 ν•˜λ‚˜λ₯Ό ν•΄μ•Ό ν•œλ‹€.

  • ν΄λΌμ΄μ–ΈνŠΈκ°€ λͺ©ν‘œλ₯Ό λ‹¬μ„±ν•˜λŠ” 데 도움이 λ˜λŠ” 연산을 λ…ΈμΆœν•˜λΌ. 연산은 계산을 μˆ˜ν–‰ν•˜κ±°λ‚˜ λΆ€μž‘μš©μ„ μ΄ˆλž˜ν•˜κ±°λ‚˜ λ‘˜ λ‹€ ν•˜λŠ” λ©”μ„œλ“œλ‹€.
  • ν΄λΌμ΄μ–ΈνŠΈκ°€ λͺ©ν‘œλ₯Ό λ‹¬μ„±ν•˜λŠ” 데 도움이 λ˜λŠ” μƒνƒœλ₯Ό λ…ΈμΆœν•˜λΌ. μƒνƒœλŠ” μ‹œμŠ€ν…œμ˜ ν˜„μž¬ μƒνƒœλ‹€.

κ΅¬ν˜„ μ„ΈλΆ€ 사항은 이 두 가지 쀑 아무것도 ν•˜μ§€ μ•ŠλŠ”λ‹€.
μ½”λ“œκ°€ 식별할 수 μžˆλŠ” λ™μž‘μΈμ§€ μ—¬λΆ€λŠ” ν•΄λ‹Ή ν΄λΌμ΄μ–ΈνŠΈκ°€ λˆ„κ΅¬μΈμ§€, 그리고 ν•΄λ‹Ή ν΄λΌμ΄μ–ΈνŠΈμ˜ λͺ©ν‘œκ°€ 무엇인지에 달렀 μžˆλ‹€. 식별할 수 μžˆλŠ” λ™μž‘μ΄ 되렀면 μ½”λ“œκ°€ μ΄λŸ¬ν•œ λͺ©ν‘œ 쀑 ν•˜λ‚˜μ—λΌλ„ 직접적인 관계가 μžˆμ–΄μ•Ό ν•œλ‹€. ν΄λΌμ΄μ–ΈνŠΈλΌλŠ” λ‹¨μ–΄λŠ” μ½”λ“œκ°€ μžˆλŠ” μœ„μΉ˜μ— 따라 λ‹€λ₯Έ 것을 μ˜λ―Έν•  수 μžˆλ‹€.

μ΄μƒμ μœΌλ‘œ μ‹œμŠ€ν…œμ˜ 곡개 APIλŠ” 식별할 수 μžˆλŠ” λ™μž‘κ³Ό μΌμΉ˜ν•΄μ•Ό ν•˜λ©°, λͺ¨λ“  κ΅¬ν˜„ μ„ΈλΆ€ 사항은 ν΄λΌμ΄μ–ΈνŠΈ λˆˆμ— 보이지 μ•Šμ•„μ•Ό ν•œλ‹€. μ΄λŸ¬ν•œ μ‹œμŠ€ν…œμ€ API 섀계가 잘돼 μžˆλ‹€.

🎈 κ΅¬ν˜„ μ„ΈλΆ€ 사항 유좜: μ—°μ‚°μ˜ μ˜ˆβ€‹

κ΅¬ν˜„ μ„ΈλΆ€ 사항이 곡개 API둜 μœ μΆœλ˜λŠ” μ½”λ“œμ˜ 에λ₯Ό μ‚΄νŽ΄λ³΄μž.

κ΅¬ν˜„ μ„ΈλΆ€ 사항을 μœ μΆœν•˜λŠ” User 클라슀
public class User {
public string Name { get; set; }

public string NormalizeName(string name) {
string result = (name ?? "").Trim();

if (result.length > 50)
return result.Substring(0, 50);

return result;
}
}

public class UserController {
public void RenameUser(int userId, string newName) {
User user = GetUserFromDatabase(userId);

string normalizedName = user.NormalizeName(newName);
user.Name = normalizedName;

SaveUserToDatabase(user);
}
}

속성과 λ©”μ„œλ“œκ°€ λ‘˜ λ‹€ κ³΅κ°œλ‹€. λ”°λΌμ„œ 클래슀 APIλ₯Ό 잘 μ„€κ³„ν•˜λ €λ©΄ ν•΄λ‹Ή 멀버가 식별할 수 μžˆλŠ” λ™μž‘μ΄ 되게 ν•΄μ•Ό ν•œλ‹€. 이λ₯Ό μœ„ν•΄μ„œλŠ” λ‹€μŒ 두 가지 쀑 ν•˜λ‚˜λ₯Ό ν•΄μ•Ό ν•œλ‹€.

  • ν΄λΌμ΄μ–ΈνŠΈκ°€ λͺ©ν‘œλ₯Ό λ‹¬μ„±ν•˜λŠ” 데 도움이 λ˜λŠ” 연산을 λ…ΈμΆœν•˜λΌ.
  • ν΄λΌμ΄μ–ΈνŠΈκ°€ λͺ©ν‘œλ₯Ό λ‹¬μ„±ν•˜λŠ” 데 도움이 λ˜λŠ” μƒνƒœλ₯Ό λ…ΈμΆœν•˜λΌ.

Name μ†μ„±λ§Œ 이 μš”κ΅¬ 사항을 μΆ©μ‘±ν•œλ‹€. NormalizeName λ©”μ„œλ“œλ„ μž‘μ—…μ΄μ§€λ§Œ, ν΄λΌμ΄μ–ΈνŠΈμ˜ λͺ©ν‘œμ— μ§κ²°λ˜μ§€ μ•ŠλŠ”λ‹€. NormalizeName은 클래슀의 곡개 API둜 μœ μΆœλ˜λŠ” κ΅¬ν˜„ μ„ΈλΆ€ 사항이.

APIκ°€ 잘 μ„€κ³„λœ User 클래슀
public class User {
private string _name;
public string Name {
get => _name;
set => _name = NormalizeName(value);
}

public string NormalizeName(string name) {
string result = (name ?? "").Trim();

if (result.Length > 50)
return result.Substring(0, 50);

return result;
}
}

public class UserController {
public void RenameUser(int userId, string newName) {
User user = GetUserFromDatabase(userId);
user.Name = newName;
SaveUserToDatabase(user);
}
}

식별할 수 μžˆλŠ” λ™μž‘(Name 속성)만 곡개돼있고, κ΅¬ν˜„ μ„ΈλΆ€ 사ν–₯(NormalizeName λ©”μ„œλ“œ)은 λΉ„κ³΅κ°œ API 뒀에 숨겨져 μžˆλ‹€.

ν΄λž˜μŠ€κ°€ κ΅¬ν˜„ μ„ΈλΆ€ 사항을 μœ μΆœν•˜λŠ”μ§€ νŒλ‹¨ν•˜λŠ” 데 도움이 λ˜λŠ” μœ μš©ν•œ κ·œμΉ™μ΄ μžˆλ‹€. λ‹¨μΌν•œ λͺ©ν‘œλ₯Ό λ‹¬μ„±ν•˜κ³ μž ν΄λž˜μŠ€μ—μ„œ ν˜ΈμΆœν•΄μ•Ό ν•˜λŠ” μ—°μ‚°μ˜ μˆ˜κ°€ 1보닀 크면 ν•΄λ‹Ή ν΄λž˜μŠ€μ—μ„œ κ΅¬ν˜„ μ„ΈλΆ€ 사항을 μœ μΆœν•  κ°€λŠ₯성이 μžˆλ‹€. μ΄μƒμ μœΌλ‘œλŠ” 단일 μ—°μ‚°μœΌλ‘œ κ°œλ³„ λͺ©ν‘œλ₯Ό 달성해야 ν•œλ‹€. 에λ₯Ό λ“€μ–΄ 첫 예제인 UserControllerλŠ” User의 두 가지 μž‘μ—…μ„ μ‚¬μš©ν•΄μ•Ό ν–ˆλ‹€.

string normalizedName = user.NormalizeName(newName);
user.Name = normalizedName;

λ¦¬νŒ©ν„°λ§ 후에 μ—°μ‚° μˆ˜κ°€ 1둜 κ°μ†Œν–ˆλ‹€.

user.Name = newName;

🎈 잘 μ„€κ³„λœ API와 μΊ‘μŠν™”β€‹

κ΅¬ν˜„ μ„ΈλΆ€ 사항을 λ…ΈμΆœν•˜λ©΄ λΆˆλ³€μ„± μœ„λ°˜μ„ κ°€μ Έμ˜¨λ‹€. μ›λž˜ λ²„μ „μ˜ UserλŠ” κ΅¬ν˜„ μ„ΈλΆ€ 사항을 μœ μΆœν•  뿐만 μ•„λ‹ˆλΌ μΊ‘μŠν™”λ₯Ό μ œλŒ€λ‘œ μœ μ§€ν•˜μ§€ λͺ»ν–ˆλ‹€.

κ³„μ†ν•΄μ„œ μ¦κ°€ν•˜λŠ” μ½”λ“œ λ³΅μž‘λ„μ— λŒ€μ²˜ν•  수 μžˆλŠ” 방법은 μ‹€μ§ˆμ μœΌλ‘œ μΊ‘μŠν™” λ§κ³ λŠ” μ—†λ‹€. μ½”λ“œ APIκ°€ ν•΄λ‹Ή μ½”λ“œλ‘œ ν•  수 μžˆλŠ” 것과 ν•  수 μ—†λŠ” 것을 μ•Œλ €μ£Όμ§€ μ•ŠμœΌλ©΄ μ½”λ“œκ°€ 변경됐을 λ•Œ λͺ¨μˆœμ΄ 생기지 μ•Šλ„λ‘ λ§Žμ€ 정보λ₯Ό 염두에 둬야 ν•œλ‹€. μ΄λ ‡κ²Œ ν•˜λŠ” 데 κ°€μž₯ 쒋은 방법은 μΊ‘μŠν™”λ₯Ό μ˜¬λ°”λ₯΄κ²Œ μœ μ§€ν•΄ μ½”λ“œλ² μ΄μŠ€μ—μ„œ 잘λͺ»ν•  수 μžˆλŠ” μ˜΅μ…˜μ‘°μ°¨ μ œκ³΅ν•˜μ§€ μ•Šλ„λ‘ ν•˜λŠ” 것이닀. μΊ‘μŠν™”λŠ” ꢁ극적으둜 λ‹¨μœ„ ν…ŒμŠ€νŠΈμ™€ λ™μΌν•œ λͺ©ν‘œλ₯Ό λ‹¬μ„±ν•œλ‹€. 즉, μ†Œν”„νŠΈμ›¨μ–΄ ν”„λ‘œμ νŠΈμ˜ 지속적인 μ„±μž₯을 κ°€λŠ₯ν•˜κ²Œ ν•˜λŠ” 것이닀.

  • κ΅¬ν˜„ μ„ΈλΆ€ 사항을 숨기면 ν΄λΌμ΄μ–ΈνŠΈμ˜ μ‹œμ•Όμ—μ„œ 클래슀 λ‚΄λΆ€λ₯Ό 가릴 수 있기 λ•Œλ¬Έμ— λ‚΄λΆ€λ₯Ό μ†μƒμ‹œν‚¬ μœ„ν—˜μ΄ 적닀.
  • 데이터와 연산을 κ²°ν•©ν•˜λ©΄ ν•΄λ‹Ή 연산이 클래슀의 λΆˆλ³€μ„±μ„ μœ„λ°˜ν•˜μ§€ μ•Šλ„λ‘ ν•  수 μžˆλ‹€.

🎈 κ΅¬ν˜„ μ„ΈλΆ€ 사항 유좜: μƒνƒœμ˜ μ˜ˆβ€‹

λ‹€μŒ μ˜ˆμ œλŠ” 4μž₯μ—μ„œ λ³Έ MessageRenderer ν΄λž˜μŠ€κ°€ μžˆλ‹€.

public class MessageRenderer : IRenderer {
public IReadOnlyList<IRenderer> SubRenderers { get; }
public MessageRenderer() {
SubRenderers = new List<IRenderer> {
new HeaderRenderer(),
new BodyRenderer(),
new FooterRenderer(),
};
}

public string Render(Message message) {
return SubRenderers
.Select(x => x.Render(message))
.Aggregate("", (str1, str2) => str1 + str2);
}
}

ν•˜μœ„ λ Œλ”λ§ 클래슀 μ»¬λ ‰μ…˜μ΄ κ³΅κ°œλ‹€. κ·ΈλŸ¬λ‚˜ 이 μ»¬λ ‰μ…˜μ΄ 식별할 수 μžˆλŠ” λ™μž‘μΈκ°€? μ•„λ‹ˆλ‹€. ν΄λΌμ΄μ–ΈνŠΈμ—κ²Œ ν•„μš”ν•œ 클래슀 λ©€λ²„λŠ” Render λ©”μ„œλ“œλΏμ΄λ‹€. λ”°λΌμ„œ SubRenderersλŠ” κ΅¬ν˜„ μ„ΈλΆ€ 사항 μœ μΆœμ΄λ‹€.

이 ν…ŒμŠ€νŠΈλŠ” κ΅¬ν˜„ μ„ΈλΆ€ 사항에 결합돼 μžˆμ–΄ 깨지기 쉽고 ν…ŒμŠ€νŠΈ λŒ€μƒμ„ Render λ©”μ„œλ“œλ‘œ λ°”κΏ”μ„œ λΆˆμ•ˆμ •μ„±μ„ ν•΄μ†Œν–ˆλ‹€. 쒋은 λ‹¨μœ„ ν…ŒμŠ€νŠΈμ™€ 잘 μ„€κ³„λœ API μ‚¬μ΄μ—λŠ” 본질적인 관계가 μžˆλ‹€. λͺ¨λ“  κ΅¬ν˜„ μ„ΈλΆ€ 사항을 λΉ„κ³΅κ°œλ‘œ ν•˜λ©΄ ν…ŒμŠ€νŠΈκ°€ 식별할 수 μžˆλŠ” λ™μž‘μ„ κ²€μ¦ν•˜λŠ” 것 μ™Έμ—λŠ” λ‹€λ₯Έ 선택지가 μ—†μœΌλ©°, 이둜 인해 λ¦¬νŒ©ν„°λ§ 내성도 μžλ™μœΌλ‘œ 쒋아진닀.

APIλ₯Ό 잘 μ„€κ³„ν•˜λ©΄ λ‹¨μœ„ ν…ŒμŠ€νŠΈλ„ μžλ™μœΌλ‘œ 쒋아진닀.

잘 μ„€κ³„λœ API의 μ •μ˜μ—μ„œ λΉ„λ‘―λœ 또 λ‹€λ₯Έ μ§€μΉ¨μœΌλ‘œ, μ—°μ‚°κ³Ό μƒνƒœλ₯Ό μ΅œμ†Œν•œμœΌλ‘œ λ…ΈμΆœν•΄μ•Ό ν•œλ‹€. ν΄λΌμ΄μ–ΈνŠΈκ°€ λͺ©ν‘œλ₯Ό λ‹¬μ„±ν•˜λŠ” 데 μ§μ ‘μ μœΌλ‘œ 도움이 λ˜λŠ” μ½”λ“œλ§Œ κ³΅κ°œν•΄μ•Ό ν•˜λ©°, λ‹€λ₯Έ λͺ¨λ“  것은 κ΅¬ν˜„ μ„ΈλΆ€ μ‚¬ν•­μ΄λ―€λ‘œ λΉ„κ³΅κ°œ API 뒀에 μˆ¨κ²¨μ•Ό ν•œλ‹€.

식별할 수 μžˆλŠ” λ™μž‘κ΅¬ν˜„ μ„ΈλΆ€ 사항
κ³΅κ°œμ’‹μŒλ‚˜μ¨
λΉ„κ³΅κ°œν•΄λ‹Ή μ—†μŒμ’‹μŒ

πŸ₯• λͺ©κ³Ό ν…ŒμŠ€νŠΈ μ·¨μ•½μ„± κ°„μ˜ 관계​

🎈 μœ‘κ°ν˜• μ•„ν‚€ν…μ²˜ μ •μ˜β€‹

μ• ν”Œλ¦¬μΌ•μ…˜ μ„œλΉ„μŠ€ 계측은 도메인 계측 μœ„μ— 있으며 μ™ΈλΆ€ ν™˜κ²½κ³Όμ˜ 톡신을 μ‘°μ •ν•œλ‹€. 이 계측은 도메인 ν΄λž˜μŠ€μ™€ ν”„λ‘œμ„ΈμŠ€ μ™ΈλΆ€ μ˜μ‘΄μ„± κ°„μ˜ μž‘μ—…μ„ μ‘°μ •ν•œλ‹€. λ‹€μŒμ€ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ„œλΉ„μŠ€μ— λŒ€ν•œ μ‘°μ •μ˜ μ˜ˆλ”°.

  • λ°μ΄ν„°λ² μ΄μŠ€λ₯Ό μ‘°νšŒν•˜κ³  ν•΄λ‹Ή λ°μ΄ν„°λ‘œ 도메인 클래슀 μΈμŠ€ν„΄μŠ€ ꡬ체화
  • ν•΄λ‹Ή μΈμŠ€ν„΄μŠ€μ— μ—°μ‚° 호좜
  • κ²°κ³Όλ₯Ό λ°μ΄ν„°λ² μ΄μŠ€μ— λ‹€μ‹œ μ €μž₯

μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ„œλΉ„μŠ€ 계측과 도메인 κ³„μΈ΅μ˜ 쑰합은 μœ‘κ°ν˜•μ„ ν˜•μ„±ν•˜λ©°, 이 μœ‘κ°ν˜•μ€ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ λ‚˜νƒ€λ‚Έλ‹€. λ˜ν•œ λ‹€λ₯Έ μ• ν”Œλ¦¬μΌ€μ΄μ…˜κ³Ό μ†Œν†΅ν•  수 있고, λ‹€λ₯Έ μ• ν”Œλ¦¬μΌ€μ΄μ…˜λ„ μœ‘κ°ν˜•μ„ λ‚˜νƒ€λ‚Έλ‹€. μœ‘κ°ν˜• μ•„ν‚€ν…μ²˜μ˜ λͺ©μ μ€ μ„Έ 가지 μ€‘μš”ν•œ 지침을 κ°•μ‘°ν•˜λŠ” 것이닀.

  • 도메인 계측과 μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ„œλΉ„μŠ€ 계측 κ°„μ˜ 관심사 뢄리
  • μ• ν”Œλ¦¬μΌ€μ΄μ…˜ λ‚΄λΆ€ 톡신: μœ‘κ°ν˜• μ•„ν‚€ν…μ²˜λŠ” μ• ν”Œλ¦¬μΌ€μ΄μ…˜ λ²„μ‹œμŠ€ κ³„μΈ΅μ—μ„œ 도메인 κ³„μΈ΅μœΌλ‘œ 흐λ₯΄λŠ” 단방ν–₯ μ˜μ‘΄μ„± 흐름을 κ·œμ •ν•œλ‹€. μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ„œλΉ„μŠ€ 계측과 도메인 계측 간에 관심사λ₯Ό λΆ„λ¦¬ν•˜λŠ” 것은 μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ„œλΉ„μŠ€ 계측이 도메인 계측에 λŒ€ν•΄ μ•„λŠ” 것을 μ˜λ―Έν•˜μ§€λ§Œ, λ°˜λŒ€λŠ” μ•„λ‹ˆλ‹€. 도메인 계측은 μ™ΈλΆ€ ν™˜κ²½μ—μ„œ μ™„μ „νžˆ 격리돼야 ν•œλ‹€.
  • μ• ν”Œλ¦¬μΌ€μ΄μ…˜ κ°„μ˜ 톡신: μ™ΈλΆ€ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ€ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ„œλΉ„μŠ€ 계측에 μžˆλŠ” 곡톡 μΈν„°νŽ˜μ΄μŠ€λ₯Ό 톡해 ν•΄λ‹Ή μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ— μ—°κ²°λœλ‹€. 아무도 도메인 계측에 직접 μ ‘κ·Όν•  수 μ—†λ‹€. μœ‘κ°ν˜•μ˜ 각 면은 μ• ν”Œλ¦¬μΌ€μ΄μ…˜ λ‚΄μ™ΈλΆ€ 연결을 λ‚˜νƒ€λ‚Έλ‹€.

식별할 수 μžˆλŠ” λ™μž‘μ€ λ°”κΉ₯ κ³„μΈ΅μ—μ„œ μ•ˆμͺ½μœΌλ‘œ 흐λ₯Έλ‹€. μ™ΈλΆ€ ν΄λΌμ΄μ–ΈνŠΈμ—κ²Œ μ€‘μš”ν•œ λͺ©ν‘œλŠ” κ°œλ³„ 도메인 ν΄λž˜μŠ€μ—μ„œ λ‹¬μ„±ν•œ ν•˜μœ„ λͺ©ν‘œλ‘œ λ³€ν™˜λœλ‹€. 도메인 클래슀의 경우 ν΄λΌμ΄μ–ΈνŠΈλŠ” μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ„œλΉ„μŠ€μ— ν•΄λ‹Ήν•˜κ³ , μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ„œλΉ„μŠ€λ©΄ μ™ΈλΆ€ ν΄λΌμ΄μ–ΈνŠΈμ— ν•΄λ‹Ήν•œλ‹€.

🎈 μ‹œμŠ€ν…œ λ‚΄λΆ€ 톡신과 μ‹œμŠ€ν…œ κ°„ 톡신​

일반적인 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ—λŠ” μ‹œμŠ€ν…œ λ‚΄λΆ€ 톡신과 μ‹œμŠ€ν…œ κ°„ 톡신이 μžˆλ‹€. μ‹œμŠ€ν…œ λ‚΄λΆ€ 톡신은 μ• ν”Œλ¦¬μΌ€μ΄μ…˜ λ‚΄ 클래슀 κ°„μ˜ 톡신이닀. μ‹œμŠ€ν…œ κ°„ 톡신은 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ λ‹€λ₯Έ μ• ν”Œλ¦¬μΌ€μ΄μ…˜κ³Ό ν†΅μ‹ ν•˜λŠ” 것을 λ§ν•œλ‹€.

μ‹œμŠ€ν…œ λ‚΄λΆ€ 톡신은 κ΅¬ν˜„ μ„ΈλΆ€ 사항이고, μ‹œμŠ€ν…œ κ°„ 톡신은 그렇지 μ•Šλ‹€.

μ‹œμŠ€ν…œ κ°„ ν†΅μ‹ μ˜ νŠΉμ„±μ€ 별도 μ• ν”Œλ¦¬μΌ€μ΄μ…˜κ³Ό ν•¨κ»˜ μ„±μž₯ν•˜λŠ” λ°©μ‹μ—μ„œ λΉ„λ‘―λœλ‹€. μ„±μž₯의 μ£Όμš” 원칙 쀑 ν•˜λ‚˜λ‘œ ν•˜μœ„ ν˜Έν™˜μ„±μ„ μ§€ν‚€λŠ” 것이닀. μ‹œμŠ€ν…œ λ‚΄λΆ€μ—μ„œ ν•˜λŠ” λ¦¬νŒ©ν„°λ§κ³Ό λ‹€λ₯΄κ²Œ, μ™ΈλΆ€ μ• ν”Œλ¦¬μΌ€μ΄μ…˜κ³Ό 톡신할 λ•Œ μ‚¬μš©ν•˜λŠ” 톡신 νŒ¨ν„΄μ€ 항상 μ™ΈλΆ€ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ 이해할 수 μžˆλ„λ‘ μœ μ§€ν•΄μ•Ό ν•œλ‹€.

λͺ©μ„ μ‚¬μš©ν•˜λ©΄ μ‹œμŠ€ν…œκ³Ό μ™ΈλΆ€ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ κ°„μ˜ 톡신 νŒ¨ν„΄μ„ 확인할 λ•Œ μ’‹λ‹€. λ°˜λŒ€λ‘œ μ‹œμŠ€ν…œ λ‚΄ 클래슀 κ°„μ˜ 톡신을 κ²€μ¦ν•˜λŠ” 데 μ‚¬μš©ν•˜λ©΄ ν…ŒμŠ€νŠΈ κ΅¬ν˜„ μ„ΈλΆ€ 사항과 κ²°ν•©λ˜λ©°, 그에 따라 λ¦¬νŒ©ν„°λ§ λ‚΄μ„± μ§€ν‘œκ°€ 미흑해진닀.

🎈 μ‹œμŠ€ν…œ λ‚΄λΆ€ 톡신과 μ‹œμŠ€ν…œ κ°„ ν†΅μ‹ μ˜ μ˜ˆβ€‹

CustomerController ν΄λž˜μŠ€λŠ” 도메인 ν΄λž˜μŠ€μ™€ μ™ΈλΆ€ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ κ°„μ˜ μž‘μ—…μ„ μ‘°μ •ν•˜λŠ” μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ„œλΉ„μŠ€λ‹€.

μ™ΈλΆ€ μ• ν”Œλ¦¬μΌ€μ΄μ…˜κ³Ό 도메인 λͺ¨λΈ μ—°κ²°ν•˜κΈ°
public class CustomerController {
public bool Purchase(int customerId, int productId, int quantity) {
Customer customer = _customerRepository.GetById(customerId);
Product product = _productRepository.GetById(productId);

bool isSuccess = customer.Purchase(
_mainStore, product, quantity
);

if (isSuccess) {
_emailGateway.SendReceipt(
customer.Email, product.Name, quantity
);
}

return isSuccess;
}
}

κ΅¬λ§€λΌλŠ” λ™μž‘μ€ μ‹œμŠ€ν…œ λ‚΄λΆ€ 톡신과 μ‹œμŠ€ν…œ κ°„ 톡신이 λͺ¨λ‘ μžˆλŠ” λΉ„μ¦ˆλ‹ˆμŠ€ μœ μŠ€μΌ€μ΄μŠ€λ‹€. μ‹œμŠ€ν…œ κ°„ 톡신은 CustomerController μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ„œλΉ„μŠ€μ™€ 두 개의 μ™ΈλΆ€ μ‹œμŠ€ν…œμΈ μ„œλ“œνŒŒν‹° μ• ν”Œλ¦¬μΌ€μ΄μ…˜κ³Ό 이메일 κ²Œμ΄νŠΈμ›¨μ΄ κ°„μ˜ 톡신이닀. μ‹œμŠ€ν…œ λ‚΄λΆ€ 톡신은 Customer와 Store 도메인 클래슀 κ°„μ˜ 톡신이닀.

λ‹€μŒ μ˜ˆμ œλŠ” λͺ©μ„ μ‚¬μš©ν•˜λŠ” νƒ€λ‹Ήν•œ 예λ₯Ό 보여쀀닀.

μ·¨μ•½ν•œ ν…ŒμŠ€νŠΈλ‘œ 이어지지 μ•ŠλŠ” λͺ© μ‚¬μš©
[Fact]
public void Successful_purchase() {
var mock = new Mock<IEmailGateway>();
var sut = new CustomerController(mock.Object);

bool isSuccess = sut.Purchase(
customerId: 1, productId: 2, quantity: 5
);

Assert.True(isSuccess);
mock.Verify(
x => x.SendReceipt(
"customer@email.com", "Shampoo", 5
), Times.Once
);
}

isSuccess ν”Œλž˜κ·ΈλŠ” μ™ΈλΆ€ ν΄λΌμ΄μ–ΈνŠΈμ—μ„œλ„ 확인할 수 있으며, 검증도 ν•„μš”ν•˜λ‹€. ν•˜μ§€λ§Œ 이 ν”Œλž˜κ·ΈλŠ” λͺ©μ΄ ν•„μš” μ—†κ³ , κ°„λ‹¨ν•œ κ°’ λΉ„κ΅λ§ŒμœΌλ‘œ μΆ©λΆ„ν•˜λ‹€.

이제 Customer ν΄λž˜μŠ€μ™€ Store 클래슀 κ°„μ˜ 톡신에 λͺ©μ„ μ‚¬μš©ν•œ ν…ŒμŠ€νŠΈλ₯Ό μ‚΄νŽ΄λ³΄μž.

μ·¨μ•½ν•œ ν…ŒμŠ€νŠΈλ‘œ μ΄μ–΄μ§€λŠ” λͺ© μ‚¬μš©
[Fact]
public void Purchase_succeeds_when_enough_inventory() {
var storeMock = new Mock<IStore>();
storeMock
.Setup(x => x.HasEnoughInventory(Product.Shampoo, 5))
.Returns(true);
var customer = new Customer();

bool success = customer.Purchase(
storeMock.Object, Product.Shampoo, 5
);

Assert.True(success);
storeMock.Verify(
x => x.RemoveInventory(Product.Shampoo, 5),
Times,Once
);
}

Customer ν΄λž˜μŠ€μ—μ„œ Store 클래슀둜의 λ©”μ„œλ“œ ν˜ΈμΆœμ€ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 경계λ₯Ό λ„˜μ§€ μ•ŠλŠ”λ‹€. ν˜ΈμΆœμžμ™€ μˆ˜μ‹ μž λͺ¨λ‘ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 내에 μžˆλ‹€. λ˜ν•œ 이 λ©”μ„œλ“œλŠ” ν΄λΌμ΄μ–ΈνŠΈκ°€ λͺ©ν‘œλ₯Ό λ‹¬μ„±ν•˜λŠ” 데 도움이 λ˜λŠ” μ—°μ‚°μ΄λ‚˜ μƒνƒœκ°€ μ•„λ‹ˆλ‹€. 이 두 도메인 클래슀의 ν΄λΌμ΄μ–ΈνŠΈλŠ” ꡬ맀λ₯Ό λͺ©ν‘œλ‘œ ν•˜λŠ” CustomerControllerλ‹€. 이 λͺ©ν‘œμ— 직접적인 관련이 μžˆλŠ” λ©€λ²„λŠ” customer.Purchase()와 store.GetInventory() μ΄λ ‡κ²Œ λ‘˜λΏμ΄λ‹€.

πŸ₯• λ‹¨μœ„ ν…ŒμŠ€νŠΈμ˜ κ³ μ „νŒŒμ™€ 런던파 μž¬κ³ β€‹

λŸ³λ˜νŒŒλŠ” λΆˆλ³€ μ˜μ‘΄μ„±μ„ μ œμ™Έν•œ λͺ¨λ“  μ˜μ‘΄μ„±μ— λͺ© μ‚¬μš©μ„ ꢌμž₯ν•˜λ©° μ‹œμŠ€ν…œ λ‚΄ 톡신과 μ‹œμŠ€ν…œ κ°„ 톡신을 κ΅¬λΆ„ν•˜μ§€ μ•ŠλŠ”λ‹€. κ·Έ κ²°κ³Ό, ν…ŒμŠ€νŠΈλŠ” μ• ν”Œλ¦¬μΌ€μ΄μ…˜κ³Ό μ™ΈλΆ€ μ‹œμŠ€ν…œ κ°„μ˜ 톡신을 ν™•μΈν•˜λŠ” κ²ƒμ²˜λŸΌ 클래슀 κ°„ 톡신도 ν™•μΈν•œλ‹€.

런던파λ₯Ό 따라 λͺ©μ„ λ¬΄λΆ„λ³„ν•˜κ²Œ μ‚¬μš©ν•˜λ©΄ μ’…μ’… κ΅¬ν˜„ μ„ΈλΆ€ 사항에 결합돼 ν…ŒμŠ€νŠΈμ— λ¦¬νŒ©ν„°λ§ 내성이 μ—†κ²Œ λœλ‹€. λ¦¬νŒ©ν„°λ§ 내성이 μ €ν•˜λ˜λ©΄ ν…ŒμŠ€νŠΈλŠ” κ°€μΉ˜κ°€ 없어진닀.

κ³ μ „νŒŒλŠ” ν…ŒμŠ€νŠΈ 간에 κ³΅μœ ν•˜λŠ” μ˜μ‘΄μ„±λ§Œ κ΅μ²΄ν•˜μžκ³  ν•˜λ―€λ‘œ 이 λ¬Έμ œμ— 훨씬 μœ λ¦¬ν•˜λ‹€. κ·ΈλŸ¬λ‚˜ κ³ μ „νŒŒ μ—­μ‹œ μ‹œμŠ€ν…œ κ°„ 톡신에 λŒ€ν•œ μ²˜λ¦¬μ— μ΄μƒμ μ΄μ§€λŠ” μ•Šλ‹€. λŸ°λ˜νŒŒλ§ŒνΌμ€ μ•„λ‹ˆμ§€λ§Œ, κ³ μ „νŒŒλ„ λͺ© μ‚¬μš©μ„ μ§€λ‚˜μΉ˜κ²Œ μž₯λ €ν•œλ‹€.

🎈 λͺ¨λ“  ν”„λ‘œμ„ΈμŠ€ μ™ΈλΆ€ μ˜μ‘΄μ„±μ„ λͺ©μœΌλ‘œ ν•΄μ•Ό ν•˜λŠ” 것은 μ•„λ‹ˆλ‹€β€‹

λ‹€μŒμ€ μ˜μ‘΄μ„± μœ ν˜•μ΄λ‹€.

  • 곡유 μ˜μ‘΄μ„±: ν…ŒμŠ€νŠΈ 간에 κ³΅μœ ν•˜λŠ” μ˜μ‘΄μ„±
  • ν”„λ‘œμ„ΈμŠ€ μ™ΈλΆ€ μ˜μ‘΄μ„±: ν”„λ‘œκ·Έλž¨μ˜ μ‹€ν–‰ ν”„λ‘œμ„ΈμŠ€ 외에 λ‹€λ₯Έ ν”„λ‘œμ„ΈμŠ€λ₯Ό μ μœ ν•˜λŠ” μ˜μ‘΄μ„±(예λ₯Ό λ“€μ–΄ λ°μ΄ν„°λ² μ΄μŠ€, λ©”μ‹œμ§€ λ²„μŠ€, SMTP μ„œλΉ„μŠ€ λ“±)
  • λΉ„κ³΅κ°œ μ˜μ‘΄μ„±: κ³΅μœ ν•˜μ§€ μ•ŠλŠ” λͺ¨λ“  μ˜μ‘΄μ„±

κ³ μ „νŒŒμ—μ„œλŠ” 곡유 μ˜μ‘΄μ„±μ„ ν”Όν•  것을 κΆŒκ³ ν•œλ‹€. ν…ŒμŠ€νŠΈκ°€ μ‹€ν–‰ μ»¨ν…μŠ€νŠΈλ₯Ό μ„œλ‘œ λ°©ν•΄ν•˜κ³ , κ²°κ΅­ 병렬 처리λ₯Ό ν•  수 μ—†κΈ° λ•Œλ¬Έμ΄λ‹€. ν…ŒμŠ€νŠΈλ₯Ό 병렬적, 순차적 λ˜λŠ” μž„μ˜μ˜ μˆœμ„œλ‘œ μ‹€ν–‰ν•  수 μžˆλŠ” 것을 ν…ŒμŠ€νŠΈ 격리라고 λΆ€λ₯Έλ‹€. μ΄λŸ¬ν•œ μ˜μ‘΄μ„±μ„ ν…ŒμŠ€νŠΈ λŒ€μ—­, 즉 λͺ©κ³Ό μŠ€ν…μœΌλ‘œ κ΅μ²΄ν•˜λŠ” 것이닀.

κ·ΈλŸ¬λ‚˜ λͺ¨λ“  ν”„λ‘œμ„ΈμŠ€ μ™ΈλΆ€ μ˜μ‘΄μ„±μ„ λͺ©μœΌλ‘œ ν•΄μ•Ό ν•˜λŠ” 것은 μ•„λ‹ˆλ‹€. ν”„λ‘œμ„ΈμŠ€ μ™ΈλΆ€ μ˜μ‘΄μ„±μ΄ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ ν†΅ν•΄μ„œλ§Œ μ ‘κ·Όν•  수 있으면, μ΄λŸ¬ν•œ μ˜μ‘΄μ„±κ³Όμ˜ 톡신은 μ‹œμŠ€ν…œμ—μ„œ 식별할 수 μžˆλŠ” λ™μž‘μ΄ μ•„λ‹ˆλ‹€. μ‹€μ œλ‘œ μ™ΈλΆ€μ—μ„œ κ΄€μ°°ν•  수 μ—†λŠ” ν”„λ‘œμ„ΈμŠ€ μ™ΈλΆ€ μ˜μ‘΄μ„±μ€ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ μΌλΆ€λ‘œ μž‘μš©ν•œλ‹€.

μ™„μ „νžˆ ν†΅μ œκΆŒμ„ 가진 ν”„λ‘œμ„ΈμŠ€ μ™ΈλΆ€ μ˜μ‘΄μ„±μ— λͺ©μ„ μ‚¬μš©ν•˜λ©΄ 깨지기 μ‰¬μš΄ ν…ŒμŠ€νŠΈλ‘œ 이어진닀. λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ ν…Œμ΄λΈ”μ„ λΆ„ν• ν•˜κ±°λ‚˜ μ €μž₯ ν”„λ‘œμ‹œμ €μ—μ„œ λ§€κ°œλ³€μˆ˜ νƒ€μž…μ„ λ³€κ²½ν•  λ•Œλ§ˆλ‹€ ν…ŒμŠ€νŠΈκ°€ 빨간색이 λ˜λŠ” 것을 아무도 μ›ν•˜μ§€ μ•ŠλŠ”λ‹€. λ°μ΄ν„°λ² μ΄μŠ€μ™€ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ€ ν•˜λ‚˜μ˜ μ‹œμŠ€ν…œμœΌλ‘œ μ·¨κΈ‰ν•΄μ•Ό ν•œλ‹€.

🎈 λͺ©μ„ μ‚¬μš©ν•œ λ™μž‘ 검증​

μ’…μ’… λͺ©μ΄ λ™μž‘μ„ κ²€μ¦ν•œλ‹€κ³  ν•œλ‹€. ν•˜μ§€λ§Œ λŒ€λΆ€λΆ„μ˜ 경우 그렇지 μ•Šλ‹€. λͺ©ν‘œλ₯Ό λ‹¬μ„±ν•˜κ³ μž 각 κ°œλ³„ ν΄λž˜μŠ€κ°€ 이웃 ν΄λž˜μŠ€μ™€ μ†Œν†΅ν•˜λŠ” 방식은 식별할 수 μžˆλŠ” λ™μž‘κ³ΌλŠ” μ•„λ¬΄λŸ° 관계가 μ—†λ‹€. (μ΄λŠ” κ΅¬ν˜„ μ„ΈλΆ€ 사항이닀.)

λͺ©μ€ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ 경계λ₯Ό λ„˜λ‚˜λ“œλŠ” μƒν˜Έ μž‘μš©μ„ 검증할 λ•Œμ™€ μ΄λŸ¬ν•œ μƒν˜Έ μž‘μš©μ˜ λΆ€μž‘μš©μ΄ μ™ΈλΆ€ ν™˜κ²½μ—μ„œ 보일 λ•Œλ§Œ λ™μž‘κ³Ό 관련이 μžˆλ‹€.