/* * 파일명: script.js * 위치: /assets/js/script.js * 기능: 키워드 조합기 JavaScript * 작성일: 2025-06-15 */ // =================================== // 전역 변수 // =================================== /* 애플리케이션 상태 */ var currentCardCount = 4; var MAX_CARDS = 4; var MIN_CARDS = 1; var MAX_KEYWORDS = 100; // =================================== // 초기화 함수 // =================================== /** * 페이지 로드 시 초기화 */ $(document).ready(function() { console.log('페이지 로드 완료'); initializeCards(); bindEvents(); updateCardControls(); }); /** * 카드 초기화 */ function initializeCards() { console.log('카드 초기화 시작'); generateCards(currentCardCount); } /** * 이벤트 바인딩 */ function bindEvents() { console.log('이벤트 바인딩 시작'); // 카드 추가/제거 버튼 $(document).on('click', '.add-card-btn', addCard); $(document).on('click', '.remove-card-btn', removeCard); // 조합 생성 버튼 $(document).on('click', '.generate-btn', generateCombinations); // 다운로드 버튼 $(document).on('click', '.download-excel-btn', downloadExcel); $(document).on('click', '.download-txt-btn', downloadTxt); // 키워드 입력 시 실시간 카운트 $(document).on('input', '.keyword-textarea', updateKeywordCount); } // =================================== // 카드 관리 함수 // =================================== /** * 카드 생성 */ function generateCards(count, preserveData) { console.log('카드 생성: ' + count + '개'); // 기존 데이터 보존이 필요한 경우 수집 var existingData = {}; if (preserveData) { $('.keyword-textarea').each(function() { var cardNum = $(this).data('card'); existingData[cardNum] = $(this).val(); }); } var cardsHtml = ''; for (var i = 1; i <= count; i++) { cardsHtml += '
'; cardsHtml += '
'; cardsHtml += '
키워드' + i + '
'; cardsHtml += '0/100개'; cardsHtml += '
'; cardsHtml += '
'; cardsHtml += ''; cardsHtml += '
'; cardsHtml += '
'; } // 카드 개수에 따른 클래스 추가 $('.keyword-cards').removeClass('cards-1 cards-2 cards-3 cards-4').addClass('cards-' + count); $('.keyword-cards').html(cardsHtml); // 기존 데이터 복원 if (preserveData) { for (var cardNum in existingData) { if (cardNum <= count) { $('.keyword-textarea[data-card="' + cardNum + '"]').val(existingData[cardNum]); updateKeywordCountForCard(cardNum, existingData[cardNum]); } } } console.log('카드 생성 완료'); } /** * 카드 추가 */ function addCard() { console.log('카드 추가 시도'); if (currentCardCount < MAX_CARDS) { currentCardCount++; generateCards(currentCardCount, true); // 데이터 보존 updateCardControls(); console.log('카드 추가 완료: ' + currentCardCount + '개'); } } /** * 카드 제거 */ function removeCard() { console.log('카드 제거 시도'); if (currentCardCount > MIN_CARDS) { currentCardCount--; generateCards(currentCardCount, true); // 데이터 보존 updateCardControls(); clearResults(); console.log('카드 제거 완료: ' + currentCardCount + '개'); } } /** * 카드 컨트롤 버튼 상태 업데이트 */ function updateCardControls() { $('.add-card-btn').prop('disabled', currentCardCount >= MAX_CARDS); $('.remove-card-btn').prop('disabled', currentCardCount <= MIN_CARDS); } /** * 키워드 개수 업데이트 */ function updateKeywordCount() { var textarea = $(this); var cardNumber = textarea.data('card'); var text = textarea.val(); updateKeywordCountForCard(cardNumber, text); } /** * 특정 카드의 키워드 개수 업데이트 */ function updateKeywordCountForCard(cardNumber, text) { var keywords = parseKeywords(text); var count = keywords.length; // 개수 표시 업데이트 $('.keyword-card[data-card="' + cardNumber + '"] .card-progress').text(count + '/100개'); // 최대 개수 초과 시 경고 var textarea = $('.keyword-textarea[data-card="' + cardNumber + '"]'); if (count > MAX_KEYWORDS) { textarea.addClass('is-invalid'); showAlert('키워드는 최대 100개까지 입력 가능합니다.', 'warning'); } else { textarea.removeClass('is-invalid'); } } // =================================== // 키워드 처리 함수 // =================================== /** * 키워드 파싱 */ function parseKeywords(text) { if (!text || text.trim() === '') { return []; } var lines = text.split('\n'); var result = []; for (var i = 0; i < lines.length; i++) { var line = lines[i].trim(); if (line !== '') { result.push(line); } } return result.slice(0, MAX_KEYWORDS); } /** * 모든 카드의 키워드 수집 */ function collectAllKeywords() { var keywordGroups = []; $('.keyword-textarea').each(function() { var keywords = parseKeywords($(this).val()); if (keywords.length > 0) { keywordGroups.push(keywords); } }); return keywordGroups; } // =================================== // 조합 생성 함수 // =================================== /** * 조합 생성 실행 */ function generateCombinations() { console.log('조합 생성 시작'); var keywordGroups = collectAllKeywords(); // 유효성 검사 if (keywordGroups.length === 0) { showAlert('최소 하나의 카드에 키워드를 입력해주세요.', 'warning'); return; } if (keywordGroups.length === 1) { showAlert('조합을 만들기 위해서는 최소 2개의 카드에 키워드가 필요합니다.', 'warning'); return; } // 로딩 상태 시작 showLoading(true); // 서버에 조합 생성 요청 $.ajax({ url: 'index.php', method: 'POST', data: { action: 'generate', keywords: keywordGroups }, dataType: 'json', success: function(response) { if (response.success) { displayResults(response.data); } else { showAlert(response.message || '조합 생성 중 오류가 발생했습니다.', 'error'); } }, error: function() { showAlert('서버 통신 중 오류가 발생했습니다.', 'error'); }, complete: function() { showLoading(false); } }); } /** * 조합 결과 표시 */ function displayResults(combinations) { if (!combinations || combinations.length === 0) { showEmptyResults(); return; } // 결과 헤더 업데이트 $('.results-info').text('총 ' + combinations.length.toLocaleString() + '개의 조합이 생성되었습니다.'); // 결과 목록 생성 var resultsHtml = ''; for (var i = 0; i < combinations.length; i++) { resultsHtml += '
' + combinations[i] + '
'; } $('.results-list').html(resultsHtml); $('.results-container').show(); // 결과 영역으로 스크롤 $('html, body').animate({ scrollTop: $('.results-container').offset().top - 20 }, 500); } /** * 빈 결과 표시 */ function showEmptyResults() { var emptyHtml = '
'; emptyHtml += ''; emptyHtml += '

