From 38ed877c494e69f856445f76238b4283407298d3 Mon Sep 17 00:00:00 2001 From: Rinjae Date: Sun, 18 Jan 2026 21:36:04 +0900 Subject: [PATCH] =?UTF-8?q?OBP=20API=20=EC=A3=BC=EC=86=8C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kjb-docs/obp-api-guide.md | 565 ++++++++++++++++++ .../onl/apim/obp/ProviderCodeValidator.java | 27 + 2 files changed, 592 insertions(+) create mode 100644 kjb-docs/obp-api-guide.md create mode 100644 src/main/java/com/eactive/eai/rms/onl/apim/obp/ProviderCodeValidator.java diff --git a/kjb-docs/obp-api-guide.md b/kjb-docs/obp-api-guide.md new file mode 100644 index 0000000..29e6e44 --- /dev/null +++ b/kjb-docs/obp-api-guide.md @@ -0,0 +1,565 @@ +# OBP API 사용자 가이드 + +## 개요 + +OBP (OpenBanking Platform) API는 API Gateway의 메트릭 데이터와 API 정보를 JSON 형식으로 제공하는 REST 기반 API입니다. + +**API 기본 URL**: `/monitoring/api/` + +**지원 형식**: JSON + +## 인증 및 권한 + +### providerCode 파라미터 + +OBP API는 모든 요청에서 `providerCode` 파라미터로 권한을 검증합니다. + +- **허용 값**: `KJB` (대소문자 무시: `kjb`, `Kjb` 등 모두 가능) +- **검증**: 요청 시 providerCode가 명시적으로 포함되어야 합니다 + +**예시**: +``` +GET /api/apis.json?providerCode=KJB +GET /api/apis.json?providerCode=kjb +GET /api/metrics/after-timeslice.json?timeslice=20251202030000&providerCode=KJB +``` + +## API 엔드포인트 + +### 1. API 전체 목록 조회 + +#### 요청 + +```http +GET /api/apis.json +``` + +**쿼리 파라미터**: +- `pretty` (선택): `true`면 JSON을 들여쓰기 형식으로 반환 + +**예시**: +```bash +curl "http://localhost:8080/monitoring/api/apis.json?pretty=true" +``` + +#### 응답 + +```json +[ + { + "interfaceId": "UAGFF00001UIE", + "interfaceName": "고객 조회 API", + "status": "1", + "description": "고객 기본정보 조회" + }, + { + "interfaceId": "UAGFF00002UIE", + "interfaceName": "거래 조회 API", + "status": "1", + "description": "거래 내역 조회" + } +] +``` + +**응답 필드**: +- `interfaceId` (string): 인터페이스 ID +- `interfaceName` (string): 인터페이스 명 +- `status` (string): 상태 (1=활성) +- `description` (string): 설명 + +**HTTP 상태 코드**: +- `200 OK`: 조회 성공 +- `500 Internal Server Error`: 서버 오류 + +--- + +### 2. API 목록 조회 (providerCode 검증) + +#### 요청 + +```http +GET /api/apis.json?providerCode=KJB +``` + +**쿼리 파라미터**: +- `providerCode` (필수): Provider 코드 (KJB만 허용, 대소문자 무시) +- `pretty` (선택): `true`면 JSON을 들여쓰기 형식으로 반환 + +**예시**: +```bash +curl "http://localhost:8080/monitoring/api/apis.json?providerCode=KJB&pretty=true" +curl "http://localhost:8080/monitoring/api/apis.json?providerCode=kjb" +``` + +#### 응답 + +전체 API 목록 (응답 형식은 엔드포인트 1과 동일) + +**HTTP 상태 코드**: +- `200 OK`: 조회 성공 +- `400 Bad Request`: 유효하지 않은 providerCode + ```json + { + "errorCode": "BIZ_EXCEPTION", + "errorMessage": "Invalid providerCode. Expected 'KJB' (case-insensitive), got: INVALID" + } + ``` +- `500 Internal Server Error`: 서버 오류 + +--- + +### 3. API 목록 조회 (기관별) + +#### 요청 + +```http +GET /api/apis.json?providerCode=KJB&partnerCode={orgCode} +``` + +**쿼리 파라미터**: +- `providerCode` (필수): Provider 코드 (KJB만 허용, 대소문자 무시) +- `partnerCode` (필수): 기관코드 (OBP 파트너코드) +- `pretty` (선택): `true`면 JSON을 들여쓰기 형식으로 반환 + +**예시**: +```bash +curl "http://localhost:8080/monitoring/api/apis.json?providerCode=KJB&partnerCode=000008-01&pretty=true" +curl "http://localhost:8080/monitoring/api/apis.json?providerCode=kjb&partnerCode=000008-01" +``` + +#### 응답 + +특정 기관이 사용하는 API 목록 (응답 형식은 엔드포인트 1과 동일) + +**주의사항**: +- 기관이 존재하지 않으면 빈 배열(`[]`)이 반환됩니다 +- 해당 기관의 활성 앱(appstatus='1')이 사용하는 API만 반환됩니다 + +**HTTP 상태 코드**: +- `200 OK`: 조회 성공 (데이터 있음 또는 없음) +- `400 Bad Request`: 유효하지 않은 providerCode 또는 partnerCode +- `500 Internal Server Error`: 서버 오류 + +--- + +### 4. GwMetric 데이터 조회 (시간대 이후) + +#### 요청 + +```http +GET /api/metrics/after-timeslice.json?timeslice={yyyyMMddHHmmss}&providerCode=KJB +``` + +**쿼리 파라미터**: +- `timeslice` (필수): 조회 시작 시간대 (형식: `yyyyMMddHHmmss`) + - 예: `20251202030000` (2025-12-02 03:00:00) + - 분/초는 자동으로 정각(00:00)으로 처리됩니다 +- `providerCode` (필수): Provider 코드 (KJB만 허용, 대소문자 무시) +- `pretty` (선택): `true`면 JSON을 들여쓰기 형식으로 반환 + +**예시**: +```bash +curl "http://localhost:8080/monitoring/api/metrics/after-timeslice.json?timeslice=20251202030000&providerCode=KJB" +curl "http://localhost:8080/monitoring/api/metrics/after-timeslice.json?timeslice=20251202030000&providerCode=kjb&pretty=true" +``` + +#### 응답 + +```json +[ + { + "timeslice": "2025-12-02 03:00:00", + "transactionCount": 1250, + "successCount": 1230, + "failureCount": 20, + "averageResponseTime": 145 + }, + { + "timeslice": "2025-12-02 04:00:00", + "transactionCount": 1380, + "successCount": 1360, + "failureCount": 20, + "averageResponseTime": 152 + } +] +``` + +**응답 필드**: +- `timeslice` (string): 시간대 (형식: `yyyy-MM-dd HH:00:00`) +- `transactionCount` (number): 총 거래 건수 +- `successCount` (number): 성공 건수 +- `failureCount` (number): 실패 건수 +- `averageResponseTime` (number): 평균 응답 시간 (ms) + +**HTTP 상태 코드**: +- `200 OK`: 조회 성공 +- `400 Bad Request`: 유효하지 않은 timeslice 또는 providerCode + ```json + { + "errorCode": "BIZ_EXCEPTION", + "errorMessage": "Invalid timeslice format. Expected 'yyyyMMddHHmmss', got: 2025-12-02_03:00:00" + } + ``` +- `500 Internal Server Error`: 서버 오류 + +--- + +## 예제 코드 + +### JavaScript (fetch API) + +```javascript +// 1. API 전체 목록 조회 +async function getAllApis() { + try { + const response = await fetch( + 'http://localhost:8080/monitoring/api/apis.json?pretty=true' + ); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + console.log('All APIs:', data); + return data; + } catch (error) { + console.error('Error fetching APIs:', error); + } +} + +// 2. 기관별 API 목록 조회 +async function getApisByPartner(partnerCode) { + try { + const response = await fetch( + `http://localhost:8080/monitoring/api/apis.json?providerCode=KJB&partnerCode=${partnerCode}&pretty=true` + ); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + console.log(`APIs for ${partnerCode}:`, data); + return data; + } catch (error) { + console.error('Error fetching APIs:', error); + } +} + +// 3. GwMetric 데이터 조회 +async function getMetricsAfterTimeslice(timeslice) { + try { + const response = await fetch( + `http://localhost:8080/monitoring/api/metrics/after-timeslice.json?timeslice=${timeslice}&providerCode=KJB&pretty=true` + ); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + console.log(`Metrics after ${timeslice}:`, data); + return data; + } catch (error) { + console.error('Error fetching metrics:', error); + } +} + +// 사용 예 +getAllApis(); +getApisByPartner('000008-01'); +getMetricsAfterTimeslice('20251202030000'); +``` + +### JavaScript (axios) + +```javascript +const axios = require('axios'); + +const BASE_URL = 'http://localhost:8080/monitoring/api'; + +// 1. API 전체 목록 조회 +async function getAllApis() { + try { + const response = await axios.get(`${BASE_URL}/apis.json`, { + params: { pretty: true } + }); + console.log('All APIs:', response.data); + return response.data; + } catch (error) { + console.error('Error fetching APIs:', error.response?.data || error.message); + } +} + +// 2. 기관별 API 목록 조회 +async function getApisByPartner(partnerCode) { + try { + const response = await axios.get(`${BASE_URL}/apis.json`, { + params: { + providerCode: 'KJB', + partnerCode: partnerCode, + pretty: true + } + }); + console.log(`APIs for ${partnerCode}:`, response.data); + return response.data; + } catch (error) { + console.error('Error fetching APIs:', error.response?.data || error.message); + } +} + +// 3. GwMetric 데이터 조회 +async function getMetricsAfterTimeslice(timeslice) { + try { + const response = await axios.get(`${BASE_URL}/metrics/after-timeslice.json`, { + params: { + timeslice: timeslice, + providerCode: 'KJB', + pretty: true + } + }); + console.log(`Metrics after ${timeslice}:`, response.data); + return response.data; + } catch (error) { + console.error('Error fetching metrics:', error.response?.data || error.message); + } +} +``` + +### Java (RestTemplate) + +```java +import org.springframework.web.client.RestTemplate; +import java.util.List; +import java.util.Map; + +public class ObpApiClient { + + private final RestTemplate restTemplate = new RestTemplate(); + private static final String BASE_URL = "http://localhost:8080/monitoring/api"; + + // 1. API 전체 목록 조회 + public List getAllApis() { + String url = BASE_URL + "/apis.json?pretty=true"; + return restTemplate.getForObject(url, List.class); + } + + // 2. 기관별 API 목록 조회 + public List getApisByPartner(String partnerCode) { + String url = BASE_URL + "/apis.json?" + + "providerCode=KJB&" + + "partnerCode=" + partnerCode + "&" + + "pretty=true"; + return restTemplate.getForObject(url, List.class); + } + + // 3. GwMetric 데이터 조회 + public List getMetricsAfterTimeslice(String timeslice) { + String url = BASE_URL + "/metrics/after-timeslice.json?" + + "timeslice=" + timeslice + "&" + + "providerCode=KJB&" + + "pretty=true"; + return restTemplate.getForObject(url, List.class); + } + + // 사용 예 + public static void main(String[] args) { + ObpApiClient client = new ObpApiClient(); + + // 1. API 목록 조회 + List apis = client.getAllApis(); + System.out.println("All APIs: " + apis); + + // 2. 기관별 API 조회 + List partnerApis = client.getApisByPartner("000008-01"); + System.out.println("Partner APIs: " + partnerApis); + + // 3. 메트릭 조회 + List metrics = client.getMetricsAfterTimeslice("20251202030000"); + System.out.println("Metrics: " + metrics); + } +} +``` + +### cURL + +```bash +# 1. API 전체 목록 조회 +curl "http://localhost:8080/monitoring/api/apis.json?pretty=true" + +# 2. 기관별 API 목록 조회 +curl "http://localhost:8080/monitoring/api/apis.json?providerCode=KJB&partnerCode=000008-01&pretty=true" + +# 3. GwMetric 데이터 조회 +curl "http://localhost:8080/monitoring/api/metrics/after-timeslice.json?timeslice=20251202030000&providerCode=KJB&pretty=true" + +# providerCode를 소문자로 전송 +curl "http://localhost:8080/monitoring/api/apis.json?providerCode=kjb" +``` + +--- + +## 에러 처리 + +### 에러 응답 형식 + +```json +{ + "errorCode": "BIZ_EXCEPTION", + "errorMessage": "Invalid providerCode. Expected 'KJB' (case-insensitive), got: INVALID" +} +``` + +### 주요 에러 코드 + +| 에러 코드 | HTTP 상태 | 원인 | 해결 방법 | +|---------|---------|------|---------| +| `BIZ_EXCEPTION` | 400 | 유효하지 않은 providerCode | providerCode를 "KJB" (대소문자 무시)로 설정 | +| `BIZ_EXCEPTION` | 400 | 유효하지 않은 timeslice 형식 | timeslice를 `yyyyMMddHHmmss` 형식으로 변경 | +| `INTERNAL_ERROR` | 500 | 서버 오류 | 시스템 관리자에게 문의 | + +--- + +## 마이그레이션 가이드 + +### 레거시 엔드포인트에서 마이그레이션 + +레거시 `/kjb/` 경로 엔드포인트는 앞으로 지원이 중단될 예정입니다. 새로운 `/api/` 경로로 마이그레이션하세요. + +#### ObpApiController + +**기존 엔드포인트** → **새 엔드포인트** + +``` +GET /kjb/apis.json + ↓ +GET /api/apis.json?providerCode=KJB +``` + +``` +GET /kjb/{orgCode}/apis.json + ↓ +GET /api/apis.json?providerCode=KJB&partnerCode={orgCode} +``` + +**예시**: + +기존: +```bash +curl "http://localhost:8080/monitoring/kjb/apis.json" +curl "http://localhost:8080/monitoring/kjb/000008-01/apis.json" +``` + +새로운: +```bash +curl "http://localhost:8080/monitoring/api/apis.json?providerCode=KJB" +curl "http://localhost:8080/monitoring/api/apis.json?providerCode=KJB&partnerCode=000008-01" +``` + +#### ObpGwMetricController + +**기존 엔드포인트** → **새 엔드포인트** + +``` +GET /kjb/gw-metrics/{datetime}.json + ↓ +GET /api/metrics/after-timeslice.json?timeslice={yyyyMMddHHmmss}&providerCode=KJB +``` + +``` +GET /kjb/gw-metrics/after-timeslice/{datetime}.json + ↓ +GET /api/metrics/after-timeslice.json?timeslice={yyyyMMddHHmmss}&providerCode=KJB +``` + +**datetime 형식 변환**: + +| 기존 형식 | 새 형식 | +|---------|--------| +| `yyyy-MM-dd_HH:mm:ss` | `yyyyMMddHHmmss` | +| `2025-12-02_03:00:00` | `20251202030000` | +| `2026-01-18_15:30:45` | `20260118153045` | + +**예시**: + +기존: +```bash +curl "http://localhost:8080/monitoring/kjb/gw-metrics/2025-12-02_03:00:00.json" +curl "http://localhost:8080/monitoring/kjb/gw-metrics/after-timeslice/2025-12-02_03:00:00.json" +``` + +새로운: +```bash +curl "http://localhost:8080/monitoring/api/metrics/after-timeslice.json?timeslice=20251202030000&providerCode=KJB" +``` + +--- + +## FAQ + +### Q1: providerCode는 무엇인가요? + +**A**: providerCode는 API 접근 권한을 검증하는 파라미터입니다. 현재 OBP API는 "KJB" (광주은행)만 허용합니다. + +### Q2: providerCode 대소문자를 혼합해도 되나요? + +**A**: 네, 가능합니다. `KJB`, `kjb`, `Kjb`, `kJB` 등 모두 허용됩니다. + +### Q3: timeslice 형식은 어떻게 변환하나요? + +**A**: `yyyy-MM-dd_HH:mm:ss` → `yyyyMMddHHmmss` 형식으로 변환합니다. +- 예: `2025-12-02_03:30:45` → `20251202033045` +- 분과 초도 포함되어 있으므로 정확하게 입력하세요. + +### Q4: timeslice에 분/초를 입력해도 되나요? + +**A**: 네, 입력 가능합니다. 하지만 시스템은 자동으로 정각(00분, 00초)으로 처리합니다. +- 입력: `20251202033045` (03:30:45) +- 처리: `2025-12-02 03:00:00` + +### Q5: 기관이 없으면 어떻게 되나요? + +**A**: 기관이 존재하지 않으면 빈 배열(`[]`)이 반환됩니다. 에러가 발생하지 않습니다. + +### Q6: 응답 형식을 변경할 수 있나요? + +**A**: 현재 JSON 형식만 지원합니다. 다른 형식이 필요하면 시스템 관리자에게 문의하세요. + +### Q7: pretty 파라미터를 사용하지 않으면 어떻게 되나요? + +**A**: pretty 파라미터를 생략하면 compact한 형식(들여쓰기 없음)으로 반환됩니다. +- `?pretty=true`: 들여쓰기 적용 (가독성 좋음, 용량 큼) +- 파라미터 없음: compact 형식 (가독성 낮음, 용량 작음) + +### Q8: API 응답 시간은 어느 정도인가요? + +**A**: 응답 헤더에 `elapsed` 정보가 포함됩니다. 로그에서도 확인할 수 있습니다. + +--- + +## 최신 변경사항 + +### v2.0 (2025-01-18) + +- **새로운 `/api/*` 경로**: 기존 `/kjb/*` 경로 대신 새로운 경로 제공 +- **providerCode 검증**: 모든 엔드포인트에 providerCode 파라미터 추가 +- **timeslice 형식 변경**: `yyyy-MM-dd_HH:mm:ss` → `yyyyMMddHHmmss` +- **레거시 엔드포인트 Deprecated**: 기존 `/kjb/*` 엔드포인트는 계속 지원되지만 향후 제거 예정 + +**마이그레이션 권장**: 가능한 한 빨리 새로운 경로로 업데이트하세요. + +--- + +## 지원 + +문제가 발생하거나 추가 정보가 필요한 경우: + +1. **기술 문의**: API 개발팀 (api-support@example.com) +2. **버그 신고**: GitHub Issues +3. **기능 요청**: 관리자에게 연락 + +--- + +**마지막 업데이트**: 2025-01-18 +**API 버전**: 2.0 diff --git a/src/main/java/com/eactive/eai/rms/onl/apim/obp/ProviderCodeValidator.java b/src/main/java/com/eactive/eai/rms/onl/apim/obp/ProviderCodeValidator.java new file mode 100644 index 0000000..3002303 --- /dev/null +++ b/src/main/java/com/eactive/eai/rms/onl/apim/obp/ProviderCodeValidator.java @@ -0,0 +1,27 @@ +package com.eactive.eai.rms.onl.apim.obp; + +import com.eactive.eai.rms.onl.common.exception.BizException; + +/** + * OBP API providerCode 검증 유틸리티 + * + *

API 요청의 providerCode 파라미터를 검증합니다.

+ */ +public class ProviderCodeValidator { + + private static final String VALID_PROVIDER_CODE = "KJB"; + + /** + * providerCode가 유효한지 검증 (대소문자 무시) + * + * @param providerCode 검증할 providerCode + * @throws BizException providerCode가 null이거나 "KJB"가 아닌 경우 + */ + public static void validateProviderCode(String providerCode) { + if (providerCode == null || !VALID_PROVIDER_CODE.equalsIgnoreCase(providerCode.trim())) { + throw new BizException( + String.format("Invalid providerCode. Expected 'KJB' (case-insensitive), got: %s", providerCode) + ); + } + } +}