1. Load CSS ๐Ÿ‘ฉโ€

1. Including CSS

CSS ๋ฅผ ์ฒ˜์Œ๋ถ€ํ„ฐ HTML ์— ๋‚ด์žฅํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค. HTML ํƒœ๊ทธ ๋‚ด๋ถ€์— style ํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•ด ์ง์ ‘ ๋‚ด์žฅํ•˜๊ฑฐ๋‚˜, HTML ์—˜๋ฆฌ๋จผํŠธ์— ์ธ๋ผ์ธ์œผ๋กœ ์ž‘์„ฑํ•˜๋ฉด ๋ณ„๋„์˜ ์™ธ๋ถ€ ํŒŒ์ผ ์—†์ด ์ž‘๋™์ด ๊ฐ€๋Šฅํ•˜๋‹ค. ๋”ฐ๋ผ์„œ CSS ์Šคํƒ€์ผ์„ ์–ป๊ธฐ ์œ„ํ•œ ๋ณ„๋„์˜ ๋„คํŠธ์›Œํฌ ํ†ต์‹ ์ด ํ•„์š”ํ•˜์ง€ ์•Š๋‹ค.

์ผ๋ฐ˜์ ์œผ๋กœ head ํƒœ๊ทธ ๋‚ด์— link ํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•ด HTTP Request ๋ฅผ ๋ณด๋‚ด๋Š” ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•œ๋‹ค.

<head>
  <link rel="stylesheet" href="/css//main.css" />
</head>

๋ธŒ๋ผ์šฐ์ €์˜ ๋„คํŠธ์›Œํฌ ํƒญ์„ ๋ณด๋ฉด ๋ฆฌ์†Œ์Šค ํƒ€์ž…์ด stylesheet ์ž„์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. ๋ฌผ๋ก , DOM ์ด ์ด๋ฏธ ๊ทธ๋ ค์กŒ๋Š”๋ฐ CSS ๊ฐ€ ๋„ˆ๋ฌด ๋Šฆ๊ฒŒ ๋กœ๋”ฉ๋œ๋‹ค๋ฉด ํ™”๋ฉด์˜ ํ‘œํ˜„์— ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ์ง€๋งŒ CSS ๋Š” JavaScript ์™€ ๋‹ฌ๋ฆฌ DOM ํŒŒ์‹ฑ๊ณผ ๊ด€๋ จํ•ด ์ž‘๋™ ์ž์ฒด๋Š” ์ˆœ์„œ์˜ ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์š”์ฒญ๋งŒ ์ž˜ ํ•˜๋ฉด ํฌ๊ฒŒ ์‹ ๊ฒฝ ์“ธ ๋ถ€๋ถ„์ด ์žˆ์ง€๋Š” ์•Š๋‹ค.

3. @import

@import url("fineprint.css") print;
@import url("bluish.css") speech;
@import "custom.css";
@import url("chrome://communicator/skin/");
@import "common.css" screen;
@import url("landscape.css") screen and (orientation: landscape);

CSS Declarations ์—์„œ๋„ ์„ค๋ช…ํ–ˆ์ง€๋งŒ CSS ํŒŒ์ผ์„ ์ข€ ๋” ํšจ์œจ์ ์œผ๋กœ ๋ถ„๋ฆฌ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š” ์žฅ์ ์€ ์žˆ์ง€๋งŒ ์ง๋ ฌ๋กœ ์š”์ฒญํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค. ์ฒ˜์Œ๋ถ€ํ„ฐ including ๋˜๋Š” ๋ฐฉ์‹์ด ์•„๋‹ˆ๊ณ  ๋จผ์ € ๋ฐ›์•„์˜จ CSS ํŒŒ์ผ์„ ํ•ด์„ํ•˜๊ณ  ์ถ”๊ฐ€๋กœ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ๋•Œ๋ฌธ์— ์‹œ๊ฐ„์ด ์ง€์—ฐ๋˜๊ณ , CSS ์šฐ์„  ์ˆœ์œ„๊ฐ€ ๋’ค๋ฐ”๋€Œ์–ด ์ž˜๋ชป๋œ ์Šคํƒ€์ผ์ด ์ ์šฉ๋˜๊ฑฐ๋‚˜, DOM ์ด ์ด๋ฏธ ๋กœ๋”ฉ ๋˜์—ˆ๋Š”๋ฐ ์ œ๋•Œ ์Šคํƒ€์ผ์ด ์ ์šฉ๋˜์ง€ ์•Š๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

๋”ฐ๋ผ์„œ CSS ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” Link ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•ด ๋ณ‘๋ ฌ๋กœ ์š”์ฒญํ•˜๊ฑฐ๋‚˜, SCSS ๋“ฑ์„ ์‚ฌ์šฉํ•ด ์ „์ฒ˜๋ฆฌ ๊ณผ์ •์„ ๊ฑฐ์ณ์•ผํ•œ๋‹ค.

4. Webpack & JavaScript

Webpack ์„ ์˜ˆ๋กœ ๋“ค์—ˆ์ง€๋งŒ, ๋‹ค๋ฅธ ๋ฒˆ๋“ค๋ง ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•ด๋„ ๋น„์Šทํ•˜๋‹ค. ์šฐ์„  ๋ฒˆ๋“ค๋ง ์˜ต์…˜์„ ์ •ํ™•ํžˆ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์ด ์‰ฝ์ง€๋Š” ์•Š์ง€๋งŒ, ๋ฒˆ๋“ค๋ง์„ ์ ์šฉํ•˜๋ฉด, CSS ํŒŒ์ผ์„ ์ตœ์ ํ™” ํ•˜๋Š” ๊ฒƒ์€ ๋ฌผ๋ก ์ด๊ณ , JavaScript ๋ฅผ ์‚ฌ์šฉํ•ด CSS ๋ฅผ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค.

import "../css/main.css"

๋˜ํ•œ ์ด ๋ฐฉ์‹์˜ ์žฅ์ ์€ SCSS ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๊ฒƒ ์—ญ์‹œ ์†์‰ฝ๊ฒŒ ์ฒ˜๋ฆฌํ•ด์ค€๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

import "../css/main.scss"

React CLI ๋˜๋Š” Vue CLI, Vite CLI ๋“ฑ์„ ์‚ฌ์šฉํ•ด ์ƒ์„ฑํ•œ ์•ฑ์—์„œ ์œ„์™€ ๊ฐ™์€ import ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•œ ์ด์œ ๋‹ค.

5. JavaScript with assert

import style from '../css/main.css' assert { type: 'css' };
document.adoptedStyleSheets = [style];

import ๋ช…๋ น์— ์ง€๊ธˆ ์š”์ฒญํ•˜๋Š” ์ด ๋ฆฌ์†Œ์Šค๋Š” ์‚ฌ์‹ค CSS๋ผ๋Š” ๊ฒƒ์„ ๋ช…์‹œํ•จ์œผ๋กœ์จ JavaScript ๊ฐ€ ์ด๊ฒƒ์„ Stylesheet ๋กœ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ์†์„ฑ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋ฉด JavaScript ๊ฐ€ ์‹คํ–‰๋˜๋Š” ์‹œ์ ์— ๋ฆฌ์†Œ์Šค๋ฅผ ๋ถˆ๋Ÿฌ์™€ ์ ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์†์‰ฝ๊ฒŒ Dynamic import ๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์„ ๊ฐ–๋Š”๋‹ค.

๋‹จ, ์ด ๋ฐฉ๋ฒ•์€ JavaScript ๊ฐ€ ๋‹ค๋ฅธ JavaScript Module ์„ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ์œ„ํ•œ ๋ช…๋ น์–ด์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ˜๋“œ์‹œ ๋ชจ๋“ˆ๋กœ ๋‹ค๋ค„์•ผ ํ•˜๋ฏ€๋กœ <script type="module"></script>๋กœ ์„ ์–ธ์ด ํ•„์š”ํ•˜๋ฉฐ, ์š”์ฒญ๋˜๋Š” CSS ๋Š” ๋ธŒ๋ผ์šฐ์ €์˜ ๋„คํŠธ์›Œํฌ ํƒญ์— ๋ฆฌ์†Œ์Šค ํƒ€์ž…์ด script ๋กœ ํ™•์ธ๋œ๋‹ค.

๋‹ค๋งŒ ์ด ๋ฐฉ๋ฒ•์€ ํ˜„์žฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค. Browser compatibility ๋ฅผ ๋ณด๋ฉด ์•Œ ์ˆ˜ ์žˆ๋“ฏ์ด assert๋Š” ํฌ๋กœ๋ฏธ์›€ ๊ธฐ๋ฐ˜์—์„œ๋งŒ ์ž‘๋™ํ•˜๊ณ , ํŒŒ์ด์–ดํญ์Šค๋‚˜ ์‚ฌํŒŒ๋ฆฌ์—์„œ๋Š” ์ž‘๋™ํ•˜์ง€ ์•Š๋Š”๋‹ค. ํ˜„์‹ค์ ์œผ๋กœ VanillaJS ๋กœ ๊ฐœ๋ฐœ์„ ํ•˜๋”๋ผ๋„ ํฌ๋กœ์Šค ๋ธŒ๋ผ์šฐ์ง• ๋ฌธ์ œ๋กœ ์•„์ง์€ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค. ํ•˜์ง€๋งŒ ์šฐ๋ฆฌ์—๊ฒŒ๋Š” Webpack, Vite ์™€ ๊ฐ™์€ ํ›Œ๋ฅญํ•œ ๋Œ€์•ˆ์ด ์กด์žฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํฌ๊ฒŒ ๋ฌธ์ œ๊ฐ€ ๋˜์ง€๋Š” ์•Š๋Š”๋‹ค.

