๊ฐ๋ฐ์ ํ๋ค ๋ณด๋ฉด ์ด๋ฐ ๊ณ ๋ฏผ์ด ๋ค ๋๊ฐ ์์ต๋๋ค.
"์กฐํ ์์ฒญ์ด ๋๋ฌด ๋ง์์ DB๊ฐ ๋ฒ๋ฒ ๊ฑฐ๋ฆฌ๋ ๊ฑธ ํด๊ฒฐํ ๋ฐฉ๋ฒ์ด ์์๊น?"
"๋ช ๋ น๊ณผ ์กฐํ๊ฐ ๊ฐ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ๊ณต์ ํ๋ค ๋ณด๋ ์ฑ๋ฅ์ด ์ ํ๋๋คโฆ"
์ด๋ฐ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋ฑ์ฅํ ํจํด์ด CQRS(Command Query Responsibility Segregation)์
๋๋ค.
๋ง ๊ทธ๋๋ก, ๋ช
๋ น(Command)๊ณผ ์กฐํ(Query)์ ์ฑ
์์ ๋ถ๋ฆฌํ๋ ์ํคํ
์ฒ ํจํด์
๋๋ค.
์ฆ, ๋ฐ์ดํฐ๋ฅผ ๋ณ๊ฒฝํ๋ ์์ฒญ(๋ช ๋ น)๊ณผ ๋ฐ์ดํฐ๋ฅผ ์กฐํํ๋ ์์ฒญ(์กฐํ)์ ๋ถ๋ฆฌํด์ ์ฒ๋ฆฌํ๋ ๋ฐฉ์์ด์ฃ .
๊ธฐ์กด์ ์ผ๋ฐ์ ์ธ ์ ํ๋ฆฌ์ผ์ด์ ์ํคํ ์ฒ์์๋ ํ๋์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ช ๋ น๊ณผ ์กฐํ๊ฐ ํจ๊ป ์ฒ๋ฆฌ๋ฉ๋๋ค.
ํ์ง๋ง ์ด๋ ๊ฒ ๋๋ฉด ๋ช ๊ฐ์ง ๋ฌธ์ ์ ์ด ์๊น๋๋ค.
์ด๋ฐ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด CQRS๋ฅผ ์ฌ์ฉํ๋ฉด ์ฝ๊ธฐ์ ์ฐ๊ธฐ๋ฅผ ๋ถ๋ฆฌํ์ฌ ์ต์ ํํ ์ ์์ต๋๋ค! ๐
CQRS ํจํด์ ํฌ๊ฒ Command(๋ช ๋ น)๊ณผ Query(์กฐํ)๋ก ๋๋ฉ๋๋ค.
๐ผ ์ฝ๊ฒ ํํํ๋ฉด?
์ฌ์ฉ์ โ Command โ DB (๋ฐ์ดํฐ ๋ณ๊ฒฝ)
์ฌ์ฉ์ โ Query โ ๋ณ๋ DB ๋๋ ์บ์ (๋ฐ์ดํฐ ์กฐํ)
์ด๋ ๊ฒ ์ฝ๊ธฐ์ ์ฐ๊ธฐ๋ฅผ ๋ถ๋ฆฌํ๋ฉด, ์กฐํ ์์ฒญ์ด ๋ง๋๋ผ๋ ๋ฐ์ดํฐ ๋ณ๊ฒฝ์๋ ์ํฅ์ ์ฃผ์ง ์์ผ๋ฉฐ, ์ฑ๋ฅ์ด ํฅ์๋ฉ๋๋ค.
@RestController
@RequestMapping("/orders")
public class OrderCommandController {
private final OrderService orderService;
public OrderCommandController(OrderService orderService) {
this.orderService = orderService;
}
@PostMapping
public ResponseEntity<Order> createOrder(@RequestBody OrderRequest request) {
Order order = orderService.createOrder(request);
return ResponseEntity.ok(order);
}
}
@RestController
@RequestMapping("/orders")
public class OrderQueryController {
private final OrderQueryService orderQueryService;
public OrderQueryController(OrderQueryService orderQueryService) {
this.orderQueryService = orderQueryService;
}
@GetMapping("/{id}")
public ResponseEntity<OrderResponse> getOrder(@PathVariable Long id) {
OrderResponse order = orderQueryService.getOrderById(id);
return ResponseEntity.ok(order);
}
}
๐ ์ฐจ์ด์
OrderCommandController
โ ์ฃผ๋ฌธ์ ์์ฑํ๋ ๋ช
๋ น์ฉ APIOrderQueryController
โ ์ฃผ๋ฌธ์ ์กฐํํ๋ ์กฐํ์ฉ API์ฆ, ์ฝ๊ธฐ์ ์ฐ๊ธฐ๋ฅผ ์์ ํ ๋ถ๋ฆฌํ์ฌ ๊ฐ๊ฐ์ ์ญํ ์ ๋๋ ๊ฒ์ ๋๋ค.
โ
์ฝ๊ธฐ/์ฐ๊ธฐ ์ฑ๋ฅ ์ต์ ํ โ ์กฐํ DB๋ฅผ ๋ถ๋ฆฌํ์ฌ ์ฝ๊ธฐ ์ฑ๋ฅ์ ๊ทน๋ํํ ์ ์์
โ
ํ์ฅ์ฑ(Scalability) ํฅ์ โ ์ฝ๊ธฐ์ ์ฐ๊ธฐ๋ฅผ ๋ณ๋๋ก ํ์ฅ ๊ฐ๋ฅ
โ
๋ฐ์ดํฐ ๋ชจ๋ธ ์ต์ ํ ๊ฐ๋ฅ โ ์กฐํ์ฉ DB๋ NoSQL, ElasticSearch ๋ฑ์ ํ์ฉํ ์ ์์
โ
๋น์ฆ๋์ค ๋ก์ง ๋จ์ํ โ ๋ช
๋ น๊ณผ ์กฐํ์ ์ญํ ์ ๋ถ๋ฆฌํ์ฌ ์ ์ง๋ณด์ ์ฉ์ด
โ ์ค๊ณ ๋ณต์ก์ฑ ์ฆ๊ฐ โ ๊ธฐ์กด๋ณด๋ค ๋ฐ์ดํฐ ๊ตฌ์กฐ๊ฐ ๋ณต์กํด์ง
โ ๋ฐ์ดํฐ ๋๊ธฐํ ๋ฌธ์ โ ๋ช
๋ น๊ณผ ์กฐํ ๋ฐ์ดํฐ๊ฐ ์ผ์นํ์ง ์์ ๊ฐ๋ฅ์ฑ
โ ์ถ๊ฐ์ ์ธ ์ธํ๋ผ ํ์ โ ์ฝ๊ธฐ/์ฐ๊ธฐ DB๋ฅผ ๋ฐ๋ก ์ด์ํด์ผ ํจ
โ Replica DB ๋๊ธฐํ ์ง์ฐ ๋ฌธ์ โ ํนํ ๋ฆฌ์ ๋ณ๋ก Replica DB๋ฅผ ๊ตฌ์ฑํ ๊ฒฝ์ฐ(์ ๊ฒฝํ๋ด ์
๋๋ค), ์๋ฅผ ๋ค์ด ๋ฐ๋์์ ์ผ๋ณธ๊น์ง ๋ฐ์ดํฐ ๋๊ธฐํ์ 1.3์ด ์ด์์ด ๊ฑธ๋ฆด ์๋ ์์ต๋๋ค. ์ด ๊ฒฝ์ฐ, Insert ํ ๋ฐ๋ก Select๋ฅผ ํ๋ฉด Read DB์๋ ํด๋น ๋ฐ์ดํฐ๊ฐ ์์ง ์กด์ฌํ์ง ์์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค. ์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ค๋ฉด ์ฆ์ ํธ์ถ์ ์ ํํ๊ฑฐ๋, Kafka๋ฅผ ํ์ฉํ ์ด๋ฒคํธ ๊ธฐ๋ฐ ๋๊ธฐํ, ๋๋ ํ๋ก ํธ์๋์์ ์ ์ ํ ์บ์ฑ ์ฒ๋ฆฌ๋ฅผ ๊ณ ๋ คํด์ผ ํฉ๋๋ค.
ํ์ง๋ง ๊ณ ์ฑ๋ฅ์ด ํ์ํ ์์คํ (์: ์ ์์๊ฑฐ๋, ๊ธ์ต, SNS ๋ฑ)์์๋ CQRS ํจํด์ด ๋งค์ฐ ์ ์ฉํ ์ ์์ต๋๋ค! ๐
๋ค์๊ณผ ๊ฐ์ ์ํฉ์์ CQRS ํจํด์ ๊ณ ๋ คํ๋ฉด ์ข์ต๋๋ค.
โ ์ฝ๊ธฐ/์ฐ๊ธฐ ํธ๋ํฝ์ด ๋น๋์นญ์ ์ธ ๊ฒฝ์ฐ โ ์ฝ๊ธฐ ์์ฒญ์ด ํจ์ฌ ๋ง์ ์๋น์ค (ex: ๋ด์ค ์ฌ์ดํธ, ๊ฒ์ ์์ง)
โ ๊ณ ์ฑ๋ฅ ์กฐํ๊ฐ ํ์ํ ๊ฒฝ์ฐ โ ElasticSearch, Redis ๋ฑ์ ํ์ฉํ ๊ฒ์ ์๋น์ค
โ ๋ณต์กํ ๋๋ฉ์ธ ๋ก์ง์ด ์๋ ๊ฒฝ์ฐ โ ์ด๋ฒคํธ ์์ฑ(Event Sourcing)๊ณผ ๊ฒฐํฉํ์ฌ ์ฌ์ฉ
โ ํ์ฅ์ฑ์ด ํ์ํ ๊ฒฝ์ฐ โ ์ฝ๊ธฐ ์๋ฒ์ ์ฐ๊ธฐ ์๋ฒ๋ฅผ ๊ฐ๋ณ์ ์ผ๋ก ํ์ฅ ๊ฐ๋ฅ
๐ CQRS๋ ๋ช
๋ น๊ณผ ์กฐํ๋ฅผ ๋ถ๋ฆฌํ์ฌ ์ฑ๋ฅ์ ์ต์ ํํ๋ ํจํด์
๋๋ค.
๐ ์ฝ๊ธฐ์ ์ฐ๊ธฐ๋ฅผ ๋ฐ๋ก ์ค๊ณํ๋ฉด ํ์ฅ์ฑ๊ณผ ์ ์ง๋ณด์์ฑ์ด ํฅ์๋ฉ๋๋ค.
๐ ์ค๊ณ๊ฐ ๋ณต์กํด์ง ์ ์์ผ๋ฏ๋ก, ์์คํ
์ ํน์ฑ์ ๊ณ ๋ คํ์ฌ ๋์
ํด์ผ ํฉ๋๋ค.
๐ CQRS ํจํด์ ์ ์ฉํ๋ฉด ๋ ๋น ๋ฅด๊ณ ์์ ์ ์ธ ์๋น์ค๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค. ํ์ง๋ง ๋ชจ๋ ํ๋ก์ ํธ์ ์ ํฉํ ๊ฒ์ ์๋๋ฏ๋ก, ์์คํ ์ ์๊ตฌ์ฌํญ์ ๋ฐ๋ผ ์ ์คํ๊ฒ ๋์ ํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค! ๐