일반 HTML 파일에 HTML include/imports 하는 방법

    반응형

    프론트엔드 개발자가 아닌 마크업을 위주로 하는 웹퍼블리셔들은

    대부분 일반 html, css 파일로 작업을하게 됩니다

    페이지가 많아지다보면 중복되는 내용이 많아지죠?

    특히 header와 footer는 거의 고정입니다

    근데 중간에 수정을 해줘야하는 일이 발생한다면?

    같은 header가 들어간 n개 이상의 페이지를 일일히 수정해줘야합니다

    express 템플린 엔진, jsp, php 등 서버사이드나 동적 언어를 사용하면

    include가 가능해서 이런 걱정을 안해도 되지만,

    html에는 include 기능이 없습니다

    검색을 하면 다양한 방법들이 나오는데,

    제가 시도해봤던 방법들의 과정과 최종 결과를 공유하려고 합니다

     

     

     


     

     

     

    1. HTML imports

    구글에 html import를 치면 맨 위에 뜨는 내용입니다

    www.html5rocks.com/ko/tutorials/webcomponents/imports/

     

    HTML Imports: #include for the web - HTML5 Rocks

    HTML Imports allows you to include HTML/CSS/JS in other HTML documents.

    www.html5rocks.com

    1
    <link rel="import" href="header.html">
    cs

    본문에서는 <link> 엘리먼트를 이용하여 html 파일을 import 해서 불러오라고 합니다

    하지만 이 글은 2013년도 글이며 (...)

    저 기능은 더이상 지원하지 않습니다. MDN에서 내용 확인이 가능합니다

    developer.mozilla.org/en-US/docs/Web/Web_Components/HTML_Imports

     

    HTML Imports

    HTML Imports is intended to be the packaging mechanism for web components, but you can also use HTML Imports by itself.

    developer.mozilla.org

     

     

     

     

    2. iframe 태그 사용하기

    css-tricks.com/the-simplest-ways-to-handle-html-includes/#use-iframes

     

    The Simplest Ways to Handle HTML Includes | CSS-Tricks

    It's extremely surprising to me that HTML has never had any way to include other HTML files within it. Nor does there seem to be anything on the horizon

    css-tricks.com

    위 본문에서 따왔습니다

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <body>
     
        <iframe src="./header.html"></iframe>
     
        ...
     
        <iframe src="./footer.html"></iframe>
     
    </body>
    cs

    iframe을 이용해 파일을 불러오는 것입니다

    iframe은 현재 페이지에 또다른 페이지를 포함하도록 해줍니다

    하지만 iframe은 문제점이 꽤나 많습니다

    • XSS 공격에 취약합니다. 악의적인 스크립트를 가지고 있는 사이트를 띄울 수 있다고 합니다. 물론 이래저래 공격을 피하는 방법으로 여러가지 방법이 제시되어 있긴합니다
    • 외부 스타일 적용이 어렵습니다. 특히 height 값이 마음대로 조작되지 않는다고 합니다 (스크롤이 자동으로 생성됨) 제이쿼리로 해결하는 방법이 있다고는 하지만 매우 번거로운 것 같습니다
    • 웹 크롤링 문제. iframe으로 불러온 소스는 숨겨져 있어서 찾기가 어렵습니다. 
    • 접근성, 사용성 저하 문제. 프레임 구조의 문서는 제목을 프레임 셋 본체의 <title> 제목으로 보여주기 때문입니다. 검색 엔진에 등록시 프레임셋 뿐만 아니라 메뉴용, 콘텐츠용 페이지들까지 함께 크롤링이 되는데, 경로로 들어가면 프레임에 넣어서 표시하도록 만든 페이지만 뜨게됩니다.
    • iframe으로 불러온 페이지의 데이터가 큰 경우 브라우저의 속도 저하가 우려됩니다

    이러한 다양한 이유로 요즘에는 iframe 사용을 자제하는 편입니다

    무엇보다 iframe으로 불러왔을때 자동으로 스크롤이 생성되더라고요

    그래서 방법을 찾긴 했습니다

    www.filamentgroup.com/lab/html-includes/

     

    HTML Includes That Work Today | Filament Group, Inc., Boston, MA

    Read this page on the Filament Group website

    www.filamentgroup.com

    1
    2
    3
    4
    5
    <iframe
     src="/images/includespost/htmlexample.html" 
    onload="this.before((this.contentDocument.body||this.contentDocument).children[0]);this.remove()"
    >
    </iframe>
    cs

    기존의 iframe으로 넣은 페이지의 소스를 보면 iframe 속 페이지의<html>, <head> 등 내용이 다 떴습니다

    근데 위의 소스같이 쓸 경우 <body> 내용만 소스에 보여집니다

    (그래서 그런지 외부 CSS 링크를 걸었거나, 스크립트는 적용이 안된채 뜹니다)

    그래서 이 방법은 제외😅

     

     

     

     

    5. object 사용

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <body>
        <div id="header"></div>
     
        <script>
            const header = document.getElementById("header");
     
            header.innerHTML = '<object type="text/html" data="header.html"></object>';
        </script>
    </body>
    cs

    iframe 방법과 비슷한 방법입니다

    iframe 대신 object 태그를 사용한 것인데, 자바스크립트로 삽입한 것 뿐입니다

    이 또한 어느 블로그가 제이쿼리로 작성한걸 자바스크립트로 바꿔 쓴 것입니다

    불러오기에 성공은 했지만 자동 스크롤이 생성되고 제어가 안되더군요

    무엇보다 object 태그는 퇴화된 태그입니다. 현재는 잘 쓰지 않죠

     

     

     

     

    4. Jquery 사용

    blog.naver.com/PostView.nhn?blogId=blacklish1&logNo=221017411129&parentCategoryNo=&categoryNo=27&viewDate=&isShowPopularPosts=true&from=search

     

    html에서 html 파일 인클루드 하기

    html 작업시 확장자가 php, asp, jsp 등등은 인클루드가 가능하다. 기본적으로 공통 영역이 되는 부분들은 ...

    blog.naver.com

    1
    2
    3
    4
    5
    6
     
    $(document).ready(function(){
        $(#header).load("/header.html");
        $(#footer).load("/footer.html");
    });
     
    cs

    한글 문서 중에 가장 많이 제시되는 방법입니다

    그러나 저희는 제이쿼리는 최대한 쓰고싶지 않아서 제외....

     

     

     

     

    5. 자바스크립트 AJAX를 이용한 방법

    sjh836.tistory.com/52

     

    JS를 이용한 HTML include(header,nav,footer 따로 관리하기)

    JS를 이용한 HTML include HTML에서 보통 머릿말, 꼬리말, 메뉴바 등이 흔하게 중복된다.(서버사이드언어나 동적언어를 사용하지 않는다면 메뉴가 바뀔때마다 전부다 수동으로 바꾸어 주어야한다.)

    sjh836.tistory.com

    위 블로그가 참고한 페이지

    https://www.w3schools.com/howto/howto_html_include.asp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    <body>
        <div include-html="header.html"></div>
     
        <script>
            function includeHTML() {
                var z, i, elmnt, file, xhttp;
                z = document.getElementsByTagName("*");
                for (i = 0; i < z.length; i++) {
                    elmnt = z[1];
                file = elmnt.getAttribute("include-html");
                if(file) {
                    xhttp = new XMLHttpRequest();
                    xhttp.onreadystatechange = function() {
                        if(this.readyState == 4 && this.status == 200) {
                            elmnt.innerHTML = this.responseText;
                            elmnt.removeAttribute("include-html");
                            includeHTML();
                    }
                }
                xhttp.open("GET", file, true);
                xhttp.send();
                return;
                }
            }
        }
        
        includeHTML();
        
        </script>
    </body>
    cs

    위 자바스크립트를 해석해보자면 getElementsByTagName 모든 엘리먼트들 중

    include-html 이름의 속성 값을 찾습니다

    그 후 include-html 속성 값을 파일 이름으로 사용하여 HTTP 요청을 합니다

    elmnt.innerHTML = this.responseText 서버에 요청하여 응답으로 받은 데이터를 문자열로 html에 그립니다

    그리고 include-html 속성을 removeAttribute 제거하고 다시 호출하는 재귀함수 사용!

    (실은 더 디테일하게 해석하고 싶지만... 잘 모르겠습니다  elmnt = z[1]; 이 부분이 왜 필요한걸까요?ㅠㅠ)

    매우 성공적으로 html 안에 html이 불러와집니다!

    💥 그러나 제 사수님께서 괜찮은 방법이었으나 코드 정리가 필요할 것 같다고 해서  (특히 재귀함수의 위험성)

    사수님께서 직접 수정을 해주신 최종 결과물!

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    <body>
         <div data-include-path="footer.html"></div>
     
        <script>
     
            window.addEventListener('load'function() {
                var allElements = document.getElementsByTagName('*');
                Array.prototype.forEach.call(allElements, function(el) {
                    var includePath = el.dataset.includePath;
                    if (includePath) {
                        var xhttp = new XMLHttpRequest();
                        xhttp.onreadystatechange = function () {
                            if (this.readyState == 4 && this.status == 200) {
                                el.outerHTML = this.responseText;
                            }
                        };
                        xhttp.open('GET', includePath, true);
                        xhttp.send();
                    }
                });
            });
     
        </script>
    </body>
    cs

    html문서가 로드되는 시점에 실행되는 함수입니다

    모든 엘리먼트들을 불러옵니다

    Array.prototype.forEach.call을 쓴 이유는 el이 유사배열이기에..? 쓴건가 봅니다..!

    (사실 완벽한 해석이 불가능합니다😭 어림짐작 검색해보며 해석중...)

    그리고 그 el 엘리먼트들 중에서 data-include-path 속성이 붙은 값을 찾습니다

    outerHTML을 쓴 이유는 각각 html에서 태그들을 무엇을 쓸지 모르기에 자기 자신을 포함하기 위해서인 것 같습니다!

    예를 들어 <header></header> 으로 쓸수도 <div id="header"></div> 로 쓸수도 있으니까 그런것 같습니다

     

     

     

     

    🔥 마무리

    이렇게 자바스크립트 AJAX로 html 노가다 수정 작업을 좀 줄일 수 있게되었습니다!

    무엇보다 코드 리뷰를 해주며 막힘없이 쭉쭉 써내려가던 사수님.. 정말 대단하십니다

    시니어 개발자는 역시 남다르다.......😵

    저도 언젠간 남이 쓴 코드를 완벽히 해석하며

    이것보단 이게 더 괜찮다고 휘리릭 코드를 수정해나가는 그런 진취적인 개발자가 될 수 있겠죠....?

    안그럴려고 애를 쓰지만 저는 남의 코드를 복붙하는 멍청이 개발자가 된 것 같아 슬픕니다😭

    반응형

    댓글