6. Dynamic import

export const loadStyle = (href) => {
  const link = document.createElement('link');
  link.rel = 'stylesheet';
  link.type = 'text/css';
  link.href = href;
  document.head.appendChild(link);
};

CSS ์—ญ์‹œ Dynamic import ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. UI ์™€ ๊ด€๋ จ๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” JavaScript ์™€ CSS ๋ฅผ ํ•จ๊ป˜ ๋ฐฐํฌํ•˜๋Š” ๋ฐ, ์ผ๋ฐ˜์ ์œผ๋กœ Webpack, Vite ๋“ฑ์ด ์•Œ์•„์„œ import๋ฅผ ์ฒ˜๋ฆฌํ•ด ์ฃผ๊ฒ ์ง€๋งŒ, VanillaJS ๋กœ ๊ฐœ๋ฐœํ•˜๊ฑฐ๋‚˜ CDN ์—์„œ ๊ฐ€์ ธ์˜ฌ ๊ฒฝ์šฐ ์œ„์™€ ๊ฐ™์€ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด head ํƒœ๊ทธ์— ์Šคํƒ€์ผ์„ ๋ถˆ๋Ÿฌ์˜ค๋„๋ก ์š”์ฒญํ•  ์ˆ˜ ์žˆ๋‹ค. ์œ„ ๋ฐฉ๋ฒ•์€ ๋ชจ๋“  ๋ธŒ๋ผ์šฐ์ €์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๊ธฐ๋ณธ์ ์ธ ๋ฐฉ์‹์ด๊ธฐ ๋•Œ๋ฌธ์— JavaScript with assert ์™€ ๊ฐ™์ด ํฌ๋กœ์Šค ๋ธŒ๋ผ์šฐ์ง• ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ํฐ ์žฅ์ ์ด๋‹ค.


2. Load JavaScript ๐Ÿ‘ฉโ€

1. Including JavaScript

CSS ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ฒ˜์Œ๋ถ€ํ„ฐ HTML ์— ๋‚ด์žฅํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค. HTML ํƒœ๊ทธ ๋‚ด๋ถ€์— script ํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•ด ์ง์ ‘ ๋‚ด์žฅํ•  ์ˆ˜ ์žˆ๋‹ค.

<!DOCTYPE html>
<html lang="ko">
  <head></head>
  <body>
    <button type="button" onclick="changeColor('#AEA')">Click!!</button>

    <script>
      function changeColor(color) {
        event.target.style.backgroundColor = color;
      }
    </script>
  </body>
</html>

์ด๋•Œ head ๋ถ€๋ถ„์— script ๋ฅผ ์œ„์น˜์‹œ์ผœ๋„ ์ƒ๊ด€ ์—†์ง€๋งŒ, script ๋‚ด JavaScript ์ฝ”๋“œ์— DOM ์ด ํ•„์š”ํ•œ ์ฝ”๋“œ๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ ์•„์ง ๊ทธ๋ ค์ง€์ง€ ์•Š์€ DOM ์„ ์ฐพ์ง€ ๋ชปํ•ด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ฃผ๋กœ body ํƒœ๊ทธ๊ฐ€ ๋‹ซํžŒ ๋‹ค์Œ ๋˜๋Š” body ํƒœ๊ทธ๊ฐ€ ๋‹ซํžˆ๊ธฐ ์ง์ „ ์— script ๋ฅผ ๋„ฃ๊ณค ํ–ˆ๋‹ค.

๋‹จ, ์œ„์™€ ๊ฐ™์ด ์ž‘์„ฑํ•œ ๊ฒฝ์šฐ onclick="changeColor('#AEA')"์— ์‚ฌ์šฉ๋œ ํ•จ์ˆ˜ changeColor๋Š” ๋ฐ˜๋“œ์‹œ DOM ์— ๋‚ด์žฅ๋˜์–ด ์žˆ์–ด์•ผ ํ•œ๋‹ค. script ๋ฅผ ์™ธ๋ถ€ ๋ฆฌ์†Œ์Šค๋กœ ๋ถ„๋ฆฌํ•ด ๋กœ๋“œํ•˜๋Š” script ๋ฐฉ์‹์œผ๋กœ๋Š” ํ•จ์ˆ˜๋ฅผ ์ฐพ์ง€ ๋ชปํ•œ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์œ„ ์ฝ”๋“œ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ๊ฒƒ์€ a ํƒœ๊ทธ์˜ href ์†์„ฑ์ด๋‚˜ ๋งŽ์€ ์—˜๋ฆฌ๋จผํŠธ์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ onclick, onmouseover, onkeyup ๊ณผ ๊ฐ™์€ ์†์„ฑ์—๋Š” JavaScript ์ž‘์„ฑ๋œ ํ…์ŠคํŠธ๋ฅผ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ JavaScript ์ฝ”๋“œ๋กœ ์ธ์‹ํ•˜๊ณ  ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ๋”ฐ๋ผ์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด CSS ์™€ ๊ฐ™์ด ์—˜๋ฆฌ๋จผํŠธ์— ์ง์ ‘ ์ธ๋ผ์ธ์œผ๋กœ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ ์—ญ์‹œ ๊ฐ€๋Šฅํ•˜๋‹ค.

