From 9830dd75bd06ab96bb4a3a1f6b23637147cb947d Mon Sep 17 00:00:00 2001 From: seuuung0 Date: Tue, 16 Jun 2026 14:33:51 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=EA=B5=90=EC=B2=B4=20=EB=B0=8F=20=EB=94=94=EC=9E=90?= =?UTF-8?q?=EC=9D=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- StartupLoginWindow.xaml.cs | 4 +- leak_test_project/App.xaml | 3 +- leak_test_project/App.xaml.cs | 13 +- leak_test_project/MainWindow.xaml | 103 +++- leak_test_project/MainWindow.xaml.cs | 72 ++- .../Services/LoginServicesStub.cs | 295 +++++++++ .../Views/StartupLoginWindow.xaml | 579 ++++++++++++++++++ .../Views/StartupLoginWindow.xaml.cs | 334 ++++++++++ .../Debug/app.publish/leak_test_project.exe | Bin 0 -> 219136 bytes .../bin/Debug/leak_test_project.application | 21 + .../bin/Debug/leak_test_project.exe | Bin 0 -> 219136 bytes .../bin/Debug/leak_test_project.exe.config | 6 + .../bin/Debug/leak_test_project.exe.manifest | 74 +++ .../bin/Debug/leak_test_project.pdb | Bin 0 -> 372224 bytes .../bin/Release/Logs/2026-06-16_system.log | 4 + .../Release/app.publish/leak_test_project.exe | Bin 166400 -> 190464 bytes .../bin/Release/leak_test_project.application | 2 +- .../bin/Release/leak_test_project.exe | Bin 166400 -> 190464 bytes .../Release/leak_test_project.exe.manifest | 12 +- .../bin/Release/leak_test_project.pdb | Bin 310784 -> 357888 bytes leak_test_project/build_error.log | Bin 0 -> 65508 bytes leak_test_project/build_error_utf8.log | 133 ++++ leak_test_project/build_error_utf8_2.log | 133 ++++ leak_test_project/leak_test_project.csproj | 8 + leak_test_project/obj/Debug/App.baml | Bin 0 -> 2557 bytes leak_test_project/obj/Debug/App.g.cs | 79 +++ .../Debug/GeneratedInternalTypeHelper.g.cs | 2 + leak_test_project/obj/Debug/MainWindow.baml | Bin 0 -> 7432 bytes leak_test_project/obj/Debug/MainWindow.g.cs | 191 ++++++ .../obj/Debug/Views/CommunicationWindow.baml | Bin 0 -> 5193 bytes .../obj/Debug/Views/CommunicationWindow.g.cs | 155 +++++ .../obj/Debug/Views/DataView.baml | Bin 0 -> 7807 bytes .../obj/Debug/Views/DataView.g.cs | 93 +++ .../obj/Debug/Views/HomeView.baml | Bin 0 -> 12565 bytes .../obj/Debug/Views/HomeView.g.cs | 75 +++ .../obj/Debug/Views/ParametersWindow.baml | Bin 0 -> 4194 bytes .../obj/Debug/Views/ParametersWindow.g.cs | 133 ++++ .../obj/Debug/Views/StartupLoginWindow.baml | Bin 0 -> 14388 bytes .../obj/Debug/Views/StartupLoginWindow.g.cs | 193 ++++++ .../obj/Debug/leak_test_project.application | 21 + ...est_project.csproj.AssemblyReference.cache | Bin 2355 -> 3244 bytes ...est_project.csproj.CoreCompileInputs.cache | 2 +- ...k_test_project.csproj.FileListAbsolute.txt | 12 +- .../obj/Debug/leak_test_project.exe | Bin 0 -> 219136 bytes .../obj/Debug/leak_test_project.exe.manifest | 74 +++ .../obj/Debug/leak_test_project.g.resources | Bin 0 -> 54736 bytes .../obj/Debug/leak_test_project.pdb | Bin 0 -> 372224 bytes .../obj/Debug/leak_test_project_Content.g.cs | 13 + .../leak_test_project_MarkupCompile.cache | 20 + .../leak_test_project_MarkupCompile.lref | 5 + leak_test_project/obj/Release/App.g.cs | 8 +- leak_test_project/obj/Release/App.g.i.cs | 8 +- ...gnTimeResolveAssemblyReferencesInput.cache | Bin 2825 -> 2735 bytes leak_test_project/obj/Release/MainWindow.baml | Bin 3931 -> 5029 bytes leak_test_project/obj/Release/MainWindow.g.cs | 80 ++- .../obj/Release/MainWindow.g.i.cs | 80 ++- .../obj/Release/Views/StartupLoginWindow.baml | Bin 0 -> 8617 bytes .../obj/Release/Views/StartupLoginWindow.g.cs | 193 ++++++ .../Release/Views/StartupLoginWindow.g.i.cs | 193 ++++++ .../obj/Release/leak_test_project.application | 2 +- ...est_project.csproj.AssemblyReference.cache | Bin 8238 -> 8933 bytes ...est_project.csproj.CoreCompileInputs.cache | 2 +- ...k_test_project.csproj.FileListAbsolute.txt | 2 + .../obj/Release/leak_test_project.exe | Bin 166400 -> 190464 bytes .../Release/leak_test_project.exe.manifest | 12 +- .../obj/Release/leak_test_project.g.resources | Bin 26062 -> 35853 bytes .../obj/Release/leak_test_project.pdb | Bin 310784 -> 357888 bytes .../leak_test_project_MarkupCompile.cache | 6 +- .../leak_test_project_MarkupCompile.i.cache | 6 +- .../leak_test_project_MarkupCompile.i.lref | 4 +- 70 files changed, 3363 insertions(+), 97 deletions(-) create mode 100644 leak_test_project/Services/LoginServicesStub.cs create mode 100644 leak_test_project/Views/StartupLoginWindow.xaml create mode 100644 leak_test_project/Views/StartupLoginWindow.xaml.cs create mode 100644 leak_test_project/bin/Debug/app.publish/leak_test_project.exe create mode 100644 leak_test_project/bin/Debug/leak_test_project.application create mode 100644 leak_test_project/bin/Debug/leak_test_project.exe create mode 100644 leak_test_project/bin/Debug/leak_test_project.exe.config create mode 100644 leak_test_project/bin/Debug/leak_test_project.exe.manifest create mode 100644 leak_test_project/bin/Debug/leak_test_project.pdb create mode 100644 leak_test_project/bin/Release/Logs/2026-06-16_system.log create mode 100644 leak_test_project/build_error.log create mode 100644 leak_test_project/build_error_utf8.log create mode 100644 leak_test_project/build_error_utf8_2.log create mode 100644 leak_test_project/obj/Debug/App.baml create mode 100644 leak_test_project/obj/Debug/App.g.cs create mode 100644 leak_test_project/obj/Debug/GeneratedInternalTypeHelper.g.cs create mode 100644 leak_test_project/obj/Debug/MainWindow.baml create mode 100644 leak_test_project/obj/Debug/MainWindow.g.cs create mode 100644 leak_test_project/obj/Debug/Views/CommunicationWindow.baml create mode 100644 leak_test_project/obj/Debug/Views/CommunicationWindow.g.cs create mode 100644 leak_test_project/obj/Debug/Views/DataView.baml create mode 100644 leak_test_project/obj/Debug/Views/DataView.g.cs create mode 100644 leak_test_project/obj/Debug/Views/HomeView.baml create mode 100644 leak_test_project/obj/Debug/Views/HomeView.g.cs create mode 100644 leak_test_project/obj/Debug/Views/ParametersWindow.baml create mode 100644 leak_test_project/obj/Debug/Views/ParametersWindow.g.cs create mode 100644 leak_test_project/obj/Debug/Views/StartupLoginWindow.baml create mode 100644 leak_test_project/obj/Debug/Views/StartupLoginWindow.g.cs create mode 100644 leak_test_project/obj/Debug/leak_test_project.application create mode 100644 leak_test_project/obj/Debug/leak_test_project.exe create mode 100644 leak_test_project/obj/Debug/leak_test_project.exe.manifest create mode 100644 leak_test_project/obj/Debug/leak_test_project.g.resources create mode 100644 leak_test_project/obj/Debug/leak_test_project.pdb create mode 100644 leak_test_project/obj/Debug/leak_test_project_Content.g.cs create mode 100644 leak_test_project/obj/Debug/leak_test_project_MarkupCompile.cache create mode 100644 leak_test_project/obj/Debug/leak_test_project_MarkupCompile.lref create mode 100644 leak_test_project/obj/Release/Views/StartupLoginWindow.baml create mode 100644 leak_test_project/obj/Release/Views/StartupLoginWindow.g.cs create mode 100644 leak_test_project/obj/Release/Views/StartupLoginWindow.g.i.cs diff --git a/StartupLoginWindow.xaml.cs b/StartupLoginWindow.xaml.cs index 6a73f50..809fd33 100644 --- a/StartupLoginWindow.xaml.cs +++ b/StartupLoginWindow.xaml.cs @@ -1,11 +1,11 @@ using System.Windows; -namespace Housing.Login +namespace leak_test_project.Login { /* * Usage in App.xaml.cs or Program.cs: * - * var loginWindow = new Housing.Login.StartupLoginWindow(); + * var loginWindow = new leak_test_project.Login.StartupLoginWindow(); * if (loginWindow.ShowDialog() != true) * { * return; diff --git a/leak_test_project/App.xaml b/leak_test_project/App.xaml index 5d338e1..e2e809d 100644 --- a/leak_test_project/App.xaml +++ b/leak_test_project/App.xaml @@ -3,8 +3,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:leak_test_project" xmlns:views="clr-namespace:leak_test_project.Views" - xmlns:viewmodels="clr-namespace:leak_test_project.ViewModels" - StartupUri="MainWindow.xaml"> + xmlns:viewmodels="clr-namespace:leak_test_project.ViewModels"> diff --git a/leak_test_project/App.xaml.cs b/leak_test_project/App.xaml.cs index c939dbc..569508d 100644 --- a/leak_test_project/App.xaml.cs +++ b/leak_test_project/App.xaml.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Configuration; using System.Data; @@ -13,5 +13,16 @@ namespace leak_test_project /// public partial class App : Application { + public static Housing.Login.StartupLoginWindowResult LoginResult { get; set; } + + protected override void OnStartup(StartupEventArgs e) + { + base.OnStartup(e); + + // 메인 윈도우 생성 및 즉시 표시 + var mainWindow = new MainWindow(); + this.MainWindow = mainWindow; + mainWindow.Show(); + } } } diff --git a/leak_test_project/MainWindow.xaml b/leak_test_project/MainWindow.xaml index 87ad759..d34c062 100644 --- a/leak_test_project/MainWindow.xaml +++ b/leak_test_project/MainWindow.xaml @@ -4,12 +4,47 @@ xmlns:viewmodels="clr-namespace:leak_test_project.ViewModels" Title="Pressure Leak Inspect System" Height="800" Width="1024" MinWidth="1150" MinHeight="700" - WindowState="Maximized" Background="#F0F0F0" WindowStyle="None"> + WindowState="Maximized" Background="#F0F0F0" WindowStyle="None" + ContentRendered="Window_ContentRendered"> + + + + + @@ -17,41 +52,59 @@ - - + + - - - + + + + - - - - + + + + + + + + - - + + + + + + - + + + + + + + + + + - - + + + + + + diff --git a/leak_test_project/MainWindow.xaml.cs b/leak_test_project/MainWindow.xaml.cs index d27beca..718bfa9 100644 --- a/leak_test_project/MainWindow.xaml.cs +++ b/leak_test_project/MainWindow.xaml.cs @@ -9,23 +9,93 @@ namespace leak_test_project public partial class MainWindow : Window { private DispatcherTimer _timer; + private bool _isClosingForcefully = false; public MainWindow() { InitializeComponent(); StartClock(); + DisplayLoginInfo(); this.Closing += MainWindow_Closing; } + private void Window_ContentRendered(object sender, EventArgs e) + { + ShowLoginDialog(); + } + + private void ShowLoginDialog() + { + try + { + var loginWindow = new Housing.Login.StartupLoginWindow(); + loginWindow.Owner = this; // 소유자 지정으로 항상 위에 모달 형식으로 띄움 + if (loginWindow.ShowDialog() == true) + { + App.LoginResult = loginWindow.Result; + DisplayLoginInfo(); + } + else + { + _isClosingForcefully = true; + this.Close(); // 로그인 취소 시 프로그램 종료 + } + } + catch (Exception ex) + { + MessageBox.Show(this, $"로그인 로드 실패: {ex.Message}", "오류", MessageBoxButton.OK, MessageBoxImage.Error); + _isClosingForcefully = true; + this.Close(); + } + } + + private void DisplayLoginInfo() + { + if (App.LoginResult != null) + { + txtOperatorInfo.Text = App.LoginResult.Operator; + txtModelInfo.Text = App.LoginResult.Model; + txtLineInfo.Text = App.LoginResult.LineNo; + } + else + { + txtOperatorInfo.Text = "---"; + txtModelInfo.Text = "---"; + txtLineInfo.Text = "---"; + } + } + + private void BtnLogout_Click(object sender, RoutedEventArgs e) + { + var result = MessageBox.Show(this, "로그아웃 하시겠습니까?", "로그아웃", MessageBoxButton.YesNo, MessageBoxImage.Question); + if (result == MessageBoxResult.Yes) + { + App.LoginResult = null; + DisplayLoginInfo(); + ShowLoginDialog(); + } + } + private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e) { - var result = MessageBox.Show("프로그램을 종료하시겠습니까?", "종료 확인", MessageBoxButton.YesNo, MessageBoxImage.Question); + if (_isClosingForcefully) + { + DisposeResources(); + return; + } + + var result = MessageBox.Show(this, "프로그램을 종료하시겠습니까?", "종료 확인", MessageBoxButton.YesNo, MessageBoxImage.Question); if (result == MessageBoxResult.No) { e.Cancel = true; return; } + DisposeResources(); + } + + private void DisposeResources() + { // ViewModel의 Dispose를 호출하여 모든 통신 연결 해제 if (this.DataContext is IDisposable disposable) { diff --git a/leak_test_project/Services/LoginServicesStub.cs b/leak_test_project/Services/LoginServicesStub.cs new file mode 100644 index 0000000..f0f945c --- /dev/null +++ b/leak_test_project/Services/LoginServicesStub.cs @@ -0,0 +1,295 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace Housing.Services +{ + // 1. DatabaseSettings + public class DatabaseSettings + { + public string ConnectionString { get; set; } = string.Empty; + + public static DatabaseSettings Load(string path) + { + var settings = new DatabaseSettings(); + try + { + if (File.Exists(path)) + { + var lines = File.ReadAllLines(path); + foreach (var line in lines) + { + var trimmed = line.Trim(); + if (trimmed.StartsWith("ConnectionString", StringComparison.OrdinalIgnoreCase)) + { + var parts = trimmed.Split(new[] { '=' }, 2); + if (parts.Length == 2) + { + settings.ConnectionString = parts[1].Trim(); + } + } + } + } + } + catch + { + // 로드 실패 시 무시 + } + return settings; + } + } + + // 2. LoginSettings + public class LoginSettings + { + public bool OfflinePreview { get; set; } = true; // 기본값은 오프라인 테스트 편의를 위해 true + + public static LoginSettings Load(string path) + { + var settings = new LoginSettings(); + try + { + if (File.Exists(path)) + { + var lines = File.ReadAllLines(path); + foreach (var line in lines) + { + var trimmed = line.Trim(); + if (trimmed.StartsWith("OfflinePreview", StringComparison.OrdinalIgnoreCase)) + { + var parts = trimmed.Split(new[] { '=' }, 2); + if (parts.Length == 2) + { + if (bool.TryParse(parts[1].Trim(), out var val)) + { + settings.OfflinePreview = val; + } + else if (int.TryParse(parts[1].Trim(), out var intVal)) + { + settings.OfflinePreview = intVal != 0; + } + } + } + } + } + } + catch + { + // 로드 실패 시 무시 + } + return settings; + } + } + + // 3. LoginHistoryStore + public class LoginHistoryStore + { + private readonly string _filePath; + private readonly Dictionary> _history = new Dictionary>(StringComparer.OrdinalIgnoreCase); + + private LoginHistoryStore(string filePath) + { + _filePath = filePath; + } + + public static LoginHistoryStore Load(string path) + { + var store = new LoginHistoryStore(path); + store.LoadFromFile(); + return store; + } + + public List GetValues(string fieldName) + { + if (_history.TryGetValue(fieldName, out var list)) + { + return new List(list); + } + return new List(); + } + + public void Remember(string fieldName, string value) + { + if (string.IsNullOrWhiteSpace(value)) return; + value = value.Trim(); + + if (!_history.TryGetValue(fieldName, out var list)) + { + list = new List(); + _history[fieldName] = list; + } + + // 중복 제거 및 맨 앞으로 이동 + list.RemoveAll(x => string.Equals(x, value, StringComparison.OrdinalIgnoreCase)); + list.Insert(0, value); + + // 최대 5개 이력 유지 + if (list.Count > 5) + { + list.RemoveRange(5, list.Count - 5); + } + } + + public bool Forget(string fieldName, string value) + { + if (string.IsNullOrWhiteSpace(value)) return false; + value = value.Trim(); + + if (_history.TryGetValue(fieldName, out var list)) + { + bool removed = list.RemoveAll(x => string.Equals(x, value, StringComparison.OrdinalIgnoreCase)) > 0; + return removed; + } + return false; + } + + public void Save() + { + try + { + var dir = Path.GetDirectoryName(_filePath); + if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir)) + { + Directory.CreateDirectory(dir); + } + + // 초간단 JSON 형식으로 직렬화 (의존성 최소화) + using (var sw = new StreamWriter(_filePath)) + { + sw.WriteLine("{"); + var keys = new List(_history.Keys); + for (int i = 0; i < keys.Count; i++) + { + var key = keys[i]; + var values = _history[key]; + var arrayItems = new List(); + foreach (var val in values) + { + // 따옴표 escape + var escaped = val.Replace("\"", "\\\""); + arrayItems.Add($"\"{escaped}\""); + } + var line = $" \"{key}\": [{string.Join(", ", arrayItems)}]"; + if (i < keys.Count - 1) + { + line += ","; + } + sw.WriteLine(line); + } + sw.WriteLine("}"); + } + } + catch + { + // 저장 오류 시 무시 + } + } + + private void LoadFromFile() + { + try + { + if (!File.Exists(_filePath)) return; + + var text = File.ReadAllText(_filePath); + // 정규식이나 아주 심플한 파싱으로 키와 배열 파싱 + // 형식 예: "Maker": ["MOBI", "TEST"] + var matches = Regex.Matches(text, @"""([^""]+)""\s*:\s*\[([^\]]*)\]"); + foreach (Match match in matches) + { + var key = match.Groups[1].Value; + var arrayContent = match.Groups[2].Value; + var valueMatches = Regex.Matches(arrayContent, @"""([^""]*)"""); + + var list = new List(); + foreach (Match valMatch in valueMatches) + { + list.Add(valMatch.Groups[1].Value); + } + _history[key] = list; + } + } + catch + { + // 로드 오류 시 무시 + } + } + } + + // 4. LoginAccountRepository + public class LoginAccountRepository + { + private readonly DatabaseSettings _dbSettings; + private readonly LoginSettings _loginSettings; + + public LoginAccountRepository(DatabaseSettings dbSettings, LoginSettings loginSettings) + { + _dbSettings = dbSettings; + _loginSettings = loginSettings; + } + + public async Task ExistsAsync(string operatorId, string password) + { + // 오프라인 모드인 경우 기본 성공 + if (_loginSettings.OfflinePreview) + { + return true; + } + + // DB 커넥션 스트링이 비어있으면 기본값 확인 + if (string.IsNullOrWhiteSpace(_dbSettings.ConnectionString)) + { + return IsDefaultAccount(operatorId, password); + } + + try + { + // 실제 DB에서 계정을 조회해보기 위한 시도 + using (var conn = new System.Data.SqlClient.SqlConnection(_dbSettings.ConnectionString)) + { + await conn.OpenAsync(); + + // 우선 Users 또는 Accounts와 같이 로그인 테이블명이 뭔지 모를 수 있으므로 + // 쿼리 실패 시 default 계정 검증으로 fallback 하도록 구성 + const string query = "SELECT COUNT(*) FROM [Users] WHERE [OperatorId] = @Id AND [Password] = @Pw"; + using (var cmd = new System.Data.SqlClient.SqlCommand(query, conn)) + { + cmd.Parameters.AddWithValue("@Id", operatorId); + cmd.Parameters.AddWithValue("@Pw", password); + + var count = (int)await cmd.ExecuteScalarAsync(); + return count > 0; + } + } + } + catch + { + // DB 연결이나 쿼리 오류 발생 시, 테스트 편의를 위해 test/test 혹은 admin/admin 등 디폴트 계정을 제공 + return IsDefaultAccount(operatorId, password); + } + } + + private bool IsDefaultAccount(string operatorId, string password) + { + // 기본 개발/테스트용 계정 처리 + if (string.Equals(operatorId, "test", StringComparison.OrdinalIgnoreCase) && + string.Equals(password, "test", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + if (string.Equals(operatorId, "admin", StringComparison.OrdinalIgnoreCase) && + string.Equals(password, "admin", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + if (string.Equals(operatorId, "su", StringComparison.OrdinalIgnoreCase) && + string.Equals(password, "su", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + return false; + } + } +} diff --git a/leak_test_project/Views/StartupLoginWindow.xaml b/leak_test_project/Views/StartupLoginWindow.xaml new file mode 100644 index 0000000..c5beb11 --- /dev/null +++ b/leak_test_project/Views/StartupLoginWindow.xaml @@ -0,0 +1,579 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +