자려하면 여러 아이디어들이 떠오른다.
오늘은 html의 load이벤트를 통해 React의 componentDidMount()
를 구현할 수 있지 않을까 하는 생각이 들었다.
무시하고 자려했으나 잠이 안 와서 간단하게 실험해보기로 했다.
첫 번째 시도
HTML의 요소들에 onload로 콘솔에 hi 태그명
을 찍어주었다.
1 2 3 4 5 6 7 8 9 10 11 12
| <!DOCTYPE html> <html lang="en"> <head onload="console.log('hi head')"> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body onload="console.log('hi body')"> <div class="content" onload="console.log('hi head')"></div> </body> </html>
|
하지만 작동하지 않았다.
load이벤트와 onload
mdn을 통해 load와 onload를 찾아보았다.
load
load mdn을 참고하였다.
load이벤트는 전체 문서 혹은 이미지, css stylesheet와 같은 종속 리소스가 로드될 때 발생한다고 되어있다.
onload
마찬가지로 onload mdn을 참고하였다.
load이벤트가 발생하는 대상인 이미지, window, XMLHttpRequest에 핸들러를 등록할 수 있다고 되어있다.
두 번째 시도
다른 방법은 없을까 고민하던 도중 blocking resouce인 <script>
태그를 이용하여 구현해주면 어떨까 하는 아이디어가 떠올랐다.
async
나 defer
속성을 지정하지 않은 script태그의 경우 파싱을 멈추고 스크립트를 로드하는 즉시 실행하므로 될 것 같았다.
바로 코드로 옮겼다.
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 31 32 33
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body> <div id="container"> <script> console.log("container load!"); </script> <div id="item">1</div> <script> console.log("item 1 load!"); </script> <div id="item">2</div> <script> console.log("item 2 load!"); </script> <div id="item">3</div> <script> console.log("item 3 load!"); </script> <div id="item">4</div> <script> console.log("item 4 load!"); </script> </div> </body> </html>
|
알 맞게 작동하였다.
세 번째 시도(코드 함수화 하기)
DOMElementDidMount 함수를 작성하여 함수화 하였다..!
callback fungtion과 js의 나머지 매개변수 문법을 이용하여 callback function을 실행해주었다.
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 31 32 33 34 35 36 37 38
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body> <div class="container"> <div class="item1">1</div> <div class="item2">2</div> <div class="item3">3</div> <div class="item4">4</div> </div> <script> function DOMElementDidMount($selector, $func, ...$params) { const $target = [...document.querySelectorAll($selector)];
$target.forEach((ele) => { const $callfunc = document.createElement("script"); const $textContext = document.createTextNode( `(${$func})(${$params})` );
$callfunc.appendChild($textContext); ele.prepend($callfunc); }); } const hi = (...$params) => console.log(`hi ${$params.join(" ")}`); DOMElementDidMount(".container", hi, '"hello"', '"container"'); DOMElementDidMount(".item1", hi, '"hello"', '"item1"'); DOMElementDidMount(".item2", hi, '"hello"', '"item2"'); DOMElementDidMount(".item3", hi, '"hello"', '"item3"'); DOMElementDidMount(".item4", hi, '"hello"', '"item4"'); </script> </body> </html>
|
알맞은 위치에 script 태그가 추가되었다.
콘솔에도 알맞게 출력하고
크롬의 성능탭에서도 5개의 prepend 이벤트가 순서대로 발생함을 볼 수있다.
한계
위 방법은 두 가지 한계가 있었다.(내가 모르는 한계가 더 있을 것이다.)
성능의 심한 저하
$target
이 된 요소마다 script 태그를 집어넣으므로 계속해서 HTML 파싱을 막게된다.
이는 성능의 큰 저하로 연결된다.
재사용성의 부재
처음 랜더링 될 때 한 번을 제외하고는 계속 호출하거나 재사용하기가 어렵다.
개선방안
클래스형을 사용하여 실제 HTML을 수정한 후 최하단에서 정의한 DOMElementDidMount()함수를 호출하면 되지 않을까 싶었다.
느낀점
예전에 vanilla js로 웹컴포넌트를 구현하는 아티클을 읽은며 따라해본 적이 있는데 그 때는 필요성을 잘 느끼지 못했다.
한 번 흉내내려고 해보니 개념도 확실하게 잡히고 확실한 필요성을 느꼈다.
리액트가 왜 클래스형 컴포넌트로 시작했는지를 느꼈다..!(계기는 사소하고 이상하지만!)