<button
    type="button"
    onclick="this.style.backgroundColor = '#AEA'"
>
  Click!!
</button>

CSS ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ฒ˜์Œ๋ถ€ํ„ฐ DOM ์— ๋‚ด์žฅ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ณ„๋„์˜ ๋„คํŠธ์›Œํฌ ํ†ต์‹ ์ด ํ•„์š”ํ•˜์ง€ ์•Š๋‹ค.

CSS ๋ฅผ ์ฒ˜์Œ๋ถ€ํ„ฐ HTML ์— ๋‚ด์žฅํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค. HTML ํƒœ๊ทธ ๋‚ด๋ถ€์— style ํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•ด ์ง์ ‘ ๋‚ด์žฅํ•˜๊ฑฐ๋‚˜, HTML ์—˜๋ฆฌ๋จผํŠธ์— ์ธ๋ผ์ธ์œผ๋กœ ์ž‘์„ฑํ•˜๋ฉด ๋ณ„๋„์˜ ์™ธ๋ถ€ ํŒŒ์ผ ์—†์ด ์ž‘๋™์ด ๊ฐ€๋Šฅํ•˜๋‹ค. ๋”ฐ๋ผ์„œ CSS ์Šคํƒ€์ผ์„ ์–ป๊ธฐ ์œ„ํ•œ ๋ณ„๋„์˜ ๋„คํŠธ์›Œํฌ ํ†ต์‹ ์ด ํ•„์š”ํ•˜์ง€ ์•Š๋‹ค.

2. script

์ผ๋ฐ˜์ ์œผ๋กœ head ํƒœ๊ทธ ๋‚ด์— scriptํƒœ๊ทธ์™€ src ์†์„ฑ์„ ์‚ฌ์šฉํ•ด HTTP Request ๋ฅผ ๋ณด๋‚ด๋Š” ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•œ๋‹ค.

<head>
  <script type="text/javascript" src="js/main.js"></script>
</head>

๋ธŒ๋ผ์šฐ์ €์˜ ๋„คํŠธ์›Œํฌ ํƒญ์„ ๋ณด๋ฉด ๋ฆฌ์†Œ์Šค ํƒ€์ž…์ด script ์ž„์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

Including JavaScript ์—์„œ ํ™•์ธํ–ˆ๋“ฏ์ด, DOM ์ด ๋จผ์ € ๊ทธ๋ ค์ ธ์•ผ ํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ์กด์žฌํ•˜๋Š” ๋ฌธ์ œ๋Š” ์—ฌ์ „ํžˆ ์œ ํšจํ•˜๋‹ค.

์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์€ MutationObserver ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ผ๋ฐ˜์ ์ด์ง€ ์•Š์€ ๋ฐฉ๋ฒ•์„ ์ œ์™ธํ•˜๋ฉด 3๊ฐ€์ง€๋กœ ์ •๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.


1 ) window.onload

<button type="button" id="foo">Click!!</button>
window.onload = () => {
  document
    .getElementById('foo')
    .addEventListener('click', () => changeColor('#AEA'));
};


2 ) addEventListener

window.onload ๋Š” HTML ๋ฌธ์„œ์— ํ•œ ๋ฒˆ๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•ด ์ค‘๋ณต์‹œ ๋งˆ์ง€๋ง‰์— ์„ ์–ธ๋œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ์ด์ „์˜ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋ชจ๋‘ ๋ฎ์–ด์“ด๋‹ค. ๋ฌผ๋ก , ์ฝ”๋“œ๋ฅผ ์ž˜ ์ž‘์„ฑํ•˜๋ฉด ๋˜์ง€๋งŒ ๋‹จ์ผ ๋ฌธ์„œ๊ฐ€ ์•„๋‹ ๊ฒฝ์šฐ ์˜ˆ์ƒํ•˜๊ธฐ ํž˜๋“  ๊ฒฝ์šฐ๊ฐ€ ์ƒ๊ธธ ์ˆ˜ ์žˆ๋‹ค.
์—ฌ์ „ํžˆ ๋งŽ์€ ์„œ๋ฒ„๋Š” SSR ์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, Next.JS ์™€ ๊ฐ™์€ ๋ชจ๋˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์•„๋‹Œ JSP, Thymeleaf, EJS, Pug(=Jade) ์™€ ๊ฐ™์€ ํ…œํ”Œ๋ฆฟ ์—”์ง„์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ์˜๋„ํ•˜์ง€ ์•Š์€ ์ค‘๋ณต์œผ๋กœ ํŠน์ • script ์˜ onload ์ฝ”๋“œ๊ฐ€ ๋ฌด์‹œ๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

