- ๋ถ์ผ: MSA, CQRS Pattern, Read Model Optimization, Performance Tuning, Hybrid Visibility Strategy
- ๊ด๋ จ ํ์ผ: ProductViewCommandService.java, ProductViewRepository.java, StockEventConsumer.java, DailyStockResetEvent.java
1. ๋ฐ๋จ: ์กฐํ ์๋น์ค(Catalog)์ '๊ฐ์์ฑ ๋ณด์ฅ'๊ณผ 'I/O ๋ถํ'์ ๋๋ ๋ง
CQRS ํจํด์ ๋ฐ๋ผ ์นดํ๋ก๊ทธ ์๋น์ค๋ ์ค๋/์ธ๋ฒคํ ๋ฆฌ์ ๋๋ฉ์ธ ์ด๋ฒคํธ๋ฅผ ๊ตฌ๋ ํ์ฌ ์กฐํ ์ ์ฉ ๋ฐ์ดํฐ์ (MongoDB)์ ์ค์๊ฐ์ผ๋ก ๋๊ธฐํํ๊ณ ์์์. ํ์ง๋ง ๋๊ท๋ชจ ํธ๋ํฝ ์๋๋ฆฌ์ค๋ฅผ ๊ณ ๋ คํ์ ๋, ์์คํ ์ ๊ตฌ์กฐ์ ๋ณ๋ชฉ ์์์ด ๊ฐ์ง๋จ
- ๊ธฐ์กด ๊ตฌ์กฐ์ ๋ณ๋ชฉ: ์ํ ์ ๋ณด ์์ , ํ์ , ์์ ์ฌ๊ณ ์ด๊ธฐํ ๋ฑ์ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ ๋๋ง๋ค ๋ชฝ๊ณ DB์ ์ค์๊ฐ Write(CUD) ์ฐ์ฐ์ด ์์์ง
→ ์ด๋ก ์ธํด Read ์ ์ฉ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ฌ์ผ ํ ๋ชฝ๊ณ DB์ I/O ์์์ด ๋๋์ Write ํธ๋ํฝ์ ์ ์ ๋นํ๋ฉด์, ์ ์ ๋ฉ์ธ ํ๋ฉด์ ์กฐํํ๋ ๊ณ ๊ฐ๋ค์ ์กฐํ ์ฑ๋ฅ์ด ์ ํ๋๋ ์ญ์ค์ ์ธ ํ์์ด ๋ฐ์ํจ.
2. ๋ฌธ์ ์ธ์ ๋ฐ ๋์๋ณ Trade-off ๋ถ์
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์นดํ๋ก๊ทธ์ ๊ฐ์์ฑ ์ ๋ฐ์ดํธ ์ฃผ๊ธฐ๋ฅผ ์กฐ์ ํ๋ ์ธ ๊ฐ์ง ์ํคํ ์ฒ ๋์์ ๊ฒํ ํจ.
| ์ํคํ ์ฒ ๋์ | ๊ตฌํ ํน์ง | ์ฅ์ | ์น๋ช ์ ์ธ ๋จ์ (Trade-off) |
| ๋์ A: ์ค์๊ฐ ์ ๋จ์ง (๊ธฐ์กด ์ํ) |
์ด๋ฒคํธ๋ฅผ ๋ฐ๋ ์ฆ์ ๋ชฝ๊ณ DB ์ํ(isVisible, status)๋ฅผ ๋ฌด์กฐ๊ฑด ์ค์๊ฐ ์์ ๋ฐ ๋ฐ์ | ๋ฐ์ดํฐ ์ ํฉ์ฑ์ด ๊ฑฐ์ ์ค์๊ฐ์ผ๋ก ์ ์ง๋จ | ๋๊ท๋ชจ ํธ๋ํฝ ์ ์ฃผ๋ฌธ/์ฌ๊ณ ์๋น์ค์ ์ด๋ฒคํธ ํญ์ฃผ๋ก ๋ชฝ๊ณ DB Write ๋ณ๋ชฉ ⇒ ์กฐํ ์ฑ๋ฅ ์ ํ |
| ๋์ B: ๊ณ ์ ์ ๋จ์งst | ์ค์๊ฐ ์ด๋ฒคํธ ์์ ์ ์์ ํ ๋ฐฐ์ ํ๊ณ , ์์ ๋ฐฐ์น ์ค์ผ์ค๋ฌ๋ฅผ ํตํด์๋ง ์ผ๊ด ์ ๋ฐ์ดํธ | ์นดํ๋ก๊ทธ ์๋น์ค๊ฐ ๊ทน๋๋ก ๊ฐ๋ฒผ์์ง๋ฉฐ ์ต๊ณ ์ ์กฐํ ์ฑ๋ฅ ํ๋ณด | ๋ฎ 11์์ ํ์ ๋ ์ํ์ด ๋ฐค 11์ 59๋ถ๊น์ง ๋ฉ์ธ์ ๋ ธ์ถ๋จ. ๊ณ ๊ฐ์ด ๊ฒฐ์ ์ฐฝ๊น์ง ๊ฐ์ผ ํ๊น |
| ๋์ C: ํ์ด๋ธ๋ฆฌ๋ ์ ๋ต (์ต์ข ์ฑํ) |
1. ์ํ๊ฐ ๋ณ๊ฒฝ(๊ธฐ๊ฐ ๋ง๋ฃ ๋ฑ): ์์ ๋ฐฐ์น๋ก ๋ฌด๊ฒ๊ฒ ๊ฐฑ์ 2. ํ์ /๋ณต๊ตฌ: ์ด๋ฒคํธ ์์ ํ๋ ์บ์ ๋ฌดํจํ๋ก Read ๋ถํ ์ปท |
Read ๋ถํ๋ฅผ ์ต์ํํ๋ฉด์ ๊ณ ๊ฐ์๊ฒ ํ์ ๋ฐ์ดํฐ ํํธํ๋ฅผ ๋ณด์ฌ์ฃผ์ง ์์ | ๋ช ๋ น(Command)๊ณผ ์กฐํ(Query) ํ์ดํ๋ผ์ธ์ ๋ณต์ก๋ ์ฆ๊ฐ |
3. ํด๊ฒฐ ๊ณผ์ : ํ์ด๋ธ๋ฆฌ๋ ๊ฐ์์ฑ ์ ๋ต์ ์ํ ๋ชฝ๊ณ DB ๋ฐฐ์น ์ฟผ๋ฆฌ ์ต์ ํ
๋์ C(ํ์ด๋ธ๋ฆฌ๋ ์ ๋ต)๋ฅผ ์ํด ๋จผ์ , ์์ ๋ฐฐ์น๊ฐ ๋ ๋ ๋ชฝ๊ณ DB์ ์๋ง ๊ฑด ๋ฌธ์ ์ ์ฒด๋ฅผ ํ ์ค์บ(findAll())ํ์ฌ ์ฌ๊ณ ๋ฅผ ๋ฆฌ์ ํ๋ ๊ธฐ์กด์ ๋ฌด๊ฑฐ์ด ๋ก์ง์ ์์ ํ์. ์จ๊น(HIDDEN) ์ฒ๋ฆฌ๋ ํด๋ฉด ์ํ๊น์ง ์ ๋ถ ๋ฉ๋ชจ๋ฆฌ์ ์ฌ๋ ค OOM์ ์ ๋ฐํ๋ ์ฝ๋๋ฅผ ๊ฑท์ด๋.
1) ํน์ ์ํ ํ๊ฒํ ์ฟผ๋ฆฌ ๋ฉ์๋ ์ถ๊ฐ (ProductViewRepository.java)
// [Before] ๊ธฐ์กด ์ ์ฒด ์กฐํ ์ค์บ
Page<ProductView> findAll(Pageable pageable);
// [After] ์ธ๋ฑ์ค ๊ธฐ๋ฐ ํน์ ์ํ ํ๊ฒํ
์ฟผ๋ฆฌ๋ก ๊ฐ์
Page<ProductView> findByStatusIn(List<String> statuses, Pageable pageable);
2) ๋ฐฐ์น ์ฒ๋ฆฌ ๋ฒ์ ์ถ์ ๋ฐ ๋ฏธ์ฌ์ฉ ํ๋ ์๊ฑฐ (ProductViewCommandService.java)
์์ ์ฌ๊ณ ์ด๊ธฐํ ์, ๋ฉ์ธ ํ๋ฉด ๋ ธ์ถ๊ณผ ๋ฐ์ ํ ACTIVE, SOLDOUT ์ํ์ ๋ํ๋จผํธ๋ง ํํฌ์ธํธ๋ก ํด์ฑํ์ฌ Chunk ๋จ์(100๊ฑด)๋ก ์์ ํ๊ฒ ๋์ด ์ฒ๋ฆฌํ๋๋ก ๋ก์ง์ ๋ฆฌํฉํฐ๋งํ์.
public void resetDailyStock(DailyStockResetEvent event) {
List<String> targetStatuses = List.of("ACTIVE", "SOLDOUT");
PageRequest pageRequest = PageRequest.of(0, 100);
Page<ProductView> page;
do {
page = productViewRepository.findByStatusIn(targetStatuses, pageRequest); // ์ต์ ํ ์ฟผ๋ฆฌ ํธ์ถ!
// ... ์์ ํ ๋ฌถ์ ์ ์ฅ(saveAll) ์งํ
pageRequest = pageRequest.next();
} while (page.hasNext());
}
4. ์ต์ข ์ฑ๊ณผ
- I/O ๋ฐ ๋คํธ์ํฌ ๋น์ฉ ์ ๊ฐ: ๋ถํ์ํ ๋ฏธ์ฌ์ฉ ํ๋(updatedOptionCount)๋ฅผ DTO์์ ์ ๊ฑฐํ์ฌ ์นดํ์นด ๋คํธ์ํฌ ํ์ด๋ก๋ ๋์ญํญ ์ต์ ํ
- ํ ์ค์บ ์ฐจ๋จ: ์์ ์ฌ๊ณ ๋ฆฌ์ ์ค์ผ์ค๋ฌ ๊ตฌ๋ ์ ์์ฒ/์๋ง ๊ฑด์ ํด๋ฉด ๋ํ๋จผํธ๋ฅผ ๋ฌด์ํ๊ณ ํ์ํ ๋ฐ์ดํฐ๋ง ์กฐํํจ์ผ๋ก์จ ๋ฐฐ์น IOPS ์ฑ๋ฅ ๋ํญ ํฅ์ ๋ฐ OOM ์์ฒ ์ฐจ๋จ