๊ด€๋ฆฌ ๋ฉ”๋‰ด

UI Code Lab

ํƒญ ์ „ํ™˜ ์Šคํฌ๋ฆฝํŠธ ๋ชจ๋“ˆํ™” ์‹ค์ „ ์˜ˆ์ œ ๋ณธ๋ฌธ

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธUI

ํƒญ ์ „ํ™˜ ์Šคํฌ๋ฆฝํŠธ ๋ชจ๋“ˆํ™” ์‹ค์ „ ์˜ˆ์ œ

๐Ÿฏ๊ฟ€์ƒ์ด๐Ÿ 2026. 1. 15. 10:47
๋ฐ˜์‘ํ˜•

 

 

์›น ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋‹ค ๋ณด๋ฉด ํƒญ ์ „ํ™˜ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ์ผ์ด ์ž์ฃผ ์ƒ๊น๋‹ˆ๋‹ค.

์ฒ˜์Œ์—๋Š” ๋‹จ์ˆœํžˆ fetch๋กœ HTML์„ ๋ถˆ๋Ÿฌ์™€์„œ innerHTML์— ๋„ฃ๊ณ ,

ํƒญ ํด๋ฆญ ์‹œ ๋ณด์—ฌ์ฃผ๊ธฐ/์ˆจ๊ธฐ๊ธฐ๋งŒ ํ•˜๋ฉด ์ถฉ๋ถ„ํ•ด ๋ณด์ด์ฃ .

 

ํ•˜์ง€๋งŒ ํ”„๋กœ์ ํŠธ๊ฐ€ ์ปค์งˆ์ˆ˜๋ก ์ฝ”๋“œ๊ฐ€ ์ค‘๋ณต๋˜๊ณ ,

์ „์—ญ ๋ณ€์ˆ˜ ์ถฉ๋Œ์ด๋‚˜ ํŽ˜์ด์ง€๋ณ„ ๊ด€๋ฆฌ์˜ ๋ถˆํŽธํ•จ ๊ฐ™์€

๋ฌธ์ œ๊ฐ€ ํ•˜๋‚˜๋‘˜์”ฉ ๋“œ๋Ÿฌ๋‚ฉ๋‹ˆ๋‹ค.

 

์ด๋Ÿฐ ์ƒํ™ฉ์—์„œ ํ•„์š”ํ•œ ๊ฑด

๊ณตํ†ต ๋กœ์ง์„ ๋ชจ๋“ˆํ™”ํ•˜๊ณ ,

๊ฐ ํŽ˜์ด์ง€์—์„œ๋Š” ์ตœ์†Œํ•œ์˜ ์„ค์ •๋งŒ ๋‚จ๊ธฐ๋Š”

๊ตฌ์กฐํ™”์ž…๋‹ˆ๋‹ค.

 

1. ๋ฌธ์ œ ์ƒํ™ฉ

  • ํƒญ ์ „ํ™˜ ๋กœ์ง์„ ํŽ˜์ด์ง€๋งˆ๋‹ค ์ค‘๋ณต ์ž‘์„ฑ → ์œ ์ง€๋ณด์ˆ˜ ์–ด๋ ค์›€
  • ์ „์—ญ ๋ณ€์ˆ˜(cache, loaded)๊ฐ€ ๋‹ค๋ฅธ ์Šคํฌ๋ฆฝํŠธ์™€ ์ถฉ๋Œ ๊ฐ€๋Šฅ์„ฑ
  • ํŽ˜์ด์ง€๋งˆ๋‹ค ๋‹ค๋ฅธ ํŒŒ์ผ๋งต์„ ๊ด€๋ฆฌํ•ด์•ผ ํ•˜๋Š”๋ฐ, ๊ณตํ†ต ๋กœ์ง๊ณผ ์„ž์—ฌ ์žˆ์–ด์„œ ๋ถˆํŽธ

 

