# Oracle/MariaDB 연결 운영 가이드

## 적용된 공급자

`.NET 8`용 실제 ADO.NET 계층은 다음 버전을 고정합니다.

- Oracle: `Oracle.ManagedDataAccess.Core` `23.26.200`
- MariaDB: `MySqlConnector` `2.6.1`

Oracle ODP.NET Core 23 계열은 TAP 기반 `OpenAsync`/명령 비동기를 지원하며, 이 구현은 Oracle pipelining을 켜지 않습니다. MariaDB Connector/NET 공식 가이드가 권장하는 MySqlConnector는 `OpenAsync`, reader 비동기와 취소를 사용합니다. Oracle 23.26.200은 Oracle Database 19c 이상을 전제로 하며, 현재 운영 스모크에서 서버 주 버전 21을 확인했습니다.

공식 참고:

- https://www.nuget.org/packages/Oracle.ManagedDataAccess.Core/23.26.200
- https://docs.oracle.com/en/database/oracle/oracle-database/26/odpnt/featAsyncPipelining.html
- https://mariadb.com/docs/connectors/mariadb-connector-net/mariadb-connector-net-guide
- https://www.nuget.org/packages/MySqlConnector/2.6.1

## 런타임 설정

원본 호환 앱은 호환성을 위해 다음 순서로 DB 설정을 선택합니다.

1. 실행 파일 옆 `Res\MmoneyCoder.ini` — 기존 수동 배치와의 호환 경로
2. `%LOCALAPPDATA%\MBN_STOCK_WEBVIEW\Res\MmoneyCoder.ini` — MSIX용 로컬 overlay
3. 기존 `%LOCALAPPDATA%\MBN_STOCK_WEBVIEW\Config\database.local.json` — INI가 없을 때 fallback

공식 Debug/Release 빌드와 개발 인수용 런타임 묶음은 첫 번째 파일을 생성하거나 복사하지
않습니다. 깨끗한 개발 PC에서는 두 번째 또는 세 번째 사용자 전용 경로만 사용합니다.
`MmoneyCoder.ini`가 들어 있는 원본 `Res` 전체를 빌드 출력이나 전달용 ZIP으로 복사하지
마세요.

`MmoneyCoder.ini`의 `[Oracle]`/`[Maria]` 섹션에서 `ConnectionName=host:port/service`,
`ID`, `Pass`를 읽습니다. BOM 없는 UTF-8과 CP949를 지원하고 환경 변수는 선택된 파일 값보다
우선합니다. 원본 `Res`의 복사본·`.bak`·`.zip`은 사용하지도 배포하지도 않습니다.

MSIX 설치 폴더는 읽기 전용이며 평문 자격증명을 패키지에 포함하지 않습니다. 패키지에서는
두 번째 또는 세 번째 경로를 사용합니다. JSON 기본 경로는 다음과 같습니다.

```text
%LOCALAPPDATA%\MBN_STOCK_WEBVIEW\Config\database.local.json
```

저장소의 [appsettings.example.json](../Config/appsettings.example.json)은 비밀값이 없는 구조 예시입니다. 실제 파일은 [Initialize-DatabaseConfig.ps1](../scripts/Initialize-DatabaseConfig.ps1)로 만들 수 있습니다. 이 스크립트는 비밀번호를 프롬프트로 받고 현재 Windows 사용자만 파일을 읽도록 ACL을 설정합니다.

```powershell
powershell -ExecutionPolicy Bypass -File .\scripts\Initialize-DatabaseConfig.ps1 `
  -OracleHost '<host>' -OraclePort 1521 -OracleSid '<sid>' -OracleUserName '<user>' `
  -MariaDbHost '<host>' -MariaDbPort 3306 -MariaDbDatabase '<database>' -MariaDbUserName '<user>'
```

Oracle은 `sid` 또는 `serviceName` 중 정확히 하나를 설정합니다. 원본 MBN_STOCK_N은 SID descriptor를 사용했으므로 legacy 연결은 `sid`로 옮깁니다. 설정 파일은 평문 비밀번호를 포함할 수 있으므로 저장소·로그·첨부파일로 복사하지 않습니다. 운영 환경에서는 실행 계정의 파일 ACL을 유지하고, 환경 변수 오버라이드 또는 조직의 승인된 비밀 저장소를 사용할 수 있습니다.

선택된 INI 또는 JSON보다 환경 변수가 우선합니다.

