From 655af7af63dcad4aadb769523e7db643137a90c1 Mon Sep 17 00:00:00 2001 From: ByungCheol Date: Sun, 5 Jul 2026 14:51:13 +0900 Subject: [PATCH] =?UTF-8?q?feat(fx):=20=ED=99=98=EC=9C=A8=20=EC=BA=90?= =?UTF-8?q?=EC=8B=9C=20=EC=8B=9C=EA=B0=81=20=EB=85=B8=EC=B6=9C=20=E2=80=94?= =?UTF-8?q?=20updatedAt=20=EC=9D=91=EB=8B=B5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - FxService: Cached 레코드에 fetchedAt(Instant) 추가, getCachedAt() 공개 - FxController: GET /api/fx/rate 응답에 updatedAt(ISO 문자열) 포함 Co-Authored-By: Claude Sonnet 4.6 --- .../com/sb/web/account/controller/FxController.java | 3 +++ .../java/com/sb/web/account/service/FxService.java | 13 +++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/sb/web/account/controller/FxController.java b/src/main/java/com/sb/web/account/controller/FxController.java index 9ee1c54..4ef8fd9 100644 --- a/src/main/java/com/sb/web/account/controller/FxController.java +++ b/src/main/java/com/sb/web/account/controller/FxController.java @@ -7,6 +7,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.math.BigDecimal; +import java.time.Instant; import java.util.HashMap; import java.util.Map; @@ -28,10 +29,12 @@ public class FxController { public Map rate(@RequestParam String from, @RequestParam(defaultValue = "KRW") String to) { BigDecimal rate = fxService.getRate(from, to); + Instant updatedAt = fxService.getCachedAt(from); Map res = new HashMap<>(); res.put("from", from == null ? null : from.toUpperCase()); res.put("to", to == null ? null : to.toUpperCase()); res.put("rate", rate); // 조회 실패 시 null + res.put("updatedAt", updatedAt != null ? updatedAt.toString() : null); return res; } } diff --git a/src/main/java/com/sb/web/account/service/FxService.java b/src/main/java/com/sb/web/account/service/FxService.java index 527663c..03300ed 100644 --- a/src/main/java/com/sb/web/account/service/FxService.java +++ b/src/main/java/com/sb/web/account/service/FxService.java @@ -6,6 +6,7 @@ import org.springframework.stereotype.Service; import org.springframework.web.client.RestClient; import java.math.BigDecimal; +import java.time.Instant; import java.time.LocalDate; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -21,9 +22,17 @@ public class FxService { private static final String URL = "https://open.er-api.com/v6/latest/{base}"; private final RestClient restClient = RestClient.builder().build(); - private record Cached(LocalDate date, JsonNode rates) {} + private record Cached(LocalDate date, Instant fetchedAt, JsonNode rates) {} private final Map cache = new ConcurrentHashMap<>(); + /** from 통화 1단위 → to 통화 환율. 조회 실패 시 null. */ + /** 해당 통화 환율이 마지막으로 캐시된 시각. 캐시 없으면 null. */ + public Instant getCachedAt(String from) { + if (from == null) return null; + Cached c = cache.get(from.trim().toUpperCase()); + return c != null ? c.fetchedAt() : null; + } + /** from 통화 1단위 → to 통화 환율. 조회 실패 시 null. */ public BigDecimal getRate(String from, String to) { if (from == null || to == null) return null; @@ -54,7 +63,7 @@ public class FxService { if (rates.isMissingNode() || !rates.isObject()) { return c != null ? c.rates() : null; } - cache.put(base, new Cached(today, rates)); + cache.put(base, new Cached(today, Instant.now(), rates)); return rates; } catch (Exception e) { log.warn("환율 조회 실패 base={}: {}", base, e.toString());