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 ๊ธฐ๋ฐ˜์˜ ์„œ๋น„์Šค ๊ฐœ๋ฐœ ๊ฒฝํ—˜์„ ์Œ“๊ณ , ์œ ์ง€๋ณด์ˆ˜ ๋ฐ ํ™•์žฅ์„ฑ์„ ๊ณ ๋ คํ•œ ๊ตฌ์กฐ๋ฅผ ์„ค๊ณ„ํ•  ์ˆ˜ ์žˆ๋Š” ์—ญ๋Ÿ‰์„ ๊ธฐ๋ฅผ ์˜ˆ์ •์ด๋‹ค. 



+ Recent posts