Frontend/javaScript

μžλ°”μŠ€ν¬λ¦½νŠΈ λͺ¨λ“ˆ νŒ¨ν„΄ (IIFE, Closure)

dddzr 2026. 4. 10. 11:05

πŸ“Œ μžλ°”μŠ€ν¬λ¦½νŠΈ λͺ¨λ“ˆ νŒ¨ν„΄μ„ ν™œμš©ν•œ μ•ˆμ „ν•œ 데이터 관리 μ „λž΅

βœ… 1. κ°œμš”: μ „μ—­ λ³€μˆ˜ μ„ μ–Έμ˜ ꡬ쑰적 취약점

μžλ°”μŠ€ν¬λ¦½νŠΈμ—μ„œ var ν‚€μ›Œλ“œλ₯Ό μ‚¬μš©ν•΄ 파일 μ΅œμƒλ‹¨μ— λ³€μˆ˜λ₯Ό μ„ μ–Έν•˜λŠ” 방식(Global Scope)은 데이터 λ…ΈμΆœκ³Ό μ˜€μ—Όμ— 맀우 μ·¨μ•½ν•˜λ‹€. 쑰회용 데이터라 할지라도 λΈŒλΌμš°μ € μ½˜μ†”μ„ 톡해 λˆ„κ΅¬λ‚˜ μ ‘κ·Όν•˜κ³  μˆ˜μ •ν•  수 μžˆλ‹€λ©΄, λŸ°νƒ€μž„ ν™˜κ²½μ—μ„œ 데이터 무결성을 보μž₯ν•  수 μ—†λ‹€. λ³Έ κΈ€μ—μ„œλŠ” μ¦‰μ‹œ μ‹€ν–‰ ν•¨μˆ˜(IIFE)와 ν΄λ‘œμ €(Closure)λ₯Ό ν™œμš©ν•˜μ—¬ 데이터λ₯Ό μΊ‘μŠν™”ν•˜λŠ” λͺ¨λ“ˆ νŒ¨ν„΄μ— λŒ€ν•΄ κΈ°μˆ ν•œλ‹€.


βœ… 2. κ°œλ… 및 νŒ¨ν„΄ κΈ°λ³Έ ꡬ쑰

βœ… 2-1. κ°œλ…

  • μ¦‰μ‹œ μ‹€ν–‰ ν•¨μˆ˜(IIFE): ν•¨μˆ˜ μ •μ˜μ™€ λ™μ‹œμ— μ‹€ν–‰λ˜λŠ” νŒ¨ν„΄μœΌλ‘œ, λ‚΄λΆ€ λ³€μˆ˜κ°€ μ „μ—­ μŠ€μ½”ν”„μ— μΉ¨λ²”ν•˜λŠ” 것을 λ°©μ§€ν•œλ‹€.
  • ν΄λ‘œμ €(Closure): ν•¨μˆ˜κ°€ 선언될 λ‹Ήμ‹œμ˜ λ ‰μ‹œμ»¬ ν™˜κ²½μ„ κΈ°μ–΅ν•˜μ—¬, μ™ΈλΆ€ ν•¨μˆ˜κ°€ μ’…λ£Œλœ 후에도 λ‚΄λΆ€ λ³€μˆ˜μ— μ ‘κ·Όν•  수 μžˆλŠ” λ©”μ»€λ‹ˆμ¦˜μ΄λ‹€.
  • λͺ¨λ“ˆ νŒ¨ν„΄(Module Pattern): ν΄λ‘œμ €λ₯Ό μ΄μš©ν•˜μ—¬ μ™ΈλΆ€μ—μ„œ μ ‘κ·Ό λΆˆκ°€λŠ₯ν•œ Private λ³€μˆ˜λ₯Ό μƒμ„±ν•˜κ³ , ν•„μš”ν•œ κΈ°λŠ₯만 Public λ©”μ„œλ“œλ‘œ λ…ΈμΆœν•˜λŠ” 섀계 방식이닀.

βœ… 2-2. κΈ°λ³Έ ꡬ쑰

var ModuleName = (function() {
    // Private: μ™ΈλΆ€μ—μ„œ 직접 μ ‘κ·Ό λΆˆκ°€
    var _privateData = []; 

    // Public: μ™ΈλΆ€λ‘œ λ…ΈμΆœλ˜λŠ” μΈν„°νŽ˜μ΄μŠ€
    return {
        setData: function(data) {
            _privateData = data;
        },
        getData: function() {
            return _privateData;
        }
    };
})();

βœ… 3. ꡬ쑰적 κ°œμ„  비ꡐ (Before vs After)

βœ… 3-1. [Before] μ „μ—­ λ³€μˆ˜ 방식 (Global Variable)

데이터가 μ „μ—­ μŠ€μ½”ν”„μ— λ…ΈμΆœλ˜μ–΄ λ³΄μ•ˆκ³Ό 관리 μΈ‘λ©΄μ—μ„œ μœ„ν—˜ν•˜λ‹€.

// 각 JS 파일 μ΅œμƒλ‹¨ μ„ μ–Έ
var factoryListRowData = []; 

