# VIPS - Vehicle Information Processing System

## 프로젝트 개요

VIPS는 자동차 정보 조회 시스템으로, 정부 VMIS(Vehicle Management Information System)와 연계하여 차량 기본정보 및 등록원부 정보를 조회하는 Spring Boot 기반 웹 애플리케이션입니다.

### 주요 기능

- **차량 기본정보 조회**: 차량번호로 기본사항 조회 (차대번호, 소유자 정보, 차종 등)
- **차량 등록원부 조회**: 자동차등록원부(갑) 정보 조회
- **통합 조회**: 기본정보 + 등록원부 정보를 한 번에 조회
- **GPKI 인증**: 정부 시스템과의 안전한 통신을 위한 GPKI 암호화 지원
- **이력 관리**: 모든 조회 요청/응답 이력을 데이터베이스에 저장

---

## 아키텍처

### 동작 모드

VIPS는 두 가지 모드로 동작하며, 설정을 통해 전환할 수 있습니다:

#### 1. **Internal Mode** (내부 모듈 직접 호출)
- VMIS 모듈을 애플리케이션 내부에 직접 통합하여 정부 API 호출
- 네트워크 오버헤드 없이 직접 호출로 성능 향상
- 단일 서버 환경에 적합

```
Client → VehicleInterfaceController
       → InternalVehicleInfoServiceImpl
       → VmisCarBassMatterInqireService / VmisCarLedgerFrmbkService
       → GovernmentApiClient
       → 정부 VMIS API
```

#### 2. **External Mode** (외부 REST API 호출)
- 별도로 실행 중인 VIPS 서버의 REST API를 호출
- 서비스 분리를 통한 확장성 및 유지보수성 향상
- MSA(Microservices Architecture) 환경에 적합

```
Client → VehicleInterfaceController
       → ExternalVehicleInfoServiceImpl
       → ExternalVehicleApiServiceImpl (RestTemplate)
       → VIPS 서버 (별도 실행)
       → 정부 VMIS API
```

### 모드 설정 방법

`application.yml` 또는 환경별 설정 파일에서 설정:

```yaml
vmis:
  integration:
    mode: internal  # 또는 external
```

### 모드 분기 메커니즘

VIPS는 Spring Boot의 `@ConditionalOnProperty` 어노테이션을 활용하여 설정값에 따라 자동으로 적절한 구현체를 선택합니다.

#### 핵심 컴포넌트

**1. VehicleInfoService (인터페이스)**
```java
public interface VehicleInfoService {
    VehicleApiResponseVO getVehicleInfo(String vehicleNumber);
    VehicleApiResponseVO getVehicleInfo(BasicRequest basicRequest);
    BasicResponse getBasicInfo(BasicRequest request);
    LedgerResponse getLedgerInfo(LedgerRequest request);
    // ...
}
```

**2. InternalVehicleInfoServiceImpl (Internal 모드 구현체)**
```java
@Service
@ConditionalOnProperty(name = "vmis.integration.mode", havingValue = "internal")
public class InternalVehicleInfoServiceImpl implements VehicleInfoService {
    // 내부 VMIS 모듈 직접 호출 로직
}
```
- `vmis.integration.mode=internal` 일 때만 Bean으로 등록
- 내부 VMIS 서비스를 직접 호출
- GPKI 암호화 처리
- 데이터베이스에 이력 저장

**3. ExternalVehicleInfoServiceImpl (External 모드 구현체)**
```java
@Service
@ConditionalOnProperty(name = "vmis.integration.mode", havingValue = "external", matchIfMissing = true)
public class ExternalVehicleInfoServiceImpl implements VehicleInfoService {
    // 외부 REST API 호출 로직
}
```
- `vmis.integration.mode=external` 일 때만 Bean으로 등록
- `matchIfMissing = true`: 설정값이 없으면 External 모드가 기본값
- ExternalVehicleApiService를 통해 외부 서버 REST API 호출

**4. VmisIntegrationConfig (통합 설정 및 모니터링)**
```java
@Configuration
public class VmisIntegrationConfig {

    @Bean
    public CommandLineRunner vmisIntegrationModeLogger(VehicleInfoService vehicleInfoService) {
        // 애플리케이션 시작 시 현재 모드 및 설정 정보 출력
    }
}
```

#### 동작 원리

