일반 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/
1
|
<link rel="import" href="header.html">
|
cs |
본문에서는 <link> 엘리먼트를 이용하여 html 파일을 import 해서 불러오라고 합니다
하지만 이 글은 2013년도 글이며 (...)
저 기능은 더이상 지원하지 않습니다. MDN에서 내용 확인이 가능합니다
developer.mozilla.org/en-US/docs/Web/Web_Components/HTML_Imports
2. iframe 태그 사용하기
css-tricks.com/the-simplest-ways-to-handle-html-includes/#use-iframes
위 본문에서 따왔습니다
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/
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 사용
1
2
3
4
5
6
|
$(document).ready(function(){
$(#header).load("/header.html");
$(#footer).load("/footer.html");
});
|
cs |
한글 문서 중에 가장 많이 제시되는 방법입니다
그러나 저희는 제이쿼리는 최대한 쓰고싶지 않아서 제외....
5. 자바스크립트 AJAX를 이용한 방법
위 블로그가 참고한 페이지
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 노가다 수정 작업을 좀 줄일 수 있게되었습니다!
무엇보다 코드 리뷰를 해주며 막힘없이 쭉쭉 써내려가던 사수님.. 정말 대단하십니다
시니어 개발자는 역시 남다르다.......😵
저도 언젠간 남이 쓴 코드를 완벽히 해석하며
이것보단 이게 더 괜찮다고 휘리릭 코드를 수정해나가는 그런 진취적인 개발자가 될 수 있겠죠....?
안그럴려고 애를 쓰지만 저는 남의 코드를 복붙하는 멍청이 개발자가 된 것 같아 슬픕니다😭