🔗 프로젝트 관리
🔗 USER SERVICE 🔗 PRODUCT SERVICE 🔗 ORDER SERVICE
🔗 GATEWAY 🔗 EUREKA SERVER
| 분류 | 기술 | 비고 |
|---|---|---|
| 프로그래밍 언어 | ||
| 빌드 도구 | ||
| 프레임워크 | ||
| ORM | ||
| 데이터베이스 | ||
| 테스트 DB | ||
| 캐시 | ||
| 테스트 도구 | 통합 테스트 | |
| 단위 테스트 | ||
| 테스트 커버리지 리포트 | ||
| 정적 분석 도구 | 코드 품질 분석 | |
| 쿼리 빌더 | ||
| 인증/인가 | ||
| mail 인증 | ||
| 컨테이너화 도구 | ||
| 스프링 클라우드 | ||
| config value Serving | ||
| endpoint 단일화 | ||
| Discovery | ||
| Registry |
| 메서드 | 엔드포인트 | 설명 | 요청 데이터 및 쿼리 파라미터 예시 |
|---|---|---|---|
| POST | /api/auth/login | 로그인 |
application/x-www-form-urlencoded[email protected]&password=Password1! |
| POST | /api/auth/send-email-auth-code | 인증코드 발송 |
application/json{ "email": "[email protected]" }
|
| POST | /api/auth/signup | 회원가입 |
application/json{
"name": "홍길동",
"email": "[email protected]",
"password": "Password1!",
"confirmPassword": "Password1!",
"tellNumber": "010-7777-7777",
"address": "서울시 용산구 351 오오오",
"authCode": "131327"
}
|
| POST | /api/orders | 주문 생성 |
application/json[{
"wishlistItemId": 1505
},
{
"wishlistItemId": 1504
}]
|
| PATCH | /api/orders/{orderId}/status | 주문 상태 변경 |
application/json"CANCEL" |
| GET | /api/products/{productId} | 제품 조회 | |
| GET | /api/products | 제품 목록 조회 |
쿼리 파라미터pageSize=5&listSort=PRICE_DESC&pageNumber=2&storeId=56 |
| 제품 검색 |
쿼리 파라미터pageSize=5&listSort=PRICE_DESC&pageNumber=2&searchKeyword=21 |
||
| 카테고리별 제품 조회 |
쿼리 파라미터pageSize=5&listSort=PRICE_DESC&pageNumber=2&productCategory=ACCESSORY |
||
| POST | /api/wishlists | 위시리스트 생성 |
application/json[{
"optionId": 1516,
"quantity": 2
},
{
"optionId": 1517,
"quantity": 1
}]
|
| GET | /api/wishlists | 위시리스트 조회 |
쿼리 파라미터memberId=58 |
| PATCH | /api/wishlists/items/{itemId}/quantity | 위시리스트 항목 수량 변경 |
application/json{
"action": "DECREASE"
}
|
📌 Resilience4j
Resilience4j를 통해 외부 API 호출 시 발생할 수 있는 장애로부터 시스템을 보호하는 기능을 구현하였습니다.
외부 서비스의 장애가 시스템에 미치는 영향을 최소화하여 회복 탄력성을 강화할 수 있도록 도와줍니다.
Resilience4j 도입 배경
프로젝트가 기존의 단일 애플리케이션 아키텍처(Monolithic Architecture, MA)에서 마이크로서비스 아키텍처(Microservices Architecture, MSA)로 변경되었습니다.
이 과정에서 Feign Client를 사용하여 다른 마이크로서비스와 통신하는 부분이 추가되었습니다. 이에 따라 외부 API 호출 시 발생할 수 있는 장애로부터 시스템을 보호하고, 각 마이크로서비스 간의 안정적인 통신을 보장하기 위해 Resilience4j를 도입하였습니다.
- Circuit Breaker: 외부 서비스가 불안정할 때 자동으로 요청을 차단하여 시스템 과부하를 방지하고, 잠시 후 다시 시도할 수 있는 상태로 전환합니다.
- Retry: 외부 서비스가 일시적으로 사용 불가능한 경우(예: 네트워크 문제, 서버 다운 등) 일정 횟수까지 재시도를 수행하여 일시적인 오류를 해결합니다.
Resilience4j 설정
resilience4j:
retry:
retry-aspect-order: 2
configs:
default:
max-attempts: 2
wait-duration: 500
retry-exceptions:
- feign.FeignException.ServiceUnavailable
ignore-exceptions:
- feign.FeignException.BadGateway
- feign.FeignException.NotImplemented
- feign.FeignException.FeignClientException
instances:
simpleRetryConfig:
baseConfig: default- 설정 설명
- 최대 재시도 횟수: 2회로 설정. 선착순 구매 시스템의 특성상 지나치게 많은 재시도가 시스템에 부담을 줄 수 있어 제한
- 재시도 간격: 500ms로 설정. 너무 긴 대기 시간이 아니라 빠른 재시도를 통해 오류 해결 시도
- 재시도할 예외: 서버가 일시적으로 사용 불가능한 경우(ServiceUnavailable)에만 재시도
- 무시할 예외: 게이트웨이 오류나 클라이언트 오류 등은 재시도가 의미 없으므로 무시
resilience4j:
circuitbreaker:
circuit-breaker-aspect-order: 1
configs:
default:
sliding-window-type: COUNT_BASED
minimum-number-of-calls: 7
sliding-window-size: 10
wait-duration-in-open-state: 10s
failure-rate-threshold: 50
slow-call-duration-threshold: 3000ms
slow-call-rate-threshold: 60
permitted-number-of-calls-in-half-open-state: 5
automatic-transition-from-open-to-half-open-enabled: true
record-exceptions:
- feign.FeignException.ServiceUnavailable
- feign.FeignException.GatewayTimeout
- java.net.SocketTimeoutException
- feign.FeignException.BadGateway
ignore-exceptions:
- feign.FeignException.NotImplemented
- feign.FeignException.FeignClientException
instances:
simpleCircuitBreakerConfig:
baseConfig: default
- 설정 설명:
- 슬라이딩 윈도우 유형: 요청 수 기반(COUNT_BASED)으로 빠르게 상태 판단
- 최소 호출 수: 7회로 설정, 실패율 판단을 위해 최소 호출 수를 설정하여 안정성 확보
- 실패율 임계값: 50%로 설정, 절반 이상의 요청이 실패하면 서킷이 열림
- 느린 호출 임계값: 3초 이상 걸리는 호출을 실패로 간주
- 반 개방 상태에서 허용할 호출 수: 5회, 시스템이 반 개방 상태에서 천천히 트래픽을 다시 받아들일 수 있도록 설정
- 처리할 예외: 일시적인 서버 오류나 게이트웨이 문제를 처리
- 무시할 예외: 클라이언트 측 오류는 서킷 브레이커를 무시
- MA(Monolithic Architecture) : 모든 기능이 하나의 애플리케이션 내부에 통합되어 있으며, 단일 코드베이스에서 작동하도록 하는 소프트웨어 설계
- 장점: 개발과 관리가 용이합니다.
- 단점: 시스템이 복잡해지고 커질수록 코드를 이해하기가 어려워지고 그럴수록 유지보수하기 어려워집니다.
- MSA(Microservice Architecture) : 소프트웨어 시스템을 여러 작은 독립적인 서비스로 나누고, 각 서비스를 독립적으로 배포하고 운영하는 소프트웨어 설계
- 장점: 대규모 애플리케이션을 더 작고 관리 가능한 단위로 분해함으로써, 개발, 배포, 확장, 유지보수 등의 측면에서 더 높은 유연성과 확장성을 제공합니다.
- 단점: 여러 모듈들이 분산되어 있어 관리 및 모니터링이 힘들며, 통합 테스트가 힘듭니다.
MA vs MSA 비교표
MA (Monolithic Architecture) MSA (Microservices Architecture) 구조 하나의 통합된 애플리케이션 독립적인 여러 마이크로서비스 배포 전체 시스템을 하나의 단위로 배포 각 서비스 개별 배포 가능 확장성 전체 시스템 단위로 확장해야 함 필요한 서비스만 확장 가능 개발 속도 초기 개발은 빠름 복잡도 높아 초기 구축이 어려울 수 있음 유지 보수 코드베이스 커지면 어려워짐 서비스가 작아 유지 보수에 유리 기술 스택 하나의 통일된 기술 스택 사용 각 서비스별로 다른 기술 스택 선택 가능 복잡도 상대적 단순 서비스 간 통신, 데이터 일관성 관리가 어려움 운영 비용 단순, 적은 운영 리소스 필요 많은 리소스 필요 (모니터링, 네트워크 통신 등) MSA 도입 배경
- 확장성 : MSA는 개별 마이크로서비스 단위로 확장이 가능하여 특정 서비스의 트래픽이 많을 때만 확장이 가능합니다.
- 유지보수
- 모든 기능이 통합된 하나의 코드베이스는 시간이 지남에 따라 복잡해집니다.
- 코드 수정 시 다른 기능에 영향을 미치는 경우가 많아 유지보수가 어려워집니다.
- MSA는 서비스 단위로 코드가 나뉘어져 있어 유지보수가 용이합니다.
- 의존성 : MSA에서는 서비스 간 의존성을 줄일 수 있습니다.
분산 시스템을 구축하기 위한 Spring Framework의 확장 도구
MSA 환경에서 마이크로서비스 간의 통신, 설정 관리, 서비스 디스커버리, 로드 밸런싱, 장애 복구, 모니터링 등 다양한 기능을 제공하여 마이크로서비스의 복잡한 인프라 문제를 쉽게 해결할 수 있도록 돕는 도구입니다.
Spring Cloud 도입 배경
- 서비스 간 통신 문제 해결
- MSA 환경에서는 서비스들이 서로 다른 네트워크 상에서 통신해야 합니다.
- 이를 쉽게 구현하기 위해 Spring Cloud는 서비스 디스커버리와 API Gateway를 제공합니다.
- 설정 관리의 복잡성 해결 : 마이크로서비스가 많아질수록 각 서비스의 설정을 일관성 있게 관리하는 것이 어려워지는데, Spring Cloud Config를 통해 중앙에서 설정을 통합 관리할 수 있습니다.
- 추적 및 모니터링의 필요성: 분산 추적과 모니터링 도구를 제공하여 운영의 투명성을 높입니다.
3개의 마이크로 서비스로 만들기
- 서비스
- 유저 서비스: 회원 로직(회원가입, 이메일 확인 등) 기능을 담당하는 서비스를 구현합니다.
- 상품 서비스: 상품 로직(상품 조회 등) 기능을 담당하는 서비스를 구현합니다.
- 주문 서비스: 주문 로직(주문 생성, 수정, 반품) 기능을 담당하는 서비스를 구현합니다.
- 각 서비스는 독립적으로 배포 가능해야 합니다.
Feign client를 활용한 마이크로 서비스 간의 상호 작용
서비스 기능별로 구분해서 독립적인 애플리케이션을 개발하면 각 서비스 간 통신을 해야하는 경우가 발생합니다.
한 서버가 다른 서버에 통신을 요청하는 것을 서버간 통신이라고 합니다. 서버와 클라이언트의 구조가 되며 대표 적인 통신방식이 HTTP/HTTPS입니다.
Spring Cloud에서 제공하는 HTTP 클라이언트로, 마이크로서비스 간의 통신을 간편하게 하기 위해 사용됩니다.
REST API를 호출할 때 HTTP 요청을 직접 작성하는 대신, 인터페이스를 정의하고 필요한 서비스 호출을 메서드처럼 사용할 수 있게 해줍니다.
- 유저 서비스
- 유저관련 내부 통신 api 제공
GET,/internal/v0/members/{memberId}/exists: memberId 존재 유무 확인 api
- 유저관련 내부 통신 api 제공
- 상품 서비스
- 상품관련 내부 통신 api 제공
PUT,/internal/v0/products/options/stock:상품 재고 업데이트 api
- 상품관련 내부 통신 api 제공
- 주문 서비스
- 유저 서비스, 상품 서비스 내부 통신 api feign client를 활용해 상호 작용
@FeignClient(name = "member") public interface UserFeignClient { @GetMapping("/internal/v0/members/{memberId}/exists") boolean existsMemberId(@PathVariable Long memberId); }
@FeignClient(name = "product") public interface ProductFeignClient { @PutMapping("/internal/v0/products/options/stock") void updateProductStock(@RequestBody UpdateQuantityByProductOptionsDto dto); }
- 유저 서비스, 상품 서비스 내부 통신 api feign client를 활용해 상호 작용
Spring Cloud Gateway 만들기
MSA 가장 앞단에서 클라이언트들로 부터 오는 요청을 받은 후 경로와 조건에 알맞은 마이크로서비스 로직에 요청을 전달하는 게이트웨이
클라이언트에 제공되는 API가 세 서비스로 분산되어 있어
클라이언트는 각 서버가 제공하는 API 목록을 알아야 하며, 각 서버에 맞는 요청을 보내야 합니다.각 서버의 정보를 클라이언트가 알아야해서 불편하고, 서비스 구조가 외부로 공개된다는 단점이 있어 Gateway가 필요합니다.
- API GATEWAY 서비스를 새로 만들어 실행시키기(port: 8080)
- 기존의 모노리스 서비스가 가지고 있던 모든 API 들을 API GATEWAY에 노출한다.
- 해당 API로 들어온 요청을, 내부의 마이크로 서비스로 전달한다.
Eureka Server 만들기
Eureka는 Netflix에서 개발한 서비스 등록 및 디스커버리 도구로, MSA 환경에서 서비스 간 통신을 돕기 위해 각 서비스의 정보를 관리합니다. Eureka Server는 마이크로서비스들을 등록하고, 서로의 위치를 찾아주는 역할을 합니다.
MSA에서는 서비스들이 독립적으로 배포되며, 각 서비스의 위치(IP, 포트)가 동적으로 변할 수 있습니다. 이를 해결하기 위해 서비스의 위치 정보를 동적으로 관리하는 Eureka Server가 필요합니다. 클라이언트는 Eureka에 등록된 서비스 주소를 통해 해당 서비스에 접근할 수 있습니다.
- Eureka Server를 Spring Boot로 구성 (port: 8761)
-
@EnableEurekaServer애너테이션으로 Eureka Server 활성화 - 각 마이크로서비스들이 Eureka Client로 등록되도록 설정
- Eureka Dashboard로 각 서비스 상태 확인 (http://localhost:8761)
Config Server & Config Repository 만들기
Config Server는 외부 설정 파일을 중앙에서 관리하며, 모든 마이크로서비스가 설정을 공유하고 동기화할 수 있도록 돕는 도구입니다. 이를 통해 서비스 재시작 없이 설정을 동적으로 변경할 수 있습니다.
MSA 환경에서는 서비스마다 개별 설정이 존재하며, 이를 관리하는 것이 복잡합니다. Config Server를 사용하면 모든 설정을 중앙에서 관리하고 버전 관리가 가능해져, 서비스 간 설정 불일치 문제를 해결할 수 있습니다.
- Spring Cloud Config Server를 사용하여 Config Server를 구축 (port: 8888)
- Github를 Config Repository로 활용하여 설정 파일 관리
- 각 마이크로서비스는 Config Server를 통해 설정을 로드하도록 설정
-
@EnableConfigServer애너테이션을 사용하여 Config Server 활성화 - Git Repository에
*.yml파일을 생성하여 마이크로서비스별 설정 파일 저장
본 프로젝트는 자동화된 테스트를 사용하여 안정성을 보장합니다.
JUnit5:
- 프로젝트의 주요 테스트 프레임워크로 사용되었으며, 각 계층의 테스트 케이스를 작성하고 실행하는 데 사용되었습니다.
Mockito:
- Controller 단위 테스트에서 의존성 주입을 Mocking하여 외부 종속성과의 결합을 줄이고, 독립적으로 메서드를 테스트하기 위해 사용되었습니다.
JaCoCo:
- 코드 커버리지 분석 도구로, 테스트 케이스가 전체 코드베이스에서 얼마나 많은 부분을 커버하고 있는지 확인하기 위해 사용했습니다. 현재 프로젝트의 커버리지는 92.7%입니다.
SonarQube:
- 코드 품질 분석 도구로, 코드 내의 잠재적인 버그, 보안 문제, 중복 코드, 유지보수성 등을 검토하고, 코드 품질을 지속적으로 개선하기 위해 사용했습니다.
- JaCoCo 보고서:
- 테스트 커버리지는 전체 코드베이스의 92.7%에 달하며, 이를 통해 주요 비즈니스 로직이 충분히 검증되었음을 확인했습니다.
- SonarQube 분석 결과:
- SonarQube에서 보안, 신뢰성, 유지보수성 측면에서 모두 A 등급을 받았으며, 코드 품질에 대한 높은 기준을 유지하고 있습니다.
- 중복 코드 비율은 0.0%이며, 보안 취약점 및 핫스팟도 발견되지 않았습니다.
테스트 종류 및 계층
-
Controller 단위 테스트:
- Controller 계층에서는 JUnit5와 Mockito를 사용하여 단위 테스트를 수행했습니다. 이를 통해 각 Controller 메서드의 로직을 개별적으로 검증하고, Mock 객체를 사용하여 종속성을 분리하였습니다.
-
Service 및 Repository 통합 테스트:
- Service 및 Repository 계층에서는 통합 테스트를 진행하여 각 계층 간의 상호작용을 검증했습니다. 이를 통해 실제 데이터베이스와의 연동을 포함한 통합적인 로직의 정상 작동을 보장하였습니다.
회원가입 및 로그인
- 회원가입 기능을 통해 사용자는 계정을 생성할 수 있습니다.
- 이메일 인증을 통한 회원가입 기능을 만든다.
- 개인정보,
이름, 비밀번호, 주소, 이메일은암호화하여 저장한다.
회원가입 및 로그인
- 로그인 시 access-token 발급한다.







