ES 모듈 관리
ES Modules을 활용하면 웹 브라우저 환경에서도 모듈 관리가 수월해집니다. (ES 6+ 코드로 변경)
import * as Euid from './modules/Euid/index.js'
import { getNode, createElement, render } from './modules/DOM.js'
// Euid 멤버에서 하위 모듈 추출
const {
logger: { success, error },
tester: { test, expect },
utils: { isFunction },
} = Euid
/* -------------------------------------------------------------------------- */
// 타이머 설정
window.setTimeout(() => {
console.group('MODULE → 모듈 관리 상태')
isFunction(getNode)
? success('의존성 모듈 관리에 문제가 없어 앱이 정상 작동합니다.')
: error('의존성 모듈 관리에 문제가 있어 앱이 정상 작동하지 않습니다.')
})
/* -------------------------------------------------------------------------- */
// 테스트
test('createElement() 전달 속성', () => {
const vNode = createElement('h3', { className: 'heading-3' }, 'TDD')
expect(vNode.type).toBe('h3')
expect(vNode.props.children).toBe('tdd')
})
/* -------------------------------------------------------------------------- */
// vNode 생성
const moduleLink = createElement(
'a',
{
href: 'https://bit.ly/3brDMBS',
rel: 'noopener noreferrer',
target: '_blank',
className: 'externalLink',
},
'모듈'
)
const cube = createElement('img', {
className: 'cube',
alt: '',
src: './src/assets/cube.gif',
height: 32,
})
const headline = createElement(
'h1',
{ className: 'headline' },
moduleLink,
' 관리',
cube
)
const slogan = createElement(
'p',
{ className: 'slogan' },
'웹 브라우저 환경에서의 모듈 관리는 까다롭습니다.'
)
const container = createElement(
'div',
{ className: 'container' },
headline,
slogan
)
/* -------------------------------------------------------------------------- */
// 렌더링
render(container, getNode('#root'))
export * as logger from './logger.js'
export * as tester from './tester.js'
export * as utils from './utils.js'
/* -------------------------------------------------------------------------- */
// 메시지 스타일
const 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;
`,
}
/* -------------------------------------------------------------------------- */
// 메시지 유틸리티
export function log(message, messageStyle = MESSAGE_STYLES.log) {
console.log(`%c${message}`, messageStyle)
}
export function info(message) {
return log(`🔵 ${message}`, MESSAGE_STYLES.info)
}
export function success(message) {
return log(`🟢 ${message}`, MESSAGE_STYLES.success)
}
export function warn(message) {
return log(`🟠 ${message}`, MESSAGE_STYLES.warn)
}
export function error(message) {
return log(`🔴 ${message}`, MESSAGE_STYLES.error)
}
import { error } from './logger.js'
// JSON 메서드 추출
const { stringify: _serialize, parse: _deserialize } = window.JSON
// 타입 검사 유틸리티
export function typeIs(data) {
return Object.prototype.toString.call(data).slice(8, -1).toLowerCase()
}
export function isNumber(data) {
return typeIs(data) === 'number'
}
export function isString(data) {
return typeIs(data) === 'string'
}
export function isBoolean(data) {
return typeIs(data) === 'boolean'
}
export function isFunction(data) {
return typeIs(data) === 'function'
}
export function isArray(data) {
return typeIs(data) === 'array'
}
export function isObject(data) {
return typeIs(data) === 'object'
}
/* -------------------------------------------------------------------------- */
// 배열 유틸리티
export function makeArray(likeArray) {
return Array.from
? Array.from(likeArray)
: Array.prototype.slice.call(likeArray)
}
/* -------------------------------------------------------------------------- */
// 시리얼라이즈 유틸리티
export const serialize = (data, prettiy) =>
!prettiy ? _serialize(data) : _serialize(data, null, 2)
export const deserialize = (json) => _deserialize(json)
/* -------------------------------------------------------------------------- */
// 믹스인 유틸리티
export function mixins() {
return makeArray(arguments).reduce((o1, o2) => {
for (let key in o2) {
if (o2.hasOwnProperty(key)) {
const o1Value = o1[key]
const 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 ?? []), ...o2Value]
} else {
o1[key] = o2Value
}
}
}
return o1
}, {})
}
const _checkValueType = (method, value, key) => {
if (!method(value)) {
const message = `혼합할 각 객체 ${key} 속성 유형이 다릅니다.`
error(message)
}
}
import { log, success, error } from './logger.js'
import { serialize } from './utils.js'
// 테스트 유틸리티
export const test = (title, callback) => {
console.group(`TEST → ${title}`)
try {
log('테스트 결과:')
callback()
} catch (error) {
error('테스트 실패: ' + error.message)
}
console.groupEnd()
}
// 익스펙트 유틸리티
export const expect = (actual /* 실제 값 */) => ({
toBe: (expected /* 기대 값 */) => {
expected !== actual
? error(
`결과 값(${serialize(actual)})과 기대 값(${expected})이 다릅니다.`
)
: success(
`결과 값(${serialize(actual)})과 기대 값(${expected})이 같습니다.`
)
},
notToBe: (expected) => {
// ...
},
toBeGreaterThan: (expected) => {
// ...
},
toBeLessThan: (expected) => {
// ...
},
})
import { isString, isFunction } from './Euid/utils.js'
/* -------------------------------------------------------------------------- */
// 유틸리티 함수
export function getById(idName) {
return document.getElementById(idName)
}
export function getNode(selector, context = document) {
return context.querySelector(selector)
}
export function getNodeList(selector, context = document) {
return context.querySelectorAll(selector)
}
/* -------------------------------------------------------------------------- */
// vNode 생성 유틸리티
export function createElement(type, props, ...children) {
props = { ...props, children }
// type이 함수 컴포넌트인 경우
if (isFunction(type)) {
// 함수 호출 (props 전달)
return type.call(null, props)
}
return { type, props }
}
// [비공개] 속성 바인딩 유틸리티
const _bindProps = (element, props) => {
// props 복제
props = { ...props }
// children 속성 제거
delete props.children
Object.entries(props).forEach(([prop, value]) => {
// 클래스 속성 설정
if (prop === 'className') {
element.classList.add(value)
}
// 이벤트 속성
const isEventProp = /^on/.test(prop)
const propIsClassName = prop !== 'className'
if (isEventProp && propIsClassName) {
element.addEventListener(prop.replace(/on/, '').toLowerCase(), value)
}
// 나머지 속성
if (!isEventProp && propIsClassName) {
element.setAttribute(prop, value)
}
})
}
// [비공개] vNode 렌더링 유틸리티
const _renderElement = (vNode) => {
// vNode가 텍스트인 경우
if (isString(vNode)) {
return document.createTextNode(vNode)
}
// vNode = {type, props}
// 요소 생성
const element = document.createElement(vNode.type)
// 속성 바인딩
_bindProps(element, vNode.props)
// 자식(들) 순환
vNode.props.children
// 재귀 호출
.map(_renderElement)
// 자식 노드 마운트
.forEach((childNode) => element.appendChild(childNode))
// 요소 반환
return element
}
/* -------------------------------------------------------------------------- */
// vNode → DOM 노드 마운트(mount)
export const render = (vNode, domNode) =>
domNode.appendChild(_renderElement(vNode))
IE 웹 브라우저는 ES Modules를 지원하지 않습니다.
참고
Last updated