1. **애플리케이션 시작 시**
   - Spring Boot가 `vmis.integration.mode` 설정값 확인
   - 조건에 맞는 VehicleInfoService 구현체를 Bean으로 등록
   - 다른 구현체는 Bean으로 등록되지 않음

2. **컨트롤러에서 서비스 호출 시**
   ```java
   @RestController
   public class VehicleInterfaceController {

       // Spring이 자동으로 적절한 구현체 주입
       private final VehicleInfoService vehicleInfoService;

       @PostMapping("/info.ajax")
       public ResponseEntity<Envelope<VehicleApiResponseVO>> info(...) {
           // 설정에 따라 Internal 또는 External 구현체가 실행됨
           VehicleApiResponseVO resp = vehicleInfoService.getVehicleInfo(request);
           return ResponseEntity.ok(new Envelope<>(resp));
       }
   }
   ```

3. **모드 확인 로그**

   애플리케이션 시작 시 콘솔에 다음과 같은 로그가 출력됩니다:

   **Internal 모드:**
   ```
   ========================================
   VMIS Integration Mode: internal
   Active Implementation: InternalVehicleInfoServiceImpl
   ========================================
   [Internal Mode] 내부 VMIS 모듈을 직접 사용합니다
     - 정부 API 호스트: https://gov-api-url
     - 기본사항 조회 경로: /api/basic
     - 등록원부 조회 경로: /api/ledger
     - GPKI 암호화: Y
     - 연결 타임아웃: 30000ms
     - 읽기 타임아웃: 60000ms
   ```

   **External 모드:**
   ```
   ========================================
   VMIS Integration Mode: external
   Active Implementation: ExternalVehicleInfoServiceImpl
   ========================================
   [External Mode] 외부 REST API를 사용합니다
     - 외부 API Base URL: http://localhost:8081/api/v1/vehicles
     - 연결 타임아웃: 10000ms
     - 읽기 타임아웃: 30000ms
   ```

#### 모드 전환 시 주의사항

**Internal → External 전환 시:**
1. 외부 VIPS 서버가 실행 중이어야 함
2. `vmis.external.api.url.base` 설정 필요
3. 네트워크 방화벽 설정 확인
4. GPKI 암호화는 외부 서버에서 처리됨

**External → Internal 전환 시:**
1. GPKI 인증서 설정 필요
2. 정부 API 직접 호출을 위한 네트워크 설정
3. `vmis.gov.host`, `vmis.gov.services` 설정 필요
4. 데이터베이스 이력 저장용 테이블 필요

#### 관련 소스 파일

**모드 분기 관련 핵심 파일:**
```
src/main/java/go/kr/project/api/
├── config/
│   └── VmisIntegrationConfig.java              # 통합 모드 설정 및 로깅
│
├── service/
│   └── VehicleInfoService.java                  # 공통 인터페이스
│
├── internal/service/impl/
│   └── InternalVehicleInfoServiceImpl.java      # Internal 모드 구현체
│                                                 # @ConditionalOnProperty(havingValue = "internal")
│
└── external/service/impl/
    └── ExternalVehicleInfoServiceImpl.java      # External 모드 구현체
                                                  # @ConditionalOnProperty(havingValue = "external")
```

**설정 파일:**
```
src/main/resources/
├── application.yml                               # 공통 설정
├── application-local.yml                         # 로컬 환경 (Internal 모드 권장)
├── application-dev.yml                           # 개발 환경
└── application-prd.yml                           # 운영 환경
```

---

## 프로젝트 구조

