본문 바로가기

DEV/C#

2025년 차세대 나라장터 조회하기 - 코드공개

반응형

 개요

차세대 나라장터 크롤링하여 메일로 발송하는 C# 코드입니다.
소스 길이가 길어 이번 글에는 코드만 공유하고 다음장에 C# 개발 환경에 대해 설명 드리겠습니다.

아래 파일을 다운받아 압축을 풀면 코드가 있습니다.

g2b_simple.zip
18.54MB
g2b_simple.z01
19.53MB
g2b_simple.z02
19.53MB
g2b_simple.z03
19.53MB
g2b_simple.z04
19.53MB


 코드 내용


메인폼(FormG2B.cs) 

using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;
using OpenQA.Selenium;
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;
using WebDriverManager;
using WebDriverManager.DriverConfigs.Impl;
using System.Diagnostics;
using System.IO;
using OpenQA.Selenium.Interactions;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.TaskbarClock;
using System.Net;
using System.Net.Mail;
using AngleSharp.Dom;
using System.Data.SQLite;
using AngleSharp.Common;
using WebDriverManager.Helpers;

namespace g2b
{
    public partial class FormG2B : Form
    {
        IWebDriver driver;
        
        readonly CommonSQL cs = new CommonSQL();
        MailForm mailForm;
        private string smtp = "test@test.co.kr";
        private string sendPassword = "";
        string cr = System.Environment.NewLine;
        bool isDebug = false; // 디버그 모드 여부

        public FormG2B()
        {
            InitializeComponent();

            // 창의 크기를 전체크기로 설정
            this.Size = new System.Drawing.Size(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height - 38);

            this.Cursor = Cursors.Hand;	
            contextMenuStripMainForm.Cursor = Cursors.Hand;	

            this.notifyIconMainForm.ContextMenuStrip = contextMenuStripMainForm; // NotifyIcon에 ContextMenuStrip 설정
            this.notifyIconMainForm.Visible = true;//Tray 보이기 속성

            toolStripMenuItemClose.Click += new EventHandler(CloseApp);
            toolStripMenuItemHide.Click += new EventHandler(HideForm);

#if DEBUG
            isDebug = true; // 디버그 모드 활성화
#else
            isDebug = false; // 프로덕션 모드 활성화
#endif

            if (isDebug)
            {
                textBoxLog.AppendText("디버그 모드로 실행 중입니다." + cr);
                textBoxLog.AppendText("크롬창 및 크롬드라이버가 모두 닫힙니다." + cr);

                foreach (Process process in Process.GetProcesses()) // 프로세스 목록들 모두에 대해
                {
                    if (process.ProcessName.StartsWith("chrome"))
                    {
                        process.Kill();    // 그 프로세스를 강제 종료
                    }
                }

            }
            else
            {
                //textBoxLog.AppendText("프로덕션 모드로 실행 중입니다." + cr);
            }

            // Chrome 폴더 삭제
            try
            {
                string chromeDir = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Chrome");
                if (Directory.Exists(chromeDir))
                {
                    Directory.Delete(chromeDir, true); // 하위 폴더/파일까지 모두 삭제
                                                       // 필요시 로그 출력
                                                       // textBoxLog.AppendText("Chrome 폴더 삭제 완료" + cr);
                }
            }
            catch (Exception ex)
            {
                // 삭제 실패 시 로그 출력
                textBoxLog.AppendText("Chrome 폴더 삭제 실패: " + ex.Message + cr);
            }


            GetBaseInfo();

            /*
            //초기 설정
            textBoxBizNm.Text = "비점오염";               //테스트 중에만 사용 
            comboBoxRunTM.SelectedItem = "17";          //17시 실행 기본값 설정
            listViewEmail.Items.Add("dhhan@neobrix.co.kr");
            */
            checkedListBoxBizType.SetItemChecked(2, true); // 일반용역 기본값 설정


            // 창의 시작 위치를 (0, 0) 좌표로 설정
            this.Location = new System.Drawing.Point(0, 0);
        }

        private void GetBaseInfo()
        {
            cs.InitDB();

            // 메일 발송 정보 가져오기
            string sql = "select fromUser, pw from mailAuth";
            SQLiteDataReader sdr = cs.SelectSql(sql);
            while (sdr.Read())
            {
                smtp = sdr["fromUser"].ToString();
                sendPassword = sdr["pw"].ToString();
                //textBoxLog.AppendText($"메일 발송 정보: {smtp}, {sendPassword}" + cr);
            }
            sdr.Close();

            // 메일 수신자 정보 가져오기
            sql = "select email from mailTarget";
            sdr = cs.SelectSql(sql);
            while (sdr.Read())
            {
                listViewEmail.Items.Add(sdr["email"].ToString());
                //textBoxLog.AppendText($"메일 수신자: {sdr["email"].ToString()}" + cr);
            }
            sdr.Close();

            // 검색어 정보 가져오기
            sql = "select word from searchWord";
            sdr = cs.SelectSql(sql);
            while (sdr.Read())
            {
                textBoxBizNm.Text = sdr["word"].ToString();
                //textBoxLog.AppendText($"검색어: {sdr["word"].ToString()}" + cr);
            }
            sdr.Close();

            // 발송 시간 정보 가져오기
            sql = "select time from sendTime";
            sdr = cs.SelectSql(sql);
            while (sdr.Read())
            {
                comboBoxRunTM.SelectedItem = sdr["time"].ToString();
                //textBoxLog.AppendText($"발송 시간: {sdr["time"].ToString()}" + cr);
            }
        }

        /* 크롬 드라이버 업데이트 및 초기화 설정
         */
        public void InitSelenium()
        {
            try
            {
                // Chrome 버전 확인
                string chromeVersion = GetChromeVersion();
                textBoxLog.AppendText($"Chrome 버전: {chromeVersion}" + cr);

                // ChromeDriver 설정
                try
                {
                    var driverManager = new DriverManager();
                    //driverManager.SetUpDriver(new ChromeConfig(), chromeVersion);
                    driverManager.SetUpDriver(new ChromeConfig(), VersionResolveStrategy.MatchingBrowser);

                    textBoxLog.AppendText("ChromeDriver 설정 완료" + cr);
                }
                catch (Exception ex)
                {
                    textBoxLog.AppendText($"ChromeDriver 자동 설정 실패: {ex.Message}" + cr);
                    textBoxLog.AppendText("기본 ChromeDriver 사용" + cr);
                }
                
                // Chrome 옵션 설정
                ChromeOptions options = new ChromeOptions();

                if(isDebug)
                {
                    //options.AddArgument("--headless=new"); // 헤드리스 모드 제거
                }
                else
                {
                    //options.AddArgument("headless"); // 헤드리스
                }

                options.AddArgument("--disable-blink-features=AutomationControlled");
                options.AddArgument("--disable-notifications");
                options.AddArgument("--start-maximized");
                options.AddArgument("--no-sandbox");
                options.AddArgument("--disable-dev-shm-usage");
                options.AddArgument("--disable-gpu");
                options.AddArgument("--disable-extensions");
                options.AddArgument("--disable-popup-blocking");
                options.AddArgument("--disable-infobars");
                options.AddArgument("--disable-web-security");
                options.AddArgument("--disable-features=IsolateOrigins,site-per-process");
                options.AddArgument("--disable-site-isolation-trials");
                options.AddArgument("--dns-prefetch-disable");
                options.AddArgument("--disable-application-cache");
                options.AddArgument("--disable-cache");
                options.AddArgument("--disable-offline-load-stale-cache");
                options.AddArgument("--disk-cache-size=0");
                options.AddArgument("--ignore-certificate-errors");
                options.AddArgument("--allow-running-insecure-content");
                options.PageLoadStrategy = PageLoadStrategy.Normal;
                options.AddExcludedArgument("enable-automation");
                options.AddAdditionalOption("useAutomationExtension", false);
                options.AddArgument($"--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{chromeVersion} Safari/537.36");

                // ChromeDriver 초기화
                if (driver != null)
                {
                    try
                    {
                        driver.Quit();
                    }
                    catch { }
                    driver.Dispose();
                }

                ChromeDriverService driverService = ChromeDriverService.CreateDefaultService();
                driverService.HideCommandPromptWindow = true; // 콘솔 창 숨기기
                driver = new ChromeDriver(driverService, options);
                textBoxLog.AppendText("ChromeDriver 초기화 완료" + cr);
            }
            catch(Exception ex)
            {
                textBoxLog.AppendText("InitSelenium" + cr);
                textBoxLog.AppendText($"에러 발생: {ex.Message}" + cr);
                if (driver != null)
                {
                    try
                    {
                        driver.Quit();
                    }
                    catch { }
                    driver.Dispose();
                    driver = null;
                }
                throw;
            }            
        }