๋”ฐ๋ผ์„œ window ์— load ์ด๋ฒคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ด๋ฒคํŠธ๋ฅผ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ์–ด ์œ„ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

window.addEventListener('load', () => {
  document
    .getElementById('foo')
    .addEventListener('click', () => changeColor('#AEA'));
});

์ค‘๋ณต ๋“ฑ๋ก์— ๋Œ€ํ•œ ๋ฌธ์ œ๋Š” ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์ง€๋งŒ window ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๊ฒƒ์€ ๋™์ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— DOM ์ด ์ด๋ฏธ ์กด์žฌํ•จ์—๋„ ๋‹ค๋ฅธ ๋ชจ๋“  ๋ฆฌ์†Œ์Šค๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋ฏ€๋กœ ๋ถˆํ•„์š”ํ•œ ์ง€์—ฐ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค. load ๋Œ€์‹  DOMContentLoaded ์ด๋ฒคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, โ‘  HTML ๋ฌธ์„œ์˜ ํŒŒ์‹ฑ์ด ์™„๋ฃŒ๋˜๊ณ , โ‘ก ๋ชจ๋“  ์ง€์—ฐ๋œ ์Šคํฌ๋ฆฝํŠธ(<script defer src="...">์™€ <script type="module">)๊ฐ€ ๋‹ค์šด๋กœ๋“œ๋˜๊ณ  ์‹คํ–‰๋  ๋•Œ ํŠธ๋ฆฌ๋˜๋ฏ€๋กœ ๋ชจ๋“  ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

window.addEventListener('DOMContentLoaded', () => {
  document
    .getElementById('foo')
    .addEventListener('click', () => changeColor('#AEA'));
});

์ข€ ๋” ์ž์„ธํžˆ ์„ค๋ช…ํ•˜๋ฉด, ์•„๋ž˜์„œ ์„ค๋ช…ํ•  defer ์†์„ฑ์ด DOMContentLoaded๊ฐ€ ํŠธ๋ฆฌ๊ฑฐ ๋˜๋Š” ๊ฒƒ์„ ๋ง‰๋Š”๋‹ค. ๊ทธ๋ฆฌ๊ณ  JavaScript Module์€ ๊ธฐ๋ณธ์ ์œผ๋กœ defer ์ฒ˜๋ฆฌ ๋œ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— โ‘  HTML ๋ฌธ์„œ์˜ ํŒŒ์‹ฑ์ด ์™„๋ฃŒ๋˜๊ณ , โ‘ก ๋ชจ๋“  ์ง€์—ฐ๋œ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๋‹ค์šด๋กœ๋“œ๋˜๊ณ  ์‹คํ–‰๋˜๋Š” ๊ฒƒ์ด๋‹ค.


3 ) defer

ํ•˜์ง€๋งŒ, DOMContentLoaded ์—ญ์‹œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ ํ•ญ์ƒ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๋ฅผ ๋“ฑ๋กํ•ด์•ผํ•˜๋ฏ€๋กœ ์ฝ”๋“œ ์ž‘์„ฑ์˜ ํ๋ฆ„์ด๋‚˜ ๊ฐ€๋…์„ฑ์— ์˜ํ–ฅ์„ ์ค€๋‹ค. ๋˜ํ•œ, ํ•ด๋‹น ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๋Š” ์ตœ์ดˆ ๋กœ๋”ฉ ํ›„ ๋”์ด์ƒ ํŠธ๋ฆฌ๊ฑฐ ๋  ์ผ์€ ์—†์ง€๋งŒ, ์ข€ ๋” ์ตœ์ ํ™”๋ฅผ ํ•˜๊ณ ์ž ํ•œ๋‹ค๋ฉด ์ด๋ฒคํŠธ๋ฅผ ์ œ๊ฑฐํ•˜๋Š” ์ž‘์—…๊นŒ์ง€ ํฌํ•จ๋˜์–ด์•ผ ํ•œ๋‹ค. ๊ฒฐ๋ก ์ ์œผ๋กœ, ์ฝ”๋“œ๋ฅผ ๋ถ„๋ฆฌํ•ด ๊ด€๋ฆฌ ํ•  ์ˆ˜ ์žˆ๋Š” ์žฅ์ ์€ ์กด์žฌํ•˜์ง€๋งŒ, ์ตœ์ ํ™”์— ์ถ”๊ฐ€์ ์ธ ๋…ธ๋ ฅ ํ•„์š”, ์ฝ”๋“œ๋Ÿ‰ ์ฆ๊ฐ€, ๊ฐ€๋…์„ฑ ํ•˜๋ฝ ๋“ฑ์˜ ๋ฌธ์ œ๊ฐ€ ์žˆ์–ด
body ํƒœ๊ทธ๊ฐ€ ๋‹ซํžŒ ๋‹ค์Œ ๋˜๋Š” body ํƒœ๊ทธ๊ฐ€ ๋‹ซํžˆ๊ธฐ ์ง์ „ ์— script ๋ฅผ ๋„ฃ๋Š” ๊ฒƒ์ด ์˜คํžˆ๋ ค ๊ฐ€์žฅ ์ตœ์ ํ™”๋œ ๊ฐ€์žฅ ์ข‹์€ ์ฝ”๋“œ๊ฐ€ ๋œ๋‹ค.