```
src/main/java/go/kr/project/api/
├── config/                          # 설정 클래스
│   ├── ApiConstant.java            # API 상수 정의 (결과 코드 등)
│   ├── VmisIntegrationConfig.java  # VMIS 통합 모드 설정
│   ├── RestTemplateConfig.java     # RestTemplate 빈 설정
│   └── properties/
│       └── VmisProperties.java     # VMIS 설정 프로퍼티
│
├── controller/                      # REST 컨트롤러
│   └── VehicleInterfaceController.java  # 차량 정보 조회 API 엔드포인트
│
├── service/                         # 서비스 인터페이스
│   └── VehicleInfoService.java     # 차량 정보 조회 서비스 인터페이스
│
├── internal/                        # Internal 모드 관련 클래스
│   ├── service/
│   │   ├── impl/
│   │   │   └── InternalVehicleInfoServiceImpl.java  # Internal 모드 구현체
│   │   ├── VmisCarBassMatterInqireService.java      # 기본정보 조회 서비스
│   │   ├── VmisCarLedgerFrmbkService.java           # 등록원부 조회 서비스
│   │   └── VmisRequestEnricher.java                 # 요청 데이터 자동 설정
│   ├── client/
│   │   ├── GovernmentApi.java      # 정부 API Feign 인터페이스
│   │   └── GovernmentApiClient.java # 정부 API 호출 클라이언트
│   ├── gpki/                        # GPKI 암호화 관련
│   │   ├── GpkiService.java
│   │   ├── RealGpkiService.java
│   │   └── NoopGpkiService.java
│   └── mapper/                      # MyBatis 매퍼
│       ├── VmisCarBassMatterInqireMapper.java
│       └── VmisCarLedgerFrmbkMapper.java
│
├── external/                        # External 모드 관련 클래스
│   └── service/
│       ├── ExternalVehicleApiService.java           # 외부 API 호출 인터페이스
│       └── impl/
│           ├── ExternalVehicleInfoServiceImpl.java  # External 모드 구현체
│           └── ExternalVehicleApiServiceImpl.java   # REST API 호출 구현체
│
└── model/                           # 데이터 모델
    ├── request/
    │   ├── BasicRequest.java        # 기본정보 조회 요청
    │   └── LedgerRequest.java       # 등록원부 조회 요청
    ├── response/
    │   ├── BasicResponse.java       # 기본정보 조회 응답
    │   └── LedgerResponse.java      # 등록원부 조회 응답
    ├── VehicleApiResponseVO.java    # 통합 조회 응답
    └── Envelope.java                # 공통 메시지 래퍼
```

---

## 기술 스택

- **Backend Framework**: Spring Boot 2.7.x
- **Language**: Java 1.8
- **Database**: MariaDB
- **ORM**: MyBatis 3.x
- **API Documentation**: Springdoc OpenAPI (Swagger)
- **Build Tool**: Gradle
- **Security**: GPKI (Government Public Key Infrastructure)

---

## 환경 설정

### 필수 설정 파일

프로젝트는 환경별로 다음 설정 파일들을 사용합니다:

- `application.yml`: 공통 설정
- `application-local.yml`: 로컬 개발 환경
- `application-dev.yml`: 개발 환경
- `application-prd.yml`: 운영 환경

### VMIS 통합 설정 예시

```yaml
vmis:
  integration:
    mode: internal  # internal 또는 external

  # Internal 모드 설정
  gov:
    api:
      url: https://gov-api-url
    services:
      basic:
        cntcInfoCode: AC1_FD11_01
      ledger:
        cntcInfoCode: AC1_FD11_02

  # System 정보 (공통)
  system:
    infoSysId: "41-345"
    infoSysIp: "105.19.10.135"
    sigunguCode: "41460"
    chargerId: ""
    chargerIp: ""
    chargerNm: ""

  # External 모드 설정
  external:
    api:
      url:
        base: http://localhost:8081/api/v1/vehicles
```

---

## API 엔드포인트

모든 API는 `/api/v1/vehicles` 경로 하위에 정의되어 있습니다.

### 1. 통합 조회 (기본정보 + 등록원부)

**POST** `/api/v1/vehicles/info.ajax`

요청 예시:
```json
{
  "data": [{
    "VHRNO": "12가3456",
    "LEVY_STDDE": "20250101",
    "INQIRE_SE_CODE": "1",
    "VIN": "KMHAB812345678901"
  }]
}
```

응답 예시:
```json
{
  "data": [{
    "vhrno": "12가3456",
    "success": true,
    "message": "조회 성공",
    "basicInfo": { /* 기본정보 */ },
    "ledgerInfo": { /* 등록원부 정보 */ }
  }]
}
```

### 2. 기본정보만 조회

**POST** `/api/v1/vehicles/basic.ajax`

요청 예시:
```json
{
  "data": [{
    "VHRNO": "12가3456",
    "LEVY_STDDE": "20250101",
    "INQIRE_SE_CODE": "1",
    "VIN": "KMHAB812345678901"
  }]
}
```

### 3. 등록원부만 조회

**POST** `/api/v1/vehicles/ledger.ajax`

