ν μ€νΈ μμ±νλ€ λ³΄λ©΄ μΈλΆ μμΈμ΄ νμν μμ μ΄ μλ€.
public class AutoDebitRegisterTest {
private AutoDebitRegister register;
@BeforeEach
void setUp() {
CardNumberValidator validator = new CardNumberValidator();
AutoDebitInfoRepository repository = new JpaAutoDebitInfoRepository();
register = new AutoDebitRegister(validator, repository);
}
@Test
void validCard() {
// μ
체μμ λ°μ ν
μ€νΈμ© μ ν¨ν μΉ΄λ λ²νΈ μ¬μ©
AutoDebitReq req = new AutoDebitReq("user1", "1234123412341234");
RegisterResult result = this.register.register(req);
assertEquals(VALID, result.getValidity());
}
@Test
void theftCard() {
// μ
체μμ λ°μ λλ ν
μ€νΈμ© μΉ΄λ λ²νΈ μ¬μ©
AutoDebitReq req = new AutoDebitReq("user1", "1234567890123456");
RegisterResult result = this.register.register(req);
assertEquals(THEFT, result.getValidity());
}
}
μ΄λ κ² ν μ€νΈ λμμμ μμ‘΄νλ μμΈ λλ¬Έμ ν μ€νΈκ° μ΄λ €μΈ λλ λμμ μ¨μ ν μ€νΈλ₯Ό μ§νν μ μλ€.
public class StubCardNumberValidator extends CardNumberValidator {
private String invalidNo;
private String theftNo;
public void setInvalidNo(String invalidNo) {
this.invalidNo = invalidNo;
}
public void setTheftNo(String theftNo) {
this.theftNo = theftNo;
}
@Override
public CardValidity validate(String cardNumber) {
if (invalidNo != null && invalidNo.equals(cardNumber)) {
return CardValidity.INVALID;
}
if (theftNo != null && theftNo.equals(cardNumber)) {
return CardValidity.THEFT;
}
return CardValidity.VALID;
}
}
μ΄μ²λΌ λ¨μν ꡬνμΌλ‘ μ€μ ꡬνμ λ체νλ€.
public class AutoDebitRegister_Stub_Test {
private AutoDebitRegister register;
private StubCardNumberValidator stubValidator;
private StubAutoDebitInfoRepository stubRepository;
@BeforeEach
void setUp() {
stubValidator = new StubCardNumberValidator();
stubRepository = new StubAutoDebitInfoRepository();
register = new AutoDebitRegister(stubValidator, stubRepository);
}
@Test
void invalidCard() {
stubValidator.setInvalidNo("111122223333");
AutoDebitReq req = new AutoDebitReq("user1", "111122223333");
RegisterResult result = this.register.register(req);
assertEquals(INVALID, result.getValidity());
}
@Test
void theftCard() {
stubValidator.setTheftNo("1234567890123456");
AutoDebitReq req = new AutoDebitReq("user1", "1234567890123456");
RegisterResult result = this.register.register(req);
assertEquals(CardValidity.THEFT, result.getValidity());
}
@Test
void validCard() {
AutoDebitReq req = new AutoDebitReq("user1", "1234123412341234");
RegisterResult result = this.register.register(req);
assertEquals(VALID, result.getValidity());
}
}
λμμ μ¬μ©νλ©΄ DB μμ΄ AutoDebitRegisterλ₯Ό ν μ€νΈν μ μλ€.
AutoDebitInfoRepository λμ ꡬν
public class MemoryAutoDebitInfoRepository implements AutoDebitInfoRepository {
private Map<String, AutoDebitInfo> infos = new HashMap<>();
@Override
public void save(AutoDebitInfo info) {
infos.put(info.getUserId(), info);
}
@Override
public AutoDebitInfo findOne(String userId) {
return infos.get(userId);
}
}
MemoryAutoDebitInfoRepository λ₯Ό μ΄μ©ν ν μ€νΈ
public class AutoDebitRegister_Fake_Test {
private AutoDebitRegister register;
private StubCardNumberValidator cardNumberValidator;
private MemoryAutoDebitInfoRepository repository;
@BeforeEach
void setUp() {
cardNumberValidator = new StubCardNumberValidator();
repository = new MemoryAutoDebitInfoRepository();
register = new AutoDebitRegister(cardNumberValidator, repository);
}
@Test
void alreadyRegistered_InfoUpdated() {
repository.save(
new AutoDebitInfo("user1", "111222333444", LocalDateTime.now()));
AutoDebitReq req = new AutoDebitReq("user1", "123456789012");
RegisterResult result = this.register.register(req);
AutoDebitInfo saved = repository.findOne("user1");
assertEquals("123456789012", saved.getCardNumber());
}
@Test
void notYetRegistered_newInfoRegistered() {
AutoDebitReq req = new AutoDebitReq("user1", "1234123412341234");
RegisterResult result = this.register.register(req);
AutoDebitInfo saved = repository.findOne("user1");
assertEquals("1234123412341234", saved.getCardNumber());
}
}
λμ μ’ λ₯ | μ€λͺ |
---|---|
Stub | ꡬνμ λ¨μν κ²μΌλ‘ λ체νλ€. ν μ€νΈμ λ§κ² λ¨μν μνλ λμμ μννλ€. μ) StubCardNumberValidator |
Fake | μ νμλ μ ν©νμ§ μμ§λ§, μ€μ λμνλ ꡬνμ μ 곡νλ€. DB λμ μ λ©λͺ¨λ¦¬λ₯Ό μ΄μ©ν΄μ ꡬνν MemoryAutoDebitInfoRepositoryκ° κ°μ§ λμμ ν΄λΉνλ€. |
Spy | νΈμΆλ λ΄μμ κΈ°λ‘νλ€. κΈ°λ‘ν λ΄μ©μ ν μ€νΈ κ²°κ³Όλ₯Ό κ²μ¦ν λ μ¬μ©νλ€. μ€ν μ΄κΈ°λ νλ€. |
Mock | κΈ°λν λλ‘ μνΈμμ©νλμ§ νμλ₯Ό κ²μ¦νλ€. κΈ°λν λλ‘ λμνμ§ μμΌλ©΄ μ΅μ μ μ λ°μν μ μλ€. λͺ¨μ κ°μ²΄λ μ€ν μ΄μ μ€νμ΄λ λλ€. |
κ° νμ μ μν
ꡬννκΈ° μ μ μ€κ³
λ¨μ κΈ°λ₯μ ꡬννκΈ°μ μμ μ΄λ€ κ΅¬μ± μμκ° νμν μ§ κ³ λ―Όνλ κ²μ μμ‘΄ λμμ λμΆν λ λμμ΄ λλ€.
μ½ν μνΈ μΈμ§ μ¬λΆλ₯Ό μλ €μ£ΌκΈ°λ§ νλ©΄ λλ―λ‘ μ€ν λμμ μ¬μ©νλ€.
ν μ€νΈ 골격
λ°©λ²
κ²μ¦νκΈ° μν ν μ€νΈ μ½λ 골격
μ΄λ©μΌ λ°μ‘ μ¬λΆλ₯Ό μ΄λ»κ² νμΈ λ°©λ²
μ€νμ΄ κ΅¬ν
package com.example.tdd.chap07.user;
public class SpyEmailNotifier implements EmailNotifier {
private boolean called;
private String email;
public boolean isCalled() {
return called;
}
public String getEmail() {
return email;
}
}
μ΄ λ¨μΈμ ν΅κ³Όνλ €λ©΄ λ€μ λκ°μ§λ₯Ό ν΄μΌνλ€
// UserRegister
public void register(String id, String pw, String email) {
if (passwordChecker.checkPasswordWeak(pw)) {
throw new WeakPasswordException();
}
User user = userRepository.findById(id);
if (user != null) {
throw new DupIdException();
}
userRepository.save(new User(id, pw, email));
emailNotifier.sendRegisterEmail(email);
}
// SpyEmailNotifier
@Override
public void sendRegisterEmail(String email) {
this.called = true;
this.email = email;
}
dependencies {
// mockito μμ‘΄ μ€μ
testImplementation('org.mockito:mockito-core:2.26.0')
}
Mockito.mock()
λ©μλλ₯Ό μ΄μ©νλ©΄ νΉμ νμ
μ λͺ¨μ κ°μ²΄λ₯Ό μμ±ν μ μλ€.
Mockito.mock()
λ©μλλ ν΄λμ€, μΈν°νμ΄μ€, μΆμ ν΄λμ€μ λν΄ λͺ¨μ κ°μ²΄λ₯Ό μμ±ν μ μλ€.
@Test
void mockStubTest() {
// GameNumGen νμ
μ λͺ¨μ κ°μ²΄ μμ±
GameNumGen genMock = **mock**(GameNumGen.class);
}
public interface GameNumGen {
String generate(GameLevel level);
}
λͺ¨μ κ°μ²΄λ₯Ό μμ±ν λ€μλ BDDMockito
ν΄λμ€λ₯Ό μ΄μ©ν΄μ λͺ¨μ κ°μ²΄μ μ€ν
μ ꡬμ±ν μ μλ€.
BDDMockito.given
import static org.mockito.BDDMockito.given;
@Test
void mockStubTest() {
// Mock μμ±
GameNumGen genMock = mock(GameNumGen.class);
// Stub ꡬμ±
**given(genMock.generate(GameLevel.EASY)).willReturn("123");**
// Stub μ€μ μ λ§€μΉλλ λ©μλ μ€ν
String num = **genMock.generate(GameLevel.EASY)**;
assertEquals("123", num);
}
@Test
void mockThrowTest() {
GameNumGen genMock = mock(GameNumGen.class);
**given(genMock.generate(null)).willThrow(new IllegalArgumentException());**
assertThrows(
IllegalArgumentException.class,
() -> **genMock.generate(null)**);
}
BDDMockito.givenμ μ΄μ©νλ©΄ λͺ¨μ κ°μ²΄μ λ©μλκ° νΉμ κ°μ 리ν΄νλλ‘ μ€μ ν μ μλ€.
willReturn()
λ©μλλ μ€ν
μ μ μν λ©μλκ° λ¦¬ν΄ν κ°μ μ§μ νλ€.willThrow()
λ©μλλ μ΅μ
μ
μ λ°μνκ² μ€μ ν μ μλ€.λ¦¬ν΄ νμ μ΄ voidμΈ λ©μλμ λν΄ μ΅μ μ μ λ°μμν€λ λ°©λ²
public class VoidMethodStubTest {
@Test
void voidMethodWillThrowTest() {
List<String> mockList = mock(List.class);
**willThrow**(UnsupportedOperationException.class)
.**given**(mockList)
.clear();
assertThrows(
UnsupportedOperationException.class,
() -> mockList.clear()
);
}
}
given()
λ©μλλ μΈμλ‘ μ λ¬λ°μ λͺ¨μ κ°μ²΄ μμ μ 리ν΄νλλ° μ΄λ μ΅μ
μ
μ λ°μν λ©μλλ₯Ό νΈμΆνλ€.Mockitoλ μΌμΉνλ μ€ν
μ€μ μ΄ μμ κ²½μ° λ¦¬ν΄ νμ
μ κΈ°λ³Έ κ°μ 리ν΄νλ€.
μλ₯Ό λ€μ΄ λ¦¬ν΄ νμ
μ΄ intλ©΄ 0μ 리ν΄νκ³ , booleanμ΄λ©΄ falseλ₯Ό 리ν΄νλ€. κΈ°λ³Έ λ°μ΄ν° νμ
μ΄ μλ Stringμ΄λ Listμ κ°μ μ°Έμ‘° νμ
μ΄λ©΄ nullμ 리ν΄νλ€.
ArgumentMatchers.any()
λ©μλλ λͺ¨λ κ°μ μΌμΉνλλ‘ μ€ν
μ μ€μ νλ€.μ€ν
μ μ€μ ν λ©μλμ μΈμκ° λ κ° μ΄μμΈ κ²½μ° μ£Όμμ
Mockitoλ ν μΈμλΌλ ArgumentMatcherλ₯Ό μ¬μ©ν κ²½μ° λͺ¨λ μΈμλ₯Ό ArgumentMatcherλ₯Ό μ΄μ©ν΄μ μ€μ νλλ‘ νκ³ μλ€.
λͺ¨μ κ°μ²΄μ νμλ₯Ό κ²μ¦νλ€.
λͺ¨μ κ°μ²΄ genMockμ generate() λ©μλκ° GameLevel.EASY μΈμλ₯Ό μ¬μ©ν΄μ νΈμΆλμλμ§ κ²μ¦νλ€.
public class GameTest {
@Test
void init() {
GameNumGen genMock = mock(GameNumGen.class);
Game game = new Game(genMock);
game.init(GameLevel.EASY);
**then**(genMock).**should**().generate(GameLevel.EASY);
// **then**(genMock).**should**(only()).generate(GameLevel.EASY);
}
}
BDDMockito.then()
μ λ©μλ νΈμΆ μ¬λΆλ₯Ό κ²μ¦ν λͺ¨μ κ°μ²΄λ₯Ό μ λ¬λ°λλ€. should()
λ©μλλ λͺ¨μ κ°μ²΄μ λ©μλκ° λΆλ €μΌ νλ€λ κ²μ μ€μ νκ³ μ€μ λΆλ €μΌ ν λ©μλλ₯Ό μ§μ νλ€.
λ©μλ νΈμΆ νμλ₯Ό κ²μ¦ν μ μλ€.
λ¨μ ν
μ€νΈλ₯Ό μ€ννλ€λ³΄λ©΄ λͺ¨μ κ°μ²΄λ₯Ό νΈμΆν λ μ¬μ©ν μΈμλ₯Ό κ²μ¦ν΄μΌ ν λκ° μλ€.
Mockitoμ ArgumentCaptor
λ₯Ό μ¬μ©νλ©΄ λ©μλ νΈμΆ μ¬λΆλ₯Ό κ²μ¦νλ κ³Όμ μμ μ€μ νΈμΆν λ μ λ¬ν μΈμλ₯Ό 보κ΄ν μ μλ€.
@DisplayName("κ°μ
νλ©΄ λ©μΌμ μ μ‘ν¨")
@Test
void whenRegisterThenSendMail() {
userRegister.register("id", "pw", "email@email.com");
**ArgumentCaptor**<String> captor = **ArgumentCaptor.forClass(String.class);**
BDDMockito.then(mockEmailNotifier).should()
.sendRegisterEmail(**captor.capture()**);
String realEmail = **captor.getValue()**;
assertEquals("email@email.com", realEmail);
}
BDDMockito.then(mockEmailNotifier).should().sendRegisterEmail(**captor.capture()**);
: λͺ¨μ κ°μ²΄ νΈμΆ μ¬λΆλ₯Ό κ²μ¦νλ μ½λμμ μΈμλ‘ μ λ¬νλ€. μΈμλ‘ μ λ¬ν λλ ArgumentCaptor#captor()
λ©μλλ₯Ό μ λ¬νλ€.mockito-junit-jupiter μμ‘΄μ μΆκ°νλ©΄ μ λ
Έν
μ΄μ
μ μ΄μ©ν΄μ λͺ¨μ κ°μ²΄λ₯Ό μμ±ν μ μλ€.
@Mock μ λ
Έν
μ΄μ
μ λΆμΈ νλμ λν΄ μλμΌλ‘ λͺ¨μ κ°μ²΄λ₯Ό μμ±ν΄μ€λ€.
@ExtendWith(MockitoExtension.class)
public class JUnit5ExtensionTest {
@Mock
private GameNumGen genMock;
}
μ μ΄νκΈ° νλ μΈλΆ μν©μ΄ μ‘΄μ¬νλ©΄ λμμΌλ‘ λμ ν μ μλ€
λΉμ₯ ꡬννλλ° μκ°μ΄ 걸리λ λ‘μ§λ λΆλ¦¬νκΈ°μ μ’μ ν보μ΄λ€.
λμμ μ¬μ©νμ§ μκ³ μ€μ ꡬνμ μ¬μ©νλ€λ©΄ λκΈ° μκ°μ΄ λ°μνλ€.
λμμ μ¬μ©νλ©΄ μ₯μ
λ¬Έμ
save() λ©μλκ° νΈμΆλμλμ§ νμΈν΄μΌ νκ³ ArgumentCaptorλ₯Ό μ΄μ©ν΄μ νΈμΆν λ μ λ¬ν μΈμλ₯Ό μ μ₯ν΄μΌ νλ€. β ν μ€νΈ μ½λκ° λ³΅μ‘ν΄μ§
ν΄κ²°
λ©λͺ¨λ¦¬λ₯Ό μ΄μ©ν κ°μ§ ꡬνμ μ¬μ©νλ©΄ μ½λκ° λ¨μν΄μ§κ³ μλ―Έλ λͺ ννλ€.
λͺ¨μ κ°μ²΄λ₯Ό μ΄μ©νλ©΄ λμ ν΄λμ€λ₯Ό λ§λ€μ§ μμλ λλκΉ νΈν μ μλ€.
νμ§λ§ κ²°κ³Ό κ°μ νμΈνλ μλ¨μΌλ‘ λͺ¨μ κ°μ²΄λ₯Ό μ¬μ©νκΈ° μμνλ©΄ κ²°κ³Ό κ²μ¦ μ½λκ° κΈΈμ΄μ§κ³ 볡μ‘ν΄μ§λ€.
ν μ€νΈκ° μ΄λ €μ΄ μ½λ | ν μ€νΈ κ°λ₯ν μ€κ³ | μΆκ° μ€λͺ |
---|---|---|
νλ μ½λ©λ κ²½λ‘ | κ²½λ‘λ μμλ₯Ό μμ±μλ λ©μλ νλΌλ―Έν°λ‘ λ°κΈ° | νλ μ½λ©λ κ°μ μ κ±°νκ³ , μ μ°νκ² λ³κ²½ν μ μλλ‘ μ€μ νκ±°λ μ£Όμ ν¨μΌλ‘μ¨ λ€μν ν μ€νΈ μλ리μ€λ₯Ό μ μ©ν μ μμ. |
νλ μ½λ©λ μμ | μμλ₯Ό μμ±μλ λ©μλ νλΌλ―Έν°λ‘ λ°κΈ° | μμλ₯Ό μΈλΆμμ μ£Όμ λ°μμΌλ‘μ¨ λ€μν νκ²½μμμ λμμ ν μ€νΈν μ μμΌλ©°, μ½λμ μ¬μ¬μ©μ±λ λμμ§. |
μμ‘΄ κ°μ²΄λ₯Ό μ§μ μμ± | μμ‘΄ λμμ μ£Όμ λ°κΈ° (μμ±μ λλ μΈν°λ₯Ό ν΅ν΄) | μ§μ κ°μ²΄λ₯Ό μμ±νλ©΄ ν μ€νΈ μ€μ λͺ¨νΉμ΄ μ΄λ €μ. μ£Όμ λ°λλ‘ μ€κ³νλ©΄ μμ‘΄μ±μ μ½κ² κ΅μ²΄νκ±°λ λͺ¨νΉν μ μμ΄ ν μ€νΈκ° μ©μ΄ν΄μ§. |
μ μ λ©μλ μ¬μ© | μΈμ€ν΄μ€ λ©μλλ‘ λ³κ²½νκ±°λ, ν μ€νΈ κ°λ₯ν κ΅¬μ‘°λ‘ λ³κ²½ | μ μ λ©μλλ μνλ₯Ό κ°μ§μ§ μκΈ° λλ¬Έμ ν μ€νΈκ° μ΄λ €μΈ μ μμ. μ΄λ₯Ό μΈμ€ν΄μ€ λ©μλλ‘ λ³κ²½νκ±°λ, μ μ λ©μλλ₯Ό κ°μΈλ ν΄λμ€λ₯Ό μ¬μ©νμ¬ μ μ°μ±μ λμ. |
μΈλΆ λΌμ΄λΈλ¬λ¦¬λ₯Ό μ§μ μ¬μ© | μΈλΆ λΌμ΄λΈλ¬λ¦¬λ₯Ό κ°μΈλ λνΌ ν΄λμ€λ₯Ό λ§λ€μ΄ μ¬μ© | μΈλΆ λΌμ΄λΈλ¬λ¦¬μ λν μ§μ μ μΈ μμ‘΄μ±μ μ€μ΄κ³ , λ³κ²½ μμλ λνΌ ν΄λμ€λ§ μμ νλ©΄ λλ―λ‘ μ μ§λ³΄μμ±μ΄ ν₯μλλ©°, λͺ¨νΉμ ν΅ν΄ ν μ€νΈλ μ¬μμ§. |
μ€ν μμ μ λ°λΌ λ¬λΌμ§λ κ²°κ³Ό | ν μ€νΈνκ³ μΆμ μ½λ λΆλ¦¬, μμΈ‘ κ°λ₯ν μ λ ₯κ³Ό μΆλ ₯μΌλ‘ λ³κ²½ | μ€ν μμ μ λ°λΌ λ¬λΌμ§λ λΆλΆμ λΆλ¦¬νμ¬ μμΈ‘ κ°λ₯ν κ²°κ³Όλ₯Ό μ 곡νλλ‘ μ€κ³νλ©΄ ν μ€νΈμ μΌκ΄μ±μ μ μ§ν μ μμ. νΉν, μκ°μ΄λ λλ€ κ°μ λΆλ¦¬νλ κ²μ΄ μ€μ. |
μν μ΄ μμ¬ μλ μ½λ | κ° μν μ λ΄λΉνλ ν΄λμ€λ‘ λΆλ¦¬νκΈ° | λ¨μΌ μ± μ μμΉ(SRP)μ μ μ©νμ¬ μ½λλ₯Ό λΆλ¦¬νλ©΄ κ° μν μ λν ν μ€νΈκ° λͺ νν΄μ§κ³ , μ½λμ κ°λ μ±κ³Ό μ μ§λ³΄μμ±μ΄ ν₯μλ¨. |
μκ°μ΄λ μμ κ° μμ± κΈ°λ₯ ν¬ν¨ | μκ°μ΄λ μμ κ° μμ±μ μΈν°νμ΄μ€λ‘ μΆμννμ¬ μ£Όμ λ°κΈ° | μκ°μ΄λ λλ€ κ° μμ± λ‘μ§μ μΈν°νμ΄μ€λ‘ λΆλ¦¬νκ³ μ£Όμ λ°μΌλ©΄, ν μ€νΈ μ μνλ κ°μ μ½κ² λͺ¨νΉν μ μμ΄ μμΈ‘ κ°λ₯ν ν μ€νΈκ° κ°λ₯ν΄μ§. |
볡μ‘ν λΉμ¦λμ€ λ‘μ§μ΄ ν¬ν¨λ λ©μλ | 볡μ‘ν λ‘μ§μ μμ λ¨μλ‘ λΆλ¦¬νκ³ , κ°κ°μ κ°λ³μ μΌλ‘ ν μ€νΈνκΈ° | 볡μ‘ν λ‘μ§μ μμ λ©μλλ‘ λΆλ¦¬νμ¬ κ°κ°μ λ©μλλ₯Ό κ°λ³μ μΌλ‘ ν μ€νΈν μ μλλ‘ μ€κ³. μ΄λ λλ²κΉ κ³Ό μ½λ μ΄ν΄λλ₯Ό λμ΄κ³ , ν μ€νΈλ₯Ό λ³΄λ€ μ² μ νκ² μνν μ μκ² ν¨. |
λΉλκΈ° μ½λ, μ½λ°± μ¬μ© | λΉλκΈ° μ½λλ₯Ό λκΈ° μ½λλ‘ κ°μΈκ±°λ, λΉλκΈ° νΈμΆμ μΆμννμ¬ ν μ€νΈνκΈ° | λΉλκΈ° μ½λμ ν μ€νΈλ μ΄λ €μΈ μ μμΌλ―λ‘, λκΈ°μ μΌλ‘ λμνκ² νκ±°λ μΆμνλ μΈν°νμ΄μ€λ‘ κ°μΈμ ν μ€νΈν μ μλλ‘ μ€κ³. |
νμΌ, λ€νΈμν¬, DBμ κ°μ μΈλΆ 리μμ€μ μμ‘΄νλ μ½λ | μΈλΆ 리μμ€ μ κ·Όμ μΈν°νμ΄μ€λ‘ μΆμννκ³ , ν μ€νΈ μ λͺ¨μ κ°μ²΄(mock)λ₯Ό μ¬μ© | μΈλΆ 리μμ€μ μμ‘΄νλ μ½λλ₯Ό μΆμννμ¬ μμ‘΄μ±μ μ€μ΄κ³ , ν μ€νΈ μ€μλ λͺ¨μ κ°μ²΄λ₯Ό μ¬μ©ν¨μΌλ‘μ¨ ν μ€νΈκ° λ 립μ μΌλ‘ μ΄λ£¨μ΄μ§ μ μκ² ν¨. |
κ·Έ μΈ ν μ€νΈνκΈ° μ΄λ €μ΄ μ½λ | λ¨μΌ μ± μ μμΉ(SRP)μ μ μ©νμ¬ μ½λ λΆλ¦¬, ν μ€νΈ κ°λ₯ν μ½λλ‘ λ¦¬ν©ν λ§ | λ¨μΌ μ± μ μμΉμ μ μ©νλ©΄ μ½λλ₯Ό λͺ ννκ² λΆλ¦¬ν μ μμ΄, κ° λΆλΆμ λ 립μ μΌλ‘ ν μ€νΈνκ³ μ μ§λ³΄μνλ κ²μ΄ μ©μ΄ν΄μ§. |