0.ํ๋ก์ ํธ ๊ฐ์
์ด๋ฒ ํ๋ก์ ํธ์์๋ MSA(Microservices Architecture)๊ธฐ๋ฐ์ ์
์ฒด ๊ด๋ฆฌ ์๋น์ค๋ฅผ ๊ฐ๋ฐํ๊ธฐ๋ก ํ๋ค.
์ ์ฒด(Company)๋ ๋ฌผ๋ฅ ๋ฐ ์ฃผ๋ฌธ ํ๋ฆ์์ ํต์ฌ ์ญํ ์ ํ๋ฉฐ, ํ๋ธ(Hub) ๋ฐ ์ฃผ๋ฌธ(Order)๊ณผ ๊ธด๋ฐํ๊ฒ ์ฐ๊ฒฐ๋์ด ์๋ค.
์ด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก store-service(์
์ฒด ๊ด๋ฆฌ ๋ง์ดํฌ๋ก์๋น์ค)๋ฅผ ๊ตฌ์ถํ๋ฉฐ, API ์ค๊ณ, DB ๋ชจ๋ธ๋ง, ์ธ์ฆ/์ธ๊ฐ ์์คํ
, ์๋น์ค ๊ฐ ํต์ , ๋ถ์ฐ ํธ๋ ์ด์ฑ ๋ฑ์ ๊ตฌํํ ์์ ์ด๋ค.
1. ์
์ฒด ๋๋ฉ์ธ ๋ฐ ์ญํ
1.1 ์
์ฒด ๋๋ฉ์ธ์ ํต์ฌ ๊ธฐ๋ฅ
- ์
์ฒด(Company)๋ ์ฃผ๋ฌธ(Order) ๋ฐ ํ๋ธ(Hub)์ ์ฐ๊ด๋์ด ์์
- ์
์ฒด๋ ์ํ(Product)์ ๋ณด์ ํ๊ณ ์์ผ๋ฉฐ, ํ๋ธ์ ์ํธ์์ฉํ๋ฉด์ ๋ฐฐ์ก ๊ฒฝ๋ก ๊ฒฐ์
- ๊ถํ(Role) ์์คํ
ํ์ → ์
์ฒด ๋ด๋น์๋ ์์ ์ ์
์ฒด๋ง ์์ ๊ฐ๋ฅ
1.2 ์ฃผ์ ๊ธฐ๋ฅ ์ ๋ฆฌ
1. ์
์ฒด ๋ฑ๋ก (ํ์ฌ ์ ๋ณด ์ ์ฅ)
2. ์
์ฒด ์ ๋ณด ์กฐํ (๋จ์ผ ์กฐํ & ๋ฆฌ์คํธ ์กฐํ)
3. ์
์ฒด ์ ๋ณด ์์ (ํ์ฌ ์ ๋ณด ์
๋ฐ์ดํธ)
4. ์
์ฒด ์ญ์ (๋
ผ๋ฆฌ์ ์ญ์ ์ ์ฉ)
5. ์
์ฒด๋ณ ๋ฉ๋ด ๊ด๋ฆฌ (์
์ฒด์ ์ํ ๋ฉ๋ด ๋ฆฌ์คํธ ์กฐํ)
2. ํ๋ก์ ํธ ์๊ตฌ์ฌํญ ๋ฐ ๊ตฌํ ๋ฐฉํฅ
2.1 MSA ํ๊ฒฝ ๊ตฌ์ถ
MSA(Microservices Architecture) ํ๊ฒฝ์ ๊ณ ๋ คํ์ฌ, API Gateway, Eureka, Config Server, Docker ๊ธฐ๋ฐ์ ์คํ ํ๊ฒฝ์ ํฌํจํ๋ค.
๋์
๊ธฐ์ :
- API Gateway: ํด๋ผ์ด์ธํธ์ ์๋น์ค ๊ฐ ์์ฒญ์ ๊ด๋ฆฌ
- Eureka: ์๋น์ค ๋ฑ๋ก ๋ฐ ๋ฐ๊ฒฌ
- Config Server: ํ๊ฒฝ ์ค์ ์ค์ ๊ด๋ฆฌ
- Docker: ์ปจํ
์ด๋ ๊ธฐ๋ฐ ์๋น์ค ์คํ
- Spring Security + JWT: ์ธ์ฆ ๋ฐ ์ธ๊ฐ ์ฒ๋ฆฌ
2.2 ์
์ฒด ๊ถํ ๊ด๋ฆฌ
์๊ตฌ์ฌํญ
- ๋ง์คํฐ ๊ด๋ฆฌ์ : ๋ชจ๋ ์
์ฒด ๊ด๋ฆฌ ๊ฐ๋ฅ
- ํ๋ธ ๊ด๋ฆฌ์ : ๋ด๋น ํ๋ธ์ ์
์ฒด๋ง ๊ด๋ฆฌ ๊ฐ๋ฅ
- ์
์ฒด ๋ด๋น์ : ์์ ์ ์
์ฒด๋ง ์์ ๊ฐ๋ฅ, ๋ค๋ฅธ ์
์ฒด๋ ์กฐํ๋ง ๊ฐ๋ฅ
๊ตฌํ ๋ฐฉ๋ฒ
-Spring Security + JWT ๊ธฐ๋ฐ ์ธ๊ฐ ๋ก์ง ์ถ๊ฐ
- API Gateway์์ ์ธ์ฆ ๋ฐ ๊ถํ ๊ฒ์ฆ ์ํ
- @PreAuthorize ์ด๋
ธํ
์ด์
์ ํ์ฉํ ์ ๊ทผ ์ ํ
@PreAuthorize("hasRole('MASTER') or (hasRole('HUB_MANAGER') and #store.hubId == authentication.principal.hubId) or (hasRole('STORE_MANAGER') and #store.ownerId == authentication.principal.userId)")
public ResponseEntity<StoreDTO> updateStore(@PathVariable Long storeId, @RequestBody StoreUpdateRequest request) {
return storeService.updateStore(storeId, request);
}
2.3 ์
์ฒด ๊ฒ์ ๋ฐ ์ ๋ ฌ ๊ธฐ๋ฅ ์ถ๊ฐ
์๊ตฌ์ฌํญ
- ๊ฒ์ ์กฐ๊ฑด: ์
์ฒด๋ช
, ์
์ฒด ํ์
, ์
์ฒด ์ฃผ์, ํ๋ธ ID
- ์ ๋ ฌ ๊ธฐ์ค: ์์ฑ์ผ, ์์ ์ผ
- QueryDSL์ ํ์ฉํ ๋์ ๊ฒ์ ๋ฐ ์ ๋ ฌ ๊ธฐ๋ฅ ์ ์ฉ
public Page<Store> searchStores(StoreSearchCriteria criteria, Pageable pageable) {
BooleanBuilder builder = new BooleanBuilder();
if (StringUtils.hasText(criteria.getName())) {
builder.and(store.name.contains(criteria.getName()));
}
if (criteria.getType() != null) {
builder.and(store.type.eq(criteria.getType()));
}
if (criteria.getHubId() != null) {
builder.and(store.hubId.eq(criteria.getHubId()));
}
return storeRepository.findAll(builder, pageable);
}
2.4 MSA ๊ฐ ๋ฐ์ดํฐ ๋๊ธฐํ ๋ฐ ์ฌ์๋ ๋ก์ง
์๊ตฌ์ฌํญ
- ์
์ฒด ์์ฑ/์์ ์ ํ๋ธ ID ๊ฒ์ฆ ํ์
- ์๋น์ค ๊ฐ API ํธ์ถ ์คํจ ์ Resilience4J ๊ธฐ๋ฐ ์ฌ์๋ ๋ก์ง ์ ์ฉ
@Retry(name = "hubService", fallbackMethod = "fallbackCheckHub")
public boolean checkHubExists(Long hubId) {
return hubClient.existsById(hubId);
}
private boolean fallbackCheckHub(Long hubId, Throwable t) {
log.error("ํ๋ธ ๊ฒ์ฆ ์คํจ. ๊ธฐ๋ณธ๊ฐ(false) ๋ฐํ", t);
return false;
}
2.5 ๋
ผ๋ฆฌ์ ์ญ์ ์ฒ๋ฆฌ ๋ณด์
์๊ตฌ์ฌํญ
- deleted_at, deleted_by ํ๋๋ฅผ ํ์ฉํ ๋
ผ๋ฆฌ ์ญ์
- ์กฐํ ์ ์ญ์ ๋ ๋ฐ์ดํฐ๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๊ฒ์๋์ง ์๋๋ก ์ฒ๋ฆฌ
public Page<Store> findAllActiveStores(Pageable pageable) {
return storeRepository.findAll(store.deletedAt.isNull(), pageable);
}
2.6 Zipkin์ ํ์ฉํ ๋ถ์ฐ ํธ๋ ์ด์ฑ ์ถ๊ฐ
์๊ตฌ์ฌํญ
- Zipkin์ ์ ์ฉํ์ฌ ์๋น์ค ํธ์ถ ์ถ์ ๊ฐ๋ฅํ๋๋ก ์ค์
๊ตฌํ ๋ฐฉ๋ฒ
```yaml
spring:
zipkin:
base-url: http://zipkin-server:9411
sleuth:
sampler:
probability: 1.0
```
3. MSA ํ๊ฒฝ์์ ๊ตฌํํด์ผ ํ ๊ฒ
DDD ๊ธฐ๋ฐ Aggregate ์ค๊ณ
- Aggregate Root: Store (์
์ฒด)
- Store (์
์ฒด ์ํฐํฐ)
- Menu (N:1 ๊ด๊ณ, ์
์ฒด์ ์ํ ๋ฉ๋ด)
- StoreOwner (1:1 ๊ด๊ณ, ์
์ฒด ์ด์์)
- StoreAddress (1:1 ๊ด๊ณ, ์
์ฒด ์ฃผ์)
API ์ค๊ณ ๋ฐ MSA ํต์
- ์
์ฒด CRUD API ๊ฐ๋ฐ
- ์
์ฒด-๋ฉ๋ด ์ฐ๋ API ๊ฐ๋ฐ
- ์
์ฒด-ํ๋ธ ์ฐ๋ API ๊ฐ๋ฐ
- Spring Security + JWT ์ธ์ฆ ์ ์ฉ
- API Gateway & Eureka ์ค์
- PostgreSQL ๊ธฐ๋ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ชจ๋ธ๋ง ์ ์ฉ
4. Docker๋ฅผ ํ์ฉํ ๋ฐฐํฌ ํ๊ฒฝ ๊ตฌ์ถ
PostgreSQL ๊ธฐ๋ฐ MSA ์คํ
1. docker-compose.yml ์ค์
```yaml
version: '3.8'
services:
postgres:
image: postgres:14
container_name: postgres-container
environment:
POSTGRES_DB: company_db
POSTGRES_USER: user
POSTGRES_PASSWORD: password
ports:
- "5432:5432"
```
2. Spring Boot PostgreSQL ์ฐ๊ฒฐ ์ค์
```properties
spring.datasource.url=jdbc:postgresql://postgres:5432/company_db
spring.datasource.username=user
spring.datasource.password=password
spring.jpa.database=postgresql
spring.jpa.hibernate.ddl-auto=update
```
์ด๋ฒ ํ๋ก์ ํธ์์๋ MSA ํ๊ฒฝ์์ ์
์ฒด ๊ด๋ฆฌ ์๋น์ค๋ฅผ ๊ตฌ์ถํ๋ฉฐ, API ์ค๊ณ, ์ธ์ฆ/์ธ๊ฐ, ๋ฐ์ดํฐ ๋๊ธฐํ, ๋ถ์ฐ ํธ๋ ์ด์ฑ, Docker ๊ธฐ๋ฐ ๋ฐฐํฌ๊น์ง ์ ์ฉํ ๊ณํ์ด๋ค. ์ด๋ฅผ ํตํด MSA ๊ธฐ๋ฐ์ ์๋น์ค ๊ฐ๋ฐ ๊ฒฝํ์ ์๊ณ , ์ ์ง๋ณด์ ๋ฐ ํ์ฅ์ฑ์ ๊ณ ๋ คํ ๊ตฌ์กฐ๋ฅผ ์ค๊ณํ ์ ์๋ ์ญ๋์ ๊ธฐ๋ฅผ ์์ ์ด๋ค.