요청 예시:
```json
{
  "data": [{
    "VHRNO": "12가3456",
    "ONES_INFORMATION_OPEN": "1",
    "CPTTR_NM": "홍길동",
    "CPTTR_IHIDNUM": "8801011234567",
    "ROUTE_SE_CODE": "3",
    "DETAIL_EXPRESSION": "1",
    "INQIRE_SE_CODE": "1"
  }]
}
```

---

## 주요 클래스 및 컴포넌트

### 1. VehicleInfoService (인터페이스)

차량 정보 조회의 핵심 인터페이스로, Internal/External 모드에 따라 다른 구현체가 자동으로 주입됩니다.

**구현체:**
- `InternalVehicleInfoServiceImpl`: Internal 모드
- `ExternalVehicleInfoServiceImpl`: External 모드

### 2. VmisRequestEnricher

요청 객체에 시스템 정보를 자동으로 채워주는 컴포넌트입니다.

**자동 설정 항목:**
- INFO_SYS_ID, INFO_SYS_IP, SIGUNGU_CODE
- CNTC_INFO_CODE (서비스별)
- CHARGER_ID, CHARGER_IP, CHARGER_NM

**고정값 설정 (LedgerRequest):**
- ONES_INFORMATION_OPEN: "1" (소유자 공개)
- ROUTE_SE_CODE: "3"
- DETAIL_EXPRESSION: "1" (전체내역)
- INQIRE_SE_CODE: "1" (열람)

### 3. ApiConstant

API 응답 코드 및 시스템 상수를 관리합니다.

**주요 상수:**
- `CNTC_RESULT_CODE_SUCCESS`: "MSG50000" (성공)
- `CNTC_RESULT_CODE_ERROR`: "ERROR" (에러)
- `DEFAULT_REGISTRANT`: "SYSTEM"

---

## 데이터베이스

### 주요 테이블

- `tb_car_bass_matter_inqire`: 차량 기본정보 조회 이력
- `tb_car_ledger_frmbk`: 차량 등록원부 조회 이력

### MyBatis 매퍼 위치

```
src/main/resources/mybatis/mapper/api-internal/
├── CarBassMatterInqireMapper_maria.xml
└── CarLedgerFrmbkMapper_maria.xml
```

---

## 빌드 및 실행

### 빌드

```bash
# 클린 빌드
./gradlew clean build

# 컴파일만 (테스트 제외)
./gradlew clean compileJava
```

### 실행

```bash
# 기본 실행 (local 프로파일)
./gradlew bootRun

# 특정 프로파일로 실행
./gradlew bootRun --args='--spring.profiles.active=dev'
```

### 포트 설정

기본 포트: `8080`

변경 방법:
```yaml
server:
  port: 8080
```



#### 방법 2: JAR 파일 실행 (배포 권장)

**개발 환경**
```bash
# Linux/Mac
java -Dspring.profiles.active=dev -jar build/libs/VIPS-BOOT.war

# Windows PowerShell
java -Dspring.profiles.active=dev -jar build/libs/VIPS-BOOT.war

# 또는 환경변수 사용 (Windows PowerShell)
$env:SPRING_PROFILES_ACTIVE = "dev"
java -jar build/libs/VIPS-BOOT.war
```

**운영 환경 (환경변수 포함)**
```bash
# Linux/Mac
export SPRING_PROFILES_ACTIVE=prd
export VMIS_SYSTEM_INFO_SYS_ID="41-345"
export VMIS_SYSTEM_SIGUNGU_CODE="41460"
java -jar build/libs/VIPS-BOOT.war

# Windows PowerShell
$env:SPRING_PROFILES_ACTIVE = "prd"
$env:VMIS_SYSTEM_INFO_SYS_ID = "41-345"
$env:VMIS_SYSTEM_SIGUNGU_CODE = "41460"
java -jar build/libs/VIPS-BOOT.war
```

