Webpack 러닝 가이드
  • Webpack 모듈 번들러
  • Web Modules
    • Legacy 모듈 관리
    • ES 모듈 관리
  • Babel
    • Babel JavaScript 컴파일러
    • Babel 독립형 빌드
    • Babel 노드 (Node.js) ✘
    • Babel CLI 구성
    • Babel 플러그인
  • webpack
    • Webpack 개발 환경 구성
      • Webpack 설치
      • Webpack 통합
      • Webpack 구성 파일
      • Webpack 워치
      • Webpack 모드
      • Webpack 별칭 등록
      • Webpack 호환성
      • Webpack 개발 서버
      • Webpack 멀티 페이지 설정
    • Webpack 로더
      • File 로더
      • CSS 로더
      • PostCSS 로더
      • Sass 로더
      • Babel 로더
      • TypeScript 로더
    • Webpack 플러그인
      • 환경 변수 등록
      • 빌드 결과 자동 정리
      • 빌드 결과 자동 주입
      • CSS 파일 개별 추출
      • CSS 파일 크기 최적화
      • CSS 미디어쿼리 스플리팅
      • 이미지 파일 크기 최적화
  • React
    • React App 매뉴얼 구성
      • Webpack 초기 구성
      • Babel 컴파일러 구성
      • CSS 스타일 구성
      • 이미지 구성
      • 환경변수 플러그인
      • HTML 플러그인
      • 최적화 구성
      • 코드 스플리팅
      • 개발 서버
      • 이미지, 폰트 에셋 구성
      • 환경 변수 + HTML 구성
      • 빌드 최적화 구성
      • 빌드 자동 정리 구성
      • 개발 서버 구성
      • 폴리필 구성
Powered by GitBook
On this page
  • 웹 브라우저 환경에서의 모듈
  • 참고

Was this helpful?

  1. Web Modules

Legacy 모듈 관리

PreviousWebpack 모듈 번들러NextES 모듈 관리

Last updated 4 years ago

Was this helpful?

웹 브라우저 환경에서의 모듈

웹 애플리케이션 개발에서 모듈 관리는 매우 중요함에도, 관리가 쉽지 않습니다. 관리가 쉽지 않음을 체험하기 위해 간단한 실습을 진행해봅니다. 실습 파일 트리 구조는 다음과 같습니다.

.
├── src/
│   ├── modules/
│   │   ├── Euid/
│   │   │   ├── utils.js
│   │   │   ├── logger.js
│   │   │   └── tester.js
│   │   └── DOM.js
│   └── index.js
└── index.html

실습할 모듈 코드는 다음과 같습니다. 각 탭의 코드를 복사하여 "디렉토리 구조" 대로 구성 해봅니다.

