From e27ee29dccc4fb5849ba0caacd27fc228bba5eea Mon Sep 17 00:00:00 2001 From: KINDNICK <68893236+KINDNICK@users.noreply.github.com> Date: Thu, 7 Aug 2025 01:20:11 +0900 Subject: [PATCH] =?UTF-8?q?Fix=20:=20=EA=B5=AC=EA=B8=80=20=EC=84=9C?= =?UTF-8?q?=EC=B9=98=EC=96=B4=EB=93=9C=EB=B0=94=EC=9D=B4=EC=A0=80=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=83=89=EC=9D=B8=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .htaccess | 63 ++++++++++++++++++++++++++++++++++ robots.txt | 18 ++++++++++ script.js | 97 +++++++++++++++++++++++++++++++++++++++++------------ sitemap.xml | 9 +++++ 4 files changed, 165 insertions(+), 22 deletions(-) create mode 100644 .htaccess create mode 100644 robots.txt create mode 100644 sitemap.xml diff --git a/.htaccess b/.htaccess new file mode 100644 index 0000000..5db8815 --- /dev/null +++ b/.htaccess @@ -0,0 +1,63 @@ +# Enable URL rewriting +RewriteEngine On + +# Force HTTPS (if SSL is available) +# RewriteCond %{HTTPS} off +# RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301] + +# Remove trailing slashes +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule ^(.*)/$ /$1 [L,R=301] + +# Remove .html extension +RewriteCond %{REQUEST_FILENAME} !-d +RewriteCond %{REQUEST_FILENAME} !-f +RewriteRule ^([^\.]+)$ $1.html [NC,L] + +# Handle SPA routing - redirect all non-file requests to index.html +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule ^(.*)$ /index.html [L] + +# Handle 404 errors +ErrorDocument 404 /index.html + +# Enable compression + + AddOutputFilterByType DEFLATE text/plain + AddOutputFilterByType DEFLATE text/html + AddOutputFilterByType DEFLATE text/xml + AddOutputFilterByType DEFLATE text/css + AddOutputFilterByType DEFLATE application/xml + AddOutputFilterByType DEFLATE application/xhtml+xml + AddOutputFilterByType DEFLATE application/rss+xml + AddOutputFilterByType DEFLATE application/javascript + AddOutputFilterByType DEFLATE application/x-javascript + + +# Set browser caching + + ExpiresActive on + ExpiresByType text/css "access plus 1 year" + ExpiresByType application/javascript "access plus 1 year" + ExpiresByType image/png "access plus 1 year" + ExpiresByType image/jpg "access plus 1 year" + ExpiresByType image/jpeg "access plus 1 year" + ExpiresByType image/gif "access plus 1 year" + ExpiresByType image/webp "access plus 1 year" + ExpiresByType image/svg+xml "access plus 1 year" + + +# Security headers + + Header always set X-Content-Type-Options nosniff + Header always set X-Frame-Options DENY + Header always set X-XSS-Protection "1; mode=block" + Header always set Referrer-Policy "strict-origin-when-cross-origin" + + +# Prevent access to sensitive files + + Order Allow,Deny + Deny from all + \ No newline at end of file diff --git a/robots.txt b/robots.txt new file mode 100644 index 0000000..679158e --- /dev/null +++ b/robots.txt @@ -0,0 +1,18 @@ +User-agent: * +Allow: / + +# Sitemap +Sitemap: https://minglestudio.co.kr/sitemap.xml + +# Disallow specific files and patterns +Disallow: /cgi-bin/ +Disallow: /tmp/ +Disallow: /private/ +Disallow: /*#* + +# Allow important files +Allow: /index.html +Allow: /styles.css +Allow: /script.js +Allow: /mingle-logo.webp +Allow: /Studio_Image/ \ No newline at end of file diff --git a/script.js b/script.js index bb47b81..422f2a6 100644 --- a/script.js +++ b/script.js @@ -5,42 +5,93 @@ document.addEventListener('DOMContentLoaded', function() { const hamburger = document.querySelector('.hamburger'); const navMenu = document.querySelector('.nav-menu'); - hamburger.addEventListener('click', function() { - hamburger.classList.toggle('active'); - navMenu.classList.toggle('active'); - }); + if (hamburger) { + hamburger.addEventListener('click', function() { + hamburger.classList.toggle('active'); + navMenu.classList.toggle('active'); + }); + } // 네비게이션 링크 클릭 시 모바일 메뉴 닫기 document.querySelectorAll('.nav-link').forEach(link => { link.addEventListener('click', () => { - hamburger.classList.remove('active'); - navMenu.classList.remove('active'); + if (hamburger) hamburger.classList.remove('active'); + if (navMenu) navMenu.classList.remove('active'); }); }); - // 스무스 스크롤 + // 스무스 스크롤 (리디렉션 방지) - 개선된 버전 document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', function (e) { e.preventDefault(); - const target = document.querySelector(this.getAttribute('href')); + const targetId = this.getAttribute('href'); + const target = document.querySelector(targetId); + if (target) { - target.scrollIntoView({ - behavior: 'smooth', - block: 'start' + // URL 업데이트 (히스토리 API 사용) - 리디렉션 방지 + if (history.pushState) { + history.pushState(null, null, targetId); + } + + // 스무스 스크롤 + const headerOffset = 80; // 네비게이션 높이 고려 + const elementPosition = target.getBoundingClientRect().top; + const offsetPosition = elementPosition + window.pageYOffset - headerOffset; + + window.scrollTo({ + top: offsetPosition, + behavior: 'smooth' }); } }); }); + // 브라우저 뒤로가기/앞으로가기 처리 + window.addEventListener('popstate', function(e) { + const hash = window.location.hash; + if (hash) { + const target = document.querySelector(hash); + if (target) { + const headerOffset = 80; + const elementPosition = target.getBoundingClientRect().top; + const offsetPosition = elementPosition + window.pageYOffset - headerOffset; + + window.scrollTo({ + top: offsetPosition, + behavior: 'smooth' + }); + } + } + }); + + // 페이지 로드 시 해시가 있으면 해당 섹션으로 스크롤 + if (window.location.hash) { + setTimeout(() => { + const target = document.querySelector(window.location.hash); + if (target) { + const headerOffset = 80; + const elementPosition = target.getBoundingClientRect().top; + const offsetPosition = elementPosition + window.pageYOffset - headerOffset; + + window.scrollTo({ + top: offsetPosition, + behavior: 'smooth' + }); + } + }, 100); + } + // 네비게이션 스크롤 효과 window.addEventListener('scroll', function() { const navbar = document.querySelector('.navbar'); - if (window.scrollY > 100) { - navbar.style.background = 'rgba(255, 255, 255, 0.98)'; - navbar.style.boxShadow = '0 2px 20px rgba(0, 0, 0, 0.1)'; - } else { - navbar.style.background = 'rgba(255, 255, 255, 0.95)'; - navbar.style.boxShadow = 'none'; + if (navbar) { + if (window.scrollY > 100) { + navbar.style.background = 'rgba(255, 255, 255, 0.98)'; + navbar.style.boxShadow = '0 2px 20px rgba(0, 0, 0, 0.1)'; + } else { + navbar.style.background = 'rgba(255, 255, 255, 0.95)'; + navbar.style.boxShadow = 'none'; + } } }); @@ -52,10 +103,10 @@ document.addEventListener('DOMContentLoaded', function() { // 폼 데이터 수집 const formData = new FormData(this); - const name = this.querySelector('input[type="text"]').value; - const email = this.querySelector('input[type="email"]').value; - const subject = this.querySelectorAll('input[type="text"]')[1].value; - const message = this.querySelector('textarea').value; + const name = this.querySelector('input[type="text"]')?.value || ''; + const email = this.querySelector('input[type="email"]')?.value || ''; + const subject = this.querySelectorAll('input[type="text"]')[1]?.value || ''; + const message = this.querySelector('textarea')?.value || ''; // 간단한 유효성 검사 if (!name || !email || !subject || !message) { @@ -119,7 +170,9 @@ document.addEventListener('DOMContentLoaded', function() { setTimeout(() => { notification.style.transform = 'translateX(100%)'; setTimeout(() => { - notification.remove(); + if (notification.parentNode) { + notification.remove(); + } }, 300); }, 3000); } diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 0000000..3980824 --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,9 @@ + + + + https://minglestudio.co.kr/ + 2025-01-27 + weekly + 1.0 + + \ No newline at end of file