#### 백그라운드 실행 (Linux 서버)
```bash
# nohup 사용 (기본 java 경로, 콘솔 출력을 app.log에 저장)
nohup java -Dspring.profiles.active=prd -jar build/libs/VIPS-BOOT.war > app.log 2>&1 &

# Java 경로를 직접 지정하는 방법 (Java 17 이상)
nohup /usr/bin/java -Dspring.profiles.active=prd -jar build/libs/VIPS-BOOT.war > app.log 2>&1 &

# 또는 사용자 지정 Java 경로 사용
nohup /opt/jdk-17/bin/java -Dspring.profiles.active=prd -jar build/libs/VIPS-BOOT.war > app.log 2>&1 &

# 콘솔 출력을 남기지 않을 때 (애플리케이션 자체 로그는 logback-spring.xml 설정에 따라 별도 저장됨)
# 개발 환경
nohup /usr/bin/java -Dspring.profiles.active=dev -jar build/libs/VIPS-BOOT.war > /dev/null 2>&1 &

# 운영 환경 (애플리케이션 로그: d:/data/VIPS/logs/vips.log)
nohup /usr/bin/java -Dspring.profiles.active=prd -jar build/libs/VIPS-BOOT.war > /dev/null 2>&1 &

# Java 경로 확인 방법
which java
# 또는
readlink -f $(which java)

# 프로세스 확인
ps aux | grep VIPS

# 종료
kill -9 <PID>
```

**참고:** `/dev/null` 사용 시 콘솔 출력만 버려지며, 애플리케이션 자체 로그는 `logging.file.path` 설정에 따라 정상적으로 기록됩니다.
- 개발: `d:/data/VIPS/logs/vips.log`
- 운영: `d:/data/VIPS/logs/vips.log`

#### 백그라운드 실행 (Windows 서버)

**PowerShell - 백그라운드 프로세스로 실행**
```powershell
# 개발 환경 (콘솔 출력을 app.log에 저장)
Start-Process -NoNewWindow -FilePath "java" -ArgumentList "-Dspring.profiles.active=dev","-jar","build\libs\VIPS-BOOT.war" -RedirectStandardOutput "app.log" -RedirectStandardError "app.log"

# 운영 환경 (콘솔 출력을 app.log에 저장)
Start-Process -NoNewWindow -FilePath "java" -ArgumentList "-Dspring.profiles.active=prd","-jar","build\libs\VIPS-BOOT.war" -RedirectStandardOutput "app.log" -RedirectStandardError "app.log"

# Java 경로를 직접 지정하는 방법
Start-Process -NoNewWindow -FilePath "D:\DEV\.jdks\jdk1.8.0_271\bin\java.exe" -ArgumentList "-Dspring.profiles.active=prd","-jar","build\libs\VIPS-BOOT.war" -RedirectStandardOutput "app.log" -RedirectStandardError "app.log"

# 콘솔 출력을 남기지 않을 때 (NUL로 리다이렉트)
Start-Process -NoNewWindow -FilePath "java" -ArgumentList "-Dspring.profiles.active=prd","-jar","build\libs\VIPS-BOOT.war" -RedirectStandardOutput "NUL" -RedirectStandardError "NUL"
```

**CMD - javaw 사용 (콘솔 창 없이 실행)**
```cmd
# 개발 환경
start /B javaw -Dspring.profiles.active=dev -jar build\libs\VIPS-BOOT.war

# 운영 환경
start /B javaw -Dspring.profiles.active=prd -jar build\libs\VIPS-BOOT.war

# Java 경로를 직접 지정 (경로에 공백이 있을 경우 빈 창제목 "" 필요)
start "" /B "D:\DEV\.jdks\jdk1.8.0_271\bin\javaw.exe" -Dspring.profiles.active=prd -jar build\libs\VIPS-BOOT.war

# 또는 절대 경로 사용
start "" /B "D:\DEV\.jdks\jdk1.8.0_271\bin\javaw.exe" -Dspring.profiles.active=prd -jar D:\workspace\git\VIPS\build\libs\VIPS-BOOT.war


cd /d d:
D:\VIPS\azul-17.0.14\bin

java -Dspring.profiles.active=prd -jar D:\VIPS\VIPS-BOOT.war

"D:\VIPS\azul-17.0.14\bin\javaw.exe" -Dspring.profiles.active=prd -jar D:\VIPS\VIPS-BOOT.war

start "" /B "D:\VIPS\azul-17.0.14\bin\javaw.exe" -Dspring.profiles.active=prd -jar D:\VIPS\VIPS-BOOT.war
```

**참고:** `start` 명령어는 첫 번째 따옴표 문자열을 창 제목으로 인식하므로, 경로를 따옴표로 묶을 때는 앞에 빈 창제목 `""`을 추가해야 합니다.