// μœ„ν—˜μ„± μ˜ˆμ‹œ: μ½˜μ†”μ°½μ—μ„œ μ¦‰μ‹œ λ³€μ‘° κ°€λŠ₯
// > factoryListRowData = "malicious data"; (κΈ°μ‘΄ 데이터 파괴)

βœ… 3-2. [After] λͺ¨λ“ˆ νŒ¨ν„΄ 적용 (Encapsulated)

데이터λ₯Ό ν•¨μˆ˜ μŠ€μ½”ν”„ μ•ˆμ— 가두어 μ™ΈλΆ€μ˜ 직접적인 접근을 μ°¨λ‹¨ν•œλ‹€.

var FactoryModule = (function() {
    var _factoryListRowData = []; // 금고 λ‚΄λΆ€

    return {
        setList: function(list) {
            _factoryListRowData = list;
        },
        getRow: function(id) {
            return _factoryListRowData.find(function(item) {
                return item.portlet_id === id;
            });
        }
    };
})();

// 호좜 μ‹œ: FactoryModule.getRow('ID01');
// μ‘°μž‘ μ‹œλ„: FactoryModule._factoryListRowData; // undefined (μ ‘κ·Ό λΆˆκ°€)

βœ… 4. 데이터 κ²€λ¬Έμ†Œ 역할을 ν†΅ν•œ μ•ˆμ •μ„± 확보

λ‹¨μˆœνžˆ λ³€μˆ˜λ₯Ό μˆ¨κΈ°λŠ” 것을 λ„˜μ–΄, λͺ¨λ“ˆ νŒ¨ν„΄μ€ λ°μ΄ν„°μ˜ μž…κ΅¬(Setter)에 'κ²€λ¬Έμ†Œ'λ₯Ό μ„Έμ›Œ 데이터 무결성을 확보할 수 μžˆλ‹€.

βœ…4-1. μˆ˜μ • μ˜λ„μ˜ λͺ…ν™•ν•œ μ œν•œ (Validation)

데이터 μž…λ ₯ μ‹œ μœ νš¨μ„± 검사 λ‘œμ§μ„ ν¬ν•¨ν•˜μ—¬ 잘λͺ»λœ 데이터가 μœ μž…λ˜λŠ” 것을 λ°©μ§€ν•œλ‹€.

setList: function(list) {
    if (!Array.isArray(list)) {
        console.error("λ°°μ—΄ ν˜•μ‹μ΄ μ•„λ‹™λ‹ˆλ‹€.");
        return;
    }
    _factoryListRowData = list;
}

βœ…4-2. 읽기 μ „μš© μƒνƒœ κ΅¬ν˜„ (Read-Only)

졜초 1회만 데이터 μ €μž₯을 ν—ˆμš©ν•˜μ—¬ λŸ°νƒ€μž„ 쀑 μ˜λ„μΉ˜ μ•Šμ€ μˆ˜μ •μ„ μ›μ²œ μ°¨λ‹¨ν•œλ‹€.

var isInitialized = false;

return {
    setList: function(list) {
        if (isInitialized) {
            console.warn("이미 μ΄ˆκΈ°ν™”λœ λ°μ΄ν„°λŠ” μˆ˜μ •ν•  수 μ—†μŠ΅λ‹ˆλ‹€.");
            return; 
        }
        _factoryListRowData = list;
        isInitialized = true;
    }
};

βœ… 4-3. λ””λ²„κΉ…μ˜ νš¨μœ¨μ„± μ¦λŒ€ (Traceability)

데이터 변경이 μΌμ–΄λ‚˜λŠ” μ‹œμ μ— λ‘œκΉ…μ΄λ‚˜ μŠ€νƒ 좔적을 μˆ˜ν–‰ν•˜μ—¬ 였λ₯˜ λ°œμƒ 원인을 λΉ λ₯΄κ²Œ νŒŒμ•…ν•  수 μžˆλ‹€.

setList: function(list) {
    console.trace("데이터 λ³€κ²½ 좔적:"); // 호좜 μŠ€νƒ 좜λ ₯
    _factoryListRowData = list;
}

βœ… 5. κ²°λ‘ : λͺ¨λ“ˆ νŒ¨ν„΄μ˜ ν•„μš”μ„±

λͺ¨λ“ˆ νŒ¨ν„΄μ€ κ΄€μ‹¬μ‚¬μ˜ 뢄리(SoC)λ₯Ό μ‹€ν˜„ν•˜κ³ , λ°μ΄ν„°μ˜ 무결성을 보μž₯ν•˜λ©°, 예츑 κ°€λŠ₯ν•œ μ½”λ“œλ₯Ό μž‘μ„±ν•˜κ²Œ ν•œλ‹€. μ΄λŠ” λŒ€κ·œλͺ¨ ν”„λ‘œμ νŠΈμ—μ„œ μ „μ—­ μ˜€μ—Όμ„ λ°©μ§€ν•˜κ³  λ³΄μ•ˆ μ‚¬κ³ μ˜ μ‹€λ―Έλ₯Ό μ°¨λ‹¨ν•˜λŠ” κ°€μž₯ 기본적인 방어적 ν”„λ‘œκ·Έλž˜λ°μ˜ μ‹œμž‘μ΄λ‹€!!