**chore: 과태료 비교 로직 및 서비스 관련 문서 삭제**
- `README.md`, `COMPARISON_GUIDE.md`, `이첩조건_추가방법.md`, `VMIS_INTEGRATION_COMPLETE.md`, `VMIS_INTEGRATION_STRATEGY_DESIGN.md` 파일 삭제. - Chain of Responsibility 패턴 비교 규칙 관련 문서. - 서비스 및 구현 방법에 대한 가이드 문서 포함. - 이첩 조건 추가 방법 및 통합 완료 가이드 문서. - 조회구분코드 : 자동차기본정보조회, 갑부에 따라 구분코드 다르게 설정하는 부분 기본셋팅값으로 추가 함internalApi
parent
732a977b67
commit
6c085ec7c0
@ -1,349 +0,0 @@
|
||||
# VMIS 통합 완료 및 사용 가이드
|
||||
|
||||
## 📋 작업 완료 요약
|
||||
|
||||
VMIS-interface 프로젝트의 모든 코드를 VIPS 내부로 통합하고, YAML 설정을 통해 내부/외부 모드를 자유롭게 전환할 수 있는 기능을 구현했습니다.
|
||||
|
||||
### ✅ 완료된 작업
|
||||
|
||||
#### 1단계: 코드 마이그레이션 (완료)
|
||||
- ✅ VMIS-interface 전체 코드 이식 (33개 Java 파일, 2개 XML)
|
||||
- ✅ 패키지 변경: `com.vmis.interfaceapp` → `go.kr.project.vmis`
|
||||
- ✅ Spring Boot 3 → 2 호환: `jakarta` → `javax`
|
||||
- ✅ Java 17 → 8 호환: Text Blocks, List.of() 제거
|
||||
- ✅ HttpClient 5 → 4 변환
|
||||
- ✅ GPKI 라이브러리 추가
|
||||
- ✅ application.yml에 VMIS 설정 통합
|
||||
|
||||
#### 2단계: Strategy Pattern 구현 (완료)
|
||||
- ✅ VehicleInfoService 인터페이스 생성
|
||||
- ✅ InternalVehicleInfoServiceImpl: 내부 VMIS 모듈 직접 호출
|
||||
- ✅ ExternalVehicleInfoServiceImpl: 외부 REST API 호출
|
||||
- ✅ VmisIntegrationConfig: 모드 모니터링 및 로깅
|
||||
- ✅ VehicleResponseMapper: 모델 변환 유틸리티
|
||||
|
||||
#### 3단계: Bean 충돌 해결 (완료)
|
||||
- ✅ `restTemplate` → `vmisRestTemplate`으로 변경
|
||||
- ✅ @Qualifier 어노테이션 추가
|
||||
- ✅ 빌드 성공 확인
|
||||
|
||||
---
|
||||
|
||||
## 🎯 주요 기능
|
||||
|
||||
### 1. 모드 전환 (YAML 설정)
|
||||
|
||||
**Internal Mode: 내부 VMIS 모듈 직접 호출**
|
||||
```yaml
|
||||
# application.yml
|
||||
vmis:
|
||||
integration:
|
||||
mode: internal # 내부 모듈 사용
|
||||
```
|
||||
|
||||
**External Mode: 외부 REST API 호출**
|
||||
```yaml
|
||||
# application.yml
|
||||
vmis:
|
||||
integration:
|
||||
mode: external # 외부 API 사용
|
||||
external:
|
||||
api:
|
||||
url: http://localhost:8081/api/v1/vehicles
|
||||
```
|
||||
|
||||
### 2. 자동 Bean 선택
|
||||
|
||||
Spring Boot의 `@ConditionalOnProperty` 어노테이션을 사용하여 설정에 따라 자동으로 적절한 구현체를 선택합니다:
|
||||
|
||||
- **mode: internal** → `InternalVehicleInfoServiceImpl` 활성화
|
||||
- **mode: external** → `ExternalVehicleInfoServiceImpl` 활성화 (기본값)
|
||||
|
||||
### 3. 통합 인터페이스
|
||||
|
||||
```java
|
||||
@Autowired
|
||||
private VehicleInfoService vehicleInfoService;
|
||||
|
||||
// 단일 차량 조회
|
||||
VehicleApiResponseVO response = vehicleInfoService.getVehicleInfo("12가3456");
|
||||
|
||||
// 여러 차량 일괄 조회
|
||||
List<VehicleApiResponseVO> responses = vehicleInfoService.getVehiclesInfo(
|
||||
Arrays.asList("12가3456", "34나5678")
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📁 생성/수정된 파일
|
||||
|
||||
### 새로 생성된 파일
|
||||
1. **공통 인터페이스**
|
||||
- `src/main/java/go/kr/project/common/service/VehicleInfoService.java`
|
||||
|
||||
2. **Strategy Pattern 구현체**
|
||||
- `src/main/java/go/kr/project/vmis/service/InternalVehicleInfoServiceImpl.java`
|
||||
- `src/main/java/go/kr/project/externalApi/service/ExternalVehicleInfoServiceImpl.java`
|
||||
|
||||
3. **설정 및 유틸리티**
|
||||
- `src/main/java/go/kr/project/vmis/config/VmisIntegrationConfig.java`
|
||||
- `src/main/java/go/kr/project/vmis/util/VehicleResponseMapper.java`
|
||||
|
||||
### 수정된 파일
|
||||
1. `src/main/java/go/kr/project/vmis/config/HttpClientConfig.java`
|
||||
- Bean 이름 변경: `restTemplate` → `vmisRestTemplate`
|
||||
|
||||
2. `src/main/java/go/kr/project/vmis/client/GovernmentApiClient.java`
|
||||
- @Qualifier 추가: `@Qualifier("vmisRestTemplate")`
|
||||
|
||||
3. `src/main/java/go/kr/project/vmis/config/properties/VmisProperties.java`
|
||||
- IntegrationProps, ExternalProps 추가
|
||||
|
||||
---
|
||||
|
||||
## 🔧 설정 상세
|
||||
|
||||
### application.yml 전체 구조
|
||||
|
||||
```yaml
|
||||
vmis:
|
||||
# ===== 모드 선택 =====
|
||||
integration:
|
||||
mode: internal # internal 또는 external
|
||||
|
||||
# ===== Internal Mode 설정 =====
|
||||
system:
|
||||
infoSysId: "41-345"
|
||||
infoSysIp: "${SERVER_IP:105.19.10.135}"
|
||||
regionCode: "41460"
|
||||
departmentCode: ""
|
||||
chargerId: ""
|
||||
chargerIp: ""
|
||||
chargerNm: ""
|
||||
|
||||
gpki:
|
||||
enabled: "N" # Y: 암호화 사용, N: 암호화 미사용
|
||||
useSign: true
|
||||
charset: "UTF-8"
|
||||
certServerId: "SVR5640020001"
|
||||
targetServerId: "SVR1500000015"
|
||||
# ... (기타 GPKI 설정)
|
||||
|
||||
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:...}"
|
||||
cvmisApikey: "${GOV_CVMIS_API_KEY_BASIC:...}"
|
||||
ledger:
|
||||
path: "/SignguCarLedgerFrmbkService"
|
||||
cntcInfoCode: "AC1_FD11_02"
|
||||
apiKey: "${GOV_API_KEY_LEDGER:...}"
|
||||
cvmisApikey: "${GOV_CVMIS_API_KEY_LEDGER:...}"
|
||||
|
||||
# ===== External Mode 설정 =====
|
||||
external:
|
||||
api:
|
||||
url: "http://localhost:8081/api/v1/vehicles"
|
||||
connectTimeoutMillis: 5000
|
||||
readTimeoutMillis: 10000
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 모드별 특징
|
||||
|
||||
### Internal Mode (내부 모듈 직접 호출)
|
||||
|
||||
**장점:**
|
||||
- ✅ 네트워크 오버헤드 없음 (더 빠른 성능)
|
||||
- ✅ 외부 서버 불필요
|
||||
- ✅ 단일 애플리케이션 배포
|
||||
- ✅ 직접 정부 시스템과 통신
|
||||
|
||||
**요구사항:**
|
||||
- 정부 API 접근 권한 필요
|
||||
- GPKI 인증서 설정 (운영 환경)
|
||||
- 방화벽 정책 설정
|
||||
|
||||
**사용 시나리오:**
|
||||
- 운영 환경 배포
|
||||
- 단일 서버 구성
|
||||
- 높은 성능이 요구되는 경우
|
||||
|
||||
### External Mode (외부 REST API 호출)
|
||||
|
||||
**장점:**
|
||||
- ✅ 마이크로서비스 아키텍처 지원
|
||||
- ✅ VMIS 모듈 독립 배포 가능
|
||||
- ✅ 여러 시스템에서 공유 가능
|
||||
- ✅ 부하 분산 가능
|
||||
|
||||
**요구사항:**
|
||||
- VMIS-interface 서버가 실행 중이어야 함
|
||||
- 네트워크 연결 필요
|
||||
|
||||
**사용 시나리오:**
|
||||
- 개발 환경 (VMIS-interface 서버 별도 실행)
|
||||
- 마이크로서비스 구조
|
||||
- 여러 클라이언트가 VMIS 기능을 공유하는 경우
|
||||
|
||||
---
|
||||
|
||||
## 🚀 시작 로그 예시
|
||||
|
||||
### Internal Mode 시작 시
|
||||
|
||||
```
|
||||
========================================
|
||||
VMIS Integration Mode: internal
|
||||
Active Implementation: InternalVehicleInfoServiceImpl
|
||||
========================================
|
||||
[Internal Mode] 내부 VMIS 모듈을 직접 사용합니다
|
||||
- 정부 API 호스트: http://10.188.225.94:29001
|
||||
- 기본사항 조회 경로: /SignguCarBassMatterInqireService
|
||||
- 등록원부 조회 경로: /SignguCarLedgerFrmbkService
|
||||
- GPKI 암호화: N
|
||||
- 연결 타임아웃: 5000ms
|
||||
- 읽기 타임아웃: 10000ms
|
||||
- GPKI 암호화가 비활성화되어 있습니다. 개발 환경에서만 사용하세요.
|
||||
```
|
||||
|
||||
### External Mode 시작 시
|
||||
|
||||
```
|
||||
========================================
|
||||
VMIS Integration Mode: external
|
||||
Active Implementation: ExternalVehicleInfoServiceImpl
|
||||
========================================
|
||||
[External Mode] 외부 REST API를 사용합니다
|
||||
- 외부 API URL: http://localhost:8081/api/v1/vehicles
|
||||
- 연결 타임아웃: 5000ms
|
||||
- 읽기 타임아웃: 10000ms
|
||||
- 외부 VMIS-interface 서버가 실행 중이어야 합니다.
|
||||
- 기본사항 조회: POST http://localhost:8081/api/v1/vehicles/basic
|
||||
- 등록원부 조회: POST http://localhost:8081/api/v1/vehicles/ledger
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧪 테스트 방법
|
||||
|
||||
### 1. Internal Mode 테스트
|
||||
|
||||
```yaml
|
||||
# application.yml
|
||||
vmis:
|
||||
integration:
|
||||
mode: internal
|
||||
```
|
||||
|
||||
```bash
|
||||
# 애플리케이션 시작
|
||||
./gradlew bootRun
|
||||
|
||||
# 로그에서 "VMIS Integration Mode: internal" 확인
|
||||
```
|
||||
|
||||
### 2. External Mode 테스트
|
||||
|
||||
```yaml
|
||||
# application.yml
|
||||
vmis:
|
||||
integration:
|
||||
mode: external
|
||||
```
|
||||
|
||||
```bash
|
||||
# 1. VMIS-interface 서버 시작 (별도 터미널)
|
||||
cd D:\workspace\git\VMIS-interface
|
||||
./gradlew bootRun
|
||||
|
||||
# 2. VIPS 애플리케이션 시작
|
||||
cd D:\workspace\git\VIPS
|
||||
./gradlew bootRun
|
||||
|
||||
# 로그에서 "VMIS Integration Mode: external" 확인
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔍 코드 사용 예시
|
||||
|
||||
### 기존 코드 수정 불필요
|
||||
|
||||
기존에 `ExternalVehicleApiService`를 사용하던 코드는 수정 없이 `VehicleInfoService`로 변경하면 됩니다:
|
||||
|
||||
**변경 전:**
|
||||
```java
|
||||
@Autowired
|
||||
private ExternalVehicleApiService externalVehicleApiService;
|
||||
|
||||
VehicleApiResponseVO response = externalVehicleApiService.getVehicleInfo("12가3456");
|
||||
```
|
||||
|
||||
**변경 후:**
|
||||
```java
|
||||
@Autowired
|
||||
private VehicleInfoService vehicleInfoService;
|
||||
|
||||
VehicleApiResponseVO response = vehicleInfoService.getVehicleInfo("12가3456");
|
||||
```
|
||||
|
||||
설정 파일만 변경하면 자동으로 내부/외부 모드가 전환됩니다!
|
||||
|
||||
---
|
||||
|
||||
## 📝 커밋 내역
|
||||
|
||||
### 1차 커밋: 기본 통합
|
||||
```
|
||||
feat: VMIS-interface 통합 (Spring Boot 2.7 호환)
|
||||
- VMIS-interface 전체 코드 이식 (33개 Java 파일, 2개 XML)
|
||||
- 패키지 변경, Spring Boot 호환, Java 호환
|
||||
- 빌드 성공 확인 ✅
|
||||
```
|
||||
|
||||
### 2차 커밋: Strategy Pattern
|
||||
```
|
||||
feat: VMIS 통합 모드 전환 기능 구현 (Strategy Pattern)
|
||||
- VehicleInfoService 인터페이스 및 구현체
|
||||
- Bean 충돌 해결
|
||||
- 설정 확장 및 모니터링
|
||||
- 빌드 성공 확인 ✅
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 주의사항
|
||||
|
||||
### Bean 이름 변경
|
||||
- 기존 `restTemplate` → 새로 추가된 `vmisRestTemplate`
|
||||
- 기존 RestTemplateConfig의 restTemplate은 그대로 유지
|
||||
- VMIS 내부 모듈은 vmisRestTemplate 사용
|
||||
|
||||
### 기본 모드
|
||||
- 설정이 없거나 `mode`가 지정되지 않으면 **external 모드**가 기본값
|
||||
- Internal 모드를 사용하려면 명시적으로 `mode: internal` 설정 필요
|
||||
|
||||
### GPKI 인증서
|
||||
- 운영 환경에서는 반드시 `gpki.enabled: Y` 설정
|
||||
- 개발 환경에서는 `gpki.enabled: N` 사용 가능
|
||||
|
||||
---
|
||||
|
||||
## 🎉 완료!
|
||||
|
||||
모든 작업이 성공적으로 완료되었습니다. YAML 설정만으로 내부/외부 모드를 자유롭게 전환할 수 있습니다.
|
||||
|
||||
**질문이나 문제가 있으면 다음 문서를 참조하세요:**
|
||||
- `VMIS_INTERFACE_INTEGRATION_ANALYSIS.md`: 통합 분석 문서
|
||||
- `VMIS_INTERFACE_MIGRATION_PLAN.md`: 마이그레이션 계획
|
||||
- `VMIS_INTEGRATION_STRATEGY_DESIGN.md`: Strategy Pattern 설계
|
||||
@ -1,802 +0,0 @@
|
||||
# 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
|
||||
@ -1,765 +0,0 @@
|
||||
# VMIS-interface → VIPS 통합 프로젝트 분석 문서
|
||||
|
||||
**작성일**: 2025-11-06
|
||||
**작업 방식**: 방법 2 - 직접 통합 (Spring Boot 2.7 다운그레이드)
|
||||
|
||||
---
|
||||
|
||||
## 목차
|
||||
|
||||
1. [프로젝트 개요](#1-프로젝트-개요)
|
||||
2. [VIPS 프로젝트 구조](#2-vips-프로젝트-구조)
|
||||
3. [VMIS-interface 프로젝트 구조](#3-vmis-interface-프로젝트-구조)
|
||||
4. [기술 스택 비교](#4-기술-스택-비교)
|
||||
5. [통합 방식 선택 이유](#5-통합-방식-선택-이유)
|
||||
6. [주요 차이점 및 해결 방안](#6-주요-차이점-및-해결-방안)
|
||||
|
||||
---
|
||||
|
||||
## 1. 프로젝트 개요
|
||||
|
||||
### VIPS (차량 점검 페널티 시스템)
|
||||
- **위치**: D:\workspace\git\VIPS
|
||||
- **목적**: 자동차 점검 페널티 관리 업무 시스템
|
||||
- **형태**: 단일 모듈 Spring Boot 웹 애플리케이션
|
||||
|
||||
### VMIS-interface (차량 정보 조회 API)
|
||||
- **위치**: D:\workspace\git\VMIS-interface
|
||||
- **목적**: 도로교통공단 Open API 연동하여 차량 정보 조회
|
||||
- **형태**: REST API 서버
|
||||
|
||||
### 통합 목표
|
||||
VMIS-interface의 모든 설정, 로직, 데이터베이스를 VIPS 내부로 이식하여 외부 API 호출 없이 내부 모듈로 작동
|
||||
|
||||
---
|
||||
|
||||
## 2. VIPS 프로젝트 구조
|
||||
|
||||
### 2.1 기본 정보
|
||||
```
|
||||
프로젝트명: VIPS
|
||||
Spring Boot: 2.7.18
|
||||
Java: 1.8
|
||||
빌드: Gradle
|
||||
패키징: WAR
|
||||
데이터베이스: MariaDB
|
||||
ORM: MyBatis
|
||||
```
|
||||
|
||||
### 2.2 디렉토리 구조
|
||||
```
|
||||
D:\workspace\git\VIPS\
|
||||
├── src/main/java/
|
||||
│ ├── egovframework/ (전자정부 프레임워크)
|
||||
│ │ ├── config/ (설정 클래스)
|
||||
│ │ │ ├── RestTemplateConfig.java
|
||||
│ │ │ ├── DataSourceProxyConfig.java
|
||||
│ │ │ ├── EgovConfigCommon.java
|
||||
│ │ │ ├── EgovConfigWeb.java
|
||||
│ │ │ ├── SqlLoggingInterceptor.java
|
||||
│ │ │ └── SwaggerConfig.java
|
||||
│ │ ├── configProperties/
|
||||
│ │ │ └── RestTemplateProperties.java
|
||||
│ │ ├── filter/
|
||||
│ │ │ └── XssFilterConfig.java
|
||||
│ │ └── util/
|
||||
│ │ ├── ApiResponseEntity.java
|
||||
│ │ └── ApiResponseUtil.java
|
||||
│ │
|
||||
│ └── go/kr/project/
|
||||
│ ├── externalApi/ (외부 API 통합 모듈) ★
|
||||
│ │ ├── service/
|
||||
│ │ │ └── ExternalVehicleApiService.java
|
||||
│ │ └── vo/
|
||||
│ │ ├── Envelope.java
|
||||
│ │ ├── VehicleApiResponseVO.java
|
||||
│ │ ├── VehicleBasicInfoVO.java
|
||||
│ │ ├── VehicleBasicRequestVO.java
|
||||
│ │ ├── VehicleLedgerRequestVO.java
|
||||
│ │ └── VehicleLedgerVO.java
|
||||
│ ├── carInspectionPenalty/
|
||||
│ ├── common/
|
||||
│ ├── login/
|
||||
│ ├── main/
|
||||
│ ├── mypage/
|
||||
│ └── system/
|
||||
│
|
||||
├── src/main/resources/
|
||||
│ ├── application.yml
|
||||
│ ├── application-local.yml
|
||||
│ ├── application-dev.yml
|
||||
│ ├── application-prd.yml
|
||||
│ ├── logback-spring.xml
|
||||
│ └── mybatis/
|
||||
│ ├── mybatis-config.xml
|
||||
│ └── mapper/
|
||||
│
|
||||
└── build.gradle
|
||||
```
|
||||
|
||||
### 2.3 주요 설정 (application.yml)
|
||||
```yaml
|
||||
server:
|
||||
port: 8080
|
||||
|
||||
mybatis:
|
||||
config-location: classpath:mybatis/mybatis-config.xml
|
||||
mapper-locations: classpath:mybatis/mapper/**/*_${Globals.DbType}.xml
|
||||
|
||||
rest-template:
|
||||
timeout:
|
||||
connect-timeout-millis: 10000
|
||||
read-timeout-millis: 12000
|
||||
connection-pool:
|
||||
max-total: 100
|
||||
max-per-route: 20
|
||||
rate-limit:
|
||||
permits-per-second: 5.0
|
||||
```
|
||||
|
||||
### 2.4 기존 외부 API 통합 구조
|
||||
|
||||
**ExternalVehicleApiService.java**:
|
||||
- VMIS-interface를 REST API로 호출 (http://localhost:8081)
|
||||
- 배치 처리 지원
|
||||
- 에러 처리 및 로깅
|
||||
|
||||
**RestTemplateConfig.java**:
|
||||
- Apache HttpClient 4 기반
|
||||
- 연결 풀 관리 (PoolingHttpClientConnectionManager)
|
||||
- Rate Limiting (Guava RateLimiter)
|
||||
|
||||
### 2.5 주요 의존성
|
||||
```gradle
|
||||
dependencies {
|
||||
implementation 'org.springframework.boot:spring-boot-starter-web:2.7.18'
|
||||
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.3.1'
|
||||
implementation 'org.mariadb.jdbc:mariadb-java-client'
|
||||
implementation 'org.apache.httpcomponents:httpclient'
|
||||
implementation 'com.google.guava:guava:32.1.3-jre'
|
||||
implementation 'org.egovframe.rte:org.egovframe.rte.fdl.cmmn:4.3.0'
|
||||
implementation 'org.egovframe.rte:org.egovframe.rte.ptl.mvc:4.3.0'
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. VMIS-interface 프로젝트 구조
|
||||
|
||||
### 3.1 기본 정보
|
||||
```
|
||||
프로젝트명: VMIS-interface
|
||||
Spring Boot: 3.3.4
|
||||
Java: 17
|
||||
빌드: Gradle
|
||||
패키징: JAR
|
||||
데이터베이스: MariaDB
|
||||
ORM: MyBatis
|
||||
```
|
||||
|
||||
### 3.2 디렉토리 구조
|
||||
```
|
||||
D:\workspace\git\VMIS-interface\
|
||||
├── src/main/java/com/vmis/interfaceapp/
|
||||
│ ├── InterfaceApplication.java (메인 클래스)
|
||||
│ │
|
||||
│ ├── config/ (설정 계층)
|
||||
│ │ ├── MyBatisConfig.java
|
||||
│ │ ├── OpenApiConfig.java
|
||||
│ │ └── VmisProperties.java (핵심 설정)
|
||||
│ │
|
||||
│ ├── controller/ (API 진입점)
|
||||
│ │ └── VehicleInterfaceController.java
|
||||
│ │
|
||||
│ ├── service/ (비즈니스 로직)
|
||||
│ │ ├── CarBassMatterInqireService.java (기본사항 조회)
|
||||
│ │ ├── CarLedgerFrmbkService.java (등록원부 조회)
|
||||
│ │ ├── CarBassMatterInqireLogService.java (로그)
|
||||
│ │ └── CarLedgerFrmbkLogService.java (로그)
|
||||
│ │
|
||||
│ ├── client/ (외부 API 호출)
|
||||
│ │ └── GovernmentApiClient.java (도로교통공단 API)
|
||||
│ │
|
||||
│ ├── enricher/ (데이터 보강)
|
||||
│ │ └── RequestEnricher.java
|
||||
│ │
|
||||
│ ├── model/ (도메인 모델)
|
||||
│ │ ├── entity/ (DB 엔티티 - 7개)
|
||||
│ │ ├── vo/ (VO - 6개)
|
||||
│ │ └── dto/ (DTO - 2개)
|
||||
│ │
|
||||
│ ├── mapper/ (MyBatis Mapper)
|
||||
│ │ ├── CarBassMatterInqireLogMapper.java
|
||||
│ │ └── CarLedgerFrmbkLogMapper.java
|
||||
│ │
|
||||
│ └── util/ (유틸리티)
|
||||
│ ├── EncryptionUtil.java (GPKI 암호화)
|
||||
│ └── TransactionIdGenerator.java
|
||||
│
|
||||
├── src/main/resources/
|
||||
│ ├── application.yml
|
||||
│ ├── application-dev.yml
|
||||
│ ├── application-prd.yml
|
||||
│ ├── logback-spring.xml
|
||||
│ └── mybatis/
|
||||
│ ├── mybatis-config.xml
|
||||
│ └── mapper/
|
||||
│ ├── CarBassMatterInqireLogMapper.xml
|
||||
│ └── CarLedgerFrmbkLogMapper.xml
|
||||
│
|
||||
├── lib/
|
||||
│ └── libgpkiapi_jni_1.5.jar (GPKI 라이브러리)
|
||||
│
|
||||
├── ddl/vips/ (데이터베이스 스키마)
|
||||
│ ├── tb_car_bass_matter_inqire.sql
|
||||
│ ├── seq_car_bass_matter_inqire.sql
|
||||
│ ├── tb_car_ledger_frmbk.sql
|
||||
│ ├── seq_car_ledger_frmbk.sql
|
||||
│ ├── tb_car_ledger_frmbk_dtl.sql
|
||||
│ └── seq_car_ledger_frmbk_dtl.sql
|
||||
│
|
||||
└── build.gradle
|
||||
```
|
||||
|
||||
### 3.3 핵심 파일 목록 (34개 Java 파일)
|
||||
|
||||
#### 설정 (3개)
|
||||
1. `MyBatisConfig.java`
|
||||
2. `OpenApiConfig.java`
|
||||
3. `VmisProperties.java` ⭐ (핵심 설정)
|
||||
|
||||
#### 컨트롤러 (1개)
|
||||
4. `VehicleInterfaceController.java`
|
||||
|
||||
#### 서비스 (4개)
|
||||
5. `CarBassMatterInqireService.java`
|
||||
6. `CarLedgerFrmbkService.java`
|
||||
7. `CarBassMatterInqireLogService.java`
|
||||
8. `CarLedgerFrmbkLogService.java`
|
||||
|
||||
#### 클라이언트 (1개)
|
||||
9. `GovernmentApiClient.java` ⭐ (가장 복잡)
|
||||
|
||||
#### 데이터 보강 (1개)
|
||||
10. `RequestEnricher.java`
|
||||
|
||||
#### 엔티티 (7개)
|
||||
11. `CarBassMatterInqire.java`
|
||||
12. `CarBassMatterInqireLog.java`
|
||||
13. `CarLedgerFrmbk.java`
|
||||
14. `CarLedgerFrmbkDtl.java`
|
||||
15. `CarLedgerFrmbkLog.java`
|
||||
16. `VehicleBasicInfo.java`
|
||||
17. `VehicleLedger.java`
|
||||
|
||||
#### VO (6개)
|
||||
18. `CarBassMatterInqireRequestVO.java`
|
||||
19. `CarLedgerFrmbkRequestVO.java`
|
||||
20. `Envelope.java`
|
||||
21. `GovApiBasicRequest.java`
|
||||
22. `GovApiBasicResponse.java`
|
||||
23. `GovApiLedgerRequest.java`
|
||||
24. `GovApiLedgerResponse.java`
|
||||
|
||||
#### DTO (2개)
|
||||
25. `VehicleBasicInfoDTO.java`
|
||||
26. `VehicleLedgerDTO.java`
|
||||
|
||||
#### Mapper (2개)
|
||||
27. `CarBassMatterInqireLogMapper.java`
|
||||
28. `CarLedgerFrmbkLogMapper.java`
|
||||
|
||||
#### 유틸리티 (2개)
|
||||
29. `EncryptionUtil.java`
|
||||
30. `TransactionIdGenerator.java`
|
||||
|
||||
#### 메인 클래스 (1개)
|
||||
31. `InterfaceApplication.java`
|
||||
|
||||
**총 31개 핵심 파일 + 3개 추가 클래스**
|
||||
|
||||
### 3.4 VmisProperties 구조 (핵심)
|
||||
|
||||
```java
|
||||
@ConfigurationProperties(prefix = "vmis")
|
||||
public class VmisProperties {
|
||||
private SystemProps system; // 시스템 정보
|
||||
private GpkiProps gpki; // GPKI 암호화 설정
|
||||
private GovProps government; // 정부 API 설정
|
||||
|
||||
public static class SystemProps {
|
||||
private String infoSysId; // 정보시스템 ID
|
||||
private String infoSysIp; // 시스템 IP
|
||||
private String managerId; // 담당자 ID
|
||||
private String managerName; // 담당자명
|
||||
private String managerTel; // 담당자 전화번호
|
||||
}
|
||||
|
||||
public static class GpkiProps {
|
||||
private boolean enabled; // 암호화 사용 여부
|
||||
private String certPath; // 인증서 경로
|
||||
private String privateKeyPath; // 개인키 경로
|
||||
private String privateKeyPassword; // 개인키 비밀번호
|
||||
}
|
||||
|
||||
public static class GovProps {
|
||||
private String host; // API 호스트
|
||||
private String basePath; // 기본 경로
|
||||
private int connectTimeout; // 연결 타임아웃
|
||||
private int readTimeout; // 읽기 타임아웃
|
||||
private Services services; // 서비스별 설정
|
||||
|
||||
public static class Services {
|
||||
private ServiceConfig basic; // 기본사항 API
|
||||
private ServiceConfig ledger; // 등록원부 API
|
||||
|
||||
public static class ServiceConfig {
|
||||
private String path; // API 경로
|
||||
private String apiKey; // API 키
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.5 application.yml 설정
|
||||
```yaml
|
||||
vmis:
|
||||
system:
|
||||
info-sys-id: "VMIS001"
|
||||
info-sys-ip: "192.168.1.100"
|
||||
manager-id: "admin"
|
||||
manager-name: "관리자"
|
||||
manager-tel: "02-1234-5678"
|
||||
|
||||
gpki:
|
||||
enabled: false # 개발환경에서는 false
|
||||
cert-path: "/path/to/cert.der"
|
||||
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}"
|
||||
```
|
||||
|
||||
### 3.6 API 엔드포인트
|
||||
|
||||
#### 1. 자동차 기본사항 조회
|
||||
```
|
||||
POST /api/v1/vehicles/basic
|
||||
Content-Type: application/json
|
||||
|
||||
Request:
|
||||
{
|
||||
"vhrno": "12가3456"
|
||||
}
|
||||
|
||||
Response:
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"vhrno": "12가3456",
|
||||
"success": true,
|
||||
"message": "조회 성공",
|
||||
"basicInfo": {
|
||||
"vhrno": "12가3456",
|
||||
"vhcleNm": "소나타",
|
||||
...
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 자동차 등록원부 조회
|
||||
```
|
||||
POST /api/v1/vehicles/ledger
|
||||
Content-Type: application/json
|
||||
|
||||
Request:
|
||||
{
|
||||
"vhrno": "12가3456"
|
||||
}
|
||||
|
||||
Response:
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"vhrno": "12가3456",
|
||||
"success": true,
|
||||
"message": "조회 성공",
|
||||
"ledgerInfo": {
|
||||
"master": {...},
|
||||
"details": [...]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 3.7 데이터베이스 테이블 (6개)
|
||||
|
||||
1. **tb_car_bass_matter_inqire** (기본사항 로그)
|
||||
- seq_car_bass_matter_inqire (시퀀스)
|
||||
|
||||
2. **tb_car_ledger_frmbk** (등록원부 마스터)
|
||||
- seq_car_ledger_frmbk (시퀀스)
|
||||
|
||||
3. **tb_car_ledger_frmbk_dtl** (등록원부 상세)
|
||||
- seq_car_ledger_frmbk_dtl (시퀀스)
|
||||
|
||||
### 3.8 주요 의존성
|
||||
```gradle
|
||||
dependencies {
|
||||
implementation 'org.springframework.boot:spring-boot-starter-web:3.3.4'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-validation'
|
||||
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3'
|
||||
implementation 'org.mariadb.jdbc:mariadb-java-client:3.3.3'
|
||||
implementation 'org.apache.httpcomponents.client5:httpclient5:5.2.3'
|
||||
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0'
|
||||
implementation 'org.projectlombok:lombok:1.18.34'
|
||||
implementation files('lib/libgpkiapi_jni_1.5.jar')
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 기술 스택 비교
|
||||
|
||||
| 항목 | VIPS | VMIS-interface | 비고 |
|
||||
|------|------|----------------|------|
|
||||
| **Spring Boot** | 2.7.18 | 3.3.4 | 메이저 버전 차이 |
|
||||
| **Java** | 1.8 | 17 | 9개 버전 차이 |
|
||||
| **패키지** | javax.* | jakarta.* | 네임스페이스 변경 |
|
||||
| **HttpClient** | Apache 4 | Apache 5 | API 변경 |
|
||||
| **MyBatis** | 2.3.1 | 3.0.3 | 호환 가능 |
|
||||
| **MariaDB** | 기본 | 3.3.3 | 호환 가능 |
|
||||
| **Swagger** | springdoc 1.7.0 | springdoc 2.6.0 | 버전 차이 |
|
||||
| **Lombok** | 기본 | 1.18.34 | 호환 가능 |
|
||||
|
||||
### 4.1 주요 호환성 이슈
|
||||
|
||||
#### Spring Boot 2.x → 3.x 주요 변경사항
|
||||
1. **패키지 변경**: `javax.*` → `jakarta.*`
|
||||
2. **최소 Java 버전**: Java 8 → Java 17
|
||||
3. **Spring Security**: 아키텍처 변경
|
||||
4. **Configuration Properties**: 일부 속성명 변경
|
||||
|
||||
#### HttpClient 4 → 5 주요 변경사항
|
||||
1. **패키지명**: `org.apache.http.*` → `org.apache.hc.client5.*`
|
||||
2. **API 재설계**: 빌더 패턴 강화
|
||||
3. **클래스명 변경**: `HttpClientBuilder` → `HttpClients`
|
||||
|
||||
---
|
||||
|
||||
## 5. 통합 방식 선택 이유
|
||||
|
||||
### 방법 2: 직접 통합 (Spring Boot 2.7 다운그레이드)
|
||||
|
||||
#### 선택 이유
|
||||
1. **내부 시스템 자체 완결**: 외부 API 호출 없이 내부 메서드 호출로 처리
|
||||
2. **트랜잭션 일관성**: 단일 애플리케이션 내 DB 트랜잭션 관리
|
||||
3. **배포 단순화**: 하나의 WAR 파일로 배포
|
||||
4. **성능 최적화**: 네트워크 오버헤드 제거
|
||||
|
||||
#### 트레이드오프
|
||||
- Spring Boot 3 → 2 다운그레이드 필요
|
||||
- Java 17 → Java 8 호환성 작업 필요
|
||||
- 최신 기술 스택 사용 불가
|
||||
|
||||
---
|
||||
|
||||
## 6. 주요 차이점 및 해결 방안
|
||||
|
||||
### 6.1 Spring Boot 버전 차이
|
||||
|
||||
#### 문제
|
||||
- Spring Boot 3.3.4 → 2.7.18 다운그레이드
|
||||
|
||||
#### 해결 방안
|
||||
```gradle
|
||||
// VMIS-interface build.gradle 수정
|
||||
plugins {
|
||||
id 'org.springframework.boot' version '2.7.18'
|
||||
id 'io.spring.dependency-management' version '1.0.15.RELEASE'
|
||||
}
|
||||
|
||||
sourceCompatibility = '1.8'
|
||||
targetCompatibility = '1.8'
|
||||
```
|
||||
|
||||
### 6.2 패키지 네임스페이스 변경
|
||||
|
||||
#### 문제
|
||||
- `jakarta.*` → `javax.*` 전체 변경 필요
|
||||
|
||||
#### 영향 범위
|
||||
```
|
||||
jakarta.servlet.* → javax.servlet.*
|
||||
jakarta.validation.* → javax.validation.*
|
||||
jakarta.persistence.* → javax.persistence.*
|
||||
jakarta.annotation.* → javax.annotation.*
|
||||
```
|
||||
|
||||
#### 해결 방안
|
||||
- 전체 파일에서 `jakarta` → `javax` 일괄 교체
|
||||
- IDE의 Find & Replace 기능 활용
|
||||
|
||||
### 6.3 HttpClient 버전 차이
|
||||
|
||||
#### 문제
|
||||
- Apache HttpClient 5 → 4 다운그레이드
|
||||
|
||||
#### GovernmentApiClient.java 수정 필요
|
||||
```java
|
||||
// Before (HttpClient 5)
|
||||
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
|
||||
import org.apache.hc.client5.http.impl.classic.HttpClients;
|
||||
|
||||
// After (HttpClient 4)
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
```
|
||||
|
||||
### 6.4 Java 17 → Java 8 호환성
|
||||
|
||||
#### 주요 체크 포인트
|
||||
1. **Text Blocks**: Java 15+ 기능 사용 시 일반 문자열로 변경
|
||||
2. **Records**: Java 14+ 기능 사용 시 일반 클래스로 변경
|
||||
3. **Switch Expressions**: Java 14+ 기능 사용 시 전통적 switch로 변경
|
||||
4. **var 키워드**: Java 10+ 기능 (사용 가능하나 명시적 타입 권장)
|
||||
|
||||
#### 현재 VMIS-interface 코드 검토 필요
|
||||
- 대부분 전통적 Java 문법 사용 예상
|
||||
- Lombok 적극 활용 중 (호환 가능)
|
||||
|
||||
### 6.5 MyBatis 설정 차이
|
||||
|
||||
#### VMIS-interface mybatis-config.xml
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-config.dtd">
|
||||
<configuration>
|
||||
<settings>
|
||||
<setting name="mapUnderscoreToCamelCase" value="true"/>
|
||||
<setting name="callSettersOnNulls" value="true"/>
|
||||
</settings>
|
||||
</configuration>
|
||||
```
|
||||
|
||||
#### VIPS 설정과 통합
|
||||
- VIPS의 기존 mybatis-config.xml에 설정 병합
|
||||
- mapper 위치 추가: `mybatis/mapper/vmis/**/*_maria.xml`
|
||||
|
||||
### 6.6 패키지 구조 변경
|
||||
|
||||
#### Before (VMIS-interface)
|
||||
```
|
||||
com.vmis.interfaceapp.*
|
||||
```
|
||||
|
||||
#### After (VIPS 통합)
|
||||
```
|
||||
go.kr.project.vmis.*
|
||||
```
|
||||
|
||||
#### 변경 계획
|
||||
```
|
||||
com.vmis.interfaceapp.config → go.kr.project.vmis.config
|
||||
com.vmis.interfaceapp.controller → go.kr.project.vmis.controller
|
||||
com.vmis.interfaceapp.service → go.kr.project.vmis.service
|
||||
com.vmis.interfaceapp.client → go.kr.project.vmis.client
|
||||
com.vmis.interfaceapp.model → go.kr.project.vmis.model
|
||||
com.vmis.interfaceapp.mapper → go.kr.project.vmis.mapper
|
||||
com.vmis.interfaceapp.util → go.kr.project.vmis.util
|
||||
```
|
||||
|
||||
### 6.7 설정 파일 통합
|
||||
|
||||
#### VmisProperties 통합 방안
|
||||
|
||||
**Option 1**: VIPS의 application.yml에 vmis 섹션 추가
|
||||
```yaml
|
||||
# VIPS application.yml
|
||||
vmis:
|
||||
system:
|
||||
info-sys-id: "VMIS001"
|
||||
...
|
||||
government:
|
||||
...
|
||||
```
|
||||
|
||||
**Option 2**: application-vmis.yml 별도 파일 생성
|
||||
```yaml
|
||||
# application-vmis.yml
|
||||
vmis:
|
||||
...
|
||||
```
|
||||
|
||||
**권장**: Option 1 (단일 설정 파일 유지)
|
||||
|
||||
### 6.8 GPKI 라이브러리 통합
|
||||
|
||||
#### 파일 위치
|
||||
```
|
||||
D:\workspace\git\VMIS-interface\lib\libgpkiapi_jni_1.5.jar
|
||||
↓
|
||||
D:\workspace\git\VIPS\lib\libgpkiapi_jni_1.5.jar
|
||||
```
|
||||
|
||||
#### build.gradle 수정
|
||||
```gradle
|
||||
dependencies {
|
||||
implementation files('lib/libgpkiapi_jni_1.5.jar')
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 마이그레이션 체크리스트
|
||||
|
||||
### Phase 1: 환경 준비
|
||||
- [ ] 현재 브랜치 상태 확인 (이미 생성됨)
|
||||
- [ ] VIPS 프로젝트 백업 (선택사항)
|
||||
- [ ] GPKI 라이브러리 복사
|
||||
|
||||
### Phase 2: 코드 이식
|
||||
- [ ] VmisProperties 이식 및 패키지 변경
|
||||
- [ ] 설정 클래스 이식 (MyBatisConfig, OpenApiConfig)
|
||||
- [ ] GovernmentApiClient 이식 (HttpClient 4 변경)
|
||||
- [ ] 모델 클래스 이식 (entity, vo, dto)
|
||||
- [ ] 서비스 클래스 이식
|
||||
- [ ] 컨트롤러 이식
|
||||
- [ ] Mapper 인터페이스 및 XML 이식
|
||||
- [ ] 유틸리티 클래스 이식
|
||||
|
||||
### Phase 3: 설정 통합
|
||||
- [ ] application.yml 설정 통합
|
||||
- [ ] mybatis-config.xml 설정 병합
|
||||
- [ ] logback-spring.xml 로그 설정 추가
|
||||
- [ ] build.gradle 의존성 추가
|
||||
|
||||
### Phase 4: 패키지 변경
|
||||
- [ ] jakarta → javax 일괄 변경
|
||||
- [ ] com.vmis.interfaceapp → go.kr.project.vmis 변경
|
||||
- [ ] import 문 정리
|
||||
|
||||
### Phase 5: 데이터베이스
|
||||
- [ ] 데이터베이스 연결 테스트 (테이블은 이미 존재)
|
||||
|
||||
### Phase 6: 빌드 및 테스트
|
||||
- [ ] Gradle 빌드 성공 확인
|
||||
- [ ] 애플리케이션 구동 확인
|
||||
- [ ] API 엔드포인트 테스트
|
||||
- [ ] 정부 API 연동 테스트
|
||||
- [ ] 로그 저장 확인
|
||||
|
||||
### Phase 7: 전략 패턴 구현 (내부/외부 통신 분기)
|
||||
- [ ] VehicleInfoService 인터페이스 생성
|
||||
- [ ] Internal/External 모드 구현체 작성
|
||||
- [ ] VmisIntegrationConfig 설정 클래스
|
||||
- [ ] application.yml 설정 추가 (vmis.integration.mode)
|
||||
- [ ] 기존 클라이언트 코드 인터페이스 의존으로 변경
|
||||
|
||||
### Phase 8: 기존 코드 정리
|
||||
- [ ] ExternalVehicleApiService 코드 리팩토링
|
||||
- [ ] 불필요한 REST 호출 로직 제거
|
||||
- [ ] 내부 메서드 호출로 변경
|
||||
|
||||
### Phase 9: 문서화 및 배포
|
||||
- [ ] API 문서 업데이트
|
||||
- [ ] 운영 가이드 작성
|
||||
- [ ] 코드 리뷰
|
||||
- [ ] main 브랜치 머지
|
||||
|
||||
---
|
||||
|
||||
## 8. 주요 파일 절대 경로
|
||||
|
||||
### VIPS
|
||||
```
|
||||
D:\workspace\git\VIPS\src\main\java\egovframework\config\RestTemplateConfig.java
|
||||
D:\workspace\git\VIPS\src\main\java\go\kr\project\externalApi\service\ExternalVehicleApiService.java
|
||||
D:\workspace\git\VIPS\src\main\resources\application.yml
|
||||
D:\workspace\git\VIPS\build.gradle
|
||||
```
|
||||
|
||||
### VMIS-interface
|
||||
```
|
||||
D:\workspace\git\VMIS-interface\src\main\java\com\vmis\interfaceapp\config\VmisProperties.java
|
||||
D:\workspace\git\VMIS-interface\src\main\java\com\vmis\interfaceapp\client\GovernmentApiClient.java
|
||||
D:\workspace\git\VMIS-interface\src\main\resources\application.yml
|
||||
D:\workspace\git\VMIS-interface\lib\libgpkiapi_jni_1.5.jar
|
||||
D:\workspace\git\VMIS-interface\ddl\vips\*.sql
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 위험 요소 및 대응 방안
|
||||
|
||||
### 9.1 Spring Boot 다운그레이드 실패
|
||||
**위험**: 일부 Spring Boot 3 전용 기능 사용 시 빌드 실패
|
||||
**대응**: 코드 리뷰 후 대체 코드 작성
|
||||
|
||||
### 9.2 GPKI 암호화 라이브러리 호환성
|
||||
**위험**: GPKI JNI 라이브러리가 특정 Java 버전에서만 작동
|
||||
**대응**: 개발환경(gpki.enabled=false)에서 먼저 테스트
|
||||
|
||||
### 9.3 HttpClient 버전 차이
|
||||
**위험**: API 차이로 인한 런타임 에러
|
||||
**대응**: GovernmentApiClient 전면 재작성 고려
|
||||
|
||||
### 9.4 데이터베이스 트랜잭션
|
||||
**위험**: 기존 VIPS와 새로운 VMIS 로직의 트랜잭션 충돌
|
||||
**대응**: @Transactional 범위 명확히 설정
|
||||
|
||||
---
|
||||
|
||||
## 10. 예상 작업 시간
|
||||
|
||||
| 단계 | 예상 시간 | 비고 |
|
||||
|------|----------|------|
|
||||
| Phase 1: 환경 준비 | 20분 | 디렉토리 생성, 라이브러리 복사 |
|
||||
| Phase 2: 코드 이식 | 3시간 | 34개 파일 이식 |
|
||||
| Phase 3: 설정 통합 | 1시간 | YAML, XML 병합 |
|
||||
| Phase 4: 패키지 변경 | 1시간 | 일괄 변경 |
|
||||
| Phase 5: 데이터베이스 | 10분 | 연결 테스트 (테이블 기존재) |
|
||||
| Phase 6: 빌드/테스트 | 2시간 | 디버깅 포함 |
|
||||
| Phase 7: 전략 패턴 구현 | 1.5시간 | 내부/외부 통신 분기 처리 |
|
||||
| Phase 8: 기존 코드 정리 | 1시간 | 리팩토링 |
|
||||
| Phase 9: 문서화 | 1시간 | 문서 작성 |
|
||||
| **총 예상 시간** | **10시간 50분** | 순수 작업 시간 |
|
||||
|
||||
---
|
||||
|
||||
## 11. 다음 단계
|
||||
|
||||
상세한 마이그레이션 계획은 `VMIS_INTERFACE_MIGRATION_PLAN.md` 파일을 참조하세요.
|
||||
|
||||
**작업 진행 방법**:
|
||||
브랜치는 이미 생성되어 있으므로, `VMIS_INTERFACE_MIGRATION_PLAN.md`의 Phase 1부터 시작하세요.
|
||||
|
||||
```bash
|
||||
cd D:\workspace\git\VIPS
|
||||
git status # 현재 브랜치 확인
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**문서 버전**: 1.2
|
||||
**최종 수정**: 2025-11-06
|
||||
**주요 변경사항**:
|
||||
- 브랜치 생성 관련 내용 제거 (이미 생성됨)
|
||||
- 데이터베이스 테이블 생성 제거 (이미 존재)
|
||||
- Phase 7 추가: 전략 패턴 구현 (내부/외부 통신 분기)
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,261 +0,0 @@
|
||||
# 과태료 대상 비교 규칙 모듈
|
||||
|
||||
## 개요
|
||||
|
||||
이 모듈은 과태료 대상 차량을 API 응답 데이터와 비교하여 자동으로 분류하는 기능을 제공합니다.
|
||||
**Chain of Responsibility 패턴**을 사용하여 각 비교 규칙을 독립적으로 관리하고 확장할 수 있습니다.
|
||||
|
||||
## 아키텍처
|
||||
|
||||
```
|
||||
comparison/
|
||||
├── ComparisonRule.java # 비교 규칙 인터페이스
|
||||
├── ComparisonContext.java # 비교에 필요한 데이터 컨테이너
|
||||
├── ComparisonResult.java # 비교 결과 객체
|
||||
├── ComparisonRuleProcessor.java # 규칙 실행 체인 관리자
|
||||
└── rules/ # 개별 규칙 구현체
|
||||
├── ProductUseComparisonRule.java # 상품용 규칙
|
||||
└── TransferComparisonRule.java # 이첩 규칙
|
||||
```
|
||||
|
||||
## 동작 방식
|
||||
|
||||
1. **ComparisonRuleProcessor**가 모든 `@Component` 규칙을 자동으로 찾아서 등록
|
||||
2. 규칙들을 `getOrder()` 순서대로 정렬 (낮을수록 먼저 실행)
|
||||
3. 각 규칙을 순차적으로 실행
|
||||
4. 규칙이 `applied=true`를 반환하면 즉시 중단
|
||||
5. 모든 규칙이 `applied=false`를 반환하면 "정상" 처리
|
||||
|
||||
## 새로운 비교 규칙 추가 방법
|
||||
|
||||
### 1. 규칙 클래스 생성
|
||||
|
||||
`rules` 패키지에 새로운 규칙 클래스를 생성합니다.
|
||||
|
||||
```java
|
||||
package go.kr.project.carInspectionPenalty.registration.comparison.rules;
|
||||
|
||||
import go.kr.project.carInspectionPenalty.registration.comparison.*;
|
||||
import go.kr.project.carInspectionPenalty.registration.mapper.CarFfnlgTrgtMapper;
|
||||
import go.kr.project.carInspectionPenalty.registration.model.CarFfnlgTrgtVO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* 내사종결 비교 규칙
|
||||
*
|
||||
* 적용 조건:
|
||||
* - 예시: 차량이 말소된 경우
|
||||
*
|
||||
* 처리 내용:
|
||||
* - TASK_PRCS_STTS_CD = 04 (내사종결)
|
||||
* - TASK_PRCS_YMD = 현재 날짜
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class InvestigationClosedComparisonRule implements ComparisonRule {
|
||||
|
||||
private static final String STATUS_CODE = "04"; // 내사종결
|
||||
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd");
|
||||
|
||||
private final CarFfnlgTrgtMapper mapper;
|
||||
|
||||
@Override
|
||||
public ComparisonResult execute(ComparisonContext context) {
|
||||
String vhclno = context.getVhclno();
|
||||
|
||||
// 1. API 응답 데이터 유효성 검사
|
||||
if (context.getApiResponse().getBasicInfo() == null ||
|
||||
context.getApiResponse().getBasicInfo().getRecord() == null ||
|
||||
context.getApiResponse().getBasicInfo().getRecord().isEmpty()) {
|
||||
log.debug("[{}] API 응답 데이터가 없어 규칙을 적용할 수 없습니다. 차량번호: {}",
|
||||
getRuleName(), vhclno);
|
||||
return ComparisonResult.notApplied();
|
||||
}
|
||||
|
||||
// 2. 필요한 데이터 추출
|
||||
var basicInfo = context.getApiResponse().getBasicInfo().getRecord().get(0);
|
||||
String ersrRegistSeCode = basicInfo.getErsrRegistSeCode(); // 말소등록구분코드
|
||||
|
||||
// 3. 비교 로직 (말소된 차량인지 확인)
|
||||
if (ersrRegistSeCode == null || ersrRegistSeCode.isEmpty()) {
|
||||
log.debug("[{}] 말소되지 않은 차량입니다. 차량번호: {}", getRuleName(), vhclno);
|
||||
return ComparisonResult.notApplied();
|
||||
}
|
||||
|
||||
log.info("[{}] 내사종결 감지! 차량번호: {}, 말소구분: {}",
|
||||
getRuleName(), vhclno, ersrRegistSeCode);
|
||||
|
||||
// 4. DB 업데이트
|
||||
CarFfnlgTrgtVO updateData = context.getExistingData();
|
||||
updateData.setTaskPrcsSttsCd(STATUS_CODE);
|
||||
updateData.setTaskPrcsYmd(LocalDate.now().format(DATE_FORMATTER));
|
||||
// 필요시 추가 필드 설정
|
||||
|
||||
int updateResult = mapper.update(updateData);
|
||||
|
||||
// 5. 결과 반환
|
||||
if (updateResult > 0) {
|
||||
log.info("[{}] 처리 완료! 차량번호: {}", getRuleName(), vhclno);
|
||||
return ComparisonResult.applied(STATUS_CODE, "내사종결로 처리되었습니다.");
|
||||
} else {
|
||||
log.error("[{}] 업데이트 실패! 차량번호: {}", getRuleName(), vhclno);
|
||||
throw new RuntimeException(String.format("내사종결 업데이트 실패: %s", vhclno));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRuleName() {
|
||||
return "내사종결";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return 30; // 상품용(10), 이첩(20) 다음으로 실행
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 자동 등록
|
||||
|
||||
Spring이 자동으로 `@Component` 어노테이션이 붙은 클래스를 찾아서 등록합니다.
|
||||
**별도의 설정 파일 수정이 필요 없습니다!**
|
||||
|
||||
### 3. 실행 순서 제어
|
||||
|
||||
`getOrder()` 메서드로 실행 순서를 제어할 수 있습니다.
|
||||
|
||||
```java
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return 30; // 숫자가 낮을수록 먼저 실행
|
||||
}
|
||||
```
|
||||
|
||||
**현재 순서:**
|
||||
- 상품용: 10
|
||||
- 이첩: 20
|
||||
- (향후 추가): 30, 40, 50...
|
||||
|
||||
## 기존 규칙 예제
|
||||
|
||||
### 상품용 규칙 (ProductUseComparisonRule)
|
||||
|
||||
```java
|
||||
// 대표소유자성명에 "상품용" 문자열이 포함되어 있는지 확인
|
||||
String mberNm = basicInfo.getMberNm();
|
||||
if (mberNm != null && mberNm.contains("상품용")) {
|
||||
// 상품용으로 처리
|
||||
return ComparisonResult.applied("02", "상품용으로 처리되었습니다.");
|
||||
}
|
||||
```
|
||||
|
||||
### 이첩 규칙 (TransferComparisonRule)
|
||||
|
||||
```java
|
||||
// 법정동코드 앞 4자리와 사용자 조직코드 앞 4자리 비교
|
||||
String legalDong4 = useStrnghldLegaldongCode.substring(0, 4);
|
||||
String userOrg4 = userOrgCd.substring(0, 4);
|
||||
|
||||
if (!legalDong4.equals(userOrg4)) {
|
||||
// 이첩으로 처리
|
||||
return ComparisonResult.applied("03", "이첩으로 처리되었습니다.");
|
||||
}
|
||||
```
|
||||
|
||||
## API 응답 데이터 활용
|
||||
|
||||
`ComparisonContext`를 통해 다음 데이터에 접근할 수 있습니다:
|
||||
|
||||
```java
|
||||
// 기존 과태료 대상 데이터
|
||||
CarFfnlgTrgtVO existingData = context.getExistingData();
|
||||
|
||||
// API 응답 - 기본 정보
|
||||
var basicInfo = context.getApiResponse().getBasicInfo().getRecord().get(0);
|
||||
String mberNm = basicInfo.getMberNm(); // 대표소유자성명
|
||||
String vhrno = basicInfo.getVhrno(); // 차량번호
|
||||
String useStrnghldLegaldongCode = basicInfo.getUseStrnghldLegaldongCode(); // 사용본거지법정동코드
|
||||
String ersrRegistSeCode = basicInfo.getErsrRegistSeCode(); // 말소등록구분코드
|
||||
// ... 기타 필드들
|
||||
|
||||
// API 응답 - 등록원부
|
||||
var ledgerInfo = context.getApiResponse().getLedgerInfo();
|
||||
|
||||
// 사용자 ID
|
||||
String userId = context.getUserId();
|
||||
```
|
||||
|
||||
## 의존성 주입
|
||||
|
||||
규칙 클래스에서 필요한 빈을 자유롭게 주입받을 수 있습니다.
|
||||
|
||||
```java
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class MyComparisonRule implements ComparisonRule {
|
||||
|
||||
private final CarFfnlgTrgtMapper mapper; // Mapper
|
||||
private final UserMapper userMapper; // User 정보 필요 시
|
||||
private final SomeOtherService someService; // 다른 서비스
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## 로깅
|
||||
|
||||
규칙 실행 중 상세한 로그가 자동으로 출력됩니다.
|
||||
|
||||
```
|
||||
[상품용] 규칙 실행 중... 차량번호: 12가3456
|
||||
[상품용] 상품용 감지! 차량번호: 12가3456, 소유자명: 상품용차량
|
||||
[상품용] 처리 완료! 차량번호: 12가3456
|
||||
```
|
||||
|
||||
## 테스트 방법
|
||||
|
||||
규칙을 독립적으로 테스트할 수 있습니다.
|
||||
|
||||
```java
|
||||
@SpringBootTest
|
||||
class InvestigationClosedComparisonRuleTest {
|
||||
|
||||
@Autowired
|
||||
private InvestigationClosedComparisonRule rule;
|
||||
|
||||
@Test
|
||||
void 말소된_차량은_내사종결로_처리된다() {
|
||||
// Given
|
||||
ComparisonContext context = ComparisonContext.builder()
|
||||
.existingData(existingData)
|
||||
.apiResponse(apiResponse)
|
||||
.userId("USER001")
|
||||
.build();
|
||||
|
||||
// When
|
||||
ComparisonResult result = rule.execute(context);
|
||||
|
||||
// Then
|
||||
assertTrue(result.isApplied());
|
||||
assertEquals("04", result.getStatusCode());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 주의사항
|
||||
|
||||
1. **순서 관리**: `getOrder()` 값이 중복되지 않도록 주의
|
||||
2. **트랜잭션**: 각 규칙 내에서 DB 업데이트 수행 (전체는 Service에서 @Transactional)
|
||||
3. **예외 처리**: 업데이트 실패 시 RuntimeException 발생 → 전체 롤백
|
||||
4. **null 체크**: API 응답 데이터는 항상 null 체크 필수
|
||||
5. **로깅**: DEBUG 레벨로 상세한 로그 남기기
|
||||
|
||||
## 문의
|
||||
|
||||
추가 비교 규칙이 필요하거나 질문이 있으면 개발팀에 문의하세요.
|
||||
@ -1,234 +0,0 @@
|
||||
# 비교 로직 구현 방법 가이드
|
||||
|
||||
## 📌 두 가지 구현 방법 제공
|
||||
|
||||
현재 프로젝트는 두 가지 비교 로직 구현 방법을 제공합니다.
|
||||
프로젝트 상황에 맞게 선택하여 사용하세요.
|
||||
|
||||
---
|
||||
|
||||
## 방법1: 일반 Service/Impl 패턴 ⭐ **추천**
|
||||
|
||||
### 특징
|
||||
- ✅ **간단하고 직관적** - 학습 곡선 낮음
|
||||
- ✅ **명확한 코드 흐름** - 순차적 실행으로 이해하기 쉬움
|
||||
- ✅ **빠른 개발** - 메서드 하나만 추가하면 됨
|
||||
- ✅ **디버깅 쉬움** - 코드 추적이 직관적
|
||||
|
||||
### 구조
|
||||
```
|
||||
service/
|
||||
├── ComparisonService.java # 인터페이스
|
||||
└── impl/
|
||||
└── ComparisonServiceImpl.java # 구현체
|
||||
```
|
||||
|
||||
### 새로운 비교 조건 추가 방법
|
||||
|
||||
#### 1단계: ComparisonServiceImpl에 private 메서드 추가
|
||||
|
||||
```java
|
||||
/**
|
||||
* 4. 새로운 비교 조건 (예: 장기미검차량)
|
||||
*
|
||||
* <p>조건: 검사 유효기간 종료일로부터 1년 이상 경과</p>
|
||||
* <p>처리: TASK_PRCS_STTS_CD = 05</p>
|
||||
*/
|
||||
private String checkLongTermUninspected(CarFfnlgTrgtVO existingData, BasicResponse.Record basicInfo) {
|
||||
String vhclno = existingData.getVhclno();
|
||||
String insptValidPdEndde = basicInfo.getInsptValidPdEndde(); // 검사유효기간종료일자
|
||||
|
||||
// 조건 체크 로직
|
||||
if (insptValidPdEndde == null || insptValidPdEndde.isEmpty()) {
|
||||
log.debug("[장기미검] 조건 미충족. 차량번호: {}", vhclno);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 1년 경과 여부 확인 로직
|
||||
LocalDate endDate = LocalDate.parse(insptValidPdEndde, DATE_FORMATTER);
|
||||
LocalDate oneYearAgo = LocalDate.now().minusYears(1);
|
||||
|
||||
if (endDate.isAfter(oneYearAgo)) {
|
||||
log.debug("[장기미검] 조건 미충족. 차량번호: {}, 종료일: {}", vhclno, insptValidPdEndde);
|
||||
return null;
|
||||
}
|
||||
|
||||
log.info("[장기미검] 조건 충족! 차량번호: {}, 종료일: {}", vhclno, insptValidPdEndde);
|
||||
|
||||
// DB 업데이트
|
||||
existingData.setTaskPrcsSttsCd("05"); // 장기미검
|
||||
existingData.setTaskPrcsYmd(LocalDate.now().format(DATE_FORMATTER));
|
||||
|
||||
int updateCount = carFfnlgTrgtMapper.update(existingData);
|
||||
if (updateCount == 0) {
|
||||
throw new RuntimeException(String.format("[장기미검] 업데이트 실패: %s", vhclno));
|
||||
}
|
||||
|
||||
log.info("[장기미검] 처리 완료! 차량번호: {}", vhclno);
|
||||
return "05";
|
||||
}
|
||||
```
|
||||
|
||||
#### 2단계: executeComparison() 메서드에 호출 추가
|
||||
|
||||
```java
|
||||
@Override
|
||||
public String executeComparison(CarFfnlgTrgtVO existingData, VehicleApiResponseVO apiResponse, String userId) {
|
||||
// ... 기존 코드 ...
|
||||
|
||||
// ========== 3. 향후 추가될 비교 로직들 ==========
|
||||
String longTermResult = checkLongTermUninspected(existingData, basicInfo);
|
||||
if (longTermResult != null) {
|
||||
log.info("========== 비교 로직 종료 (장기미검): {} ==========", vhclno);
|
||||
return longTermResult;
|
||||
}
|
||||
|
||||
// ... 나머지 코드 ...
|
||||
}
|
||||
```
|
||||
|
||||
#### 끝! 매우 간단합니다.
|
||||
|
||||
### 파일 위치
|
||||
- `service/ComparisonService.java`
|
||||
- `service/impl/ComparisonServiceImpl.java`
|
||||
|
||||
### 실행 순서
|
||||
1. 상품용 체크 → 2. 이첩 체크 → 3. 새로운 조건들...
|
||||
- **순서대로 실행**되며, 하나라도 조건이 맞으면 즉시 종료
|
||||
|
||||
---
|
||||
|
||||
## 방법2: Chain of Responsibility 패턴
|
||||
|
||||
### 특징
|
||||
- ✅ **확장성 우수** - 규칙 추가/삭제가 독립적
|
||||
- ✅ **테스트 용이** - 각 규칙을 독립적으로 테스트 가능
|
||||
- ✅ **순서 제어** - getOrder()로 실행 순서 명확히 제어
|
||||
- ❌ **복잡한 구조** - 학습 곡선 높음
|
||||
- ❌ **파일 분산** - 여러 파일을 확인해야 함
|
||||
|
||||
### 구조
|
||||
```
|
||||
comparison/
|
||||
├── ComparisonRule.java # 규칙 인터페이스
|
||||
├── ComparisonContext.java # 데이터 컨테이너
|
||||
├── ComparisonResult.java # 결과 객체
|
||||
├── ComparisonRuleProcessor.java # 체인 관리자
|
||||
└── rules/ # 규칙 구현체들
|
||||
├── ProductUseComparisonRule.java
|
||||
├── TransferComparisonRule.java
|
||||
└── ... (새 규칙 파일들)
|
||||
```
|
||||
|
||||
### 새로운 비교 조건 추가 방법
|
||||
|
||||
#### 1단계: rules/ 폴더에 새 클래스 생성
|
||||
|
||||
```java
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class LongTermUninspectedComparisonRule implements ComparisonRule {
|
||||
|
||||
@Override
|
||||
public ComparisonResult execute(ComparisonContext context) {
|
||||
// 비교 로직 작성
|
||||
if (/* 조건 충족 */) {
|
||||
// DB 업데이트
|
||||
return ComparisonResult.applied("05", "장기미검으로 처리");
|
||||
}
|
||||
return ComparisonResult.notApplied();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRuleName() { return "장기미검"; }
|
||||
|
||||
@Override
|
||||
public int getOrder() { return 40; } // 실행 순서
|
||||
}
|
||||
```
|
||||
|
||||
#### 끝! 자동으로 등록됩니다.
|
||||
|
||||
### 파일 위치
|
||||
- `comparison/` 패키지 전체
|
||||
|
||||
---
|
||||
|
||||
## 🎯 어떤 방법을 선택해야 할까?
|
||||
|
||||
### 방법1 선택 (Service/Impl) - 다음과 같은 경우 추천
|
||||
|
||||
- ✅ **간단한 프로젝트** - 비교 조건이 10개 이하
|
||||
- ✅ **빠른 개발 필요** - 당장 구현해야 할 때
|
||||
- ✅ **팀원 경험 부족** - 디자인 패턴에 익숙하지 않은 경우
|
||||
- ✅ **유지보수 단순** - 한 파일에서 모든 로직 확인 가능
|
||||
|
||||
### 방법2 선택 (Chain of Responsibility) - 다음과 같은 경우 추천
|
||||
|
||||
- ✅ **복잡한 프로젝트** - 비교 조건이 10개 이상
|
||||
- ✅ **장기 유지보수** - 규칙이 자주 추가/변경될 것으로 예상
|
||||
- ✅ **팀 규모 큽** - 여러 개발자가 동시에 작업
|
||||
- ✅ **테스트 중요** - 각 규칙을 독립적으로 테스트해야 함
|
||||
|
||||
---
|
||||
|
||||
## 💡 현재 설정 변경 방법
|
||||
|
||||
`CarFfnlgTrgtServiceImpl.java`의 `executeComparisonLogic()` 메서드에서 주석을 변경하면 됩니다.
|
||||
|
||||
```java
|
||||
private String executeComparisonLogic(...) {
|
||||
// 방법1 사용하려면: (현재 설정)
|
||||
return executeWithServicePattern(existingData, apiResponse, userId);
|
||||
|
||||
// 방법2 사용하려면:
|
||||
// return executeWithChainPattern(existingData, apiResponse, userId);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 비교표
|
||||
|
||||
| 항목 | 방법1 (Service/Impl) | 방법2 (Chain of Responsibility) |
|
||||
|------|---------------------|--------------------------------|
|
||||
| **구현 난이도** | ⭐ 쉬움 | ⭐⭐⭐ 어려움 |
|
||||
| **학습 시간** | 10분 | 1시간+ |
|
||||
| **추가 시간** | 5분 | 15분 |
|
||||
| **코드 가독성** | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
|
||||
| **확장성** | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
|
||||
| **테스트 용이성** | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
|
||||
| **파일 개수** | 2개 | N+4개 |
|
||||
| **디버깅** | ⭐⭐⭐⭐⭐ 쉬움 | ⭐⭐⭐ 보통 |
|
||||
|
||||
---
|
||||
|
||||
## 🚀 빠른 시작
|
||||
|
||||
### 방법1로 시작하기 (추천)
|
||||
1. `ComparisonServiceImpl.java` 열기
|
||||
2. 기존 메서드 참고하여 새 메서드 추가
|
||||
3. `executeComparison()`에서 호출
|
||||
4. 끝!
|
||||
|
||||
### 방법2로 시작하기
|
||||
1. `comparison/README.md` 읽기
|
||||
2. `rules/` 폴더에 새 규칙 클래스 생성
|
||||
3. `@Component` 붙이기
|
||||
4. 끝!
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 주의사항
|
||||
|
||||
1. **두 방법을 동시에 사용하지 마세요** - 하나만 선택
|
||||
2. **트랜잭션 관리** - 각 비교 메서드는 DB 업데이트를 직접 수행
|
||||
3. **예외 처리** - 업데이트 실패 시 RuntimeException 발생 → 전체 롤백
|
||||
4. **로깅** - 각 조건의 충족/미충족 여부를 명확히 로깅
|
||||
|
||||
---
|
||||
|
||||
## 📞 문의
|
||||
|
||||
추가 질문이나 제안사항은 개발팀에 문의하세요.
|
||||
@ -1,263 +0,0 @@
|
||||
# 이첩 조건 추가 방법 (OR 구조)
|
||||
|
||||
## 📌 현재 구조
|
||||
|
||||
이첩 비교 로직은 **OR 구조**로 되어 있습니다.
|
||||
- **여러 조건 중 하나라도 만족하면** 이첩으로 처리
|
||||
- 조건을 순차적으로 체크하다가 **하나라도 true가 나오면 즉시 이첩 처리**
|
||||
|
||||
---
|
||||
|
||||
## 🎯 새로운 이첩 조건 추가하기
|
||||
|
||||
### 방법1: Service/Impl 패턴 (간단) ⭐ 추천
|
||||
|
||||
`ComparisonServiceImpl.java` 파일 수정
|
||||
|
||||
#### 1단계: 조건 체크 메서드 추가
|
||||
|
||||
```java
|
||||
/**
|
||||
* 이첩 조건2: 차량 소유자 주소 불일치 (예시)
|
||||
* 조건: 차량 등록지와 소유자 실거주지가 다른 경우
|
||||
*/
|
||||
private boolean checkTransferCondition2_AddressMismatch(BasicResponse.Record basicInfo, String userId, String vhclno) {
|
||||
String useStrnghldAdresNm = basicInfo.getUseStrnghldAdresNm(); // 사용본거지주소명
|
||||
String ownerAdresNm = basicInfo.getOwnerAdresNm(); // 소유자주소명
|
||||
|
||||
// 주소 유효성 검사
|
||||
if (useStrnghldAdresNm == null || ownerAdresNm == null) {
|
||||
log.debug("[이첩][조건2] 주소 정보 없음. 차량번호: {}", vhclno);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 주소 일치 여부 확인 (시/도 단위 비교 등)
|
||||
if (useStrnghldAdresNm.equals(ownerAdresNm)) {
|
||||
log.debug("[이첩][조건2] 주소 일치. 차량번호: {}", vhclno);
|
||||
return false;
|
||||
}
|
||||
|
||||
log.info("[이첩][조건2] 주소 불일치! 차량번호: {}, 사용본거지: {}, 소유자주소: {}",
|
||||
vhclno, useStrnghldAdresNm, ownerAdresNm);
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
#### 2단계: checkTransfer() 메서드에 조건 추가
|
||||
|
||||
```java
|
||||
private String checkTransfer(CarFfnlgTrgtVO existingData, BasicResponse.Record basicInfo, String userId) {
|
||||
String vhclno = existingData.getVhclno();
|
||||
|
||||
// ========== 이첩 조건들 (OR 로직: 하나라도 만족하면 이첩) ==========
|
||||
|
||||
// 조건1: 법정동코드 불일치
|
||||
if (checkTransferCondition1_LegalDongMismatch(basicInfo, userId, vhclno)) {
|
||||
return processTransfer(existingData, basicInfo, vhclno, "법정동코드 불일치");
|
||||
}
|
||||
|
||||
// 조건2: 주소 불일치 (새로 추가!)
|
||||
if (checkTransferCondition2_AddressMismatch(basicInfo, userId, vhclno)) {
|
||||
return processTransfer(existingData, basicInfo, vhclno, "주소 불일치");
|
||||
}
|
||||
|
||||
// 조건3: 향후 추가될 조건들...
|
||||
// if (checkTransferCondition3_XXX(basicInfo, userId, vhclno)) {
|
||||
// return processTransfer(existingData, basicInfo, vhclno, "XXX");
|
||||
// }
|
||||
|
||||
return null;
|
||||
}
|
||||
```
|
||||
|
||||
#### 끝! 매우 간단합니다.
|
||||
|
||||
---
|
||||
|
||||
### 방법2: Chain of Responsibility 패턴
|
||||
|
||||
`TransferComparisonRule.java` 파일 수정
|
||||
|
||||
#### 1단계: 조건 체크 메서드 추가
|
||||
|
||||
```java
|
||||
/**
|
||||
* 조건2: 주소 불일치 체크
|
||||
*/
|
||||
private boolean checkAddressMismatch(ComparisonContext context,
|
||||
go.kr.project.api.model.response.BasicResponse.Record basicInfo,
|
||||
String vhclno) {
|
||||
String useStrnghldAdresNm = basicInfo.getUseStrnghldAdresNm();
|
||||
String ownerAdresNm = basicInfo.getOwnerAdresNm();
|
||||
|
||||
if (useStrnghldAdresNm == null || ownerAdresNm == null) {
|
||||
log.debug("[{}][조건2] 주소 정보 없음. 차량번호: {}", getRuleName(), vhclno);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (useStrnghldAdresNm.equals(ownerAdresNm)) {
|
||||
log.debug("[{}][조건2] 주소 일치. 차량번호: {}", getRuleName(), vhclno);
|
||||
return false;
|
||||
}
|
||||
|
||||
log.info("[{}][조건2] 주소 불일치! 차량번호: {}", getRuleName(), vhclno);
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
#### 2단계: execute() 메서드에 조건 추가
|
||||
|
||||
```java
|
||||
@Override
|
||||
public ComparisonResult execute(ComparisonContext context) {
|
||||
String vhclno = context.getVhclno();
|
||||
|
||||
// ... API 유효성 검사 ...
|
||||
|
||||
var basicInfo = context.getApiResponse().getBasicInfo().getRecord().get(0);
|
||||
|
||||
// ========== 이첩 조건들 (OR 로직) ==========
|
||||
|
||||
// 조건1: 법정동코드 불일치
|
||||
if (checkLegalDongCodeMismatch(context, basicInfo, vhclno)) {
|
||||
return processTransfer(context, basicInfo, vhclno, "법정동코드 불일치");
|
||||
}
|
||||
|
||||
// 조건2: 주소 불일치 (새로 추가!)
|
||||
if (checkAddressMismatch(context, basicInfo, vhclno)) {
|
||||
return processTransfer(context, basicInfo, vhclno, "주소 불일치");
|
||||
}
|
||||
|
||||
return ComparisonResult.notApplied();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 현재 이첩 조건 목록
|
||||
|
||||
### 조건1: 법정동코드 불일치 ✅ 구현됨
|
||||
- **체크 항목**: 사용본거지법정동코드 앞 4자리 vs 사용자 조직코드 앞 4자리
|
||||
- **이첩 기준**: 두 값이 다르면 이첩
|
||||
|
||||
### 조건2: (예시) 주소 불일치
|
||||
- **체크 항목**: 사용본거지주소 vs 소유자주소
|
||||
- **이첩 기준**: 주소가 다르면 이첩
|
||||
|
||||
### 조건3: (예시) 기타 조건들...
|
||||
- 필요에 따라 추가
|
||||
|
||||
---
|
||||
|
||||
## 💡 동작 방식
|
||||
|
||||
```java
|
||||
// 이첩 체크 시작
|
||||
checkTransfer(...)
|
||||
↓
|
||||
조건1 체크 → TRUE → 즉시 이첩 처리 후 return "03"
|
||||
↓ FALSE
|
||||
조건2 체크 → TRUE → 즉시 이첩 처리 후 return "03"
|
||||
↓ FALSE
|
||||
조건3 체크 → TRUE → 즉시 이첩 처리 후 return "03"
|
||||
↓ FALSE
|
||||
모든 조건 FALSE → return null (이첩 아님)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔍 로그 예시
|
||||
|
||||
### 조건1에 걸린 경우
|
||||
```
|
||||
[이첩][조건1] 법정동코드 불일치! 차량번호: 12가3456, 법정동: 1100, 조직: 4100
|
||||
[이첩] 조건 충족! 차량번호: 12가3456, 사유: 법정동코드 불일치
|
||||
[이첩] 처리 완료! 차량번호: 12가3456, 시군구: 서울특별시(11000), 사유: 법정동코드 불일치
|
||||
```
|
||||
|
||||
### 조건2에 걸린 경우
|
||||
```
|
||||
[이첩][조건1] 법정동코드 일치. 차량번호: 12가3456, 법정동: 1100, 조직: 1100
|
||||
[이첩][조건2] 주소 불일치! 차량번호: 12가3456
|
||||
[이첩] 조건 충족! 차량번호: 12가3456, 사유: 주소 불일치
|
||||
[이첩] 처리 완료! 차량번호: 12가3456, 시군구: 서울특별시(11000), 사유: 주소 불일치
|
||||
```
|
||||
|
||||
### 모든 조건에 안 걸린 경우
|
||||
```
|
||||
[이첩][조건1] 법정동코드 일치. 차량번호: 12가3456
|
||||
[이첩][조건2] 주소 일치. 차량번호: 12가3456
|
||||
[이첩] 모든 조건 미충족. 차량번호: 12가3456
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 주의사항
|
||||
|
||||
1. **조건 순서**: 위에서부터 순차적으로 체크됩니다
|
||||
- 자주 걸리는 조건을 위쪽에 배치하면 성능 향상
|
||||
|
||||
2. **조건 메서드 네이밍**: `checkTransferConditionN_XXX` 형식 권장
|
||||
- 예: `checkTransferCondition1_LegalDongMismatch`
|
||||
- 예: `checkTransferCondition2_AddressMismatch`
|
||||
|
||||
3. **return 값**:
|
||||
- `true`: 이 조건에 **해당함** → 이첩 처리
|
||||
- `false`: 이 조건에 **해당 안함** → 다음 조건 체크
|
||||
|
||||
4. **processTransfer() 공통 사용**:
|
||||
- DB 업데이트는 `processTransfer()` 메서드에서 공통으로 처리
|
||||
- 각 조건에서는 `true/false`만 반환
|
||||
|
||||
---
|
||||
|
||||
## 📝 체크리스트
|
||||
|
||||
새로운 이첩 조건 추가 시 확인사항:
|
||||
|
||||
- [ ] 조건 메서드 작성 완료
|
||||
- [ ] checkTransfer()에 조건 추가 완료
|
||||
- [ ] 로그 메시지에 조건 번호 포함 (`[조건N]`)
|
||||
- [ ] null 체크 처리 완료
|
||||
- [ ] 컴파일 테스트 완료
|
||||
- [ ] 실제 데이터로 테스트 완료
|
||||
|
||||
---
|
||||
|
||||
## 🚀 빠른 템플릿
|
||||
|
||||
```java
|
||||
/**
|
||||
* 이첩 조건N: XXX
|
||||
* 조건 설명
|
||||
*/
|
||||
private boolean checkTransferConditionN_XXX(BasicResponse.Record basicInfo, String userId, String vhclno) {
|
||||
// 1. 데이터 추출
|
||||
String data1 = basicInfo.getXXX();
|
||||
String data2 = basicInfo.getYYY();
|
||||
|
||||
// 2. 유효성 검사
|
||||
if (data1 == null || data2 == null) {
|
||||
log.debug("[이첩][조건N] 데이터 없음. 차량번호: {}", vhclno);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. 조건 체크
|
||||
if (조건_만족) {
|
||||
log.info("[이첩][조건N] 조건 충족! 차량번호: {}, 상세정보...", vhclno);
|
||||
return true; // 이첩!
|
||||
}
|
||||
|
||||
log.debug("[이첩][조건N] 조건 미충족. 차량번호: {}", vhclno);
|
||||
return false;
|
||||
}
|
||||
|
||||
// checkTransfer()에 추가
|
||||
if (checkTransferConditionN_XXX(basicInfo, userId, vhclno)) {
|
||||
return processTransfer(existingData, basicInfo, vhclno, "XXX");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
끝!
|
||||
@ -0,0 +1,788 @@
|
||||
# 과태료 대상 비교 로직 구현 가이드
|
||||
|
||||
## 📌 목차
|
||||
|
||||
1. [개요](#개요)
|
||||
2. [두 가지 구현 방법 비교](#두-가지-구현-방법-비교)
|
||||
3. [방법1: Service/Impl 패턴 (추천)](#방법1-serviceimpl-패턴-추천)
|
||||
4. [방법2: Chain of Responsibility 패턴](#방법2-chain-of-responsibility-패턴)
|
||||
5. [어떤 방법을 선택해야 할까?](#어떤-방법을-선택해야-할까)
|
||||
6. [참고사항](#참고사항)
|
||||
|
||||
---
|
||||
|
||||
## 개요
|
||||
|
||||
이 모듈은 과태료 대상 차량을 API 응답 데이터와 비교하여 자동으로 분류하는 기능을 제공합니다.
|
||||
현재 프로젝트는 **두 가지 비교 로직 구현 방법**을 제공하며, 프로젝트 상황에 맞게 선택하여 사용할 수 있습니다.
|
||||
|
||||
### 비교 로직 처리 흐름
|
||||
|
||||
```
|
||||
API 응답 데이터 수신
|
||||
↓
|
||||
비교 로직 실행
|
||||
↓
|
||||
조건1 (상품용) 체크 → 충족 → TASK_PRCS_STTS_CD = 02 → 종료
|
||||
↓ 미충족
|
||||
조건2 (이첩) 체크 → 충족 → TASK_PRCS_STTS_CD = 03 → 종료
|
||||
↓ 미충족
|
||||
조건3 (내사종결) 체크 → 충족 → TASK_PRCS_STTS_CD = 04 → 종료
|
||||
↓ 미충족
|
||||
모든 조건 미충족 → 정상 처리 (null 반환)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 두 가지 구현 방법 비교
|
||||
|
||||
### 비교표
|
||||
|
||||
| 항목 | 방법1 (Service/Impl) ⭐ | 방법2 (Chain of Responsibility) |
|
||||
|------|---------------------|--------------------------------|
|
||||
| **구현 난이도** | ⭐ 쉬움 | ⭐⭐⭐ 어려움 |
|
||||
| **학습 시간** | 10분 | 1시간+ |
|
||||
| **조건 추가 시간** | 5분 | 15분 |
|
||||
| **코드 가독성** | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
|
||||
| **확장성** | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
|
||||
| **테스트 용이성** | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
|
||||
| **파일 개수** | 2개 | N+4개 |
|
||||
| **디버깅** | ⭐⭐⭐⭐⭐ 쉬움 | ⭐⭐⭐ 보통 |
|
||||
| **추천 조건 개수** | ~10개 | 10개 이상 |
|
||||
|
||||
---
|
||||
|
||||
## 방법1: Service/Impl 패턴 (추천)
|
||||
|
||||
### 🌟 특징
|
||||
|
||||
- ✅ **간단하고 직관적** - 학습 곡선 낮음
|
||||
- ✅ **명확한 코드 흐름** - 순차적 실행으로 이해하기 쉬움
|
||||
- ✅ **빠른 개발** - 메서드 하나만 추가하면 됨
|
||||
- ✅ **디버깅 쉬움** - 코드 추적이 직관적
|
||||
- ✅ **한 파일에서 관리** - 모든 비교 로직을 한눈에 확인
|
||||
|
||||
### 📁 디렉토리 구조
|
||||
|
||||
```
|
||||
service/
|
||||
├── ComparisonService.java # 인터페이스
|
||||
└── impl/
|
||||
└── ComparisonServiceImpl.java # 구현체 (모든 비교 로직 포함)
|
||||
```
|
||||
|
||||
### 🚀 새로운 비교 조건 추가 방법
|
||||
|
||||
#### 예제 1: 일반 비교 조건 추가
|
||||
|
||||
**1단계: ComparisonServiceImpl에 private 메서드 추가**
|
||||
|
||||
```java
|
||||
/**
|
||||
* 4. 장기미검차량 체크
|
||||
*
|
||||
* <p>조건: 검사 유효기간 종료일로부터 1년 이상 경과</p>
|
||||
* <p>처리: TASK_PRCS_STTS_CD = 05</p>
|
||||
*
|
||||
* @return 05 (적용됨) 또는 null (미적용)
|
||||
*/
|
||||
private String checkLongTermUninspected(CarFfnlgTrgtVO existingData, BasicResponse.Record basicInfo) {
|
||||
String vhclno = existingData.getVhclno();
|
||||
String insptValidPdEndde = basicInfo.getInsptValidPdEndde(); // 검사유효기간종료일자
|
||||
|
||||
// 조건 체크 로직
|
||||
if (insptValidPdEndde == null || insptValidPdEndde.isEmpty()) {
|
||||
log.debug("[장기미검] 조건 미충족. 차량번호: {}", vhclno);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 1년 경과 여부 확인 로직
|
||||
LocalDate endDate = LocalDate.parse(insptValidPdEndde, DATE_FORMATTER);
|
||||
LocalDate oneYearAgo = LocalDate.now().minusYears(1);
|
||||
|
||||
if (endDate.isAfter(oneYearAgo)) {
|
||||
log.debug("[장기미검] 조건 미충족. 차량번호: {}, 종료일: {}", vhclno, insptValidPdEndde);
|
||||
return null;
|
||||
}
|
||||
|
||||
log.info("[장기미검] 조건 충족! 차량번호: {}, 종료일: {}", vhclno, insptValidPdEndde);
|
||||
|
||||
// DB 업데이트
|
||||
existingData.setTaskPrcsSttsCd("05"); // 장기미검
|
||||
existingData.setTaskPrcsYmd(LocalDate.now().format(DATE_FORMATTER));
|
||||
|
||||
int updateCount = carFfnlgTrgtMapper.update(existingData);
|
||||
if (updateCount == 0) {
|
||||
throw new RuntimeException(String.format("[장기미검] 업데이트 실패: %s", vhclno));
|
||||
}
|
||||
|
||||
log.info("[장기미검] 처리 완료! 차량번호: {}", vhclno);
|
||||
return "05";
|
||||
}
|
||||
```
|
||||
|
||||
**2단계: executeComparison() 메서드에 호출 추가**
|
||||
|
||||
```java
|
||||
@Override
|
||||
public String executeComparison(CarFfnlgTrgtVO existingData, VehicleApiResponseVO apiResponse, String userId) {
|
||||
String vhclno = existingData.getVhclno();
|
||||
log.info("========== 비교 로직 시작: {} ==========", vhclno);
|
||||
|
||||
// API 응답 데이터 유효성 검사
|
||||
if (!isValidApiResponse(apiResponse)) {
|
||||
log.warn("API 응답 데이터가 유효하지 않습니다. 차량번호: {}", vhclno);
|
||||
return null;
|
||||
}
|
||||
|
||||
BasicResponse.Record basicInfo = apiResponse.getBasicInfo().getRecord().get(0);
|
||||
|
||||
// ========== 1. 상품용 체크 ==========
|
||||
String productUseResult = checkProductUse(existingData, basicInfo);
|
||||
if (productUseResult != null) {
|
||||
log.info("========== 비교 로직 종료 (상품용): {} ==========", vhclno);
|
||||
return productUseResult;
|
||||
}
|
||||
|
||||
// ========== 2. 이첩 체크 ==========
|
||||
String transferResult = checkTransfer(existingData, basicInfo, userId);
|
||||
if (transferResult != null) {
|
||||
log.info("========== 비교 로직 종료 (이첩): {} ==========", vhclno);
|
||||
return transferResult;
|
||||
}
|
||||
|
||||
// ========== 3. 장기미검 체크 (새로 추가!) ==========
|
||||
String longTermResult = checkLongTermUninspected(existingData, basicInfo);
|
||||
if (longTermResult != null) {
|
||||
log.info("========== 비교 로직 종료 (장기미검): {} ==========", vhclno);
|
||||
return longTermResult;
|
||||
}
|
||||
|
||||
// 모든 비교 로직에 해당하지 않음
|
||||
log.info("========== 비교 로직 종료 (정상): {} ==========", vhclno);
|
||||
return null;
|
||||
}
|
||||
```
|
||||
|
||||
**끝! 매우 간단합니다.**
|
||||
|
||||
---
|
||||
|
||||
#### 예제 2: 이첩 조건 추가 (OR 구조)
|
||||
|
||||
이첩 조건은 **OR 로직**으로 구성되어 있습니다. **여러 조건 중 하나라도 만족하면** 이첩으로 처리됩니다.
|
||||
|
||||
**현재 이첩 구조:**
|
||||
|
||||
```java
|
||||
private String checkTransfer(CarFfnlgTrgtVO existingData, BasicResponse.Record basicInfo, String userId) {
|
||||
String vhclno = existingData.getVhclno();
|
||||
|
||||
// ========== 이첩 조건들 (OR 로직: 하나라도 만족하면 이첩) ==========
|
||||
|
||||
// 조건1: 법정동코드 불일치
|
||||
if (checkTransferCondition1_LegalDongMismatch(basicInfo, userId, vhclno)) {
|
||||
return processTransfer(existingData, basicInfo, vhclno, "법정동코드 불일치");
|
||||
}
|
||||
|
||||
// 조건2: 향후 추가될 이첩 조건들...
|
||||
// if (checkTransferCondition2_XXX(basicInfo, userId, vhclno)) {
|
||||
// return processTransfer(existingData, basicInfo, vhclno, "XXX");
|
||||
// }
|
||||
|
||||
// 모든 이첩 조건에 해당하지 않음
|
||||
log.debug("[이첩] 모든 조건 미충족. 차량번호: {}", vhclno);
|
||||
return null;
|
||||
}
|
||||
```
|
||||
|
||||
**새로운 이첩 조건 추가:**
|
||||
|
||||
**1단계: 조건 체크 메서드 추가**
|
||||
|
||||
```java
|
||||
/**
|
||||
* 이첩 조건2: 차량 소유자 주소 불일치 (예시)
|
||||
* 조건: 차량 등록지와 소유자 실거주지가 다른 경우
|
||||
*/
|
||||
private boolean checkTransferCondition2_AddressMismatch(BasicResponse.Record basicInfo, String userId, String vhclno) {
|
||||
String useStrnghldAdresNm = basicInfo.getUseStrnghldAdresNm(); // 사용본거지주소명
|
||||
String ownerAdresNm = basicInfo.getOwnerAdresNm(); // 소유자주소명
|
||||
|
||||
// 주소 유효성 검사
|
||||
if (useStrnghldAdresNm == null || ownerAdresNm == null) {
|
||||
log.debug("[이첩][조건2] 주소 정보 없음. 차량번호: {}", vhclno);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 주소 일치 여부 확인 (시/도 단위 비교 등)
|
||||
if (useStrnghldAdresNm.equals(ownerAdresNm)) {
|
||||
log.debug("[이첩][조건2] 주소 일치. 차량번호: {}", vhclno);
|
||||
return false;
|
||||
}
|
||||
|
||||
log.info("[이첩][조건2] 주소 불일치! 차량번호: {}, 사용본거지: {}, 소유자주소: {}",
|
||||
vhclno, useStrnghldAdresNm, ownerAdresNm);
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
**2단계: checkTransfer() 메서드에 조건 추가**
|
||||
|
||||
```java
|
||||
private String checkTransfer(CarFfnlgTrgtVO existingData, BasicResponse.Record basicInfo, String userId) {
|
||||
String vhclno = existingData.getVhclno();
|
||||
|
||||
// ========== 이첩 조건들 (OR 로직: 하나라도 만족하면 이첩) ==========
|
||||
|
||||
// 조건1: 법정동코드 불일치
|
||||
if (checkTransferCondition1_LegalDongMismatch(basicInfo, userId, vhclno)) {
|
||||
return processTransfer(existingData, basicInfo, vhclno, "법정동코드 불일치");
|
||||
}
|
||||
|
||||
// 조건2: 주소 불일치 (새로 추가!)
|
||||
if (checkTransferCondition2_AddressMismatch(basicInfo, userId, vhclno)) {
|
||||
return processTransfer(existingData, basicInfo, vhclno, "주소 불일치");
|
||||
}
|
||||
|
||||
// 조건3: 향후 추가될 조건들...
|
||||
// if (checkTransferCondition3_XXX(basicInfo, userId, vhclno)) {
|
||||
// return processTransfer(existingData, basicInfo, vhclno, "XXX");
|
||||
// }
|
||||
|
||||
return null;
|
||||
}
|
||||
```
|
||||
|
||||
**끝! 매우 간단합니다.**
|
||||
|
||||
---
|
||||
|
||||
### 📊 이첩 조건 OR 로직 동작 방식
|
||||
|
||||
```java
|
||||
// 이첩 체크 시작
|
||||
checkTransfer(...)
|
||||
↓
|
||||
조건1 체크 → TRUE → 즉시 이첩 처리 후 return "03"
|
||||
↓ FALSE
|
||||
조건2 체크 → TRUE → 즉시 이첩 처리 후 return "03"
|
||||
↓ FALSE
|
||||
조건3 체크 → TRUE → 즉시 이첩 처리 후 return "03"
|
||||
↓ FALSE
|
||||
모든 조건 FALSE → return null (이첩 아님)
|
||||
```
|
||||
|
||||
### 🔍 로그 예시
|
||||
|
||||
#### 조건1에 걸린 경우
|
||||
```
|
||||
[이첩][조건1] 법정동코드 불일치! 차량번호: 12가3456, 법정동: 1100, 조직: 4100
|
||||
[이첩] 조건 충족! 차량번호: 12가3456, 사유: 법정동코드 불일치
|
||||
[이첩] 처리 완료! 차량번호: 12가3456, 시군구: 서울특별시(11000), 사유: 법정동코드 불일치
|
||||
```
|
||||
|
||||
#### 조건2에 걸린 경우
|
||||
```
|
||||
[이첩][조건1] 법정동코드 일치. 차량번호: 12가3456, 법정동: 1100, 조직: 1100
|
||||
[이첩][조건2] 주소 불일치! 차량번호: 12가3456
|
||||
[이첩] 조건 충족! 차량번호: 12가3456, 사유: 주소 불일치
|
||||
[이첩] 처리 완료! 차량번호: 12가3456, 시군구: 서울특별시(11000), 사유: 주소 불일치
|
||||
```
|
||||
|
||||
#### 모든 조건에 안 걸린 경우
|
||||
```
|
||||
[이첩][조건1] 법정동코드 일치. 차량번호: 12가3456
|
||||
[이첩][조건2] 주소 일치. 차량번호: 12가3456
|
||||
[이첩] 모든 조건 미충족. 차량번호: 12가3456
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 🚀 빠른 템플릿
|
||||
|
||||
#### 일반 비교 조건 템플릿
|
||||
|
||||
```java
|
||||
/**
|
||||
* N. 비교 조건명
|
||||
*
|
||||
* <p>조건: 조건 설명</p>
|
||||
* <p>처리: TASK_PRCS_STTS_CD = XX</p>
|
||||
*
|
||||
* @return XX (적용됨) 또는 null (미적용)
|
||||
*/
|
||||
private String checkXXX(CarFfnlgTrgtVO existingData, BasicResponse.Record basicInfo) {
|
||||
String vhclno = existingData.getVhclno();
|
||||
|
||||
// 1. 데이터 추출
|
||||
String data1 = basicInfo.getXXX();
|
||||
String data2 = basicInfo.getYYY();
|
||||
|
||||
// 2. 유효성 검사
|
||||
if (data1 == null || data2 == null) {
|
||||
log.debug("[비교조건명] 조건 미충족. 차량번호: {}", vhclno);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 3. 조건 체크
|
||||
if (조건_만족) {
|
||||
log.info("[비교조건명] 조건 충족! 차량번호: {}, 상세정보...", vhclno);
|
||||
|
||||
// 4. DB 업데이트
|
||||
existingData.setTaskPrcsSttsCd("XX");
|
||||
existingData.setTaskPrcsYmd(LocalDate.now().format(DATE_FORMATTER));
|
||||
// 필요한 필드 추가 설정
|
||||
|
||||
int updateCount = carFfnlgTrgtMapper.update(existingData);
|
||||
if (updateCount == 0) {
|
||||
throw new RuntimeException(String.format("[비교조건명] 업데이트 실패: %s", vhclno));
|
||||
}
|
||||
|
||||
log.info("[비교조건명] 처리 완료! 차량번호: {}", vhclno);
|
||||
return "XX";
|
||||
}
|
||||
|
||||
log.debug("[비교조건명] 조건 미충족. 차량번호: {}", vhclno);
|
||||
return null;
|
||||
}
|
||||
```
|
||||
|
||||
#### 이첩 조건 템플릿
|
||||
|
||||
```java
|
||||
/**
|
||||
* 이첩 조건N: XXX
|
||||
* 조건 설명
|
||||
*/
|
||||
private boolean checkTransferConditionN_XXX(BasicResponse.Record basicInfo, String userId, String vhclno) {
|
||||
// 1. 데이터 추출
|
||||
String data1 = basicInfo.getXXX();
|
||||
String data2 = basicInfo.getYYY();
|
||||
|
||||
// 2. 유효성 검사
|
||||
if (data1 == null || data2 == null) {
|
||||
log.debug("[이첩][조건N] 데이터 없음. 차량번호: {}", vhclno);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. 조건 체크
|
||||
if (조건_만족) {
|
||||
log.info("[이첩][조건N] 조건 충족! 차량번호: {}, 상세정보...", vhclno);
|
||||
return true; // 이첩!
|
||||
}
|
||||
|
||||
log.debug("[이첩][조건N] 조건 미충족. 차량번호: {}", vhclno);
|
||||
return false;
|
||||
}
|
||||
|
||||
// checkTransfer()에 추가
|
||||
if (checkTransferConditionN_XXX(basicInfo, userId, vhclno)) {
|
||||
return processTransfer(existingData, basicInfo, vhclno, "XXX");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### ⚠️ 주의사항
|
||||
|
||||
1. **조건 순서**: 위에서부터 순차적으로 체크됩니다
|
||||
- 자주 걸리는 조건을 위쪽에 배치하면 성능 향상
|
||||
|
||||
2. **조건 메서드 네이밍**:
|
||||
- 일반: `checkXXX`
|
||||
- 이첩: `checkTransferConditionN_XXX` 형식 권장
|
||||
|
||||
3. **return 값**:
|
||||
- 일반 조건: `"상태코드"` (적용됨) 또는 `null` (미적용)
|
||||
- 이첩 조건: `true` (해당함) 또는 `false` (해당 안함)
|
||||
|
||||
4. **processTransfer() 공통 사용**:
|
||||
- DB 업데이트는 `processTransfer()` 메서드에서 공통으로 처리
|
||||
- 각 조건에서는 `true/false`만 반환
|
||||
|
||||
---
|
||||
|
||||
### 📝 체크리스트
|
||||
|
||||
새로운 비교 조건 추가 시 확인사항:
|
||||
|
||||
- [ ] 조건 메서드 작성 완료
|
||||
- [ ] executeComparison()에 조건 추가 완료 (일반 조건)
|
||||
- [ ] checkTransfer()에 조건 추가 완료 (이첩 조건)
|
||||
- [ ] 로그 메시지에 조건 정보 포함
|
||||
- [ ] null 체크 처리 완료
|
||||
- [ ] 컴파일 테스트 완료
|
||||
- [ ] 실제 데이터로 테스트 완료
|
||||
|
||||
---
|
||||
|
||||
## 방법2: Chain of Responsibility 패턴
|
||||
|
||||
### 🌟 특징
|
||||
|
||||
- ✅ **확장성 우수** - 규칙 추가/삭제가 독립적
|
||||
- ✅ **테스트 용이** - 각 규칙을 독립적으로 테스트 가능
|
||||
- ✅ **순서 제어** - getOrder()로 실행 순서 명확히 제어
|
||||
- ✅ **관심사 분리** - 각 규칙이 독립된 파일로 관리
|
||||
- ❌ **복잡한 구조** - 학습 곡선 높음
|
||||
- ❌ **파일 분산** - 여러 파일을 확인해야 함
|
||||
|
||||
### 📁 디렉토리 구조
|
||||
|
||||
```
|
||||
comparison/
|
||||
├── ComparisonRule.java # 비교 규칙 인터페이스
|
||||
├── ComparisonContext.java # 비교에 필요한 데이터 컨테이너
|
||||
├── ComparisonResult.java # 비교 결과 객체
|
||||
├── ComparisonRuleProcessor.java # 규칙 실행 체인 관리자
|
||||
└── rules/ # 개별 규칙 구현체
|
||||
├── ProductUseComparisonRule.java # 상품용 규칙
|
||||
└── TransferComparisonRule.java # 이첩 규칙
|
||||
```
|
||||
|
||||
### 🔄 동작 방식
|
||||
|
||||
1. **ComparisonRuleProcessor**가 모든 `@Component` 규칙을 자동으로 찾아서 등록
|
||||
2. 규칙들을 `getOrder()` 순서대로 정렬 (낮을수록 먼저 실행)
|
||||
3. 각 규칙을 순차적으로 실행
|
||||
4. 규칙이 `applied=true`를 반환하면 즉시 중단
|
||||
5. 모든 규칙이 `applied=false`를 반환하면 "정상" 처리
|
||||
|
||||
```
|
||||
ComparisonRuleProcessor
|
||||
↓
|
||||
규칙 자동 스캔 (@Component)
|
||||
↓
|
||||
순서대로 정렬 (getOrder())
|
||||
↓
|
||||
ProductUseComparisonRule (order=10) → 적용됨? → 종료
|
||||
↓ 미적용
|
||||
TransferComparisonRule (order=20) → 적용됨? → 종료
|
||||
↓ 미적용
|
||||
YourNewRule (order=30) → 적용됨? → 종료
|
||||
↓ 미적용
|
||||
정상 처리
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 🚀 새로운 비교 규칙 추가 방법
|
||||
|
||||
#### 1단계: rules/ 폴더에 새 클래스 생성
|
||||
|
||||
```java
|
||||
package go.kr.project.carInspectionPenalty.registration.comparison.rules;
|
||||
|
||||
import go.kr.project.carInspectionPenalty.registration.comparison.*;
|
||||
import go.kr.project.carInspectionPenalty.registration.mapper.CarFfnlgTrgtMapper;
|
||||
import go.kr.project.carInspectionPenalty.registration.model.CarFfnlgTrgtVO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* 내사종결 비교 규칙
|
||||
*
|
||||
* 적용 조건:
|
||||
* - 예시: 차량이 말소된 경우
|
||||
*
|
||||
* 처리 내용:
|
||||
* - TASK_PRCS_STTS_CD = 04 (내사종결)
|
||||
* - TASK_PRCS_YMD = 현재 날짜
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class InvestigationClosedComparisonRule implements ComparisonRule {
|
||||
|
||||
private static final String STATUS_CODE = "04"; // 내사종결
|
||||
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd");
|
||||
|
||||
private final CarFfnlgTrgtMapper mapper;
|
||||
|
||||
@Override
|
||||
public ComparisonResult execute(ComparisonContext context) {
|
||||
String vhclno = context.getVhclno();
|
||||
|
||||
// 1. API 응답 데이터 유효성 검사
|
||||
if (context.getApiResponse().getBasicInfo() == null ||
|
||||
context.getApiResponse().getBasicInfo().getRecord() == null ||
|
||||
context.getApiResponse().getBasicInfo().getRecord().isEmpty()) {
|
||||
log.debug("[{}] API 응답 데이터가 없어 규칙을 적용할 수 없습니다. 차량번호: {}",
|
||||
getRuleName(), vhclno);
|
||||
return ComparisonResult.notApplied();
|
||||
}
|
||||
|
||||
// 2. 필요한 데이터 추출
|
||||
go.kr.project.api.model.response.BasicResponse.Record basicInfo =
|
||||
context.getApiResponse().getBasicInfo().getRecord().get(0);
|
||||
String ersrRegistSeCode = basicInfo.getErsrRegistSeCode(); // 말소등록구분코드
|
||||
|
||||
// 3. 비교 로직 (말소된 차량인지 확인)
|
||||
if (ersrRegistSeCode == null || ersrRegistSeCode.isEmpty()) {
|
||||
log.debug("[{}] 말소되지 않은 차량입니다. 차량번호: {}", getRuleName(), vhclno);
|
||||
return ComparisonResult.notApplied();
|
||||
}
|
||||
|
||||
log.info("[{}] 내사종결 감지! 차량번호: {}, 말소구분: {}",
|
||||
getRuleName(), vhclno, ersrRegistSeCode);
|
||||
|
||||
// 4. DB 업데이트
|
||||
CarFfnlgTrgtVO updateData = context.getExistingData();
|
||||
updateData.setTaskPrcsSttsCd(STATUS_CODE);
|
||||
updateData.setTaskPrcsYmd(LocalDate.now().format(DATE_FORMATTER));
|
||||
// 필요시 추가 필드 설정
|
||||
|
||||
int updateResult = mapper.update(updateData);
|
||||
|
||||
// 5. 결과 반환
|
||||
if (updateResult > 0) {
|
||||
log.info("[{}] 처리 완료! 차량번호: {}", getRuleName(), vhclno);
|
||||
return ComparisonResult.applied(STATUS_CODE, "내사종결로 처리되었습니다.");
|
||||
} else {
|
||||
log.error("[{}] 업데이트 실패! 차량번호: {}", getRuleName(), vhclno);
|
||||
throw new RuntimeException(String.format("내사종결 업데이트 실패: %s", vhclno));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRuleName() {
|
||||
return "내사종결";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return 30; // 상품용(10), 이첩(20) 다음으로 실행
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 2단계: 자동 등록
|
||||
|
||||
Spring이 자동으로 `@Component` 어노테이션이 붙은 클래스를 찾아서 등록합니다.
|
||||
**별도의 설정 파일 수정이 필요 없습니다!**
|
||||
|
||||
#### 끝! 자동으로 등록됩니다.
|
||||
|
||||
---
|
||||
|
||||
### 📊 실행 순서 제어
|
||||
|
||||
`getOrder()` 메서드로 실행 순서를 제어할 수 있습니다.
|
||||
|
||||
```java
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return 30; // 숫자가 낮을수록 먼저 실행
|
||||
}
|
||||
```
|
||||
|
||||
**현재 순서:**
|
||||
- 상품용: 10
|
||||
- 이첩: 20
|
||||
- (향후 추가): 30, 40, 50...
|
||||
|
||||
---
|
||||
|
||||
### 📦 API 응답 데이터 활용
|
||||
|
||||
`ComparisonContext`를 통해 다음 데이터에 접근할 수 있습니다:
|
||||
|
||||
```java
|
||||
// 기존 과태료 대상 데이터
|
||||
CarFfnlgTrgtVO existingData = context.getExistingData();
|
||||
|
||||
// API 응답 - 기본 정보
|
||||
var basicInfo = context.getApiResponse().getBasicInfo().getRecord().get(0);
|
||||
String mberNm = basicInfo.getMberNm(); // 대표소유자성명
|
||||
String vhrno = basicInfo.getVhrno(); // 차량번호
|
||||
String useStrnghldLegaldongCode = basicInfo.getUseStrnghldLegaldongCode(); // 사용본거지법정동코드
|
||||
String ersrRegistSeCode = basicInfo.getErsrRegistSeCode(); // 말소등록구분코드
|
||||
// ... 기타 필드들
|
||||
|
||||
// API 응답 - 등록원부
|
||||
var ledgerInfo = context.getApiResponse().getLedgerInfo();
|
||||
|
||||
// 사용자 ID
|
||||
String userId = context.getUserId();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 💉 의존성 주입
|
||||
|
||||
규칙 클래스에서 필요한 빈을 자유롭게 주입받을 수 있습니다.
|
||||
|
||||
```java
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class MyComparisonRule implements ComparisonRule {
|
||||
|
||||
private final CarFfnlgTrgtMapper mapper; // Mapper
|
||||
private final UserMapper userMapper; // User 정보 필요 시
|
||||
private final SomeOtherService someService; // 다른 서비스
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 📝 로깅
|
||||
|
||||
규칙 실행 중 상세한 로그가 자동으로 출력됩니다.
|
||||
|
||||
```
|
||||
[상품용] 규칙 실행 중... 차량번호: 12가3456
|
||||
[상품용] 상품용 감지! 차량번호: 12가3456, 소유자명: 상품용차량
|
||||
[상품용] 처리 완료! 차량번호: 12가3456
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 🧪 테스트 방법
|
||||
|
||||
규칙을 독립적으로 테스트할 수 있습니다.
|
||||
|
||||
```java
|
||||
@SpringBootTest
|
||||
class InvestigationClosedComparisonRuleTest {
|
||||
|
||||
@Autowired
|
||||
private InvestigationClosedComparisonRule rule;
|
||||
|
||||
@Test
|
||||
void 말소된_차량은_내사종결로_처리된다() {
|
||||
// Given
|
||||
ComparisonContext context = ComparisonContext.builder()
|
||||
.existingData(existingData)
|
||||
.apiResponse(apiResponse)
|
||||
.userId("USER001")
|
||||
.build();
|
||||
|
||||
// When
|
||||
ComparisonResult result = rule.execute(context);
|
||||
|
||||
// Then
|
||||
assertTrue(result.isApplied());
|
||||
assertEquals("04", result.getStatusCode());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 📚 기존 규칙 예제
|
||||
|
||||
#### 상품용 규칙 (ProductUseComparisonRule)
|
||||
|
||||
```java
|
||||
// 대표소유자성명에 "상품용" 문자열이 포함되어 있는지 확인
|
||||
String mberNm = basicInfo.getMberNm();
|
||||
if (mberNm != null && mberNm.contains("상품용")) {
|
||||
// 상품용으로 처리
|
||||
return ComparisonResult.applied("02", "상품용으로 처리되었습니다.");
|
||||
}
|
||||
```
|
||||
|
||||
#### 이첩 규칙 (TransferComparisonRule)
|
||||
|
||||
```java
|
||||
// 법정동코드 앞 4자리와 사용자 조직코드 앞 4자리 비교
|
||||
String legalDong4 = useStrnghldLegaldongCode.substring(0, 4);
|
||||
String userOrg4 = userOrgCd.substring(0, 4);
|
||||
|
||||
if (!legalDong4.equals(userOrg4)) {
|
||||
// 이첩으로 처리
|
||||
return ComparisonResult.applied("03", "이첩으로 처리되었습니다.");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 어떤 방법을 선택해야 할까?
|
||||
|
||||
### 방법1 선택 (Service/Impl) - 다음과 같은 경우 추천 ⭐
|
||||
|
||||
- ✅ **간단한 프로젝트** - 비교 조건이 10개 이하
|
||||
- ✅ **빠른 개발 필요** - 당장 구현해야 할 때
|
||||
- ✅ **팀원 경험 부족** - 디자인 패턴에 익숙하지 않은 경우
|
||||
- ✅ **유지보수 단순** - 한 파일에서 모든 로직 확인 가능
|
||||
- ✅ **작은 팀** - 1~3명 정도의 소규모 팀
|
||||
|
||||
### 방법2 선택 (Chain of Responsibility) - 다음과 같은 경우 추천
|
||||
|
||||
- ✅ **복잡한 프로젝트** - 비교 조건이 10개 이상
|
||||
- ✅ **장기 유지보수** - 규칙이 자주 추가/변경될 것으로 예상
|
||||
- ✅ **팀 규모 큽** - 여러 개발자가 동시에 작업
|
||||
- ✅ **테스트 중요** - 각 규칙을 독립적으로 테스트해야 함
|
||||
- ✅ **확장성 중시** - 규칙을 동적으로 추가/제거 필요
|
||||
|
||||
---
|
||||
|
||||
## 참고사항
|
||||
|
||||
### 💡 현재 설정 변경 방법
|
||||
|
||||
`CarFfnlgTrgtServiceImpl.java`의 `executeComparisonLogic()` 메서드에서 주석을 변경하면 됩니다.
|
||||
|
||||
```java
|
||||
private String executeComparisonLogic(...) {
|
||||
// 방법1 사용하려면: (현재 설정)
|
||||
return executeWithServicePattern(existingData, apiResponse, userId);
|
||||
|
||||
// 방법2 사용하려면:
|
||||
// return executeWithChainPattern(existingData, apiResponse, userId);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### ⚠️ 주의사항
|
||||
|
||||
1. **두 방법을 동시에 사용하지 마세요** - 하나만 선택
|
||||
2. **트랜잭션 관리** - 각 비교 메서드는 DB 업데이트를 직접 수행
|
||||
3. **예외 처리** - 업데이트 실패 시 RuntimeException 발생 → 전체 롤백
|
||||
4. **로깅** - 각 조건의 충족/미충족 여부를 명확히 로깅
|
||||
5. **순서 관리** (방법2) - `getOrder()` 값이 중복되지 않도록 주의
|
||||
6. **null 체크** - API 응답 데이터는 항상 null 체크 필수
|
||||
|
||||
---
|
||||
|
||||
### 🚀 빠른 시작
|
||||
|
||||
#### 방법1로 시작하기 (추천)
|
||||
1. `ComparisonServiceImpl.java` 열기
|
||||
2. 기존 메서드 참고하여 새 메서드 추가
|
||||
3. `executeComparison()`에서 호출
|
||||
4. 끝!
|
||||
|
||||
#### 방법2로 시작하기
|
||||
1. `comparison/rules/` 폴더에 새 규칙 클래스 생성
|
||||
2. `@Component` 붙이기
|
||||
3. `ComparisonRule` 인터페이스 구현
|
||||
4. 끝!
|
||||
|
||||
---
|
||||
|
||||
### 📞 문의
|
||||
|
||||
추가 질문이나 제안사항은 개발팀에 문의하세요.
|
||||
|
||||
---
|
||||
|
||||
### 📖 관련 파일
|
||||
|
||||
- **방법1 파일**:
|
||||
- `service/ComparisonService.java`
|
||||
- `service/impl/ComparisonServiceImpl.java`
|
||||
|
||||
- **방법2 파일**:
|
||||
- `comparison/` 패키지 전체
|
||||
|
||||
- **통합 지점**:
|
||||
- `CarFfnlgTrgtServiceImpl.java` - `executeComparisonLogic()` 메서드
|
||||
@ -0,0 +1,38 @@
|
||||
{
|
||||
"vhrno": "11가1111",
|
||||
"success": true,
|
||||
"message": "조회 성공",
|
||||
"basicInfo": {
|
||||
"CNTC_RESULT_CODE": "MSG50000",
|
||||
"CNTC_RESULT_DTLS": "정상",
|
||||
"record": [
|
||||
{
|
||||
"VHRNO": "11가1111",
|
||||
"MBER_NM": "김철수",
|
||||
"MBER_SE_CODE": "1",
|
||||
"MBER_SE_NO": "8001011234567",
|
||||
"USE_STRNGHLD_LEGALDONG_CODE": "1100000000",
|
||||
"USE_STRNGHLD_ADRES_NM": "서울특별시 강남구 테헤란로 123",
|
||||
"OWNER_ADRES_NM": "서울특별시 강남구 테헤란로 123",
|
||||
"CNM": "그랜저",
|
||||
"VHCTY_ASORT_NM": "승용차",
|
||||
"VHCTY_TY_NM": "중형",
|
||||
"VHCTY_SE_NM": "일반형",
|
||||
"PRYE": "2022",
|
||||
"REGIST_DE": "20220315",
|
||||
"FRST_REGIST_DE": "20220315",
|
||||
"INSPT_VALID_PD_BGNDE": "20240315",
|
||||
"INSPT_VALID_PD_ENDDE": "20260315",
|
||||
"ERSR_REGIST_SE_CODE": "",
|
||||
"ERSR_REGIST_DE": "",
|
||||
"OWNER_LEGALDONG_CODE": "1100000000",
|
||||
"USE_STRNGHLD_GRC_CODE": "1100",
|
||||
"TELNO": "01012345678"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ledgerInfo": {
|
||||
"VHRNO": "11가1111",
|
||||
"VIN": "KMHXX00XXXX111111"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
{
|
||||
"vhrno": "22나2222",
|
||||
"success": true,
|
||||
"message": "조회 성공",
|
||||
"basicInfo": {
|
||||
"CNTC_RESULT_CODE": "MSG50000",
|
||||
"CNTC_RESULT_DTLS": "정상",
|
||||
"record": [
|
||||
{
|
||||
"VHRNO": "22나2222",
|
||||
"MBER_NM": "상품용",
|
||||
"MBER_SE_CODE": "2",
|
||||
"MBER_SE_NO": "1234567890123",
|
||||
"USE_STRNGHLD_LEGALDONG_CODE": "1100000000",
|
||||
"USE_STRNGHLD_ADRES_NM": "서울특별시 중구 세종대로 110",
|
||||
"OWNER_ADRES_NM": "서울특별시 중구 세종대로 110",
|
||||
"CNM": "쏘나타",
|
||||
"VHCTY_ASORT_NM": "승용차",
|
||||
"VHCTY_TY_NM": "중형",
|
||||
"VHCTY_SE_NM": "일반형",
|
||||
"PRYE": "2023",
|
||||
"REGIST_DE": "20230101",
|
||||
"FRST_REGIST_DE": "20230101",
|
||||
"INSPT_VALID_PD_BGNDE": "20250101",
|
||||
"INSPT_VALID_PD_ENDDE": "20270101",
|
||||
"ERSR_REGIST_SE_CODE": "",
|
||||
"ERSR_REGIST_DE": "",
|
||||
"OWNER_LEGALDONG_CODE": "1100000000",
|
||||
"USE_STRNGHLD_GRC_CODE": "1100",
|
||||
"TELNO": "0212345678"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ledgerInfo": {
|
||||
"VHRNO": "22나2222",
|
||||
"VIN": "KMHYY00YYYY222222"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
{
|
||||
"vhrno": "33다3333",
|
||||
"success": true,
|
||||
"message": "조회 성공",
|
||||
"basicInfo": {
|
||||
"CNTC_RESULT_CODE": "MSG50000",
|
||||
"CNTC_RESULT_DTLS": "정상",
|
||||
"record": [
|
||||
{
|
||||
"VHRNO": "33다3333",
|
||||
"MBER_NM": "이영희",
|
||||
"MBER_SE_CODE": "1",
|
||||
"MBER_SE_NO": "9002022345678",
|
||||
"USE_STRNGHLD_LEGALDONG_CODE": "4100000000",
|
||||
"USE_STRNGHLD_ADRES_NM": "경기도 수원시 팔달구 효원로 1",
|
||||
"OWNER_ADRES_NM": "경기도 수원시 팔달구 효원로 1",
|
||||
"CNM": "아반떼",
|
||||
"VHCTY_ASORT_NM": "승용차",
|
||||
"VHCTY_TY_NM": "소형",
|
||||
"VHCTY_SE_NM": "일반형",
|
||||
"PRYE": "2021",
|
||||
"REGIST_DE": "20210520",
|
||||
"FRST_REGIST_DE": "20210520",
|
||||
"INSPT_VALID_PD_BGNDE": "20230520",
|
||||
"INSPT_VALID_PD_ENDDE": "20250520",
|
||||
"ERSR_REGIST_SE_CODE": "",
|
||||
"ERSR_REGIST_DE": "",
|
||||
"OWNER_LEGALDONG_CODE": "4100000000",
|
||||
"USE_STRNGHLD_GRC_CODE": "4100",
|
||||
"TELNO": "01098765432"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ledgerInfo": {
|
||||
"VHRNO": "33다3333",
|
||||
"VIN": "KMHZZ00ZZZZ333333"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
{
|
||||
"vhrno": "44라4444",
|
||||
"success": true,
|
||||
"message": "조회 성공",
|
||||
"basicInfo": {
|
||||
"CNTC_RESULT_CODE": "MSG50000",
|
||||
"CNTC_RESULT_DTLS": "정상",
|
||||
"record": [
|
||||
{
|
||||
"VHRNO": "44라4444",
|
||||
"MBER_NM": "박민수",
|
||||
"MBER_SE_CODE": "1",
|
||||
"MBER_SE_NO": "8503033456789",
|
||||
"USE_STRNGHLD_LEGALDONG_CODE": "2600000000",
|
||||
"USE_STRNGHLD_ADRES_NM": "부산광역시 해운대구 우동 1234",
|
||||
"OWNER_ADRES_NM": "부산광역시 해운대구 우동 1234",
|
||||
"CNM": "카니발",
|
||||
"VHCTY_ASORT_NM": "승합차",
|
||||
"VHCTY_TY_NM": "대형",
|
||||
"VHCTY_SE_NM": "일반형",
|
||||
"PRYE": "2020",
|
||||
"REGIST_DE": "20200815",
|
||||
"FRST_REGIST_DE": "20200815",
|
||||
"INSPT_VALID_PD_BGNDE": "20220815",
|
||||
"INSPT_VALID_PD_ENDDE": "20240815",
|
||||
"ERSR_REGIST_SE_CODE": "",
|
||||
"ERSR_REGIST_DE": "",
|
||||
"OWNER_LEGALDONG_CODE": "2600000000",
|
||||
"USE_STRNGHLD_GRC_CODE": "2600",
|
||||
"TELNO": "01055554444"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ledgerInfo": {
|
||||
"VHRNO": "44라4444",
|
||||
"VIN": "KMHAA00AAAA444444"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
{
|
||||
"vhrno": "55마5555",
|
||||
"success": true,
|
||||
"message": "조회 성공",
|
||||
"basicInfo": {
|
||||
"CNTC_RESULT_CODE": "MSG50000",
|
||||
"CNTC_RESULT_DTLS": "정상",
|
||||
"record": [
|
||||
{
|
||||
"VHRNO": "55마5555",
|
||||
"MBER_NM": "최영수",
|
||||
"MBER_SE_CODE": "1",
|
||||
"MBER_SE_NO": "7504044567890",
|
||||
"USE_STRNGHLD_LEGALDONG_CODE": "3000000000",
|
||||
"USE_STRNGHLD_ADRES_NM": "대전광역시 유성구 대학로 99",
|
||||
"OWNER_ADRES_NM": "대전광역시 유성구 대학로 99",
|
||||
"CNM": "모닝",
|
||||
"VHCTY_ASORT_NM": "승용차",
|
||||
"VHCTY_TY_NM": "경형",
|
||||
"VHCTY_SE_NM": "일반형",
|
||||
"PRYE": "2019",
|
||||
"REGIST_DE": "20190910",
|
||||
"FRST_REGIST_DE": "20190910",
|
||||
"INSPT_VALID_PD_BGNDE": "20210910",
|
||||
"INSPT_VALID_PD_ENDDE": "20230910",
|
||||
"ERSR_REGIST_SE_CODE": "1",
|
||||
"ERSR_REGIST_DE": "20231201",
|
||||
"OWNER_LEGALDONG_CODE": "3000000000",
|
||||
"USE_STRNGHLD_GRC_CODE": "3000",
|
||||
"TELNO": "01066667777"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ledgerInfo": {
|
||||
"VHRNO": "55마5555",
|
||||
"VIN": "KMHBB00BBBB555555"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
{
|
||||
"vhrno": "66바6666",
|
||||
"success": true,
|
||||
"message": "조회 성공",
|
||||
"basicInfo": {
|
||||
"CNTC_RESULT_CODE": "MSG50000",
|
||||
"CNTC_RESULT_DTLS": "정상",
|
||||
"record": [
|
||||
{
|
||||
"VHRNO": "66바6666",
|
||||
"MBER_NM": "정수진",
|
||||
"MBER_SE_CODE": "1",
|
||||
"MBER_SE_NO": "9205055678901",
|
||||
"USE_STRNGHLD_LEGALDONG_CODE": "2800000000",
|
||||
"USE_STRNGHLD_ADRES_NM": "인천광역시 남동구 구월동 1234-5",
|
||||
"OWNER_ADRES_NM": "인천광역시 남동구 구월동 1234-5",
|
||||
"CNM": "스포티지",
|
||||
"VHCTY_ASORT_NM": "승용차",
|
||||
"VHCTY_TY_NM": "중형",
|
||||
"VHCTY_SE_NM": "SUV",
|
||||
"PRYE": "2024",
|
||||
"REGIST_DE": "20240101",
|
||||
"FRST_REGIST_DE": "20240101",
|
||||
"INSPT_VALID_PD_BGNDE": "20260101",
|
||||
"INSPT_VALID_PD_ENDDE": "20280101",
|
||||
"ERSR_REGIST_SE_CODE": "",
|
||||
"ERSR_REGIST_DE": "",
|
||||
"OWNER_LEGALDONG_CODE": "2800000000",
|
||||
"USE_STRNGHLD_GRC_CODE": "2800",
|
||||
"TELNO": "01077778888"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ledgerInfo": {
|
||||
"VHRNO": "66바6666",
|
||||
"VIN": "KMHCC00CCCC666666"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
{
|
||||
"vhrno": "77사7777",
|
||||
"success": true,
|
||||
"message": "조회 성공",
|
||||
"basicInfo": {
|
||||
"CNTC_RESULT_CODE": "MSG50000",
|
||||
"CNTC_RESULT_DTLS": "정상",
|
||||
"record": [
|
||||
{
|
||||
"VHRNO": "77사7777",
|
||||
"MBER_NM": "전시용",
|
||||
"MBER_SE_CODE": "2",
|
||||
"MBER_SE_NO": "2208123456789",
|
||||
"USE_STRNGHLD_LEGALDONG_CODE": "4100000000",
|
||||
"USE_STRNGHLD_ADRES_NM": "경기도 성남시 분당구 판교역로 231",
|
||||
"OWNER_ADRES_NM": "경기도 성남시 분당구 판교역로 231",
|
||||
"CNM": "제네시스 G80",
|
||||
"VHCTY_ASORT_NM": "승용차",
|
||||
"VHCTY_TY_NM": "대형",
|
||||
"VHCTY_SE_NM": "일반형",
|
||||
"PRYE": "2025",
|
||||
"REGIST_DE": "20250201",
|
||||
"FRST_REGIST_DE": "20250201",
|
||||
"INSPT_VALID_PD_BGNDE": "20270201",
|
||||
"INSPT_VALID_PD_ENDDE": "20290201",
|
||||
"ERSR_REGIST_SE_CODE": "",
|
||||
"ERSR_REGIST_DE": "",
|
||||
"OWNER_LEGALDONG_CODE": "4100000000",
|
||||
"USE_STRNGHLD_GRC_CODE": "4100",
|
||||
"TELNO": "0315551234"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ledgerInfo": {
|
||||
"VHRNO": "77사7777",
|
||||
"VIN": "KMHDD00DDDD777777"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
{
|
||||
"vhrno": "88아8888",
|
||||
"success": true,
|
||||
"message": "조회 성공",
|
||||
"basicInfo": {
|
||||
"CNTC_RESULT_CODE": "MSG50000",
|
||||
"CNTC_RESULT_DTLS": "정상",
|
||||
"record": [
|
||||
{
|
||||
"VHRNO": "88아8888",
|
||||
"MBER_NM": "강원철",
|
||||
"MBER_SE_CODE": "1",
|
||||
"MBER_SE_NO": "8806066789012",
|
||||
"USE_STRNGHLD_LEGALDONG_CODE": "5100000000",
|
||||
"USE_STRNGHLD_ADRES_NM": "강원특별자치도 춘천시 중앙로 1",
|
||||
"OWNER_ADRES_NM": "강원특별자치도 춘천시 중앙로 1",
|
||||
"CNM": "투싼",
|
||||
"VHCTY_ASORT_NM": "승용차",
|
||||
"VHCTY_TY_NM": "소형",
|
||||
"VHCTY_SE_NM": "SUV",
|
||||
"PRYE": "2023",
|
||||
"REGIST_DE": "20230715",
|
||||
"FRST_REGIST_DE": "20230715",
|
||||
"INSPT_VALID_PD_BGNDE": "20250715",
|
||||
"INSPT_VALID_PD_ENDDE": "20270715",
|
||||
"ERSR_REGIST_SE_CODE": "",
|
||||
"ERSR_REGIST_DE": "",
|
||||
"OWNER_LEGALDONG_CODE": "5100000000",
|
||||
"USE_STRNGHLD_GRC_CODE": "5100",
|
||||
"TELNO": "01088889999"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ledgerInfo": {
|
||||
"VHRNO": "88아8888",
|
||||
"VIN": "KMHEE00EEEE888888"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
{
|
||||
"vhrno": "99자9999",
|
||||
"success": true,
|
||||
"message": "조회 성공",
|
||||
"basicInfo": {
|
||||
"CNTC_RESULT_CODE": "MSG50000",
|
||||
"CNTC_RESULT_DTLS": "정상",
|
||||
"record": [
|
||||
{
|
||||
"VHRNO": "99자9999",
|
||||
"MBER_NM": "상품용차량",
|
||||
"MBER_SE_CODE": "2",
|
||||
"MBER_SE_NO": "3209087890123",
|
||||
"USE_STRNGHLD_LEGALDONG_CODE": "2700000000",
|
||||
"USE_STRNGHLD_ADRES_NM": "대구광역시 중구 동성로 123",
|
||||
"OWNER_ADRES_NM": "대구광역시 중구 동성로 123",
|
||||
"CNM": "팰리세이드",
|
||||
"VHCTY_ASORT_NM": "승용차",
|
||||
"VHCTY_TY_NM": "대형",
|
||||
"VHCTY_SE_NM": "SUV",
|
||||
"PRYE": "2024",
|
||||
"REGIST_DE": "20240901",
|
||||
"FRST_REGIST_DE": "20240901",
|
||||
"INSPT_VALID_PD_BGNDE": "20260901",
|
||||
"INSPT_VALID_PD_ENDDE": "20280901",
|
||||
"ERSR_REGIST_SE_CODE": "",
|
||||
"ERSR_REGIST_DE": "",
|
||||
"OWNER_LEGALDONG_CODE": "2700000000",
|
||||
"USE_STRNGHLD_GRC_CODE": "2700",
|
||||
"TELNO": "0539990000"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ledgerInfo": {
|
||||
"VHRNO": "99자9999",
|
||||
"VIN": "KMHFF00FFFF999999"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
{
|
||||
"vhrno": "00차0000",
|
||||
"success": true,
|
||||
"message": "조회 성공",
|
||||
"basicInfo": {
|
||||
"CNTC_RESULT_CODE": "MSG50000",
|
||||
"CNTC_RESULT_DTLS": "정상",
|
||||
"record": [
|
||||
{
|
||||
"VHRNO": "00차0000",
|
||||
"MBER_NM": "윤하나",
|
||||
"MBER_SE_CODE": "1",
|
||||
"MBER_SE_NO": "9507077890234",
|
||||
"USE_STRNGHLD_LEGALDONG_CODE": "4300000000",
|
||||
"USE_STRNGHLD_ADRES_NM": "충청남도 천안시 동남구 만남로 1",
|
||||
"OWNER_ADRES_NM": "충청남도 천안시 동남구 만남로 1",
|
||||
"CNM": "K5",
|
||||
"VHCTY_ASORT_NM": "승용차",
|
||||
"VHCTY_TY_NM": "중형",
|
||||
"VHCTY_SE_NM": "일반형",
|
||||
"PRYE": "2022",
|
||||
"REGIST_DE": "20221010",
|
||||
"FRST_REGIST_DE": "20221010",
|
||||
"INSPT_VALID_PD_BGNDE": "20241010",
|
||||
"INSPT_VALID_PD_ENDDE": "20261010",
|
||||
"ERSR_REGIST_SE_CODE": "",
|
||||
"ERSR_REGIST_DE": "",
|
||||
"OWNER_LEGALDONG_CODE": "4300000000",
|
||||
"USE_STRNGHLD_GRC_CODE": "4300",
|
||||
"TELNO": "01000001111"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ledgerInfo": {
|
||||
"VHRNO": "00차0000",
|
||||
"VIN": "KMHGG00GGGG000000"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,89 @@
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"VHMNO": "KMHXX00XXXX111111-01",
|
||||
"MTRS_FOM_NM": "G6DH",
|
||||
"PRCS_IMPRTY_RSN_CD": "00",
|
||||
"FRST_TRNSFR_YMD": "2022-03-15",
|
||||
"MRTG_CNT": "0",
|
||||
"COLOR_NM": "검정",
|
||||
"ERSR_REG_SE_CD": null,
|
||||
"CHCK_VLD_PD_END_YMD": null,
|
||||
"NOPLT_CSDY_YN": "N",
|
||||
"USG_SE_NM": "자가용",
|
||||
"VEAG_END_YMD": null,
|
||||
"record": [
|
||||
{
|
||||
"CHG_YMD": "2022-03-15",
|
||||
"VHMNO": "KMHXX00XXXX111111-01",
|
||||
"SPCABL_MTTR": "성명(상호) : 김철수 800101-1234567\n주소 : 서울특별시 강남구 테헤란로 123",
|
||||
"CHG_TASK_SE_NM": "신규등록(신조차)",
|
||||
"FLAG": null,
|
||||
"SZR_RMV_DTL_SN": "1",
|
||||
"MAIN_NO": "1-1",
|
||||
"DTL_SN": "1",
|
||||
"CHG_TASK_SE_CD": "01",
|
||||
"SNO": null,
|
||||
"APLY_RCPT_NO": "1100-202203-001234",
|
||||
"LEDGER_INDIV_NO": "1",
|
||||
"LEDGER_GROUP_NO": "1",
|
||||
"HSHLDR_NM": "김철수",
|
||||
"HSHLDR_IDECNO": "800101-1234567"
|
||||
}
|
||||
],
|
||||
"TAXXMPT_TRPR_SE_CD": "0",
|
||||
"SPCABL_MTTR_CNT": "0",
|
||||
"DRVNG_DSTNC": "25800",
|
||||
"NOPLT_CSDY_AVTSMT_YMD": null,
|
||||
"ERSR_REG_SE_NM": null,
|
||||
"OWNR_ADDR": "서울특별시 강남구 테헤란로 123",
|
||||
"DRIV_SRGBTRY_IDNTF_NO": null,
|
||||
"YRIDNW": "2022",
|
||||
"SPMNNO_1": "A01",
|
||||
"BSS_USE_PD_YMD": null,
|
||||
"LINK_RSLT_CD": "MSG50000",
|
||||
"SPMNNO_2": "1-00012-0345-0678",
|
||||
"SZR_CNT": "0",
|
||||
"CARMDL_ASORT_CD": "1",
|
||||
"USGSRHLD_ADDR_1": "서울특별시 강남구 테헤란로 123",
|
||||
"USGSRHLD_ADDR_DTL_1": "101동 1001호",
|
||||
"RPRS_OWNR_TELNO": "01012345678",
|
||||
"NOPLT_SPCFCT_CD": "2",
|
||||
"REG_DTL_CD": "100",
|
||||
"ACQS_AMT": "35,000,000",
|
||||
"FOM_NM": "DN8-G6DH-P3A",
|
||||
"LINK_RSLT_DTL": "정상",
|
||||
"REG_APLY_SE_NM": "신조차",
|
||||
"OCTHT_ERSR_PRVNTC_AVTSMT_YMD": null,
|
||||
"OWNR_ADDR_DTL": "101동 1001호",
|
||||
"LEDGER_INDIV_NO": "1",
|
||||
"LEDGER_GROUP_NO": "1",
|
||||
"ISSU_NO": null,
|
||||
"INSP_VLD_PD_END_YMD": "2026-03-14",
|
||||
"CARMDL_ASORT_NM": "승용 중형",
|
||||
"CHCK_VLD_PD_BGNG_YMD": null,
|
||||
"USGSRHLD_DONG_NM": "삼성동",
|
||||
"REG_DTL_NM": "일반소유용",
|
||||
"NOPLT_SPCFCT_NM": "긴번호판",
|
||||
"FBCTN_YMD": "2022-03-10",
|
||||
"RPRSV_OWNR_IDECNO": "800101-1234567",
|
||||
"FRST_REG_YMD": "2022-03-15",
|
||||
"RPRS_OWNR_NM": "김철수",
|
||||
"PRCS_IMPRTY_RSN_DTLS": "운행차량",
|
||||
"FRST_REG_APLY_RCPT_NO": "1100-202203-001234",
|
||||
"PRVNTC_CNT": "0",
|
||||
"RPRS_OWNR_MBR_SE_CD": "11",
|
||||
"TAXXMPT_APLCN_SE_CD": "미적용",
|
||||
"ERSR_REG_YMD": null,
|
||||
"COLOR_CD": "01",
|
||||
"STRCT_CHG_CNT": "0",
|
||||
"INDVDL_BZMN_YN": "N",
|
||||
"VIN": "KMHXX00XXXX111111",
|
||||
"USG_SE_CD": "2",
|
||||
"XPORT_FLFL_YN_DCLR_YMD": null,
|
||||
"VHRNO": "11가1111",
|
||||
"INSP_VLD_PD_BGNG_YMD": "2022-03-15",
|
||||
"ATMB_NM": "그랜저"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,106 @@
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"VHMNO": "KMHYY00YYYY222222-01",
|
||||
"MTRS_FOM_NM": "G4NA",
|
||||
"PRCS_IMPRTY_RSN_CD": "00",
|
||||
"FRST_TRNSFR_YMD": "2023-01-01",
|
||||
"MRTG_CNT": "0",
|
||||
"COLOR_NM": "은색",
|
||||
"ERSR_REG_SE_CD": null,
|
||||
"CHCK_VLD_PD_END_YMD": null,
|
||||
"NOPLT_CSDY_YN": "N",
|
||||
"USG_SE_NM": "영업용",
|
||||
"VEAG_END_YMD": null,
|
||||
"record": [
|
||||
{
|
||||
"CHG_YMD": "2023-01-01",
|
||||
"VHMNO": "KMHYY00YYYY222222-01",
|
||||
"SPCABL_MTTR": "성명(상호) : 상품용 123-45-67890\n주소 : 서울특별시 중구 세종대로 110",
|
||||
"CHG_TASK_SE_NM": "신규등록(신조차)",
|
||||
"FLAG": null,
|
||||
"SZR_RMV_DTL_SN": "1",
|
||||
"MAIN_NO": "1-1",
|
||||
"DTL_SN": "1",
|
||||
"CHG_TASK_SE_CD": "01",
|
||||
"SNO": null,
|
||||
"APLY_RCPT_NO": "1100-202301-005678",
|
||||
"LEDGER_INDIV_NO": "1",
|
||||
"LEDGER_GROUP_NO": "1",
|
||||
"HSHLDR_NM": "상품용",
|
||||
"HSHLDR_IDECNO": "123-45-67890"
|
||||
},
|
||||
{
|
||||
"CHG_YMD": "2023-06-15",
|
||||
"VHMNO": "KMHYY00YYYY222222-01",
|
||||
"SPCABL_MTTR": "촉탁기관 : 서울시청 구분:압류\r압류관리번호:1100-20230615-000123\n\r압류내역 : 서울시청 주정차위반과태료[2023-01-100001] 교통과-12345 \n\r촉탁일자 : 2023-06-15",
|
||||
"CHG_TASK_SE_NM": "압류등록(압류)",
|
||||
"FLAG": null,
|
||||
"SZR_RMV_DTL_SN": "2",
|
||||
"MAIN_NO": "(1-2)",
|
||||
"DTL_SN": "2",
|
||||
"CHG_TASK_SE_CD": "41",
|
||||
"SNO": null,
|
||||
"APLY_RCPT_NO": "1100-230615-000123",
|
||||
"LEDGER_INDIV_NO": "1",
|
||||
"LEDGER_GROUP_NO": "1",
|
||||
"HSHLDR_NM": "",
|
||||
"HSHLDR_IDECNO": ""
|
||||
}
|
||||
],
|
||||
"TAXXMPT_TRPR_SE_CD": "0",
|
||||
"SPCABL_MTTR_CNT": "1",
|
||||
"DRVNG_DSTNC": "18500",
|
||||
"NOPLT_CSDY_AVTSMT_YMD": null,
|
||||
"ERSR_REG_SE_NM": null,
|
||||
"OWNR_ADDR": "서울특별시 중구 세종대로 110",
|
||||
"DRIV_SRGBTRY_IDNTF_NO": null,
|
||||
"YRIDNW": "2023",
|
||||
"SPMNNO_1": "A01",
|
||||
"BSS_USE_PD_YMD": null,
|
||||
"LINK_RSLT_CD": "MSG50000",
|
||||
"SPMNNO_2": "1-00023-0456-0789",
|
||||
"SZR_CNT": "1",
|
||||
"CARMDL_ASORT_CD": "1",
|
||||
"USGSRHLD_ADDR_1": "서울특별시 중구 세종대로 110",
|
||||
"USGSRHLD_ADDR_DTL_1": "상가동 1층",
|
||||
"RPRS_OWNR_TELNO": "0212345678",
|
||||
"NOPLT_SPCFCT_CD": "2",
|
||||
"REG_DTL_CD": "100",
|
||||
"ACQS_AMT": "28,500,000",
|
||||
"FOM_NM": "LF-G4NA-P2A",
|
||||
"LINK_RSLT_DTL": "정상",
|
||||
"REG_APLY_SE_NM": "신조차",
|
||||
"OCTHT_ERSR_PRVNTC_AVTSMT_YMD": null,
|
||||
"OWNR_ADDR_DTL": "상가동 1층",
|
||||
"LEDGER_INDIV_NO": "1",
|
||||
"LEDGER_GROUP_NO": "1",
|
||||
"ISSU_NO": null,
|
||||
"INSP_VLD_PD_END_YMD": "2027-01-01",
|
||||
"CARMDL_ASORT_NM": "승용 중형",
|
||||
"CHCK_VLD_PD_BGNG_YMD": null,
|
||||
"USGSRHLD_DONG_NM": "소공동",
|
||||
"REG_DTL_NM": "일반소유용",
|
||||
"NOPLT_SPCFCT_NM": "긴번호판",
|
||||
"FBCTN_YMD": "2022-12-20",
|
||||
"RPRSV_OWNR_IDECNO": "123-45-67890",
|
||||
"FRST_REG_YMD": "2023-01-01",
|
||||
"RPRS_OWNR_NM": "상품용",
|
||||
"PRCS_IMPRTY_RSN_DTLS": "운행차량",
|
||||
"FRST_REG_APLY_RCPT_NO": "1100-202301-005678",
|
||||
"PRVNTC_CNT": "0",
|
||||
"RPRS_OWNR_MBR_SE_CD": "13",
|
||||
"TAXXMPT_APLCN_SE_CD": "미적용",
|
||||
"ERSR_REG_YMD": null,
|
||||
"COLOR_CD": "05",
|
||||
"STRCT_CHG_CNT": "0",
|
||||
"INDVDL_BZMN_YN": "N",
|
||||
"VIN": "KMHYY00YYYY222222",
|
||||
"USG_SE_CD": "1",
|
||||
"XPORT_FLFL_YN_DCLR_YMD": null,
|
||||
"VHRNO": "22나2222",
|
||||
"INSP_VLD_PD_BGNG_YMD": "2023-01-01",
|
||||
"ATMB_NM": "쏘나타"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,123 @@
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"VHMNO": "KMHZZ00ZZZZ333333-01",
|
||||
"MTRS_FOM_NM": "G4FD",
|
||||
"PRCS_IMPRTY_RSN_CD": "00",
|
||||
"FRST_TRNSFR_YMD": "2021-05-20",
|
||||
"MRTG_CNT": "0",
|
||||
"COLOR_NM": "흰색",
|
||||
"ERSR_REG_SE_CD": null,
|
||||
"CHCK_VLD_PD_END_YMD": null,
|
||||
"NOPLT_CSDY_YN": "N",
|
||||
"USG_SE_NM": "자가용",
|
||||
"VEAG_END_YMD": null,
|
||||
"record": [
|
||||
{
|
||||
"CHG_YMD": "2021-05-20",
|
||||
"VHMNO": "KMHZZ00ZZZZ333333-01",
|
||||
"SPCABL_MTTR": "성명(상호) : 이영희 900202-2345678\n주소 : 경기도 수원시 팔달구 효원로 1",
|
||||
"CHG_TASK_SE_NM": "신규등록(신조차)",
|
||||
"FLAG": null,
|
||||
"SZR_RMV_DTL_SN": "1",
|
||||
"MAIN_NO": "1-1",
|
||||
"DTL_SN": "1",
|
||||
"CHG_TASK_SE_CD": "01",
|
||||
"SNO": null,
|
||||
"APLY_RCPT_NO": "4100-202105-002345",
|
||||
"LEDGER_INDIV_NO": "1",
|
||||
"LEDGER_GROUP_NO": "1",
|
||||
"HSHLDR_NM": "이영희",
|
||||
"HSHLDR_IDECNO": "900202-2345678"
|
||||
},
|
||||
{
|
||||
"CHG_YMD": "2024-03-10",
|
||||
"VHMNO": "KMHZZ00ZZZZ333333-01",
|
||||
"SPCABL_MTTR": "촉탁기관 : 수원시청 구분:압류\r압류관리번호:4100-20240310-000456\n\r압류내역 : 수원시청 주정차위반과태료[2024-01-200001] 교통관리과-67890 \n\r촉탁일자 : 2024-03-10",
|
||||
"CHG_TASK_SE_NM": "압류등록(압류)",
|
||||
"FLAG": null,
|
||||
"SZR_RMV_DTL_SN": "2",
|
||||
"MAIN_NO": "(1-2)",
|
||||
"DTL_SN": "2",
|
||||
"CHG_TASK_SE_CD": "41",
|
||||
"SNO": null,
|
||||
"APLY_RCPT_NO": "4100-240310-000456",
|
||||
"LEDGER_INDIV_NO": "1",
|
||||
"LEDGER_GROUP_NO": "1",
|
||||
"HSHLDR_NM": "",
|
||||
"HSHLDR_IDECNO": ""
|
||||
},
|
||||
{
|
||||
"CHG_YMD": "2024-08-20",
|
||||
"VHMNO": "KMHZZ00ZZZZ333333-01",
|
||||
"SPCABL_MTTR": "촉탁기관 : 수원시청 구분:압류\r압류관리번호:4100-20240310-000456\n\r압류명세 : 수원시청 주정차위반과태료[2024-01-200001] 교통관리과-67890\n\r촉탁일자 : 2024-08-20",
|
||||
"CHG_TASK_SE_NM": "압류해제(압류)",
|
||||
"FLAG": null,
|
||||
"SZR_RMV_DTL_SN": "3",
|
||||
"MAIN_NO": null,
|
||||
"DTL_SN": "3",
|
||||
"CHG_TASK_SE_CD": "42",
|
||||
"SNO": "(1-2)",
|
||||
"APLY_RCPT_NO": "4100-240820-001789",
|
||||
"LEDGER_INDIV_NO": "1",
|
||||
"LEDGER_GROUP_NO": "1",
|
||||
"HSHLDR_NM": "",
|
||||
"HSHLDR_IDECNO": ""
|
||||
}
|
||||
],
|
||||
"TAXXMPT_TRPR_SE_CD": "0",
|
||||
"SPCABL_MTTR_CNT": "0",
|
||||
"DRVNG_DSTNC": "85600",
|
||||
"NOPLT_CSDY_AVTSMT_YMD": null,
|
||||
"ERSR_REG_SE_NM": null,
|
||||
"OWNR_ADDR": "경기도 수원시 팔달구 효원로 1",
|
||||
"DRIV_SRGBTRY_IDNTF_NO": null,
|
||||
"YRIDNW": "2021",
|
||||
"SPMNNO_1": "A01",
|
||||
"BSS_USE_PD_YMD": null,
|
||||
"LINK_RSLT_CD": "MSG50000",
|
||||
"SPMNNO_2": "1-00034-0567-0890",
|
||||
"SZR_CNT": "0",
|
||||
"CARMDL_ASORT_CD": "1",
|
||||
"USGSRHLD_ADDR_1": "경기도 수원시 팔달구 효원로 1",
|
||||
"USGSRHLD_ADDR_DTL_1": "201호",
|
||||
"RPRS_OWNR_TELNO": "01098765432",
|
||||
"NOPLT_SPCFCT_CD": "2",
|
||||
"REG_DTL_CD": "100",
|
||||
"ACQS_AMT": "19,800,000",
|
||||
"FOM_NM": "AD-G4FD-P1A",
|
||||
"LINK_RSLT_DTL": "정상",
|
||||
"REG_APLY_SE_NM": "신조차",
|
||||
"OCTHT_ERSR_PRVNTC_AVTSMT_YMD": null,
|
||||
"OWNR_ADDR_DTL": "201호",
|
||||
"LEDGER_INDIV_NO": "1",
|
||||
"LEDGER_GROUP_NO": "1",
|
||||
"ISSU_NO": null,
|
||||
"INSP_VLD_PD_END_YMD": "2025-05-19",
|
||||
"CARMDL_ASORT_NM": "승용 소형",
|
||||
"CHCK_VLD_PD_BGNG_YMD": null,
|
||||
"USGSRHLD_DONG_NM": "인계동",
|
||||
"REG_DTL_NM": "일반소유용",
|
||||
"NOPLT_SPCFCT_NM": "긴번호판",
|
||||
"FBCTN_YMD": "2021-05-10",
|
||||
"RPRSV_OWNR_IDECNO": "900202-2345678",
|
||||
"FRST_REG_YMD": "2021-05-20",
|
||||
"RPRS_OWNR_NM": "이영희",
|
||||
"PRCS_IMPRTY_RSN_DTLS": "운행차량",
|
||||
"FRST_REG_APLY_RCPT_NO": "4100-202105-002345",
|
||||
"PRVNTC_CNT": "0",
|
||||
"RPRS_OWNR_MBR_SE_CD": "11",
|
||||
"TAXXMPT_APLCN_SE_CD": "미적용",
|
||||
"ERSR_REG_YMD": null,
|
||||
"COLOR_CD": "02",
|
||||
"STRCT_CHG_CNT": "0",
|
||||
"INDVDL_BZMN_YN": "N",
|
||||
"VIN": "KMHZZ00ZZZZ333333",
|
||||
"USG_SE_CD": "2",
|
||||
"XPORT_FLFL_YN_DCLR_YMD": null,
|
||||
"VHRNO": "33다3333",
|
||||
"INSP_VLD_PD_BGNG_YMD": "2021-05-20",
|
||||
"ATMB_NM": "아반떼"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,106 @@
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"VHMNO": "KMHAA00AAAA444444-01",
|
||||
"MTRS_FOM_NM": "D4CB",
|
||||
"PRCS_IMPRTY_RSN_CD": "00",
|
||||
"FRST_TRNSFR_YMD": "2020-08-15",
|
||||
"MRTG_CNT": "1",
|
||||
"COLOR_NM": "파랑",
|
||||
"ERSR_REG_SE_CD": null,
|
||||
"CHCK_VLD_PD_END_YMD": null,
|
||||
"NOPLT_CSDY_YN": "N",
|
||||
"USG_SE_NM": "자가용",
|
||||
"VEAG_END_YMD": null,
|
||||
"record": [
|
||||
{
|
||||
"CHG_YMD": "2020-08-15",
|
||||
"VHMNO": "KMHAA00AAAA444444-01",
|
||||
"SPCABL_MTTR": "성명(상호) : 박민수 850303-3456789\n주소 : 부산광역시 해운대구 우동 1234",
|
||||
"CHG_TASK_SE_NM": "신규등록(신조차)",
|
||||
"FLAG": null,
|
||||
"SZR_RMV_DTL_SN": "1",
|
||||
"MAIN_NO": "1-1",
|
||||
"DTL_SN": "1",
|
||||
"CHG_TASK_SE_CD": "01",
|
||||
"SNO": null,
|
||||
"APLY_RCPT_NO": "2600-202008-003456",
|
||||
"LEDGER_INDIV_NO": "1",
|
||||
"LEDGER_GROUP_NO": "1",
|
||||
"HSHLDR_NM": "박민수",
|
||||
"HSHLDR_IDECNO": "850303-3456789"
|
||||
},
|
||||
{
|
||||
"CHG_YMD": "2021-03-01",
|
||||
"VHMNO": "KMHAA00AAAA444444-01",
|
||||
"SPCABL_MTTR": "채권최고액 : 30,000,000원\n채권자 : 국민은행 부산지점\n채무자 : 박민수",
|
||||
"CHG_TASK_SE_NM": "저당권설정",
|
||||
"FLAG": null,
|
||||
"SZR_RMV_DTL_SN": "2",
|
||||
"MAIN_NO": "(1-2)",
|
||||
"DTL_SN": "2",
|
||||
"CHG_TASK_SE_CD": "31",
|
||||
"SNO": null,
|
||||
"APLY_RCPT_NO": "2600-210301-004567",
|
||||
"LEDGER_INDIV_NO": "1",
|
||||
"LEDGER_GROUP_NO": "1",
|
||||
"HSHLDR_NM": "",
|
||||
"HSHLDR_IDECNO": ""
|
||||
}
|
||||
],
|
||||
"TAXXMPT_TRPR_SE_CD": "0",
|
||||
"SPCABL_MTTR_CNT": "1",
|
||||
"DRVNG_DSTNC": "42300",
|
||||
"NOPLT_CSDY_AVTSMT_YMD": null,
|
||||
"ERSR_REG_SE_NM": null,
|
||||
"OWNR_ADDR": "부산광역시 해운대구 우동 1234",
|
||||
"DRIV_SRGBTRY_IDNTF_NO": null,
|
||||
"YRIDNW": "2020",
|
||||
"SPMNNO_1": "B01",
|
||||
"BSS_USE_PD_YMD": null,
|
||||
"LINK_RSLT_CD": "MSG50000",
|
||||
"SPMNNO_2": "2-00045-0678-0901",
|
||||
"SZR_CNT": "0",
|
||||
"CARMDL_ASORT_CD": "2",
|
||||
"USGSRHLD_ADDR_1": "부산광역시 해운대구 우동 1234",
|
||||
"USGSRHLD_ADDR_DTL_1": "303호",
|
||||
"RPRS_OWNR_TELNO": "01055554444",
|
||||
"NOPLT_SPCFCT_CD": "2",
|
||||
"REG_DTL_CD": "100",
|
||||
"ACQS_AMT": "38,500,000",
|
||||
"FOM_NM": "KA4-D4CB-P4B",
|
||||
"LINK_RSLT_DTL": "정상",
|
||||
"REG_APLY_SE_NM": "신조차",
|
||||
"OCTHT_ERSR_PRVNTC_AVTSMT_YMD": null,
|
||||
"OWNR_ADDR_DTL": "303호",
|
||||
"LEDGER_INDIV_NO": "1",
|
||||
"LEDGER_GROUP_NO": "1",
|
||||
"ISSU_NO": null,
|
||||
"INSP_VLD_PD_END_YMD": "2024-08-14",
|
||||
"CARMDL_ASORT_NM": "승합 대형",
|
||||
"CHCK_VLD_PD_BGNG_YMD": null,
|
||||
"USGSRHLD_DONG_NM": "우동",
|
||||
"REG_DTL_NM": "일반소유용",
|
||||
"NOPLT_SPCFCT_NM": "긴번호판",
|
||||
"FBCTN_YMD": "2020-08-05",
|
||||
"RPRSV_OWNR_IDECNO": "850303-3456789",
|
||||
"FRST_REG_YMD": "2020-08-15",
|
||||
"RPRS_OWNR_NM": "박민수",
|
||||
"PRCS_IMPRTY_RSN_DTLS": "운행차량",
|
||||
"FRST_REG_APLY_RCPT_NO": "2600-202008-003456",
|
||||
"PRVNTC_CNT": "0",
|
||||
"RPRS_OWNR_MBR_SE_CD": "11",
|
||||
"TAXXMPT_APLCN_SE_CD": "미적용",
|
||||
"ERSR_REG_YMD": null,
|
||||
"COLOR_CD": "06",
|
||||
"STRCT_CHG_CNT": "0",
|
||||
"INDVDL_BZMN_YN": "N",
|
||||
"VIN": "KMHAA00AAAA444444",
|
||||
"USG_SE_CD": "2",
|
||||
"XPORT_FLFL_YN_DCLR_YMD": null,
|
||||
"VHRNO": "44라4444",
|
||||
"INSP_VLD_PD_BGNG_YMD": "2020-08-15",
|
||||
"ATMB_NM": "카니발"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,106 @@
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"VHMNO": "KMHBB00BBBB555555-01",
|
||||
"MTRS_FOM_NM": "G3LA",
|
||||
"PRCS_IMPRTY_RSN_CD": "01",
|
||||
"FRST_TRNSFR_YMD": "2019-09-10",
|
||||
"MRTG_CNT": "0",
|
||||
"COLOR_NM": "빨강",
|
||||
"ERSR_REG_SE_CD": "1",
|
||||
"CHCK_VLD_PD_END_YMD": null,
|
||||
"NOPLT_CSDY_YN": "N",
|
||||
"USG_SE_NM": "자가용",
|
||||
"VEAG_END_YMD": null,
|
||||
"record": [
|
||||
{
|
||||
"CHG_YMD": "2019-09-10",
|
||||
"VHMNO": "KMHBB00BBBB555555-01",
|
||||
"SPCABL_MTTR": "성명(상호) : 최영수 750404-4567890\n주소 : 대전광역시 유성구 대학로 99",
|
||||
"CHG_TASK_SE_NM": "신규등록(신조차)",
|
||||
"FLAG": null,
|
||||
"SZR_RMV_DTL_SN": "1",
|
||||
"MAIN_NO": "1-1",
|
||||
"DTL_SN": "1",
|
||||
"CHG_TASK_SE_CD": "01",
|
||||
"SNO": null,
|
||||
"APLY_RCPT_NO": "3000-201909-004567",
|
||||
"LEDGER_INDIV_NO": "1",
|
||||
"LEDGER_GROUP_NO": "1",
|
||||
"HSHLDR_NM": "최영수",
|
||||
"HSHLDR_IDECNO": "750404-4567890"
|
||||
},
|
||||
{
|
||||
"CHG_YMD": "2023-12-01",
|
||||
"VHMNO": "KMHBB00BBBB555555-01",
|
||||
"SPCABL_MTTR": "말소사유 : 폐차\n말소일자 : 2023-12-01",
|
||||
"CHG_TASK_SE_NM": "말소등록",
|
||||
"FLAG": null,
|
||||
"SZR_RMV_DTL_SN": "2",
|
||||
"MAIN_NO": null,
|
||||
"DTL_SN": "2",
|
||||
"CHG_TASK_SE_CD": "11",
|
||||
"SNO": "(1-1)",
|
||||
"APLY_RCPT_NO": "3000-231201-005678",
|
||||
"LEDGER_INDIV_NO": "1",
|
||||
"LEDGER_GROUP_NO": "1",
|
||||
"HSHLDR_NM": "",
|
||||
"HSHLDR_IDECNO": ""
|
||||
}
|
||||
],
|
||||
"TAXXMPT_TRPR_SE_CD": "0",
|
||||
"SPCABL_MTTR_CNT": "0",
|
||||
"DRVNG_DSTNC": "125400",
|
||||
"NOPLT_CSDY_AVTSMT_YMD": null,
|
||||
"ERSR_REG_SE_NM": "폐차",
|
||||
"OWNR_ADDR": "대전광역시 유성구 대학로 99",
|
||||
"DRIV_SRGBTRY_IDNTF_NO": null,
|
||||
"YRIDNW": "2019",
|
||||
"SPMNNO_1": "A01",
|
||||
"BSS_USE_PD_YMD": null,
|
||||
"LINK_RSLT_CD": "MSG50000",
|
||||
"SPMNNO_2": "1-00056-0789-1012",
|
||||
"SZR_CNT": "0",
|
||||
"CARMDL_ASORT_CD": "1",
|
||||
"USGSRHLD_ADDR_1": "대전광역시 유성구 대학로 99",
|
||||
"USGSRHLD_ADDR_DTL_1": "A동 501호",
|
||||
"RPRS_OWNR_TELNO": "01066667777",
|
||||
"NOPLT_SPCFCT_CD": "2",
|
||||
"REG_DTL_CD": "100",
|
||||
"ACQS_AMT": "10,500,000",
|
||||
"FOM_NM": "TA-G3LA-P1A",
|
||||
"LINK_RSLT_DTL": "정상",
|
||||
"REG_APLY_SE_NM": "신조차",
|
||||
"OCTHT_ERSR_PRVNTC_AVTSMT_YMD": null,
|
||||
"OWNR_ADDR_DTL": "A동 501호",
|
||||
"LEDGER_INDIV_NO": "1",
|
||||
"LEDGER_GROUP_NO": "1",
|
||||
"ISSU_NO": null,
|
||||
"INSP_VLD_PD_END_YMD": "2023-09-09",
|
||||
"CARMDL_ASORT_NM": "승용 경형",
|
||||
"CHCK_VLD_PD_BGNG_YMD": null,
|
||||
"USGSRHLD_DONG_NM": "궁동",
|
||||
"REG_DTL_NM": "일반소유용",
|
||||
"NOPLT_SPCFCT_NM": "긴번호판",
|
||||
"FBCTN_YMD": "2019-09-01",
|
||||
"RPRSV_OWNR_IDECNO": "750404-4567890",
|
||||
"FRST_REG_YMD": "2019-09-10",
|
||||
"RPRS_OWNR_NM": "최영수",
|
||||
"PRCS_IMPRTY_RSN_DTLS": "말소차량",
|
||||
"FRST_REG_APLY_RCPT_NO": "3000-201909-004567",
|
||||
"PRVNTC_CNT": "0",
|
||||
"RPRS_OWNR_MBR_SE_CD": "11",
|
||||
"TAXXMPT_APLCN_SE_CD": "미적용",
|
||||
"ERSR_REG_YMD": "2023-12-01",
|
||||
"COLOR_CD": "03",
|
||||
"STRCT_CHG_CNT": "0",
|
||||
"INDVDL_BZMN_YN": "N",
|
||||
"VIN": "KMHBB00BBBB555555",
|
||||
"USG_SE_CD": "2",
|
||||
"XPORT_FLFL_YN_DCLR_YMD": null,
|
||||
"VHRNO": "55마5555",
|
||||
"INSP_VLD_PD_BGNG_YMD": "2019-09-10",
|
||||
"ATMB_NM": "모닝"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,89 @@
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"VHMNO": "KMHCC00CCCC666666-01",
|
||||
"MTRS_FOM_NM": "D4HA",
|
||||
"PRCS_IMPRTY_RSN_CD": "00",
|
||||
"FRST_TRNSFR_YMD": "2024-01-01",
|
||||
"MRTG_CNT": "0",
|
||||
"COLOR_NM": "회색",
|
||||
"ERSR_REG_SE_CD": null,
|
||||
"CHCK_VLD_PD_END_YMD": null,
|
||||
"NOPLT_CSDY_YN": "N",
|
||||
"USG_SE_NM": "자가용",
|
||||
"VEAG_END_YMD": null,
|
||||
"record": [
|
||||
{
|
||||
"CHG_YMD": "2024-01-01",
|
||||
"VHMNO": "KMHCC00CCCC666666-01",
|
||||
"SPCABL_MTTR": "성명(상호) : 정수진 920505-5678901\n주소 : 인천광역시 남동구 구월동 1234-5",
|
||||
"CHG_TASK_SE_NM": "신규등록(신조차)",
|
||||
"FLAG": null,
|
||||
"SZR_RMV_DTL_SN": "1",
|
||||
"MAIN_NO": "1-1",
|
||||
"DTL_SN": "1",
|
||||
"CHG_TASK_SE_CD": "01",
|
||||
"SNO": null,
|
||||
"APLY_RCPT_NO": "2800-202401-005678",
|
||||
"LEDGER_INDIV_NO": "1",
|
||||
"LEDGER_GROUP_NO": "1",
|
||||
"HSHLDR_NM": "정수진",
|
||||
"HSHLDR_IDECNO": "920505-5678901"
|
||||
}
|
||||
],
|
||||
"TAXXMPT_TRPR_SE_CD": "0",
|
||||
"SPCABL_MTTR_CNT": "0",
|
||||
"DRVNG_DSTNC": "8500",
|
||||
"NOPLT_CSDY_AVTSMT_YMD": null,
|
||||
"ERSR_REG_SE_NM": null,
|
||||
"OWNR_ADDR": "인천광역시 남동구 구월동 1234-5",
|
||||
"DRIV_SRGBTRY_IDNTF_NO": null,
|
||||
"YRIDNW": "2024",
|
||||
"SPMNNO_1": "A01",
|
||||
"BSS_USE_PD_YMD": null,
|
||||
"LINK_RSLT_CD": "MSG50000",
|
||||
"SPMNNO_2": "1-00067-0890-1123",
|
||||
"SZR_CNT": "0",
|
||||
"CARMDL_ASORT_CD": "1",
|
||||
"USGSRHLD_ADDR_1": "인천광역시 남동구 구월동 1234-5",
|
||||
"USGSRHLD_ADDR_DTL_1": "B동 1502호",
|
||||
"RPRS_OWNR_TELNO": "01077778888",
|
||||
"NOPLT_SPCFCT_CD": "2",
|
||||
"REG_DTL_CD": "100",
|
||||
"ACQS_AMT": "32,800,000",
|
||||
"FOM_NM": "NQ5-D4HA-P3C",
|
||||
"LINK_RSLT_DTL": "정상",
|
||||
"REG_APLY_SE_NM": "신조차",
|
||||
"OCTHT_ERSR_PRVNTC_AVTSMT_YMD": null,
|
||||
"OWNR_ADDR_DTL": "B동 1502호",
|
||||
"LEDGER_INDIV_NO": "1",
|
||||
"LEDGER_GROUP_NO": "1",
|
||||
"ISSU_NO": null,
|
||||
"INSP_VLD_PD_END_YMD": "2028-01-01",
|
||||
"CARMDL_ASORT_NM": "승용 중형",
|
||||
"CHCK_VLD_PD_BGNG_YMD": null,
|
||||
"USGSRHLD_DONG_NM": "구월동",
|
||||
"REG_DTL_NM": "일반소유용",
|
||||
"NOPLT_SPCFCT_NM": "긴번호판",
|
||||
"FBCTN_YMD": "2023-12-20",
|
||||
"RPRSV_OWNR_IDECNO": "920505-5678901",
|
||||
"FRST_REG_YMD": "2024-01-01",
|
||||
"RPRS_OWNR_NM": "정수진",
|
||||
"PRCS_IMPRTY_RSN_DTLS": "운행차량",
|
||||
"FRST_REG_APLY_RCPT_NO": "2800-202401-005678",
|
||||
"PRVNTC_CNT": "0",
|
||||
"RPRS_OWNR_MBR_SE_CD": "11",
|
||||
"TAXXMPT_APLCN_SE_CD": "미적용",
|
||||
"ERSR_REG_YMD": null,
|
||||
"COLOR_CD": "07",
|
||||
"STRCT_CHG_CNT": "0",
|
||||
"INDVDL_BZMN_YN": "N",
|
||||
"VIN": "KMHCC00CCCC666666",
|
||||
"USG_SE_CD": "2",
|
||||
"XPORT_FLFL_YN_DCLR_YMD": null,
|
||||
"VHRNO": "66바6666",
|
||||
"INSP_VLD_PD_BGNG_YMD": "2024-01-01",
|
||||
"ATMB_NM": "스포티지"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,89 @@
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"VHMNO": "KMHDD00DDDD777777-01",
|
||||
"MTRS_FOM_NM": "T3LA",
|
||||
"PRCS_IMPRTY_RSN_CD": "00",
|
||||
"FRST_TRNSFR_YMD": "2025-02-01",
|
||||
"MRTG_CNT": "0",
|
||||
"COLOR_NM": "진주",
|
||||
"ERSR_REG_SE_CD": null,
|
||||
"CHCK_VLD_PD_END_YMD": null,
|
||||
"NOPLT_CSDY_YN": "N",
|
||||
"USG_SE_NM": "영업용",
|
||||
"VEAG_END_YMD": null,
|
||||
"record": [
|
||||
{
|
||||
"CHG_YMD": "2025-02-01",
|
||||
"VHMNO": "KMHDD00DDDD777777-01",
|
||||
"SPCABL_MTTR": "성명(상호) : 전시용 220-81-23456\n주소 : 경기도 성남시 분당구 판교역로 231",
|
||||
"CHG_TASK_SE_NM": "신규등록(신조차)",
|
||||
"FLAG": null,
|
||||
"SZR_RMV_DTL_SN": "1",
|
||||
"MAIN_NO": "1-1",
|
||||
"DTL_SN": "1",
|
||||
"CHG_TASK_SE_CD": "01",
|
||||
"SNO": null,
|
||||
"APLY_RCPT_NO": "4100-202502-006789",
|
||||
"LEDGER_INDIV_NO": "1",
|
||||
"LEDGER_GROUP_NO": "1",
|
||||
"HSHLDR_NM": "전시용",
|
||||
"HSHLDR_IDECNO": "220-81-23456"
|
||||
}
|
||||
],
|
||||
"TAXXMPT_TRPR_SE_CD": "0",
|
||||
"SPCABL_MTTR_CNT": "0",
|
||||
"DRVNG_DSTNC": "150",
|
||||
"NOPLT_CSDY_AVTSMT_YMD": null,
|
||||
"ERSR_REG_SE_NM": null,
|
||||
"OWNR_ADDR": "경기도 성남시 분당구 판교역로 231",
|
||||
"DRIV_SRGBTRY_IDNTF_NO": null,
|
||||
"YRIDNW": "2025",
|
||||
"SPMNNO_1": "A01",
|
||||
"BSS_USE_PD_YMD": null,
|
||||
"LINK_RSLT_CD": "MSG50000",
|
||||
"SPMNNO_2": "1-00078-0901-1234",
|
||||
"SZR_CNT": "0",
|
||||
"CARMDL_ASORT_CD": "1",
|
||||
"USGSRHLD_ADDR_1": "경기도 성남시 분당구 판교역로 231",
|
||||
"USGSRHLD_ADDR_DTL_1": "판교테크원타워 10층",
|
||||
"RPRS_OWNR_TELNO": "0315551234",
|
||||
"NOPLT_SPCFCT_CD": "2",
|
||||
"REG_DTL_CD": "100",
|
||||
"ACQS_AMT": "68,900,000",
|
||||
"FOM_NM": "RG3-T3LA-P5D",
|
||||
"LINK_RSLT_DTL": "정상",
|
||||
"REG_APLY_SE_NM": "신조차",
|
||||
"OCTHT_ERSR_PRVNTC_AVTSMT_YMD": null,
|
||||
"OWNR_ADDR_DTL": "판교테크원타워 10층",
|
||||
"LEDGER_INDIV_NO": "1",
|
||||
"LEDGER_GROUP_NO": "1",
|
||||
"ISSU_NO": null,
|
||||
"INSP_VLD_PD_END_YMD": "2029-02-01",
|
||||
"CARMDL_ASORT_NM": "승용 대형",
|
||||
"CHCK_VLD_PD_BGNG_YMD": null,
|
||||
"USGSRHLD_DONG_NM": "삼평동",
|
||||
"REG_DTL_NM": "일반소유용",
|
||||
"NOPLT_SPCFCT_NM": "긴번호판",
|
||||
"FBCTN_YMD": "2025-01-20",
|
||||
"RPRSV_OWNR_IDECNO": "220-81-23456",
|
||||
"FRST_REG_YMD": "2025-02-01",
|
||||
"RPRS_OWNR_NM": "전시용",
|
||||
"PRCS_IMPRTY_RSN_DTLS": "운행차량",
|
||||
"FRST_REG_APLY_RCPT_NO": "4100-202502-006789",
|
||||
"PRVNTC_CNT": "0",
|
||||
"RPRS_OWNR_MBR_SE_CD": "13",
|
||||
"TAXXMPT_APLCN_SE_CD": "미적용",
|
||||
"ERSR_REG_YMD": null,
|
||||
"COLOR_CD": "08",
|
||||
"STRCT_CHG_CNT": "0",
|
||||
"INDVDL_BZMN_YN": "N",
|
||||
"VIN": "KMHDD00DDDD777777",
|
||||
"USG_SE_CD": "1",
|
||||
"XPORT_FLFL_YN_DCLR_YMD": null,
|
||||
"VHRNO": "77사7777",
|
||||
"INSP_VLD_PD_BGNG_YMD": "2025-02-01",
|
||||
"ATMB_NM": "제네시스 G80"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,106 @@
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"VHMNO": "KMHEE00EEEE888888-01",
|
||||
"MTRS_FOM_NM": "G4FJ",
|
||||
"PRCS_IMPRTY_RSN_CD": "00",
|
||||
"FRST_TRNSFR_YMD": "2023-07-15",
|
||||
"MRTG_CNT": "0",
|
||||
"COLOR_NM": "청색",
|
||||
"ERSR_REG_SE_CD": null,
|
||||
"CHCK_VLD_PD_END_YMD": null,
|
||||
"NOPLT_CSDY_YN": "N",
|
||||
"USG_SE_NM": "자가용",
|
||||
"VEAG_END_YMD": null,
|
||||
"record": [
|
||||
{
|
||||
"CHG_YMD": "2023-07-15",
|
||||
"VHMNO": "KMHEE00EEEE888888-01",
|
||||
"SPCABL_MTTR": "성명(상호) : 강원철 880606-6789012\n주소 : 강원특별자치도 춘천시 중앙로 1",
|
||||
"CHG_TASK_SE_NM": "신규등록(신조차)",
|
||||
"FLAG": null,
|
||||
"SZR_RMV_DTL_SN": "1",
|
||||
"MAIN_NO": "1-1",
|
||||
"DTL_SN": "1",
|
||||
"CHG_TASK_SE_CD": "01",
|
||||
"SNO": null,
|
||||
"APLY_RCPT_NO": "5100-202307-007890",
|
||||
"LEDGER_INDIV_NO": "1",
|
||||
"LEDGER_GROUP_NO": "1",
|
||||
"HSHLDR_NM": "강원철",
|
||||
"HSHLDR_IDECNO": "880606-6789012"
|
||||
},
|
||||
{
|
||||
"CHG_YMD": "2024-11-01",
|
||||
"VHMNO": "KMHEE00EEEE888888-01",
|
||||
"SPCABL_MTTR": "예방조치기관 : 춘천시청\n예방조치사유 : 과태료 미납\n예방조치일자 : 2024-11-01",
|
||||
"CHG_TASK_SE_NM": "예방조치",
|
||||
"FLAG": null,
|
||||
"SZR_RMV_DTL_SN": "2",
|
||||
"MAIN_NO": "(1-2)",
|
||||
"DTL_SN": "2",
|
||||
"CHG_TASK_SE_CD": "51",
|
||||
"SNO": null,
|
||||
"APLY_RCPT_NO": "5100-241101-008901",
|
||||
"LEDGER_INDIV_NO": "1",
|
||||
"LEDGER_GROUP_NO": "1",
|
||||
"HSHLDR_NM": "",
|
||||
"HSHLDR_IDECNO": ""
|
||||
}
|
||||
],
|
||||
"TAXXMPT_TRPR_SE_CD": "0",
|
||||
"SPCABL_MTTR_CNT": "1",
|
||||
"DRVNG_DSTNC": "35200",
|
||||
"NOPLT_CSDY_AVTSMT_YMD": null,
|
||||
"ERSR_REG_SE_NM": null,
|
||||
"OWNR_ADDR": "강원특별자치도 춘천시 중앙로 1",
|
||||
"DRIV_SRGBTRY_IDNTF_NO": null,
|
||||
"YRIDNW": "2023",
|
||||
"SPMNNO_1": "A01",
|
||||
"BSS_USE_PD_YMD": null,
|
||||
"LINK_RSLT_CD": "MSG50000",
|
||||
"SPMNNO_2": "1-00089-1012-1345",
|
||||
"SZR_CNT": "0",
|
||||
"CARMDL_ASORT_CD": "1",
|
||||
"USGSRHLD_ADDR_1": "강원특별자치도 춘천시 중앙로 1",
|
||||
"USGSRHLD_ADDR_DTL_1": "춘천타워 602호",
|
||||
"RPRS_OWNR_TELNO": "01088889999",
|
||||
"NOPLT_SPCFCT_CD": "2",
|
||||
"REG_DTL_CD": "100",
|
||||
"ACQS_AMT": "27,300,000",
|
||||
"FOM_NM": "TL-G4FJ-P2B",
|
||||
"LINK_RSLT_DTL": "정상",
|
||||
"REG_APLY_SE_NM": "신조차",
|
||||
"OCTHT_ERSR_PRVNTC_AVTSMT_YMD": null,
|
||||
"OWNR_ADDR_DTL": "춘천타워 602호",
|
||||
"LEDGER_INDIV_NO": "1",
|
||||
"LEDGER_GROUP_NO": "1",
|
||||
"ISSU_NO": null,
|
||||
"INSP_VLD_PD_END_YMD": "2027-07-14",
|
||||
"CARMDL_ASORT_NM": "승용 소형",
|
||||
"CHCK_VLD_PD_BGNG_YMD": null,
|
||||
"USGSRHLD_DONG_NM": "조양동",
|
||||
"REG_DTL_NM": "일반소유용",
|
||||
"NOPLT_SPCFCT_NM": "긴번호판",
|
||||
"FBCTN_YMD": "2023-07-05",
|
||||
"RPRSV_OWNR_IDECNO": "880606-6789012",
|
||||
"FRST_REG_YMD": "2023-07-15",
|
||||
"RPRS_OWNR_NM": "강원철",
|
||||
"PRCS_IMPRTY_RSN_DTLS": "운행차량",
|
||||
"FRST_REG_APLY_RCPT_NO": "5100-202307-007890",
|
||||
"PRVNTC_CNT": "1",
|
||||
"RPRS_OWNR_MBR_SE_CD": "11",
|
||||
"TAXXMPT_APLCN_SE_CD": "미적용",
|
||||
"ERSR_REG_YMD": null,
|
||||
"COLOR_CD": "06",
|
||||
"STRCT_CHG_CNT": "0",
|
||||
"INDVDL_BZMN_YN": "N",
|
||||
"VIN": "KMHEE00EEEE888888",
|
||||
"USG_SE_CD": "2",
|
||||
"XPORT_FLFL_YN_DCLR_YMD": null,
|
||||
"VHRNO": "88아8888",
|
||||
"INSP_VLD_PD_BGNG_YMD": "2023-07-15",
|
||||
"ATMB_NM": "투싼"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,123 @@
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"VHMNO": "KMHFF00FFFF999999-01",
|
||||
"MTRS_FOM_NM": "T4LA",
|
||||
"PRCS_IMPRTY_RSN_CD": "00",
|
||||
"FRST_TRNSFR_YMD": "2024-09-01",
|
||||
"MRTG_CNT": "1",
|
||||
"COLOR_NM": "백색",
|
||||
"ERSR_REG_SE_CD": null,
|
||||
"CHCK_VLD_PD_END_YMD": null,
|
||||
"NOPLT_CSDY_YN": "N",
|
||||
"USG_SE_NM": "영업용",
|
||||
"VEAG_END_YMD": null,
|
||||
"record": [
|
||||
{
|
||||
"CHG_YMD": "2024-09-01",
|
||||
"VHMNO": "KMHFF00FFFF999999-01",
|
||||
"SPCABL_MTTR": "성명(상호) : 상품용차량 320-90-87890\n주소 : 대구광역시 중구 동성로 123",
|
||||
"CHG_TASK_SE_NM": "신규등록(신조차)",
|
||||
"FLAG": null,
|
||||
"SZR_RMV_DTL_SN": "1",
|
||||
"MAIN_NO": "1-1",
|
||||
"DTL_SN": "1",
|
||||
"CHG_TASK_SE_CD": "01",
|
||||
"SNO": null,
|
||||
"APLY_RCPT_NO": "2700-202409-009012",
|
||||
"LEDGER_INDIV_NO": "1",
|
||||
"LEDGER_GROUP_NO": "1",
|
||||
"HSHLDR_NM": "상품용차량",
|
||||
"HSHLDR_IDECNO": "320-90-87890"
|
||||
},
|
||||
{
|
||||
"CHG_YMD": "2024-10-15",
|
||||
"VHMNO": "KMHFF00FFFF999999-01",
|
||||
"SPCABL_MTTR": "채권최고액 : 50,000,000원\n채권자 : 우리은행 대구지점\n채무자 : 상품용차량",
|
||||
"CHG_TASK_SE_NM": "저당권설정",
|
||||
"FLAG": null,
|
||||
"SZR_RMV_DTL_SN": "2",
|
||||
"MAIN_NO": "(1-2)",
|
||||
"DTL_SN": "2",
|
||||
"CHG_TASK_SE_CD": "31",
|
||||
"SNO": null,
|
||||
"APLY_RCPT_NO": "2700-241015-010123",
|
||||
"LEDGER_INDIV_NO": "1",
|
||||
"LEDGER_GROUP_NO": "1",
|
||||
"HSHLDR_NM": "",
|
||||
"HSHLDR_IDECNO": ""
|
||||
},
|
||||
{
|
||||
"CHG_YMD": "2024-12-01",
|
||||
"VHMNO": "KMHFF00FFFF999999-01",
|
||||
"SPCABL_MTTR": "촉탁기관 : 대구시청 구분:압류\r압류관리번호:2700-20241201-000789\n\r압류내역 : 대구시청 주정차위반과태료[2024-01-300002] 교통관리과-11111 \n\r촉탁일자 : 2024-12-01",
|
||||
"CHG_TASK_SE_NM": "압류등록(압류)",
|
||||
"FLAG": null,
|
||||
"SZR_RMV_DTL_SN": "3",
|
||||
"MAIN_NO": "(1-3)",
|
||||
"DTL_SN": "3",
|
||||
"CHG_TASK_SE_CD": "41",
|
||||
"SNO": null,
|
||||
"APLY_RCPT_NO": "2700-241201-000789",
|
||||
"LEDGER_INDIV_NO": "1",
|
||||
"LEDGER_GROUP_NO": "1",
|
||||
"HSHLDR_NM": "",
|
||||
"HSHLDR_IDECNO": ""
|
||||
}
|
||||
],
|
||||
"TAXXMPT_TRPR_SE_CD": "0",
|
||||
"SPCABL_MTTR_CNT": "2",
|
||||
"DRVNG_DSTNC": "12300",
|
||||
"NOPLT_CSDY_AVTSMT_YMD": null,
|
||||
"ERSR_REG_SE_NM": null,
|
||||
"OWNR_ADDR": "대구광역시 중구 동성로 123",
|
||||
"DRIV_SRGBTRY_IDNTF_NO": null,
|
||||
"YRIDNW": "2024",
|
||||
"SPMNNO_1": "A01",
|
||||
"BSS_USE_PD_YMD": null,
|
||||
"LINK_RSLT_CD": "MSG50000",
|
||||
"SPMNNO_2": "1-00090-1123-1456",
|
||||
"SZR_CNT": "1",
|
||||
"CARMDL_ASORT_CD": "1",
|
||||
"USGSRHLD_ADDR_1": "대구광역시 중구 동성로 123",
|
||||
"USGSRHLD_ADDR_DTL_1": "대구빌딩 8층",
|
||||
"RPRS_OWNR_TELNO": "0539990000",
|
||||
"NOPLT_SPCFCT_CD": "2",
|
||||
"REG_DTL_CD": "100",
|
||||
"ACQS_AMT": "52,800,000",
|
||||
"FOM_NM": "LX2-T4LA-P4E",
|
||||
"LINK_RSLT_DTL": "정상",
|
||||
"REG_APLY_SE_NM": "신조차",
|
||||
"OCTHT_ERSR_PRVNTC_AVTSMT_YMD": null,
|
||||
"OWNR_ADDR_DTL": "대구빌딩 8층",
|
||||
"LEDGER_INDIV_NO": "1",
|
||||
"LEDGER_GROUP_NO": "1",
|
||||
"ISSU_NO": null,
|
||||
"INSP_VLD_PD_END_YMD": "2028-09-01",
|
||||
"CARMDL_ASORT_NM": "승용 대형",
|
||||
"CHCK_VLD_PD_BGNG_YMD": null,
|
||||
"USGSRHLD_DONG_NM": "동인동",
|
||||
"REG_DTL_NM": "일반소유용",
|
||||
"NOPLT_SPCFCT_NM": "긴번호판",
|
||||
"FBCTN_YMD": "2024-08-20",
|
||||
"RPRSV_OWNR_IDECNO": "320-90-87890",
|
||||
"FRST_REG_YMD": "2024-09-01",
|
||||
"RPRS_OWNR_NM": "상품용차량",
|
||||
"PRCS_IMPRTY_RSN_DTLS": "운행차량",
|
||||
"FRST_REG_APLY_RCPT_NO": "2700-202409-009012",
|
||||
"PRVNTC_CNT": "0",
|
||||
"RPRS_OWNR_MBR_SE_CD": "13",
|
||||
"TAXXMPT_APLCN_SE_CD": "미적용",
|
||||
"ERSR_REG_YMD": null,
|
||||
"COLOR_CD": "02",
|
||||
"STRCT_CHG_CNT": "0",
|
||||
"INDVDL_BZMN_YN": "N",
|
||||
"VIN": "KMHFF00FFFF999999",
|
||||
"USG_SE_CD": "1",
|
||||
"XPORT_FLFL_YN_DCLR_YMD": null,
|
||||
"VHRNO": "99자9999",
|
||||
"INSP_VLD_PD_BGNG_YMD": "2024-09-01",
|
||||
"ATMB_NM": "팰리세이드"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,106 @@
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"VHMNO": "KMHGG00GGGG000000-01",
|
||||
"MTRS_FOM_NM": "G4NC",
|
||||
"PRCS_IMPRTY_RSN_CD": "00",
|
||||
"FRST_TRNSFR_YMD": "2022-10-10",
|
||||
"MRTG_CNT": "0",
|
||||
"COLOR_NM": "검정",
|
||||
"ERSR_REG_SE_CD": null,
|
||||
"CHCK_VLD_PD_END_YMD": null,
|
||||
"NOPLT_CSDY_YN": "N",
|
||||
"USG_SE_NM": "자가용",
|
||||
"VEAG_END_YMD": null,
|
||||
"record": [
|
||||
{
|
||||
"CHG_YMD": "2022-10-10",
|
||||
"VHMNO": "KMHGG00GGGG000000-01",
|
||||
"SPCABL_MTTR": "성명(상호) : 윤하나 950707-7890234\n주소 : 충청남도 천안시 동남구 만남로 1",
|
||||
"CHG_TASK_SE_NM": "신규등록(신조차)",
|
||||
"FLAG": null,
|
||||
"SZR_RMV_DTL_SN": "1",
|
||||
"MAIN_NO": "1-1",
|
||||
"DTL_SN": "1",
|
||||
"CHG_TASK_SE_CD": "01",
|
||||
"SNO": null,
|
||||
"APLY_RCPT_NO": "4300-202210-011234",
|
||||
"LEDGER_INDIV_NO": "1",
|
||||
"LEDGER_GROUP_NO": "1",
|
||||
"HSHLDR_NM": "윤하나",
|
||||
"HSHLDR_IDECNO": "950707-7890234"
|
||||
},
|
||||
{
|
||||
"CHG_YMD": "2023-05-20",
|
||||
"VHMNO": "KMHGG00GGGG000000-01",
|
||||
"SPCABL_MTTR": "양수인 : 김영호 870808-8901345\n주소 : 충청남도 천안시 서북구 성정로 50",
|
||||
"CHG_TASK_SE_NM": "이전등록",
|
||||
"FLAG": null,
|
||||
"SZR_RMV_DTL_SN": "2",
|
||||
"MAIN_NO": "1-2",
|
||||
"DTL_SN": "2",
|
||||
"CHG_TASK_SE_CD": "21",
|
||||
"SNO": null,
|
||||
"APLY_RCPT_NO": "4300-230520-012345",
|
||||
"LEDGER_INDIV_NO": "1",
|
||||
"LEDGER_GROUP_NO": "1",
|
||||
"HSHLDR_NM": "김영호",
|
||||
"HSHLDR_IDECNO": "870808-8901345"
|
||||
}
|
||||
],
|
||||
"TAXXMPT_TRPR_SE_CD": "0",
|
||||
"SPCABL_MTTR_CNT": "0",
|
||||
"DRVNG_DSTNC": "45800",
|
||||
"NOPLT_CSDY_AVTSMT_YMD": null,
|
||||
"ERSR_REG_SE_NM": null,
|
||||
"OWNR_ADDR": "충청남도 천안시 서북구 성정로 50",
|
||||
"DRIV_SRGBTRY_IDNTF_NO": null,
|
||||
"YRIDNW": "2022",
|
||||
"SPMNNO_1": "A01",
|
||||
"BSS_USE_PD_YMD": null,
|
||||
"LINK_RSLT_CD": "MSG50000",
|
||||
"SPMNNO_2": "1-00001-1234-1567",
|
||||
"SZR_CNT": "0",
|
||||
"CARMDL_ASORT_CD": "1",
|
||||
"USGSRHLD_ADDR_1": "충청남도 천안시 서북구 성정로 50",
|
||||
"USGSRHLD_ADDR_DTL_1": "성정아파트 101동 704호",
|
||||
"RPRS_OWNR_TELNO": "01000001111",
|
||||
"NOPLT_SPCFCT_CD": "2",
|
||||
"REG_DTL_CD": "100",
|
||||
"ACQS_AMT": "29,500,000",
|
||||
"FOM_NM": "DL3-G4NC-P2C",
|
||||
"LINK_RSLT_DTL": "정상",
|
||||
"REG_APLY_SE_NM": "신조차",
|
||||
"OCTHT_ERSR_PRVNTC_AVTSMT_YMD": null,
|
||||
"OWNR_ADDR_DTL": "성정아파트 101동 704호",
|
||||
"LEDGER_INDIV_NO": "1",
|
||||
"LEDGER_GROUP_NO": "1",
|
||||
"ISSU_NO": null,
|
||||
"INSP_VLD_PD_END_YMD": "2026-10-09",
|
||||
"CARMDL_ASORT_NM": "승용 중형",
|
||||
"CHCK_VLD_PD_BGNG_YMD": null,
|
||||
"USGSRHLD_DONG_NM": "성정동",
|
||||
"REG_DTL_NM": "일반소유용",
|
||||
"NOPLT_SPCFCT_NM": "긴번호판",
|
||||
"FBCTN_YMD": "2022-10-01",
|
||||
"RPRSV_OWNR_IDECNO": "870808-8901345",
|
||||
"FRST_REG_YMD": "2022-10-10",
|
||||
"RPRS_OWNR_NM": "김영호",
|
||||
"PRCS_IMPRTY_RSN_DTLS": "운행차량",
|
||||
"FRST_REG_APLY_RCPT_NO": "4300-202210-011234",
|
||||
"PRVNTC_CNT": "0",
|
||||
"RPRS_OWNR_MBR_SE_CD": "11",
|
||||
"TAXXMPT_APLCN_SE_CD": "미적용",
|
||||
"ERSR_REG_YMD": null,
|
||||
"COLOR_CD": "01",
|
||||
"STRCT_CHG_CNT": "0",
|
||||
"INDVDL_BZMN_YN": "N",
|
||||
"VIN": "KMHGG00GGGG000000",
|
||||
"USG_SE_CD": "2",
|
||||
"XPORT_FLFL_YN_DCLR_YMD": null,
|
||||
"VHRNO": "00차0000",
|
||||
"INSP_VLD_PD_BGNG_YMD": "2022-10-10",
|
||||
"ATMB_NM": "K5"
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
Reference in New Issue