You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
VIPS/docs/API_ARCHITECTURE.md

32 KiB

VMIS API 통합 아키텍처 문서

📋 목차


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 (전략 패턴)

// 공통 인터페이스
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)

# application.yml
vmis:
  integration:
    mode: internal  # 또는 external

Spring의 @ConditionalOnProperty를 통해 설정값에 따라 자동으로 적절한 구현체가 Bean으로 등록됩니다.

@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

@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 통신을 수행합니다.

@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

요청 데이터를 자동으로 보강합니다.

@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 암호화를 사용합니다.

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 설정 예시

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

@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를 호출합니다.

@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 설정 예시

vmis:
  integration:
    mode: external

  external:
    api:
      url: "http://localhost:8081/api/v1/vehicles"
      connectTimeoutMillis: 5000
      readTimeoutMillis: 10000

6. 공통 구성요소

6.1 VehicleInfoService 인터페이스

public interface VehicleInfoService {
    // 단일 차량 조회
    VehicleApiResponseVO getVehicleInfo(String vehicleNumber);

    // 일괄 조회
    List<VehicleApiResponseVO> getVehiclesInfo(List<String> vehicleNumbers);
}

6.2 공통 VO (vo 패키지)

VehicleApiResponseVO

@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의 설정을 바인딩하는 클래스입니다.

@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를 스캔합니다.

@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 설정

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 설정

vmis:
  integration:
    mode: external  # ⭐ External Mode 활성화

  # 외부 API 서버 설정
  external:
    api:
      url: "http://localhost:8081/api/v1/vehicles"
      connectTimeoutMillis: 5000
      readTimeoutMillis: 10000

8.3 개발 환경 설정 (GPKI 비활성화)

vmis:
  integration:
    mode: internal

  gpki:
    enabled: "N"  # ⭐ GPKI 비활성화

9. 사용 예제

9.1 기본 사용법

@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에서 사용

@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 응답 데이터 활용

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 일괄 조회 예제

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 개발 → 운영 전환

# 개발 환경 (application-local.yml)
vmis:
  integration:
    mode: internal
  gpki:
    enabled: "N"  # GPKI 비활성화
# 운영 환경 (application-prod.yml)
vmis:
  integration:
    mode: internal
  gpki:
    enabled: "Y"  # GPKI 활성화
  system:
    infoSysIp: "${SERVER_IP}"  # 실제 서버 IP

10.2 Monolithic → MSA 전환

# Phase 1: 단일 애플리케이션 (Internal Mode)
vmis:
  integration:
    mode: internal
# 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 설정이 잘못되었거나 누락됨

해결:

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 관리

vmis:
  gov:
    services:
      basic:
        apiKey: "${GOV_API_KEY_BASIC}"  # 환경변수 사용

13.3 로깅 주의사항

  • 차량번호 등 개인정보는 마스킹 처리
  • 요청/응답 전문은 DEBUG 레벨로 출력

14. 확장 가능성

14.1 새로운 모드 추가

Strategy Pattern을 사용하므로 새로운 구현체 추가 가능:

@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