From 4d1450d2c5f4bb3ce824e19bd4e985543e0ab556 Mon Sep 17 00:00:00 2001 From: chiDT Date: Tue, 12 Aug 2025 19:32:37 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EB=B8=8C=EB=9D=BC=EC=9A=B0=EC=A0=80=20?= =?UTF-8?q?=EC=84=A4=EC=B9=98=20=ED=99=95=EC=9D=B8=20=EB=B0=8F=20=EB=93=9C?= =?UTF-8?q?=EB=9D=BC=EC=9D=B4=EB=B2=84=20=EC=83=9D=EC=84=B1=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 브라우저 설치 여부 확인 메서드 추가 (Chrome, Edge) - TestDriver 메서드를 우선순위 기반 테스트로 개선 (Edge > Chrome) - 드라이버 콘솔창 숨김 기능 추가 (HideCommandPromptWindow) - 웹드라이버 감지 방지 스크립트 안전성 개선 - 관리자 권한 없이도 브라우저 설치 확인 가능 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- unimarc/unimarc/SearchModel/SeleniumHelper.cs | 601 +++++++++++++++--- 1 file changed, 513 insertions(+), 88 deletions(-) diff --git a/unimarc/unimarc/SearchModel/SeleniumHelper.cs b/unimarc/unimarc/SearchModel/SeleniumHelper.cs index ef230f0..c5fb309 100644 --- a/unimarc/unimarc/SearchModel/SeleniumHelper.cs +++ b/unimarc/unimarc/SearchModel/SeleniumHelper.cs @@ -1,4 +1,6 @@ -using OpenQA.Selenium; +using AR; +using BokBonCheck; +using OpenQA.Selenium; using OpenQA.Selenium.Chrome; using OpenQA.Selenium.Chromium; using OpenQA.Selenium.Edge; @@ -19,22 +21,43 @@ namespace UniMarc.SearchModel { public static class SeleniumHelper { - static ChromiumDriver _driver; - static string pathname_userdata = "WebDriverUserData"; public enum eBrowserType { edge, chrome } + + //####### public + public static string DriverPath { get; private set; } public static eBrowserType Browser = eBrowserType.edge; + /// + /// test 혹은 create 가 성공하면 이 값이 True 가 됩니다. + /// + public static bool IsReady { get; private set; } = false; + + /// + /// 사용자데이터 폴더의 기본 경로를 반환합니다. + /// + public static string UserData_BaseDirectory + { + get + { + return System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"WebDriver", "UserData"); + } + } + + /// /// 드라이버파일을 다운로드 하고 완료된 경우 드라이버 실행파일 명을 반환합니다. /// /// MatchingBrowser,Latest /// - public static string Download(string versiontype = "MatchingBrowser") + public static async Task Download(string versiontype = "MatchingBrowser", DownloadProgressForm progressForm = null) { + if (progressForm != null) + progressForm.UpdateProgress(30, $"{Browser} 드라이버 다운로드 중..."); + var dnpath = new System.IO.DirectoryInfo("WebDriver\\Download"); if (dnpath.Exists == false) dnpath.Create(); var drv = new DriverManager(dnpath.FullName); @@ -43,94 +66,260 @@ namespace UniMarc.SearchModel if (Browser == eBrowserType.edge) config = new EdgeConfig(); else if (Browser == eBrowserType.chrome) config = new ChromeConfig(); - return drv.SetUpDriver(config, versiontype); + DriverPath = drv.SetUpDriver(config, versiontype); + if (DriverPath.isEmpty() == false && System.IO.File.Exists(DriverPath)) + { + Console.WriteLine($"드라이버 다운로드 성공: {DriverPath}"); + } + else + { + Console.WriteLine($"드라이버파일이 존재하지 않습니다 파일명:{DriverPath}"); + } + await Task.Delay(1); + return DriverPath; } + + /// + /// 랜덤으로 사용자 폴더를 생성하여 폴더명을 반환합니다 + /// 현재 실행되는 폴더에 WebDriver 폴더를 생성하고, 브라우저 종류와 타임스탬프, 랜덤 ID를 포함한 폴더명을 만듭니다. + /// + /// static string MakeUserDataPath() { string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss_fff"); string randomId = Guid.NewGuid().ToString("N").Substring(0, 8); - var userDataDir = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"WebDriver", $"Edge_{timestamp}_{randomId}"); - + var userDataDir = System.IO.Path.Combine(UserData_BaseDirectory, $"{Browser}_{timestamp}_{randomId}"); var userpath = new System.IO.DirectoryInfo(userDataDir); if (userpath.Exists == false) userpath.Create(); return userpath.FullName; } - public static ChromiumDriver MakeDriver() + + /// + /// 설치된 브라우저를 확인하고 우선순위에 따라 드라이버 생성 테스트를 실행합니다 + /// 우선순위: Edge > Chrome + /// + /// 테스트 성공 여부 + public static async Task TestDriver(DownloadProgressForm progressForm = null) { - ChromiumDriver driver = nul; - var options = MakeBaseOption(); - if (Browser == eBrowserType.edge) + try { - driver = new EdgeDriver((EdgeOptions)options); // Edge 드라이버 초기화 - } - else if (Browser == eBrowserType.chrome) - { - driver = new ChromeDriver((ChromeOptions)options); // Edge 드라이버 초기화 - } - - if( driver != null) - { - // 웹드라이버 감지 방지 - ((IJavaScriptExecutor)_driver).ExecuteScript("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"); - } + // 설치된 브라우저 확인 + bool edgeInstalled = IsEdgeInstalled(); + bool chromeInstalled = IsChromeInstalled(); - return driver; - } - static ChromiumOptions MakeBaseOption() - { - EdgeOptions options = nul; - if (Browser == eBrowserType.edge) - { - options = new EdgeOptions(); - //options.AddArgument($"--user-data-dir={userDataDir}"); - options.AddArgument("--no-first-run"); - options.AddArgument("--no-default-browser-check"); - options.AddArgument("--disable-default-apps"); - options.AddArgument("--disable-popup-blocking"); - options.AddArgument("--disable-translate"); - options.AddArgument("--disable-background-timer-throttling"); - options.AddArgument("--disable-renderer-backgrounding"); - options.AddArgument("--disable-backgrounding-occluded-windows"); - options.AddArgument("--disable-client-side-phishing-detection"); - options.AddArgument("--disable-sync"); - options.AddArgument("--disable-extensions"); - options.AddArgument("--disable-component-extensions-with-background-pages"); - options.AddArgument("--disable-background-networking"); - options.AddArgument("--disable-background-mode"); - options.AddArgument("--no-sandbox"); - options.AddArgument("--disable-dev-shm-usage"); - options.AddArgument("--disable-blink-features=AutomationControlled"); - options.AddArgument("--remote-debugging-port=0"); // 랜덤 포트 사용 + if (progressForm != null) + progressForm.UpdateProgress(10, "설치된 브라우저 확인 중..."); + + // 둘 다 설치되어 있지 않으면 오류 + if (!edgeInstalled && !chromeInstalled) + { + IsReady = false; + throw new Exception("Chrome 또는 Edge 브라우저가 설치되어 있지 않습니다. 브라우저를 설치한 후 다시 시도하세요."); + } + + // Edge 우선 테스트 (설치되어 있는 경우) + if (edgeInstalled) + { + Console.WriteLine("Edge 브라우저로 드라이버 테스트를 시작합니다."); + if (progressForm != null) + progressForm.UpdateProgress(20, "Edge 브라우저 테스트 중..."); + + var originalBrowser = Browser; + Browser = eBrowserType.edge; + + try + { + await Download(progressForm: progressForm); + var options = CreateBaseBrowserOption(true); + options.AddArgument("--log-level=3"); + options.AddArgument("--silent"); + options.AddArgument("--disable-blink-features=AutomationControlled"); + options.AddArgument("--enable-aggressive-domstorage-flushing"); + + var driver = await CreateDriver(options, progressForm); + if (driver != null) + { + driver.Quit(); + driver.Dispose(); + IsReady = true; + Console.WriteLine("Edge 브라우저 드라이버 테스트 성공"); + return true; + } + } + catch (Exception ex) + { + Console.WriteLine($"Edge 브라우저 테스트 실패: {ex.Message}"); + Browser = originalBrowser; + } + } + + // Chrome 테스트 (Edge 실패 시 또는 Edge가 설치되지 않은 경우) + if (chromeInstalled) + { + Console.WriteLine("Chrome 브라우저로 드라이버 테스트를 시작합니다."); + if (progressForm != null) + progressForm.UpdateProgress(60, "Chrome 브라우저 테스트 중..."); + + var originalBrowser = Browser; + Browser = eBrowserType.chrome; + + try + { + await Download(progressForm: progressForm); + var options = CreateBaseBrowserOption(true); + options.AddArgument("--log-level=3"); + options.AddArgument("--silent"); + options.AddArgument("--disable-blink-features=AutomationControlled"); + options.AddArgument("--enable-aggressive-domstorage-flushing"); + + var driver = await CreateDriver(options, progressForm); + if (driver != null) + { + driver.Quit(); + driver.Dispose(); + IsReady = true; + Console.WriteLine("Chrome 브라우저 드라이버 테스트 성공"); + return true; + } + } + catch (Exception ex) + { + Console.WriteLine($"Chrome 브라우저 테스트 실패: {ex.Message}"); + Browser = originalBrowser; + } + } + + // 모든 테스트 실패 + IsReady = false; + throw new Exception("설치된 모든 브라우저에서 드라이버 테스트가 실패했습니다."); } - else if (Browser == eBrowserType.chrome) + catch (Exception ex) { - options = new ChromeOptions(); - //options.AddArgument($"--user-data-dir={userDataDir}"); - options.AddArgument("--no-first-run"); - options.AddArgument("--no-default-browser-check"); - options.AddArgument("--disable-default-apps"); - options.AddArgument("--disable-popup-blocking"); - options.AddArgument("--disable-translate"); - options.AddArgument("--disable-background-timer-throttling"); - options.AddArgument("--disable-renderer-backgrounding"); - options.AddArgument("--disable-backgrounding-occluded-windows"); - options.AddArgument("--disable-client-side-phishing-detection"); - options.AddArgument("--disable-sync"); - options.AddArgument("--disable-extensions"); - options.AddArgument("--disable-component-extensions-with-background-pages"); - options.AddArgument("--disable-background-networking"); - options.AddArgument("--disable-background-mode"); - options.AddArgument("--no-sandbox"); - options.AddArgument("--disable-dev-shm-usage"); - options.AddArgument("--disable-blink-features=AutomationControlled"); - options.AddArgument("--remote-debugging-port=0"); // 랜덤 포트 사용 - + IsReady = false; + Console.WriteLine($"드라이버 생성 테스트 중 오류: {ex.Message}"); + if (progressForm != null) + progressForm.UpdateProgress(100, $"테스트 실패: {ex.Message}"); + + return false; } - options.AddArgument("--window-size=1920,1080"); - return options; } + + public static async Task CreateDriver(ChromiumOptions options = null, DownloadProgressForm progressForm = null) + { + + if (progressForm != null) + progressForm.UpdateProgress(100, $"{Browser} 드라이버 생성 중..."); + + try + { + ChromiumDriver driver = null; + if (options == null) + { + Console.WriteLine("브라우저 옵션을 기본으로 설정합니다"); + options = CreateBaseBrowserOption(); + } + + if (Browser == eBrowserType.edge) + { + // Edge 드라이버 서비스 생성 (콘솔창 숨김) + var edgeService = string.IsNullOrEmpty(DriverPath) ? + EdgeDriverService.CreateDefaultService() : + EdgeDriverService.CreateDefaultService(System.IO.Path.GetDirectoryName(DriverPath)); + + edgeService.HideCommandPromptWindow = true; + edgeService.SuppressInitialDiagnosticInformation = true; + + driver = new EdgeDriver(edgeService, (EdgeOptions)options); + } + else if (Browser == eBrowserType.chrome) + { + // Chrome 드라이버 서비스 생성 (콘솔창 숨김) + var chromeService = string.IsNullOrEmpty(DriverPath) ? + ChromeDriverService.CreateDefaultService() : + ChromeDriverService.CreateDefaultService(System.IO.Path.GetDirectoryName(DriverPath)); + + chromeService.HideCommandPromptWindow = true; + chromeService.SuppressInitialDiagnosticInformation = true; + + driver = new ChromeDriver(chromeService, (ChromeOptions)options); + } + + await Task.Delay(1); + + if (driver != null) + { + // 웹드라이버 감지 방지 (안전한 방법) + try + { + ((IJavaScriptExecutor)driver).ExecuteScript(@" + if (typeof navigator.webdriver !== 'undefined') { + try { + delete navigator.webdriver; + } catch(e) {} + try { + Object.defineProperty(navigator, 'webdriver', { + get: () => undefined, + configurable: true + }); + } catch(e) {} + } + "); + Console.WriteLine($"웹드라이버 감지 방지 스크립트 실행 완료"); + } + catch (Exception ex) + { + Console.WriteLine($"웹드라이버 감지 방지 스크립트 실행 중 오류 (무시됨): {ex.Message}"); + } + await Task.Delay(1); + } + + IsReady = driver != null; + return driver; + } + catch (Exception ex) + { + IsReady = false; + Console.WriteLine($"드라이버 생성 중 오류: {ex.Message}"); + return null; + } + } + static ChromiumOptions CreateBaseBrowserOption(bool hideBrowser = true, int width = 800, int height = 600) + { + ChromiumOptions options = null; + if (Browser == eBrowserType.edge) + { + options = new EdgeOptions(); + } + else if (Browser == eBrowserType.chrome) + { + options = new ChromeOptions(); + } + + //options.AddArgument($"--user-data-dir={userDataDir}"); + options.AddArgument("--no-first-run"); + options.AddArgument("--no-default-browser-check"); + options.AddArgument("--disable-default-apps"); + options.AddArgument("--disable-popup-blocking"); + options.AddArgument("--disable-translate"); + options.AddArgument("--disable-background-timer-throttling"); + options.AddArgument("--disable-renderer-backgrounding"); + options.AddArgument("--disable-backgrounding-occluded-windows"); + options.AddArgument("--disable-client-side-phishing-detection"); + options.AddArgument("--disable-sync"); + options.AddArgument("--disable-extensions"); + options.AddArgument("--disable-component-extensions-with-background-pages"); + options.AddArgument("--disable-background-networking"); + options.AddArgument("--disable-background-mode"); + options.AddArgument("--no-sandbox"); + options.AddArgument("--disable-dev-shm-usage"); + options.AddArgument("--disable-blink-features=AutomationControlled"); + options.AddArgument("--remote-debugging-port=0"); // 랜덤 포트 사용 + if (hideBrowser) options.AddArgument("--headless"); + options.AddArgument($"--window-size={width},{height}"); + return options; + } public static void KillExistingDrivers() { string[] processNames = { }; @@ -163,7 +352,7 @@ namespace UniMarc.SearchModel } // 임시 WebDriver 사용자 데이터 폴더들 정리 - CleanupTempUserDataDirectories(); + ClearDriverCache(); // 프로세스가 완전히 종료될 때까지 대기 Thread.Sleep(2000); @@ -176,28 +365,35 @@ namespace UniMarc.SearchModel } - static void CleanupTempUserDataDirectories() + /// + /// 사용자데이터 폴더를 정리합니다. + /// + static void ClearDriverCache() { try { - var tempPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, pathname_userdata, $"{Browser}"); - string[] directories = System.IO.Directory.GetDirectories(tempPath); - - if (directories.Length > 0) + Console.WriteLine("Chrome 드라이버 캐시 정리 시작..."); + if (System.IO.Directory.Exists(UserData_BaseDirectory)) { - Console.WriteLine($"기존 임시 사용자 데이터 폴더 {directories.Length}개를 정리하는 중..."); - foreach (string dir in directories) + string[] directories = System.IO.Directory.GetDirectories(UserData_BaseDirectory); + + if (directories.Length > 0) { - try + Console.WriteLine($"기존 임시 사용자 데이터 폴더 {directories.Length}개를 정리하는 중..."); + foreach (string dir in directories) { - System.IO.Directory.Delete(dir, true); - } - catch (Exception ex) - { - Console.WriteLine($"폴더 삭제 중 오류: {ex.Message}"); + try + { + System.IO.Directory.Delete(dir, true); + } + catch (Exception ex) + { + Console.WriteLine($"폴더 삭제 중 오류: {ex.Message}"); + } } } } + } catch (Exception ex) { @@ -205,6 +401,25 @@ namespace UniMarc.SearchModel } } + public static void Dispose() + { + try + { + KillExistingDrivers(); + ClearDriverCache(); + } + catch (Exception ex) + { + Console.WriteLine($"Exception-Dispose={ex.Message}"); + } + finally { + IsReady = false; + } + } + + + #region "브라우저별 버젼 확인" + /// /// 현재 설치된 브라우저 버전을 확인합니다. /// @@ -326,5 +541,215 @@ namespace UniMarc.SearchModel return $"Edge version check failed: {ex.Message}"; } } + + #endregion + + #region "브라우저 설치 확인" + + /// + /// Chrome 브라우저가 설치되어 있는지 확인합니다. + /// 관리자 권한 없이도 작동합니다. + /// + /// Chrome 설치 여부 + public static bool IsChromeInstalled() + { + try + { + // 1. 레지스트리 확인 (CurrentUser) + using (var key = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\Google\Chrome\BLBeacon")) + { + if (key != null && key.GetValue("version") != null) + return true; + } + + // 2. 레지스트리 확인 (LocalMachine) - 읽기 권한은 일반 사용자도 가능 + try + { + using (var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Google\Chrome\BLBeacon")) + { + if (key != null && key.GetValue("version") != null) + return true; + } + } + catch + { + // 접근 권한이 없어도 계속 진행 + } + + // 3. WOW6432Node 확인 + try + { + using (var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\WOW6432Node\Google\Chrome\BLBeacon")) + { + if (key != null && key.GetValue("version") != null) + return true; + } + } + catch + { + // 접근 권한이 없어도 계속 진행 + } + + // 4. 기본 설치 경로 확인 + string[] chromePaths = { + @"C:\Program Files\Google\Chrome\Application\chrome.exe", + @"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe", + System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Google\Chrome\Application\chrome.exe") + }; + + foreach (string path in chromePaths) + { + if (System.IO.File.Exists(path)) + return true; + } + + return false; + } + catch (Exception ex) + { + Console.WriteLine($"Chrome 설치 확인 중 오류: {ex.Message}"); + return false; + } + } + + /// + /// Edge 브라우저가 설치되어 있는지 확인합니다. + /// 관리자 권한 없이도 작동합니다. + /// + /// Edge 설치 여부 + public static bool IsEdgeInstalled() + { + try + { + // 1. 레지스트리 확인 (CurrentUser) + using (var key = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Edge\BLBeacon")) + { + if (key != null && key.GetValue("version") != null) + return true; + } + + // 2. 레지스트리 확인 (LocalMachine) - 읽기 권한은 일반 사용자도 가능 + try + { + using (var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Edge\BLBeacon")) + { + if (key != null && key.GetValue("version") != null) + return true; + } + } + catch + { + // 접근 권한이 없어도 계속 진행 + } + + // 3. WOW6432Node 확인 + try + { + using (var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\WOW6432Node\Microsoft\Edge\BLBeacon")) + { + if (key != null && key.GetValue("version") != null) + return true; + } + } + catch + { + // 접근 권한이 없어도 계속 진행 + } + + // 4. 기본 설치 경로 확인 + string[] edgePaths = { + @"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe", + @"C:\Program Files\Microsoft\Edge\Application\msedge.exe" + }; + + foreach (string path in edgePaths) + { + if (System.IO.File.Exists(path)) + return true; + } + + // 5. Windows 10/11의 기본 Edge 확인 (시스템 앱) + try + { + using (var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Classes\MSEdgeHTM")) + { + if (key != null) + return true; + } + } + catch + { + // 접근 권한이 없어도 계속 진행 + } + + return false; + } + catch (Exception ex) + { + Console.WriteLine($"Edge 설치 확인 중 오류: {ex.Message}"); + return false; + } + } + + /// + /// 설치된 브라우저 목록을 반환합니다. + /// + /// 설치된 브라우저 목록 + public static List GetInstalledBrowsers() + { + var installedBrowsers = new List(); + + if (IsChromeInstalled()) + installedBrowsers.Add(eBrowserType.chrome); + + if (IsEdgeInstalled()) + installedBrowsers.Add(eBrowserType.edge); + + return installedBrowsers; + } + + /// + /// 현재 설정된 브라우저가 설치되어 있는지 확인합니다. + /// + /// 현재 브라우저 설치 여부 + public static bool IsBrowserInstalled() + { + switch (Browser) + { + case eBrowserType.chrome: + return IsChromeInstalled(); + case eBrowserType.edge: + return IsEdgeInstalled(); + default: + return false; + } + } + + /// + /// 브라우저 설치 상태 정보를 문자열로 반환합니다. + /// + /// 브라우저 설치 상태 정보 + public static string GetBrowserInstallationStatus() + { + var status = new StringBuilder(); + status.AppendLine("=== 브라우저 설치 상태 ==="); + + bool chromeInstalled = IsChromeInstalled(); + bool edgeInstalled = IsEdgeInstalled(); + + status.AppendLine($"Chrome: {(chromeInstalled ? "설치됨" : "미설치")}"); + if (chromeInstalled) + status.AppendLine($" 버전: {GetChromeVersion()}"); + + status.AppendLine($"Edge: {(edgeInstalled ? "설치됨" : "미설치")}"); + if (edgeInstalled) + status.AppendLine($" 버전: {GetEdgeVersion()}"); + + status.AppendLine($"현재 설정: {Browser} ({(IsBrowserInstalled() ? "사용 가능" : "사용 불가")})"); + + return status.ToString(); + } + + #endregion } }