์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด script ํƒœ๊ทธ์— defer๋ผ๋Š” ์†์„ฑ์ด ์ถ”๊ฐ€๋˜์—ˆ๋‹ค. ๋‹จ์ง€ defer ์†์„ฑ์„ ๋ช…์‹œํ•˜๋Š” ๊ฒƒ ๋งŒ์œผ๋กœ ๊ธฐ์กด๊ณผ ๋™์ผํ•˜๊ฒŒ HTTP Request ๋ฅผ ๋ณด๋‚ด ์™ธ๋ถ€ ๋ฆฌ์†Œ์Šค๋ฅผ ์š”์ฒญํ•˜์ง€๋งŒ, HTML ๋ฌธ์„œ์˜ ํŒŒ์‹ฑ ์™„๋ฃŒ ๋ฐ ๋ชจ๋“  ์ง€์—ฐ๋œ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๋‹ค์šด๋กœ๋“œ๋˜๊ณ  execute ํ•œ๋‹ค.

Async and Defer

default async defer module module async
fetch ๋กœ ์ธํ•œ ํŒŒ์‹ฑ ์ค‘๋‹จ O X X X X
execute ๋กœ ์ธํ•œ ํŒŒ์‹ฑ ์ค‘๋‹จ O O X X O
script ์ˆœ์„œ ๋ณด์žฅ O X O O X
modulation X X X O O

๋ฌผ๋ก , ๋‹ค๋ฅธ ์Šคํฌ๋ฆฝํŠธ๋‚˜ DOM ์— ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š๋Š” ๊ฒฝ์šฐ async ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋‚˜ ์ผ๋ฐ˜์ ์œผ๋กœ, fetch, execute ๋กœ ์ธํ•œ HTML ์˜ ํŒŒ์‹ฑ์€ ์ค‘๋‹จ๋˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ข‹๊ณ , ์Šคํฌ๋ฆฝํŠธ์˜ ์ˆœ์„œ๋Š” ํ•„์š”์‹œ ๋ณด์žฅ๋˜์–ด์•ผํ•˜๋ฉฐ, ๋ชจ๋“ˆ ํƒ€์ž…์œผ๋กœ ๋‹ค๋ฃจ๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

๋”ฐ๋ผ์„œ ์˜ค๋Š˜๋‚  ๊ฐ€์žฅ ์ ํ•ฉํ•œ ๋ฐฉ์‹์€ defer ๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, module ํƒ€์ž…์„ ์‚ฌ์šฉํ•˜๊ณ  ํ•„์š”์‹œ async ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ์ข‹๋‹ค๊ณ  ํ•  ์ˆ˜ ์žˆ๋‹ค.

module๋กœ ์„ค์ •ํ•˜๊ฒŒ๋˜๋ฉด, ์ด๊ฒƒ์€ script ๊ฐ€ JavaScript Module ๋กœ ์ฒ˜๋ฆฌํ•˜๋„๋ก ๊ฐ„์ฃผ๋˜๋ฉฐ, ์ฝ”๋“œ์˜ ์‹คํ–‰์ด deferred ๋œ๋‹ค. ๋”ฐ๋ผ์„œ ๋”์ด์ƒ charset๊ณผ defer ์†์„ฑ์€ ์˜๋ฏธ๊ฐ€ ์—†์–ด ์ž‘์„ฑํ•  ํ•„์š”๊ฐ€ ์—†์œผ๋ฉฐ, ํด๋ž˜์‹ํ•œ ๋ฐฉ์‹์˜ text/javascript์™€ ๋‹ฌ๋ฆฌ Cross-Origin Fetching ์— CORS ํ”„๋กœํ† ์ฝœ์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

3. import

๋ชจ๋“ˆ๋กœ ๋‹ค๋ฃฐ ๊ฒฝ์šฐ ์ง์ ‘ ํ˜ธ์ŠคํŒ… ์ค‘์ธ ์„œ๋ฒ„์˜ ์Šคํฌ๋ฆฝํŠธ ๋ฟ ์•„๋‹ˆ๋ผ ์ผ๋ถ€ CDN ์˜ ๊ฒฝ์šฐ ๋ฆฌ์†Œ์Šค๋ฅผ ๋ชจ๋“ˆ๋กœ ์ œ๊ณตํ•ด import๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค.

