-
Notifications
You must be signed in to change notification settings - Fork 36
[volume-6] 외부 시스템 장애 및 지연 대응 #167
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from 5 commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
6a03748
feat: DIP위반 피드백 반영
adminhelper c41ffe6
chore: BaseEntity상속으로 인한 createdAt제거
adminhelper afcae1c
chore: 피드백 반영
adminhelper df0fb03
docs : 6round.md
adminhelper 7b1111b
docs: md파일 수정
adminhelper 5e36c3f
feat : pg-simulator 모듈 추가
adminhelper 8fb6e51
feat : pg-simulator 모듈 추가
adminhelper 23b9170
feat : 주문 API 적용
adminhelper a1ffe5e
Refactor: DIP위반 제거
adminhelper f1205ab
fix: Point 환불 메서드 추가
adminhelper c5f6eaf
feat: resilience4j 의존성 추가
adminhelper 8c6d050
docs: 6round.md
adminhelper 0e3419f
feat: resilience4j, feign 의존성 추가
adminhelper 20d6942
feat: pg, feign, resilience4j 설정
adminhelper 7d12217
fix: user 생성시 point 생성 로직 수정
adminhelper 38838b4
feat: PG 결제 요청 API 로직 추가
adminhelper ff83042
fix: 6round 피드백
adminhelper File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
17 changes: 17 additions & 0 deletions
17
apps/commerce-api/src/main/java/com/loopers/domain/product/ProductCache.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| package com.loopers.domain.product; | ||
|
|
||
| import org.springframework.data.domain.Page; | ||
| import org.springframework.data.domain.Pageable; | ||
|
|
||
| import java.util.Optional; | ||
|
|
||
| public interface ProductCache { | ||
|
|
||
| Optional<Page<Product>> getProductList(Long brandId, Pageable pageable); | ||
|
|
||
| void putProductList(Long brandId, Pageable pageable, Page<Product> products); | ||
|
|
||
| Optional<Product> getProductDetail(Long productId); | ||
|
|
||
| void putProductDetail(Long productId, Product product); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
76 changes: 76 additions & 0 deletions
76
apps/commerce-api/src/main/java/com/loopers/infrastructure/product/RedisProductCache.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| package com.loopers.infrastructure.product; | ||
|
|
||
| import com.loopers.domain.product.Product; | ||
| import com.loopers.domain.product.ProductCache; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.data.domain.Page; | ||
| import org.springframework.data.domain.Pageable; | ||
| import org.springframework.data.redis.core.RedisTemplate; | ||
| import org.springframework.stereotype.Component; | ||
|
|
||
| import java.time.Duration; | ||
| import java.util.Optional; | ||
|
|
||
| @Component | ||
| @RequiredArgsConstructor | ||
| public class RedisProductCache implements ProductCache { | ||
|
|
||
| private static final Duration TTL_LIST = Duration.ofMinutes(10); | ||
| private static final Duration TTL_DETAIL = Duration.ofMinutes(5); | ||
|
|
||
| private final RedisTemplate<String, Object> redisTemplate; | ||
|
|
||
| @Override | ||
| @SuppressWarnings("unchecked") | ||
| public Optional<Page<Product>> getProductList(Long brandId, Pageable pageable) { | ||
| String key = listKey(brandId, pageable); | ||
| try { | ||
| Page<Product> cached = (Page<Product>) redisTemplate.opsForValue().get(key); | ||
| return Optional.ofNullable(cached); | ||
| } catch (Exception ignored) { | ||
| return Optional.empty(); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public void putProductList(Long brandId, Pageable pageable, Page<Product> products) { | ||
| String key = listKey(brandId, pageable); | ||
| try { | ||
| redisTemplate.opsForValue().set(key, products, TTL_LIST); | ||
| } catch (Exception ignored) { | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public Optional<Product> getProductDetail(Long productId) { | ||
| String key = detailKey(productId); | ||
| try { | ||
| Product cached = (Product) redisTemplate.opsForValue().get(key); | ||
| return Optional.ofNullable(cached); | ||
| } catch (Exception ignored) { | ||
| return Optional.empty(); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public void putProductDetail(Long productId, Product product) { | ||
| String key = detailKey(productId); | ||
| try { | ||
| redisTemplate.opsForValue().set(key, product, TTL_DETAIL); | ||
| } catch (Exception ignored) { | ||
| } | ||
| } | ||
|
|
||
| private String listKey(Long brandId, Pageable pageable) { | ||
| String sortkey = pageable.getSort().toString(); | ||
| return "product:list:" | ||
| + (brandId == null ? "all" : brandId) + ":" | ||
| + pageable.getPageNumber() + ":" | ||
| + pageable.getPageSize() + ":" | ||
| + sortkey; | ||
| } | ||
|
|
||
| private String detailKey(Long productId) { | ||
| return "product:detail:" + productId; | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| # 📝 Round 6 Quests | ||
|
|
||
| --- | ||
|
|
||
| ## 💻 Implementation Quest | ||
|
|
||
| > 외부 시스템(PG) 장애 및 지연에 대응하는 Resilience 설계를 학습하고 적용해봅니다. | ||
| `pg-simulator` 모듈을 활용하여 다양한 비동기 시스템과의 연동 및 실패 시나리오를 구현, 점검합니다. | ||
|
|
||
| <aside> | ||
| 🎯 | ||
|
|
||
| **Must-Have (이번 주에 무조건 가져가야 좋을 것-**무조건****하세요**)** | ||
|
|
||
| - Fallback | ||
| - Timeout | ||
| - CircuitBreaker | ||
|
|
||
| **Nice-To-Have (부가적으로 가져가면 좋을 것-**시간이****허락하면****꼭****해보세요**)** | ||
|
|
||
| - Retryer | ||
|
|
||
| </aside> | ||
|
|
||
| ### **📦 추가 요구사항** | ||
|
|
||
| ```java | ||
| ###결제 요청 | ||
|
|
||
| POST { | ||
| { | ||
| pg - simulator | ||
| } | ||
| }/api/v1/payments | ||
| X-USER-ID:135135 | ||
| Content-Type:application/ | ||
|
|
||
| json { | ||
| "orderId":"1351039135", | ||
| "cardType":"SAMSUNG", | ||
| "cardNo":"1234-5678-9814-1451", | ||
| "amount" :"5000", | ||
| "callbackUrl":"http://localhost:8080/api/v1/examples/callback" | ||
| } | ||
|
|
||
| ### | ||
| 결제 정보 | ||
| 확인 | ||
|
|
||
| GET { | ||
| { | ||
| pg - simulator | ||
| } | ||
| }/api/v1/payments/20250816:TR:9577c5 | ||
| X-USER-ID:135135 | ||
|
|
||
| ### | ||
| 주문에 엮인 | ||
| 결제 정보 | ||
| 조회 | ||
|
|
||
| GET { | ||
| { | ||
| pg - simulator | ||
| } | ||
| }/api/v1/payments?orderId=1351039135 | ||
| X-USER-ID:135135 | ||
| ``` | ||
|
Comment on lines
+27
to
+68
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. API 예시의 포맷팅을 수정하세요. 코드 블록 내 JSON 형식이 깨져있습니다. HTTP 요청 예시와 JSON 페이로드가 혼재되어 가독성이 떨어집니다. 다음과 같이 분리하여 작성하는 것을 권장합니다: -###결제 요청
-
-POST {
- {
- pg - simulator
- }
-}/api/v1/payments
-X-USER-ID:135135
-Content-Type:application/
-
-json {
- "orderId":"1351039135",
- "cardType":"SAMSUNG",
- "cardNo":"1234-5678-9814-1451",
- "amount" :"5000",
- "callbackUrl":"http://localhost:8080/api/v1/examples/callback"
-}
+### 결제 요청
+
+POST http://localhost:8080/api/v1/payments
+X-USER-ID: 135135
+Content-Type: application/json
+
+{
+ "orderId": "1351039135",
+ "cardType": "SAMSUNG",
+ "cardNo": "1234-5678-9814-1451",
+ "amount": "5000",
+ "callbackUrl": "http://localhost:8080/api/v1/examples/callback"
+}🤖 Prompt for AI Agents |
||
|
|
||
| - 결제 수단으로 PG 기반 카드 결제 기능을 추가합니다. | ||
| - PG 시스템은 로컬에서 실행가능한 `pg-simulator` 모듈이 제공됩니다. ( 별도 SpringBootApp ) | ||
| - PG 시스템은 **비동기 결제** 기능을 제공합니다. | ||
|
|
||
| > *비동기 결제란, 요청과 실제 처리가 분리되어 있음을 의미합니다.* | ||
| **요청 성공 확률 : 60% | ||
| 요청 지연 :** 100ms ~ 500ms | ||
| **처리 지연** : 1s ~ 5s | ||
| **처리 결과** | ||
|
|
||
| * 성공 : 70% | ||
| * 한도 초과 : 20% | ||
| * 잘못된 카드 : 10% | ||
|
|
||
| > | ||
|
|
||
| ### 📋 과제 정보 | ||
|
|
||
| - 외부 시스템에 대해 적절한 타임아웃 기준에 대해 고려해보고, 적용합니다. | ||
| - 외부 시스템의 응답 지연 및 실패에 대해서 대처할 방법에 대해 고민해 봅니다. | ||
| - PG 결제 결과를 적절하게 시스템과 연동하고 이를 기반으로 주문 상태를 안전하게 처리할 방법에 대해 고민해 봅니다. | ||
| - 서킷브레이커를 통해 외부 시스템의 지연, 실패에 대해 대응하여 서비스 전체가 무너지지 않도록 보호합니다. | ||
|
|
||
| --- | ||
|
|
||
| ## ✅ Checklist | ||
|
|
||
| ### **⚡ PG 연동 대응** | ||
|
|
||
| - [ ] PG 연동 API는 RestTemplate 혹은 FeignClient 로 외부 시스템을 호출한다. | ||
| - [ ] 응답 지연에 대해 타임아웃을 설정하고, 실패 시 적절한 예외 처리 로직을 구현한다. | ||
| - [ ] 결제 요청에 대한 실패 응답에 대해 적절한 시스템 연동을 진행한다. | ||
| - [ ] 콜백 방식 + **결제 상태 확인 API**를 활용해 적절하게 시스템과 결제정보를 연동한다. | ||
|
|
||
| ### **🛡 Resilience 설계** | ||
|
|
||
| - [ ] 서킷 브레이커 혹은 재시도 정책을 적용하여 장애 확산을 방지한다. | ||
| - [ ] 외부 시스템 장애 시에도 내부 시스템은 **정상적으로 응답**하도록 보호한다. | ||
| - [ ] 콜백이 오지 않더라도, 일정 주기 혹은 수동 API 호출로 상태를 복구할 수 있다. | ||
| - [ ] PG 에 대한 요청이 타임아웃에 의해 실패되더라도 해당 결제건에 대한 정보를 확인하여 정상적으로 시스템에 반영한다. | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.