parent
b160476ddc
commit
6c677f7f69
@ -0,0 +1,802 @@
|
||||
# VMIS 통합 전략 설계 문서
|
||||
|
||||
**작성일**: 2025-11-06
|
||||
**목적**: 설정 기반으로 내부/외부 차량 정보 조회 방식을 선택할 수 있는 아키텍처 설계
|
||||
|
||||
---
|
||||
|
||||
## 목차
|
||||
|
||||
1. [요구사항](#1-요구사항)
|
||||
2. [아키텍처 설계](#2-아키텍처-설계)
|
||||
3. [구현 가이드](#3-구현-가이드)
|
||||
4. [설정 예시](#4-설정-예시)
|
||||
5. [테스트 방법](#5-테스트-방법)
|
||||
|
||||
---
|
||||
|
||||
## 1. 요구사항
|
||||
|
||||
### 1.1 핵심 요구사항
|
||||
|
||||
**application.yml 설정에 따라 두 가지 방식 중 선택 가능**:
|
||||
|
||||
1. **Internal Mode (내부 모드)**
|
||||
- 신규 통합된 VMIS 모듈을 직접 호출
|
||||
- 네트워크 오버헤드 없음
|
||||
- 단일 트랜잭션 처리 가능
|
||||
- 높은 성능
|
||||
|
||||
2. **External Mode (외부 모드)**
|
||||
- 기존 ExternalVehicleApiService를 통해 외부 REST API 호출
|
||||
- 독립적인 VMIS-interface 서버와 통신
|
||||
- 기존 방식 유지 (하위 호환성)
|
||||
|
||||
### 1.2 전환 시나리오
|
||||
|
||||
- **개발 단계**: External 모드로 테스트 (기존 방식)
|
||||
- **통합 테스트**: Internal 모드로 전환하여 성능 검증
|
||||
- **점진적 배포**: 일부 서버는 External, 일부는 Internal
|
||||
- **롤백 대비**: 문제 발생 시 External 모드로 즉시 전환
|
||||
|
||||
---
|
||||
|
||||
## 2. 아키텍처 설계
|
||||
|
||||
### 2.1 전략 패턴 (Strategy Pattern) 적용
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ VehicleInfoServiceFacade │ (클라이언트 코드)
|
||||
│ - getBasicInfo(vhrno) │
|
||||
│ - getLedgerInfo(vhrno) │
|
||||
└─────────────────┬───────────────────────┘
|
||||
│ 의존
|
||||
↓
|
||||
┌─────────────────────────────────────────┐
|
||||
│ <<interface>> │
|
||||
│ VehicleInfoService │ (전략 인터페이스)
|
||||
│ + getBasicInfo(vhrno): ResponseDTO │
|
||||
│ + getLedgerInfo(vhrno): ResponseDTO │
|
||||
└─────────────────┬───────────────────────┘
|
||||
│
|
||||
┌────────┴─────────┐
|
||||
↓ ↓
|
||||
┌──────────────────┐ ┌─────────────────────────┐
|
||||
│ Internal모드 │ │ External모드 │
|
||||
│ │ │ │
|
||||
│ Internal │ │ External │
|
||||
│ VehicleInfo │ │ VehicleInfo │
|
||||
│ ServiceImpl │ │ ServiceImpl │
|
||||
│ │ │ │
|
||||
│ @Conditional │ │ @Conditional │
|
||||
│ OnProperty │ │ OnProperty │
|
||||
│ (internal) │ │ (external) │
|
||||
│ │ │ │
|
||||
│ ↓ │ │ ↓ │
|
||||
│ VMIS 모듈 │ │ RestTemplate │
|
||||
│ 직접 호출 │ │ (HTTP 호출) │
|
||||
└──────────────────┘ └─────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.2 컴포넌트 설계
|
||||
|
||||
#### 2.2.1 인터페이스
|
||||
|
||||
**VehicleInfoService.java** (전략 인터페이스)
|
||||
```java
|
||||
package go.kr.project.common.service;
|
||||
|
||||
import go.kr.project.common.vo.VehicleBasicInfoResponseVO;
|
||||
import go.kr.project.common.vo.VehicleLedgerResponseVO;
|
||||
|
||||
public interface VehicleInfoService {
|
||||
|
||||
/**
|
||||
* 차량 기본정보 조회
|
||||
* @param vhrno 차량번호
|
||||
* @return 차량 기본정보 응답
|
||||
*/
|
||||
VehicleBasicInfoResponseVO getBasicInfo(String vhrno);
|
||||
|
||||
/**
|
||||
* 차량 등록원부 조회
|
||||
* @param vhrno 차량번호
|
||||
* @return 차량 등록원부 응답
|
||||
*/
|
||||
VehicleLedgerResponseVO getLedgerInfo(String vhrno);
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2.2 구현체 1: Internal Mode
|
||||
|
||||
**InternalVehicleInfoServiceImpl.java**
|
||||
```java
|
||||
package go.kr.project.vmis.service;
|
||||
|
||||
import go.kr.project.common.service.VehicleInfoService;
|
||||
import go.kr.project.common.vo.*;
|
||||
import go.kr.project.vmis.service.CarBassMatterInqireService;
|
||||
import go.kr.project.vmis.service.CarLedgerFrmbkService;
|
||||
import go.kr.project.vmis.model.vo.*;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@ConditionalOnProperty(name = "vmis.integration.mode", havingValue = "internal")
|
||||
public class InternalVehicleInfoServiceImpl implements VehicleInfoService {
|
||||
|
||||
private final CarBassMatterInqireService carBassMatterInqireService;
|
||||
private final CarLedgerFrmbkService carLedgerFrmbkService;
|
||||
|
||||
@Override
|
||||
public VehicleBasicInfoResponseVO getBasicInfo(String vhrno) {
|
||||
log.info("[INTERNAL MODE] 차량 기본정보 조회 - 차량번호: {}", vhrno);
|
||||
|
||||
try {
|
||||
// 내부 VMIS 모듈 직접 호출
|
||||
CarBassMatterInqireRequestVO request = new CarBassMatterInqireRequestVO();
|
||||
request.setVhrno(vhrno);
|
||||
|
||||
Envelope<VehicleBasicInfoDTO> response =
|
||||
carBassMatterInqireService.getBasicInfo(request);
|
||||
|
||||
// DTO → VO 변환
|
||||
return convertToBasicInfoResponse(response);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("[INTERNAL MODE] 차량 기본정보 조회 실패 - 차량번호: {}", vhrno, e);
|
||||
return createErrorResponse(vhrno, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public VehicleLedgerResponseVO getLedgerInfo(String vhrno) {
|
||||
log.info("[INTERNAL MODE] 차량 등록원부 조회 - 차량번호: {}", vhrno);
|
||||
|
||||
try {
|
||||
// 내부 VMIS 모듈 직접 호출
|
||||
CarLedgerFrmbkRequestVO request = new CarLedgerFrmbkRequestVO();
|
||||
request.setVhrno(vhrno);
|
||||
|
||||
Envelope<VehicleLedgerDTO> response =
|
||||
carLedgerFrmbkService.getLedgerInfo(request);
|
||||
|
||||
// DTO → VO 변환
|
||||
return convertToLedgerResponse(response);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("[INTERNAL MODE] 차량 등록원부 조회 실패 - 차량번호: {}", vhrno, e);
|
||||
return createLedgerErrorResponse(vhrno, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// DTO → VO 변환 메서드들...
|
||||
private VehicleBasicInfoResponseVO convertToBasicInfoResponse(Envelope<VehicleBasicInfoDTO> envelope) {
|
||||
// 구현...
|
||||
}
|
||||
|
||||
private VehicleLedgerResponseVO convertToLedgerResponse(Envelope<VehicleLedgerDTO> envelope) {
|
||||
// 구현...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2.3 구현체 2: External Mode
|
||||
|
||||
**ExternalVehicleInfoServiceImpl.java**
|
||||
```java
|
||||
package go.kr.project.externalApi.service;
|
||||
|
||||
import go.kr.project.common.service.VehicleInfoService;
|
||||
import go.kr.project.common.vo.*;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@ConditionalOnProperty(name = "vmis.integration.mode", havingValue = "external", matchIfMissing = true)
|
||||
public class ExternalVehicleInfoServiceImpl implements VehicleInfoService {
|
||||
|
||||
private final RestTemplate restTemplate;
|
||||
|
||||
@Value("${vmis.external.api.url:http://localhost:8081/api/v1/vehicles}")
|
||||
private String vmisApiUrl;
|
||||
|
||||
@Override
|
||||
public VehicleBasicInfoResponseVO getBasicInfo(String vhrno) {
|
||||
log.info("[EXTERNAL MODE] 차량 기본정보 조회 - 차량번호: {}", vhrno);
|
||||
|
||||
try {
|
||||
String url = vmisApiUrl + "/basic";
|
||||
|
||||
// 기존 ExternalVehicleApiService 로직 활용
|
||||
// RestTemplate으로 외부 API 호출
|
||||
VehicleBasicRequestVO request = new VehicleBasicRequestVO();
|
||||
request.setVhrno(vhrno);
|
||||
|
||||
VehicleBasicInfoResponseVO response = restTemplate.postForObject(
|
||||
url,
|
||||
request,
|
||||
VehicleBasicInfoResponseVO.class
|
||||
);
|
||||
|
||||
return response;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("[EXTERNAL MODE] 차량 기본정보 조회 실패 - 차량번호: {}", vhrno, e);
|
||||
return createErrorResponse(vhrno, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public VehicleLedgerResponseVO getLedgerInfo(String vhrno) {
|
||||
log.info("[EXTERNAL MODE] 차량 등록원부 조회 - 차량번호: {}", vhrno);
|
||||
|
||||
try {
|
||||
String url = vmisApiUrl + "/ledger";
|
||||
|
||||
VehicleLedgerRequestVO request = new VehicleLedgerRequestVO();
|
||||
request.setVhrno(vhrno);
|
||||
|
||||
VehicleLedgerResponseVO response = restTemplate.postForObject(
|
||||
url,
|
||||
request,
|
||||
VehicleLedgerResponseVO.class
|
||||
);
|
||||
|
||||
return response;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("[EXTERNAL MODE] 차량 등록원부 조회 실패 - 차량번호: {}", vhrno, e);
|
||||
return createLedgerErrorResponse(vhrno, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2.4 설정 클래스
|
||||
|
||||
**VmisIntegrationConfig.java**
|
||||
```java
|
||||
package go.kr.project.vmis.config;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(VmisProperties.class)
|
||||
public class VmisIntegrationConfig {
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnProperty(name = "vmis.integration.mode", havingValue = "internal")
|
||||
static class InternalModeConfig {
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
log.info("=================================================");
|
||||
log.info("VMIS Integration Mode: INTERNAL");
|
||||
log.info("차량 정보 조회: 내부 VMIS 모듈 직접 호출");
|
||||
log.info("=================================================");
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnProperty(name = "vmis.integration.mode", havingValue = "external", matchIfMissing = true)
|
||||
static class ExternalModeConfig {
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
log.info("=================================================");
|
||||
log.info("VMIS Integration Mode: EXTERNAL");
|
||||
log.info("차량 정보 조회: 외부 REST API 호출");
|
||||
log.info("=================================================");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 구현 가이드
|
||||
|
||||
### 3.1 파일 구조
|
||||
|
||||
```
|
||||
go/kr/project/
|
||||
├── common/
|
||||
│ ├── service/
|
||||
│ │ └── VehicleInfoService.java (인터페이스)
|
||||
│ └── vo/
|
||||
│ ├── VehicleBasicInfoResponseVO.java
|
||||
│ └── VehicleLedgerResponseVO.java
|
||||
│
|
||||
├── vmis/
|
||||
│ ├── config/
|
||||
│ │ ├── VmisProperties.java
|
||||
│ │ └── VmisIntegrationConfig.java (설정)
|
||||
│ └── service/
|
||||
│ └── InternalVehicleInfoServiceImpl.java (내부 모드 구현)
|
||||
│
|
||||
└── externalApi/
|
||||
└── service/
|
||||
└── ExternalVehicleInfoServiceImpl.java (외부 모드 구현)
|
||||
```
|
||||
|
||||
### 3.2 기존 코드 리팩토링
|
||||
|
||||
#### Before (기존 ExternalVehicleApiService)
|
||||
```java
|
||||
@Service
|
||||
public class ExternalVehicleApiService {
|
||||
|
||||
private final RestTemplate restTemplate;
|
||||
|
||||
public VehicleApiResponseVO getBasicInfo(String vhrno) {
|
||||
// RestTemplate으로 외부 API 호출
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### After (전략 패턴 적용)
|
||||
```java
|
||||
// 기존 ExternalVehicleApiService는 ExternalVehicleInfoServiceImpl로 대체
|
||||
// 클라이언트 코드는 VehicleInfoService 인터페이스에 의존
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class CarInspectionService {
|
||||
|
||||
private final VehicleInfoService vehicleInfoService; // 인터페이스 주입
|
||||
|
||||
public void processInspection(String vhrno) {
|
||||
// 설정에 따라 자동으로 내부/외부 구현체가 주입됨
|
||||
VehicleBasicInfoResponseVO info = vehicleInfoService.getBasicInfo(vhrno);
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 구현 순서
|
||||
|
||||
1. **VehicleInfoService 인터페이스 생성**
|
||||
- 위치: `go/kr/project/common/service/`
|
||||
- 메서드: getBasicInfo, getLedgerInfo
|
||||
|
||||
2. **InternalVehicleInfoServiceImpl 구현**
|
||||
- 위치: `go/kr/project/vmis/service/`
|
||||
- VMIS 모듈 직접 호출
|
||||
|
||||
3. **ExternalVehicleInfoServiceImpl 구현**
|
||||
- 위치: `go/kr/project/externalApi/service/`
|
||||
- 기존 ExternalVehicleApiService 로직 이전
|
||||
|
||||
4. **VmisIntegrationConfig 생성**
|
||||
- 설정 로깅 및 검증
|
||||
|
||||
5. **기존 클라이언트 코드 수정**
|
||||
- ExternalVehicleApiService → VehicleInfoService 의존성 변경
|
||||
|
||||
---
|
||||
|
||||
## 4. 설정 예시
|
||||
|
||||
### 4.1 application.yml (공통)
|
||||
|
||||
```yaml
|
||||
# VMIS 통합 모드 설정
|
||||
vmis:
|
||||
integration:
|
||||
mode: internal # internal | external
|
||||
|
||||
# Internal 모드 설정
|
||||
system:
|
||||
info-sys-id: "VMIS001"
|
||||
info-sys-ip: "${SERVER_IP:192.168.1.100}"
|
||||
manager-id: "admin"
|
||||
manager-name: "관리자"
|
||||
manager-tel: "02-1234-5678"
|
||||
|
||||
gpki:
|
||||
enabled: false
|
||||
cert-path: "${GPKI_CERT_PATH:/path/to/cert.der}"
|
||||
private-key-path: "${GPKI_PRIVATE_KEY_PATH:/path/to/private.key}"
|
||||
private-key-password: "${GPKI_PASSWORD:}"
|
||||
|
||||
government:
|
||||
host: "https://www.vemanet.com"
|
||||
base-path: "/openapi"
|
||||
connect-timeout: 10000
|
||||
read-timeout: 15000
|
||||
services:
|
||||
basic:
|
||||
path: "/carBassMatterInqire"
|
||||
api-key: "${GOV_API_KEY_BASIC:}"
|
||||
ledger:
|
||||
path: "/carLedgerFrmbk"
|
||||
api-key: "${GOV_API_KEY_LEDGER:}"
|
||||
|
||||
# External 모드 설정
|
||||
external:
|
||||
api:
|
||||
url: "http://localhost:8081/api/v1/vehicles"
|
||||
connect-timeout: 5000
|
||||
read-timeout: 10000
|
||||
```
|
||||
|
||||
### 4.2 application-local.yml (개발 환경)
|
||||
|
||||
```yaml
|
||||
vmis:
|
||||
integration:
|
||||
mode: external # 개발 중에는 외부 API 사용
|
||||
|
||||
external:
|
||||
api:
|
||||
url: "http://localhost:8081/api/v1/vehicles"
|
||||
```
|
||||
|
||||
### 4.3 application-dev.yml (개발 서버)
|
||||
|
||||
```yaml
|
||||
vmis:
|
||||
integration:
|
||||
mode: internal # 개발 서버에서는 내부 모듈 테스트
|
||||
|
||||
gpki:
|
||||
enabled: false # 개발 서버는 암호화 비활성화
|
||||
```
|
||||
|
||||
### 4.4 application-prd.yml (운영 환경)
|
||||
|
||||
```yaml
|
||||
vmis:
|
||||
integration:
|
||||
mode: internal # 운영 환경은 내부 모듈 사용
|
||||
|
||||
gpki:
|
||||
enabled: true # 운영 환경은 암호화 활성화
|
||||
cert-path: "${GPKI_CERT_PATH}"
|
||||
private-key-path: "${GPKI_PRIVATE_KEY_PATH}"
|
||||
private-key-password: "${GPKI_PASSWORD}"
|
||||
```
|
||||
|
||||
### 4.5 환경변수 설정 예시
|
||||
|
||||
```bash
|
||||
# Internal 모드 (운영)
|
||||
export VMIS_INTEGRATION_MODE=internal
|
||||
export GOV_API_KEY_BASIC=your-api-key-basic
|
||||
export GOV_API_KEY_LEDGER=your-api-key-ledger
|
||||
export GPKI_PASSWORD=your-gpki-password
|
||||
|
||||
# External 모드 (롤백 시)
|
||||
export VMIS_INTEGRATION_MODE=external
|
||||
export VMIS_EXTERNAL_API_URL=http://vmis-interface-server:8081/api/v1/vehicles
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 테스트 방법
|
||||
|
||||
### 5.1 단위 테스트
|
||||
|
||||
#### InternalVehicleInfoServiceImplTest.java
|
||||
```java
|
||||
@SpringBootTest
|
||||
@TestPropertySource(properties = {
|
||||
"vmis.integration.mode=internal"
|
||||
})
|
||||
class InternalVehicleInfoServiceImplTest {
|
||||
|
||||
@Autowired
|
||||
private VehicleInfoService vehicleInfoService;
|
||||
|
||||
@Test
|
||||
void testGetBasicInfo() {
|
||||
// given
|
||||
String vhrno = "12가3456";
|
||||
|
||||
// when
|
||||
VehicleBasicInfoResponseVO response = vehicleInfoService.getBasicInfo(vhrno);
|
||||
|
||||
// then
|
||||
assertNotNull(response);
|
||||
assertEquals(vhrno, response.getVhrno());
|
||||
assertTrue(vehicleInfoService instanceof InternalVehicleInfoServiceImpl);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### ExternalVehicleInfoServiceImplTest.java
|
||||
```java
|
||||
@SpringBootTest
|
||||
@TestPropertySource(properties = {
|
||||
"vmis.integration.mode=external",
|
||||
"vmis.external.api.url=http://localhost:8081/api/v1/vehicles"
|
||||
})
|
||||
class ExternalVehicleInfoServiceImplTest {
|
||||
|
||||
@Autowired
|
||||
private VehicleInfoService vehicleInfoService;
|
||||
|
||||
@Test
|
||||
void testGetBasicInfo() {
|
||||
// given
|
||||
String vhrno = "12가3456";
|
||||
|
||||
// when
|
||||
VehicleBasicInfoResponseVO response = vehicleInfoService.getBasicInfo(vhrno);
|
||||
|
||||
// then
|
||||
assertNotNull(response);
|
||||
assertTrue(vehicleInfoService instanceof ExternalVehicleInfoServiceImpl);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 통합 테스트
|
||||
|
||||
#### 1. Internal 모드 테스트
|
||||
```bash
|
||||
# application-local.yml 수정
|
||||
vmis:
|
||||
integration:
|
||||
mode: internal
|
||||
|
||||
# 애플리케이션 실행
|
||||
./gradlew bootRun --args='--spring.profiles.active=local'
|
||||
|
||||
# 로그 확인
|
||||
# [INTERNAL MODE] 차량 기본정보 조회 - 차량번호: 12가3456
|
||||
|
||||
# API 호출
|
||||
curl -X POST http://localhost:8080/api/inspection \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"vhrno":"12가3456"}'
|
||||
```
|
||||
|
||||
#### 2. External 모드 테스트
|
||||
```bash
|
||||
# 1. VMIS-interface 서버 실행 (8081 포트)
|
||||
cd D:\workspace\git\VMIS-interface
|
||||
./gradlew bootRun
|
||||
|
||||
# 2. application-local.yml 수정
|
||||
vmis:
|
||||
integration:
|
||||
mode: external
|
||||
external:
|
||||
api:
|
||||
url: "http://localhost:8081/api/v1/vehicles"
|
||||
|
||||
# 3. VIPS 실행
|
||||
cd D:\workspace\git\VIPS
|
||||
./gradlew bootRun --args='--spring.profiles.active=local'
|
||||
|
||||
# 로그 확인
|
||||
# [EXTERNAL MODE] 차량 기본정보 조회 - 차량번호: 12가3456
|
||||
|
||||
# API 호출
|
||||
curl -X POST http://localhost:8080/api/inspection \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"vhrno":"12가3456"}'
|
||||
```
|
||||
|
||||
### 5.3 성능 비교 테스트
|
||||
|
||||
```java
|
||||
@SpringBootTest
|
||||
class VehicleInfoServicePerformanceTest {
|
||||
|
||||
@Autowired
|
||||
private VehicleInfoService vehicleInfoService;
|
||||
|
||||
@Test
|
||||
void comparePerformance() {
|
||||
String vhrno = "12가3456";
|
||||
int iterations = 100;
|
||||
|
||||
// Warmup
|
||||
for (int i = 0; i < 10; i++) {
|
||||
vehicleInfoService.getBasicInfo(vhrno);
|
||||
}
|
||||
|
||||
// Performance test
|
||||
long startTime = System.currentTimeMillis();
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
vehicleInfoService.getBasicInfo(vhrno);
|
||||
}
|
||||
long endTime = System.currentTimeMillis();
|
||||
|
||||
long avgTime = (endTime - startTime) / iterations;
|
||||
log.info("평균 응답 시간: {}ms (모드: {})", avgTime,
|
||||
vehicleInfoService.getClass().getSimpleName());
|
||||
|
||||
// Internal 모드가 External 모드보다 빠를 것으로 예상
|
||||
assertTrue(avgTime < 100); // 100ms 이내
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.4 장애 상황 테스트
|
||||
|
||||
#### 시나리오 1: External 모드에서 외부 API 장애
|
||||
```bash
|
||||
# VMIS-interface 서버 중지
|
||||
# → External 모드는 에러 반환
|
||||
# → 설정을 Internal 모드로 변경하여 복구
|
||||
```
|
||||
|
||||
#### 시나리오 2: Internal 모드에서 DB 장애
|
||||
```bash
|
||||
# DB 연결 차단
|
||||
# → Internal 모드는 에러 반환
|
||||
# → 설정을 External 모드로 변경하여 복구
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 운영 시나리오
|
||||
|
||||
### 6.1 점진적 배포 (Blue-Green)
|
||||
|
||||
**1단계: 준비**
|
||||
```bash
|
||||
# 모든 서버: External 모드
|
||||
vmis.integration.mode=external
|
||||
```
|
||||
|
||||
**2단계: 일부 서버 전환**
|
||||
```bash
|
||||
# 서버 A, B: External 모드
|
||||
vmis.integration.mode=external
|
||||
|
||||
# 서버 C, D: Internal 모드 (테스트)
|
||||
vmis.integration.mode=internal
|
||||
```
|
||||
|
||||
**3단계: 모니터링 후 전체 전환**
|
||||
```bash
|
||||
# 모든 서버: Internal 모드
|
||||
vmis.integration.mode=internal
|
||||
```
|
||||
|
||||
### 6.2 롤백 절차
|
||||
|
||||
```bash
|
||||
# 1. application-prd.yml 또는 환경변수 수정
|
||||
vmis.integration.mode=external
|
||||
|
||||
# 2. 애플리케이션 재시작 (또는 설정 리로드)
|
||||
# 3. 로그 확인
|
||||
# [EXTERNAL MODE] ...
|
||||
|
||||
# 4. VMIS-interface 외부 서버가 실행 중인지 확인
|
||||
curl http://vmis-interface-server:8081/actuator/health
|
||||
```
|
||||
|
||||
### 6.3 모니터링 지표
|
||||
|
||||
**Prometheus/Grafana 메트릭**:
|
||||
```java
|
||||
@Component
|
||||
public class VehicleInfoServiceMetrics {
|
||||
|
||||
private final Counter internalCalls;
|
||||
private final Counter externalCalls;
|
||||
private final Timer internalTimer;
|
||||
private final Timer externalTimer;
|
||||
|
||||
// 메트릭 수집...
|
||||
}
|
||||
```
|
||||
|
||||
**로그 분석**:
|
||||
```bash
|
||||
# Internal 모드 호출 건수
|
||||
grep "\[INTERNAL MODE\]" application.log | wc -l
|
||||
|
||||
# External 모드 호출 건수
|
||||
grep "\[EXTERNAL MODE\]" application.log | wc -l
|
||||
|
||||
# 평균 응답 시간
|
||||
grep "응답시간" application.log | awk '{sum+=$NF; count++} END {print sum/count}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 주의사항
|
||||
|
||||
### 7.1 설정 검증
|
||||
|
||||
**애플리케이션 시작 시 체크**:
|
||||
```java
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class VmisIntegrationValidator implements ApplicationRunner {
|
||||
|
||||
@Value("${vmis.integration.mode}")
|
||||
private String integrationMode;
|
||||
|
||||
@Autowired(required = false)
|
||||
private VehicleInfoService vehicleInfoService;
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) {
|
||||
if (vehicleInfoService == null) {
|
||||
throw new IllegalStateException(
|
||||
"VehicleInfoService 빈이 생성되지 않았습니다. " +
|
||||
"vmis.integration.mode 설정을 확인하세요: " + integrationMode
|
||||
);
|
||||
}
|
||||
|
||||
log.info("VMIS 통합 모드 검증 완료: {} (구현체: {})",
|
||||
integrationMode,
|
||||
vehicleInfoService.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 7.2 External 모드 사용 시 주의점
|
||||
|
||||
1. **외부 서버 가용성 확인**
|
||||
- VMIS-interface 서버가 실행 중이어야 함
|
||||
- Health check 엔드포인트 모니터링
|
||||
|
||||
2. **네트워크 타임아웃 설정**
|
||||
- connect-timeout, read-timeout 적절히 설정
|
||||
- Circuit Breaker 패턴 고려
|
||||
|
||||
3. **API 버전 호환성**
|
||||
- External 서버와 클라이언트의 API 계약 일치 확인
|
||||
|
||||
### 7.3 Internal 모드 사용 시 주의점
|
||||
|
||||
1. **트랜잭션 범위**
|
||||
- @Transactional 설정 주의
|
||||
- DB 연결 풀 크기 조정
|
||||
|
||||
2. **GPKI 설정**
|
||||
- 운영 환경에서는 gpki.enabled=true
|
||||
- 인증서 경로 및 비밀번호 확인
|
||||
|
||||
3. **API 키 보안**
|
||||
- 정부 API 키는 환경변수로 관리
|
||||
- 코드에 하드코딩 금지
|
||||
|
||||
---
|
||||
|
||||
## 8. FAQ
|
||||
|
||||
### Q1: 설정을 변경하면 재시작이 필요한가요?
|
||||
**A**: 네, @ConditionalOnProperty는 애플리케이션 시작 시에만 평가됩니다. 설정 변경 후 재시작이 필요합니다.
|
||||
|
||||
### Q2: 두 모드를 동시에 사용할 수 있나요?
|
||||
**A**: 아니오, 한 번에 하나의 모드만 활성화됩니다. 설정에 따라 하나의 구현체만 빈으로 등록됩니다.
|
||||
|
||||
### Q3: External 모드에서 VMIS-interface 서버가 다운되면?
|
||||
**A**: RestTemplate 타임아웃이 발생하고 에러가 반환됩니다. Circuit Breaker 패턴을 추가로 구현하면 장애 전파를 방지할 수 있습니다.
|
||||
|
||||
### Q4: Internal 모드가 External 모드보다 얼마나 빠른가요?
|
||||
**A**: 네트워크 오버헤드가 없으므로 평균 50-100ms 정도 빠를 것으로 예상됩니다. 실제 성능은 환경에 따라 다릅니다.
|
||||
|
||||
### Q5: 개발 중에는 어떤 모드를 사용하나요?
|
||||
**A**: 개발 초기에는 External 모드를 사용하여 기존 방식으로 개발하고, 통합 테스트 단계에서 Internal 모드로 전환하여 검증합니다.
|
||||
|
||||
---
|
||||
|
||||
**문서 버전**: 1.0
|
||||
**최종 수정**: 2025-11-06
|
||||
Loading…
Reference in New Issue