import Swiper from "https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.mjs"

const swiper = new Swiper(...)

๋‹จ, ์ง์ ‘ ํ˜ธ์ŠคํŒ… ํ•˜๋Š” ์„œ๋ฒ„์—์„œ ์ œ๊ณตํ•˜๋Š” ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์•„๋‹ ๊ฒฝ์šฐ, ์œ„์™€ ๊ฐ™์€ import๋ฅผ ์‚ฌ์šฉํ•œ ์š”์ฒญ์ด ๋ชจ๋“  CDN ์— ์ ์šฉ ๊ฐ€๋Šฅํ•œ ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค. ์ฐธ๊ณ ๋กœ ์œ„์™€ ๊ฐ™์ด import๋กœ ์š”์ฒญํ•œ ๋ชจ๋“ˆ ์Šคํฌ๋ฆฝํŠธ๊นŒ์ง€ ๋‹ค์šด๋กœ๋“œ๊ฐ€ ์™„๋ฃŒ๋˜๊ธฐ ์ „๊นŒ์ง€ ๋ชจ๋“  ๋ชจ๋“ˆ ์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ์˜ ์‹คํ–‰์ด ์ง€์—ฐ๋œ๋‹ค. ๋”ฐ๋ผ์„œ ํ•ด๋‹น ์Šคํฌ๋ฆฝํŠธ ๋‚ด import๋ณด๋‹ค ์œ„์— ์ž‘์„ฑ๋œ ์ฝ”๋“œ๋ผ ํ•˜๋”๋ผ๋„ ๋ชจ๋“ˆ ์Šคํฌ๋ฆฝํŠธ์˜ ์ฝ”๋“œ๊ฐ€ ๋‹ค์šด๋กœ๋“œ ์™„๋ฃŒ๋˜๊ธฐ ์ „๊นŒ์ง€ execute ๋˜์ง€ ์•Š์•„ ์ฝ”๋“œ์˜ ์‹คํ–‰์ด ์ง€์—ฐ๋˜๋ฏ€๋กœ ๋ณ„๋„์˜ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ ์—†์ด const swiper = new Swiper(...)์™€ ๊ฐ™์ด ๋™๊ธฐ์ ์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด ๋œ๋‹ค.

4. Webpack & JavaScript

์š”์ฆ˜ ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์€ ๋Œ€๋ถ€๋ถ„ ์ด ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•œ๋‹ค. ๋ชจ๋“ˆ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‚ฌ์šฉํ•ด import๋กœ ํ•„์š”ํ•œ ๋ชจ๋“ˆ ์ „์ฒด ๋˜๋Š” ๋ฉ”์„œ๋“œ๋งŒ ๊ฐ€์ ธ์˜ค๊ณ , Webpack ์ด Tree Shaking ๋“ฑ์„ ํ†ตํ•ด ์ตœ์ ํ™” ํ•œ๋‹ค.

// Import Swiper React components
import { Swiper, SwiperSlide } from 'swiper/react';

// Import Swiper styles
import 'swiper/css';

5. Dynamic import

์š”์ฆ˜ ํ”„๋ก ํŠธ์—”๋“œ๋Š” ์ฝ”๋“œ๋ฅผ chunk ๋‹จ์œ„๋กœ ๋‚˜๋ˆ„๊ณ  dynamic import ๋ฅผ ์‚ฌ์šฉํ•ด ํ•œ ๋ฒˆ์— ๋ชจ๋“  ๋ฆฌ์†Œ์Šค๋ฅผ ์š”์ฒญํ•˜์ง€ ์•Š๋„๋ก ํ•ด ํŽ˜์ด์ง€์˜ ์ดˆ๊ธฐ ๋กœ๋”ฉ ์†๋„๋ฅผ ๋น ๋ฅด๊ฒŒ ๊ฐœ์„ ํ•˜๊ณ , ํ•„์š”ํ•œ ์‹œ์ ์— ํ•„์š”ํ•œ ๋ฆฌ์†Œ์Šค๋งŒ ์š”์ฒญํ•˜๋„๋ก ์ตœ์ ํ™”๋ฅผ ํ•˜๊ณ  ์žˆ๋‹ค. import๋Š” ์ฝ”๋“œ ์ค‘๊ฐ„์—์„œ๋„ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋ฉฐ, ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ•จ์ˆ˜๋กœ ๋งŒ๋“ค๋ฉด ์‰ฝ๊ฒŒ dynamic import ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.

export const loadScript = async (src) => import(src);
const Swiper = await loadScript(
    'https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.mjs'
);

const swiper = new Swiper(...)


ํ•˜์ง€๋งŒ Webpack ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ฑฐ๋‚˜ CDN ๊ฐ™์€ ๊ณณ์— ์š”์ฒญํ•ด์•ผ ํ•  ๊ฒฝ์šฐ, import ์‚ฌ์šฉ์ด ๋ถˆ๊ฐ€๋Šฅํ•œ ํ™˜๊ฒฝ๋„ ๊ณ ๋ ค์• ํ–ํ•œ๋‹ค. CSS Dynamic import ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๊ฐ„๋‹จํ•˜๊ฒŒ HTML ์— script ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

