diff --git a/docs/API_ARCHITECTURE.md b/docs/API_ARCHITECTURE.md new file mode 100644 index 0000000..99f397b --- /dev/null +++ b/docs/API_ARCHITECTURE.md @@ -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 getVehiclesInfo(List 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 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 getVehiclesInfo(List 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 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> getVehicles( + @RequestBody List vehicleNumbers) { + + List 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 vehicleNumbers = Arrays.asList( + "12가3456", + "34나5678", + "56다7890" +); + +List responses = + vehicleInfoService.getVehiclesInfo(vehicleNumbers); + +// 성공/실패 분류 +List successful = responses.stream() + .filter(VehicleApiResponseVO::isSuccess) + .collect(Collectors.toList()); + +List 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