diff --git a/BokBonCheck.sln b/BokBonCheck.sln
new file mode 100644
index 0000000..20313f8
--- /dev/null
+++ b/BokBonCheck.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.13.35919.96 d17.13
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BokBonCheck", "BokBonCheck\BokBonCheck.csproj", "{BFFF0C63-1231-442F-B800-C21760132E4D}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {BFFF0C63-1231-442F-B800-C21760132E4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BFFF0C63-1231-442F-B800-C21760132E4D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BFFF0C63-1231-442F-B800-C21760132E4D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BFFF0C63-1231-442F-B800-C21760132E4D}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {319FC245-1B17-4BD0-B760-FD95BA4EA2C3}
+ EndGlobalSection
+EndGlobal
diff --git a/BokBonCheck/App.config b/BokBonCheck/App.config
new file mode 100644
index 0000000..193aecc
--- /dev/null
+++ b/BokBonCheck/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/BokBonCheck/BokBonCheck.csproj b/BokBonCheck/BokBonCheck.csproj
new file mode 100644
index 0000000..8ae727b
--- /dev/null
+++ b/BokBonCheck/BokBonCheck.csproj
@@ -0,0 +1,95 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {BFFF0C63-1231-442F-B800-C21760132E4D}
+ WinExe
+ BokBonCheck
+ BokBonCheck
+ v4.8
+ 512
+ true
+ true
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Form
+
+
+
+ Form
+
+
+ Form1.cs
+
+
+
+
+ Form1.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+ Designer
+
+
+ True
+ Resources.resx
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+ True
+ Settings.settings
+ True
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/BokBonCheck/BokBonCheck.sln b/BokBonCheck/BokBonCheck.sln
new file mode 100644
index 0000000..e5a7a34
--- /dev/null
+++ b/BokBonCheck/BokBonCheck.sln
@@ -0,0 +1,24 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.5.2.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BokBonCheck", "BokBonCheck.csproj", "{BEFC3363-0F54-A638-019B-6A76908217A4}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {BEFC3363-0F54-A638-019B-6A76908217A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BEFC3363-0F54-A638-019B-6A76908217A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BEFC3363-0F54-A638-019B-6A76908217A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BEFC3363-0F54-A638-019B-6A76908217A4}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {7A76F894-89FF-4B58-8C94-36A77F1E2B44}
+ EndGlobalSection
+EndGlobal
diff --git a/BokBonCheck/BookSearchService.cs b/BokBonCheck/BookSearchService.cs
new file mode 100644
index 0000000..ce6d99f
--- /dev/null
+++ b/BokBonCheck/BookSearchService.cs
@@ -0,0 +1,71 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using System.Linq;
+
+namespace BokBonCheck
+{
+ public class BookSearchResult
+ {
+ public string SiteName { get; set; }
+ public int BookCount { get; set; }
+ public string SearchTerm { get; set; }
+ public DateTime SearchTime { get; set; }
+ public string ErrorMessage { get; set; }
+ public bool IsSuccess { get; set; }
+ }
+
+ public class BookSearchService
+ {
+ private readonly List _searchers;
+
+ public BookSearchService()
+ {
+ _searchers = new List
+ {
+ new NamguLibrarySearcher()
+ // 나중에 다른 도서관 검색기를 여기에 추가할 수 있습니다
+ };
+ }
+
+ public async Task> SearchBooksAsync(string searchTerm)
+ {
+ var results = new List();
+
+ foreach (var searcher in _searchers)
+ {
+ var result = await searcher.SearchAsync(searchTerm);
+ results.Add(result);
+ }
+
+ return results;
+ }
+
+ public void AddSearcher(ILibrarySearcher searcher)
+ {
+ if (!_searchers.Any(s => s.SiteName == searcher.SiteName))
+ {
+ _searchers.Add(searcher);
+ }
+ }
+
+ public void RemoveSearcher(string siteName)
+ {
+ var searcher = _searchers.FirstOrDefault(s => s.SiteName == siteName);
+ if (searcher != null)
+ {
+ _searchers.Remove(searcher);
+ }
+ }
+
+ public List GetAvailableSites()
+ {
+ return _searchers.Select(s => s.SiteName).ToList();
+ }
+
+ public void ClearSearchers()
+ {
+ _searchers.Clear();
+ }
+ }
+}
\ No newline at end of file
diff --git a/BokBonCheck/ChromeDriverManager.cs b/BokBonCheck/ChromeDriverManager.cs
new file mode 100644
index 0000000..a864b52
--- /dev/null
+++ b/BokBonCheck/ChromeDriverManager.cs
@@ -0,0 +1,378 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Threading.Tasks;
+using WebDriverManager;
+using WebDriverManager.DriverConfigs.Impl;
+using System.Reflection;
+using System.Net.Http;
+using System.Threading;
+using System.IO.Compression;
+
+namespace BokBonCheck
+{
+ public static class ChromeDriverManager
+ {
+ private static string _driverPath = null;
+
+ public static async Task SetupChromeDriverAsync(DownloadProgressForm progressForm = null)
+ {
+ try
+ {
+ // Chrome 버전 확인
+ var chromeVersion = GetChromeVersion();
+ Console.WriteLine($"설치된 Chrome 버전: {chromeVersion}");
+
+ if (progressForm != null)
+ {
+ progressForm.UpdateProgress(10, $"Chrome 버전 확인 중... ({chromeVersion})");
+ }
+
+ // 기존 드라이버 경로 확인
+ var existingDriverPath = GetExistingDriverPath();
+ if (!string.IsNullOrEmpty(existingDriverPath) && File.Exists(existingDriverPath))
+ {
+ Console.WriteLine($"기존 드라이버 발견: {existingDriverPath}");
+
+ // 기존 드라이버 테스트
+ if (await TestExistingDriver(existingDriverPath))
+ {
+ if (progressForm != null)
+ {
+ progressForm.UpdateProgress(100, "기존 드라이버 사용");
+ progressForm.SetCompleted("기존 드라이버를 사용합니다.");
+ }
+
+ _driverPath = existingDriverPath;
+ Environment.SetEnvironmentVariable("webdriver.chrome.driver", existingDriverPath);
+ return existingDriverPath;
+ }
+ else
+ {
+ Console.WriteLine("기존 드라이버 테스트 실패 - 새로 다운로드");
+ // 기존 드라이버가 작동하지 않으면 삭제
+ try
+ {
+ File.Delete(existingDriverPath);
+ Console.WriteLine("기존 드라이버 삭제됨");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"기존 드라이버 삭제 실패: {ex.Message}");
+ }
+ }
+ }
+
+ if (progressForm != null)
+ {
+ progressForm.UpdateProgress(30, "Chrome 버전에 맞는 드라이버 다운로드 중...");
+ }
+
+ // WebDriverManager를 사용하여 설치된 Chrome 버전에 맞는 드라이버 다운로드
+ var driverManager = new DriverManager();
+ var driverPath = driverManager.SetUpDriver(new ChromeConfig(), "MatchingBrowser");
+
+ // 다운로드된 드라이버 경로 저장
+ _driverPath = driverPath;
+
+ // 환경 변수 설정
+ Environment.SetEnvironmentVariable("webdriver.chrome.driver", driverPath);
+
+ if (progressForm != null)
+ {
+ progressForm.UpdateProgress(100, "드라이버 다운로드 완료");
+ progressForm.SetCompleted("드라이버 다운로드 완료!");
+ }
+
+ Console.WriteLine($"ChromeDriver 경로: {driverPath}");
+ Console.WriteLine($"환경 변수 설정: webdriver.chrome.driver = {driverPath}");
+
+ return driverPath;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"ChromeDriver 설정 오류: {ex.Message}");
+ if (progressForm != null)
+ {
+ progressForm.SetError($"설정 오류: {ex.Message}");
+ }
+ throw;
+ }
+ }
+
+ private static string GetExistingDriverPath()
+ {
+ try
+ {
+ // 환경 변수에서 확인
+ var envPath = Environment.GetEnvironmentVariable("webdriver.chrome.driver");
+ if (!string.IsNullOrEmpty(envPath) && File.Exists(envPath))
+ {
+ return envPath;
+ }
+
+ // WebDriverManager 캐시 폴더에서 확인
+ var cachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
+ ".cache", "selenium");
+ if (Directory.Exists(cachePath))
+ {
+ var chromeDriverFiles = Directory.GetFiles(cachePath, "chromedriver*", SearchOption.AllDirectories);
+ foreach (var file in chromeDriverFiles)
+ {
+ if (file.EndsWith(".exe") || !Path.HasExtension(file))
+ {
+ return file;
+ }
+ }
+ }
+
+ return null;
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
+ public static string GetDriverPath()
+ {
+ return _driverPath;
+ }
+
+ private static string GetChromeVersion()
+ {
+ try
+ {
+ // Windows에서 Chrome 설치 경로 확인
+ var chromePaths = new[]
+ {
+ @"C:\Program Files\Google\Chrome\Application\chrome.exe",
+ @"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe",
+ Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
+ @"Google\Chrome\Application\chrome.exe")
+ };
+
+ foreach (var path in chromePaths)
+ {
+ if (File.Exists(path))
+ {
+ var versionInfo = FileVersionInfo.GetVersionInfo(path);
+ var version = versionInfo.FileVersion;
+ Console.WriteLine($"Chrome 버전 감지: {version} (경로: {path})");
+ return version;
+ }
+ }
+
+ return "알 수 없음";
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Chrome 버전 감지 실패: {ex.Message}");
+ return "확인 실패";
+ }
+ }
+
+ public static bool IsChromeInstalled()
+ {
+ try
+ {
+ var chromePaths = new[]
+ {
+ @"C:\Program Files\Google\Chrome\Application\chrome.exe",
+ @"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe",
+ Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
+ @"Google\Chrome\Application\chrome.exe")
+ };
+
+ foreach (var path in chromePaths)
+ {
+ if (File.Exists(path))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ public static async Task TestChromeDriverAsync(DownloadProgressForm progressForm = null)
+ {
+ try
+ {
+ var driverPath = await SetupChromeDriverAsync(progressForm);
+
+ if (progressForm != null)
+ {
+ progressForm.UpdateProgress(50, "드라이버 테스트 중...");
+ }
+
+ // ChromeDriver 서비스 설정
+ var service = OpenQA.Selenium.Chrome.ChromeDriverService.CreateDefaultService(Path.GetDirectoryName(driverPath));
+ service.HideCommandPromptWindow = true;
+ // 간단한 테스트 실행
+ var options = new OpenQA.Selenium.Chrome.ChromeOptions();
+ options.AddArgument("--headless");
+ options.AddArgument("--no-sandbox");
+ options.AddArgument("--disable-dev-shm-usage");
+ options.AddArgument("--disable-gpu");
+ options.AddArgument("--remote-debugging-port=0");
+
+ using (var driver = new OpenQA.Selenium.Chrome.ChromeDriver(service, options))
+ {
+ driver.Navigate().GoToUrl("https://www.google.com");
+ var title = driver.Title;
+
+ Console.WriteLine($"드라이버 테스트 성공: {title}");
+
+ if (progressForm != null)
+ {
+ progressForm.UpdateProgress(100, "드라이버 테스트 성공!");
+ progressForm.SetCompleted("드라이버 테스트 성공!");
+ }
+
+ return !string.IsNullOrEmpty(title);
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"ChromeDriver 테스트 실패: {ex.Message}");
+ if (progressForm != null)
+ {
+ progressForm.SetError($"테스트 실패: {ex.Message}");
+ }
+ return false;
+ }
+ }
+
+ public static void ClearDriverCache()
+ {
+ try
+ {
+ Console.WriteLine("Chrome 드라이버 캐시 정리 시작...");
+
+ // WebDriverManager 캐시 폴더 정리
+ var cachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
+ ".cache", "selenium");
+ if (Directory.Exists(cachePath))
+ {
+ Directory.Delete(cachePath, true);
+ Console.WriteLine($"WebDriverManager 캐시 정리됨: {cachePath}");
+ }
+
+ // 환경 변수에서 설정된 드라이버 경로도 정리
+ var envDriverPath = Environment.GetEnvironmentVariable("webdriver.chrome.driver");
+ if (!string.IsNullOrEmpty(envDriverPath) && File.Exists(envDriverPath))
+ {
+ try
+ {
+ File.Delete(envDriverPath);
+ Console.WriteLine($"환경 변수 드라이버 삭제됨: {envDriverPath}");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"환경 변수 드라이버 삭제 실패: {ex.Message}");
+ }
+ }
+
+ // 저장된 드라이버 경로도 정리
+ if (!string.IsNullOrEmpty(_driverPath) && File.Exists(_driverPath))
+ {
+ try
+ {
+ File.Delete(_driverPath);
+ Console.WriteLine($"저장된 드라이버 삭제됨: {_driverPath}");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"저장된 드라이버 삭제 실패: {ex.Message}");
+ }
+ }
+
+ _driverPath = null;
+ Environment.SetEnvironmentVariable("webdriver.chrome.driver", null);
+
+ Console.WriteLine("Chrome 드라이버 캐시 정리 완료");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"캐시 정리 실패: {ex.Message}");
+ }
+ }
+
+ public static bool IsDriverReady()
+ {
+ try
+ {
+ var driverPath = GetExistingDriverPath();
+ if (string.IsNullOrEmpty(driverPath) || !File.Exists(driverPath))
+ {
+ Console.WriteLine("기존 드라이버가 없습니다.");
+ return false;
+ }
+
+ Console.WriteLine($"드라이버 준비 상태 확인: {driverPath}");
+
+ // 간단한 테스트 실행
+ var service = OpenQA.Selenium.Chrome.ChromeDriverService.CreateDefaultService(Path.GetDirectoryName(driverPath));
+ service.HideCommandPromptWindow = true;
+ var options = new OpenQA.Selenium.Chrome.ChromeOptions();
+ options.AddArgument("--headless");
+ options.AddArgument("--no-sandbox");
+ options.AddArgument("--disable-dev-shm-usage");
+ options.AddArgument("--disable-gpu");
+ options.AddArgument("--remote-debugging-port=0");
+
+ using (var driver = new OpenQA.Selenium.Chrome.ChromeDriver(service, options))
+ {
+ driver.Navigate().GoToUrl("https://www.google.com");
+ var result = !string.IsNullOrEmpty(driver.Title);
+ Console.WriteLine($"드라이버 준비 상태: {(result ? "준비됨" : "실패")}");
+ return result;
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"드라이버 준비 상태 확인 실패: {ex.Message}");
+ return false;
+ }
+ }
+
+ private static async Task TestExistingDriver(string driverPath)
+ {
+ try
+ {
+ Console.WriteLine($"기존 드라이버 테스트: {driverPath}");
+
+ // ChromeDriver 서비스 설정
+ var service = OpenQA.Selenium.Chrome.ChromeDriverService.CreateDefaultService(Path.GetDirectoryName(driverPath));
+ service.HideCommandPromptWindow = true;
+
+ // 간단한 테스트 실행
+ var options = new OpenQA.Selenium.Chrome.ChromeOptions();
+ options.AddArgument("--headless");
+ options.AddArgument("--no-sandbox");
+ options.AddArgument("--disable-dev-shm-usage");
+ options.AddArgument("--disable-gpu");
+ options.AddArgument("--remote-debugging-port=0");
+
+ using (var driver = new OpenQA.Selenium.Chrome.ChromeDriver(service, options))
+ {
+ driver.Navigate().GoToUrl("https://www.google.com");
+ var title = driver.Title;
+ Console.WriteLine($"드라이버 테스트 성공: {title}");
+ return !string.IsNullOrEmpty(title);
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"기존 드라이버 테스트 실패: {ex.Message}");
+ return false;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/BokBonCheck/DownloadProgressForm.cs b/BokBonCheck/DownloadProgressForm.cs
new file mode 100644
index 0000000..0d54622
--- /dev/null
+++ b/BokBonCheck/DownloadProgressForm.cs
@@ -0,0 +1,142 @@
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+using System.Threading.Tasks;
+
+namespace BokBonCheck
+{
+ public partial class DownloadProgressForm : Form
+ {
+ private ProgressBar progressBar;
+ private Label lblStatus;
+ private Label lblProgress;
+ private Button btnCancel;
+ private bool isCancelled = false;
+
+ public DownloadProgressForm()
+ {
+ InitializeComponent();
+ this.StartPosition = FormStartPosition.CenterScreen;
+ }
+
+ private void InitializeComponent()
+ {
+ this.Text = "Chrome 드라이버 다운로드";
+ this.Size = new Size(400, 150);
+ this.StartPosition = FormStartPosition.CenterParent;
+ this.FormBorderStyle = FormBorderStyle.FixedDialog;
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.ControlBox = false;
+ this.TopMost = true;
+
+ // 상태 라벨
+ lblStatus = new Label
+ {
+ Text = "Chrome 드라이버를 확인하고 있습니다...",
+ Location = new Point(20, 20),
+ Size = new Size(360, 20),
+ Font = new Font("맑은 고딕", 9),
+ TextAlign = ContentAlignment.MiddleCenter
+ };
+
+ // 진행률 라벨
+ lblProgress = new Label
+ {
+ Text = "0%",
+ Location = new Point(20, 50),
+ Size = new Size(360, 20),
+ Font = new Font("맑은 고딕", 10, FontStyle.Bold),
+ TextAlign = ContentAlignment.MiddleCenter,
+ ForeColor = Color.Blue
+ };
+
+ // 프로그레스 바
+ progressBar = new ProgressBar
+ {
+ Location = new Point(20, 80),
+ Size = new Size(360, 25),
+ Style = ProgressBarStyle.Continuous,
+ Minimum = 0,
+ Maximum = 100,
+ Value = 0
+ };
+
+ // 취소 버튼
+ btnCancel = new Button
+ {
+ Text = "취소",
+ Location = new Point(150, 115),
+ Size = new Size(100, 30),
+ Font = new Font("맑은 고딕", 9),
+ BackColor = Color.LightCoral
+ };
+
+ btnCancel.Click += BtnCancel_Click;
+
+ // 컨트롤 추가
+ this.Controls.AddRange(new Control[]
+ {
+ lblStatus, lblProgress, progressBar, btnCancel
+ });
+ }
+
+ private void BtnCancel_Click(object sender, EventArgs e)
+ {
+ isCancelled = true;
+ btnCancel.Enabled = false;
+ lblStatus.Text = "취소 중...";
+ }
+
+ public bool IsCancelled => isCancelled;
+
+ public void UpdateProgress(int percentage, string status = null)
+ {
+ if (this.InvokeRequired)
+ {
+ this.Invoke(new Action(UpdateProgress), percentage, status);
+ return;
+ }
+
+ progressBar.Value = Math.Min(percentage, 100);
+ lblProgress.Text = $"{percentage}%";
+
+ if (!string.IsNullOrEmpty(status))
+ {
+ lblStatus.Text = status;
+ }
+ }
+
+ public void SetCompleted(string message = "다운로드 완료!")
+ {
+ if (this.InvokeRequired)
+ {
+ this.Invoke(new Action(SetCompleted), message);
+ return;
+ }
+
+ progressBar.Value = 100;
+ lblProgress.Text = "100%";
+ lblStatus.Text = message;
+ btnCancel.Text = "확인";
+ btnCancel.BackColor = Color.LightGreen;
+ btnCancel.Enabled = true;
+ }
+
+ public void SetError(string errorMessage)
+ {
+ if (this.InvokeRequired)
+ {
+ this.Invoke(new Action(SetError), errorMessage);
+ return;
+ }
+
+ lblStatus.Text = "오류 발생";
+ lblProgress.Text = "실패";
+ lblProgress.ForeColor = Color.Red;
+ btnCancel.Text = "확인";
+ btnCancel.BackColor = Color.LightCoral;
+ btnCancel.Enabled = true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/BokBonCheck/Form1.Designer.cs b/BokBonCheck/Form1.Designer.cs
new file mode 100644
index 0000000..8221c59
--- /dev/null
+++ b/BokBonCheck/Form1.Designer.cs
@@ -0,0 +1,47 @@
+namespace BokBonCheck
+{
+ partial class Form1
+ {
+ ///
+ /// 필수 디자이너 변수입니다.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// 사용 중인 모든 리소스를 정리합니다.
+ ///
+ /// 관리되는 리소스를 삭제해야 하면 true이고, 그렇지 않으면 false입니다.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form 디자이너에서 생성한 코드
+
+ ///
+ /// 디자이너 지원에 필요한 메서드입니다.
+ /// 이 메서드의 내용을 코드 편집기로 수정하지 마세요.
+ ///
+ private void InitializeComponent()
+ {
+ this.SuspendLayout();
+ //
+ // Form1
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(800, 450);
+ this.Name = "Form1";
+ this.Text = "Form1";
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+ }
+}
+
diff --git a/BokBonCheck/Form1.cs b/BokBonCheck/Form1.cs
new file mode 100644
index 0000000..1fdd45c
--- /dev/null
+++ b/BokBonCheck/Form1.cs
@@ -0,0 +1,411 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace BokBonCheck
+{
+ public partial class Form1 : Form
+ {
+ private readonly BookSearchService _searchService;
+ private bool _isSearching = false;
+ private bool _isDriverReady = false;
+
+ public Form1()
+ {
+ InitializeComponent();
+ _searchService = new BookSearchService();
+ InitializeUI();
+ InitializeChromeDriver();
+ }
+
+ private async void InitializeChromeDriver()
+ {
+ var lblStatus = (Label)this.Controls["lblStatus"];
+ lblStatus.Text = "Chrome 드라이버 확인 중...";
+ lblStatus.ForeColor = Color.Blue;
+
+ try
+ {
+ // Chrome 설치 확인
+ if (!ChromeDriverManager.IsChromeInstalled())
+ {
+ MessageBox.Show("Google Chrome이 설치되어 있지 않습니다. Chrome을 설치한 후 프로그램을 다시 실행해주세요.",
+ "Chrome 필요", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ lblStatus.Text = "Chrome이 설치되지 않음";
+ lblStatus.ForeColor = Color.Red;
+ return;
+ }
+
+ // 기존 드라이버가 준비되어 있는지 확인
+ if (ChromeDriverManager.IsDriverReady())
+ {
+ lblStatus.Text = "Chrome 드라이버 준비 완료 - 검색할 준비가 되었습니다.";
+ lblStatus.ForeColor = Color.Green;
+
+ var btnSearch = (Button)this.Controls["btnSearch"];
+ btnSearch.Enabled = true;
+ _isDriverReady = true;
+ return;
+ }
+
+ // 드라이버가 없거나 작동하지 않으면 다운로드 진행 창 표시
+ using (var progressForm = new DownloadProgressForm())
+ {
+ progressForm.Show();
+
+ try
+ {
+ // ChromeDriver 설정
+ await ChromeDriverManager.SetupChromeDriverAsync(progressForm);
+
+ // 드라이버 테스트
+ var isWorking = await ChromeDriverManager.TestChromeDriverAsync(progressForm);
+
+ if (isWorking)
+ {
+ _isDriverReady = true;
+ lblStatus.Text = "Chrome 드라이버 준비 완료 - 검색할 준비가 되었습니다.";
+ lblStatus.ForeColor = Color.Green;
+
+ var btnSearch = (Button)this.Controls["btnSearch"];
+ btnSearch.Enabled = true;
+ }
+ else
+ {
+ lblStatus.Text = "Chrome 드라이버 테스트 실패";
+ lblStatus.ForeColor = Color.Red;
+ MessageBox.Show("Chrome 드라이버 테스트에 실패했습니다. 인터넷 연결을 확인하고 프로그램을 다시 실행해주세요.",
+ "드라이버 오류", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ }
+ catch (OperationCanceledException)
+ {
+ lblStatus.Text = "드라이버 다운로드가 취소되었습니다.";
+ lblStatus.ForeColor = Color.Orange;
+ return;
+ }
+ catch (Exception ex)
+ {
+ lblStatus.Text = "Chrome 드라이버 설정 실패";
+ lblStatus.ForeColor = Color.Red;
+ MessageBox.Show($"Chrome 드라이버 설정 중 오류가 발생했습니다: {ex.Message}",
+ "설정 오류", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ return;
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ lblStatus.Text = "Chrome 드라이버 설정 실패";
+ lblStatus.ForeColor = Color.Red;
+ MessageBox.Show($"Chrome 드라이버 설정 중 오류가 발생했습니다: {ex.Message}",
+ "설정 오류", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ }
+
+ private void InitializeUI()
+ {
+ // 폼 설정
+ this.Text = "도서 검색 프로그램";
+ this.Size = new Size(820, 600);
+ this.StartPosition = FormStartPosition.CenterScreen;
+
+ // 검색 입력 영역
+ var lblSearch = new Label
+ {
+ Text = "도서명을 입력하세요:",
+ Location = new Point(20, 20),
+ Size = new Size(150, 25),
+ Font = new Font("맑은 고딕", 10)
+ };
+
+ var txtSearch = new TextBox
+ {
+ Name = "txtSearch",
+ Location = new Point(20, 50),
+ Size = new Size(300, 25),
+ Font = new Font("맑은 고딕", 10)
+ };
+
+ var btnSearch = new Button
+ {
+ Name = "btnSearch",
+ Text = "검색",
+ Location = new Point(340, 50),
+ Size = new Size(80, 25),
+ Font = new Font("맑은 고딕", 10),
+ BackColor = Color.LightBlue,
+ Enabled = false // 초기에는 비활성화
+ };
+
+ var btnClear = new Button
+ {
+ Name = "btnClear",
+ Text = "초기화",
+ Location = new Point(430, 50),
+ Size = new Size(80, 25),
+ Font = new Font("맑은 고딕", 10),
+ BackColor = Color.LightGray
+ };
+
+ var btnResetDriver = new Button
+ {
+ Name = "btnResetDriver",
+ Text = "드라이버 재설정",
+ Location = new Point(520, 50),
+ Size = new Size(100, 25),
+ Font = new Font("맑은 고딕", 9),
+ BackColor = Color.LightYellow
+ };
+
+ // 진행 상황 표시
+ var progressBar = new ProgressBar
+ {
+ Name = "progressBar",
+ Location = new Point(20, 90),
+ Size = new Size(600, 20),
+ Visible = false
+ };
+
+ var lblStatus = new Label
+ {
+ Name = "lblStatus",
+ Text = "Chrome 드라이버 설정 중...",
+ Location = new Point(20, 120),
+ Size = new Size(490, 25),
+ Font = new Font("맑은 고딕", 9),
+ ForeColor = Color.Blue
+ };
+
+ // 결과 표시 영역
+ var lblResults = new Label
+ {
+ Text = "검색 결과:",
+ Location = new Point(20, 160),
+ Size = new Size(150, 25),
+ Font = new Font("맑은 고딕", 10, FontStyle.Bold)
+ };
+
+ var dataGridView = new DataGridView
+ {
+ Name = "dataGridView",
+ Location = new Point(20, 190),
+ Size = new Size(760, 350),
+ AllowUserToAddRows = false,
+ AllowUserToDeleteRows = false,
+ ReadOnly = true,
+ SelectionMode = DataGridViewSelectionMode.FullRowSelect,
+ AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill,
+ BackgroundColor = Color.White
+ };
+
+ // 컨트롤 추가
+ this.Controls.AddRange(new Control[]
+ {
+ lblSearch, txtSearch, btnSearch, btnClear, btnResetDriver,
+ progressBar, lblStatus, lblResults, dataGridView
+ });
+
+ // 이벤트 핸들러 연결
+ btnSearch.Click += BtnSearch_Click;
+ btnClear.Click += BtnClear_Click;
+ btnResetDriver.Click += BtnResetDriver_Click;
+ txtSearch.KeyPress += TxtSearch_KeyPress;
+
+ // DataGridView 설정
+ SetupDataGridView();
+ }
+
+ private void SetupDataGridView()
+ {
+ var dataGridView = (DataGridView)this.Controls["dataGridView"];
+
+ dataGridView.Columns.Clear();
+ dataGridView.Columns.Add("SiteName", "도서관");
+ dataGridView.Columns.Add("BookCount", "도서 수");
+ dataGridView.Columns.Add("SearchTime", "검색 시간");
+ dataGridView.Columns.Add("Status", "상태");
+
+ // 컬럼 너비 설정
+ dataGridView.Columns["SiteName"].Width = 150;
+ dataGridView.Columns["BookCount"].Width = 100;
+ dataGridView.Columns["SearchTime"].Width = 150;
+ dataGridView.Columns["Status"].Width = 100;
+ }
+
+ private async void BtnSearch_Click(object sender, EventArgs e)
+ {
+ if (_isSearching || !_isDriverReady) return;
+
+ var txtSearch = (TextBox)this.Controls["txtSearch"];
+ var btnSearch = (Button)this.Controls["btnSearch"];
+ var progressBar = (ProgressBar)this.Controls["progressBar"];
+ var lblStatus = (Label)this.Controls["lblStatus"];
+ var dataGridView = (DataGridView)this.Controls["dataGridView"];
+
+ if (string.IsNullOrWhiteSpace(txtSearch.Text))
+ {
+ MessageBox.Show("도서명을 입력해주세요.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ return;
+ }
+
+ _isSearching = true;
+ btnSearch.Enabled = false;
+ progressBar.Visible = true;
+ progressBar.Style = ProgressBarStyle.Marquee;
+ lblStatus.Text = "검색 중...";
+ lblStatus.ForeColor = Color.Blue;
+
+ try
+ {
+ // 기존 결과 초기화
+ dataGridView.Rows.Clear();
+
+ // 검색 실행
+ var results = await _searchService.SearchBooksAsync(txtSearch.Text.Trim());
+
+ // 결과 표시
+ foreach (var result in results)
+ {
+ var row = new object[]
+ {
+ result.SiteName,
+ result.IsSuccess ? result.BookCount.ToString() : "오류",
+ result.SearchTime.ToString("yyyy-MM-dd HH:mm:ss"),
+ result.IsSuccess ? "성공" : "실패"
+ };
+
+ dataGridView.Rows.Add(row);
+
+ // 실패한 경우 오류 메시지 표시
+ if (!result.IsSuccess)
+ {
+ MessageBox.Show($"{result.SiteName}: {result.ErrorMessage}", "검색 오류", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ }
+
+ lblStatus.Text = $"검색 완료 - {results.Count}개 사이트 검색됨";
+ lblStatus.ForeColor = Color.Green;
+ }
+ catch (Exception ex)
+ {
+ lblStatus.Text = "검색 중 오류가 발생했습니다.";
+ lblStatus.ForeColor = Color.Red;
+ MessageBox.Show($"검색 중 오류가 발생했습니다: {ex.Message}", "오류", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ finally
+ {
+ _isSearching = false;
+ btnSearch.Enabled = _isDriverReady;
+ progressBar.Visible = false;
+ }
+ }
+
+ private void BtnClear_Click(object sender, EventArgs e)
+ {
+ var txtSearch = (TextBox)this.Controls["txtSearch"];
+ var dataGridView = (DataGridView)this.Controls["dataGridView"];
+ var lblStatus = (Label)this.Controls["lblStatus"];
+
+ txtSearch.Clear();
+ dataGridView.Rows.Clear();
+
+ if (_isDriverReady)
+ {
+ lblStatus.Text = "검색할 준비가 되었습니다.";
+ lblStatus.ForeColor = Color.Green;
+ }
+ else
+ {
+ lblStatus.Text = "Chrome 드라이버 설정 실패";
+ lblStatus.ForeColor = Color.Red;
+ }
+ }
+
+ private async void BtnResetDriver_Click(object sender, EventArgs e)
+ {
+ var btnResetDriver = (Button)sender;
+ var lblStatus = (Label)this.Controls["lblStatus"];
+
+ btnResetDriver.Enabled = false;
+ lblStatus.Text = "드라이버 재설정 중...";
+ lblStatus.ForeColor = Color.Blue;
+
+ try
+ {
+ // 강제로 캐시 정리 (재설정이므로)
+ ChromeDriverManager.ClearDriverCache();
+
+ // 다운로드 진행 창 표시
+ using (var progressForm = new DownloadProgressForm())
+ {
+ progressForm.Text = "Chrome 드라이버 재설정";
+ progressForm.Show();
+
+ try
+ {
+ // 드라이버 재설정
+ await ChromeDriverManager.SetupChromeDriverAsync(progressForm);
+
+ // 테스트
+ var isWorking = await ChromeDriverManager.TestChromeDriverAsync(progressForm);
+
+ if (isWorking)
+ {
+ _isDriverReady = true;
+ lblStatus.Text = "드라이버 재설정 완료 - 검색할 준비가 되었습니다.";
+ lblStatus.ForeColor = Color.Green;
+
+ var btnSearch = (Button)this.Controls["btnSearch"];
+ btnSearch.Enabled = true;
+ }
+ else
+ {
+ lblStatus.Text = "드라이버 재설정 실패";
+ lblStatus.ForeColor = Color.Red;
+ MessageBox.Show("드라이버 재설정에 실패했습니다.", "재설정 실패", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ }
+ catch (OperationCanceledException)
+ {
+ lblStatus.Text = "드라이버 재설정이 취소되었습니다.";
+ lblStatus.ForeColor = Color.Orange;
+ return;
+ }
+ catch (Exception ex)
+ {
+ lblStatus.Text = "드라이버 재설정 중 오류 발생";
+ lblStatus.ForeColor = Color.Red;
+ MessageBox.Show($"드라이버 재설정 중 오류가 발생했습니다: {ex.Message}", "오류", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ return;
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ lblStatus.Text = "드라이버 재설정 중 오류 발생";
+ lblStatus.ForeColor = Color.Red;
+ MessageBox.Show($"드라이버 재설정 중 오류가 발생했습니다: {ex.Message}", "오류", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ finally
+ {
+ btnResetDriver.Enabled = true;
+ }
+ }
+
+ private void TxtSearch_KeyPress(object sender, KeyPressEventArgs e)
+ {
+ if (e.KeyChar == (char)Keys.Enter)
+ {
+ e.Handled = true;
+ BtnSearch_Click(sender, e);
+ }
+ }
+ }
+}
diff --git a/BokBonCheck/Form1.resx b/BokBonCheck/Form1.resx
new file mode 100644
index 0000000..1af7de1
--- /dev/null
+++ b/BokBonCheck/Form1.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/BokBonCheck/NamguLibrarySearcher.cs b/BokBonCheck/NamguLibrarySearcher.cs
new file mode 100644
index 0000000..33e9cef
--- /dev/null
+++ b/BokBonCheck/NamguLibrarySearcher.cs
@@ -0,0 +1,203 @@
+using System;
+using System.Threading.Tasks;
+using OpenQA.Selenium;
+using OpenQA.Selenium.Chrome;
+using OpenQA.Selenium.Support.UI;
+using System.Text.RegularExpressions;
+using WebDriverManager;
+using WebDriverManager.DriverConfigs.Impl;
+using System.IO;
+
+namespace BokBonCheck
+{
+ public class NamguLibrarySearcher : ILibrarySearcher
+ {
+ public string SiteName => "남구통합도서관";
+ public string SiteUrl => "https://lib.namgu.gwangju.kr/main/bookSearch";
+
+ public async Task SearchAsync(string searchTerm)
+ {
+ var result = new BookSearchResult
+ {
+ SiteName = SiteName,
+ SearchTerm = searchTerm,
+ SearchTime = DateTime.Now
+ };
+
+ IWebDriver driver = null;
+ ChromeDriverService service = null;
+
+ try
+ {
+ // ChromeDriverManager에서 설정한 드라이버 경로 사용
+ var driverPath = ChromeDriverManager.GetDriverPath();
+ if (string.IsNullOrEmpty(driverPath) || !File.Exists(driverPath))
+ {
+ // 드라이버가 없으면 다시 설정
+ driverPath = await ChromeDriverManager.SetupChromeDriverAsync();
+ }
+
+ // ChromeDriver 서비스 설정
+ service = ChromeDriverService.CreateDefaultService(Path.GetDirectoryName(driverPath), Path.GetFileName(driverPath));
+ service.HideCommandPromptWindow = true;
+
+ var options = new ChromeOptions();
+ options.AddArgument("--headless");
+ options.AddArgument("--no-sandbox");
+ options.AddArgument("--disable-dev-shm-usage");
+ options.AddArgument("--disable-gpu");
+ options.AddArgument("--window-size=1920,1080");
+ options.AddArgument("--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36");
+ options.AddArgument("--disable-blink-features=AutomationControlled");
+ options.AddExcludedArgument("enable-automation");
+ options.AddAdditionalOption("useAutomationExtension", false);
+
+ // 명시적으로 서비스와 옵션을 사용하여 드라이버 생성
+ driver = new ChromeDriver(service, options);
+
+ // 자동화 감지 방지
+ ((IJavaScriptExecutor)driver).ExecuteScript("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})");
+
+ driver.Navigate().GoToUrl(SiteUrl);
+
+ // 페이지 로딩 대기
+ var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(15));
+
+ // 검색창 찾기 (남구통합도서관 사이트의 특정 선택자 사용)
+ IWebElement searchBox = null;
+ try
+ {
+ // 여러 가능한 선택자 시도
+ var selectors = new[]
+ {
+ "input[name='query']",
+ "input[id='query']",
+ "input[type='text']",
+ };
+
+ foreach (var selector in selectors)
+ {
+ try
+ {
+ searchBox = wait.Until(d => d.FindElement(By.CssSelector(selector)));
+ break;
+ }
+ catch
+ {
+ continue;
+ }
+ }
+
+ if (searchBox == null)
+ {
+ throw new Exception("검색창을 찾을 수 없습니다.");
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new Exception($"검색창 찾기 실패: {ex.Message}");
+ }
+
+ // 검색어 입력
+ searchBox.Clear();
+ searchBox.SendKeys(searchTerm);
+
+ // 검색 버튼 클릭
+ IWebElement searchButton = null;
+ try
+ {
+ var buttonSelectors = new[]
+ {
+ "button[type='submit']",
+ "input[type='submit']",
+ ".search-btn",
+ ".btn-search",
+ "button:contains('검색')",
+ "input[value*='검색']",
+ "button[class*='search']",
+ "input[class*='search']"
+ };
+
+ foreach (var selector in buttonSelectors)
+ {
+ try
+ {
+ searchButton = driver.FindElement(By.CssSelector(selector));
+ break;
+ }
+ catch
+ {
+ continue;
+ }
+ }
+
+ if (searchButton == null)
+ {
+ // Enter 키로 검색 시도
+ searchBox.SendKeys(Keys.Enter);
+ }
+ else
+ {
+ searchButton.Click();
+ }
+ }
+ catch (Exception ex)
+ {
+ // Enter 키로 검색 시도
+ searchBox.SendKeys(Keys.Enter);
+ }
+
+ // 검색 결과 로딩 대기
+ await Task.Delay(3000);
+
+ // 검색 결과 수 추출
+ var resultCount = ExtractBookCount(driver);
+
+ result.BookCount = resultCount;
+ result.IsSuccess = true;
+ }
+ catch (Exception ex)
+ {
+ result.IsSuccess = false;
+ result.ErrorMessage = ex.Message;
+ result.BookCount = 0;
+ }
+ finally
+ {
+ driver?.Quit();
+ driver?.Dispose();
+ service?.Dispose();
+ }
+
+ return result;
+ }
+
+ private int ExtractBookCount(IWebDriver driver)
+ {
+ try
+ {
+ // div.search-result 내부의 span에서 '전체 N' 텍스트 추출
+ var resultDiv = driver.FindElement(By.CssSelector("div.search-result"));
+ var span = resultDiv.FindElement(By.XPath(".//span[contains(text(),'전체')]"));
+ string text = span.Text; // 예: "전체 5 "
+ var match = System.Text.RegularExpressions.Regex.Match(text, @"전체\s*(\d+)");
+ if (match.Success)
+ {
+ return int.Parse(match.Groups[1].Value);
+ }
+ return 0;
+ }
+ catch
+ {
+ return 0;
+ }
+ }
+ }
+
+ public interface ILibrarySearcher
+ {
+ string SiteName { get; }
+ string SiteUrl { get; }
+ Task SearchAsync(string searchTerm);
+ }
+}
\ No newline at end of file
diff --git a/BokBonCheck/Program.cs b/BokBonCheck/Program.cs
new file mode 100644
index 0000000..f69ad18
--- /dev/null
+++ b/BokBonCheck/Program.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace BokBonCheck
+{
+ internal static class Program
+ {
+ ///
+ /// 해당 애플리케이션의 주 진입점입니다.
+ ///
+ [STAThread]
+ static void Main()
+ {
+ Application.EnableVisualStyles();
+ Application.SetCompatibleTextRenderingDefault(false);
+ Application.Run(new Form1());
+ }
+ }
+}
diff --git a/BokBonCheck/Properties/AssemblyInfo.cs b/BokBonCheck/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..7432889
--- /dev/null
+++ b/BokBonCheck/Properties/AssemblyInfo.cs
@@ -0,0 +1,33 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 어셈블리에 대한 일반 정보는 다음 특성 집합을 통해
+// 제어됩니다. 어셈블리와 관련된 정보를 수정하려면
+// 이러한 특성 값을 변경하세요.
+[assembly: AssemblyTitle("BokBonCheck")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("BokBonCheck")]
+[assembly: AssemblyCopyright("Copyright © 2025")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// ComVisible을 false로 설정하면 이 어셈블리의 형식이 COM 구성 요소에
+// 표시되지 않습니다. COM에서 이 어셈블리의 형식에 액세스하려면
+// 해당 형식에 대해 ComVisible 특성을 true로 설정하세요.
+[assembly: ComVisible(false)]
+
+// 이 프로젝트가 COM에 노출되는 경우 다음 GUID는 typelib의 ID를 나타냅니다.
+[assembly: Guid("bfff0c63-1231-442f-b800-c21760132e4d")]
+
+// 어셈블리의 버전 정보는 다음 네 가지 값으로 구성됩니다.
+//
+// 주 버전
+// 부 버전
+// 빌드 번호
+// 수정 버전
+//
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/BokBonCheck/Properties/Resources.Designer.cs b/BokBonCheck/Properties/Resources.Designer.cs
new file mode 100644
index 0000000..e7f2df7
--- /dev/null
+++ b/BokBonCheck/Properties/Resources.Designer.cs
@@ -0,0 +1,71 @@
+//------------------------------------------------------------------------------
+//
+// 이 코드는 도구를 사용하여 생성되었습니다.
+// 런타임 버전:4.0.30319.42000
+//
+// 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면
+// 이러한 변경 내용이 손실됩니다.
+//
+//------------------------------------------------------------------------------
+
+namespace BokBonCheck.Properties
+{
+
+
+ ///
+ /// 지역화된 문자열 등을 찾기 위한 강력한 형식의 리소스 클래스입니다.
+ ///
+ // 이 클래스는 ResGen 또는 Visual Studio와 같은 도구를 통해 StronglyTypedResourceBuilder
+ // 클래스에서 자동으로 생성되었습니다.
+ // 멤버를 추가하거나 제거하려면 .ResX 파일을 편집한 다음 /str 옵션을 사용하여
+ // ResGen을 다시 실행하거나 VS 프로젝트를 다시 빌드하십시오.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources
+ {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources()
+ {
+ }
+
+ ///
+ /// 이 클래스에서 사용하는 캐시된 ResourceManager 인스턴스를 반환합니다.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager
+ {
+ get
+ {
+ if ((resourceMan == null))
+ {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("BokBonCheck.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// 이 강력한 형식의 리소스 클래스를 사용하여 모든 리소스 조회에 대해 현재 스레드의 CurrentUICulture 속성을
+ /// 재정의합니다.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture
+ {
+ get
+ {
+ return resourceCulture;
+ }
+ set
+ {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/BokBonCheck/Properties/Resources.resx b/BokBonCheck/Properties/Resources.resx
new file mode 100644
index 0000000..af7dbeb
--- /dev/null
+++ b/BokBonCheck/Properties/Resources.resx
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/BokBonCheck/Properties/Settings.Designer.cs b/BokBonCheck/Properties/Settings.Designer.cs
new file mode 100644
index 0000000..bb8d50f
--- /dev/null
+++ b/BokBonCheck/Properties/Settings.Designer.cs
@@ -0,0 +1,30 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace BokBonCheck.Properties
+{
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
+ {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default
+ {
+ get
+ {
+ return defaultInstance;
+ }
+ }
+ }
+}
diff --git a/BokBonCheck/Properties/Settings.settings b/BokBonCheck/Properties/Settings.settings
new file mode 100644
index 0000000..3964565
--- /dev/null
+++ b/BokBonCheck/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/BokBonCheck/README.md b/BokBonCheck/README.md
new file mode 100644
index 0000000..e29d30c
--- /dev/null
+++ b/BokBonCheck/README.md
@@ -0,0 +1,134 @@
+# 도서 검색 프로그램 (BokBonCheck)
+
+## 개요
+이 프로그램은 여러 도서관 웹사이트에서 도서명을 검색하여 각 사이트별로 검색된 도서 수를 확인할 수 있는 C# WinForms 애플리케이션입니다.
+
+## 주요 기능
+- **다중 사이트 검색**: 여러 도서관 사이트에서 동시에 도서 검색
+- **실시간 결과 표시**: 검색 결과를 테이블 형태로 실시간 표시
+- **확장 가능한 구조**: 새로운 도서관 사이트를 쉽게 추가 가능
+- **비동기 처리**: 검색 중에도 UI가 응답 가능
+- **자동 Chrome 드라이버 관리**: Chrome 버전에 맞는 드라이버 자동 다운로드 및 관리
+
+## 현재 지원하는 도서관
+- 남구통합도서관 (https://lib.namgu.gwangju.kr/main/bookSearch)
+
+## 기술 스택
+- **.NET Framework 4.8**
+- **C# WinForms**
+- **Selenium WebDriver**
+- **WebDriverManager** (Chrome 드라이버 자동 관리)
+
+## 설치 및 실행
+
+### 필수 요구사항
+1. .NET Framework 4.8 이상
+2. Google Chrome 브라우저 (최신 버전 권장)
+3. 인터넷 연결 (Chrome 드라이버 다운로드용)
+4. Visual Studio 2019 이상 (개발용)
+
+### 빌드 및 실행
+1. 프로젝트를 Visual Studio에서 열기
+2. NuGet 패키지 복원 (자동으로 실행됨)
+3. 프로젝트 빌드 (Ctrl+Shift+B)
+4. 실행 (F5)
+
+### 배포
+1. Release 모드로 빌드
+2. `bin/Release` 폴더의 모든 파일을 배포 대상 폴더에 복사
+3. BokBonCheck.exe 실행
+
+## 사용법
+
+### 프로그램 시작
+1. 프로그램 실행 시 Chrome 드라이버 자동 설정
+2. Chrome 설치 확인 및 적절한 드라이버 다운로드
+3. 드라이버 테스트 완료 후 검색 가능
+
+### 기본 검색
+1. 검색창에 도서명 입력
+2. "검색" 버튼 클릭 또는 Enter 키 입력
+3. 검색 결과 확인
+
+### 결과 해석
+- **도서관**: 검색한 도서관 이름
+- **도서 수**: 해당 도서관에서 검색된 도서의 총 개수
+- **검색 시간**: 검색이 완료된 시간
+- **상태**: 검색 성공/실패 여부
+
+## 프로젝트 구조
+
+```
+BokBonCheck/
+├── Form1.cs # 메인 UI 폼
+├── BookSearchService.cs # 검색 서비스 관리
+├── NamguLibrarySearcher.cs # 남구통합도서관 전용 검색기
+├── ChromeDriverManager.cs # Chrome 드라이버 자동 관리
+├── ILibrarySearcher.cs # 검색기 인터페이스
+└── Program.cs # 프로그램 진입점
+```
+
+## Chrome 드라이버 자동 관리
+
+### WebDriverManager 기능
+- **자동 버전 감지**: 설치된 Chrome 버전 자동 감지
+- **드라이버 자동 다운로드**: Chrome 버전에 맞는 드라이버 자동 다운로드
+- **캐시 관리**: 다운로드된 드라이버 캐시 관리
+- **업데이트 지원**: Chrome 업데이트 시 드라이버 자동 업데이트
+
+### 동작 과정
+1. 프로그램 시작 시 Chrome 설치 확인
+2. Chrome 버전 정보 수집
+3. WebDriverManager를 통한 적절한 드라이버 다운로드
+4. 드라이버 테스트 실행
+5. 검색 기능 활성화
+
+## 새로운 도서관 추가하기
+
+새로운 도서관을 추가하려면 `ILibrarySearcher` 인터페이스를 구현하는 클래스를 생성하고 `BookSearchService`에 등록하면 됩니다.
+
+### 예시:
+```csharp
+public class NewLibrarySearcher : ILibrarySearcher
+{
+ public string SiteName => "새로운도서관";
+ public string SiteUrl => "https://newlibrary.com/search";
+
+ public async Task SearchAsync(string searchTerm)
+ {
+ // 검색 로직 구현
+ }
+}
+```
+
+그리고 `BookSearchService` 생성자에서:
+```csharp
+_searchers.Add(new NewLibrarySearcher());
+```
+
+## 문제 해결
+
+### Chrome 관련 오류
+- **Chrome 미설치**: Google Chrome을 설치하고 프로그램 재실행
+- **드라이버 다운로드 실패**: 인터넷 연결 확인 후 프로그램 재실행
+- **드라이버 테스트 실패**: 방화벽이나 보안 프로그램 설정 확인
+
+### 검색 실패
+- 인터넷 연결 상태 확인
+- 해당 도서관 사이트가 정상 작동하는지 확인
+- 사이트 구조가 변경되었을 수 있으므로 검색 로직 업데이트 필요
+
+### 성능 문제
+- 검색 중에는 다른 검색을 시작하지 마세요
+- 네트워크 상태에 따라 검색 시간이 달라질 수 있습니다
+- 첫 실행 시 드라이버 다운로드로 인한 지연이 있을 수 있습니다
+
+## 업데이트 내역
+- v1.1.0: Chrome 드라이버 자동 관리 기능 추가 (WebDriverManager 사용)
+- v1.0.0: 초기 버전 - 남구통합도서관 검색 기능 구현
+
+## 라이선스
+이 프로젝트는 개인 및 교육 목적으로 사용할 수 있습니다.
+
+## 기여
+버그 리포트나 기능 제안은 이슈로 등록해 주세요.
\ No newline at end of file