```text
MBN_STOCK_ORACLE_HOST / PORT / SID / SERVICE_NAME / USERNAME / PASSWORD
MBN_STOCK_MARIADB_HOST / PORT / DATABASE / USERNAME / PASSWORD / TLS_MODE
MBN_STOCK_DB_OPERATION_TIMEOUT_SECONDS
MBN_STOCK_DB_MAX_RETRY_COUNT
MBN_STOCK_DB_INITIAL_RETRY_DELAY_MS
```

MariaDB `tlsMode`는 `Disabled`, `Preferred`, `Required`, `VerifyCertificate`, `VerifyIdentity`를 지원합니다. 인증서와 호스트 이름을 운영 환경에서 검증할 수 있으면 `VerifyIdentity`를 사용합니다. legacy LAN과의 호환을 위해 예시 기본값은 `Preferred`입니다.

## 실행 정책

각 조회는 연결을 새로 만들고 공급자 풀링을 사용합니다. `OpenAsync`, `ExecuteReaderAsync`, `ReadAsync`에 같은 취소 토큰을 전달하며, 공급자 command timeout과 전체 작업 timeout을 모두 적용합니다. 호출자가 취소한 작업은 재시도하지 않습니다. 재시도는 `SELECT`/`WITH` 읽기와 검토된 일시적 네트워크 오류에만 제한하고, 매 시도마다 기존 연결을 폐기한 뒤 새 연결을 엽니다. INSERT/UPDATE/DDL과 인증·스키마·SQL 오류는 재실행하지 않습니다.

DB 오류는 `DatabaseOperationException`의 안전한 한국어 메시지로 변환됩니다. provider 예외, raw connection string, 사용자명, 비밀번호, SQL은 WebView 메시지나 예외 `ToString()`에 포함하지 않습니다. health 상태는 Oracle과 MariaDB를 별도로 표시하므로 한쪽 장애가 앱 종료나 다른 쪽 결과 표시를 막지 않습니다.

## WebView 메시지

Web UI가 `request-database-status`를 보내면 native bridge가 양쪽 health 상태를 반환합니다. `kospi`, `kosdaq`, `index`, `overseas` 메뉴는 `request-market-data`를 보내고, native는 bounded DTO로 열 이름·행·전체 행 수를 반환합니다. `DataTable` 자체를 JSON으로 직렬화하지 않으며, 이전 `requestId` 응답은 Web UI가 버립니다. 오류는 `market-data-error`로 표시되고 기존 플레이리스트·PREPARE/TAKE IN/TAKE OUT UI는 계속 동작합니다.

## 검증 명령

단위 테스트:

```powershell
dotnet test MBN_STOCK_WEBVIEW.sln -c Release -p:Platform=x64
```

실제 DB 스모크(설정 파일을 읽어 비밀값은 출력하지 않음):

```powershell
dotnet run --project .\tools\MBN_STOCK_WEBVIEW.DbSmoke\MBN_STOCK_WEBVIEW.DbSmoke.csproj -c Release
```

원본 실행 폴더의 `Res\MmoneyCoder.ini` 경로를 그대로 검증할 때는 다음처럼 실행합니다.

```powershell
dotnet run --project .\tools\MBN_STOCK_WEBVIEW.DbSmoke\MBN_STOCK_WEBVIEW.DbSmoke.csproj `
  -c Release -- --legacy-runtime-root `
  "C:\Users\MD\source\repos\MBN_STOCK_N\MBN_STOCK_N\bin\Debug"
```

이 모드의 NXT endpoint 감사는 지정한 INI의 host/port/database를 환경변수 적용 전
비교 기준으로 사용합니다. 기본/`--config` 모드도 선택한 JSON의 환경변수 적용 전
endpoint를 기준으로 사용하므로 환경변수가 연결 대상을 바꾼 경우를 감지할 수
있습니다. 비교 출력은 SHA-256과 일치 여부뿐이며 계정과 비밀번호는 포함하지 않습니다.

스모크는 양쪽 `SELECT 1`, Oracle 서버 버전, 코스피·코스닥·NXT·5개 국내 지수·해외 종목 카탈로그의 실제 행을 확인합니다. 출력에는 상태, 테이블 이름, 행 수만 포함합니다.

## MSIX

패키지 앱은 `internetClient`와 `privateNetworkClientServer` capability를 사용합니다. Debug/Release x64와 MSIX 생성은 저장소 루트 README의 명령을 사용합니다. `ThirdPartyNotices`의 Oracle 및 MySqlConnector 고지는 MSIX 콘텐츠에 포함됩니다. 실제 운영 배포는 조직 인증서로 서명해야 하며 `.pfx`/비밀키는 저장소에 넣지 않습니다.
