// ==UserScript==
// @name         종합 웹 크롤러 8.6 초격차 개선 버전
// @namespace    http://tampermonkey.net/
// @version      9.0.0
// @description  고급 기능을 갖춘 웹 크롤러로 더 빠르고 많은 검색 결과를 실시간으로 수집하고 자세한 분석을 제공합니다 (초격차 개선 버전)
// @author       YourName
// @match        *://www.google.com/search*
// @match        *://www.bing.com/search*
// @match        *://search.daum.net/search*
// @match        *://search.naver.com/search*
// @match        *://search.yahoo.com/search*
// @match        *://duckduckgo.com/*
// @match        *://www.startpage.com/do/search*
// @match        *://www.ecosia.org/search*
// @match        *://www.qwant.com/*
// @match        *://ahmia.fi/*
// @match        *://torchsearch.de/*
// @match        *://notracker.org/*
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_notification
// @connect      *
// @require      https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js
// @require      https://cdn.jsdelivr.net/npm/chart.js
// ==/UserScript==

(function() {
    'use strict';

    // *******************************
    // API 키 설정 (필요 시 본인의 API 키로 변경)
    // *******************************
    const WHOIS_API_KEY = 'YOUR_JSONWHOIS_API_KEY'; // 여기에 본인의 JSONWHOIS API 키를 입력하세요.

    GM_addStyle(`
        /* UI 전반적인 스타일 개선 */
        #crawler-container {
            position: fixed;
            top: 10px;
            right: 10px;
            background-color: #ffffff;
            border: 2px solid #2196F3;
            padding: 10px;
            z-index: 10000;
            width: 400px;
            max-height: 80vh;
            overflow: hidden;
            box-shadow: 0 4px 12px rgba(0,0,0,0.3);
            border-radius: 10px;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            font-size: 14px;
            resize: both;
            display: flex;
            flex-direction: column;
            transition: all 0.3s ease;
        }
        #crawler-container header {
            cursor: move;
            background-color: #2196F3;
            color: white;
            padding: 8px 12px;
            border-radius: 8px 8px 0 0;
            text-align: center;
            font-size: 18px;
            font-weight: bold;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        #crawler-container header .title {
            flex-grow: 1;
        }
        #crawler-container header .close-btn {
            cursor: pointer;
            font-size: 20px;
            color: #FF5252;
            margin-left: 10px;
        }
        #crawler-container .tabs {
            display: flex;
            border-bottom: 2px solid #2196F3;
            margin-top: 8px;
        }
        #crawler-container .tab {
            flex: 1;
            padding: 8px 0;
            text-align: center;
            cursor: pointer;
            background-color: #E3F2FD;
            border-right: 1px solid #BBDEFB;
            font-weight: bold;
            transition: background-color 0.3s, color 0.3s;
            font-size: 13px;
        }
        #crawler-container .tab:last-child {
            border-right: none;
        }
        #crawler-container .tab.active {
            background-color: #FFFFFF;
            border-bottom: 3px solid #2196F3;
            color: #2196F3;
        }
        #crawler-container .tab-content {
            display: none;
            padding: 10px;
            overflow-y: auto;
            flex-grow: 1;
        }
        #crawler-container .tab-content.active {
            display: block;
        }
        #crawler-container button {
            padding: 6px 12px;
            margin: 4px 2px;
            border: none;
            border-radius: 4px;
            background-color: #2196F3;
            color: white;
            cursor: pointer;
            transition: background-color 0.3s;
            font-size: 13px;
        }
        #crawler-container button:hover {
            background-color: #1976D2;
        }
        #crawler-container button.export-btn {
            background-color: #4CAF50;
        }
        #crawler-container button.export-btn:hover {
            background-color: #388E3C;
        }
        #crawler-container button.pause-btn {
            background-color: #FFC107;
        }
        #crawler-container button.pause-btn:hover {
            background-color: #FFA000;
        }
        #crawler-container .options,
        #crawler-container .filters,
        #crawler-container .exclude-domains,
        #crawler-container .advanced-options,
        #crawler-container .controls,
        #crawler-container .search-engines,
        #crawler-container .api-settings {
            margin-bottom: 12px;
            display: flex;
            flex-wrap: wrap;
            align-items: center;
            justify-content: space-between;
        }
        #crawler-container .options label,
        #crawler-container .advanced-options label,
        #crawler-container .controls label,
        #crawler-container .search-engines label,
        #crawler-container .api-settings label {
            margin-right: 12px;
            display: inline-flex;
            align-items: center;
            font-weight: normal;
            font-size: 13px;
        }
        #crawler-container .options input[type="checkbox"],
        #crawler-container .advanced-options input[type="checkbox"],
        #crawler-container .controls input[type="number"],
        #crawler-container .search-engines select,
        #crawler-container .api-settings input[type="text"] {
            margin-right: 6px;
        }
        #crawler-container input[type="text"],
        #crawler-container textarea,
        #crawler-container select {
            width: 100%;
            padding: 6px;
            margin: 4px 0 8px 0;
            border: 1px solid #B0BEC5;
            border-radius: 4px;
            font-size: 13px;
        }
        #crawler-container table {
            border-collapse: collapse;
            width: 100%;
            margin-top: 8px;
            font-size: 13px;
        }
        #crawler-container th, #crawler-container td {
            padding: 8px;
            border: 1px solid #E0E0E0;
            text-align: left;
            vertical-align: top;
        }
        #crawler-container th {
            background-color: #2196F3;
            color: white;
            position: sticky;
            top: 0;
            z-index: 1;
            cursor: pointer;
        }
        #crawler-container .log {
            max-height: 150px;
            overflow-y: auto;
            background-color: #E3F2FD;
            padding: 8px;
            border: 1px solid #81D4FA;
            border-radius: 4px;
            font-size: 13px;
            white-space: pre-wrap;
        }
        #crawler-container progress {
            width: 70%;
            height: 16px;
            margin-right: 8px;
            border-radius: 4px;
        }
        #crawler-container .status {
            margin-top: 10px;
            font-weight: bold;
            font-size: 13px;
        }
        #crawler-container .chart-container {
            position: relative;
            height: 200px;
            width: 100%;
            margin-top: 15px;
        }
        #crawler-container .notification-area {
            flex-direction: row;
            align-items: center;
            width: 100%;
        }
        @media (max-width: 600px) {
            #crawler-container {
                width: 95%;
                right: 2.5%;
            }
            #crawler-container .controls,
            #crawler-container .options,
            #crawler-container .advanced-options,
            #crawler-container .notification-area,
            #crawler-container .search-engines,
            #crawler-container .api-settings {
                flex-direction: column;
                align-items: flex-start;
            }
            #crawler-container .search-engines select,
            #crawler-container .api-settings input[type="text"] {
                width: 100%;
            }
            #crawler-container progress {
                width: 100%;
            }
        }
        /* WHOIS Modal 스타일 */
        #whois-modal {
            display: none;
            position: fixed;
            z-index: 10001;
            left: 50%;
            top: 50%;
            transform: translate(-50%, -50%);
            width: 80%;
            max-width: 700px;
            height: 70vh;
            background-color: #ffffff;
            border: 2px solid #2196F3;
            border-radius: 10px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.3);
            overflow: hidden;
            flex-direction: column;
            display: flex;
        }
        #whois-modal header {
            background-color: #2196F3;
            color: white;
            padding: 10px 16px;
            cursor: move;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        #whois-modal header .close-modal-btn {
            cursor: pointer;
            font-size: 20px;
            color: #FF5252;
        }
        #whois-modal .modal-content {
            padding: 16px;
            overflow-y: auto;
            flex-grow: 1;
            background-color: #E3F2FD;
            font-size: 13px;
        }
        /* Tooltip 스타일 */
        .preview-tooltip {
            position: absolute;
            background-color: #ffffff;
            border: 1px solid #90CAF9;
            border-radius: 5px;
            padding: 8px;
            z-index: 10002;
            box-shadow: 0 3px 8px rgba(0,0,0,0.2);
            display: none;
            width: 280px;
        }
        .preview-tooltip iframe {
            width: 100%;
            height: 180px;
            border: none;
            border-radius: 4px;
        }
        /* 추가된 스타일 */
        #crawler-container .advanced-results {
            display: flex;
            flex-direction: column;
            gap: 12px;
            margin-top: 12px;
        }
        #crawler-container .advanced-results .result-section {
            background-color: #f5f5f5;
            padding: 8px;
            border-radius: 4px;
            border: 1px solid #ccc;
        }
    `);

    const container = document.createElement('div');
    container.id = 'crawler-container';
    container.innerHTML = `
        <header>
            <span class="title">🔥 웹 크롤러 8.6</span>
            <span class="close-btn" title="닫기">&times;</span>
        </header>
        <div class="tabs">
            <div class="tab active" data-tab="settings">설정</div>
            <div class="tab" data-tab="results">결과</div>
            <div class="tab" data-tab="logs">로그</div>
            <div class="tab" data-tab="analytics">분석</div>
            <div class="tab" data-tab="about">정보</div>
        </div>
        <div class="tab-content active" id="settings">
            <div class="notification-area">
                <button id="startBtn" title="크롤링 시작">▶️ 시작</button>
                <button id="pauseBtn" class="pause-btn" title="크롤링 일시정지/재개">⏸️ 일시정지</button>
                <button id="stopBtn" title="크롤링 중지">⏹️ 중지</button>
                <button id="resetBtn" title="크롤링 초기화">🔄 초기화</button>
                <button id="exportBtn" class="export-btn" title="결과 내보내기">💾 내보내기</button>
                <button id="clearLogsBtn" title="로그 초기화">🧹 로그 초기화</button>
            </div>
            <div class="search-engines">
                <label for="searchEngineSelect">검색 엔진 선택:</label>
                <select id="searchEngineSelect">
                    <option value="google" selected>Google</option>
                    <option value="bing">Bing</option>
                    <option value="daum">Daum</option>
                    <option value="naver">Naver</option>
                    <option value="yahoo">Yahoo</option>
                    <option value="duckduckgo">DuckDuckGo</option>
                    <option value="startpage">StartPage</option>
                    <option value="ecosia">Ecosia</option>
                    <option value="qwant">Qwant</option>
                    <option value="ahmia">Ahmia (Tor)</option>
                    <option value="torch">Torch (Tor)</option>
                    <option value="notracker">NOTRACKER (Tor)</option>
                </select>
            </div>
            <div class="api-settings">
                <label for="whoisApiKey">WHOIS API 키:</label>
                <input type="text" id="whoisApiKey" placeholder="API 키를 입력하세요" value="${WHOIS_API_KEY}">
            </div>
            <div class="options">
                <label><input type="checkbox" id="toggleHTML" checked> HTML 가져오기</label>
                <label><input type="checkbox" id="toggleImages" checked> 이미지 가져오기</label>
                <label><input type="checkbox" id="toggleAds" checked> 광고 제외</label>
                <label><input type="checkbox" id="toggleTechStack" checked> 기술 스택 분석</label>
                <label><input type="checkbox" id="toggleOpenGraph" checked> OpenGraph 정보 가져오기</label>
                <label><input type="checkbox" id="toggleSecurityHeaders" checked> 보안 헤더 분석</label>
                <label><input type="checkbox" id="toggleDomainAge" checked> 도메인 연령 분석</label>
                <label><input type="checkbox" id="toggleBacklinks" checked> 백링크 분석</label>
                <label><input type="checkbox" id="togglePageStructure" checked> 페이지 구조 분석</label>
            </div>
            <div class="filters">
                <label for="filterInput">필터 단어 (정규식 또는 단어, 쉼표로 구분):</label>
                <input type="text" id="filterInput" placeholder="예: 기부, 광고, /promo/i">
            </div>
            <div class="exclude-domains">
                <label for="excludeDomainsInput">제외할 도메인 (쉼표로 구분):</label>
                <input type="text" id="excludeDomainsInput" placeholder="예: facebook.com, youtube.com">
            </div>
            <div class="advanced-options">
                <label for="duplicateThreshold">중복 허용 횟수:</label>
                <input type="number" id="duplicateThreshold" value="2" min="1" style="width: 60px;">
                <label for="retryAttempts">재시도 횟수:</label>
                <input type="number" id="retryAttempts" value="3" min="1" style="width: 60px;">
            </div>
            <div style="margin-top:12px;">
                <label for="progressBar">진행률:</label>
                <progress id="progressBar" value="0" max="100"></progress>
                <span id="progressText">0%</span>
            </div>
            <div class="status">
                현재 페이지: <span id="currentPage">0</span> /
                총 페이지: <span id="totalPages">0</span>
                <br>
                방문된 사이트: <span id="visitedCount">0</span>
                <br>
                총 요청 수: <span id="totalRequests">0</span>
                <br>
                성공 요청 수: <span id="successfulRequests">0</span>
                <br>
                실패 요청 수: <span id="failedRequests">0</span>
                <br>
                경과 시간: <span id="elapsedTime">00:00:00</span>
                <br>
                예상 남은 시간: <span id="remainingTime">00:00:00</span>
            </div>
        </div>
        <div class="tab-content" id="results">
            <div style="margin-top:8px; max-height: 300px; overflow: auto;">
                <table id="resultsTable">
                    <thead>
                        <tr>
                            <th data-sort="0">사이트 이름 &#x25B2;&#x25BC;</th>
                            <th data-sort="1">주소 &#x25B2;&#x25BC;</th>
                            <th data-sort="2">설명 &#x25B2;&#x25BC;</th>
                            <th>이미지</th>
                            <th>HTML 링크</th>
                            <th>WHOIS 정보</th>
                            <th data-sort="6">페이지 로딩 시간 (ms) &#x25B2;&#x25BC;</th>
                            <th>언어</th>
                            <th>메타 키워드</th>
                            <th>메타 robots</th>
                            <th>OpenGraph</th>
                            <th>보안 헤더</th>
                            <th>도메인 연령</th>
                            <th>기술 스택</th>
                            <th>백링크</th>
                            <th>페이지 구조</th>
                        </tr>
                    </thead>
                    <tbody id="resultsBody">
                    </tbody>
                </table>
            </div>
            <div style="margin-top:10px;">
                <input type="text" id="searchResultsInput" placeholder="결과 검색...">
            </div>
        </div>
        <div class="tab-content" id="logs">
            <div class="log" id="log">
                <strong>로그:</strong>
            </div>
        </div>
        <div class="tab-content" id="analytics">
            <div class="chart-container">
                <canvas id="crawlChart"></canvas>
            </div>
            <div class="chart-container">
                <canvas id="loadTimeChart"></canvas>
            </div>
            <div class="chart-container">
                <canvas id="domainDistributionChart"></canvas>
            </div>
            <div class="chart-container">
                <canvas id="errorRateChart"></canvas>
            </div>
            <div class="chart-container">
                <canvas id="cumulativeVisitsChart"></canvas>
            </div>
            <div class="chart-container">
                <canvas id="keywordDistributionChart"></canvas>
            </div>
            <div class="chart-container">
                <canvas id="languageDistributionChart"></canvas>
            </div>
            <div class="chart-container">
                <canvas id="techStackChart"></canvas>
            </div>
            <div class="chart-container">
                <canvas id="domainAgeChart"></canvas>
            </div>
            <div class="chart-container" id="backlinksChartContainer" style="display:none;">
                <canvas id="backlinksChart"></canvas>
            </div>
            <div class="chart-container" id="pageStructureChartContainer" style="display:none;">
                <canvas id="pageStructureChart"></canvas>
            </div>
            <div id="whois-modal">
                <header>
                    <span>WHOIS 정보</span>
                    <span class="close-modal-btn" title="닫기">&times;</span>
                </header>
                <div class="modal-content" id="whoisContent">
                    <!-- WHOIS 정보가 여기에 표시됩니다 -->
                </div>
            </div>
            <div class="advanced-results">
                <div class="result-section" id="backlinksSection" style="display:none;">
                    <h3>추가 분석 1: 백링크 분석</h3>
                    <p>백링크 정보는 현재 지원되지 않습니다.</p>
                </div>
                <div class="result-section" id="pageStructureSection" style="display:none;">
                    <h3>추가 분석 2: 페이지 구조 분석</h3>
                    <p>페이지 구조 정보는 현재 지원되지 않습니다.</p>
                </div>
                <!-- 필요에 따라 추가 분석 섹션을 추가할 수 있습니다 -->
            </div>
        </div>
        <div class="tab-content" id="about">
            <h2>웹 크롤러 8.6 정보</h2>
            <p>종합 웹 크롤러는 다양한 검색 엔진에서 빠르게 검색 결과를 수집하고 심층 분석할 수 있는 도구입니다. 사용자의 편의성과 데이터 가독성을 극대화하여 초격차로 개선된 버전입니다.</p>
            <ul>
                <li>지원하는 검색 엔진: Google, Bing, Daum, Naver, Yahoo, DuckDuckGo, StartPage, Ecosia, Qwant, Ahmia (Tor), Torch (Tor), NOTRACKER (Tor)</li>
                <li>실시간 검색어 감지 및 고속 크롤링</li>
                <li>고급 필터링 및 제외 옵션 제공</li>
                <li>결과를 XLSX, CSV, JSON 형식으로 내보내기</li>
                <li>시각화된 데이터 차트 및 그래프 제공</li>
                <li>사용자 친화적인 인터페이스 및 반응형 디자인</li>
                <li>결과 테이블 정렬 및 실시간 검색 기능 추가</li>
                <li>상세한 로그 제공</li>
                <li>페이지 로딩 시간 및 언어 분석</li>
                <li>WHOIS 정보 연동 및 즉시 보기 기능</li>
                <li>도메인 연령 분석 기능 추가</li>
                <li>추가적인 보안 헤더 분석 기능</li>
                <li>기술 스택 분석 기능 강화</li>
                <li>다양한 알림 방식 지원</li>
                <li>자동 스크롤 및 실시간 업데이트</li>
                <li>고급 에러 핸들링 및 재시도 로직 강화</li>
                <li>로그 초기화 및 결과 검색 기능 추가</li>
                <li>추가 분석 탭에서 다양한 그래프 제공</li>
                <li>도메인 분포 및 오류 발생률 분석</li>
                <li>누적 방문 횟수 추적</li>
                <li>HTML 링크 클릭 시 UI 내에서 사이트 미리보기 제공</li>
                <li>마우스 호버 시 사이트 미리보기 표시</li>
                <li>WHOIS 정보 바로보기 및 상세 분석 기능 추가</li>
                <li>추가 메타 정보 수집 및 분석 기능 강화</li>
                <li>더 빠르고 많은 사이트 정보 수집 기능 추가</li>
                <li>사이트 기술 스택 심층 분석 기능 추가</li>
                <li>추가 분석: 백링크 및 페이지 구조 분석 기능 (미지원)</li>
            </ul>
            <p><strong>버전:</strong> 9.0.0</p>
            <p><strong>제작자:</strong> YourName</p>
            <p><strong>문의:</strong> your.email@example.com</p>
        </div>
    `;
    document.body.appendChild(container);

    // 드래그 기능
    (function makeDraggable(elmnt) {
        const header = elmnt.querySelector('header');
        const closeBtn = elmnt.querySelector('.close-btn');
        let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
        header.onmousedown = dragMouseDown;

        closeBtn.onclick = () => {
            elmnt.style.display = 'none';
            log('🗑 크롤러 창을 닫았습니다.');
        }

        function dragMouseDown(e) {
            e = e || window.event;
            e.preventDefault();
            pos3 = e.clientX;
            pos4 = e.clientY;
            document.onmouseup = closeDragElement;
            document.onmousemove = elementDrag;
        }

        function elementDrag(e) {
            e = e || window.event;
            e.preventDefault();
            pos1 = pos3 - e.clientX;
            pos2 = pos4 - e.clientY;
            pos3 = e.clientX;
            pos4 = e.clientY;
            elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
            elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
        }

        function closeDragElement() {
            document.onmouseup = null;
            document.onmousemove = null;
        }
    })(container);

    // 탭 설정
    (function setupTabs() {
        const tabs = container.querySelectorAll('.tab');
        const tabContents = container.querySelectorAll('.tab-content');

        tabs.forEach(tab => {
            tab.addEventListener('click', () => {
                tabs.forEach(t => t.classList.remove('active'));
                tab.classList.add('active');

                const target = tab.getAttribute('data-tab');
                tabContents.forEach(content => {
                    if(content.id === target){
                        content.classList.add('active');
                    } else {
                        content.classList.remove('active');
                    }
                });

                // 추가 분석 섹션 표시/숨기기
                if (target === 'analytics') {
                    toggleAdvancedResults();
                }
            });
        });
    })();

    // 변수 초기화
    let isRunning = false;
    let isPaused = false;
    let currentPage = 1;
    let totalPages = 0;
    let results = [];
    let searchQuery = '';
    let searchEngine = 'google';
    let visitedLinks = new Map();
    let logMessages = [];
    let filterPatterns = [];
    let excludeDomains = [];
    let duplicateThreshold = 2;
    let retryAttempts = 3;
    const fixedRetryInterval = 100; // 크롤링 속도 고정 100ms
    let startTime = null;
    let elapsedInterval = null;
    let crawlChart = null;
    let loadTimeChart = null;
    let domainDistributionChart = null;
    let errorRateChart = null;
    let cumulativeVisitsChart = null;
    let keywordDistributionChart = null;
    let languageDistributionChart = null;
    let techStackChart = null;
    let domainAgeChart = null;
    let backlinksChart = null;
    let pageStructureChart = null;
    let crawlData = {
        labels: [],
        datasets: [{
            label: '방문 횟수',
            data: [],
            backgroundColor: 'rgba(33, 150, 243, 0.6)',
            borderColor: 'rgba(33, 150, 243, 1)',
            borderWidth: 1
        },
        {
            label: '평균 페이지 로딩 시간 (ms)',
            data: [],
            backgroundColor: 'rgba(76, 175, 80, 0.6)',
            borderColor: 'rgba(76, 175, 80, 1)',
            borderWidth: 1
        }]
    };
    let loadTimeData = {
        labels: [],
        datasets: [{
            label: '페이지 로딩 시간 (ms)',
            data: [],
            backgroundColor: 'rgba(255, 193, 7, 0.6)',
            borderColor: 'rgba(255,193,7,1)',
            borderWidth: 1
        }]
    };
    let domainDistributionData = {
        labels: [],
        datasets: [{
            label: '도메인 분포',
            data: [],
            backgroundColor: [
                'rgba(33, 150, 243, 0.6)',
                'rgba(255, 87, 34, 0.6)',
                'rgba(76, 175, 80, 0.6)',
                'rgba(156, 39, 176, 0.6)',
                'rgba(255, 152, 0, 0.6)',
                'rgba(244, 67, 54, 0.6)',
                'rgba(158, 158, 158, 0.6)'
            ],
            borderColor: 'rgba(33, 150, 243, 1)',
            borderWidth: 1
        }]
    };
    let errorRateData = {
        labels: [],
        datasets: [{
            label: '오류 발생 수',
            data: [],
            backgroundColor: 'rgba(255, 99, 132, 0.6)',
            borderColor: 'rgba(255, 99, 132,1)',
            borderWidth: 1
        }]
    };
    let cumulativeVisitsData = {
        labels: [],
        datasets: [{
            label: '누적 방문 횟수',
            data: [],
            backgroundColor: 'rgba(3, 169, 244, 0.6)',
            borderColor: 'rgba(3, 169, 244,1)',
            borderWidth: 1
        }]
    };
    let keywordDistributionData = {
        labels: [],
        datasets: [{
            label: '키워드 분포',
            data: [],
            backgroundColor: [
                'rgba(63, 81, 181, 0.6)',
                'rgba(255, 152, 0, 0.6)',
                'rgba(76, 175, 80, 0.6)',
                'rgba(244, 67, 54, 0.6)',
                'rgba(33, 150, 243, 0.6)',
                'rgba(153, 102, 255, 0.6)'
            ],
            borderColor: 'rgba(63, 81, 181,1)',
            borderWidth: 1
        }]
    };
    let languageDistributionData = {
        labels: [],
        datasets: [{
            label: '언어 분포',
            data: [],
            backgroundColor: [
                'rgba(38, 166, 154, 0.6)',
                'rgba(211, 47, 47, 0.6)',
                'rgba(3, 169, 244, 0.6)',
                'rgba(255, 193, 7, 0.6)',
                'rgba(156, 39, 176, 0.6)',
                'rgba(0, 188, 212, 0.6)'
            ],
            borderColor: 'rgba(38, 166, 154,1)',
            borderWidth: 1
        }]
    };
    let techStackData = {
        labels: [],
        datasets: [{
            label: '기술 스택 분포',
            data: [],
            backgroundColor: [
                'rgba(103, 58, 183, 0.6)',
                'rgba(255, 202, 40, 0.6)',
                'rgba(0, 150, 136, 0.6)',
                'rgba(233, 30, 99, 0.6)',
                'rgba(245, 124, 0, 0.6)',
                'rgba(255, 235, 59, 0.6)'
            ],
            borderColor: 'rgba(103, 58, 183,1)',
            borderWidth: 1
        }]
    };
    let domainAgeData = {
        labels: [],
        datasets: [{
            label: '도메인 연령 분포 (년)',
            data: [],
            backgroundColor: [
                'rgba(255, 159, 64, 0.6)',
                'rgba(75, 192, 192, 0.6)',
                'rgba(54, 162, 235, 0.6)',
                'rgba(153, 102, 255, 0.6)',
                'rgba(255, 99, 132, 0.6)'
            ],
            borderColor: 'rgba(255, 159, 64,1)',
            borderWidth: 1
        }]
    };
    // 추가 차트 데이터
    let backlinksData = {
        labels: [],
        datasets: [{
            label: '백링크 수',
            data: [],
            backgroundColor: 'rgba(255, 206, 86, 0.6)',
            borderColor: 'rgba(255, 206, 86, 1)',
            borderWidth: 1
        }]
    };
    let pageStructureData = {
        labels: ['헤더', '푸터', '네비게이션', '콘텐츠', '사이드바'],
        datasets: [{
            label: '페이지 구조 요소 분포',
            data: [],
            backgroundColor: [
                'rgba(255, 99, 132, 0.6)',
                'rgba(54, 162, 235, 0.6)',
                'rgba(255, 206, 86, 0.6)',
                'rgba(75, 192, 192, 0.6)',
                'rgba(153, 102, 255, 0.6)'
            ],
            borderColor: 'rgba(255, 159, 64, 1)',
            borderWidth: 1
        }]
    };

    // 요소 참조
    const startBtn = document.getElementById('startBtn');
    const pauseBtn = document.getElementById('pauseBtn');
    const stopBtn = document.getElementById('stopBtn');
    const resetBtn = document.getElementById('resetBtn');
    const exportBtn = document.getElementById('exportBtn');
    const clearLogsBtn = document.getElementById('clearLogsBtn');
    const progressBar = document.getElementById('progressBar');
    const progressText = document.getElementById('progressText');
    const resultsBody = document.getElementById('resultsBody');
    const logContainer = document.getElementById('log');
    const currentPageElem = document.getElementById('currentPage');
    const totalPagesElem = document.getElementById('totalPages');
    const toggleHTML = document.getElementById('toggleHTML');
    const toggleImages = document.getElementById('toggleImages');
    const toggleAds = document.getElementById('toggleAds');
    const toggleTechStack = document.getElementById('toggleTechStack');
    const toggleOpenGraph = document.getElementById('toggleOpenGraph');
    const toggleSecurityHeaders = document.getElementById('toggleSecurityHeaders');
    const toggleDomainAge = document.getElementById('toggleDomainAge');
    const toggleBacklinks = document.getElementById('toggleBacklinks');
    const togglePageStructure = document.getElementById('togglePageStructure');
    const filterInput = document.getElementById('filterInput');
    const excludeDomainsInput = document.getElementById('excludeDomainsInput');
    const duplicateThresholdInput = document.getElementById('duplicateThreshold');
    const retryAttemptsInput = document.getElementById('retryAttempts');
    const visitedCountElem = document.getElementById('visitedCount');
    const elapsedTimeElem = document.getElementById('elapsedTime');
    const remainingTimeElem = document.getElementById('remainingTime');
    const crawlChartCtx = document.getElementById('crawlChart').getContext('2d');
    const loadTimeChartCtx = document.getElementById('loadTimeChart').getContext('2d');
    const domainDistributionChartCtx = document.getElementById('domainDistributionChart').getContext('2d');
    const errorRateChartCtx = document.getElementById('errorRateChart').getContext('2d');
    const cumulativeVisitsChartCtx = document.getElementById('cumulativeVisitsChart').getContext('2d');
    const keywordDistributionChartCtx = document.getElementById('keywordDistributionChart').getContext('2d');
    const languageDistributionChartCtx = document.getElementById('languageDistributionChart').getContext('2d');
    const techStackChartCtx = document.getElementById('techStackChart').getContext('2d');
    const domainAgeChartCtx = document.getElementById('domainAgeChart').getContext('2d');
    const backlinksChartCtx = document.getElementById('backlinksChart')?.getContext('2d');
    const pageStructureChartCtx = document.getElementById('pageStructureChart')?.getContext('2d');
    const searchEngineSelect = document.getElementById('searchEngineSelect');
    const searchResultsInput = document.getElementById('searchResultsInput');
    const whoisModal = document.getElementById('whois-modal');
    const whoisContent = document.getElementById('whoisContent');
    const closeModalBtn = whoisModal.querySelector('.close-modal-btn');
    const whoisApiKeyInput = document.getElementById('whoisApiKey');

    // 미리보기 툴팁 생성
    const tooltip = document.createElement('div');
    tooltip.className = 'preview-tooltip';
    document.body.appendChild(tooltip);

    // 이벤트 리스너 등록
    startBtn.addEventListener('click', startCrawling);
    pauseBtn.addEventListener('click', togglePause);
    stopBtn.addEventListener('click', stopCrawling);
    resetBtn.addEventListener('click', resetCrawling);
    exportBtn.addEventListener('click', exportResults);
    clearLogsBtn.addEventListener('click', clearLogs);
    filterInput.addEventListener('input', filterResults);
    excludeDomainsInput.addEventListener('input', updateExcludeDomains);
    duplicateThresholdInput.addEventListener('change', updateDuplicateThreshold);
    retryAttemptsInput.addEventListener('change', updateRetryAttempts);
    searchEngineSelect.addEventListener('change', updateSearchEngine);
    searchResultsInput.addEventListener('input', filterResults);
    closeModalBtn.addEventListener('click', () => {
        whoisModal.style.display = 'none';
        whoisContent.innerHTML = '';
    });
    whoisApiKeyInput.addEventListener('change', updateWhoisApiKey);

    window.addEventListener('beforeunload', () => {
        resetCrawling();
    });

    // 로그 및 크롤 데이터 로드
    loadLogs();
    loadCrawlData();
    loadSettings();

    // 차트 초기화 함수
    function initializeChart() {
        crawlChart = new Chart(crawlChartCtx, {
            type: 'bar',
            data: crawlData,
            options: {
                responsive: true,
                maintainAspectRatio: false,
                scales: {
                    y: {
                        beginAtZero: true,
                        ticks: {
                            precision:0
                        }
                    }
                },
                plugins: {
                    legend: {
                        display: true,
                        position: 'top'
                    },
                    tooltip: {
                        enabled: true
                    }
                }
            }
        });

        loadTimeChart = new Chart(loadTimeChartCtx, {
            type: 'line',
            data: loadTimeData,
            options: {
                responsive: true,
                maintainAspectRatio: false,
                scales: {
                    y: {
                        beginAtZero: true,
                        ticks: {
                            precision:0
                        }
                    }
                },
                plugins: {
                    legend: {
                        display: true,
                        position: 'top'
                    },
                    tooltip: {
                        enabled: true
                    }
                }
            }
        });

        domainDistributionChart = new Chart(domainDistributionChartCtx, {
            type: 'pie',
            data: domainDistributionData,
            options: {
                responsive: true,
                maintainAspectRatio: false,
                plugins: {
                    legend: {
                        display: true,
                        position: 'right'
                    },
                    tooltip: {
                        enabled: true
                    }
                }
            }
        });

        errorRateChart = new Chart(errorRateChartCtx, {
            type: 'bar',
            data: errorRateData,
            options: {
                responsive: true,
                maintainAspectRatio: false,
                scales: {
                    y: {
                        beginAtZero: true,
                        ticks: {
                            precision:0
                        }
                    }
                },
                plugins: {
                    legend: {
                        display: false
                    },
                    tooltip: {
                        enabled: true
                    }
                }
            }
        });

        cumulativeVisitsChart = new Chart(cumulativeVisitsChartCtx, {
            type: 'line',
            data: cumulativeVisitsData,
            options: {
                responsive: true,
                maintainAspectRatio: false,
                scales: {
                    y: {
                        beginAtZero: true,
                        ticks: {
                            precision:0
                        }
                    }
                },
                plugins: {
                    legend: {
                        display: true,
                        position: 'top'
                    },
                    tooltip: {
                        enabled: true
                    }
                }
            }
        });

        keywordDistributionChart = new Chart(keywordDistributionChartCtx, {
            type: 'doughnut',
            data: keywordDistributionData,
            options: {
                responsive: true,
                maintainAspectRatio: false,
                plugins: {
                    legend: {
                        display: true,
                        position: 'right'
                    },
                    tooltip: {
                        enabled: true
                    }
                }
            }
        });

        languageDistributionChart = new Chart(languageDistributionChartCtx, {
            type: 'pie',
            data: languageDistributionData,
            options: {
                responsive: true,
                maintainAspectRatio: false,
                plugins: {
                    legend: {
                        display: true,
                        position: 'right'
                    },
                    tooltip: {
                        enabled: true
                    }
                }
            }
        });

        techStackChart = new Chart(techStackChartCtx, {
            type: 'bar',
            data: techStackData,
            options: {
                responsive: true,
                maintainAspectRatio: false,
                scales: {
                    y: {
                        beginAtZero: true,
                        ticks: {
                            precision:0
                        }
                    }
                },
                plugins: {
                    legend: {
                        display: false
                    },
                    tooltip: {
                        enabled: true
                    }
                }
            }
        });

        domainAgeChart = new Chart(domainAgeChartCtx, {
            type: 'bar',
            data: domainAgeData,
            options: {
                responsive: true,
                maintainAspectRatio: false,
                scales: {
                    y: {
                        beginAtZero: true,
                        title: {
                            display: true,
                            text: '연령 (년)'
                        }
                    }
                },
                plugins: {
                    legend: {
                        display: false
                    },
                    tooltip: {
                        enabled: true
                    }
                }
            }
        });

        // 추가 차트 초기화
        if (backlinksChartCtx) {
            backlinksChart = new Chart(backlinksChartCtx, {
                type: 'bar',
                data: backlinksData,
                options: {
                    responsive: true,
                    maintainAspectRatio: false,
                    scales: {
                        y: {
                            beginAtZero: true,
                            ticks: {
                                precision:0
                            }
                        }
                    },
                    plugins: {
                        legend: {
                            display: false
                        },
                        tooltip: {
                            enabled: true
                        }
                    }
                }
            });
        }

        if (pageStructureChartCtx) {
            pageStructureChart = new Chart(pageStructureChartCtx, {
                type: 'bar',
                data: pageStructureData,
                options: {
                    responsive: true,
                    maintainAspectRatio: false,
                    scales: {
                        y: {
                            beginAtZero: true,
                            ticks: {
                                precision:0
                            }
                        }
                    },
                    plugins: {
                        legend: {
                            display: false
                        },
                        tooltip: {
                            enabled: true
                        }
                    }
                }
            });
        }
    }
    initializeChart();

    // 설정 로드
    function loadSettings() {
        const storedSearchEngine = GM_getValue('searchEngine', 'google');
        searchEngine = storedSearchEngine;
        searchEngineSelect.value = searchEngine;

        const storedWhoisApiKey = GM_getValue('whoisApiKey', WHOIS_API_KEY);
        whoisApiKeyInput.value = storedWhoisApiKey;

        const storedToggleHTML = GM_getValue('toggleHTML', true);
        toggleHTML.checked = storedToggleHTML;

        const storedToggleImages = GM_getValue('toggleImages', true);
        toggleImages.checked = storedToggleImages;

        const storedToggleAds = GM_getValue('toggleAds', true);
        toggleAds.checked = storedToggleAds;

        const storedToggleTechStack = GM_getValue('toggleTechStack', true);
        toggleTechStack.checked = storedToggleTechStack;

        const storedToggleOpenGraph = GM_getValue('toggleOpenGraph', true);
        toggleOpenGraph.checked = storedToggleOpenGraph;

        const storedToggleSecurityHeaders = GM_getValue('toggleSecurityHeaders', true);
        toggleSecurityHeaders.checked = storedToggleSecurityHeaders;

        const storedToggleDomainAge = GM_getValue('toggleDomainAge', true);
        toggleDomainAge.checked = storedToggleDomainAge;

        const storedToggleBacklinks = GM_getValue('toggleBacklinks', false);
        toggleBacklinks.checked = storedToggleBacklinks;
        toggleBacklinks.parentElement.style.display = 'block';

        const storedTogglePageStructure = GM_getValue('togglePageStructure', false);
        togglePageStructure.checked = storedTogglePageStructure;
        togglePageStructure.parentElement.style.display = 'block';

        const storedDuplicateThreshold = GM_getValue('duplicateThreshold', 2);
        duplicateThreshold = storedDuplicateThreshold;
        duplicateThresholdInput.value = duplicateThreshold;

        const storedRetryAttempts = GM_getValue('retryAttempts', 3);
        retryAttempts = storedRetryAttempts;
        retryAttemptsInput.value = retryAttempts;

        const storedFilterInput = GM_getValue('filterInput', '');
        filterInput.value = storedFilterInput;
        updateFilterPatterns();

        const storedExcludeDomainsInput = GM_getValue('excludeDomainsInput', '');
        excludeDomainsInput.value = storedExcludeDomainsInput;
        updateExcludeDomains();
    }

    // 설정 저장 함수
    function saveSettings() {
        GM_setValue('searchEngine', searchEngine);
        GM_setValue('whoisApiKey', whoisApiKeyInput.value.trim());
        GM_setValue('toggleHTML', toggleHTML.checked);
        GM_setValue('toggleImages', toggleImages.checked);
        GM_setValue('toggleAds', toggleAds.checked);
        GM_setValue('toggleTechStack', toggleTechStack.checked);
        GM_setValue('toggleOpenGraph', toggleOpenGraph.checked);
        GM_setValue('toggleSecurityHeaders', toggleSecurityHeaders.checked);
        GM_setValue('toggleDomainAge', toggleDomainAge.checked);
        GM_setValue('toggleBacklinks', toggleBacklinks.checked);
        GM_setValue('togglePageStructure', togglePageStructure.checked);
        GM_setValue('duplicateThreshold', duplicateThreshold);
        GM_setValue('retryAttempts', retryAttempts);
        GM_setValue('filterInput', filterInput.value.trim());
        GM_setValue('excludeDomainsInput', excludeDomainsInput.value.trim());
    }

    // API 키 업데이트 함수
    function updateWhoisApiKey() {
        saveSettings();
        log(`🔄 WHOIS API 키가 업데이트되었습니다.`);
    }

    // 검색 엔진 변경 시 처리
    function updateSearchEngine() {
        searchEngine = searchEngineSelect.value;
        GM_setValue('searchEngine', searchEngine);
        log(`🔄 검색 엔진 변경: ${capitalizeFirstLetter(searchEngine)}`);
    }

    // 다양한 설정 변경 시 설정 저장
    toggleHTML.addEventListener('change', saveSettings);
    toggleImages.addEventListener('change', saveSettings);
    toggleAds.addEventListener('change', saveSettings);
    toggleTechStack.addEventListener('change', saveSettings);
    toggleOpenGraph.addEventListener('change', saveSettings);
    toggleSecurityHeaders.addEventListener('change', saveSettings);
    toggleDomainAge.addEventListener('change', saveSettings);
    toggleBacklinks.addEventListener('change', saveSettings);
    togglePageStructure.addEventListener('change', saveSettings);
    filterInput.addEventListener('change', saveSettings);
    excludeDomainsInput.addEventListener('change', saveSettings);

    // 추가 분석 섹션 토글 기능
    function toggleAdvancedResults() {
        const backlinksSection = document.getElementById('backlinksSection');
        const pageStructureSection = document.getElementById('pageStructureSection');
        const backlinksChartContainer = document.getElementById('backlinksChartContainer');
        const pageStructureChartContainer = document.getElementById('pageStructureChartContainer');

        if (toggleBacklinks.checked) {
            backlinksSection.style.display = 'block';
            backlinksChartContainer.style.display = 'block';
        } else {
            backlinksSection.style.display = 'none';
            backlinksChartContainer.style.display = 'none';
        }

        if (togglePageStructure.checked) {
            pageStructureSection.style.display = 'block';
            pageStructureChartContainer.style.display = 'block';
        } else {
            pageStructureSection.style.display = 'none';
            pageStructureChartContainer.style.display = 'none';
        }
    }

    // 로그 로드
    function loadLogs() {
        const storedLogs = GM_getValue('crawlerLogs', []);
        if (storedLogs.length > 0) {
            logMessages = JSON.parse(storedLogs);
            logContainer.innerHTML = `<strong>로그:</strong><br>` + logMessages.join('<br>');
            logContainer.scrollTop = logContainer.scrollHeight;
        }
    }

    // 필터 패턴 업데이트
    function updateFilterPatterns() {
        const input = filterInput.value.trim();
        filterPatterns = input ? input.split(',').map(word => {
            word = word.trim();
            if (word.startsWith('/') && word.endsWith('/')) {
                try {
                    return new RegExp(word.slice(1, -1), 'i');
                } catch (e) {
                    log(`⚠️ 유효하지 않은 정규식: ${word}`);
                    return null;
                }
            } else {
                return new RegExp(word, 'i');
            }
        }).filter(regex => regex !== null) : [];
        log(`🛠 필터 패턴 업데이트: [${filterPatterns.map(r => r.toString()).join(', ')}]`);
        saveSettings();
    }

    // 제외 도메인 업데이트
    function updateExcludeDomains() {
        const input = excludeDomainsInput.value.trim();
        excludeDomains = input ? input.split(',').map(domain => domain.trim().toLowerCase()) : [];
        log(`🚫 제외 도메인 업데이트: [${excludeDomains.join(', ')}]`);
        saveSettings();
    }

    // 중복 허용 횟수 업데이트
    function updateDuplicateThreshold() {
        const value = parseInt(duplicateThresholdInput.value);
        duplicateThreshold = isNaN(value) || value < 1 ? 2 : value;
        log(`🔄 중복 허용 횟수 설정: ${duplicateThreshold}`);
        saveSettings();
    }

    // 재시도 횟수 업데이트
    function updateRetryAttempts() {
        const value = parseInt(retryAttemptsInput.value);
        retryAttempts = isNaN(value) || value < 1 ? 3 : value;
        log(`🔄 재시도 횟수 설정: ${retryAttempts}`);
        saveSettings();
    }

    // 크롤링 시작 함수
    async function startCrawling() {
        if (isRunning) {
            alert('이미 크롤링이 진행 중입니다.');
            return;
        }
        getSearchQueryAndEngine();
        if (!searchQuery) {
            alert('현재 페이지에서 검색어를 찾을 수 없습니다.');
            log('⚠️ 검색어 미입력 또는 인식 실패.');
            return;
        }
        isRunning = true;
        isPaused = false;
        currentPage = 1;
        totalPages = 0;
        results = [];
        visitedLinks.clear();
        resultsBody.innerHTML = '';
        visitedCountElem.innerText = '0';
        document.getElementById('totalRequests').innerText = '0';
        document.getElementById('successfulRequests').innerText = '0';
        document.getElementById('failedRequests').innerText = '0';
        elapsedTimeElem.innerText = '00:00:00';
        remainingTimeElem.innerText = '00:00:00';
        logMessages = [];
        logContainer.innerHTML = `<strong>로그:</strong><br>`;
        GM_setValue('crawlerLogs', JSON.stringify(logMessages));
        log('✅ 크롤링을 시작합니다.');
        updateDuplicateThreshold();
        updateRetryAttempts();
        await crawlPage();
        startTime = Date.now();
        elapsedInterval = setInterval(updateElapsedTime, 1000);
    }

    // 일시정지/재개 함수
    function togglePause() {
        if (!isRunning) {
            alert('일시정지/재개할 크롤링이 없습니다.');
            return;
        }
        isPaused = !isPaused;
        if (isPaused) {
            log('⏸️ 크롤링을 일시정지하였습니다.');
            pauseBtn.innerText = '▶️ 재개';
            pauseBtn.title = '크롤링 재개';
        } else {
            log('▶️ 크롤링을 재개하였습니다.');
            pauseBtn.innerText = '⏸️ 일시정지';
            pauseBtn.title = '크롤링 일시정지';
            crawlPage();
        }
    }

    // 크롤링 중지 함수
    function stopCrawling() {
        if (!isRunning) {
            alert('중지할 크롤링이 없습니다.');
            return;
        }
        isRunning = false;
        isPaused = false;
        pauseBtn.innerText = '⏸️ 일시정지';
        pauseBtn.title = '크롤링 일시정지';
        clearInterval(elapsedInterval);
        updateProgress();
        log('🚫 크롤링을 중지하였습니다.');
        GM_notification({
            text: '크롤링이 중지되었습니다.',
            title: '웹 크롤러 8.6',
            timeout: 3000
        });
    }

    // 크롤링 초기화 함수
    function resetCrawling() {
        stopCrawling();
        currentPage = 1;
        totalPages = 0;
        results = [];
        visitedLinks.clear();
        resultsBody.innerHTML = '';
        visitedCountElem.innerText = '0';
        document.getElementById('totalRequests').innerText = '0';
        document.getElementById('successfulRequests').innerText = '0';
        document.getElementById('failedRequests').innerText = '0';
        progressBar.value = 0;
        progressText.innerText = '0%';
        elapsedTimeElem.innerText = '00:00:00';
        remainingTimeElem.innerText = '00:00:00';
        logMessages = [];
        logContainer.innerHTML = `<strong>로그:</strong><br>`;
        GM_setValue('crawlerLogs', JSON.stringify(logMessages));

        // 차트 데이터 초기화
        crawlData = {
            labels: [],
            datasets: [{
                label: '방문 횟수',
                data: [],
                backgroundColor: 'rgba(33, 150, 243, 0.6)',
                borderColor: 'rgba(33, 150, 243, 1)',
                borderWidth: 1
            },
            {
                label: '평균 페이지 로딩 시간 (ms)',
                data: [],
                backgroundColor: 'rgba(76, 175, 80, 0.6)',
                borderColor: 'rgba(76, 175, 80, 1)',
                borderWidth: 1
            }]
        };
        loadTimeData = {
            labels: [],
            datasets: [{
                label: '페이지 로딩 시간 (ms)',
                data: [],
                backgroundColor: 'rgba(255, 193, 7, 0.6)',
                borderColor: 'rgba(255,193,7,1)',
                borderWidth: 1
            }]
        };
        domainDistributionData = {
            labels: [],
            datasets: [{
                label: '도메인 분포',
                data: [],
                backgroundColor: [
                    'rgba(33, 150, 243, 0.6)',
                    'rgba(255, 87, 34, 0.6)',
                    'rgba(76, 175, 80, 0.6)',
                    'rgba(156, 39, 176, 0.6)',
                    'rgba(255, 152, 0, 0.6)',
                    'rgba(244, 67, 54, 0.6)',
                    'rgba(158, 158, 158, 0.6)'
                ],
                borderColor: 'rgba(33, 150, 243, 1)',
                borderWidth: 1
            }]
        };
        errorRateData = {
            labels: [],
            datasets: [{
                label: '오류 발생 수',
                data: [],
                backgroundColor: 'rgba(255, 99, 132, 0.6)',
                borderColor: 'rgba(255, 99, 132,1)',
                borderWidth: 1
            }]
        };
        cumulativeVisitsData = {
            labels: [],
            datasets: [{
                label: '누적 방문 횟수',
                data: [],
                backgroundColor: 'rgba(3, 169, 244, 0.6)',
                borderColor: 'rgba(3, 169, 244,1)',
                borderWidth: 1
            }]
        };
        keywordDistributionData = {
            labels: [],
            datasets: [{
                label: '키워드 분포',
                data: [],
                backgroundColor: [
                    'rgba(63, 81, 181, 0.6)',
                    'rgba(255, 152, 0, 0.6)',
                    'rgba(76, 175, 80, 0.6)',
                    'rgba(244, 67, 54, 0.6)',
                    'rgba(33, 150, 243, 0.6)',
                    'rgba(153, 102, 255, 0.6)'
                ],
                borderColor: 'rgba(63, 81, 181,1)',
                borderWidth: 1
            }]
        };
        languageDistributionData = {
            labels: [],
            datasets: [{
                label: '언어 분포',
                data: [],
                backgroundColor: [
                    'rgba(38, 166, 154, 0.6)',
                    'rgba(211, 47, 47, 0.6)',
                    'rgba(3, 169, 244, 0.6)',
                    'rgba(255, 193, 7, 0.6)',
                    'rgba(156, 39, 176, 0.6)',
                    'rgba(0, 188, 212, 0.6)'
                ],
                borderColor: 'rgba(38, 166, 154,1)',
                borderWidth: 1
            }]
        };
        techStackData = {
            labels: [],
            datasets: [{
                label: '기술 스택 분포',
                data: [],
                backgroundColor: [
                    'rgba(103, 58, 183, 0.6)',
                    'rgba(255, 202, 40, 0.6)',
                    'rgba(0, 150, 136, 0.6)',
                    'rgba(233, 30, 99, 0.6)',
                    'rgba(245, 124, 0, 0.6)',
                    'rgba(255, 235, 59, 0.6)'
                ],
                borderColor: 'rgba(103, 58, 183,1)',
                borderWidth: 1
            }]
        };
        domainAgeData = {
            labels: [],
            datasets: [{
                label: '도메인 연령 분포 (년)',
                data: [],
                backgroundColor: [
                    'rgba(255, 159, 64, 0.6)',
                    'rgba(75, 192, 192, 0.6)',
                    'rgba(54, 162, 235, 0.6)',
                    'rgba(153, 102, 255, 0.6)',
                    'rgba(255, 99, 132, 0.6)'
                ],
                borderColor: 'rgba(255, 159, 64,1)',
                borderWidth: 1
            }]
        };
        backlinksData = {
            labels: [],
            datasets: [{
                label: '백링크 수',
                data: [],
                backgroundColor: 'rgba(255, 206, 86, 0.6)',
                borderColor: 'rgba(255, 206, 86, 1)',
                borderWidth: 1
            }]
        };
        pageStructureData = {
            labels: ['헤더', '푸터', '네비게이션', '콘텐츠', '사이드바'],
            datasets: [{
                label: '페이지 구조 요소 분포',
                data: [],
                backgroundColor: [
                    'rgba(255, 99, 132, 0.6)',
                    'rgba(54, 162, 235, 0.6)',
                    'rgba(255, 206, 86, 0.6)',
                    'rgba(75, 192, 192, 0.6)',
                    'rgba(153, 102, 255, 0.6)'
                ],
                borderColor: 'rgba(255, 159, 64, 1)',
                borderWidth: 1
            }]
        };

        if (backlinksChart) {
            backlinksChart.destroy();
        }
        if (pageStructureChart) {
            pageStructureChart.destroy();
        }
        initializeChart();
        log('🔄 크롤링을 초기화하였습니다.');
    }

    // 크롤링 페이지 함수
    async function crawlPage() {
        if (!isRunning || isPaused) {
            return;
        }

        let url = constructURL(currentPage);
        if (!url) {
            log('❌ 지원하지 않는 검색 엔진입니다.');
            alert('지원하지 않는 검색 엔진입니다.');
            isRunning = false;
            return;
        }

        log(`📄 페이지 ${currentPage} 크롤링 중: ${url}`);

        GM_xmlhttpRequest({
            method: "GET",
            url: url,
            headers: {
                "User-Agent": navigator.userAgent,
                "Accept": "text/html"
            },
            onload: function(response) {
                if (response.status !== 200) {
                    handleCrawlError(currentPage, retryAttempts);
                    document.getElementById('failedRequests').innerText = parseInt(document.getElementById('failedRequests').innerText) + 1;
                    return;
                }

                const parser = new DOMParser();
                const doc = parser.parseFromString(response.responseText, 'text/html');
                let items = extractItems(doc);
                if (currentPage === 1) {
                    totalPages = determineTotalPages(doc);
                    totalPagesElem.innerText = totalPages;
                }

                log(`📊 페이지 ${currentPage}에서 ${items.length}개 항목 발견.`);
                document.getElementById('totalRequests').innerText = parseInt(document.getElementById('totalRequests').innerText) + items.length;

                items.forEach(item => {
                    processItem(item);
                });

                updateChart();
                updateLoadTimeChart();
                updateDomainDistributionChart();
                updateErrorRateChart();
                updateCumulativeVisitsChart();
                updateKeywordDistributionChart();
                updateLanguageDistributionChart();
                updateTechStackChart();
                updateDomainAgeChart();
                updateBacklinksChart();
                updatePageStructureChart();
                updateProgress();
                currentPage++;
                currentPageElem.innerText = currentPage;
                if (currentPage <= totalPages && isRunning && !isPaused) {
                    setTimeout(crawlPage, fixedRetryInterval); // 크롤링 속도 고정 100ms
                } else {
                    finalizeCrawling();
                }
            },
            onerror: function() {
                handleCrawlError(currentPage, retryAttempts);
                document.getElementById('failedRequests').innerText = parseInt(document.getElementById('failedRequests').innerText) + 1;
            }
        });
    }

    // 크롤링 완료 시 처리 함수
    function finalizeCrawling() {
        isRunning = false;
        clearInterval(elapsedInterval);
        log('✅ 크롤링이 완료되었습니다.');
        GM_notification({
            text: '크롤링이 완료되었습니다.',
            title: '웹 크롤러 8.6',
            timeout: 3000
        });
    }

    // 크롤링 오류 처리 함수
    function handleCrawlError(page, retriesLeft) {
        if (retriesLeft > 0 && isRunning) {
            log(`🔄 페이지 ${page} 재시도 (${retriesLeft}회 남음).`);
            setTimeout(() => {
                const url = constructURL(page);
                GM_xmlhttpRequest({
                    method: "GET",
                    url: url,
                    headers: {
                        "User-Agent": navigator.userAgent,
                        "Accept": "text/html"
                    },
                    onload: function(response) {
                        if (response.status === 200) {
                            log(`✅ 페이지 ${page} 재시도 성공.`);
                            document.getElementById('successfulRequests').innerText = parseInt(document.getElementById('successfulRequests').innerText) + 1;
                            crawlPage();
                        } else {
                            handleCrawlError(page, retriesLeft - 1);
                            document.getElementById('failedRequests').innerText = parseInt(document.getElementById('failedRequests').innerText) + 1;
                        }
                    },
                    onerror: function() {
                        handleCrawlError(page, retriesLeft - 1);
                        document.getElementById('failedRequests').innerText = parseInt(document.getElementById('failedRequests').innerText) + 1;
                    }
                });
            }, fixedRetryInterval);
        } else {
            log(`❌ 페이지 ${page} 로드 실패: 최대 재시도 횟수 초과.`);
            document.getElementById('failedRequests').innerText = parseInt(document.getElementById('failedRequests').innerText) + 1;
            currentPage++;
            currentPageElem.innerText = currentPage;
            if (currentPage <= totalPages && isRunning && !isPaused) {
                setTimeout(crawlPage, fixedRetryInterval);
            } else {
                finalizeCrawling();
            }
        }
    }

    // URL 구성 함수
    function constructURL(page) {
        switch(searchEngine) {
            case 'google':
                return `https://www.google.com/search?q=${encodeURIComponent(searchQuery)}&start=${(page-1)*10}`;
            case 'bing':
                return `https://www.bing.com/search?q=${encodeURIComponent(searchQuery)}&first=${(page-1)*10 +1}`;
            case 'daum':
                return `https://search.daum.net/search?q=${encodeURIComponent(searchQuery)}&page=${page}`;
            case 'naver':
                return `https://search.naver.com/search.naver?query=${encodeURIComponent(searchQuery)}&start=${(page-1)*10 +1}`;
            case 'yahoo':
                return `https://search.yahoo.com/search?p=${encodeURIComponent(searchQuery)}&b=${(page-1)*10 +1}`;
            case 'duckduckgo':
                return `https://duckduckgo.com/html/?q=${encodeURIComponent(searchQuery)}&s=${(page-1)*30}`;
            case 'startpage':
                return `https://www.startpage.com/do/search?query=${encodeURIComponent(searchQuery)}&b=${(page-1)*10 +1}`;
            case 'ecosia':
                return `https://www.ecosia.org/search?q=${encodeURIComponent(searchQuery)}&page=${page}`;
            case 'qwant':
                return `https://www.qwant.com/?q=${encodeURIComponent(searchQuery)}&t=web&s=${page}`;
            case 'ahmia':
                return `http://ahmia.fi/search/?q=${encodeURIComponent(searchQuery)}&page=${page}`;
            case 'torch':
                return `http://torchsearch.de/search/?q=${encodeURIComponent(searchQuery)}&page=${page}`;
            case 'notracker':
                return `http://notracker.org/search/?q=${encodeURIComponent(searchQuery)}&page=${page}`;
            default:
                return '';
        }
    }

    // 항목 추출 함수
    function extractItems(doc) {
        let items = [];
        switch(searchEngine) {
            case 'google':
                items = doc.querySelectorAll('div.g');
                break;
            case 'bing':
                items = doc.querySelectorAll('li.b_algo');
                break;
            case 'daum':
                items = doc.querySelectorAll('div.wrap_cont');
                break;
            case 'naver':
                items = doc.querySelectorAll('div.basic1');
                break;
            case 'yahoo':
                items = doc.querySelectorAll('div.compTitle');
                break;
            case 'duckduckgo':
                items = doc.querySelectorAll('div.result');
                break;
            case 'startpage':
                items = doc.querySelectorAll('div.w-gl__result');
                break;
            case 'ecosia':
                items = doc.querySelectorAll('div.result');
                break;
            case 'qwant':
                items = doc.querySelectorAll('div.results div.result');
                break;
            case 'ahmia':
                items = doc.querySelectorAll('div#main div.result');
                break;
            case 'torch':
                items = doc.querySelectorAll('div.result');
                break;
            case 'notracker':
                items = doc.querySelectorAll('div.result');
                break;
            default:
                break;
        }
        return items;
    }

    // 총 페이지 수 결정 함수
    function determineTotalPages(doc) {
        let pages = 1;
        switch(searchEngine) {
            case 'google':
                const googlePager = doc.querySelectorAll('a[href*="start="]');
                pages = Math.min(googlePager.length, 10) || 1;
                break;
            case 'bing':
                const bingPager = doc.querySelectorAll('a.sb_pagS');
                pages = bingPager.length || 1;
                break;
            case 'daum':
                const daumPager = doc.querySelectorAll('a.pagination__link');
                pages = daumPager.length || 1;
                break;
            case 'naver':
                const naverPager = doc.querySelectorAll('a.nav');
                pages = naverPager.length || 1;
                break;
            case 'yahoo':
                const yahooPager = doc.querySelectorAll('a.fz-l.tco-pagination__anchor');
                pages = yahooPager.length || 1;
                break;
            case 'duckduckgo':
                const ddgPager = doc.querySelectorAll('a.result--more__btn');
                pages = ddgPager.length || 1;
                break;
            case 'startpage':
                const spPager = doc.querySelectorAll('a.paginator__link');
                pages = spPager.length || 1;
                break;
            case 'ecosia':
                const ecosiaPager = doc.querySelectorAll('a.pagination__link');
                pages = ecosiaPager.length || 1;
                break;
            case 'qwant':
                const qwantPager = doc.querySelectorAll('a.pagination__link');
                pages = qwantPager.length || 1;
                break;
            case 'ahmia':
                const ahmiaPager = doc.querySelectorAll('a.pagination__link');
                pages = ahmiaPager.length || 1;
                break;
            case 'torch':
                const torchPager = doc.querySelectorAll('a.pagination__link');
                pages = torchPager.length || 1;
                break;
            case 'notracker':
                const ntPager = doc.querySelectorAll('a.pagination__link');
                pages = ntPager.length || 1;
                break;
            default:
                pages = 1;
                break;
        }
        return pages;
    }

    // 항목 처리 함수
    function processItem(item) {
        let title = 'N/A';
        let link = 'N/A';
        let description = 'N/A';
        let image = 'N/A';
        let htmlLink = 'N/A';
        let whoisInfo = 'N/A';
        let pageLoadTime = 'N/A';
        let language = 'N/A';
        let metaKeywords = 'N/A';
        let metaRobots = 'N/A';
        let openGraph = 'N/A';
        let securityHeaders = 'N/A';
        let domainAge = 'N/A';
        let techStack = 'N/A';
        let backlinks = 'N/A';
        let pageStructure = 'N/A';

        switch(searchEngine) {
            case 'google':
                const gTitleElem = item.querySelector('h3');
                const gLinkElem = item.querySelector('a');
                const gDescElem = item.querySelector('.IsZvec') || item.querySelector('.VwiC3b');
                if (gTitleElem) title = gTitleElem.innerText;
                if (gLinkElem) link = gLinkElem.href;
                if (gDescElem) description = gDescElem.innerText;

                const gImgElem = item.querySelector('img');
                if (gImgElem && gImgElem.src) {
                    image = `<img src="${gImgElem.src}" alt="이미지" style="max-width: 80px; max-height: 80px;">`;
                }

                if (toggleHTML.checked && link !== 'N/A') {
                    htmlLink = `<button class="html-view-btn" data-link="${encodeURIComponent(link)}">보기</button>`;
                }
                break;
            case 'bing':
                const bTitleElem = item.querySelector('h2 a');
                const bDescElem = item.querySelector('p');
                if (bTitleElem) {
                    title = bTitleElem.innerText;
                    link = bTitleElem.href;
                }
                if (bDescElem) description = bDescElem.innerText;

                const bImgElem = item.querySelector('img');
                if (bImgElem && bImgElem.src) {
                    image = `<img src="${bImgElem.src}" alt="이미지" style="max-width: 80px; max-height: 80px;">`;
                }

                if (toggleHTML.checked && link !== 'N/A') {
                    htmlLink = `<button class="html-view-btn" data-link="${encodeURIComponent(link)}">보기</button>`;
                }
                break;
            case 'daum':
                const dTitleElem = item.querySelector('a.link_fax');
                const dDescElem = item.querySelector('div.cont_inner');
                if (dTitleElem) {
                    title = dTitleElem.innerText.trim();
                    link = dTitleElem.href;
                }
                if (dDescElem) description = dDescElem.innerText.trim();

                const dImgElem = item.querySelector('img');
                if (dImgElem && dImgElem.src) {
                    image = `<img src="${dImgElem.src}" alt="이미지" style="max-width: 80px; max-height: 80px;">`;
                }

                if (toggleHTML.checked && link !== 'N/A') {
                    htmlLink = `<button class="html-view-btn" data-link="${encodeURIComponent(link)}">보기</button>`;
                }
                break;
            case 'naver':
                const nTitleElem = item.querySelector('a.tit');
                const nDescElem = item.querySelector('div.desc');
                if (nTitleElem) {
                    title = nTitleElem.innerText;
                    link = nTitleElem.href;
                }
                if (nDescElem) description = nDescElem.innerText;

                const nImgElem = item.querySelector('img.thumb');
                if (nImgElem && nImgElem.src) {
                    image = `<img src="${nImgElem.src}" alt="이미지" style="max-width: 80px; max-height: 80px;">`;
                }

                if (toggleHTML.checked && link !== 'N/A') {
                    htmlLink = `<button class="html-view-btn" data-link="${encodeURIComponent(link)}">보기</button>`;
                }
                break;
            case 'yahoo':
                const yTitleElem = item.querySelector('h3');
                const yLinkElem = item.querySelector('a');
                const yDescElem = item.querySelector('div.compText');
                if (yTitleElem) title = yTitleElem.innerText;
                if (yLinkElem) link = yLinkElem.href;
                if (yDescElem) description = yDescElem.innerText;

                const yImgElem = item.querySelector('img');
                if (yImgElem && yImgElem.src) {
                    image = `<img src="${yImgElem.src}" alt="이미지" style="max-width: 80px; max-height: 80px;">`;
                }

                if (toggleHTML.checked && link !== 'N/A') {
                    htmlLink = `<button class="html-view-btn" data-link="${encodeURIComponent(link)}">보기</button>`;
                }
                break;
            case 'duckduckgo':
                const ddgTitleElem = item.querySelector('a.result__a');
                const ddgDescElem = item.querySelector('a.result__snippet');
                if (ddgTitleElem) {
                    title = ddgTitleElem.innerText;
                    link = ddgTitleElem.href;
                }
                if (ddgDescElem) description = ddgDescElem.innerText;

                const ddgImgElem = item.querySelector('img.result__image');
                if (ddgImgElem && ddgImgElem.src) {
                    image = `<img src="${ddgImgElem.src}" alt="이미지" style="max-width: 80px; max-height: 80px;">`;
                }

                if (toggleHTML.checked && link !== 'N/A') {
                    htmlLink = `<button class="html-view-btn" data-link="${encodeURIComponent(link)}">보기</button>`;
                }
                break;
            case 'startpage':
                const spTitleElem = item.querySelector('a.w-gl__result-title');
                const spDescElem = item.querySelector('p.w-gl__result-description');
                if (spTitleElem) {
                    title = spTitleElem.innerText;
                    link = spTitleElem.href;
                }
                if (spDescElem) description = spDescElem.innerText;

                const spImgElem = item.querySelector('img.w-gl__result-image');
                if (spImgElem && spImgElem.src) {
                    image = `<img src="${spImgElem.src}" alt="이미지" style="max-width: 80px; max-height: 80px;">`;
                }

                if (toggleHTML.checked && link !== 'N/A') {
                    htmlLink = `<button class="html-view-btn" data-link="${encodeURIComponent(link)}">보기</button>`;
                }
                break;
            case 'ecosia':
                const ecosiaTitleElem = item.querySelector('h2.result-title a');
                const ecosiaDescElem = item.querySelector('p.result-description');
                if (ecosiaTitleElem) {
                    title = ecosiaTitleElem.innerText;
                    link = ecosiaTitleElem.href;
                }
                if (ecosiaDescElem) description = ecosiaDescElem.innerText;

                const ecosiaImgElem = item.querySelector('img.result-image');
                if (ecosiaImgElem && ecosiaImgElem.src) {
                    image = `<img src="${ecosiaImgElem.src}" alt="이미지" style="max-width: 80px; max-height: 80px;">`;
                }

                if (toggleHTML.checked && link !== 'N/A') {
                    htmlLink = `<button class="html-view-btn" data-link="${encodeURIComponent(link)}">보기</button>`;
                }
                break;
            case 'qwant':
                const qwantTitleElem = item.querySelector('a.result__title');
                const qwantDescElem = item.querySelector('p.result__description');
                if (qwantTitleElem) {
                    title = qwantTitleElem.innerText;
                    link = qwantTitleElem.href;
                }
                if (qwantDescElem) description = qwantDescElem.innerText;

                const qwantImgElem = item.querySelector('img.result__image');
                if (qwantImgElem && qwantImgElem.src) {
                    image = `<img src="${qwantImgElem.src}" alt="이미지" style="max-width: 80px; max-height: 80px;">`;
                }

                if (toggleHTML.checked && link !== 'N/A') {
                    htmlLink = `<button class="html-view-btn" data-link="${encodeURIComponent(link)}">보기</button>`;
                }
                break;
            case 'ahmia':
                const ahmiaTitleElem = item.querySelector('h2.result-title a');
                const ahmiaDescElem = item.querySelector('p.result-description');
                if (ahmiaTitleElem) {
                    title = ahmiaTitleElem.innerText;
                    link = ahmiaTitleElem.href;
                }
                if (ahmiaDescElem) description = ahmiaDescElem.innerText;

                const ahmiaImgElem = item.querySelector('img.result-image');
                if (ahmiaImgElem && ahmiaImgElem.src) {
                    image = `<img src="${ahmiaImgElem.src}" alt="이미지" style="max-width: 80px; max-height: 80px;">`;
                }

                if (toggleHTML.checked && link !== 'N/A') {
                    whoisInfo = `<button class="whois-view-btn" data-domain="${encodeURIComponent(extractDomain(link))}">보기</button>`;
                    htmlLink = `<button class="html-view-btn" data-link="${encodeURIComponent(link)}">보기</button>`;
                }
                break;
            case 'torch':
                const torchTitleElem = item.querySelector('h3.result-title a');
                const torchDescElem = item.querySelector('p.result-description');
                if (torchTitleElem) {
                    title = torchTitleElem.innerText;
                    link = torchTitleElem.href;
                }
                if (torchDescElem) description = torchDescElem.innerText;

                const torchImgElem = item.querySelector('img.result-image');
                if (torchImgElem && torchImgElem.src) {
                    image = `<img src="${torchImgElem.src}" alt="이미지" style="max-width: 80px; max-height: 80px;">`;
                }

                if (toggleHTML.checked && link !== 'N/A') {
                    whoisInfo = `<button class="whois-view-btn" data-domain="${encodeURIComponent(extractDomain(link))}">보기</button>`;
                    htmlLink = `<button class="html-view-btn" data-link="${encodeURIComponent(link)}">보기</button>`;
                }
                break;
            case 'notracker':
                const ntTitleElem = item.querySelector('h3.result-title a');
                const ntDescElem = item.querySelector('p.result-description');
                if (ntTitleElem) {
                    title = ntTitleElem.innerText;
                    link = ntTitleElem.href;
                }
                if (ntDescElem) description = ntDescElem.innerText;

                const ntImgElem = item.querySelector('img.result-image');
                if (ntImgElem && ntImgElem.src) {
                    image = `<img src="${ntImgElem.src}" alt="이미지" style="max-width: 80px; max-height: 80px;">`;
                }

                if (toggleHTML.checked && link !== 'N/A') {
                    whoisInfo = `<button class="whois-view-btn" data-domain="${encodeURIComponent(extractDomain(link))}">보기</button>`;
                    htmlLink = `<button class="html-view-btn" data-link="${encodeURIComponent(link)}">보기</button>`;
                }
                break;
            default:
                break;
        }

        // 추가 메타 정보 수집
        try {
            const metaKeywordElem = item.querySelector('meta[name="keywords"]');
            if (metaKeywordElem) {
                metaKeywords = metaKeywordElem.getAttribute('content');
                if (metaKeywords) {
                    metaKeywords = metaKeywords.split(',').map(k => k.trim()).join(', ');
                } else {
                    metaKeywords = 'N/A';
                }
            }

            const metaRobotsElem = item.querySelector('meta[name="robots"]');
            if (metaRobotsElem) {
                metaRobots = metaRobotsElem.getAttribute('content') || 'N/A';
            }

            const ogTitle = item.querySelector('meta[property="og:title"]')?.getAttribute('content') || 'N/A';
            const ogDescription = item.querySelector('meta[property="og:description"]')?.getAttribute('content') || 'N/A';
            const ogImage = item.querySelector('meta[property="og:image"]')?.getAttribute('content') || 'N/A';
            openGraph = `Title: ${ogTitle}, Description: ${ogDescription}, Image: ${ogImage}`;

            // 보안 헤더 추출 (간단한 예시)
            let securityHeadersList = [];
            const hsts = item.querySelector('meta[http-equiv="Strict-Transport-Security"]');
            if (hsts) securityHeadersList.push('HSTS');
            const contentSecurityPolicy = item.querySelector('meta[http-equiv="Content-Security-Policy"]');
            if (contentSecurityPolicy) securityHeadersList.push('CSP');
            securityHeaders = securityHeadersList.length > 0 ? securityHeadersList.join(', ') : 'N/A';

            const domainInfo = getDomainInfo(link);
            domainAge = domainInfo ? domainInfo.age : 'N/A';

            const htmlLang = item.querySelector('html');
            if (htmlLang && htmlLang.lang) {
                language = htmlLang.lang;
            }

            // 기술 스택 정보 수집 (예시: 간단한 스택 감지)
            const techs = [];
            if (item.querySelector('.wordpress')) techs.push('WordPress');
            if (item.querySelector('.shopify')) techs.push('Shopify');
            if (item.querySelector('.react')) techs.push('React');
            if (item.querySelector('.angular')) techs.push('Angular');
            if (item.querySelector('.vue')) techs.push('Vue.js');
            techStack = techs.length > 0 ? techs.join(', ') : 'N/A';

            // 백링크 정보 수집 (미지원)
            if (toggleBacklinks.checked) {
                // 실제 백링크 정보를 수집하려면 외부 API 연동이 필요합니다.
                backlinks = '현재 지원되지 않습니다.';
            }

            // 페이지 구조 분석 (미지원)
            if (togglePageStructure.checked) {
                // 실제 페이지 구조 분석을 위해서는 추가적인 로직이 필요합니다.
                pageStructure = '현재 지원되지 않습니다.';
            }
        } catch (e) {
            log(`⚠️ 메타 정보 수집 중 오류 발생: ${e.message}`);
        }

        // 광고 제외 필터링
        if (toggleAds.checked) {
            const combinedText = `${title} ${description}`.toLowerCase();
            const isAd = combinedText.includes('advertisement') || combinedText.includes('광고') || combinedText.includes('ad ');
            if (isAd) {
                log(`🚫 광고 항목 제외: "${title}"`);
                return;
            }
        }

        // 필터 패턴 적용
        if (filterPatterns.length > 0) {
            const combinedText = `${title} ${description}`;
            const isFiltered = filterPatterns.some(regex => regex.test(combinedText));
            if (isFiltered) {
                log(`❌ 필터 단어 포함 항목 제외: "${title}"`);
                return;
            }
        }

        // 도메인 추출 및 제외 도메인 적용
        const domain = extractDomain(link);
        if (!domain) return;

        if (excludeDomains.includes(domain.toLowerCase())) {
            log(`🚫 제외 도메인 항목 제외: "${link}"`);
            return;
        }

        // 중복 방문 제한
        let visitCount = visitedLinks.get(domain) || 0;
        if (visitCount >= duplicateThreshold) {
            log(`🔄 중복 방문 제한: "${domain}"`);
            return;
        }

        visitedLinks.set(domain, visitCount + 1);
        visitedCountElem.innerText = visitedLinks.size.toString();

        // 페이지 로딩 시간 측정
        const startLoad = performance.now();
        fetch(link, { mode: 'no-cors' })
            .then(response => {
                const endLoad = performance.now();
                pageLoadTime = Math.round(endLoad - startLoad);
                document.getElementById('successfulRequests').innerText = parseInt(document.getElementById('successfulRequests').innerText) + 1;
                updateRow();
            })
            .catch(err => {
                pageLoadTime = '계산 불가';
                document.getElementById('failedRequests').innerText = parseInt(document.getElementById('failedRequests').innerText) + 1;
                updateRow();
            });

        // 언어, 키워드, 로봇 메타 정보 저장
        const itemLanguage = language;
        const itemKeywords = metaKeywords;
        const itemRobots = metaRobots;

        // 백링크 및 페이지 구조 정보 저장 (현재 미지원)

        // 이미지 옵션 적용
        if (!toggleImages.checked) {
            image = '비활성화';
        }

        // OpenGraph 옵션 적용
        if (!toggleOpenGraph.checked) {
            openGraph = '비활성화';
        }

        // 보안 헤더 옵션 적용
        if (!toggleSecurityHeaders.checked) {
            securityHeaders = '비활성화';
        }

        // 도메인 연령 옵션 적용
        if (!toggleDomainAge.checked) {
            domainAge = '비활성화';
        }

        // 백링크 옵션 적용
        if (!toggleBacklinks.checked) {
            backlinks = '비활성화';
        }

        // 페이지 구조 옵션 적용
        if (!togglePageStructure.checked) {
            pageStructure = '비활성화';
        }

        // 결과 배열에 추가
        results.push({ title, link, description, image, htmlLink, whoisInfo, pageLoadTime, language, metaKeywords, metaRobots, openGraph, securityHeaders, domainAge, techStack, backlinks, pageStructure });

        // 결과 테이블에 행 추가
        const row = document.createElement('tr');
        row.innerHTML = `
            <td>${escapeHTML(title)}</td>
            <td><a href="${escapeHTML(link)}" target="_blank">${escapeHTML(link)}</a></td>
            <td>${escapeHTML(description)}</td>
            <td>${image}</td>
            <td>${htmlLink}</td>
            <td>${whoisInfo}</td>
            <td>${pageLoadTime}</td>
            <td>${escapeHTML(itemLanguage)}</td>
            <td>${escapeHTML(itemKeywords)}</td>
            <td>${escapeHTML(itemRobots)}</td>
            <td>${escapeHTML(openGraph)}</td>
            <td>${escapeHTML(securityHeaders)}</td>
            <td>${escapeHTML(domainAge)}</td>
            <td>${escapeHTML(techStack)}</td>
            <td>${backlinks}</td>
            <td>${pageStructure}</td>
        `;
        resultsBody.appendChild(row);

        // 미리보기 이벤트 추가
        addPreviewEvents(row, link);

        // WHOIS 버튼 이벤트 추가
        const whoisBtn = row.querySelector('.whois-view-btn');
        if (whoisBtn) {
            whoisBtn.addEventListener('click', () => {
                const encodedDomain = whoisBtn.getAttribute('data-domain');
                const decodedDomain = decodeURIComponent(encodedDomain);
                fetchWhoisData(decodedDomain);
            });
        }

        function updateRow() {
            // 업데이트된 로딩 시간을 테이블에 반영
            row.cells[6].innerText = pageLoadTime;
            row.cells[10].innerText = openGraph;
            row.cells[11].innerText = securityHeaders;
            row.cells[12].innerText = domainAge;
            row.cells[13].innerText = techStack;
            row.cells[14].innerText = backlinks;
            row.cells[15].innerText = pageStructure;
            updateChart();
            updateLoadTimeChart();
            updateDomainDistributionChart();
            updateErrorRateChart();
            updateCumulativeVisitsChart();
            updateKeywordDistributionChart();
            updateLanguageDistributionChart();
            updateTechStackChart();
            updateDomainAgeChart();
            updateBacklinksChart();
            updatePageStructureChart();
            updateProgress();
        }
    }

    // HTML 이스케프 함수
    function escapeHTML(str) {
        if (!str) return '';
        return str.replace(/&/g, "&amp;")
                  .replace(/</g, "&lt;")
                  .replace(/>/g, "&gt;")
                  .replace(/"/g, "&quot;")
                  .replace(/'/g, "&#039;");
    }

    // 도메인 추출 함수
    function extractDomain(url) {
        try {
            const urlObj = new URL(url);
            return urlObj.hostname.replace('www.', '');
        } catch (e) {
            return null;
        }
    }

    // 도메인 연령 정보 가져오기 함수
    function getDomainInfo(url) {
        try {
            const urlObj = new URL(url);
            const now = new Date();
            // 실제 WHOIS 데이터를 통해 도메인 생성일을 가져와야 하지만, 예시로 임의의 연령을 설정
            // 본인의 JSONWHOIS API를 활용하여 실제 데이터를 받아오도록 구현 필요
            // 예시:
            // return { age: calculatedAge };
            return { age: 'N/A' };
        } catch (e) {
            return null;
        }
    }

    // 경과 시간 업데이트 함수
    function updateElapsedTime() {
        if (!startTime) return;
        const now = Date.now();
        const elapsed = Math.floor((now - startTime) / 1000);
        elapsedTimeElem.innerText = formatTime(elapsed);
        const estimatedTotal = totalPages * ((elapsed > 0) ? (elapsed / (currentPage - 1)) : 0.5);
        const remaining = Math.max(Math.round(estimatedTotal - elapsed), 0);
        remainingTimeElem.innerText = formatTime(remaining);
    }

    // 시간 형식화 함수
    function formatTime(seconds) {
        const hrs = String(Math.floor(seconds / 3600)).padStart(2, '0');
        const mins = String(Math.floor((seconds % 3600) / 60)).padStart(2, '0');
        const secs = String(seconds % 60).padStart(2, '0');
        return `${hrs}:${mins}:${secs}`;
    }

    // 차트 업데이트 함수
    function updateChart() {
        crawlData.labels = Array.from(visitedLinks.keys());
        crawlData.datasets[0].data = Array.from(visitedLinks.values());

        // 평균 페이지 로딩 시간 계산
        let loadTimes = results.filter(r => typeof r.pageLoadTime === 'number').map(r => r.pageLoadTime);
        let averageLoadTime = loadTimes.length > 0 ? Math.round(loadTimes.reduce((a, b) => a + b, 0) / loadTimes.length) : 0;
        crawlData.datasets[1].data = Array.from(visitedLinks.keys()).map(domain => {
            const times = results.filter(r => extractDomain(r.link) === domain && typeof r.pageLoadTime === 'number').map(r => r.pageLoadTime);
            return times.length > 0 ? Math.round(times.reduce((a, b) => a + b, 0) / times.length) : 0;
        });

        if (crawlChart) {
            crawlChart.update();
        }
        GM_setValue('crawlerData', JSON.stringify(crawlData));
    }

    // 추가 차트 업데이트 함수
    function updateLoadTimeChart() {
        loadTimeData.labels = results.map(r => r.title).slice(-20); // 최근 20개
        loadTimeData.datasets[0].data = results.map(r => r.pageLoadTime).slice(-20);
        if (loadTimeChart) {
            loadTimeChart.update();
        }
    }

    function updateDomainDistributionChart() {
        domainDistributionData.labels = Array.from(visitedLinks.keys());
        domainDistributionData.datasets[0].data = Array.from(visitedLinks.values());
        if (domainDistributionChart) {
            domainDistributionChart.update();
        }
    }

    function updateErrorRateChart() {
        // 오류 발생 수를 도메인별로 집계
        const errorCounts = {};
        results.forEach(r => {
            if (r.pageLoadTime === '계산 불가') {
                const domain = extractDomain(r.link);
                if (domain) {
                    errorCounts[domain] = (errorCounts[domain] || 0) + 1;
                }
            }
        });
        errorRateData.labels = Object.keys(errorCounts);
        errorRateData.datasets[0].data = Object.values(errorCounts);
        if (errorRateChart) {
            errorRateChart.update();
        }
    }

    function updateCumulativeVisitsChart() {
        let cumulative = 0;
        cumulativeVisitsData.labels = results.map((_, index) => index + 1);
        cumulativeVisitsData.datasets[0].data = results.map(() => { cumulative += 1; return cumulative; });
        if (cumulativeVisitsChart) {
            cumulativeVisitsChart.update();
        }
    }

    function updateKeywordDistributionChart() {
        const keywordCounts = {};
        results.forEach(r => {
            if (r.metaKeywords && r.metaKeywords !== 'N/A') {
                const keywords = r.metaKeywords.split(',').map(k => k.trim());
                keywords.forEach(k => {
                    if (k) {
                        keywordCounts[k] = (keywordCounts[k] || 0) + 1;
                    }
                });
            }
        });
        keywordDistributionData.labels = Object.keys(keywordCounts);
        keywordDistributionData.datasets[0].data = Object.values(keywordCounts);
        if (keywordDistributionChart) {
            keywordDistributionChart.update();
        }
    }

    function updateLanguageDistributionChart() {
        const languageCounts = {};
        results.forEach(r => {
            if (r.language && r.language !== 'N/A') {
                languageCounts[r.language] = (languageCounts[r.language] || 0) + 1;
            }
        });
        languageDistributionData.labels = Object.keys(languageCounts);
        languageDistributionData.datasets[0].data = Object.values(languageCounts);
        if (languageDistributionChart) {
            languageDistributionChart.update();
        }
    }

    function updateTechStackChart() {
        const techCounts = {};
        results.forEach(r => {
            if (r.techStack && r.techStack !== 'N/A') {
                const techs = r.techStack.split(',').map(t => t.trim());
                techs.forEach(t => {
                    if (t) {
                        techCounts[t] = (techCounts[t] || 0) + 1;
                    }
                });
            }
        });
        techStackData.labels = Object.keys(techCounts);
        techStackData.datasets[0].data = Object.values(techCounts);
        if (techStackChart) {
            techStackChart.update();
        }
    }

    function updateDomainAgeChart() {
        const ageCounts = {};
        results.forEach(r => {
            if (r.domainAge && r.domainAge !== 'N/A') {
                const age = parseInt(r.domainAge);
                if (!isNaN(age)) {
                    if (age >= 0 && age <= 1) {
                        ageCounts['0-1'] = (ageCounts['0-1'] || 0) + 1;
                    } else if (age <= 3) {
                        ageCounts['2-3'] = (ageCounts['2-3'] || 0) + 1;
                    } else if (age <= 5) {
                        ageCounts['4-5'] = (ageCounts['4-5'] || 0) + 1;
                    } else if (age <= 10) {
                        ageCounts['6-10'] = (ageCounts['6-10'] || 0) + 1;
                    } else {
                        ageCounts['10+'] = (ageCounts['10+'] || 0) + 1;
                    }
                }
            }
        });
        domainAgeData.labels = Object.keys(ageCounts);
        domainAgeData.datasets[0].data = Object.values(ageCounts);
        if (domainAgeChart) {
            domainAgeChart.update();
        }
    }

    function updateBacklinksChart() {
        const backlinkCounts = {};
        results.forEach(r => {
            if (r.backlinks && r.backlinks !== 'N/A') {
                const count = parseInt(r.backlinks);
                if (!isNaN(count)) {
                    backlinkCounts[r.title] = (backlinkCounts[r.title] || 0) + count;
                }
            }
        });
        backlinksData.labels = Object.keys(backlinkCounts);
        backlinksData.datasets[0].data = Object.values(backlinkCounts);
        if (backlinksChart) {
            backlinksChart.update();
        }
    }

    function updatePageStructureChart() {
        const structureCounts = { '헤더':0, '푸터':0, '네비게이션':0, '콘텐츠':0, '사이드바':0 };
        results.forEach(r => {
            if (r.pageStructure && r.pageStructure !== 'N/A') {
                // 실제 분석 로직이 필요하지만, 현재는 미지원
                // 예시로 임의의 값 증가
                structureCounts['콘텐츠'] += 1;
            }
        });
        pageStructureData.datasets[0].data = Object.values(structureCounts);
        if (pageStructureChart) {
            pageStructureChart.update();
        }
    }

    // 크롤 데이터 로드 함수
    function loadCrawlData() {
        const storedData = GM_getValue('crawlerData', null);
        if (storedData) {
            const parsedData = JSON.parse(storedData);
            crawlData = parsedData;
            if (crawlChart) {
                crawlChart.data = crawlData;
                crawlChart.update();
            }
        }
    }

    // 크롤 데이터 저장 함수
    function saveCrawlData() {
        GM_setValue('crawlerData', JSON.stringify(crawlData));
    }

    // 주기적으로 크롤 데이터 저장
    setInterval(saveCrawlData, 5000);

    // 검색어 및 검색 엔진 감지 함수
    function getSearchQueryAndEngine() {
        const url = window.location.href;
        if (url.includes('google.com/search')) {
            const params = new URLSearchParams(window.location.search);
            searchQuery = params.get('q') || '';
            searchEngine = 'google';
        } else if (url.includes('bing.com/search')) {
            const params = new URLSearchParams(window.location.search);
            searchQuery = params.get('q') || '';
            searchEngine = 'bing';
        } else if (url.includes('daum.net/search')) {
            const params = new URLSearchParams(window.location.search);
            searchQuery = params.get('q') || '';
            searchEngine = 'daum';
        } else if (url.includes('search.naver.com/search')) {
            const params = new URLSearchParams(window.location.search);
            searchQuery = params.get('query') || '';
            searchEngine = 'naver';
        } else if (url.includes('search.yahoo.com/search')) {
            const params = new URLSearchParams(window.location.search);
            searchQuery = params.get('p') || '';
            searchEngine = 'yahoo';
        } else if (url.includes('duckduckgo.com/')) {
            const params = new URLSearchParams(window.location.search);
            searchQuery = params.get('q') || '';
            searchEngine = 'duckduckgo';
        } else if (url.includes('startpage.com/do/search')) {
            const params = new URLSearchParams(window.location.search);
            searchQuery = params.get('query') || '';
            searchEngine = 'startpage';
        } else if (url.includes('ecosia.org/search')) {
            const params = new URLSearchParams(window.location.search);
            searchQuery = params.get('q') || '';
            searchEngine = 'ecosia';
        } else if (url.includes('qwant.com/')) {
            const params = new URLSearchParams(window.location.search);
            searchQuery = params.get('q') || '';
            searchEngine = 'qwant';
        } else if (url.includes('ahmia.fi/')) {
            const params = new URLSearchParams(window.location.search);
            searchQuery = params.get('q') || '';
            searchEngine = 'ahmia';
        } else if (url.includes('torchsearch.de/')) {
            const params = new URLSearchParams(window.location.search);
            searchQuery = params.get('q') || '';
            searchEngine = 'torch';
        } else if (url.includes('notracker.org/')) {
            const params = new URLSearchParams(window.location.search);
            searchQuery = params.get('q') || '';
            searchEngine = 'notracker';
        } else {
            searchQuery = '';
            searchEngine = '';
        }
        log(`🔍 검색 엔진: ${capitalizeFirstLetter(searchEngine)}, 검색어: "${searchQuery}"`);
    }

    // 로그 기록 함수
    function log(message) {
        const time = new Date().toLocaleTimeString();
        const entry = `[${time}] ${message}`;
        logMessages.push(entry);
        logContainer.innerHTML += `${entry}<br>`;
        logContainer.scrollTop = logContainer.scrollHeight;
        GM_setValue('crawlerLogs', JSON.stringify(logMessages));
    }

    // 결과 내보내기 함수 (중복 제거 및 정렬 추가)
    function exportResults() {
        if (results.length === 0) {
            alert('내보낼 결과가 없습니다.');
            return;
        }
        const format = prompt('내보낼 형식을 선택하세요 (xlsx, csv, json):', 'xlsx');
        if (!format) return;
        if (format.toLowerCase() === 'xlsx') {
            const ws = XLSX.utils.json_to_sheet(results);
            const wb = XLSX.utils.book_new();
            XLSX.utils.book_append_sheet(wb, ws, "결과");
            XLSX.writeFile(wb, "크롤링_결과.xlsx");
            log('💾 결과를 "크롤링_결과.xlsx" 파일로 내보냈습니다.');
            GM_notification({
                text: '결과를 "크롤링_결과.xlsx" 파일로 내보냈습니다.',
                title: '웹 크롤러 8.6',
                timeout: 3000
            });
        } else if (format.toLowerCase() === 'csv') {
            const csv = results.map(row => `"${row.title.replace(/"/g, '""')}","${row.link.replace(/"/g, '""')}","${row.description.replace(/"/g, '""')}","${row.image.replace(/"/g, '""')}","${row.htmlLink}","${row.whoisInfo}","${row.pageLoadTime}","${row.language}","${row.metaKeywords}","${row.metaRobots}","${row.openGraph}","${row.securityHeaders}","${row.domainAge}","${row.techStack}","${row.backlinks}","${row.pageStructure}"`).join('\n');
            const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = '크롤링_결과.csv';
            a.click();
            log('💾 결과를 "크롤링_결과.csv" 파일로 내보냈습니다.');
            GM_notification({
                text: '결과를 "크롤링_결과.csv" 파일로 내보냈습니다.',
                title: '웹 크롤러 8.6',
                timeout: 3000
            });
        } else if (format.toLowerCase() === 'json') {
            const json = JSON.stringify(results, null, 2);
            const blob = new Blob([json], { type: 'application/json;charset=utf-8;' });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = '크롤링_결과.json';
            a.click();
            log('💾 결과를 "크롤링_결과.json" 파일로 내보냈습니다.');
            GM_notification({
                text: '결과를 "크롤링_결과.json" 파일로 내보냈습니다.',
                title: '웹 크롤러 8.6',
                timeout: 3000
            });
        } else {
            alert('지원하지 않는 형식입니다.');
        }
    }

    // 결과 테이블 정렬 함수
    function sortTable(n) {
        const table = document.getElementById("resultsTable");
        let switching = true;
        let dir = "asc";
        let switchcount = 0;
        while (switching) {
            switching = false;
            const rows = table.rows;
            for (let i = 1; i < (rows.length - 1); i++) {
                let shouldSwitch = false;
                const x = rows[i].getElementsByTagName("TD")[n];
                const y = rows[i + 1].getElementsByTagName("TD")[n];
                let xContent = x.textContent || x.innerText;
                let yContent = y.textContent || y.innerText;

                // 숫자 비교
                if (n === 6 || n === 12 || n === 14 || n === 15) { // 페이지 로딩 시간, 도메인 연령, 백링크, 페이지 구조
                    xContent = parseInt(xContent);
                    yContent = parseInt(yContent);
                }

                if (dir === "asc") {
                    if (xContent > yContent) {
                        shouldSwitch = true;
                        break;
                    }
                } else if (dir === "desc") {
                    if (xContent < yContent) {
                        shouldSwitch = true;
                        break;
                    }
                }
            }
            if (shouldSwitch) {
                rows[i].parentNode.insertBefore(rows[i + 1], rows[i]);
                switching = true;
                switchcount++;
            } else {
                if (switchcount === 0 && dir === "asc") {
                    dir = "desc";
                    switching = true;
                }
            }
        }
    }

    // 사용자 경험 향상을 위한 함수 추가
    function capitalizeFirstLetter(string) {
        if (!string) return '';
        return string.charAt(0).toUpperCase() + string.slice(1);
    }

    // 항목 검색 기능
    function filterResults() {
        const filter = searchResultsInput.value.toLowerCase();
        const rows = resultsBody.getElementsByTagName('tr');
        for (let i = 0; i < rows.length; i++) {
            const cells = rows[i].getElementsByTagName('td');
            let match = false;
            for (let j = 0; j < cells.length; j++) {
                if (cells[j].innerText.toLowerCase().includes(filter)) {
                    match = true;
                    break;
                }
            }
            rows[i].style.display = match ? '' : 'none';
        }
    }

    // 로그 초기화 함수
    function clearLogs() {
        if(confirm('로그를 초기화하시겠습니까?')) {
            logMessages = [];
            logContainer.innerHTML = `<strong>로그:</strong><br>`;
            GM_setValue('crawlerLogs', JSON.stringify(logMessages));
            log('🧹 로그를 초기화하였습니다.');
        }
    }

    // 진행률 업데이트 함수
    function updateProgress() {
        if (totalPages === 0) {
            progressBar.value = 0;
            progressText.innerText = '0%';
            return;
        }
        const progress = Math.min((currentPage - 1) / totalPages * 100, 100);
        progressBar.value = progress;
        progressText.innerText = `${Math.round(progress)}%`;
    }

    // 크롤링 오류가 발생한 행에 대한 미리보기 추가
    function addPreviewEvents(row, link) {
        const htmlBtn = row.querySelector('.html-view-btn');
        if (htmlBtn) {
            htmlBtn.addEventListener('click', () => {
                const decodedLink = decodeURIComponent(htmlBtn.getAttribute('data-link'));
                openPreview(decodedLink);
            });

            // HTML 버튼에 마우스 호버 시 미리보기 표시
            htmlBtn.addEventListener('mouseover', (e) => {
                const decodedLink = decodeURIComponent(htmlBtn.getAttribute('data-link'));
                showPreview(e, decodedLink);
            });
            htmlBtn.addEventListener('mouseout', hidePreview);
        }
    }

    // 미리보기 표시 함수
    function showPreview(event, link) {
        tooltip.style.left = (event.pageX + 15) + 'px';
        tooltip.style.top = (event.pageY + 15) + 'px';
        tooltip.style.display = 'block';
        tooltip.innerHTML = `<iframe src="${link}" title="미리보기"></iframe>`;
    }

    function hidePreview() {
        tooltip.style.display = 'none';
        tooltip.innerHTML = '';
    }

    // 사이트 미리보기 열기 함수
    function openPreview(link) {
        const analyticsTab = container.querySelector('#analytics');
        const previewContainerId = 'site-preview-container';
        let previewContainer = container.querySelector(`#${previewContainerId}`);

        if (!previewContainer) {
            previewContainer = document.createElement('div');
            previewContainer.id = previewContainerId;
            previewContainer.className = 'chart-container';
            previewContainer.style.height = '450px';
            analyticsTab.appendChild(previewContainer);
        }

        previewContainer.innerHTML = `
            <h3>사이트 미리보기: <a href="${link}" target="_blank">${link}</a></h3>
            <iframe src="${link}" style="width: 100%; height: 400px; border: none; border-radius: 4px;"></iframe>
        `;

        // 분석 탭으로 이동
        const analyticsTabBtn = container.querySelector('.tab[data-tab="analytics"]');
        analyticsTabBtn.click();
    }

    // WHOIS 정보 가져오기 및 표시 함수
    function fetchWhoisData(domain) {
        log(`📡 WHOIS 정보를 가져오는 중: ${domain}`);
        const apiUrl = `https://jsonwhois.com/api/v1/whois?domain=${encodeURIComponent(domain)}`;

        GM_xmlhttpRequest({
            method: "GET",
            url: apiUrl,
            headers: {
                "Content-Type": "application/json",
                "Authorization": `Token token=${whoisApiKeyInput.value.trim()}`
            },
            onload: function(response) {
                if (response.status === 200) {
                    const data = JSON.parse(response.responseText);
                    displayWhoisData(data, domain);
                    log(`✅ WHOIS 정보를 성공적으로 가져왔습니다: ${domain}`);
                    document.getElementById('successfulRequests').innerText = parseInt(document.getElementById('successfulRequests').innerText) + 1;
                } else {
                    log(`❌ WHOIS 정보 가져오기 실패: ${domain}, 상태 코드: ${response.status}`);
                    whoisContent.innerHTML = `<p>WHOIS 정보를 가져오는 데 실패했습니다.</p>`;
                    whoisModal.style.display = 'block';
                }
            },
            onerror: function() {
                log(`❌ WHOIS 정보 요청 중 오류 발생: ${domain}`);
                document.getElementById('failedRequests').innerText = parseInt(document.getElementById('failedRequests').innerText) + 1;
                whoisContent.innerHTML = `<p>WHOIS 정보를 가져오는 데 오류가 발생했습니다.</p>`;
                whoisModal.style.display = 'block';
            }
        });
    }

    // WHOIS 데이터 표시 함수
    function displayWhoisData(data, domain) {
        if (!data) {
            whoisContent.innerHTML = `<p>No WHOIS data available.</p>`;
            whoisModal.style.display = 'block';
            return;
        }

        const whoisHtml = `
            <h3>${domain}의 WHOIS 정보</h3>
            <pre>${JSON.stringify(data, null, 2)}</pre>
        `;
        whoisContent.innerHTML = whoisHtml;
        whoisModal.style.display = 'block';
    }

    // *******************************
    // 시작
    // *******************************
})();코드 구조 개선하기:
코드를 더 모듈화하고 함수 단위로 나누어 관리하면 이해하기 쉬워지고, 필요한 부분만 집중적으로 검토할 수 있습니다. 이렇게 하면 코드의 길이를 자연스럽게 줄일 수 있습니다.