// 모듈 주입(injection)
(function main(global, Euid, DOM) {
  'use strict';

  /* -------------------------------------------------------------------------- */

  // Euid 모듈 멤버 추출
  var logger = Euid.logger;
  var tester = Euid.tester;
  var utils = Euid.utils;

  // logger 모듈 멤버 추출
  var success = logger.success;
  var error = logger.error;

  // tester 모듈 멤버 추출
  var test = tester.test;
  var expect = tester.expect;

  // utils 모듈 멤버 추출
  var isFunction = utils.isFunction;

  /* -------------------------------------------------------------------------- */

  // DOM 모듈 추출
  var getNode = DOM.getNode;
  var createElement = DOM.createElement;
  var render = DOM.render;

  /* -------------------------------------------------------------------------- */

  // 유효성 검사 조건 변수
  var isValid = isFunction(getNode);

  // 타이머 설정
  global.setTimeout(function () {
    console.group('MODULE → 모듈 관리 상태');
    isValid
      ? success('의존성 모듈 관리에 문제가 없어 앱이 정상 작동합니다.')
      : error('의존성 모듈 관리에 문제가 있어 앱이 정상 작동하지 않습니다.');
  });

  /* -------------------------------------------------------------------------- */
  // 테스트

  test('createElement() 전달 속성', function () {
    const vNode = createElement('h3', { className: 'heading-3' }, 'TDD');

    expect(vNode.type).toBe('h3');
    expect(vNode.props.children).toBe('tdd');
  });

  /* -------------------------------------------------------------------------- */
  // vNode 생성

  var moduleLink = createElement(
    'a',
    {
      href: 'https://bit.ly/3brDMBS',
      rel: 'noopener noreferrer',
      target: '_blank',
      className: 'externalLink',
    },
    '모듈'
  );

  var cube = createElement('img', {
    className: 'cube',
    alt: '',
    src: './src/assets/cube.gif',
    height: 32,
  });

  var headline = createElement(
    'h1',
    { className: 'headline' },
    moduleLink,
    ' 관리',
    cube
  );

  var slogan = createElement(
    'p',
    { className: 'slogan' },
    '웹 브라우저 환경에서의 모듈 관리는 까다롭습니다.'
  );

  var container = createElement(
    'div',
    { className: 'container' },
    headline,
    slogan
  );

  /* -------------------------------------------------------------------------- */
  // 렌더링

  render(container, getNode('#root'));
})(window, window.Euid, window.DOM);
(function logger(Euid) {
  'use strict';

  /* -------------------------------------------------------------------------- */
  // 메시지 스타일

  var MESSAGE_STYLES = {
    log: '\
      color: #1c1c1d;\
      font-weight: bold;\
    ',
    success: '\
      color: #00c712;\
      font-weight: bold;\
    ',
    info: '\
      color: #006afc;\
      font-weight: bold;\
    ',
    warn: '\
      color: #ff9500;\
      font-weight: bold;\
    ',
    error: '\
      color: #ee3327;\
      font-weight: bold;\
    ',
  };

  /* -------------------------------------------------------------------------- */
  // 메시지 유틸리티

  function log(message, messageStyle) {
    console.log('%c' + message, messageStyle || MESSAGE_STYLES.log);
  }

  function info(message) {
    return log('🔵 ' + message, MESSAGE_STYLES.info);
  }

  function success(message) {
    return log('🟢 ' + message, MESSAGE_STYLES.success);
  }

  function warn(message) {
    return log('🟠 ' + message, MESSAGE_STYLES.warn);
  }

  function error(message) {
    return log('🔴 ' + message, MESSAGE_STYLES.error);
  }

  /* -------------------------------------------------------------------------- */
  // 모듈 내보내기

  Euid.logger = {
    log,
    warn,
    error,
    success,
  };
})(window.Euid = window.Euid || {});
(function utils(Euid) {
  'use strict';

  /* -------------------------------------------------------------------------- */
  // 타입 검사 유틸리티

  var typeIs = function (data) {
    return Object.prototype.toString.call(data).slice(8, -1).toLowerCase();
  };

  var isNumber = function (data) {
    return typeIs(data) === 'number';
  };

  var isString = function (data) {
    return typeIs(data) === 'string';
  };

  var isBoolean = function (data) {
    return typeIs(data) === 'boolean';
  };

  var isFunction = function (data) {
    return typeIs(data) === 'function';
  };

  var isArray = function (data) {
    return typeIs(data) === 'array';
  };

  var isObject = function (data) {
    return typeIs(data) === 'object';
  };

  /* -------------------------------------------------------------------------- */
  // 배열 유틸리티

  var makeArray = function (likeArray) {
    return Array.prototype.slice.call(likeArray);
  };

  /* -------------------------------------------------------------------------- */
  // 시리얼라이즈 유틸리티
  
  var serialize = function(data, prettiy) {
    return !prettiy ? JSON.stringify(data) : JSON.stringify(data, null, 2) 
  }

  var deserialize = function(json) {
    return JSON.parse(json)
  }

  /* -------------------------------------------------------------------------- */
  // 믹스인 유틸리티

  var mixins = function () {
    return makeArray(arguments).reduce(function (o1, o2) {
      for (var key in o2) {
        if (o2.hasOwnProperty(key)) {
          var o1Value = o1[key];
          var o2Value = o2[key];
          if (isObject(o2Value)) {
            o1Value && _checkValueType(isObject, o1Value, key)
            o1[key] = mixins(o1Value || {}, o2Value);
          } 
          else if (isArray(o2Value)) {
            o1Value && _checkValueType(isArray, o1Value, key)
            o1[key] = (o1Value || []).concat(o2Value);
          } 
          else {
            o1[key] = o2Value;
          }
        }
      }
      return o1;
    }, {});
  };

  var _checkValueType = function(method, value, key) {
    if (!method(value)) {
      var message = '혼합할 각 객체 ' + key + ' 속성 유형이 다릅니다.';
      if (Euid.logger) {
        Euid.logger.error(message)
      } else {
        throw new Error(message);
      }
    }
  }

  /* -------------------------------------------------------------------------- */
  // 모듈 내보내기

  Euid.utils = {
    typeIs: typeIs,
    isNumber: isNumber,
    isString: isString,
    isBoolean: isBoolean,
    isFunction: isFunction,
    isArray: isArray,
    isObject: isObject,
    makeArray: makeArray,
    serialize: serialize,
    deserialize: deserialize,
    mixins: mixins,
  };
})(window.Euid = window.Euid || {});
(function tester(Euid) {
  'use strict';

  // Euid 모듈 멤버 추출
  var logger = Euid.logger;
  var utils = Euid.utils;

  /* -------------------------------------------------------------------------- */

  // 테스트 유틸리티
  var test = function (title, callback) {
    console.group('TEST → ' + title);
    try {
      logger.log('테스트 결과:');
      callback();
    } catch (error) {
      logger.error('테스트 실패: ' + error.message);
    }
    console.groupEnd();
  };

  // 익스펙트 유틸리티
  var expect = function (actual /* 결과 값 */) {
    return {
      toBe: function (expected /* 기대 값 */) {
        if (expected !== actual) {
          logger.error(
            '결과 값(' + utils.serialize(actual) + 
            ')과 기대 값("' + expected + '")이 다릅니다.');
        } else {
          logger.success(
            '결과 값(' + utils.serialize(actual) + 
            ')과 기대 값("' + expected + '")이 같습니다.');
        }
      },
      notToBe: function (expected) {
        // ...
      },
      toBeGreaterThan: function (expected) {
        // ...
      },
      toBeLessThan: function (expected) {
        // ...
      },
    };
  };

  Euid.tester = {
    test,
    expect,
  };
})((window.Euid = window.Euid || {}));
(function DOM(global, Euid) {
  'use strict';

  /* -------------------------------------------------------------------------- */
  // 의존 모듈 검사

  if (!Euid) {
    throw new Error('DOM 모듈이 정상 작동하려면 Euid 모듈이 필요합니다.');
  }

  /* -------------------------------------------------------------------------- */
  
  // Euid 모듈 멤버 추출
  var utils = Euid.utils;

  //  utils 멤버 추출
  var isString = utils.isString;
  var isFunction = utils.isFunction;
  var makeArray = utils.makeArray;
  var mixins = utils.mixins;

  /* -------------------------------------------------------------------------- */
  // 폴리필(Polyfill)

  if (!Object.entries) {
    Object.entries = function (obj) {
      // Object.keys() IE 9+
      var ownProps = Object.keys(obj);
      var i = ownProps.length;
      var resArray = new Array(i);
      while (i--) { 
        resArray[i] = [ownProps[i], obj[ownProps[i]]]
      }
      return resArray;
    };
  }

  /* -------------------------------------------------------------------------- */
  // 유틸리티 함수

  var getById = function (idName) {
    return document.getElementById(idName);
  };

  var getNode = function (selector, context) {
    return (context || document).querySelector(selector);
  };

  var getNodeList = function (selector, context) {
    return (context || document).querySelectorAll(selector);
  };

  /* -------------------------------------------------------------------------- */

  // vNode 생성 유틸리티
  var createElement = function() {

    // arguments → args 배열 변경
    var args = makeArray(arguments)
    var type = args[0]
    var props = args[1] || {}
    var children = args.slice(2) // 나머지 인자 집합(배열)

    props.children = children

    // type이 함수 컴포넌트인 경우
    if (isFunction(type)) {
      // 함수 호출 (props 전달)
      return type.call(null, props)
    }

    return {
      type: type, 
      props: props
    }

  }

  // [비공개] 속성 바인딩 유틸리티
  var _bindProps = function(element, props) {
    // props 복제
    var props = mixins(props)

    // children 속성 제거
    delete props.children

    var propValues = Object.entries(props)
    propValues.forEach(function(propValue) {
      var prop = propValue[0]
      var value = propValue[1]
      
      // 클래스 속성 설정
      if (prop === 'className') {
        element.classList.add(value)
      }

      // 이벤트 속성
      var isEventProp = /^on/.test(prop)
      var propIsClassName = prop !== 'className'

      if (isEventProp && propIsClassName) {
        element.addEventListener(prop.replace(/on/, '').toLowerCase(), value)
      }

      // 나머지 속성
      if (!isEventProp && propIsClassName) {
        element.setAttribute(prop, value)
      }
    })
  }

  // [비공개] vNode 렌더링 유틸리티
  var _renderElement = function(vNode) {

    // vNode가 텍스트인 경우
    if (isString(vNode)) {
      return document.createTextNode(vNode)
    }

    // vNode = {type, props}
    // 요소 생성
    var element = document.createElement(vNode.type)

    // 속성 바인딩
    _bindProps(element, vNode.props)

    // 자식(들) 순환
    vNode.props.children
      // 재귀 호출
      .map(_renderElement)
      // 자식 노드 마운트
      .forEach(function(childNode) {
        element.appendChild(childNode)
      })

    // 요소 반환
    return element
  }

  /* -------------------------------------------------------------------------- */

  // vNode → DOM 노드 마운트(mount)
  var render = function(vNode, domNode) {
    domNode.appendChild(_renderElement(vNode))
  }

  /* -------------------------------------------------------------------------- */
  // 모듈 내보내기

  global.DOM = {
    getById,
    getNode,
    getNodeList,
    createElement,
    render,
  };
})(window, window.Euid);

참고

각 모듈 파일에 ES5 코드로 작성되었습니다. ()

IE 9+
ECMAScript 5 compatibility table
Logo
446KB
manage-modules-for-web.zip
archive
모듈 관리 실습 zip (bit.ly/3qGFKVp)