생성된 조합이 없습니다.

'; emptyHtml += '
'; $('.results-list').html(emptyHtml); $('.results-info').text(''); $('.results-container').show(); } /** * 결과 초기화 */ function clearResults() { $('.results-container').hide(); $('.results-list').empty(); $('.results-info').text(''); } // =================================== // 다운로드 함수 // =================================== /** * Excel 파일 다운로드 */ function downloadExcel() { downloadFile('excel'); } /** * TXT 파일 다운로드 */ function downloadTxt() { downloadFile('txt'); } /** * 파일 다운로드 실행 */ function downloadFile(type) { var keywordGroups = collectAllKeywords(); if (keywordGroups.length === 0) { showAlert('다운로드할 조합이 없습니다.', 'warning'); return; } // 숨겨진 폼 생성하여 다운로드 var form = $('
', { method: 'POST', action: 'download.php' }); form.append($('', { type: 'hidden', name: 'type', value: type })); form.append($('', { type: 'hidden', name: 'keywords', value: JSON.stringify(keywordGroups) })); $('body').append(form); form.submit(); form.remove(); } // =================================== // UI 헬퍼 함수 // =================================== /** * 로딩 상태 표시 */ function showLoading(show) { if (show) { $('.generate-btn').prop('disabled', true).html('조합 생성 중...'); $('body').addClass('loading'); } else { $('.generate-btn').prop('disabled', false).html('조합하기'); $('body').removeClass('loading'); } } /** * 확인 대화상자 표시 */ function showConfirmDialog(message, onConfirm) { var confirmHtml = ''; // 기존 모달 제거 $('#confirmModal').remove(); // 새 모달 추가 $('body').append(confirmHtml); // 확인 버튼 이벤트 $('#confirmBtn').on('click', function() { $('#confirmModal').modal('hide'); if (onConfirm) { onConfirm(); } }); // 모달 표시 $('#confirmModal').modal('show'); } /** * 알림 표시 */ function showAlert(message, type) { type = type || 'info'; var alertClass = type === 'error' ? 'danger' : type; var iconClass = 'bi-info-circle'; if (alertClass === 'success') iconClass = 'bi-check-circle'; else if (alertClass === 'warning') iconClass = 'bi-exclamation-triangle'; else if (alertClass === 'danger') iconClass = 'bi-x-circle'; var alertHtml = ''; // 기존 알림 제거 $('.alert').remove(); // 새 알림 추가 $('.main-container').prepend(alertHtml); // 자동 제거 setTimeout(function() { $('.alert').fadeOut(function() { $(this).remove(); }); }, 5000); }