**프로세스 관리**
```powershell
# 프로세스 확인
Get-Process | Where-Object {$_.ProcessName -like "*java*"} | Select-Object Id, ProcessName, Path

# 또는 특정 포트로 확인
netstat -ano | findstr :18080

# 프로세스 종료 (PID로)
Stop-Process -Id <PID> -Force

# 또는 taskkill 사용
taskkill /F /PID <PID>
```

**Java 경로 확인 (Windows)**
```powershell
# PowerShell
where.exe java

# 또는
(Get-Command java).Source

# Java 버전 확인
java -version
```

**참고:** Windows 환경에서도 콘솔 출력을 `NUL`로 리다이렉트하면, 애플리케이션 자체 로그는 `d:/data/VIPS/logs/vips.log`에 정상적으로 기록됩니다.

### 4) API 문서(Swagger UI)
- 개발 환경: `http://localhost:8080/swagger-ui/index.html`
- 운영 환경: `http://localhost:18080/swagger-ui/index.html`

---


---

## Swagger API 문서

애플리케이션 실행 후 다음 URL에서 API 문서를 확인할 수 있습니다:

```
http://localhost:8080/swagger-ui/index.html
```

---

## 개발 가이드

### 새로운 API 추가 시

1. **Request/Response 모델 생성**
   - `model/request/` 또는 `model/response/` 패키지에 DTO 클래스 생성
   - `@JsonProperty` 어노테이션으로 JSON 필드 매핑

2. **서비스 인터페이스에 메서드 추가**
   - `VehicleInfoService` 인터페이스에 메서드 정의

3. **구현체에 로직 작성**
   - `InternalVehicleInfoServiceImpl`: Internal 모드
   - `ExternalVehicleApiServiceImpl`: External 모드

4. **컨트롤러에 엔드포인트 추가**
   - `VehicleInterfaceController`에 `@PostMapping` 추가
   - Swagger 문서화를 위한 `@Operation` 어노테이션 추가

### LedgerRequest 필수 설정 값

등록원부 조회 시 다음 값들이 자동으로 설정됩니다:

```java
// BasicInfo에서 자동 매핑
ledgerRequest.setCpttrNm(basicInfo.getMberNm());         // 민원인성명
ledgerRequest.setCpttrIhidnum(basicInfo.getMberSeNo());  // 민원인주민번호

// 고정값
ledgerRequest.setOnesInformationOpen("1");  // 개인정보공개
ledgerRequest.setRouteSeCode("3");          // 경로구분코드
ledgerRequest.setDetailExpression("2");     // 내역표시
ledgerRequest.setInqireSeCode("1");         // 조회구분코드
```

### 성공 코드 체크

응답 검증 시 반드시 상수를 사용하세요:

```java
// ❌ 하드코딩
if ("00".equals(response.getCntcResultCode())) { ... }

// ✅ 상수 사용
if (ApiConstant.CNTC_RESULT_CODE_SUCCESS.equals(response.getCntcResultCode())) { ... }
```

---

## 응답 코드

정부 API 응답 코드는 `ApiConstant.java`의 주석을 참고하세요.

**주요 응답 코드:**

| 코드 | 설명 |
|------|------|
| MSG50000 | EAI 연계 호출을 성공적으로 처리하였습니다. |
| MSG50001 | 요청하신 자료가 존재하지 않습니다. |
| MSG50002 | 기준치보다 많은 결과조회로 인해 열람이 불가합니다. |
| MSG50110 | EAI 호출을 위한 Message(Header) 구성에 실패하였습니다. |
| MSG50250 | EAI-HUB 구간 통신장애 오류 |

---

## 트러블슈팅

### 1. 모드가 전환되지 않을 때

`@ConditionalOnProperty` 어노테이션이 적용된 구현체가 제대로 로드되는지 확인:

```yaml
vmis:
  integration:
    mode: internal  # 또는 external
```

### 2. GPKI 오류

- GPKI 인증서 경로 확인
- `GpkiConfig` 설정 확인
- 로컬 개발 시 `NoopGpkiService` 사용 고려

### 3. 데이터베이스 연결 오류

- HikariCP 설정 확인
- 데이터베이스 URL, 사용자명, 비밀번호 확인
- 방화벽/네트워크 설정 확인

---

---

## 라이선스

Copyright (c) 2025 XIT Co., Ltd.

---

## 연락처

프로젝트 관련 문의사항은 개발팀에 문의하시기 바랍니다.
