docs: VMIS API 통합 아키텍처 상세 문서 추가

API 패키지 구조, Internal/External 패턴, 데이터 흐름 상세 설명

## 문서 내용
- API 패키지 전체 구조 및 각 구성요소 설명
- Strategy Pattern을 통한 Internal/External 모드 전환
- 각 모드별 상세 동작 원리 및 데이터 흐름
- 설정 가이드 및 사용 예제
- 트러블슈팅 및 성능 최적화 가이드

## 주요 섹션
1. 개요 및 설계 원칙
2. 패키지 구조 (디렉토리 트리)
3. 아키텍처 패턴 (Strategy, DI, 계층화)
4. Internal Mode 상세 (GPKI, 정부 API 연동)
5. External Mode 상세 (REST API 호출)
6. 공통 구성요소 (VO, Properties, Config)
7. 데이터 흐름 다이어그램
8. 설정 가이드 (개발/운영 환경)
9. 사용 예제 (Controller, Service)
10. 모드 전환 시나리오
11. 트러블슈팅
12. 성능 최적화
13. 보안 고려사항
14. 확장 가능성

## 파일 위치
docs/API_ARCHITECTURE.md

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
internalApi
박성영 1 month ago
parent 9b5edc7993
commit 2f03f11782

@ -0,0 +1,958 @@
# VMIS API 통합 아키텍처 문서
## 📋 목차
- [1. 개요](#1-개요)
- [2. 패키지 구조](#2-패키지-구조)
- [3. 아키텍처 패턴](#3-아키텍처-패턴)
- [4. Internal Mode 상세](#4-internal-mode-상세)
- [5. External Mode 상세](#5-external-mode-상세)
- [6. 공통 구성요소](#6-공통-구성요소)
- [7. 데이터 흐름](#7-데이터-흐름)
- [8. 설정 가이드](#8-설정-가이드)
- [9. 사용 예제](#9-사용-예제)
---
## 1. 개요
### 1.1 목적
VMIS(차량관리정보시스템) API 통합 모듈은 차량 정보 조회 기능을 제공하며, 두 가지 운영 모드를 지원합니다:
- **Internal Mode**: 내부 VMIS 모듈을 직접 호출 (성능 우선)
- **External Mode**: 외부 REST API 서버 호출 (분리된 아키텍처)
### 1.2 설계 원칙
- **Strategy Pattern**: 런타임에 알고리즘(구현체) 전환
- **Dependency Inversion**: 인터페이스에 의존, 구현체는 교체 가능
- **Single Responsibility**: 각 클래스는 단일 책임만 수행
- **Open-Closed**: 확장에는 열려있고 수정에는 닫혀있음
---
## 2. 패키지 구조
```
src/main/java/go/kr/project/api/
├── VehicleInfoService.java # 공통 인터페이스
├── config/ # 공통 설정
│ ├── ApiConstant.java # API 상수
│ ├── ApiMapperConfig.java # API Mapper 스캔 설정
│ ├── VmisIntegrationConfig.java # 통합 설정 및 모니터링
│ └── properties/
│ └── VmisProperties.java # 설정 바인딩 클래스
├── vo/ # 공통 응답 VO (공유)
│ ├── VehicleApiResponseVO.java # 최상위 응답 객체
│ ├── VehicleBasicInfoVO.java # 차량 기본정보 VO
│ ├── VehicleLedgerVO.java # 차량 등록원부 VO
│ ├── VehicleBasicRequestVO.java # 기본정보 요청 VO
│ └── VehicleLedgerRequestVO.java # 등록원부 요청 VO
├── internal/ # Internal Mode (내부 모듈)
│ ├── service/
│ │ ├── InternalVehicleInfoServiceImpl.java # 내부 모드 구현체 ⭐
│ │ ├── VmisCarBassMatterInqireService.java # 기본정보 조회 서비스
│ │ ├── VmisCarBassMatterInqireLogService.java # 기본정보 로그 서비스
│ │ ├── VmisCarLedgerFrmbkService.java # 등록원부 조회 서비스
│ │ └── VmisCarLedgerFrmbkLogService.java # 등록원부 로그 서비스
│ │
│ ├── client/
│ │ ├── GovernmentApi.java # 정부 API 인터페이스
│ │ └── GovernmentApiClient.java # 정부 API 클라이언트
│ │
│ ├── mapper/
│ │ ├── VmisCarBassMatterInqireMapper.java # 기본정보 Mapper
│ │ └── VmisCarLedgerFrmbkMapper.java # 등록원부 Mapper
│ │
│ ├── model/ # Internal 전용 모델
│ │ ├── basic/
│ │ │ ├── BasicRequest.java # 기본정보 요청
│ │ │ ├── BasicResponse.java # 기본정보 응답
│ │ │ └── VmisCarBassMatterInqireVO.java
│ │ ├── ledger/
│ │ │ ├── LedgerRequest.java # 등록원부 요청
│ │ │ ├── LedgerResponse.java # 등록원부 응답
│ │ │ ├── VmisCarLedgerFrmbkVO.java
│ │ │ └── VmisCarLedgerFrmbkDtlVO.java
│ │ └── common/
│ │ └── Envelope.java # 공통 Envelope
│ │
│ ├── config/
│ │ ├── HttpClientConfig.java # RestTemplate 설정
│ │ ├── OpenApiConfig.java # OpenAPI 설정
│ │ ├── PropertiesConfig.java # Properties 빈 등록
│ │ ├── GpkiConfig.java # GPKI 설정
│ │ └── Globals.java # 전역 상수
│ │
│ ├── controller/
│ │ └── VehicleInterfaceController.java # REST API 컨트롤러
│ │
│ ├── enricher/
│ │ └── VmisRequestEnricher.java # 요청 데이터 자동 보강
│ │
│ ├── gpki/
│ │ ├── GpkiService.java # GPKI 인터페이스
│ │ ├── RealGpkiService.java # 실제 GPKI 구현
│ │ └── NoopGpkiService.java # GPKI 비활성화 구현
│ │
│ └── util/
│ └── VehicleResponseMapper.java # 내부↔외부 VO 변환
└── external/ # External Mode (외부 API)
└── service/
├── ExternalVehicleInfoServiceImpl.java # 외부 모드 구현체 ⭐
└── ExternalVehicleApiService.java # 외부 API 호출 서비스
```
---
## 3. 아키텍처 패턴
### 3.1 Strategy Pattern (전략 패턴)
```java
// 공통 인터페이스
public interface VehicleInfoService {
VehicleApiResponseVO getVehicleInfo(String vehicleNumber);
List<VehicleApiResponseVO> getVehiclesInfo(List<String> vehicleNumbers);
}
// Internal Mode 구현체
@Service
@ConditionalOnProperty(name = "vmis.integration.mode", havingValue = "internal")
public class InternalVehicleInfoServiceImpl implements VehicleInfoService {
// 내부 VMIS 모듈 직접 호출
}
// External Mode 구현체
@Service
@ConditionalOnProperty(name = "vmis.integration.mode", havingValue = "external", matchIfMissing = true)
public class ExternalVehicleInfoServiceImpl implements VehicleInfoService {
// 외부 REST API 호출
}
```
**장점:**
- 런타임에 구현체 전환 가능
- 클라이언트 코드는 인터페이스만 의존
- 새로운 전략 추가 시 기존 코드 수정 불필요
### 3.2 의존성 주입 (Dependency Injection)
```yaml
# application.yml
vmis:
integration:
mode: internal # 또는 external
```
Spring의 `@ConditionalOnProperty`를 통해 설정값에 따라 자동으로 적절한 구현체가 Bean으로 등록됩니다.
```java
@Autowired
private VehicleInfoService vehicleInfoService;
// 설정에 따라 InternalVehicleInfoServiceImpl 또는
// ExternalVehicleInfoServiceImpl이 주입됨
```
### 3.3 계층화 아키텍처
```
┌─────────────────────────────────────┐
│ Controller Layer │ ← REST API 엔드포인트
├─────────────────────────────────────┤
│ Service Layer (VehicleInfoService)│ ← 비즈니스 로직
├─────────────────────────────────────┤
│ Client/Mapper Layer │ ← 데이터 접근
├─────────────────────────────────────┤
│ External System (정부 API/DB) │ ← 외부 시스템
└─────────────────────────────────────┘
```
---
## 4. Internal Mode 상세
### 4.1 개요
내부 VMIS 모듈을 직접 사용하여 정부 시스템(MOLIT)과 통신합니다.
**장점:**
- 네트워크 오버헤드 감소 (외부 서버 호출 불필요)
- 빠른 응답 속도
- 단일 애플리케이션으로 운영 가능
**단점:**
- GPKI 인증서 관리 필요
- 정부 API 연동 복잡도 증가
### 4.2 주요 구성요소
#### 4.2.1 InternalVehicleInfoServiceImpl
```java
@Service
@ConditionalOnProperty(name = "vmis.integration.mode", havingValue = "internal")
public class InternalVehicleInfoServiceImpl implements VehicleInfoService {
private final VmisCarBassMatterInqireService carBassMatterInqireService;
private final VmisCarLedgerFrmbkService carLedgerFrmbkService;
@Override
public VehicleApiResponseVO getVehicleInfo(String vehicleNumber) {
// 1. 기본정보 조회
VehicleBasicInfoVO basicInfo = getBasicInfo(vehicleNumber);
// 2. 등록원부 조회
VehicleLedgerVO ledgerInfo = getLedgerInfo(vehicleNumber);
// 3. 결과 통합
return VehicleApiResponseVO.builder()
.vhrno(vehicleNumber)
.basicInfo(basicInfo)
.ledgerInfo(ledgerInfo)
.build();
}
}
```
#### 4.2.2 GovernmentApiClient
정부 API와 실제 HTTP 통신을 수행합니다.
```java
@Component
@RequiredArgsConstructor
public class GovernmentApiClient implements GovernmentApi {
@Qualifier("vmisRestTemplate")
private final RestTemplate restTemplate;
@Override
public ResponseEntity<String> basic(String requestBody) {
String url = baseUrl + basicServicePath;
return restTemplate.postForEntity(url, requestBody, String.class);
}
}
```
#### 4.2.3 VmisRequestEnricher
요청 데이터를 자동으로 보강합니다.
```java
@Component
public class VmisRequestEnricher {
public void enrichBasicRequest(BasicRequest request) {
// 시스템 정보 자동 설정
request.setInfoSysId(vmisProperties.getSystem().getInfoSysId());
request.setInfoSysIp(vmisProperties.getSystem().getInfoSysIp());
request.setRegionCode(vmisProperties.getSystem().getRegionCode());
// ...
}
}
```
#### 4.2.4 GPKI 암호화
운영 환경에서 정부 API 통신 시 GPKI 암호화를 사용합니다.
```java
public interface GpkiService {
String encrypt(String plainText);
String decrypt(String encryptedText);
}
// 개발: NoopGpkiService (암호화 없음)
// 운영: RealGpkiService (실제 GPKI 암호화)
```
### 4.3 데이터 흐름
```
[Client]
[InternalVehicleInfoServiceImpl]
├─► [VmisCarBassMatterInqireService]
│ │
│ ├─► [VmisRequestEnricher] ──► Request 보강
│ ├─► [GovernmentApiClient] ──► HTTP POST
│ │ │
│ │ ▼
│ │ [정부 API - 기본정보]
│ │ │
│ │ ▼
│ ├─► [GpkiService] ──► 암호화/복호화
│ ├─► [VmisCarBassMatterInqireMapper] ──► DB 저장
│ └─► BasicResponse
├─► [VmisCarLedgerFrmbkService]
│ │
│ └─► (기본정보와 동일한 흐름)
└─► [VehicleResponseMapper] ──► 내부 VO → 외부 VO 변환
VehicleApiResponseVO
```
### 4.4 설정 예시
```yaml
vmis:
integration:
mode: internal
system:
infoSysId: "41-345"
infoSysIp: "105.19.10.135"
regionCode: "41460"
gov:
scheme: "http"
host: "10.188.225.94:29001"
basePath: "/piss/api/molit"
services:
basic:
path: "/SignguCarBassMatterInqireService"
apiKey: "${GOV_API_KEY_BASIC}"
ledger:
path: "/SignguCarLedgerFrmbkService"
apiKey: "${GOV_API_KEY_LEDGER}"
gpki:
enabled: "Y" # 운영: Y, 개발: N
certServerId: "SVR5640020001"
targetServerId: "SVR1500000015"
```
---
## 5. External Mode 상세
### 5.1 개요
외부 VMIS-interface 서버의 REST API를 호출하여 차량 정보를 조회합니다.
**장점:**
- 마이크로서비스 아키텍처 지원
- GPKI 관리를 외부 서버에 위임
- 간단한 설정
**단점:**
- 네트워크 오버헤드 증가
- 외부 서버 의존성
### 5.2 주요 구성요소
#### 5.2.1 ExternalVehicleInfoServiceImpl
```java
@Service
@ConditionalOnProperty(name = "vmis.integration.mode", havingValue = "external", matchIfMissing = true)
public class ExternalVehicleInfoServiceImpl implements VehicleInfoService {
private final ExternalVehicleApiService externalVehicleApiService;
@Override
public VehicleApiResponseVO getVehicleInfo(String vehicleNumber) {
// 외부 API 서비스에 위임
return externalVehicleApiService.getVehicleInfo(vehicleNumber);
}
}
```
#### 5.2.2 ExternalVehicleApiService
외부 REST API를 호출합니다.
```java
@Service
@RequiredArgsConstructor
public class ExternalVehicleApiService {
private final RestTemplate restTemplate;
public VehicleApiResponseVO getVehicleInfo(String vehicleNumber) {
String url = vmisProperties.getExternal().getApi().getUrl();
VehicleBasicRequestVO request = new VehicleBasicRequestVO();
request.setVhrno(vehicleNumber);
return restTemplate.postForObject(url + "/basic", request, VehicleApiResponseVO.class);
}
}
```
### 5.3 데이터 흐름
```
[Client]
[ExternalVehicleInfoServiceImpl]
[ExternalVehicleApiService]
[RestTemplate] ──► HTTP POST
[VMIS-interface Server] (외부)
├─► 정부 API 호출
├─► GPKI 암호화/복호화
├─► DB 저장
└─► VehicleApiResponseVO 반환
[Client]
```
### 5.4 설정 예시
```yaml
vmis:
integration:
mode: external
external:
api:
url: "http://localhost:8081/api/v1/vehicles"
connectTimeoutMillis: 5000
readTimeoutMillis: 10000
```
---
## 6. 공통 구성요소
### 6.1 VehicleInfoService 인터페이스
```java
public interface VehicleInfoService {
// 단일 차량 조회
VehicleApiResponseVO getVehicleInfo(String vehicleNumber);
// 일괄 조회
List<VehicleApiResponseVO> getVehiclesInfo(List<String> vehicleNumbers);
}
```
### 6.2 공통 VO (vo 패키지)
#### VehicleApiResponseVO
```java
@Data
@Builder
public class VehicleApiResponseVO {
private String vhrno; // 차량번호
private boolean success; // 성공 여부
private String message; // 메시지
private VehicleBasicInfoVO basicInfo; // 기본정보
private VehicleLedgerVO ledgerInfo; // 등록원부 정보
}
```
#### VehicleBasicInfoVO
차량 기본정보 (소유자, 차종, 연식 등)
#### VehicleLedgerVO
자동차 등록원부 정보 (등록일, 변경이력 등)
### 6.3 VmisProperties
application.yml의 설정을 바인딩하는 클래스입니다.
```java
@Data
@Component
@ConfigurationProperties(prefix = "vmis")
public class VmisProperties {
private Integration integration;
private System system;
private Gov gov;
private Gpki gpki;
private External external;
}
```
### 6.4 ApiMapperConfig
API 전용 MyBatis Mapper를 스캔합니다.
```java
@Configuration
@MapperScan(basePackages = "go.kr.project.api.internal.mapper")
public class ApiMapperConfig {
}
```
---
## 7. 데이터 흐름
### 7.1 Internal Mode 전체 흐름
```
┌─────────────────────────────────────────────────────────────────────┐
│ 1. Client Request │
│ vehicleInfoService.getVehicleInfo("12가3456") │
└────────────────────────┬────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ 2. InternalVehicleInfoServiceImpl │
│ - BasicRequest 생성 (vhrno="12가3456") │
│ - LedgerRequest 생성 (vhrno="12가3456") │
└────────────────────────┬────────────────────────────────────────────┘
┌───────────────┴───────────────┐
│ │
▼ ▼
┌──────────────────────┐ ┌──────────────────────┐
│ 3-1. Basic Service │ │ 3-2. Ledger Service │
│ - VmisRequestEnricher│ │ - VmisRequestEnricher│
│ (시스템 정보 자동 설정)│ │ (시스템 정보 자동 설정)│
└──────┬───────────────┘ └──────┬───────────────┘
│ │
▼ ▼
┌──────────────────────┐ ┌──────────────────────┐
│ 4-1. 정부 API 호출 │ │ 4-2. 정부 API 호출 │
│ - GovernmentApiClient│ │ - GovernmentApiClient│
│ - GPKI 암호화 (운영) │ │ - GPKI 암호화 (운영) │
│ - HTTP POST │ │ - HTTP POST │
└──────┬───────────────┘ └──────┬───────────────┘
│ │
▼ ▼
┌──────────────────────┐ ┌──────────────────────┐
│ 5-1. 응답 처리 │ │ 5-2. 응답 처리 │
│ - GPKI 복호화 │ │ - GPKI 복호화 │
│ - JSON 파싱 │ │ - JSON 파싱 │
│ - DB 저장 (Mapper) │ │ - DB 저장 (Mapper) │
└──────┬───────────────┘ └──────┬───────────────┘
│ │
└───────────────┬───────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ 6. VehicleResponseMapper │
│ - BasicResponse → VehicleBasicInfoVO 변환 │
│ - LedgerResponse → VehicleLedgerVO 변환 │
└────────────────────────┬────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ 7. Response │
│ VehicleApiResponseVO { │
│ vhrno: "12가3456", │
│ success: true, │
│ basicInfo: { ... }, │
│ ledgerInfo: { ... } │
│ } │
└─────────────────────────────────────────────────────────────────────┘
```
### 7.2 External Mode 전체 흐름
```
┌─────────────────────────────────────────────────────────────────────┐
│ 1. Client Request │
│ vehicleInfoService.getVehicleInfo("12가3456") │
└────────────────────────┬────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ 2. ExternalVehicleInfoServiceImpl │
│ - ExternalVehicleApiService에 위임 │
└────────────────────────┬────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ 3. ExternalVehicleApiService │
│ - VehicleBasicRequestVO 생성 │
│ - RestTemplate으로 외부 API 호출 │
└────────────────────────┬────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ 4. HTTP Request to External Server │
│ POST http://localhost:8081/api/v1/vehicles/basic │
│ Body: { vhrno: "12가3456" } │
└────────────────────────┬────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ 5. VMIS-interface Server (외부) │
│ - 정부 API 호출 │
│ - GPKI 처리 │
│ - DB 저장 │
│ - VehicleApiResponseVO 반환 │
└────────────────────────┬────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ 6. Response │
│ VehicleApiResponseVO { │
│ vhrno: "12가3456", │
│ success: true, │
│ basicInfo: { ... }, │
│ ledgerInfo: { ... } │
│ } │
└─────────────────────────────────────────────────────────────────────┘
```
---
## 8. 설정 가이드
### 8.1 Internal Mode 설정
```yaml
vmis:
integration:
mode: internal # ⭐ Internal Mode 활성화
# 시스템 정보 (요청 헤더에 자동 포함)
system:
infoSysId: "41-345"
infoSysIp: "${SERVER_IP:105.19.10.135}"
regionCode: "41460"
departmentCode: ""
chargerId: ""
chargerIp: ""
chargerNm: ""
# 정부 API 연동 설정
gov:
scheme: "http"
host: "10.188.225.94:29001"
basePath: "/piss/api/molit"
connectTimeoutMillis: 5000
readTimeoutMillis: 10000
services:
basic:
path: "/SignguCarBassMatterInqireService"
cntcInfoCode: "AC1_FD11_01"
apiKey: "${GOV_API_KEY_BASIC}"
ledger:
path: "/SignguCarLedgerFrmbkService"
cntcInfoCode: "AC1_FD11_02"
apiKey: "${GOV_API_KEY_LEDGER}"
# GPKI 암호화 (운영 환경 필수)
gpki:
enabled: "Y" # Y: 암호화 사용, N: 암호화 미사용
certServerId: "SVR5640020001"
targetServerId: "SVR1500000015"
envCertFilePathName: "src/GPKI/certs/SVR5640020001_env.cer"
sigCertFilePathName: "src/GPKI/certs/SVR5640020001_sig.cer"
```
### 8.2 External Mode 설정
```yaml
vmis:
integration:
mode: external # ⭐ External Mode 활성화
# 외부 API 서버 설정
external:
api:
url: "http://localhost:8081/api/v1/vehicles"
connectTimeoutMillis: 5000
readTimeoutMillis: 10000
```
### 8.3 개발 환경 설정 (GPKI 비활성화)
```yaml
vmis:
integration:
mode: internal
gpki:
enabled: "N" # ⭐ GPKI 비활성화
```
---
## 9. 사용 예제
### 9.1 기본 사용법
```java
@RestController
@RequestMapping("/api/vehicles")
@RequiredArgsConstructor
public class VehicleController {
private final VehicleInfoService vehicleInfoService;
/**
* 단일 차량 조회
*/
@GetMapping("/{vehicleNumber}")
public ResponseEntity<VehicleApiResponseVO> getVehicle(
@PathVariable String vehicleNumber) {
VehicleApiResponseVO response = vehicleInfoService.getVehicleInfo(vehicleNumber);
if (response.isSuccess()) {
return ResponseEntity.ok(response);
} else {
return ResponseEntity.badRequest().body(response);
}
}
/**
* 일괄 조회
*/
@PostMapping("/batch")
public ResponseEntity<List<VehicleApiResponseVO>> getVehicles(
@RequestBody List<String> vehicleNumbers) {
List<VehicleApiResponseVO> responses =
vehicleInfoService.getVehiclesInfo(vehicleNumbers);
return ResponseEntity.ok(responses);
}
}
```
### 9.2 Service Layer에서 사용
```java
@Service
@RequiredArgsConstructor
public class InspectionService {
private final VehicleInfoService vehicleInfoService;
public void performInspection(String vehicleNumber) {
// 1. 차량 정보 조회
VehicleApiResponseVO vehicleInfo =
vehicleInfoService.getVehicleInfo(vehicleNumber);
if (!vehicleInfo.isSuccess()) {
throw new VehicleNotFoundException(vehicleNumber);
}
// 2. 차량 정보를 사용한 비즈니스 로직
VehicleBasicInfoVO basicInfo = vehicleInfo.getBasicInfo();
String ownerName = basicInfo.getVhrNm(); // 소유자명
String modelYear = basicInfo.getYridnw(); // 연식
// 3. 검사 수행
// ...
}
}
```
### 9.3 응답 데이터 활용
```java
VehicleApiResponseVO response = vehicleInfoService.getVehicleInfo("12가3456");
if (response.isSuccess()) {
// 기본정보 활용
VehicleBasicInfoVO basic = response.getBasicInfo();
System.out.println("차량번호: " + basic.getVhrno());
System.out.println("소유자: " + basic.getVhrNm());
System.out.println("차종: " + basic.getVhctyNm());
System.out.println("용도: " + basic.getVhclPrposNm());
// 등록원부 활용
VehicleLedgerVO ledger = response.getLedgerInfo();
System.out.println("최초등록일: " + ledger.getFrstRegistDe());
System.out.println("등록상태: " + ledger.getRegistSttusNm());
} else {
System.err.println("조회 실패: " + response.getMessage());
}
```
### 9.4 일괄 조회 예제
```java
List<String> vehicleNumbers = Arrays.asList(
"12가3456",
"34나5678",
"56다7890"
);
List<VehicleApiResponseVO> responses =
vehicleInfoService.getVehiclesInfo(vehicleNumbers);
// 성공/실패 분류
List<VehicleApiResponseVO> successful = responses.stream()
.filter(VehicleApiResponseVO::isSuccess)
.collect(Collectors.toList());
List<VehicleApiResponseVO> failed = responses.stream()
.filter(r -> !r.isSuccess())
.collect(Collectors.toList());
System.out.println("성공: " + successful.size());
System.out.println("실패: " + failed.size());
```
---
## 10. 모드 전환 시나리오
### 10.1 개발 → 운영 전환
```yaml
# 개발 환경 (application-local.yml)
vmis:
integration:
mode: internal
gpki:
enabled: "N" # GPKI 비활성화
```
```yaml
# 운영 환경 (application-prod.yml)
vmis:
integration:
mode: internal
gpki:
enabled: "Y" # GPKI 활성화
system:
infoSysIp: "${SERVER_IP}" # 실제 서버 IP
```
### 10.2 Monolithic → MSA 전환
```yaml
# Phase 1: 단일 애플리케이션 (Internal Mode)
vmis:
integration:
mode: internal
```
```yaml
# Phase 2: 마이크로서비스 (External Mode)
vmis:
integration:
mode: external
external:
api:
url: "http://vmis-service:8080/api/v1/vehicles"
```
**코드 변경 없이 설정만으로 전환 가능!**
---
## 11. 트러블슈팅
### 11.1 Bean 충돌 오류
**증상:**
```
expected single matching bean but found 2:
internalVehicleInfoServiceImpl, externalVehicleInfoServiceImpl
```
**원인:**
`vmis.integration.mode` 설정이 잘못되었거나 누락됨
**해결:**
```yaml
vmis:
integration:
mode: internal # 또는 external (반드시 지정)
```
### 11.2 Mapper 중복 스캔 경고
**증상:**
```
Skipping MapperFactoryBean with name 'vmisCarBassMatterInqireMapper'
Bean already defined with the same name!
```
**원인:**
여러 `@MapperScan`에서 같은 패키지를 중복 스캔
**해결:**
`ApiMapperConfig`에서 API 전용 Mapper만 스캔하도록 분리되어 있음
### 11.3 GPKI 오류 (운영 환경)
**증상:**
```
GPKI 초기화 실패: 인증서 파일을 찾을 수 없습니다
```
**해결:**
1. GPKI 인증서 파일 경로 확인
2. 개발 환경에서는 `gpki.enabled: "N"` 설정
---
## 12. 성능 최적화
### 12.1 Internal Mode 최적화
- RestTemplate Connection Pool 설정 활용
- GPKI 세션 재사용
- DB 커넥션 풀 튜닝
### 12.2 External Mode 최적화
- 외부 API 서버와 동일 네트워크 배치
- Circuit Breaker 패턴 적용 (향후)
- 캐싱 전략 (향후)
---
## 13. 보안 고려사항
### 13.1 GPKI 인증서 관리
- 인증서 파일 접근 권한 제한
- 패스워드는 환경변수로 관리
- 인증서 만료 모니터링
### 13.2 API Key 관리
```yaml
vmis:
gov:
services:
basic:
apiKey: "${GOV_API_KEY_BASIC}" # 환경변수 사용
```
### 13.3 로깅 주의사항
- 차량번호 등 개인정보는 마스킹 처리
- 요청/응답 전문은 DEBUG 레벨로 출력
---
## 14. 확장 가능성
### 14.1 새로운 모드 추가
Strategy Pattern을 사용하므로 새로운 구현체 추가 가능:
```java
@Service
@ConditionalOnProperty(name = "vmis.integration.mode", havingValue = "cache")
public class CachedVehicleInfoServiceImpl implements VehicleInfoService {
// 캐시 기반 구현
}
```
### 14.2 새로운 차량 정보 조회 API 추가
1. 정부 API 인터페이스 메서드 추가
2. Service 계층에 메서드 추가
3. Mapper 및 Model 추가
4. VehicleInfoService 인터페이스 확장
---
## 15. 참고 자료
### 15.1 관련 설정 파일
- `src/main/resources/application.yml`
- `src/main/resources/mybatis/mapper/api-internal/`
### 15.2 관련 문서
- Spring Boot Conditional Beans
- Strategy Pattern
- MyBatis Integration
---
**문서 버전:** 1.0
**최종 수정일:** 2025-11-07
**작성자:** VIPS Development Team
Loading…
Cancel
Save