-
Mock, Stub μ μ°¨μ΄
mockκ³Όstubμ ν μ€νΈμμ μΈλΆ μμ‘΄μ±μ λ체νκΈ° μν΄ μ°μ΄λ κ°μ²΄μΈλ°, λμ λͺ©μ κ³Ό κΈ°λ₯ λ©΄μμ μ‘°κΈ λ¬λΌμ.
π μμ½ λΉκ΅
νλͺ© Stub Mock λͺ©μ κ³ μ λ κ° λ°ν (λ¨μ λ체) λμ κ²μ¦ + μ μ°ν μ€μ κΈ°λ₯ 미리 μ μλ μλ΅ μ 곡 νΈμΆ μ¬λΆ, νΈμΆ νμ, νλΌλ―Έν° κ²μ¦ λ± κ°λ₯ ν μ€νΈ νμ μ£Όλ‘ μν κΈ°λ° ν μ€νΈ (State-based) μ£Όλ‘ νμ κΈ°λ° ν μ€νΈ (Behavior-based) μμ “μ΄ λ©μλκ° νΈμΆλλ©΄ 무쑰건 μ΄ κ°μ λ¦¬ν΄” “μ΄ λ©μλκ° μ νν ν λ² νΈμΆλλμ§ νμΈνμ”
πΉ Stub: λ¨μνκ² λ¦¬ν΄κ°λ§ μ ν΄λμ κ°μ§
- μ€μ λ©μλ λ‘μ§μ νμ μκ³ , “μ΄ κ°μ μ£Όλ©΄ μ΄ κ°μ λλ €μ€!” κ°μ κ°λ¨ν λ체ν.
- μ£Όλ‘ “κΈ°μ‘΄ μμ€ν μμ μ΄κ±°λ§ μ λλ‘ λμνλ©΄ λΌ"λΌλ ν μ€νΈμ μ¬μ©.
when(userRepository.findById(1L)).thenReturn(Optional.of(user));
πΈ Mock: νμ(νΈμΆ μ¬λΆ λ±)κΉμ§ νμΈνλ κ°μ§
- 리ν΄κ°λ μ μν μ μμ§λ§, “μ λ§λ‘ μ΄ λ©μλκ° νΈμΆλλκ°?” κ°μ κ²μ¦λ κ°λ₯.
verify()κ°μ λ©μλλ₯Ό ν΅ν΄ ν μ€νΈμ μ νμ±μ λμ.
verify(userRepository, times(1)).findById(1L);
β μ€μ ν μ€νΈ μμ
@Mock UserRepository userRepository; @InjectMocks UserService userService; @Test void testUserFetch() { // Stub: 리ν΄κ° μ§μ when(userRepository.findById(1L)).thenReturn(Optional.of(new User("Alice"))); // λ©μλ μ€ν User user = userService.getUser(1L); // Mock: νΈμΆ μ¬λΆ κ²μ¦ verify(userRepository, times(1)).findById(1L); }
π κ°λ¨ λΉμ
- Stub: βλκ° μλ 무쑰건 μ΄ λμ¬λ§ λ§ν΄!β
- Mock: βλκ° μ€λ©΄ μ΄ λμ¬λ₯Ό νκ³ , μ λλ‘ λ§νλμ§ κ²μ¬λ ν©λλ€.!β
CreatedThu, 03 Apr 2025 15:57:29 +0900 -
ν μ€νΈ λλΈ(Test Double) μ©μ΄ μ 리
- μλλ ν μ€νΈμμ μμ£Ό μ°μ΄λ 5κ°μ§ ν μ€νΈ λμμ λλ€:
π§ͺ ν μ€νΈ λλΈ(Test Double) μ’ λ₯ μμ½
νμ μ€λͺ μ£Όλ‘ νλ μΌ λν μμ Dummy κ°μ΄ νμνμ§λ§ μ무 μν λ νμ§ μλ κ°μ²΄ λ©μλ μκ·Έλμ² μ±μ°κΈ° μ© new User(null)μ²λΌ κ°λ§ μ±μFake μ€μ ꡬνμ κ°κΉμ°λ, λ¨μνκ±°λ in-memory κΈ°λ° κ°λ¨ν λ체 ꡬν InMemory DB, FakeEmailSender Stub κ³ μ λ μλ΅μ 리ν΄νλ κ°μ²΄ μν κΈ°λ° ν μ€νΈ when(repo.findById()).thenReturn()Mock νμ κΈ°λ°, νΈμΆ μ¬λΆλ μμ κ²μ¦μ΄ κ°λ₯ν κ°μ²΄ νμ κΈ°λ° ν μ€νΈ verify(repo).save()Spy μ€μ κ°μ²΄λ₯Ό κ°μΈλ©΄μ, λΆλΆμ μΌλ‘ mock κ°λ₯ μ€μ λμ + νΈμΆ κ°μ spy(new UserService(...))
1. π£ Dummy
- κ°μ₯ λ¨μν νν.
- μ무 μΌλ νμ§ μκ³ , κ°λ§ μ±μ°λ μ©λ.
- ν μ€νΈμ μ§μ μ μλ―Έ μμ. μμ΄λ ν μ€νΈ ν΅κ³Ό κ°λ₯.
User dummyUser = new User(null); // μμ±μ λ§€κ°λ³μ λ§μΆλ €κ³ λ£λ μ©λ
2. π§ͺ Stub
- νΈμΆνλ©΄ μ ν΄μ§ κ°μ 리ν΄ν¨.
- λμ μ체λ λ¨μνκ³ κ²μ¦ λ‘μ§μ μμ.
- μν κΈ°λ° ν μ€νΈμ μ¬μ©λ¨.
when(userRepository.findById(1L)).thenReturn(Optional.of(new User("Alice")));
3. π€ Mock
- Stubμ²λΌ 리ν΄κ°μ μ€ μ μμ§λ§, μΆκ°λ‘ νΈμΆ μ¬λΆλ κ²μ¦ν¨.
- ν μ€νΈμ **νμ κ²μ¦(Behavior Verification)**μ μ΄μ .
verify(userRepository, times(1)).save(any());
4. π΅οΈ Spy
- μ€μ κ°μ²΄λ₯Ό κ°μΈλ©΄μ, νμν λΆλΆλ§ mock μ²λ¦¬ κ°λ₯.
- μΌλΆ λ©μλλ μ§μ§ μ€ν, μΌλΆλ stub/mock κ°λ₯.
- μ¬μ© μ μ‘°μ¬: μ§μ§ μμ‘΄μ±μ΄ λ°λΌμ¬ μ μμ.
UserService spyService = spy(new UserService(realRepository)); doReturn("fakeName").when(spyService).getUserName();
5. π§© Fake
- μ§μ§ ꡬν체λ μλμ§λ§, μ€μ λ‘ λμνλ κ°λ¨ν ꡬν.
- ν μ€νΈμ©μΌλ‘ κ°λ³κ² λ§λ μ§μ§ λΉμ¦λμ€ λ‘μ§μ νλ΄λΈ κ².
- μ: InMemory DB, ν μ€νΈμ© λ©μμ§ ν λ±
public class InMemoryUserRepository implements UserRepository { private Map<Long, User> db = new HashMap<>(); public Optional<User> findById(Long id) { return Optional.ofNullable(db.get(id)); } }
π― μ΄λ€ κ±Έ μΈμ μ¨μΌ ν κΉ?
ν μ€νΈ λͺ©μ μ ν©ν λλΈ νλΌλ―Έν° μ±μ°κΈ°λ§ νλ©΄ λ¨ Dummy κ³ μ λ κ° λ¦¬ν΄λ§ νμ Stub νΈμΆ μ¬λΆ/νμ κ²μ¦ Mock μ€μ κ°μ²΄ + μΌλΆ mocking Spy μ€μ ꡬν λΉμ·ν λμ νμ Fake
Fake Repository μμ
Spring νκ²½μμ μμ£Ό μ°μ΄λ Fake Repository μμ λ₯Ό 보μ¬λ릴κ²μ.
λ¨μ ν μ€νΈμμ JPA κ°μ μ€μ DB μ°κ²° μμ΄λ λ‘μ§μ κ²μ¦νκ³ μΆμ λ μμ£Ό μ μ©ν©λλ€.CreatedThu, 03 Apr 2025 15:57:29 +0900 -
MSA(Microservices Architecture)μμ trace IDμ span IDλ **λΆμ° μΆμ (distributed tracing)**μμ μ¬μ©λλ κ³ μ μλ³μλ€μ΄μΌ. μμ€ν μ 체μμ μμ²μ νλ¦μ μΆμ ν μ μκ² ν΄μ£Όλ ν΅μ¬ κ°λ μ λλ€.
1. Trace ID
- νλμ μ 체 μμ²(Request)μ μλ³νλ κ³ μ ν IDμ λλ€.
- μ: μ¬μ©μκ° μΉμμ μ΄λ€ λ²νΌμ ν΄λ¦ν΄μ API μμ²μ΄ λ€μ΄μ€κ³ , κ·Έ μμ²μ΄ μ¬λ¬ κ°μ λ§μ΄ν¬λ‘μλΉμ€λ₯Ό κ±°μ³ μ²λ¦¬λλ κ²½μ°, μ΄ μ 체 νλ¦μ νλμ traceλ‘ λ΄ λλ€.
- μ¦, trace IDλ **“μ΄ μμ²μ΄ μ΄λμ μμλΌμ μ΄λκΉμ§ κ°λκ°”**λ₯Ό λνλ.
2. Span ID
- trace μμμ **κ°κ°μ μμ λ¨μ(μμ νΈμΆ νλνλ)**λ₯Ό μλ³νλ IDμ λλ€.
- μ: A μλΉμ€κ° B μλΉμ€μ C μλΉμ€λ₯Ό νΈμΆνλ©΄, A-B, A-C κ°κ°μ΄ λ³λμ spanμ΄ λκ³ , κ°κ°μ λν span IDκ° μμ΅λλ€.
- Spanμ μμ μκ°, μ’ λ£ μκ°, μ΄λ€ μμ μΈμ§ λ± μΈλΆ μ 보λ ν¬ν¨λ©λλ€.
ꡬ쑰 μμ
[Trace ID: abc123] βββββββββββββββ βββββββββββββββ β User RequestββββββββΆβ API Gateway βββββ βββββββββββββββ βββββββββββββββ β βΌ ββββββββββββββ β Service A βββββ ββββββββββββββ β βΌ ββββββββββββββ β Service B β ββββββββββββββ- μ 체 μμ²μ νλ¦μ΄ νλμ Trace ID (
abc123)λ₯Ό 곡μ νκ³ , - κ° μλΉμ€ κ° νΈμΆμ μλ‘ λ€λ₯Έ Span IDλ₯Ό κ°μ§.
μμ½
νλͺ© μ€λͺ Trace ID μ 체 μμ² νλ¦μ μΆμ νκΈ° μν κ³ μ ID Span ID Trace λ΄μμ κ°λ³ μμ μ ꡬλΆνλ ID
첨μΈ
- μ€μ MSA ꡬνμ front endμμ api requestμμ λΆν° trace idμ span idκ° μ±λ²λκ³
- μλ₯Ό λ€μ΄, bff -> api-gateway -> user-service λ₯Ό ν΅ν΄ request κ° νλ¬κ°λ€λ©΄, κ° μλΉμ€λ λμΌν trace idλ‘ μΆμ μ΄ λμ΄μΌ ν©λλ€.
CreatedWed, 02 Apr 2025 10:50:33 +0900 -
JVM(Java Virtual Machine)μμλ λ΄λΆμ μΌλ‘ μ¬λ¬ μ’ λ₯μ μ€ν(stack) μ μ¬μ©ν©λλ€. κ°κ°μ μ€νμ JVMμ μ€ν λ° λ©λͺ¨λ¦¬ κ΄λ¦¬ λ±μμ μ€μν μν μ ν©λλ€. μ£Όμ μ€ν μ’ λ₯λ λ€μκ³Ό κ°μ΅λλ€:
π§ 1. Java Virtual Machine Stack (λλ Thread Stack)
- μ€λͺ : κ° μ€λ λ(thread) λ§λ€ νλμ© μ‘΄μ¬νλ μ€νμ λλ€.
- μν : λ©μλ νΈμΆ μ μμ±λλ μ€ν νλ μ(stack frame) μ μ μ₯ν©λλ€. κ° νλ μμλ:
- μ§μ λ³μ(Local Variables)
- νΌμ°μ°μ μ€ν(Operand Stack)
- λ©μλ λ°ν μ£Όμ λ±μ΄ ν¬ν¨λ¨
- μμΈ: μ€ν μ€λ²νλ‘μ° λ°μ μ
StackOverflowErrorλ°μ
π 2. Native Method Stack
- μ€λͺ : λ€μ΄ν°λΈ λ©μλ(Cλ‘ κ΅¬νλ JNI λ©μλ λ±) νΈμΆ μ μ¬μ©λλ μ€νμ λλ€.
- μν : C/C++ λ± JVM μΈλΆ μΈμ΄λ‘ ꡬνλ λ©μλμμ νμν λ°μ΄ν°λ₯Ό μ μ₯
- νΉμ΄μ¬ν: μΌλΆ JVM ꡬνμμλ Java μ€νκ³Ό ν΅ν©λκΈ°λ ν¨
πΎ 3. Operand Stack (νΌμ°μ°μ μ€ν)
- μ€λͺ : μμ JVM Stackμ κ° μ€ν νλ μ λ΄μ μ‘΄μ¬νλ μ€νμ λλ€.
- μν : λ°μ΄νΈμ½λ λͺ λ Ήμ΄κ° μ€ν μ€μΈ μ€κ° μ°μ° κ²°κ³Όλ₯Ό μμλ‘ μ μ₯
- μμ:
iadd,imulκ°μ λ°μ΄νΈμ½λ λͺ λ Ήμ μ΄ μ€νμμ νΌμ°μ°μλ₯Ό κΊΌλ΄ κ³μ°
π§± 4. Call Stack (νΈμΆ μ€ν)
- μ€λͺ : JVM Stackμ ꡬ쑰μ μΈ μΈ‘λ©΄μ μ§μΉνλ μ©μ΄λ‘, λ©μλ νΈμΆμ νλ¦μ λνλ λλ€.
- μν : νμ¬ μ€ν μ€μΈ λ©μλλΆν° μμν΄ νΈμΆλ λ©μλλ€μ΄ 차곑차곑 μμ¬ μμ
- νΉμ§: λλ²κΉ μ “Stack Trace"λ‘ νμΈ κ°λ₯
μΆκ°λ‘ JVMμλ μ€ν μ΄μΈμλ λ€μκ³Ό κ°μ λ©λͺ¨λ¦¬ μμμ΄ μμ΅λλ€:
CreatedTue, 01 Apr 2025 15:44:09 +0900 -
JVM(Java Virtual Machine)μμ heap λ©λͺ¨λ¦¬λ μλ° μ ν리μΌμ΄μ μ΄ λ°νμ μ λμ μΌλ‘ μμ±νλ κ°μ²΄λ€μ΄ μ μ₯λλ μμμ λλ€. μ¦,
newν€μλλ₯Ό μ¬μ©ν΄μ μμ±νλ λλΆλΆμ κ°μ²΄λ€μ΄ μ΄ μμμ μ μ₯λ©λλ€.
π Heap λ©λͺ¨λ¦¬ ꡬμ±
Heapμ ν¬κ² λ€μ λ μμμΌλ‘ λλ©λλ€:
- Young Generation (Young Gen): μλ‘ μμ±λ κ°μ²΄κ° μ μ₯λλ κ³³.
- Eden: λλΆλΆμ μ κ°μ²΄κ° μ¬κΈ°μ μμ±λ¨.
- Survivor: Edenμμ μ΄μλ¨μ κ°μ²΄λ€μ΄ μ΄λ.
- Old Generation (Tenured Gen): Young Genμ κ±°μ³ μ€λ μ΄μλ¨μ κ°μ²΄λ€μ΄ μ΄λνλ 곡κ°.
Garbage Collector(GC)λ μ΄ heap μμμ κ°μ²΄λ€μ μ£ΌκΈ°μ μΌλ‘ κ²μ¬ν΄μ λ μ΄μ μ°Έμ‘°λμ§ μλ κ°μ²΄λ λ©λͺ¨λ¦¬μμ μ κ±°ν©λλ€.
CreatedTue, 01 Apr 2025 13:59:38 +0900 - Young Generation (Young Gen): μλ‘ μμ±λ κ°μ²΄κ° μ μ₯λλ κ³³.
-
@Cacheableμ΄λ Έν μ΄μ μ Spring Frameworkμμ λ©μλμ λ°νκ°μ μΊμμ μ μ₯νμ¬, κ°μ μΈμκ° λ€μ΄μ¬ κ²½μ° μΊμλ κ°μ μ¬μ¬μ©νλλ‘ νλ κΈ°λ₯μ μ 곡ν©λλ€. μ¦, λ©μλλ₯Ό λ°λ³΅μ μΌλ‘ νΈμΆνλλΌλ μ€μ λ‘λ μ²μ ν λ²λ§ μ€νλκ³ , μ΄ν νΈμΆμ μΊμλ κ²°κ³Όλ₯Ό λ°ννκ² λ©λλ€.
π μ£Όμ μν μμ½
- μ€λ³΅ νΈμΆ λ°©μ§: λμΌν μ λ ₯κ°μ λν΄ λ©μλλ₯Ό λ°λ³΅ νΈμΆνλ©΄, μΊμλ κ²°κ³Όλ₯Ό λ°νν΄ μ±λ₯ ν₯μ.
- κ²°κ³Ό μ μ₯: λ©μλ μ€ν κ²°κ³Όλ₯Ό μΊμμ μ μ₯.
- μ‘°κ±΄λΆ μΊμ±: νΉμ 쑰건μ λ°λΌ μΊμ± μ¬λΆ μ μ΄ κ°λ₯.
β κΈ°λ³Έ μ¬μ© μμ
@Cacheable("users") public User findUserById(Long id) { // μ€μ λ‘λ DBλ μΈλΆ API νΈμΆ return userRepository.findById(id).orElse(null); }- μ λ©μλλ
usersλΌλ μΊμ μ΄λ¦μΌλ‘ κ²°κ³Όλ₯Ό μ μ₯ν©λλ€. idκ° λμΌν κ²½μ°, μ΄ν νΈμΆμ μΊμλUserκ°μ²΄λ₯Ό λ°νν©λλ€.
π§ μ£Όμ μμ±
μμ± μ€λͺ valueλλcacheNamesμ¬μ©ν μΊμ μ΄λ¦ μ§μ keyμΊμ ν€λ₯Ό μ§μ (SpEL ννμ μ¬μ© κ°λ₯) conditionμΊμ±ν 쑰건 μ§μ (SpEL) unlessκ²°κ³Όλ₯Ό μ μ₯νμ§ μμ 쑰건 (SpEL) μμ:
@Cacheable(value = "books", key = "#isbn", unless = "#result == null") public Book findBook(String isbn) { return bookRepository.findByIsbn(isbn); }
π§ ν¨κ» μμλλ©΄ μ’μ μ΄λ Έν μ΄μ
@CachePut: μΊμλ₯Ό κ°μ λ‘ μ λ°μ΄νΈν λ μ¬μ©@CacheEvict: μΊμλ₯Ό μμ ν λ μ¬μ©@Caching: μ¬λ¬ μΊμ μ΄λ Έν μ΄μ μ ν¨κ» μ¬μ©ν λ
νμνλ€λ©΄ Redisλ Caffeine κ°μ μΊμ μ€ν μ΄μ ν¨κ» μ°λλ κ°λ₯ν©λλ€.
CreatedTue, 01 Apr 2025 11:34:10 +0900