2. ํ•ด๊ฒฐ ์ „๋žต

  • ๊ณตํ†ต ๋กœ์ง์€ ํ•˜๋‚˜์˜ ๋ชจ๋“ˆ๋กœ ๋ถ„๋ฆฌ
  • ํŽ˜์ด์ง€๋ณ„๋กœ๋Š” fileMap๋งŒ ๋‚จ๊ฒจ์„œ ๊ด€๋ฆฌ
  • ๋ชจ๋“ˆ์€ init()๊ณผ setFileMap() ๋ฉ”์„œ๋“œ๋งŒ ์™ธ๋ถ€์— ๋…ธ์ถœ → ๊น”๋”ํ•œ ๋„ค์ž„์ŠคํŽ˜์ด์Šค ์œ ์ง€

 

 

3. ์ฝ”๋“œ ์˜ˆ์ œ

๊ณตํ†ต ์Šคํฌ๋ฆฝํŠธ (tabs.js)

 
const MyTabs = (function() {
    let fileMap = {};
    const loaded = {};

    function setFileMap(map) {
        fileMap = map;
    }

    async function init() {
        const links = document.querySelectorAll(".menu-link");
        let firstLink = Array.from(links).find(l => l.dataset.disabled !== "true");
        if (!firstLink) return;

        const key = firstLink.dataset.target;
        const targetEl = document.getElementById(`content-${key}`);

        const response = await fetch(fileMap[key]);
        const html = await response.text();
        targetEl.innerHTML = html;
        loaded[key] = true;
        showContent(key);
        firstLink.setAttribute("aria-current", "page");

        links.forEach(link => {
            link.addEventListener("click", async (e) => {
                e.preventDefault();
                if (link.dataset.disabled === "true") return;

                links.forEach(l => l.removeAttribute("aria-current"));
                link.setAttribute("aria-current", "page");

                const key = link.dataset.target;
                const targetEl = document.getElementById(`content-${key}`);

                if (!loaded[key]) {
                    const response = await fetch(fileMap[key]);
                    const html = await response.text();
                    targetEl.innerHTML = html;
                    loaded[key] = true;
                }
                showContent(key);
            });
        });
    }

    function showContent(key) {
        document.querySelectorAll(".container > div").forEach(div => {
            div.style.display = "none";
        });
        document.getElementById(`content-${key}`).style.display = "block";
    }

    return { init, setFileMap };
})();

 

ํŽ˜์ด์ง€๋ณ„ ์Šคํฌ๋ฆฝํŠธ

<script src="tabs.js"></script>
<script>
    document.addEventListener("DOMContentLoaded", () => {
        MyTabs.setFileMap({
            a: "content-a.html",
            b: "content-b.html"
        });
        MyTabs.init();
    });
</script>

 

 

๋งˆ๋ฌด๋ฆฌํ•˜๋ฉฐ

 

ํƒญ ์ „ํ™˜ ๊ธฐ๋Šฅ์€ ๋‹จ์ˆœํ•ด ๋ณด์ด์ง€๋งŒ,

ํ”„๋กœ์ ํŠธ๊ฐ€ ์ปค์งˆ์ˆ˜๋ก ๊ด€๋ฆฌ ํฌ์ธํŠธ๊ฐ€ ๋Š˜์–ด๋‚˜๊ณ 

์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์–ด๋ ค์›Œ์ง‘๋‹ˆ๋‹ค.

 

๊ณตํ†ต ๋กœ์ง์„ ๋ชจ๋“ˆํ™”ํ•˜๊ณ 

ํŽ˜์ด์ง€๋ณ„๋กœ fileMap๋งŒ ์ •์˜ํ•˜๋Š” ๊ตฌ์กฐ๋ฅผ ์ ์šฉํ•˜๋ฉด

์ฝ”๋“œ ์ค‘๋ณต์„ ์ค„์ด๊ณ 

์ถฉ๋Œ์„ ์˜ˆ๋ฐฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๋ฌด์—‡๋ณด๋‹ค ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ํ›จ์”ฌ ์‰ฌ์›Œ์ง€๊ณ ,

๋‹ค๋ฅธ UI ์ปดํฌ๋„ŒํŠธ์—๋„

๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ํ™•์žฅํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์—์„œ

ํฐ ์žฅ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฐ˜์‘ํ˜•