24 KiB
로그인 프로시저 연동 및 검사 결과 저장 개편 사양서
본 문서는 로그인 검증 시 특정 저장 프로시저를 연동하고, 검사 완료 결과를 신규 정의된 dbo.EOL 테이블 스키마 및 16컬럼 로컬 CSV 구조에 맞추어 통합 저장하도록 변경한 설계 및 구현 상세 사양을 정리한 기술 지식베이스입니다.
1. 로그인 프로시저 연동 설계 및 구현
1.1 설정 사양 (Database.ini)
프로그램 구동 시 로그인 및 DB 접속 세부 사양을 유연하게 제어하기 위해 윈도우 공용 폴더(C:\ProgramData\MobiLeakTest\) 하위의 Database.ini 파일을 파싱하여 설정을 로드합니다. 이를 통해 ClickOnce 배포 후 프로그램 업데이트 시에도 사용자가 정의해 둔 설정 정보가 유실되지 않도록 안정성을 확보하였습니다.
- 파일 위치:
C:\ProgramData\MobiLeakTest\Database.ini - 설정 형식:
사람이 직접 읽고 편집하기 편리하도록 개별 상세 접속 파라미터(
Db_Server,Db_Name,Db_User,Db_Password)로 줄바꿈하여 설정할 수 있습니다. (하위 호환성을 위해 단일ConnectionString통째 입력 방식도 동시에 지원합니다.); [데이터베이스 접속 상세 설정] Db_Server=192.168.0.20 Db_Name=NE1aW_PT_SENSOR Db_User=sa Db_Password=sa1234 [Login] Procedure=dbo.CheckOperator ProcessName=EOL OfflinePreview=false
1.2 소스코드 변경 내역
LoginSettings.cs
- 추가 프로퍼티:
Procedure(string): 검증에 사용할 DB 저장 프로시저명ProcessName(string): 검사에 연동된 공정명
- 로드 로직:
Database.ini파일로부터Procedure및ProcessName키를 대소문자 구분 없이 인식하여 설정 개체에 바인딩합니다.
LoginAccountRepository.cs (ExistsAsync)
- 동작 흐름:
OfflinePreview가true이면 DB 조회를 건너뛰고 무조건 로그인을 성공 처리합니다.Database.ini에Procedure설정이 정의되어 있으면SqlCommand객체의CommandType을StoredProcedure로 지정하여 해당 프로시저를 호출합니다.- SQL 매개변수:
@OperatorId: 사용자 입력 ID@Password: 사용자 입력 패스워드@ProcessName: INI 파일의ProcessName정보 (EOL)
- 인증 판단: 프로시저 반환값을 정수로 파싱하여 결과가
0보다 크면 성공(true), 그렇지 않으면 실패(false)로 판단합니다.
- SQL 매개변수:
Procedure설정이 없을 경우 Fallback 쿼리(SELECT COUNT(*) FROM [Users] WHERE [OperatorId] = @Id ...)를 작동시킵니다.- DB 연결 또는 쿼리 오류 등으로 인해 SQL Exception이 발생하더라도 프로그램 비정상 종료를 예방하는 예외 격리 구조를 갖추고 있습니다.
- DB 연결이 오프라인 등의 사유로 지연될 때 프로그램이 무한 대기하는 현상을 막기 위해
SqlConnectionStringBuilder를 이용하여 연결 제한 시간(ConnectTimeout)을 1초로 명시적 구성하였습니다.
1.3 로그인 옵션 동적 설정 (LoginOptions.json)
로그인 시 콤보박스 목록(Maker, Model, Variant 1/2, Line, Jig No) 및 로컬 로그인 검증을 위한 계정 정보를 중앙 집중식으로 동적 관리하기 위해 LoginOptions.json 설정 방식을 도입하였습니다.
- 파일 위치:
AppData/Roaming/Housing/LoginOptions.json(기본값 설정 시 실행 디렉터리의LoginOptions.json에서 자동 복사 혹은 기본 템플릿 생성) - JSON 스키마 구조:
{ "loginAccounts": [ { "id": "su", "password": "su" } ], "makers": ["AMO", "ABC"], "models": ["MODEL-1"], "variant1Values": [], "variant2Values": [], "lineValues": ["LINE-1"], "jigNoValues": ["JIG-1"] }
LoginSelectionOptions.cs (신규)
- 기능: JSON 데이터 직렬화/역직렬화를 위한 클래스 모델 정의.
- 정적 메서드:
Load(string path): JSON 파일을 역직렬화하여LoginSelectionOptions개체로 반환.CreateDefaultFile(string path): 파일이 존재하지 않는 경우su/su데모 계정과 기본 콤보 옵션 데이터를 가진 JSON 구조로 기본 파일 생성.
StartupLoginWindow.xaml / .cs (교체)
- 개선 사양:
- 고급 UX 디자인 및 리소스 오류 해결: AMO HOUSING HMI 전용 HSL 기반 세련된 테크 어두운 테마(Dark Gradient) 및 컨트롤 템플릿 적용. 이 과정에서 Window의 Background 속성이 Window.Resources 내부 로드보다 먼저 파싱되어 발생하는
XamlParseException문제를 예방하기 위해, 14종의 Hmi Theme SolidColorBrush 정의를 App.xaml의<Application.Resources>에 전역 등록하여 어떠한 순서 예외 없이 정상 작동하도록 마이그레이션하였습니다. - 동적 리스트 바인딩:
LoginOptions.json설정에 따라 메이커, 모델, 라인, 지그 등 콤보박스 목록이 동적 갱신 및 바인딩되도록 전면 개편. - 로컬 및 데이터베이스 순차 검증 로그인 (1초 강제 타스크 타임아웃): 로그인 시도 시
LoginOptions.json의loginAccounts목록을 1순위로 조회하여 일치하는 계정이 있으면 즉시 통과시킵니다. 만약 JSON에 해당 계정이 없거나 암호가 일치하지 않는 경우, 2순위 Fallback 단계로 데이터베이스 사용자 테이블(LoginAccountRepository)을 조회합니다. 이 때 DB 오프라인 환경 등에서 TCP 스택 및 드라이버 단의 고유 지연으로 인해 발생할 수 있는 15초 이상의 블로킹 현상을 방지하고자Task.WhenAny와Task.Delay(1000)를 사용해 1초 타임아웃을 강제 통제함으로써 즉각적인 실패 처리가 이루어지도록 조치하였습니다. - 투명한 오류 분석 및 원본 예외 정보 노출: 데이터베이스 쿼리 및 로그인 검증 예외 발생 시 예외 정보를 묵살(Swallow)하거나 단순 메시지만 표기하던 기존 설계를 개선하여, 본래 발생한 C# 예외의 타입(예:
SqlException,TimeoutException)과 실제 상세 에러 내용을[오류 종류] 오류 메시지형태로 정밀 포맷팅하여 출력하도록 예외 흐름 및 UI 프레젠테이션을 전면 교체하였습니다.
- 고급 UX 디자인 및 리소스 오류 해결: AMO HOUSING HMI 전용 HSL 기반 세련된 테크 어두운 테마(Dark Gradient) 및 컨트롤 템플릿 적용. 이 과정에서 Window의 Background 속성이 Window.Resources 내부 로드보다 먼저 파싱되어 발생하는
2. 검사 결과 저장 개편 (dbo.EOL 스키마 및 CSV 일원화 대응)
2.1 데이터베이스 구성 명세 (dbo.EOL)
사용자가 정의한 구성안에 맞춰 Password 컬럼을 완전히 삭제하고, 검사 결과 및 전역 로그인 메타데이터 정보를 릭(Leak) 검사 결과와 병합하여 UPSERT(MERGE) 처리합니다.
| 물리 컬럼명 | 데이터 타입 | 매핑 설명 | 비고 |
|---|---|---|---|
IC_SN |
NVARCHAR(10) |
제품의 최종 조합 ID (inspectData.IcSn) |
PK / FK |
PCB_Barcode |
NVARCHAR(10) |
고유 식별용 바코드 정보 | UK (inspectData.PcbBarcode 매핑) |
Maker |
NVARCHAR(10) |
로그인 메이커 정보 (App.LoginResult.Maker) |
|
Model |
NVARCHAR(10) |
로그인 모델 정보 (App.LoginResult.Model) |
|
Variant_1 |
NVARCHAR(10) |
로그인 변형1 정보 (App.LoginResult.Variant1) |
|
Variant_2 |
NVARCHAR(10) |
로그인 변형2 정보 (App.LoginResult.Variant2) |
|
Operator |
NVARCHAR(10) |
로그인 검사자 ID (App.LoginResult.Operator) |
|
Production_Date |
DATETIME2(0) |
검사 일시 (inspectData.ProductionDate ?? DateTime.Now) |
|
Line |
NVARCHAR(30) |
로그인 작업 라인 (App.LoginResult.LineNo) |
|
Lot_No |
NVARCHAR(8) |
로그인 Lot 번호 (App.LoginResult.LotNo) |
|
Jig_No |
NVARCHAR(10) |
로그인 지그 번호 (App.LoginResult.JigNo) |
|
Channel |
NVARCHAR(5) |
검증 수행 채널 (LEFT / RIGHT) |
|
Spec_UL |
DECIMAL(3,2) |
스펙 상한값 (decimal 변환) |
|
Spec_LL |
DECIMAL(3,2) |
스펙 하한값 (decimal 변환) |
|
Leak_Value |
DECIMAL(5,4) |
측정된 누설량 (decimal 변환) |
|
Result |
NVARCHAR(5) |
합격 여부 판정 (OK / NG) |
2.2 SQL UPSERT 쿼리 명세 (MERGE 구문)
IC_SN 키를 기준으로 이미 데이터가 적재되어 있다면 모든 관련 필드를 신규 검사 정보로 UPDATE 하고, 미등록 상태라면 새로 INSERT 하도록 설계되었습니다.
MERGE INTO [dbo].[EOL] AS Target
USING (SELECT @IC_SN AS IC_SN) AS Source
ON (Target.IC_SN = Source.IC_SN)
WHEN MATCHED THEN
UPDATE SET
Target.PCB_Barcode = @PCB_Barcode,
Target.Maker = @Maker,
Target.Model = @Model,
Target.Variant_1 = @Variant_1,
Target.Variant_2 = @Variant_2,
Target.Operator = @Operator,
Target.Production_Date = @Production_Date,
Target.Line = @Line,
Target.Lot_No = @Lot_No,
Target.Jig_No = @Jig_No,
Target.Channel = @Channel,
Target.Spec_UL = @Spec_UL,
Target.Spec_LL = @Spec_LL,
Target.Leak_Value = @Leak_Value,
Target.Result = @Result
WHEN NOT MATCHED THEN
INSERT (IC_SN, PCB_Barcode, Maker, Model, Variant_1, Variant_2, Operator, Production_Date, Line, Lot_No, Jig_No, Channel, Spec_UL, Spec_LL, Leak_Value, Result)
VALUES (@IC_SN, @PCB_Barcode, @Maker, @Model, @Variant_1, @Variant_2, @Operator, @Production_Date, @Line, @Lot_No, @Jig_No, @Channel, @Spec_UL, @Spec_LL, @Leak_Value, @Result);
2.3 소스코드 구현 핵심 사항
- EolDatabaseService.cs:
- WPF 애플리케이션의 전역 로그인 상태 개체인
App.LoginResult를 안전하게 로컬 변수로 바인딩(Null-coalescing연산자 적용)하여 로그인 정보가 비어 있는 비정상 상황에서도 DB 저장이 실패하지 않도록 설계했습니다. decimal.TryParse패턴을 도입하여SpecUL,SpecLL,MeasuredValue와 같은 수치형 데이터를 안전하게 파싱한 뒤 SQL Parameter로 전달함으로써 타입 변환 예외 위험을 격리했습니다.- 개별 매핑 및 시간 동기화:
inspectData.IcSn및inspectData.PcbBarcode를 DB 파라미터로 각각 매핑하고, 저장 시간 역시 CSV와의 정합성 일치를 위해inspectData.ProductionDate ?? DateTime.Now를 바인딩하도록 고도화했습니다. - 연결 제한 시간 설정: 네트워크 불안정 등으로 저장 실패 시 오래 대기하지 않도록
SqlConnectionStringBuilder를 사용하여 연결 제한 시간(ConnectTimeout)을 1초로 명시적 지정하였습니다.
- WPF 애플리케이션의 전역 로그인 상태 개체인
- DatabaseSettings.cs:
- SSL 인증서 체인 신뢰성 오류 대응: 데이터베이스 접속 설정(
Database.ini)을 파싱하여 조합된 커넥션 스트링을 생성할 때, 신뢰되지 않은 인증서 오류(TrustServerCertificate 누락으로 인한 SSL 공급자 에러 0)를 원천 차단하기 위해TrustServerCertificate=True;옵션이 존재하지 않을 경우 자동으로 연결 문자열 접미사에 추가하도록ConnectionString프로퍼티 get 로직을 개선했습니다.
- SSL 인증서 체인 신뢰성 오류 대응: 데이터베이스 접속 설정(
- TestProcessService.cs (EolDatabaseService 인스턴스 생성):
- 기존에 분산 관리되던 DB 접속 스트링(
config.xml의DbConnectionString) 대신, 모든 데이터베이스 접속 및 검증 주소를Database.ini파일 하나로 일원화하기 위해DatabaseSettings.Load(dbIniPath).ConnectionString방식으로 접속 정보를 읽어오도록 수정하여 설정 관리 포인트를 통합했습니다. - 메타데이터 저장 정합성 확보: 합격, 불합격, 타임아웃 등 모든 분기에서
InspectData객체 생성 시 로그인 정보(Maker,Model,Variant,Operator등) 및 실제 검사 시간(ProductionDate), 바코드 값을 완벽히 주입하여 로컬 CSV 백업 파일 작성 시 공란으로 비어있지 않도록 일원화했습니다. - 진행 상태 메시지 고도화: 비동기 DB 저장(
SaveEolDataAsync) 수행 후, 저장이 성공하면 화면에"DB 저장 완료"진행 메시지가 노출되도록NotifyProgress동작을 각 분기(통신 타임아웃, 일반 검사 완료)에 추가 연동했습니다. - 센서 ID 판독 실패 시 DB 및 CSV 저장 제외: 제품 ID(센서 ID)를 판독하지 못했을 경우(
sensorData == null), 데이터베이스 및 로컬 백업 CSV 로그에 키값이 비어 있는 불완전한 데이터가 기록되거나 업서트되는 문제를 예방하고 불필요한 파일/네트워크 I/O를 원천 방지하기 위해 DB 저장(SaveEolDataAsync) 및 CSV 로그 저장(FileLogger.LogInspectData) 로직을 완전히 삭제하였습니다. - 종료 시간 수집 확장:
TestCompleted이벤트가 전달하는TestResultEventArgs개체에ProductionDate프로퍼티를 추가하고, 데이터베이스 적재 시간(inspectData.ProductionDate)을 그대로 담아 전송하도록 확장하였습니다.
- 기존에 분산 관리되던 DB 접속 스트링(
- HomeViewModel.cs:
- 시작/종료 시간 가공: 검사 시작 시점에 생성되는 원시 시작 시간을 내부 필드(
_leftActualStartTime,_rightActualStartTime)에 백업해 두고, 화면 바인딩 값인LeftStartTime/RightStartTime에는시작시간 / -로 초기화하도록 설계했습니다. 이후 시험 완료 이벤트 수신 시점(TestCompleted)에 전달받은 DB 생산 일자(e.ProductionDate)를 결합하여시작시간 / 종료시간포맷으로 동적 갱신되도록 하였습니다.
- 시작/종료 시간 가공: 검사 시작 시점에 생성되는 원시 시작 시간을 내부 필드(
- ProjectModels.cs (AppConfig 및 InspectData):
AppConfig내부에 존재하던 데이터베이스 연결 주소 하드코딩 속성에[Obsolete]경고를 지정하고, 실제 데이터베이스 연결에서는 이 속성이 사용되지 않도록 완전히 배제했습니다.InspectData클래스 모델을 확장하여 새로운 DB 테이블 컬럼 바인딩 필드(Maker, Model, Variant 등)를 추가하여 데이터 저장 및 표현의 정합성을 수립했습니다.
- LogUtils.cs (FileLogger 및 LogParser):
- 로컬 CSV 16컬럼 저장:
FileLogger.LogInspectData를 수정하여 DBdbo.EOL테이블 스키마와 완벽히 동일한 16개 컬럼(IC_SN,PCB_Barcode,Maker,Model,Variant_1,Variant_2,Operator,Production_Date,Line,Lot_No,Jig_No,Channel,Spec_UL,Spec_LL,Leak_Value,Result) 순서 및 헤더로 CSV 로그 파일에 기록하도록 개선했습니다. - 상세 필터 검색:
LogParser.ParseLogs메서드 시그니처를 확장하여opFilter,lotFilter,modelFilter매개변수를 추가하고, DB 쿼리 실행 시LIKE조건식을 동적으로 조합해 교차 상세 검색을 수행하도록 수정했습니다. - 안정성 및 공유 잠금(FileShare) 조치: 현장 작업자가 엑셀로 백업 CSV를 열어 두어 파일 락이 발생하더라도
FileShare.ReadWrite모드를 적용하여 데이터가 안전하게 읽어지도록 개선했습니다. - 로캘 독립적 날짜 파싱: 시스템 국가 언어 설정과 관계없이 안전하게 파일을 파싱할 수 있게
DateTime.TryParseExact로 날짜 포커스를 통제했습니다. - 중복 제거 필터 적용: 동일 제품 재검사 시 DB는 덮어쓰기되고 CSV는 누적되는 동작 차이를 극복하기 위해, CSV 조회(
ParseLogsFromCsv) 시 동일 제품 코드에 한해 가장 최신 날짜/시간의 1건만 노출하도록 중복 제거 필터를 적용했습니다. - CSV Fallback 및 하위 호환성: DB 연결 실패 혹은 미등록 시 작동하는 로컬 CSV 파싱(
ParseLogsFromCsv) 과정에서, 한 행의 쉼표 분할 크기(parts.Length)를 기반으로 구형 8개 컬럼 파일과 신규 16개 컬럼 파일을 자동 감지해 맵핑하는 정합성 및 예외 안전 장치를 탑재했습니다. 필터 조건 입력 시 구형 포맷은 자동 필터링 제외 처리됩니다.
- 로컬 CSV 16컬럼 저장:
- DataViewModel.cs:
OperatorFilter,LotNoFilter,ModelFilter바인딩 속성을 신설하고, 검색 실행(ExecuteSearch) 호출 시 이를LogParser.ParseLogs에 매개변수로 전달하여 연계 검색이 수행되도록 변경했습니다.
- DataView.xaml:
- 확장된 상세 필터 조건들을 유연하게 담고 레이아웃이 훼손되지 않도록 필터 패널 영역을 2행(Rows) 구조로 전면 리디자인하여 UX 사용성을 개선했습니다.
- 데이터 그리드의 열 바인딩 정보를 신규 스키마 컬럼 구성과 일치하도록 17개 컬럼으로 보강했습니다.
2.3 DB 기반 PCB 바코드 조회 및 검사 제어 연동 (Housing_Assembly)
기밀 시험(Leak Test) 시작 시 센서에서 판독한 IC_SN을 기준으로 바코드를 조회하여 기록하고, 실패 시 오작동 방지를 위해 검사를 완전히 중단하는 시퀀스를 수립하였습니다.
- 조회 테이블:
[dbo].[Housing_Assembly]- 조회 조건:
[IC_SN] = @IC_SN - 조회 필드:
[PCB_Barcode]
- 조회 조건:
- 제어 로직:
- 1.5단계 바코드 매핑 프로세스: 센서 ID(IC_SN)를 판독한 후 2단계 릭 계측 대기로 진입하기 전 데이터베이스의
dbo.Housing_Assembly를 조회합니다. 이 때 연결 지연을 극복하기 위해 연결 제한 시간(ConnectTimeout)을 5초로 지정합니다. 바코드 조회가 성공하면 즉시 메인 화면에IC_SN / PCB_Barcode형태로 실시간 결합 렌더링됩니다. - 오프라인 미리보기 모드(OfflinePreview):
OfflinePreview설정이true로 활성화되어 있을 경우, 실제 데이터베이스 연결을 생략하고 지정된 테스트 바코드 상수값인 **"testPCBbarcode"**를PCB_Barcode데이터로 주입하여 시험을 정상적으로 이어나갑니다. - 조회 예외 및 데이터 누락 대응: DB 통신 장애로 인한 예외(SqlException 등)가 발생하거나 매핑 정보가 존재하지 않을 경우, 즉시 화면에
"바코드 매핑 실패: [ExceptionType] ExceptionMessage"또는"바코드 매핑 실패: 매핑 데이터 없음"오류를 출력하고 기밀 시험을 즉각 중단합니다. - 물리 출력 및 정합성 보장: 조회가 실패하여 중단된 장치는 물리 NG 접점(
side_NG)을 출력 처리하고,TestCompleted이벤트 및 CSV 결과 로그에PcbBarcode = "-"및 판정 결과NG를 이중 기록하여 무결성을 유지합니다. - ID 판독 실패 시 안전 제어 및 크래시 방지: 시험 기동 신호 유입 시 ID 센서로부터 ID 값(IC_SN)을 판독하는 데 실패한 경우(
sensorData == null), 즉시 화면에"ID 읽기 실패"오류를 출력하고 물리 NG 접점(side_NG)을 출력 및 결과 이벤트를 전송한 후,continue제어문을 통해 시험 시퀀스를 즉각 중단하고 다음 시험 대기 상태로 복귀하도록 구현하여NullReferenceException에 의한 프로그램 비정상 종료(크래시) 현상을 완벽히 방지하였습니다. - 메인 화면 식별자 행 분리 및 마진 최적화: 좌/우 채널 모니터링 테이블 내부의 식별자 정보를 가로 텍스트 잘림 현상 예방 및 가독성 강화를 위해 기존
"ID"행에서 **"IC_SN"**과 **"PCB_Barcode"**의 2개 독립된 행(Rows)으로 분할하여 전면 배치했습니다. 이를 통해 고정 헤더 가로폭을 확장하지 않고 기존의 깔끔한 가로 너비인 **130**을 안정적으로 유지합니다. 더불어 영문"PCB_Barcode"라벨 텍스트의 좌우 여백을 개별 마진 **Margin="6,0"**으로 미세 조율하여, 윈도우의 가로 크기를 최소 사양인 **1150**으로 줄인 창모드 상태에서도 단 1픽셀의 잘림 없이 모든 텍스트가 명확하게 표현되도록 방어했습니다. - 설정 파일 통합 및 C드라이브 배포 자동화:
- 설정 통합: 클릭원스 배포 시 설정 파편화를 방지하기 위해 로컬 로그인 설정 파일(
LoginOptions.json) 및 로그인 내역 파일(LoginHistory.json)의 생성 및 조회 경로를Database.ini와 동일한 공용 경로인 **C:\ProgramData\LeakTest\**로 일원화하였습니다. - C드라이브 설치 자동화:
ClickOnceProfile.pubxml의 게시 경로(PublishUrl)를 **C:\LeakTest\**로 수정하고,leak_test_project.csproj파일 내에PostPublishCopyMSBuild Target을 연동하여 퍼블리시 성공 시 배포 실행 패키지 파일들이 C드라이브 대상 경로(C:\LeakTest`)로 자동 생성 및 복제되도록 구축하였습니다.
- 설정 통합: 클릭원스 배포 시 설정 파편화를 방지하기 위해 로컬 로그인 설정 파일(
- 1.5단계 바코드 매핑 프로세스: 센서 ID(IC_SN)를 판독한 후 2단계 릭 계측 대기로 진입하기 전 데이터베이스의
3. 개발 및 빌드 검증 가이드
3.1 Non-SDK 스타일 빌드 경고 및 주의사항
본 프로젝트는 Non-SDK 스타일(C# .NET Framework 4.7.2) 기반의 레거시 WPF 애플리케이션입니다.
- 문제 현상:
dotnet build를 사용해 컴파일을 시도하면 XAML 파일을 파싱 및 코드비하인드로 자동 변환하는 WPF 전용 MSBuild Target 프로세스가 누락되어g.cs임시 파일들이 누락됩니다. 이로 인해 컨트롤 식별자가 정의되지 않았다는 다수의 컴파일 에러(예:CS0103 'Variant1TextBox' 이름이 현재 컨텍스트에 없습니다.)가 발생하게 됩니다. - 올바른 빌드 방법: Visual Studio 2022 환경의 정식
MSBuild.exe를 사용하여 컴파일을 수행해야 합니다.# MSBuild 정식 컴파일 명령어 (Cwd: leak_test_project/) & "C:\Program Files\Microsoft Visual Studio\18\Insiders\MSBuild\Current\Bin\MSBuild.exe" "leak_test_project\leak_test_project.csproj" /t:Rebuild /p:Configuration=Release
3.2 빌드 배포 자동화 (.csproj)
Database.ini 파일이 프로그램 실행 및 릴리즈 배포 경로에 자동 포함되도록 하기 위해 leak_test_project.csproj 파일 내에 아래와 같이 PreserveNewest 복사 수명 설정을 구현했습니다.
<ItemGroup>
<None Include="App.config" />
<None Include="Database.ini">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
이를 통해 VS에서 프로젝트를 빌드하거나 재빌드할 때마다 자동으로 최신 Database.ini 파일이 컴파일 결과물 폴더(bin/Debug/ 및 bin/Release/)로 자동 갱신 및 복사됩니다.