export const loadScript = (src) => {
  const script = document.createElement('script');
  script.src = src;
  script.type = 'module';
  script.async = false;
  document.head.appendChild(script);
};

๋‹จ, CSS ์™€ ๋‹ฌ๋ฆฌ JavaScript ๋Š” ์Šคํฌ๋ฆฝํŠธ์˜ ๋‹ค์šด๋กœ๋“œ๊ฐ€ ์™„๋ฃŒ๋œ ์‹œ์ ์„ ํ™•์ธํ•ด์•ผ ํ•  ํ•„์š”๊ฐ€ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ, ๋‹ค์šด๋กœ๋“œ ์™„๋ฃŒ ์—ฌ๋ถ€๋ฅผ ํ™•์ธ ํ›„ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์‹คํ–‰ํ•˜๋„๋ก ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ํ•  ํ•„์š”๊ฐ€ ์žˆ๋Š”๋ฐ, Promise์™€ Event Listener๋ฅผ ์ ์ ˆํžˆ ์‚ฌ์šฉํ•ด ์œ„ ํ•จ์ˆ˜๋ฅผ ๊ฐœ์„ ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.

๋‹ค์Œ์€ ์˜ˆ์ „์— ํ† ์ด ํ”„๋กœ์ ํŠธ๋ฅผ ํ•˜๋ฉด์„œ ์™ธ๋ถ€ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ํŠน์ • ํŽ˜์ด์ง€ ๋ฐฉ๋ฌธ์‹œ Dynamic import ๋ฅผ ์ ์šฉํ•˜๋˜, ์Šคํฌ๋ฆฝํŠธ ์ค‘๋ณต ์š”์ฒญ์„ ๋ฐฉ์ง€ํ•˜๊ณ , ์Šคํฌ๋ฆฝํŠธ์˜ ๋กœ๋“œ ์™„๋ฃŒ ์—ฌ๋ถ€๋ฅผ ํ™•์ธ ํ›„ ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ์ž‘๋™์‹œํ‚ค๊ธฐ ์œ„ํ•ด ๋งŒ๋“ค์—ˆ๋˜ ์ฝ”๋“œ๋ฅผ ์ผ๋ถ€๋‹ค.

export const loadScript = ({ src, mode = "none" }) =>
  new Promise((resolve, reject) => {
    try {
      const previous = document.querySelector(`script[src="${src}"]`);
      if (previous === null) {
        const script = document.createElement("script");
        script.src = src;
        setScriptMode(mode)(script);

        script.addEventListener("load", () =>
          resolve({
            load: true,
            removeScript: () => document.head.removeChild(script),
          })
        );
        script.addEventListener("error", () =>
          reject({
            load: false,
            message: `Fail to load external script from ${src}`,
          })
        );

        document.head.appendChild(script);
      } else {
        resolve({
          load: true,
          removeScript: () => document.head.removeChild(previous),
        });
      }
    } catch (e) {
      throw new Error(`Fail to load external script`);
    }
  });

const setScriptMode = (mode) => {
  switch (mode) {
    case "async":
      return (script) => (script.async = true);
    case "defer":
      return (script) => (script.defer = true);
    default:
      return () => undefined;
  }
};
const kakaoMapApiBaseUrl =
    "//dapi.kakao.com/v2/maps/sdk.js?autoload=false&appkey=";
const kakaoMapApiScriptSrc = kakaoMapApiBaseUrl + kakaoMapApiKey;

const [loadKakaoMapScript, setLoadKakaoMapScript] = useState(null);

useEffect(() => {
  let removeScript;
  const loadKakaoMapApi = async () => {
    try {
      const result = await loadScript({
        src: kakaoMapApiScriptSrc,
      });
      setLoadKakaoMapScript(result.load);
      if (result.load) {
        removeScript = result.removeScript;
      }
    } catch (e) {
      console.error(`You cannot use kakao map, error: ${e}`);
    }
  };
  loadKakaoMapApi();

  // return () => removeScript && removeScript();
}, []);

removeScript๋Š” HTML ๋‚ด์— ํ•ด๋‹น ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  ์‹ถ์„ ๋•Œ๋งŒ ์‚ฌ์šฉํ•œ๋‹ค. ์ œ๊ฑฐ์‹œ ๋‹ค์‹œ ํ•„์š”ํ•˜๋ฉด ๋‹ค์‹œ ๋กœ๋”ฉํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ผ์€ ๋งŽ์ง€ ์•Š์„ ๊ฒƒ์ด๋‹ค.




Reference

  1. โ€œimport.โ€ MDN Web Docs. Feb. 20, 2024, MDN - import.