        /* Chrome 버전 확인
         */
        private string GetChromeVersion()
        {
            try
            {
                string[] possiblePaths = new string[]
                {
                    @"C:\Program Files\Google\Chrome\Application\chrome.exe",
                    @"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe",
                    Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + @"\Google\Chrome\Application\chrome.exe"
                };

                foreach (string chromePath in possiblePaths)
                {
                    if (File.Exists(chromePath))
                    {
                        var versionInfo = FileVersionInfo.GetVersionInfo(chromePath);
                        string version = versionInfo.FileVersion;

                        textBoxLog.AppendText("Chrome version : " + version);

                        if (!string.IsNullOrEmpty(version))
                        {
                            // 버전 번호에서 마지막 부분만 사용 (예: 136.0.7049.115 -> 136)
                            return version.Split('.')[0];
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                textBoxLog.AppendText("GetChromeVersion" + cr);
                textBoxLog.AppendText($"Chrome 버전 확인 중 에러: {ex.Message}" + cr);
            }
            return "136"; // 기본값 설정
        }


        /* 크롤링 시작(발주 및 사전규격)
         */
        public void Run()
        {
            try
            {
                // 타임아웃 설정
                driver.Manage().Timeouts().PageLoad = TimeSpan.FromSeconds(120);
                driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(30);
                driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromSeconds(30);

                // 웹사이트 접속 시도
                int maxRetries = 5; // 재시도 횟수 증가
                int currentTry = 0;
                bool connected = false;

                while (!connected && currentTry < maxRetries)
                {
                    try
                    {
                        // 연결 전 대기 시간 증가
                        System.Threading.Thread.Sleep(5000);
                        
                        textBoxLog.AppendText($"접속 시도 {currentTry + 1}/{maxRetries}" + cr);
                        
                        // URL 접속
                        driver.Navigate().GoToUrl("https://www.g2b.go.kr");
                        
                        // 페이지 로드 완료 확인을 위한 대기
                        WebDriverWait pageLoadWait = new WebDriverWait(driver, TimeSpan.FromSeconds(180));
                        
                        // 1. document.readyState 확인
                        pageLoadWait.Until(driver => ((IJavaScriptExecutor)driver).ExecuteScript("return document.readyState").Equals("complete"));
                        
                        // 2. jQuery 로드 완료 확인 (jQuery가 있는 경우)
                        try
                        {
                            pageLoadWait.Until(driver => ((IJavaScriptExecutor)driver).ExecuteScript("return (typeof jQuery !== 'undefined') && jQuery.active === 0"));
                        }
                        catch
                        {
                            textBoxLog.AppendText("jQuery 로드 확인 실패 (무시됨)" + cr);
                        }
                        
                        // 3. 네트워크 요청 완료 확인
                        try
                        {
                            pageLoadWait.Until(driver => ((IJavaScriptExecutor)driver).ExecuteScript(@"
                                return new Promise((resolve) => {
                                    if (window.performance && window.performance.getEntriesByType) {
                                        const resources = window.performance.getEntriesByType('resource');
                                        const pending = resources.filter(r => !r.responseEnd);
                                        resolve(pending.length === 0);
                                    } else {
                                        resolve(true);
                                    }
                                });
                            "));
                        }
                        catch
                        {
                            textBoxLog.AppendText("네트워크 요청 확인 실패 (무시됨)" + cr);
                        }
                        
                        // 4. 페이지 로드 이벤트 확인
                        try
                        {
                            pageLoadWait.Until(driver => ((IJavaScriptExecutor)driver).ExecuteScript(@"
                                return new Promise((resolve) => {
                                    if (document.readyState === 'complete') {
                                        resolve(true);
                                    } else {
                                        window.addEventListener('load', () => resolve(true));
                                    }
                                });
                            "));
                        }
                        catch
                        {
                            textBoxLog.AppendText("페이지 로드 이벤트 확인 실패 (무시됨)" + cr);
                        }
                        
                        // 5. 특정 요소 존재 확인 (예: body)
                        pageLoadWait.Until(driver => driver.FindElement(By.TagName("body")).Displayed);
                        
                        textBoxLog.AppendText("페이지 로드 완료 확인됨" + cr);
                        
                        // 추가 대기
                        System.Threading.Thread.Sleep(3000);
                        
                        connected = true;
                        textBoxLog.AppendText("드라이버 연결 성공" + cr);
                    }
                    catch (Exception ex)
                    {
                        currentTry++;
                        textBoxLog.AppendText($"연결 시도 {currentTry}/{maxRetries} 실패: {ex.Message}" + cr);
                        if (currentTry < maxRetries)
                        {
                            System.Threading.Thread.Sleep(15000); // 대기 시간 증가
                            try 
                            { 
                                driver.Navigate().Refresh();
                                System.Threading.Thread.Sleep(5000); // 새로고침 후 대기
                            } 
                            catch { }
                        }
                    }
                }

                if (!connected)
                {
                    textBoxLog.AppendText("최대 재시도 횟수를 초과했습니다." + cr);
                    CloseDriver();
                    throw new Exception("최대 재시도 횟수를 초과했습니다.");
                }

                WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(120));
                wait.IgnoreExceptionTypes(typeof(StaleElementReferenceException));
                wait.IgnoreExceptionTypes(typeof(NoSuchElementException));

                // 발주정보 바로가기
                textBoxLog.AppendText("발주 메뉴 클릭 시도" + cr);
                
                // 발주 메뉴 찾기 및 클릭
                try
                {
                    // 먼저 "발주" 메뉴 찾기
                    IWebElement parentMenu = null;
                    string[] parentSelectors = new string[]
                    {
                        "ID: mf_wfm_gnb_wfm_gnbMenu_genDepth1_0_btn_menuLvl1_span",
                        "XPath: //span[@id='mf_wfm_gnb_wfm_gnbMenu_genDepth1_0_btn_menuLvl1_span']",
                        "XPath: //span[contains(@class, 'w2textbox') and contains(text(), '발주')]",
                        "CSS: span#mf_wfm_gnb_wfm_gnbMenu_genDepth1_0_btn_menuLvl1_span"
                    };

                    foreach (string selector in parentSelectors)
                    {
                        try
                        {
                            textBoxLog.AppendText($"상위 메뉴 {selector} 시도 중..." + cr);
                            
                            if (selector.StartsWith("ID:"))
                            {
                                string id = selector.Substring(4).Trim();
                                parentMenu = wait.Until(d => d.FindElement(By.Id(id)));
                            }
                            else if (selector.StartsWith("XPath:"))
                            {
                                string xpath = selector.Substring(7).Trim();
                                parentMenu = wait.Until(d => d.FindElement(By.XPath(xpath)));
                            }
                            else if (selector.StartsWith("CSS:"))
                            {
                                string css = selector.Substring(5).Trim();
                                parentMenu = wait.Until(d => d.FindElement(By.CssSelector(css)));
                            }

                            if (parentMenu != null && parentMenu.Displayed && parentMenu.Enabled)
                            {
                                textBoxLog.AppendText($"상위 메뉴 {selector} 찾기 성공" + cr);
                                break;
                            }
                        }
                        catch (Exception ex)
                        {
                            textBoxLog.AppendText($"상위 메뉴 {selector} 시도 실패: {ex.Message}" + cr);
                            parentMenu = null;
                        }
                    }

                    if (parentMenu == null)
                    {
                        textBoxLog.AppendText("발주 상위 메뉴를 찾을 수 없습니다.");
                        CloseDriver();
                        throw new Exception("발주 상위 메뉴를 찾을 수 없습니다.");
                    }

                    // 상위 메뉴에 마우스 오버
                    try
                    {
                        // 요소가 보이도록 스크롤
                        ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].scrollIntoView(true);", parentMenu);
                        System.Threading.Thread.Sleep(1000);

                        // Actions를 사용하여 마우스 오버
                        new Actions(driver)
                            .MoveToElement(parentMenu)
                            .Perform();
                        
                        textBoxLog.AppendText("발주 메뉴 마우스 오버 성공" + cr);
                        
                        // 하위 메뉴가 나타날 때까지 대기
                        System.Threading.Thread.Sleep(2000);
                    }
                    catch (Exception ex)
                    {
                        textBoxLog.AppendText($"마우스 오버 실패: {ex.Message}" + cr);
                        CloseDriver();
                        throw;
                    }

                    // 하위 메뉴 찾기 및 클릭
                    IWebElement orderMenu = null;
                    string[] subMenuSelectors = new string[]
                    {
                        "ID: mf_wfm_gnb_wfm_gnbMenu_genDepth1_0_genDepth2_0_btn_menuLvl2",
                        "XPath: //a[@id='mf_wfm_gnb_wfm_gnbMenu_genDepth1_0_genDepth2_0_btn_menuLvl2']",
                        "XPath: //a[contains(@class, 'w2group') and .//span[contains(text(), '발주목록')]]",
                        "CSS: a.w2group.w2trigger_anchor.btn.depth2.depth-last",
                        "CSS: a#mf_wfm_gnb_wfm_gnbMenu_genDepth1_0_genDepth2_0_btn_menuLvl2"
                    };

                    foreach (string selector in subMenuSelectors)
                    {
                        try
                        {
                            textBoxLog.AppendText($"하위 메뉴 {selector} 시도 중..." + cr);
                            
                            if (selector.StartsWith("ID:"))
                            {
                                string id = selector.Substring(4).Trim();
                                orderMenu = wait.Until(d => d.FindElement(By.Id(id)));
                            }
                            else if (selector.StartsWith("XPath:"))
                            {
                                string xpath = selector.Substring(7).Trim();
                                orderMenu = wait.Until(d => d.FindElement(By.XPath(xpath)));
                            }
                            else if (selector.StartsWith("CSS:"))
                            {
                                string css = selector.Substring(5).Trim();
                                orderMenu = wait.Until(d => d.FindElement(By.CssSelector(css)));
                            }

                            if (orderMenu != null)
                            {
                                // 요소가 클릭 가능해질 때까지 대기
                                wait.Until(d => 
                                {
                                    try
                                    {
                                        return orderMenu.Displayed && 
                                               orderMenu.Enabled && 
                                               !orderMenu.GetAttribute("class").Contains("disabled");
                                    }
                                    catch
                                    {
                                        return false;
                                    }
                                });

                                textBoxLog.AppendText($"하위 메뉴 {selector} 찾기 성공, 클릭 시도" + cr);

                                // 여러 클릭 방법 시도
                                bool clicked = false;
                                Exception lastException = null;

                                // 1. Actions 클릭 시도 (마우스 이동 후 클릭)
                                try
                                {
                                    new Actions(driver)
                                        .MoveToElement(orderMenu)
                                        .Click()
                                        .Perform();
                                    clicked = true;
                                    textBoxLog.AppendText("Actions 클릭 성공" + cr);
                                }
                                catch (Exception ex)
                                {
                                    lastException = ex;
                                    textBoxLog.AppendText($"Actions 클릭 실패: {ex.Message}" + cr);
                                }

                                // 2. JavaScript 클릭 시도
                                if (!clicked)
                                {
                                    try
                                    {
                                        ((IJavaScriptExecutor)driver).ExecuteScript(
                                            "arguments[0].click(); arguments[0].dispatchEvent(new MouseEvent('click', {bubbles: true}));", 
                                            orderMenu);
                                        clicked = true;
                                        textBoxLog.AppendText("JavaScript 클릭 성공" + cr);
                                    }
                                    catch (Exception ex)
                                    {
                                        lastException = ex;
                                        textBoxLog.AppendText($"JavaScript 클릭 실패: {ex.Message}" + cr);
                                    }
                                }

                                // 3. 일반 클릭 시도
                                if (!clicked)
                                {
                                    try
                                    {
                                        orderMenu.Click();
                                        clicked = true;
                                        textBoxLog.AppendText("일반 클릭 성공" + cr);
                                    }
                                    catch (Exception ex)
                                    {
                                        lastException = ex;
                                        textBoxLog.AppendText($"일반 클릭 실패: {ex.Message}" + cr);
                                    }
                                }

                                if (clicked)
                                {
                                    // 클릭 성공 후 페이지 로드 대기
                                    System.Threading.Thread.Sleep(5000);
                                    break;
                                }
                                else
                                {
                                    textBoxLog.AppendText($"모든 클릭 방법 실패: {lastException?.Message}" + cr);
                                    orderMenu = null;
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            textBoxLog.AppendText($"하위 메뉴 {selector} 시도 실패: {ex.Message}" + cr);
                            orderMenu = null;
                        }
                    }

                    if (orderMenu == null)
                    {
                        textBoxLog.AppendText("발주목록 하위 메뉴를 클릭할 수 없습니다.");
                        CloseDriver();
                        throw new Exception("발주목록 하위 메뉴를 클릭할 수 없습니다.");
                    }

                    textBoxLog.AppendText("발주목록 메뉴 클릭 완료" + cr);
                }
                catch (Exception ex)
                {
                    textBoxLog.AppendText($"메뉴 클릭 실패: {ex.Message}" + cr);
                    CloseDriver();
                    throw;
                }

                // 페이지 로드 대기
                //System.Threading.Thread.Sleep(5000);
                textBoxLog.AppendText("발주정보 페이지 이동 완료" + cr);

                OrderPlan(wait);

                PriSpecDisc(wait);

                if(listViewOrderPlan.Items.Count == 0 && listViewSpec.Items.Count == 0)
                {
                    textBoxLog.AppendText("조회된 데이터가 없습니다." + cr);
                    return;
                }

            }
            catch (Exception ex)
            {
                textBoxLog.AppendText("Run" + cr);
                textBoxLog.AppendText($"에러 발생: {ex.Message}" + cr);
            }
            finally
            {
                //textBoxLog.AppendText("검색이 완료되었습니다. Selenium 드라이버를 종료합니다." + cr);
                //CloseDriver();
            }
        }


        /* 크롤링 시작(입찰공고)
         */
        public void RunNotice()
        {
            try
            {
                // 타임아웃 설정
                driver.Manage().Timeouts().PageLoad = TimeSpan.FromSeconds(120);
                driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(30);
                driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromSeconds(30);

                // 웹사이트 접속 시도
                int maxRetries = 5; // 재시도 횟수 증가
                int currentTry = 0;
                bool connected = false;

                while (!connected && currentTry < maxRetries)
                {
                    try
                    {
                        // 연결 전 대기 시간 증가
                        System.Threading.Thread.Sleep(5000);

                        textBoxLog.AppendText($"접속 시도 {currentTry + 1}/{maxRetries}" + cr);

                        // URL 접속
                        driver.Navigate().GoToUrl("https://www.g2b.go.kr");

                        // 페이지 로드 완료 확인을 위한 대기
                        WebDriverWait pageLoadWait = new WebDriverWait(driver, TimeSpan.FromSeconds(180));

                        // 1. document.readyState 확인
                        pageLoadWait.Until(driver => ((IJavaScriptExecutor)driver).ExecuteScript("return document.readyState").Equals("complete"));

                        // 2. jQuery 로드 완료 확인 (jQuery가 있는 경우)
                        try
                        {
                            pageLoadWait.Until(driver => ((IJavaScriptExecutor)driver).ExecuteScript("return (typeof jQuery !== 'undefined') && jQuery.active === 0"));
                        }
                        catch
                        {
                            textBoxLog.AppendText("jQuery 로드 확인 실패 (무시됨)" + cr);
                        }

                        // 3. 네트워크 요청 완료 확인
                        try
                        {
                            pageLoadWait.Until(driver => ((IJavaScriptExecutor)driver).ExecuteScript(@"
                                return new Promise((resolve) => {
                                    if (window.performance && window.performance.getEntriesByType) {
                                        const resources = window.performance.getEntriesByType('resource');
                                        const pending = resources.filter(r => !r.responseEnd);
                                        resolve(pending.length === 0);
                                    } else {
                                        resolve(true);
                                    }
                                });
                            "));
                        }
                        catch
                        {
                            textBoxLog.AppendText("네트워크 요청 확인 실패 (무시됨)" + cr);
                        }

                        // 4. 페이지 로드 이벤트 확인
                        try
                        {
                            pageLoadWait.Until(driver => ((IJavaScriptExecutor)driver).ExecuteScript(@"
                                return new Promise((resolve) => {
                                    if (document.readyState === 'complete') {
                                        resolve(true);
                                    } else {
                                        window.addEventListener('load', () => resolve(true));
                                    }
                                });
                            "));
                        }
                        catch
                        {
                            textBoxLog.AppendText("페이지 로드 이벤트 확인 실패 (무시됨)" + cr);
                        }

                        // 5. 특정 요소 존재 확인 (예: body)
                        pageLoadWait.Until(driver => driver.FindElement(By.TagName("body")).Displayed);

                        textBoxLog.AppendText("페이지 로드 완료 확인됨" + cr);

                        // 추가 대기
                        System.Threading.Thread.Sleep(3000);

                        connected = true;
                        textBoxLog.AppendText("드라이버 연결 성공" + cr);
                    }
                    catch (Exception ex)
                    {
                        currentTry++;
                        textBoxLog.AppendText($"연결 시도 {currentTry}/{maxRetries} 실패: {ex.Message}" + cr);
                        if (currentTry < maxRetries)
                        {
                            System.Threading.Thread.Sleep(15000); // 대기 시간 증가
                            try
                            {
                                driver.Navigate().Refresh();
                                System.Threading.Thread.Sleep(5000); // 새로고침 후 대기
                            }
                            catch { }
                        }
                    }
                }

                if (!connected)
                {
                    textBoxLog.AppendText("최대 재시도 횟수를 초과했습니다." + cr);
                    CloseDriver();
                    throw new Exception("최대 재시도 횟수를 초과했습니다.");
                }

                WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(120));
                wait.IgnoreExceptionTypes(typeof(StaleElementReferenceException));
                wait.IgnoreExceptionTypes(typeof(NoSuchElementException));

                // 입찰메뉴 바로가기
                textBoxLog.AppendText("입찰 메뉴 클릭 시도" + cr);

                // 입찰 대메뉴 찾기 및 클릭
                try
                {
                    IWebElement parentMenu = null;
                    string[] parentSelectors = new string[]
                    {
                        "ID: mf_wfm_gnb_wfm_gnbMenu_genDepth1_1_btn_menuLvl1_span",
                        "XPath: //span[@id='mf_wfm_gnb_wfm_gnbMenu_genDepth1_1_btn_menuLvl1_span']",
                        "XPath: //span[contains(@class, 'w2textbox') and contains(text(), '입찰')]",
                        "CSS: span#mf_wfm_gnb_wfm_gnbMenu_genDepth1_1_btn_menuLvl1_span"
                    };

                    foreach (string selector in parentSelectors)
                    {
                        try
                        {
                            textBoxLog.AppendText($"상위 메뉴 {selector} 시도 중..." + cr);

                            if (selector.StartsWith("ID:"))
                            {
                                string id = selector.Substring(4).Trim();
                                parentMenu = wait.Until(d => d.FindElement(By.Id(id)));
                            }
                            else if (selector.StartsWith("XPath:"))
                            {
                                string xpath = selector.Substring(7).Trim();
                                parentMenu = wait.Until(d => d.FindElement(By.XPath(xpath)));
                            }
                            else if (selector.StartsWith("CSS:"))
                            {
                                string css = selector.Substring(5).Trim();
                                parentMenu = wait.Until(d => d.FindElement(By.CssSelector(css)));
                            }

                            if (parentMenu != null && parentMenu.Displayed && parentMenu.Enabled)
                            {
                                textBoxLog.AppendText($"상위 메뉴 {selector} 찾기 성공" + cr);
                                break;
                            }
                        }
                        catch (Exception ex)
                        {
                            textBoxLog.AppendText($"상위 메뉴 {selector} 시도 실패: {ex.Message}" + cr);
                            parentMenu = null;
                        }
                    }

                    if (parentMenu == null)
                    {
                        textBoxLog.AppendText("입찰 메뉴를 찾을 수 없습니다.");
                        CloseDriver();
                        throw new Exception("입찰 메뉴를 찾을 수 없습니다.");
                    }

                    // 상위 메뉴에 마우스 오버
                    try
                    {
                        // 요소가 보이도록 스크롤
                        ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].scrollIntoView(true);", parentMenu);
                        System.Threading.Thread.Sleep(1000);

                        // Actions를 사용하여 마우스 오버
                        new Actions(driver)
                            .MoveToElement(parentMenu)
                            .Perform();

                        textBoxLog.AppendText("입찰 메뉴 마우스 오버 성공" + cr);

                        // 하위 메뉴가 나타날 때까지 대기
                        System.Threading.Thread.Sleep(2000);
                    }
                    catch (Exception ex)
                    {
                        textBoxLog.AppendText($"마우스 오버 실패: {ex.Message}" + cr);
                        CloseDriver();
                        throw;
                    }

                    
                    // 하위 메뉴 찾기 및 클릭
                    IWebElement orderMenu = null;
                    string[] subMenuSelectors = new string[]
                    {
                        "ID: mf_wfm_gnb_wfm_gnbMenu_genDepth1_1_genDepth2_0_genDepth3_0_btn_menuLvl3",
                        "XPath: //a[@id='mf_wfm_gnb_wfm_gnbMenu_genDepth1_1_genDepth2_0_genDepth3_0_btn_menuLvl3']",
                        "XPath: //a[contains(@class, 'w2group') and .//span[contains(text(), '입찰공고 목록')]]",
                        //"CSS: a.w2group.w2trigger_anchor.btn.depth2.depth-last",
                        "CSS: a#mf_wfm_gnb_wfm_gnbMenu_genDepth1_1_genDepth2_0_genDepth3_0_btn_menuLvl3"
                    };

                    foreach (string selector in subMenuSelectors)
                    {
                        try
                        {
                            textBoxLog.AppendText($"하위 메뉴 {selector} 시도 중..." + cr);

                            if (selector.StartsWith("ID:"))
                            {
                                string id = selector.Substring(4).Trim();
                                orderMenu = wait.Until(d => d.FindElement(By.Id(id)));
                            }
                            else if (selector.StartsWith("XPath:"))
                            {
                                string xpath = selector.Substring(7).Trim();
                                orderMenu = wait.Until(d => d.FindElement(By.XPath(xpath)));
                            }
                            else if (selector.StartsWith("CSS:"))
                            {
                                string css = selector.Substring(5).Trim();
                                orderMenu = wait.Until(d => d.FindElement(By.CssSelector(css)));
                            }

                            if (orderMenu != null)
                            {
                                // 요소가 클릭 가능해질 때까지 대기
                                wait.Until(d =>
                                {
                                    try
                                    {
                                        return orderMenu.Displayed &&
                                               orderMenu.Enabled &&
                                               !orderMenu.GetAttribute("class").Contains("disabled");
                                    }
                                    catch
                                    {
                                        return false;
                                    }
                                });

                                textBoxLog.AppendText($"하위 메뉴 {selector} 찾기 성공, 클릭 시도" + cr);

                                // 여러 클릭 방법 시도
                                bool clicked = false;
                                Exception lastException = null;

                                // 1. Actions 클릭 시도 (마우스 이동 후 클릭)
                                try
                                {
                                    new Actions(driver)
                                        .MoveToElement(orderMenu)
                                        .Click()
                                        .Perform();
                                    clicked = true;
                                    textBoxLog.AppendText("Actions 클릭 성공" + cr);
                                }
                                catch (Exception ex)
                                {
                                    lastException = ex;
                                    textBoxLog.AppendText($"Actions 클릭 실패: {ex.Message}" + cr);
                                }

                                // 2. JavaScript 클릭 시도
                                if (!clicked)
                                {
                                    try
                                    {
                                        ((IJavaScriptExecutor)driver).ExecuteScript(
                                            "arguments[0].click(); arguments[0].dispatchEvent(new MouseEvent('click', {bubbles: true}));",
                                            orderMenu);
                                        clicked = true;
                                        textBoxLog.AppendText("JavaScript 클릭 성공" + cr);
                                    }
                                    catch (Exception ex)
                                    {
                                        lastException = ex;
                                        textBoxLog.AppendText($"JavaScript 클릭 실패: {ex.Message}" + cr);
                                    }
                                }

                                // 3. 일반 클릭 시도
                                if (!clicked)
                                {
                                    try
                                    {
                                        orderMenu.Click();
                                        clicked = true;
                                        textBoxLog.AppendText("일반 클릭 성공" + cr);
                                    }
                                    catch (Exception ex)
                                    {
                                        lastException = ex;
                                        textBoxLog.AppendText($"일반 클릭 실패: {ex.Message}" + cr);
                                    }
                                }

                                if (clicked)
                                {
                                    // 클릭 성공 후 페이지 로드 대기
                                    System.Threading.Thread.Sleep(5000);
                                    break;
                                }
                                else
                                {
                                    textBoxLog.AppendText($"모든 클릭 방법 실패: {lastException?.Message}" + cr);
                                    orderMenu = null;
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            textBoxLog.AppendText($"하위 메뉴 {selector} 시도 실패: {ex.Message}" + cr);
                            orderMenu = null;
                        }
                    }

                    if (orderMenu == null)
                    {
                        textBoxLog.AppendText("입찰공고 목록 메뉴를 클릭할 수 없습니다.");
                        CloseDriver();
                        throw new Exception("입찰공고 목록 메뉴를 클릭할 수 없습니다.");
                    }

                    textBoxLog.AppendText("입찰 메뉴 클릭 완료" + cr);
                }
                catch (Exception ex)
                {
                    textBoxLog.AppendText($"메뉴 클릭 실패: {ex.Message}" + cr);
                    CloseDriver();
                    throw;
                }

                // 페이지 로드 대기
                //System.Threading.Thread.Sleep(5000);
                textBoxLog.AppendText("입찰공고 페이지 이동 완료" + cr);

                OrderNotice(wait);

                if (listViewOrderNotice.Items.Count == 0)
                {
                    textBoxLog.AppendText("조회된 데이터가 없습니다." + cr);
                    return;
                }

                DateTime now = DateTime.Now;
                string formattedTime = now.ToString("yyyy-MM-dd HH:mm:ss");

                //SendMail("나라장터 조회 결과(" + formattedTime + ")", MakeMailBody());
            }
            catch (Exception ex)
            {
                textBoxLog.AppendText("Run" + cr);
                textBoxLog.AppendText($"에러 발생: {ex.Message}" + cr);
            }
            finally
            {
                textBoxLog.AppendText("검색이 완료되었습니다. Selenium 드라이버를 종료합니다." + cr);
                CloseDriver();
            }
        }


        /*  발주계획
         */
        private void OrderPlan(WebDriverWait wait)
        {
            //발주계획 탭으로 이동 불필요

            // 검색 설정
            textBoxLog.AppendText("검색 설정" + cr);
            try
            {
                SelectElement select = new SelectElement(driver.FindElement(By.Id("mf_wfm_container_sbxRecordCountPerPage")));
                
                //조회수 설정
                if (radioButtonRstCnt10.Checked == true)
                {
                    select.SelectByText("10"); // 조회수 설정
                }
                else if (radioButtonRstCnt30.Checked == true)
                {
                    select.SelectByText("30"); // 조회수 설정
                }
                else if (radioButtonRstCnt50.Checked == true)
                {
                    select.SelectByText("50"); // 조회수 설정
                }
                else if (radioButtonRstCnt100.Checked == true)
                {
                    select.SelectByText("100"); // 조회수 설정
                }

                if (checkedListBoxBizType.CheckedItems.Count > 0)
                {
                    foreach (var item in checkedListBoxBizType.CheckedItems)
                    {
                        //textBoxLog.AppendText("선택 아이템 : " + item.ToString()+ cr);
                        if (item.ToString() == "전체")
                        {
                            IWebElement bizType = wait.Until(d => d.FindElement(By.XPath("//*[@id=\"mf_wfm_container_chkRqdcBsneSeCd_input_0\"]")));
                            ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", bizType);
                            break;
                        }
                        else if (item.ToString() == "물품")
                        {
                            IWebElement bizType = wait.Until(d => d.FindElement(By.XPath("//*[@id=\"mf_wfm_container_chkRqdcBsneSeCd_input_1\"]")));
                            ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", bizType);
                        }
                        else if (item.ToString() == "일반용역")
                        {
                            IWebElement bizType = wait.Until(d => d.FindElement(By.XPath("//*[@id=\"mf_wfm_container_chkRqdcBsneSeCd_input_2\"]")));
                            ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", bizType);
                        }
                        else if (item.ToString() == "기술용역")
                        {
                            IWebElement bizType = wait.Until(d => d.FindElement(By.XPath("//*[@id=\"mf_wfm_container_chkRqdcBsneSeCd_input_3\"]")));
                            ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", bizType);
                        }
                        else if (item.ToString() == "공사")
                        {
                            IWebElement bizType = wait.Until(d => d.FindElement(By.XPath("//*[@id=\"mf_wfm_container_chkRqdcBsneSeCd_input_4\"]")));
                            ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", bizType);
                        }
                        else if (item.ToString() == "외자")
                        {
                            IWebElement bizType = wait.Until(d => d.FindElement(By.XPath("//*[@id=\"mf_wfm_container_chkRqdcBsneSeCd_input_5\"]")));
                            ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", bizType);
                        }
                        else if (item.ToString() == "리스")
                        {
                            IWebElement bizType = wait.Until(d => d.FindElement(By.XPath("//*[@id=\"mf_wfm_container_chkRqdcBsneSeCd_input_6\"]")));
                            ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", bizType);
                        }
                    }
                }
                else
                {
                    textBoxLog.AppendText("체크된 항목이 없습니다." + cr);
                    return;
                }

                // 검색 조건 설정 (사업명)
                IWebElement bizNmInput = wait.Until(d => d.FindElement(By.Id("mf_wfm_container_txtBizNm")));

                bizNmInput.Clear();
                bizNmInput.SendKeys(textBoxBizNm.Text);

                // 검색 실행
                IWebElement executeSearchButton = wait.Until(d => d.FindElement(By.Id("mf_wfm_container_btnS0001")));
                executeSearchButton.Click();
                textBoxLog.AppendText("검색 실행 완료" + cr);
                // 검색 후 대기
                System.Threading.Thread.Sleep(5000);
            }
            catch (Exception ex)
            {
                textBoxLog.AppendText($"검색 설정 실패: {ex.Message}" + cr);
                textBoxLog.AppendText(ex.StackTrace.ToString() + cr);
                CloseDriver();
                throw;
            }

            // 데이터 추출
            textBoxLog.AppendText("데이터 추출 시도" + cr);
            try
            {
                var item = wait.Until(d => d.FindElement(By.Id("mf_wfm_container_gridView1_body_tbody")));

                listViewOrderPlan.Items.Clear(); // 리스트뷰 초기화
                
                try
                {
                    var rows = item.FindElements(By.TagName("tr"));
                    //textBoxLog.AppendText(" : " + rows.GetAttribute("innerHTML") + cr);
                    textBoxLog.AppendText(cr + cr + cr + "=================================================================" + cr);

                    int idx = 1;
                    foreach (var row in rows)
                    {
                        string prcmBsneSeNm = row.FindElement(By.XPath(".//td[@col_id='prcmBsneSeNm']")).Text;           //업무구분
                        string bizNm = row.FindElement(By.XPath(".//td[@col_id='bizNm']")).Text;                         //사업명
                        string oderInstUntyGrpNm = row.FindElement(By.XPath(".//td[@col_id='oderInstUntyGrpNm']")).Text; //수요기관
                        
                        string picNm = row.FindElement(By.XPath(".//td[@col_id='picNm']")).Text;               //담당자
                        string prcsYmd = row.FindElement(By.XPath(".//td[@col_id='prcsYmd']")).Text;               //진행일자
                        string oderPlanPgstNm = row.FindElement(By.XPath(".//td[@col_id='oderPlanPgstNm']")).Text;               //진행상태
                        string rfrnYn = row.FindElement(By.XPath(".//td[@col_id='rfrnYn']")).Text;   //참조여부
                        string oderYymm = row.FindElement(By.XPath(".//td[@col_id='oderYymm']")).Text;   //발주시기
                        string bgtSumAmt = row.FindElement(By.XPath(".//td[@col_id='bgtSumAmt']")).Text; //예산금액

                        //textBoxLog.AppendText(idx.ToString() + " : " + row.GetAttribute("innerHTML") + cr);
                        if (bizNm == null || "".Equals(bizNm))
                            break;

                        textBoxLog.AppendText(idx.ToString() + $" 업무구분: {prcmBsneSeNm}" + cr);
                        textBoxLog.AppendText(idx.ToString() + $" 사업명: {bizNm}" + cr);
                        textBoxLog.AppendText(idx.ToString() + $" 수요기관: {oderInstUntyGrpNm}" + cr);
                        //textBoxLog.AppendText(idx.ToString() + $" 공고기관: {tkcgDeptNm}" + cr);
                        textBoxLog.AppendText(idx.ToString() + $" 담당자: {picNm}" + cr);
                        textBoxLog.AppendText(idx.ToString() + $" 진행일자: {prcsYmd}" + cr);
                        textBoxLog.AppendText(idx.ToString() + $" 진행상태: {oderPlanPgstNm}" + cr);
                        textBoxLog.AppendText(idx.ToString() + $" 참조여부: {rfrnYn}" + cr);
                        textBoxLog.AppendText(idx.ToString() + $" 발주시기: {oderYymm}" + cr);
                        textBoxLog.AppendText(idx.ToString() + $" 예산금액: {bgtSumAmt}" + cr);
                        textBoxLog.AppendText(cr + cr + cr);

                        ListViewItem listViewItem = new ListViewItem(idx.ToString());
                        listViewItem.SubItems.Add(prcmBsneSeNm);
                        listViewItem.SubItems.Add(bizNm);
                        listViewItem.SubItems.Add(oderInstUntyGrpNm);
                        listViewItem.SubItems.Add(picNm);
                        listViewItem.SubItems.Add(prcsYmd);
                        listViewItem.SubItems.Add(oderPlanPgstNm);
                        listViewItem.SubItems.Add(rfrnYn);
                        listViewItem.SubItems.Add(oderYymm);
                        listViewItem.SubItems.Add(bgtSumAmt);

                        listViewOrderPlan.Items.Add(listViewItem);

                        idx++;
                    }
                }
                catch (Exception ex)
                {
                    textBoxLog.AppendText($"항목 처리 중 에러: {ex.Message}" + cr);
                }
            }
            catch (Exception ex)
            {
                textBoxLog.AppendText($"데이터 추출 실패: {ex.Message}" + cr);
                CloseDriver();
                throw;
            }

            ///컨텐츠 내용에 따라 자동변환
            listViewOrderPlan.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
        }

        /* 사전규격공개
         */
        private void PriSpecDisc(WebDriverWait wait)
        {
            //사전규격공개 탭으로 이동 로직 추가 필요
            IWebElement radSrchTy = wait.Until(d => d.FindElement(By.XPath("//*[@id=\"mf_wfm_container_radSrchTy_input_1\"]")));
            ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", radSrchTy);

            System.Threading.Thread.Sleep(1000);

            // 검색 설정
            textBoxLog.AppendText("검색 설정" + cr);
            try
            {
                //상세 설정 클릭
                //IWebElement executeDetailButton = wait.Until(d => d.FindElement(By.CssSelector(".w2trigger.btn_cm.srch_toggle")));
                //((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", executeDetailButton);

                //System.Threading.Thread.Sleep(1000);

                SelectElement select = new SelectElement(driver.FindElement(By.Id("mf_wfm_container_sbxRecordCountPerPage")));

                //조회수 설정
                if (radioButtonRstCnt10.Checked == true)
                {
                    select.SelectByText("10"); // 조회수 설정
                }
                else if (radioButtonRstCnt30.Checked == true)
                {
                    select.SelectByText("30"); // 조회수 설정
                }
                else if (radioButtonRstCnt50.Checked == true)
                {
                    select.SelectByText("50"); // 조회수 설정
                }
                else if (radioButtonRstCnt100.Checked == true)
                {
                    select.SelectByText("100"); // 조회수 설정
                }

                System.Threading.Thread.Sleep(1000);

                // 입찰공고에서 설정되어 있는 값 동일하게 사용
                /*
                if (checkedListBoxBizType.CheckedItems.Count > 0)
                {
                    foreach (var item in checkedListBoxBizType.CheckedItems)
                    {
                        if (item.ToString() == "전체")
                        {
                            IWebElement bizType = wait.Until(d => d.FindElement(By.XPath("//*[@id=\"mf_wfm_container_chkRqdcBsneSeCd_input_0\"]")));
                            ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", bizType);
                            break;
                        }
                        else if (item.ToString() == "물품")
                        {
                            IWebElement bizType = wait.Until(d => d.FindElement(By.XPath("//*[@id=\"mf_wfm_container_chkRqdcBsneSeCd_input_1\"]")));
                            ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", bizType);
                        }
                        else if (item.ToString() == "일반용역")
                        {
                            IWebElement bizType = wait.Until(d => d.FindElement(By.XPath("//*[@id=\"mf_wfm_container_chkRqdcBsneSeCd_input_2\"]")));
                            ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", bizType);
                        }
                        else if (item.ToString() == "기술용역")
                        {
                            IWebElement bizType = wait.Until(d => d.FindElement(By.XPath("//*[@id=\"mf_wfm_container_chkRqdcBsneSeCd_input_3\"]")));
                            ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", bizType);
                        }
                        else if (item.ToString() == "공사")
                        {
                            IWebElement bizType = wait.Until(d => d.FindElement(By.XPath("//*[@id=\"mf_wfm_container_chkRqdcBsneSeCd_input_4\"]")));
                            ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", bizType);
                        }
                        else if (item.ToString() == "외자")
                        {
                            IWebElement bizType = wait.Until(d => d.FindElement(By.XPath("//*[@id=\"mf_wfm_container_chkRqdcBsneSeCd_input_5\"]")));
                            ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", bizType);
                        }
                        else if (item.ToString() == "리스")
                        {
                            IWebElement bizType = wait.Until(d => d.FindElement(By.XPath("//*[@id=\"mf_wfm_container_chkRqdcBsneSeCd_input_6\"]")));
                            ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", bizType);
                        }
                    }
                }
                else
                {
                    textBoxLog.AppendText("체크된 항목이 없습니다." + cr);
                    return;
                }
                */

                // 검색 조건 설정 (사업명)
                IWebElement bizNmInput = wait.Until(d => d.FindElement(By.Id("mf_wfm_container_txtBizNm")));

                bizNmInput.Clear();
                bizNmInput.SendKeys(textBoxBizNm.Text);

                // 검색 실행 
                IWebElement executeSearchButton = wait.Until(d => d.FindElement(By.Id("mf_wfm_container_btnS0001")));
                executeSearchButton.Click();
                textBoxLog.AppendText("검색 실행 완료" + cr);
                // 검색 후 대기
                System.Threading.Thread.Sleep(5000);
            }
            catch (Exception ex)
            {
                textBoxLog.AppendText($"검색 설정 실패: {ex.Message}" + cr);
                textBoxLog.AppendText(ex.StackTrace.ToString() + cr);
                CloseDriver();
                throw;
            }

            // 데이터 추출
            textBoxLog.AppendText("데이터 추출 시도" + cr);
            try
            {
                var item = wait.Until(d => d.FindElement(By.Id("mf_wfm_container_gridView1_body_tbody")));
                listViewSpec.Items.Clear(); // 리스트뷰 초기화

                try
                {
                    var rows = item.FindElements(By.TagName("tr"));
                    //textBoxLog.AppendText(" : " + rows.GetAttribute("innerHTML") + cr);
                    textBoxLog.AppendText(cr + cr + cr + "================================================================" + cr);

                    int idx = 1;
                    foreach (var row in rows)
                    {
                        string prcmBsneSeNm = row.FindElement(By.XPath(".//td[@col_id='prcmBsneSeNm']")).Text;           //업무구분
                        string bizNm = row.FindElement(By.XPath(".//td[@col_id='bizNm']")).Text;                         //사업명
                        string oderInstUntyGrpNm = row.FindElement(By.XPath(".//td[@col_id='oderInstUntyGrpNm']")).Text; //수요기관
                        string tkcgDeptNm = row.FindElement(By.XPath(".//td[@col_id='tkcgDeptNm']")).Text;               //공고기관
                        string picNm = row.FindElement(By.XPath(".//td[@col_id='picNm']")).Text;               //담당자
                        string prcsYmd = row.FindElement(By.XPath(".//td[@col_id='prcsYmd']")).Text;               //진행일자
                        string oderPlanPgstNm = row.FindElement(By.XPath(".//td[@col_id='oderPlanPgstNm']")).Text;               //진행상태
                        string rfrnYn = row.FindElement(By.XPath(".//td[@col_id='rfrnYn']")).Text;   //참조여부
                        string bfSpecOpnnCnt = row.FindElement(By.XPath(".//td[@col_id='bfSpecOpnnCnt']")).Text;   //업체등록수

                        //textBoxLog.AppendText(idx.ToString() + " : " + row.GetAttribute("innerHTML") + cr);

                        if (bizNm == null || "".Equals(bizNm))
                            break;

                        textBoxLog.AppendText(idx.ToString() + $" 업무구분: {prcmBsneSeNm}" + cr);
                        textBoxLog.AppendText(idx.ToString() + $" 사업명: {bizNm}" + cr);
                        textBoxLog.AppendText(idx.ToString() + $" 수요기관: {oderInstUntyGrpNm}" + cr);
                        textBoxLog.AppendText(idx.ToString() + $" 공고기관: {tkcgDeptNm}" + cr);
                        textBoxLog.AppendText(idx.ToString() + $" 담당자: {picNm}" + cr);
                        textBoxLog.AppendText(idx.ToString() + $" 진행일자: {prcsYmd}" + cr);
                        textBoxLog.AppendText(idx.ToString() + $" 진행상태: {oderPlanPgstNm}" + cr);
                        textBoxLog.AppendText(idx.ToString() + $" 참조여부: {rfrnYn}" + cr);
                        textBoxLog.AppendText(idx.ToString() + $" 업체등록수: {bfSpecOpnnCnt}" + cr);
                        textBoxLog.AppendText(cr + cr + cr);

                        ListViewItem listViewItem = new ListViewItem(idx.ToString());
                        listViewItem.SubItems.Add(prcmBsneSeNm);
                        listViewItem.SubItems.Add(bizNm);
                        listViewItem.SubItems.Add(oderInstUntyGrpNm);
                        listViewItem.SubItems.Add(tkcgDeptNm);
                        listViewItem.SubItems.Add(picNm);
                        listViewItem.SubItems.Add(prcsYmd);
                        listViewItem.SubItems.Add(oderPlanPgstNm);
                        listViewItem.SubItems.Add(rfrnYn);
                        listViewItem.SubItems.Add(bfSpecOpnnCnt);
                        
                        listViewSpec.Items.Add(listViewItem);

                        idx++;
                    }
                }
                catch (Exception ex)
                {
                    textBoxLog.AppendText($"항목 처리 중 에러: {ex.Message}" + cr);
                }
            }
            catch (Exception ex)
            {
                textBoxLog.AppendText($"데이터 추출 실패: {ex.Message}" + cr);
                CloseDriver();
                throw;
            }

            ///컨텐츠 내용에 따라 자동변환
            listViewSpec.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
        }

        /* 입찰공고 조회
         */
        private void OrderNotice(WebDriverWait wait)
        {
            // 검색 설정
            textBoxLog.AppendText("검색 설정" + cr);
            try
            {
                
                SelectElement select = new SelectElement(driver.FindElement(By.Id("mf_wfm_container_tacBidPbancLst_contents_tab2_body_sbxRecordCountPerPage1")));

                //조회수 설정
                if (radioButtonRstCnt10.Checked == true)
                {
                    select.SelectByText("10"); // 조회수 설정
                }
                else if (radioButtonRstCnt30.Checked == true)
                {
                    select.SelectByText("30"); // 조회수 설정
                }
                else if (radioButtonRstCnt50.Checked == true)
                {
                    select.SelectByText("50"); // 조회수 설정
                }
                else if (radioButtonRstCnt100.Checked == true)
                {
                    select.SelectByText("100"); // 조회수 설정
                }

                if (checkedListBoxBizType.CheckedItems.Count > 0)
                {
                    foreach (var item in checkedListBoxBizType.CheckedItems)
                    {
                        //textBoxLog.AppendText("선택 아이템 : " + item.ToString()+ cr);
                        if (item.ToString() == "전체")
                        {
                            break;
                        }
                        else if (item.ToString() == "물품")
                        {
                            IWebElement bizAll = wait.Until(d => d.FindElement(By.XPath("//*[@id=\"mf_wfm_container_tacBidPbancLst_contents_tab2_body_chkBidPbancSrchTyCd_input_0\"]")));
                            ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", bizAll);

                            IWebElement bizType = wait.Until(d => d.FindElement(By.XPath("//*[@id=\"mf_wfm_container_tacBidPbancLst_contents_tab2_body_chkBidPbancSrchTyCd_input_1\"]")));
                            ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", bizType);
                        }
                        else if (item.ToString() == "일반용역")
                        {
                            IWebElement bizAll = wait.Until(d => d.FindElement(By.XPath("//*[@id=\"mf_wfm_container_tacBidPbancLst_contents_tab2_body_chkBidPbancSrchTyCd_input_0\"]")));
                            ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", bizAll);

                            IWebElement bizType = wait.Until(d => d.FindElement(By.XPath("//*[@id=\"mf_wfm_container_tacBidPbancLst_contents_tab2_body_chkBidPbancSrchTyCd_input_2\"]")));
                            ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", bizType);
                        }
                        else if (item.ToString() == "기술용역")
                        {
                            IWebElement bizAll = wait.Until(d => d.FindElement(By.XPath("//*[@id=\"mf_wfm_container_tacBidPbancLst_contents_tab2_body_chkBidPbancSrchTyCd_input_0\"]")));
                            ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", bizAll);

                            IWebElement bizType = wait.Until(d => d.FindElement(By.XPath("//*[@id=\"mf_wfm_container_tacBidPbancLst_contents_tab2_body_chkBidPbancSrchTyCd_input_3\"]")));
                            ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", bizType);
                        }
                        else if (item.ToString() == "공사")
                        {
                            IWebElement bizAll = wait.Until(d => d.FindElement(By.XPath("//*[@id=\"mf_wfm_container_tacBidPbancLst_contents_tab2_body_chkBidPbancSrchTyCd_input_0\"]")));
                            ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", bizAll);

                            IWebElement bizType = wait.Until(d => d.FindElement(By.XPath("//*[@id=\"mf_wfm_container_tacBidPbancLst_contents_tab2_body_chkBidPbancSrchTyCd_input_4\"]")));
                            ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", bizType);
                        }
                        else if (item.ToString() == "외자")
                        {
                            IWebElement bizAll = wait.Until(d => d.FindElement(By.XPath("//*[@id=\"mf_wfm_container_tacBidPbancLst_contents_tab2_body_chkBidPbancSrchTyCd_input_0\"]")));
                            ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", bizAll);

                            IWebElement bizType = wait.Until(d => d.FindElement(By.XPath("//*[@id=\"mf_wfm_container_tacBidPbancLst_contents_tab2_body_chkBidPbancSrchTyCd_input_5\"]")));
                            ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", bizType);
                        }
                        else if (item.ToString() == "리스")
                        {
                            IWebElement bizAll = wait.Until(d => d.FindElement(By.XPath("//*[@id=\"mf_wfm_container_tacBidPbancLst_contents_tab2_body_chkBidPbancSrchTyCd_input_0\"]")));
                            ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", bizAll);

                            IWebElement bizType = wait.Until(d => d.FindElement(By.XPath("//*[@id=\"mf_wfm_container_tacBidPbancLst_contents_tab2_body_chkBidPbancSrchTyCd_input_6\"]")));
                            ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", bizType);
                        }
                    }
                }
                else
                {
                    textBoxLog.AppendText("체크된 항목이 없습니다." + cr);
                    return;
                }


                // 검색 조건 설정 (사업명)
                IWebElement bizNmInput = wait.Until(d => d.FindElement(By.Id("mf_wfm_container_tacBidPbancLst_contents_tab2_body_bidPbancNm")));

                bizNmInput.Clear();
                bizNmInput.SendKeys(textBoxBizNm.Text);

                // 검색 실행
                IWebElement executeSearchButton = wait.Until(d => d.FindElement(By.Id("mf_wfm_container_tacBidPbancLst_contents_tab2_body_btnS0004")));
                executeSearchButton.Click();
                textBoxLog.AppendText("검색 실행 완료" + cr);
                // 검색 후 대기
                System.Threading.Thread.Sleep(5000);
            }
            catch (Exception ex)
            {
                textBoxLog.AppendText($"검색 설정 실패: {ex.Message}" + cr);
                textBoxLog.AppendText(ex.StackTrace.ToString() + cr);
                CloseDriver();
                throw;
            }

            // 데이터 추출
            textBoxLog.AppendText("데이터 추출 시도" + cr);
            try
            {
                var item = wait.Until(d => d.FindElement(By.Id("mf_wfm_container_tacBidPbancLst_contents_tab2_body_gridView1_body_tbody")));

                listViewOrderNotice.Items.Clear(); // 리스트뷰 초기화

                try
                {
                    var rows = item.FindElements(By.TagName("tr"));
                    //textBoxLog.AppendText(" : " + rows.GetAttribute("innerHTML") + cr);
                    textBoxLog.AppendText(cr + cr + cr + "=================================================================" + cr);

                    int idx = 1;
                    foreach (var row in rows)
                    {
                        
                        string prcmBsneSeCdNm = row.FindElement(By.XPath(".//td[@col_id='prcmBsneSeCdNm']")).Text;          //업무구분
                        string pbancSeYnNm = row.FindElement(By.XPath(".//td[@col_id='pbancSeYnNm']")).Text;                //업무여부
                        string pbancSttsNm = row.FindElement(By.XPath(".//td[@col_id='pbancSttsNm']")).Text;                //구분
                        string bidPbancUntyNoOrd = row.FindElement(By.XPath(".//td[@col_id='bidPbancUntyNoOrd']")).Text;    //입찰공고번호 
                        string bidPbancNm = row.FindElement(By.XPath(".//td[@col_id='bidPbancNm']")).Text;    //공고명
                        string oderInstUntyGrpNm = row.FindElement(By.XPath(".//td[@col_id='oderInstUntyGrpNm']")).Text;    //공고기관
                        string dmstNm = row.FindElement(By.XPath(".//td[@col_id='dmstNm']")).Text;    //수요기관
                        string pbancPstgDt = row.FindElement(By.XPath(".//td[@col_id='pbancPstgDt']")).Text;    //게시일시
                        string stepBsneUntNm = row.FindElement(By.XPath(".//td[@col_id='stepBsneUntNm']")).Text;    //단계
                        string prssBsneUntNm = row.FindElement(By.XPath(".//td[@col_id='prssBsneUntNm']")).Text;    //세부절차
                        string bsnePrssPrgrsSeNm = row.FindElement(By.XPath(".//td[@col_id='bsnePrssPrgrsSeNm']")).Text;    //세부절차상태
                        //입찰진행
                        //요약
                        
                        if (bidPbancNm == null || "".Equals(bidPbancNm))
                            break;

                        textBoxLog.AppendText(idx.ToString() + $" 업무구분: {prcmBsneSeCdNm}" + cr);
                        textBoxLog.AppendText(idx.ToString() + $" 업무여부: {pbancSeYnNm}" + cr);
                        textBoxLog.AppendText(idx.ToString() + $" 구분:   {pbancSttsNm}" + cr);
                        textBoxLog.AppendText(idx.ToString() + $" 입찰공고번호: {bidPbancUntyNoOrd}" + cr);
                        textBoxLog.AppendText(idx.ToString() + $" 공고명: {bidPbancNm}" + cr);
                        textBoxLog.AppendText(idx.ToString() + $" 공고기관: {oderInstUntyGrpNm}" + cr);
                        textBoxLog.AppendText(idx.ToString() + $" 수요기관: {dmstNm}" + cr);
                        textBoxLog.AppendText(idx.ToString() + $" 게시일시: {pbancPstgDt}" + cr);
                        textBoxLog.AppendText(idx.ToString() + $" 단계: {stepBsneUntNm}" + cr);
                        textBoxLog.AppendText(idx.ToString() + $" 세부절차: {prssBsneUntNm}" + cr);
                        textBoxLog.AppendText(idx.ToString() + $" 세부절차상태: {bsnePrssPrgrsSeNm}" + cr);
                        textBoxLog.AppendText(cr + cr + cr);

                        ListViewItem listViewItem = new ListViewItem(idx.ToString());
                        listViewItem.SubItems.Add(prcmBsneSeCdNm);
                        listViewItem.SubItems.Add(pbancSeYnNm);
                        listViewItem.SubItems.Add(pbancSttsNm);
                        listViewItem.SubItems.Add(bidPbancUntyNoOrd);
                        listViewItem.SubItems.Add(bidPbancNm);
                        listViewItem.SubItems.Add(oderInstUntyGrpNm);
                        listViewItem.SubItems.Add(dmstNm);
                        listViewItem.SubItems.Add(pbancPstgDt);
                        listViewItem.SubItems.Add(stepBsneUntNm);
                        listViewItem.SubItems.Add(prssBsneUntNm);
                        listViewItem.SubItems.Add(bsnePrssPrgrsSeNm);

                        listViewOrderNotice.Items.Add(listViewItem);

                        idx++;
                    }
                }
                catch (Exception ex)
                {
                    textBoxLog.AppendText($"항목 처리 중 에러: {ex.Message}" + cr);
                }
            }
            catch (Exception ex)
            {
                textBoxLog.AppendText($"데이터 추출 실패: {ex.Message}" + cr);
                CloseDriver();
                throw;
            }

            ///컨텐츠 내용에 따라 자동변환
            listViewOrderNotice.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
        }

        /* 메일 전송
         * 네이버 SMTP 메일 사용 
         * 네이버 메일 설정에서 imap 사용 허용필요
         * 네이버 메일 로그인 > 환경설정 > POP3/IMAP 설정 > IMAP 사용 설정 으로 이동 후 IMAP/SMTP 사용 항목 중 사용함 선택
         */
        private void SendMail(string subject, string body)
        {
            MailAddress sendAddress = new MailAddress(smtp.ToString());

            MailMessage message = new System.Net.Mail.MailMessage();
            message.From = new System.Net.Mail.MailAddress(sendAddress.Address); //ex : ooo@naver.com

            foreach (var item in listViewEmail.Items)
            {
                ListViewItem list1 = item as ListViewItem;
                if (list1 != null)
                {
                    message.To.Add(list1.SubItems[0].Text); // 메일주소 추가
                }
            }

            if(message.To.Count == 0)
            {
                textBoxLog.AppendText("메일 수신자 없음" + cr);
                return;
            }

            message.Subject = subject;
            message.SubjectEncoding = System.Text.Encoding.UTF8;
            message.IsBodyHtml = true; // HTML 형식으로 전송  
            message.Body = body;
            message.BodyEncoding = System.Text.Encoding.UTF8;

            try
            {
                System.Net.Mail.SmtpClient smtp = new System.Net.Mail.SmtpClient("smtp.naver.com", 587);
                smtp.UseDefaultCredentials = false; // 시스템에 설정된 인증 정보를 사용하지 않는다.
                smtp.EnableSsl = true;  // SSL을 사용한다.
                smtp.DeliveryMethod = System.Net.Mail.SmtpDeliveryMethod.Network; // 이걸 하지 않으면 naver 에 인증을 받지 못한다.
                smtp.Credentials = new System.Net.NetworkCredential(sendAddress.Address, sendPassword);

                if(string.IsNullOrEmpty(sendPassword))
                {
                    textBoxLog.AppendText("메일 비밀번호가 설정되지 않았습니다." + cr);
                    return;
                }

                if (string.IsNullOrEmpty(sendAddress.Address))
                {
                    textBoxLog.AppendText("메일 주소가 설정되지 않았습니다." + cr);
                    return;
                }

                if(!isDebug)
                {
                    smtp.Send(message);
                }
                
                textBoxLog.AppendText("메일 전송 완료" + cr);
            }
            catch (Exception ex)
            {
                textBoxLog.Text += ex.ToString();
            }
            finally
            {
                //if (smtp != null) { smtp.Dispose(); }
                if (message != null) { message.Dispose(); }
            }
        }

        /* 발주계획, 사전규격공개 내용을 메일로 전송
         * HTML 형식으로 작성
         * 메일 내용이 많아 width를 늘릴수 있도록 조정 필요
         */
        private string MakeMailBody()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("<html>");
            sb.AppendLine("<head>");
            sb.AppendLine("<meta charset=\"utf-8\">");
            sb.AppendLine("<style>");
            sb.AppendLine("table { border-collapse: collapse; border: 1px solid #bfbfbf; width=100%;}");
            sb.AppendLine("table tbody{ border: 1px solid #bfbfbf; }");
            sb.AppendLine("table tbody td{ border: 1px solid #bfbfbf; 5px;}");
            sb.AppendLine("table tbody th{ border: 1px solid #bfbfbf; 5px;}");
            sb.AppendLine("th, td { border: 1px solid #bfbfbf; padding: 5px; }");
            sb.AppendLine("th { background-color: #bfbfbf; }");
            sb.AppendLine("</style>");
            sb.AppendLine("</head>");

            sb.AppendLine("<body>");
            sb.AppendLine("<h2>나라장터 발주계획 및 사전규격공개</h2>");
            sb.AppendLine("<p>아래는 나라장터에서 수집한 발주계획 및 사전규격공개 정보입니다.</p>");
            sb.AppendLine("<p>사업명 : " + textBoxBizNm.Text + "</p>");
            sb.AppendLine("<p>검색일자 : " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "</p>");
            sb.AppendLine("<br/>");

            if(listViewOrderPlan.Items.Count == 0)
            {
                sb.AppendLine("<p>발주계획 : 조회된 데이터 없음</p>");
            }
            else
            {
                sb.AppendLine("<p>발주계획</p>");
                sb.AppendLine("<table style='border-collapse: collapse; border: 1px solid #bfbfbf; width=100%;'>");
                sb.AppendLine("<tr>");
                sb.AppendLine("<th style='border-collapse: collapse; border: 1px solid #bfbfbf;'>번호</th>");
                sb.AppendLine("<th style='border-collapse: collapse; border: 1px solid #bfbfbf;'>업무구분</th>");
                sb.AppendLine("<th style='border-collapse: collapse; border: 1px solid #bfbfbf;'>사업명</th>");
                sb.AppendLine("<th style='border-collapse: collapse; border: 1px solid #bfbfbf;'>수요기관</th>");
                sb.AppendLine("<th style='border-collapse: collapse; border: 1px solid #bfbfbf;'>담당자</th>");
                sb.AppendLine("<th style='border-collapse: collapse; border: 1px solid #bfbfbf;'>진행일자</th>");
                sb.AppendLine("<th style='border-collapse: collapse; border: 1px solid #bfbfbf;'>진행상태</th>");
                sb.AppendLine("<th style='border-collapse: collapse; border: 1px solid #bfbfbf;'>참조여부</th>");
                sb.AppendLine("<th style='border-collapse: collapse; border: 1px solid #bfbfbf;'>발주시기</th>");
                sb.AppendLine("<th style='border-collapse: collapse; border: 1px solid #bfbfbf;'>예산금액</th>");
                sb.AppendLine("</tr>");

                sb.AppendLine("<tbody>");

                // 발주계획 내용
                foreach (var item in listViewOrderPlan.Items)
                {
                    ListViewItem list1 = item as ListViewItem;
                    if (list1 != null)
                    {
                        sb.AppendLine("<tr>");
                    
                        foreach (ListViewItem.ListViewSubItem sub in list1.SubItems)
                        {
                            sb.AppendLine("<td style='border-collapse: collapse; border: 1px solid #bfbfbf;'>" + sub.Text + "</td>");
                            //sb.Append(sub.Text + "\t");
                        }
                        sb.AppendLine("</tr>");
                    }
                    sb.AppendLine();
                }

                sb.AppendLine("</tbody>");

                sb.AppendLine("</table>");
                sb.AppendLine("</br>");
                sb.AppendLine("</br>");
            }


            if(listViewSpec.Items.Count == 0)
            {
                sb.AppendLine("<p>사전규격 : 조회된 데이터 없음</p>");
            }
            else
            {
                sb.AppendLine("<p>사전규격</p>");
                sb.AppendLine("<table style='border-collapse: collapse; border: 1px solid #bfbfbf; width=100%;'>");
                sb.AppendLine("<tr>");
                sb.AppendLine("<th style='border-collapse: collapse; border: 1px solid #bfbfbf;'>번호</th>");
                sb.AppendLine("<th style='border-collapse: collapse; border: 1px solid #bfbfbf;'>업무구분</th>");
                sb.AppendLine("<th style='border-collapse: collapse; border: 1px solid #bfbfbf;'>사업명</th>");
                sb.AppendLine("<th style='border-collapse: collapse; border: 1px solid #bfbfbf;'>수요기관</th>");
                sb.AppendLine("<th style='border-collapse: collapse; border: 1px solid #bfbfbf;'>공고기관</th>"); 
                sb.AppendLine("<th style='border-collapse: collapse; border: 1px solid #bfbfbf;'>담당자</th>");
                sb.AppendLine("<th style='border-collapse: collapse; border: 1px solid #bfbfbf;'>진행일자</th>");
                sb.AppendLine("<th style='border-collapse: collapse; border: 1px solid #bfbfbf;'>진행상태</th>");
                sb.AppendLine("<th style='border-collapse: collapse; border: 1px solid #bfbfbf;'>참조여부</th>");
                sb.AppendLine("<th style='border-collapse: collapse; border: 1px solid #bfbfbf;'>업체등록수</th>");

                sb.AppendLine("</tr>");

                sb.AppendLine("<tbody>");

                // 사전규격 내용
                foreach (var item in listViewSpec.Items)
                {
                    ListViewItem list2 = item as ListViewItem;
                    if (list2 != null)
                    {
                        sb.AppendLine("<tr>");
                        foreach (ListViewItem.ListViewSubItem sub in list2.SubItems)
                        {
                            sb.AppendLine("<td style='border-collapse: collapse; border: 1px solid #bfbfbf;'>" + sub.Text + "</td>");
                            //sb.Append(sub.Text + "\t");
                        }
                        sb.AppendLine("</tr>");
                    }
                    sb.AppendLine();
                }
                sb.AppendLine("</tbody>");
                sb.AppendLine("</table>");
                sb.AppendLine("</br>");
            }


            if (listViewOrderNotice.Items.Count == 0)
            {
                sb.AppendLine("<p>입찰공고 : 조회된 데이터 없음</p>");
            }
            else
            {
                sb.AppendLine("<p>입찰공고</p>");
                sb.AppendLine("<table style='border-collapse: collapse; border: 1px solid #bfbfbf; width=100%;'>");
                sb.AppendLine("<tr>");
                sb.AppendLine("<th style='border-collapse: collapse; border: 1px solid #bfbfbf;'>번호</th>");
                sb.AppendLine("<th style='border-collapse: collapse; border: 1px solid #bfbfbf;'>업무구분</th>");
                sb.AppendLine("<th style='border-collapse: collapse; border: 1px solid #bfbfbf;'>업무여부</th>");
                sb.AppendLine("<th style='border-collapse: collapse; border: 1px solid #bfbfbf;'>구분</th>");
                sb.AppendLine("<th style='border-collapse: collapse; border: 1px solid #bfbfbf;'>입찰공고번호</th>");
                sb.AppendLine("<th style='border-collapse: collapse; border: 1px solid #bfbfbf;'>공고명</th>");
                sb.AppendLine("<th style='border-collapse: collapse; border: 1px solid #bfbfbf;'>공고기관</th>");
                sb.AppendLine("<th style='border-collapse: collapse; border: 1px solid #bfbfbf;'>수요기관</th>");
                sb.AppendLine("<th style='border-collapse: collapse; border: 1px solid #bfbfbf;'>게시일시(일찰마감일시)</th>");
                sb.AppendLine("<th style='border-collapse: collapse; border: 1px solid #bfbfbf;'>단계</th>");
                sb.AppendLine("<th style='border-collapse: collapse; border: 1px solid #bfbfbf;'>세부절차</th>");
                sb.AppendLine("<th style='border-collapse: collapse; border: 1px solid #bfbfbf;'>세부절차상태</th>");

                sb.AppendLine("</tr>");

                sb.AppendLine("<tbody>");

                // 입찰공고 내용
                foreach (var item in listViewOrderNotice.Items)
                {
                    ListViewItem list3 = item as ListViewItem;
                    if (list3 != null)
                    {
                        sb.AppendLine("<tr>");
                        foreach (ListViewItem.ListViewSubItem sub in list3.SubItems)
                        {
                            sb.AppendLine("<td style='border-collapse: collapse; border: 1px solid #bfbfbf;'>" + sub.Text + "</td>");
                            //sb.Append(sub.Text + "\t");
                        }
                        sb.AppendLine("</tr>");
                    }
                    sb.AppendLine();
                }
                sb.AppendLine("</tbody>");
                sb.AppendLine("</table>");
                sb.AppendLine("</br>");
            }




            sb.AppendLine("</body>");
            sb.AppendLine("</html>");

            textBoxLog.AppendText("메일 내용 작성 완료" + cr + cr + cr);

            return sb.ToString();
        }

        /* 현재 시간 출력
         */
        private void printTime()
        {
            DateTime now = DateTime.Now;
            string formattedTime = now.ToString("yyyy-MM-dd HH:mm:ss");
            textBoxLog.AppendText(formattedTime + cr);
        }

        /* 드라이버 종료
         * 드라이버 종료 및 자원 해제
         */
        private void CloseDriver()
        {
            // 드라이버 종료
            try
            {
                driver.Quit();
            }
            catch 
            {
                driver.Dispose();
                driver = null;
                printTime();
                textBoxLog.AppendText("실행 종료" + cr + cr + cr + cr);
            }
        }




        /* 이벤트 시작
         * UI, 구현에 필요한 이벤트 처리
         */

        /* 실행 버튼 클릭 이벤트
         */
        private void buttonRun_Click(object sender, EventArgs e)
        {
            //검색어 DB저장
            string bizNm = textBoxBizNm.Text;
            if (bizNm == null || "".Equals(bizNm))
            {
                MessageBox.Show("사업명을 입력하세요.");
                return;
            }
            string sql = "delete from searchWord;"; //검색어 초기화
            cs.ExcuteSql(sql);
            sql = "insert into searchWord values( '" + bizNm + "', '', '' );";
            cs.ExcuteSql(sql);

            textBoxLog.Clear();
            printTime();
            InitSelenium();

            Run();
            RunNotice();

            DateTime now = DateTime.Now;
            string formattedTime = now.ToString("yyyy-MM-dd HH:mm:ss");

            SendMail("나라장터 조회 결과(" + formattedTime + ")", MakeMailBody());
        }

        /* 사업명 텍스트박스 엔터키 이벤트
         * 엔터키 입력 시 크롬 드라이버 실행
         * 사업명으로 검색 후 결과 리스트뷰에 출력
         */
        private void EnterTextBoxBizNm(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == System.Windows.Forms.Keys.Enter)
            {
                //검색어 DB저장
                string bizNm = textBoxBizNm.Text;
                if (bizNm == null || "".Equals(bizNm))
                {
                    MessageBox.Show("사업명을 입력하세요.");
                    return;
                }
                string sql = "delete from searchWord;"; //검색어 초기화
                cs.ExcuteSql(sql);
                sql = "insert into searchWord values( '" + bizNm + "' );";
                cs.ExcuteSql(sql);

                textBoxLog.Clear();
                printTime();
                InitSelenium();
                Run();
            }

            return;
        }

        /* 이메일 추가 버튼
         * 이메일 주소를 리스트뷰에 추가
         * 리스트뷰에 추가된 이메일 주소로 메일 발송
         */
        private void buttonEmail_Click(object sender, EventArgs e)
        {
            if(textBoxEmail.Text != null && !"".Equals(textBoxEmail.Text))
            {
                if (textBoxEmail.Text.Contains("@") == false)
                {
                    MessageBox.Show("이메일 형식이 아닙니다.");
                    return;
                }
                if (textBoxEmail.Text.Contains(".") == false)
                {
                    MessageBox.Show("이메일 형식이 아닙니다.");
                    return;
                }
                if (textBoxEmail.Text.Contains(" ") == true)
                {
                    MessageBox.Show("이메일 형식이 아닙니다.");
                    return;
                }

                //listViewEmail.Items.Clear();
                listViewEmail.Items.Add(textBoxEmail.Text);
                
                string sql = "insert into mailTarget values( '" + textBoxEmail.Text + "' );";
                cs.ExcuteSql(sql);

                textBoxEmail.Text = "";
            }
            else
            {
                MessageBox.Show("이메일을 입력하세요.");
                return;
            }
        }

        /* 타이머 60초마다 체크
         * 반복설정 시간과 비교하여 00분에만 실행
         */
        private void timerChkRun_Tick(object sender, EventArgs e)
        {
            DateTime nowDate = DateTime.Now;

            //textBoxLog.AppendText("현재시간 : " + nowDate.ToString("yyyy-MM-dd HH:mm:ss") + cr);
            //textBoxLog.AppendText("설정시간 : " + comboBoxRunTM.SelectedItem.ToString() + "시 00분" + cr);

            if ( nowDate.Hour.ToString() == comboBoxRunTM.SelectedItem.ToString() )
            {
                if(nowDate.Minute == 0)
                {
                    textBoxLog.Clear();
                    printTime();
                    InitSelenium();
                    Run();
                    RunNotice();

                    DateTime now = DateTime.Now;
                    string formattedTime = now.ToString("yyyy-MM-dd HH:mm:ss");

                    SendMail("나라장터 조회 결과(" + formattedTime + ")", MakeMailBody());
                }
            }
        }

        private void ChangeRunTM(object sender, EventArgs e)
        {
            string runTime = comboBoxRunTM.SelectedItem.ToString();
            labelTM.Text = "* 매일" + runTime + "시 00분에 실행됩니다.";

            string sql = "delete from sendTime;";
            cs.ExcuteSql(sql);

            sql = "insert into sendTime values( '" + runTime +"' );";
            cs.ExcuteSql(sql);
        }

        /* MailForm Delegate
         */ 
        public void GetMailInfo()
        {
            string from = mailForm.GetFrom();
            string pass = mailForm.GetPass();

            smtp = from;
            sendPassword = pass;
        }

        private void buttonMailAtuh_Click(object sender, EventArgs e)
        {
            try
            {
                mailForm = new MailForm();

                mailForm.FormSendEvent += new MailForm.FormSendDataHandler(GetMailInfo);
            }
            catch (Exception ex)
            {
                textBoxLog.AppendText( ex.ToString() + cr);
            }

            mailForm.ShowDialog();
        }

        private void buttonEmailDel_Click(object sender, EventArgs e)
        {
            if (listViewEmail.SelectedItems.Count > 0)
            {
                ListViewItem selectedItem = listViewEmail.SelectedItems[0];
                string email = selectedItem.Text;
                // 리스트뷰에서 선택된 항목 삭제
                listViewEmail.Items.Remove(selectedItem);
                // 데이터베이스에서 이메일 삭제
                string sql = "delete from mailTarget where email = '" + email + "';";
                cs.ExcuteSql(sql);
            }
            else
            {
                MessageBox.Show("삭제할 이메일을 선택하세요.");

            }
        }

        /* TrayIcon 
         */
        private void DClick_Tray(object sender, MouseEventArgs e)
        {
            if (this.Visible == false)
            {
                this.Visible = true;

                if (this.WindowState == FormWindowState.Minimized)
                    this.WindowState = FormWindowState.Normal; // 최소화를 멈춘다 

                this.Activate(); // 폼을 활성화 시킨다
            }
            else if (this.Visible == true)
            {
                this.Visible = false;
            }
        }

        public void CloseApp(object sender, EventArgs e)
        {
            textBoxLog.AppendText("CloseApp 클릭" + cr);
            Application.Exit();
        }

        public void HideForm(object sender, EventArgs e)
        {
            if (this.Visible == false)
            {
                this.Visible = true;

                if (this.WindowState == FormWindowState.Minimized)
                    this.WindowState = FormWindowState.Normal; // 최소화를 멈춘다 

                this.Activate(); // 폼을 활성화 시킨다
            }
            else if (this.Visible == true)
            {
                this.Visible = false;
            }
        }

        private void FormG2B_FormClosing(object sender, FormClosingEventArgs e)
        {
            //폼 닫기 이벤트 시 닫기 취소
            //트레이 아이콘에서 CloseApp 클릭 시 제외
            if (e.CloseReason != CloseReason.UserClosing)
            {
                return; // 사용자가 닫는 것이 아닌 경우에는 아무 작업도 하지 않음
            }
            e.Cancel = true; // 폼 닫기 이벤트 취소
            this.Visible = false; // 폼을 숨김
        }

        private void ConetxtCursorHand(object sender, EventArgs e)
        {
            //textBoxLog.AppendText("컨트롤에 커서 핸드 모양 적용" + cr);
            // 컨트롤에 커서 핸드 모양 적용
            this.Cursor = Cursors.Hand;
            /*
            textBoxBizNm.Cursor = Cursors.IBeam;
            textBoxLog.Cursor = Cursors.Arrow;
            listViewOrderPlan.Cursor = Cursors.Arrow;
            listViewSpec.Cursor = Cursors.Arrow;
            listViewEmail.Cursor = Cursors.Arrow;
            */
        }
            

    }
}




Mail 계정 관리 폼(MailForm.cs)

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;
using System.Data.SQLite;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;

namespace g2b
{
    public partial class MailForm : Form
    {
        readonly CommonSQL cs = new CommonSQL();

        //Form 대리자 선언
        public delegate void FormSendDataHandler();
        public event FormSendDataHandler FormSendEvent;
        private string from = "";
        private string pass = "";
        //private string to = "";

        public MailForm()
        {
            InitializeComponent();

            InitForm();
        }

        public string GetFrom() 
        {
            return from;
        }

        public string GetPass()
        {
            return pass;
        }


        public void InitForm()
        {
            try
            {
                string sql = "select fromUser, pw from mailAuth";
                SQLiteDataReader sdr = cs.SelectSql(sql);

                while (sdr.Read())
                {
                    from = textBoxFrom.Text = sdr["fromUser"].ToString();
                    pass = textBoxPass.Text = sdr["pw"].ToString();
                    //to = textBoxTo.Text = sdr["toUser"].ToString();
                }

                sdr.Close();
            }
            catch (Exception e)
            {
                cs.WriteLog(e.ToString());
            }
/*            finally
            {
                cs.CloseConn();
            }*/
        }

        private void ButtonClose_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void ButtonSave_Click(object sender, EventArgs e)
        {
            if (textBoxFrom.Text.Contains("@") == false)
            {
                MessageBox.Show("이메일 형식이 아닙니다.");
                return;
            }
            if (textBoxFrom.Text.Contains(".") == false)
            {
                MessageBox.Show("이메일 형식이 아닙니다.");
                return;
            }
            if (textBoxFrom.Text.Contains(" ") == true)
            {
                MessageBox.Show("이메일 형식이 아닙니다.");
                return;
            }

            string delSql = "delete from mailAuth";
            cs.ExcuteSql(delSql);

            //string insSql = "insert into mail (fromUser, pw) values ('" + textBoxFrom.Text + "', '" + textBoxPass.Text + "', '" + textBoxTo.Text + "')";
            string insSql = "insert into mailAuth (fromUser, pw) values ('" + textBoxFrom.Text + "', '" + textBoxPass.Text + "')";
            int res = cs.ExcuteSql(insSql);

            if (res > 0)
            {
                InitForm();
                MessageBox.Show("저장되었습니다.");
            }

            this.FormSendEvent();

            this.Close();
        }
    }
}


내장DB관리 폼(CommonSQL.cs)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.SQLite;
using System.Windows.Forms;
using System.IO;

namespace g2b
{
    class CommonSQL
    {
        public SQLiteConnection conn = null;
        public String sqlFilePath = Application.StartupPath + "\\" + @"data.sqlite";
        public String sqlConnStr = "";

        public CommonSQL()
        {
            sqlConnStr = "Data Source=" + sqlFilePath + ";Version=3;";
        }

        public void CloseConn()
        {
            conn.Close();
        }

        //데이터베이스
        public Boolean InitDB()
        {
            try
            {
                FileInfo fi = new FileInfo(sqlFilePath);

                if (!fi.Exists)
                {
                    SQLiteConnection.CreateFile(sqlFilePath);
                    
                    //메일 사용자 테이블 생성
                    string sql = "create table mailAuth (fromUser varchar(30), pw varchar(30) )";
                    ExcuteSql(sql);

                    //검색어 저장 테이블 생성
                    sql = "create table searchWord ( word varchar(100), radOrgan varchar(30), organName varchar(100)  )";
                    ExcuteSql(sql);

                    //메일 대상 테이블 생성
                    sql = "create table mailTarget (email varchar(100) )";
                    ExcuteSql(sql);

                    //발송시간 테이블 생성
                    sql = "create table sendTime (time varchar(10) )";
                    ExcuteSql(sql);
                    sql = "insert into sendTime values( '17' );";
                    ExcuteSql(sql);

                    //업무구분 테이블 생성
                    sql = "create table bizType (type varchar(30) )";
                    ExcuteSql(sql);
                    sql = "insert into bizType values( '전체' );";
                    ExcuteSql(sql);

                    //타입 선택 테이블 생성
                    //sql = "create table chceckType ( before char(1), anncm char(1) )";
                    //ExcuteSql(sql);

                    //타입 선택 데이터 입력
                    //sql = "insert into chceckType values( '0', '0' );";
                    //ExcuteSql(sql);

                    //로그 테이블 생성
                    sql = "CREATE TABLE history ( dt REAL DEFAULT(DATETIME('now')),	errMsg TEXT); ";
                    ExcuteSql(sql);
                }
            }
            catch (Exception e)
            {
                WriteLog(e.ToString());
                return false;
            }

            return true;
        }

        public SQLiteDataReader SelectSql(string sql)
        {
            SQLiteDataReader sdr = null;

            try
            {
                conn = new SQLiteConnection(sqlConnStr);
                conn.Open();
                
                SQLiteCommand command = new SQLiteCommand(sql, conn);
                sdr = command.ExecuteReader();
            }
            catch (Exception e)
            {
                WriteLog(e.ToString());
            }
            /*finally
            {
                conn.Close();
            }*/

            return sdr;
        }

        public int ExcuteSql(string sql)
        {
            int result = 0;

            try
            {
                conn = new SQLiteConnection(sqlConnStr);
                conn.Open();
                
                //WriteLog("sql : " + sql);

                SQLiteCommand command = new SQLiteCommand(sql, conn);
                result = command.ExecuteNonQuery();
            }
            catch (Exception e)
            {
                WriteLog(e.ToString());
            }
            /*finally
            {
                conn.Close();
            }*/

            return result;
        }

        public void WriteLog(String msg)
        {
            StreamWriter sw = new StreamWriter(Application.StartupPath + @"\log.txt", true);
            sw.WriteLine(msg);
            sw.Close();
        }
    }
}




반응형
댓글