TOAST UI CodeSnippet


CodeSnippet은 타입 체크나 배열 처리와 같이 자주 사용되는 코드의 모음으로, 외부 라이브러리와의 의존성을 줄이기 위해 만들었다. TOAST UI도 이런 이유로 CodeSnippet을 사용한다. CodeSnippet은 기능에 따라 여러개의 모듈로 분리되어 있어서, CodeSnippet 전체를 사용할 수도 있고, 필요한 모듈만 선택하여 사용할 수 있다. 하지만 TOAST UI 컴포넌트를 사용하고 있다면 반드시 CodeSnippet 전체를 사용하도록 한다.

이 문서는 CodeSnippet 사용이 익숙하지 않은 사용자들이 쉽게 따라 할 수 있게 하는데 목적이 있다. 그러므로 CodeSnippet이 제공하는 모든 기능을 설명하지는 않는다. CodeSnippet이 제공하는 다양한 기능과 관련 API는 아래 문서를 참고하기 바란다.

목차

설치 및 사용하기

설치하기

CondeSnippet은 npm, CDN, bower를 사용하여 설치할 수 있다.

npm으로 설치하기

npm으로 설치할 경우 아래와 같은 커맨드 라인 명령을 사용한다.

$ npm install --save tui-code-snippet # 최신 버전
$ npm install --save tui-code-snippet@<version> # 특정 버전

CDN으로 설치하기

아래와 같이 CDN에 직접 접근해서 사용할 수 있다. npm 혹은 bower를 사용할 수 없다면 사전에 의존성 정보를 확인하고, 관련 코드들도 반드시 다운로드해야 한다.

<script src="https://uicdn.toast.com/tui.code-snippet/latest/tui-code-snippet.js"></script> <!-- 최신 버전 -->
<script src="https://uicdn.toast.com/tui.code-snippet/<version>/tui-code-snippet.js"></script> <!-- 특정 버전 -->

Bower로 설치하기

Bower로 소스코드를 가져올 경우 아래와 같이 커맨드 라인 명령을 사용한다.

$ bower install "tui-code-snippet" # 최신 버전
$ bower install "tui-code-snippet#<version>" # 특정 버전
  • bower는 디프리케이트 되었기 때문에 사용을 권장하지 않는다.

파일명

CodeSnippet은 기본적으로 tui-code-snippet.jstui-code-snippet.min.js 두가지 이름으로 배포된다. 프로젝트의 성격에 따라 파일명을 재정의할 수 있지만, 혼란을 줄이기위해 가급적 파일명은 변경하지 않고 그대로 사용하기를 권장한다.

  • tui-code-snippet.js : 압축되지 않은 버전이므로 개발 시 적합
  • tui-code-snippet.min.js : 압축된 버전으로 서비스 배포 시 적합

사용하기

CommonJS

const snippet = require('tui-code-snippet'); /* CommonJS */

ES6 module import

import snippet from 'tui-code-snippet'; /* ES6 */

브라우저 script 태그

script 태그로 포함할 경우 tui.util을 네임스페이스로 갖는다.

<!DOCTYPE html>
<html>
  <head>
    <title>HTML Page</title>
  </head>
  <body>
    ...
    <script type="text/javascript" src="../js/lib/code-snippet.js"></script>
    <script type="text/javascript" src="../js/app.js"></script>
  </body>
</html>

필요한 모듈만 사용하기

import defineClass from 'tui-code-snippet/src/js/defineClass';

번들러를 사용할 때 필요한 모듈만 선택적으로 사용하면 의존성이 있는 파일만 번들에 포함되어 파일 사이즈를 줄일 수 있다.

브라우저 판별하기

CodeSnippet은 브라우저를 판별할 수 있는 snippet.browser객체를 제공한다. browser.js가 로드되면 따로 실행하지 않아도, snippet.browser객체의 속성으로 현재 브라우저의 정보가 자동으로 세팅되고 이 값들을 이용하여 브라우저별로 필요한 처리를 할 수 있다.

import snippet from 'tui-code-snippet';

const browser = snippet.browser;

if (browser.msie) {
  alert("Internet Explorer");
} else if (browser.chrome) {
  alert("Chrome");
} else if (browser.firefox) {
  alert("FireFox");
} else if (browser.safari) {
  alert("Safari");
} else if (browser.others) {
  alert("Unknown");
}

브라우저 버전별로 다른 처리가 필요한 경우라면 version 속성을 사용할 수 있다.

const browser = snippet.browser;

if (browser.msie) {
  if (browser.version < 8) {
    alert("plz, upgrade your browser");
  } else {
    ...
  }
}

타입 체크하기

CodeSnippet은 데이터의 타입을 확인할 수 있는 메서드를 제공한다.

타입을 체크하는 메서드들은 모두 접두어 is데이터 타입의 조합으로 이름 지어졌다. 예를 들어 숫자형 데이터인지 판별하는 메서드는 "is"와 "Number"가 조합된 형태로 snippet.isNumber()로 사용하면 된다.

const exam = 'fe';

snippet.isString(exam); // true
snippet.isNumber(exam); // false
snippet.isExisty(exam); // true

이 외에도isArray, isObject, isFunction 등 다양한 타입체크 메서드를 제공한다. 자세한 내용은 관련 [API 문서]를 참고하기 바란다.

HTML 엔티티 다루기

CodeSnippet은 문자열을 HTML 엔티티로, HTML엔티티를 다시 문자열로 치환하는 기능을 제공한다. encodeHTMLEntity()를 통해 예약된 문자를 HTML 엔티티로 치환할 수 있다. 또한, decodeHTMLEntity()를 통해 HTML 엔티티를 예약된 문자로 치환 할 수 있다.

const htmlEntityString = '<div>FE devleopment Team <b>CodeSnippet</b></div>';
const result = snippet.encodeHTMLEntity(htmlEntityString);
// "&lt;div&gt;FE devleopment Team &lt;b&gt;CodeSnippet&lt;/b&gt;&lt;/div&gt;"

const htmlEntityString = '&lt;div&gt;A &#39;quote&#39; is &lt;b&gt;bold&lt;/b&gt;&lt;/div&gt;';
const result = snippet.decodeHTMLEntity(htmlEntityString);
// "<div>A 'quote' is <b>bold</b></div>";

CustomEvents 사용하기

CustomEvents는 옵저버 패턴으로 구현되어 있으며 사용자가 이벤트를 정의하고 관리하는 API를 제공한다. 이어서 CustomEvents를 사용하는 두 가지 방법에 대해 설명한다.

믹스인으로 처리하기

믹스인을 통해 직접 해당하는 객체에 이벤트를 넣어 사용할 수 있다.

const Model = snippet.defineClass({
  init: function() {
    this.status = 'stop';
  },
  run: function() {
    this.status = 'run';
  },
  getStatus: function() {
    return this.status;
  }
});

snippet.CustomEvents.mixin(Model);
const m = new Model(); // 인스턴스 생성

m.on('run', m.run.bind(m)); // 사용자 정의 이벤트 attach
m.fire('run'); // 사용자 정의 이벤트 발생 (이벤트 핸들러 실행)
m.getStatus(); // 'run'

내부 프로퍼티로 처리하기

다른 방법은 사용자 이벤트 객체를 대상 객체 내부 프로퍼티로 할당하여 이벤트를 처리하는 방법이다. init에서 이벤트 객체를 생성하여 내부 프로퍼티로 할당하면 해당 프로퍼티를 통해 이벤트를 발생시킬 수 있다.

const Model = snippet.defineClass({
  init: function() {
    this.status = 'stop';
  },
  run: function() {
    this.status = 'run';
  },
  getStatus: function() {
    return this.status;
  }
});
const m = new Model();

m.event.on('run', m.run.bind(m), m); // event(내부 프로퍼티)를 통해 사용자 이벤트 attach
m.event.fire('run'); // event(내부 프로퍼티)를 통해 사용자 이벤트 발생 (이벤트 핸들러 실행)
m.getStatus(); // 'run'

추가로 이벤트 핸들러 처리 결과에 따라 다른 처리가 필요할 경우 fire() 대신 invoke()로 이벤트를 발생시키고 결과 값을 반환 받을 수 있다. 사용 방법은 동일하다.

const m = new Model(); // 인스턴스 생성
m.event.on('run', handler, m); // 사용자 이벤트 attach

// invoke로 발생시킨 이벤트 결과에 따라 분기 처리
if (m.event.invoke('ready')) {
  ...
} else {
  ...
}

열거타입(Enum) 사용하기

Enum은 각기 다른 의미를 갖는 숫자의 나열을 가독성 있게 표현할 수 있다. 아래는 에러 타입을 Enum을 이용하여 표현하는 예제이다.

// Enum 객체에 각 에러 코드를 순차적으로 넣어 ERRORCODE를 생성
const ERRORCODE = new snippet.Enum(['TYPE', 'VALUE', 'OPERATION', 'MEMORY', 'STATE']);

function printError(code) {
  const {TYPE, VALUE, OPERATION, MEMORY, STATE} = ERRORCODE;

  switch (code) {
    case TYPE:
      alert('타입 에러!');
      break;
    case VALUE:
      alert('유효한 값이 아닙니다.');
      break;
    case OPERATION:
      alert('유효하지 않은 호출입니다.');
      break;
    case MEMORY:
      alert('메모리가 부족합니다.');
      break;
    case STATE:
      alert('상태 에러!');
      break;
    default:
      alert('알수없는 에러');
      break;
  }
}

function setObject(obj) {
  // 인자의 데이터 타입이 바르지 않을 경우 타입 에러를 출력
  if (!snippet.isObject(obj)) {
    printError(ERRORCODE.TYPE); // 타입 에러를 발생
  }
}
setObject('fe');

ES5 환경을 위한 API

이 장에서는 ES5 이하 환경에서 사용할 수 있는 편의 기능을 설명한다. 브라우저가 같은 기능의 네이티브 API를 제공하는 경우 해당 API를 사용하기를 권장하며, 대체할 수 있는 API는 각 장의 상단에 명시하였다.

배열 다루기

forEach

(ES5: Array.prototype.forEach)

forEach()를 이용하면 배열 및 객체를 순회할 수 있다. 유사 배열의 경우, 순회하기 전에 배열로 변환이 필요하다.

// 일반 배열 순회
let sum = 0;
forEach([1, 2, 3], (value) => {
  sum += value;
});

// 일반 객체 순회
let sum = 0,
  obj = {
    apple: 100,
    orange: 200,
    graph: 500,
    ...
  };
forEach(obj, (value) => {
  sum += value;
});

// 유사 배열 순회
function sum() {
  //arguments(유사 배열)를 배열로 변환
  const factors = Array.prototype.slice.call(arguments);
  forEach(factors, (value, index) => {
    ...
  });
}

filter

(ES5: Array.prototype.filter)

filter()를 이용하면 조건에 맞는 요소로만 추출된 부분 배열을 얻을 수 있다. 첫 번째 인자에 배열과 유사 배열을 모두 사용할 수 있으며, 두 번째 인자에 조건 비교를 수행할 함수를 넣는다.

const original = [100, 111, 23, 99, 101, 203, 1, 50];
snippet.filter(original, (value) => value > 100);  // [111, 101, 203];

map

(ES5: Array.prototype.map)

map()을 이용하면 연산을 통해 변경된 값으로 구성된 배열을 얻을 수 있다. filter와 마찬가지로 첫 번째 인자에 배열과 유사 배열을 모두 사용할 수 있으며, 두 번째 인자에 조건 비교를 수행할 함수를 넣는다.

const placeCode = {
  '100': 'INTERENCE',
  '200': 'HELP DESK',
  '300': 'RESTAURANT',
  '400': 'LOCKER ROOM',
  '500': 'CAFE'
};
snippet.map([100, 200, 300, 400, 500], (value) => placeCode[value]);
// ["INTERENCE", "HELP DESK", "RESTAURANT", "LOCKER ROOM", "CAFE"]

reduce

(ES5: Array.prototype.reduce)

reduce를 이용하면 연산 결과를 누적할 수 있다. 첫 번째 인자는 명령을 수행할 배열, 두 번째 인자는 값을 변경하기 위한 함수를 넣는다.

let nArr, i;

for (nArr = [], i = 0; i < 100; i += 1) {
  nArr.push(i);
}

const result = snippet.reduce(nArr, (stored, value) => {
  if (value && value % 3 === 0) {
    return stored + value;
  }
  return stored;
});

console.log(result); // 1683

toArray

(ES5: Array.from)

toArray를 이용하면 유사배열을 배열로 변환할 수 있다.

const  similarArray = {
  0: 'one',
  1: 'two',
  2: 'three',
  3: 'four',
  length: 4
};

const result = snippet.toArray(similarArray); // ['one', 'two', 'three', 'four'];

해시맵(HashMap)

(ES2015: Map 객체)

CodeSnippet은 해시 데이터를 설정하고 관리하는 해시맵을 제공한다. 필요한 CodeSnippet 파일을 로드한 후, 아래와 같이 해시맵을 생성한다.

const data = [
  {
    value: 'apple',
    type: 'fruit'
  },
  {
    value: 'mercedes',
    type: 'car'
  },
  {
    value: 'truck',
    type: 'car'
  },
  {
    value: 'lemonade',
    type: 'beverage'
  },
  {
    value: 'coke',
    type: 'beverage'
  }
];

let i = 0;
let len = data.length;
let cell, type;
let food = new snippet.HashMap(); // 해시맵 생성

for (; i < len; i += 1) {
  cell = data[i];
  type = food.get(cell.type);

  // 이미 존재하면 추가, 아니면 새로 생성한다.
  if (food.has(cell.type)) {
    type.push(cell.value);
  } else {
    food.set(cell.type, [cell.value]);
  }
}

// 결과 출력 함수
function showResult() {
  i = 0;
  len = food.length;
  const keys = food.keys();
  for(; i < len; i+= 1) {
    console.log(keys[i], food.get(keys[i]));
  }
}

console.log(food.length); // 3
console.log(showResult());  // fruit ["apple"]
                          // car ["mercedes", "truck"]
                          // beverage ["lemonade", "coke"]

each()를 이용하면 해시맵을 순회할 수 있으며, remove()를 이용하여 해시 데이터를 삭제할 수 있다.

food.each((value, key) => {
  if (key === 'fruit') {
    food.remove(key);
  }
});
console.log(food.length); // 2
console.log(showResult());  // car ["mercedes", "truck"]
                          // beverage ["lemonade", "coke"]

객체 확장하기

(ES2015: Object.assign)

CodeSnippet은 extend를 이용하여 객체를 확장하는 기능을 제공한다. extend로 객체를 확장할 때에는 첫 번째 인자에 확장할 대상을 그리고 두 번째 인자는 확장할 값들을 넣어주면 된다.

const def = {
  a: 10000,
  b: 20000,
  c: 30000
};
snippet.extend(def, {
  d: 40000,
  e: 50000,
  f: 60000
}); // {a: 10000, b: 20000, c: 30000, d: 40000, e: 50000,  f: 60000}

CustomEvent mixin과, defineClass에서 static 속성을 생성할 때 사용된다.

// CustomEvent mixin
CustomEvents.mixin = function(func) {
  snippet.extend(func.prototype, CustomEvents.prototype);
};

// defineClass static
if (props.hasOwnProperty('static')) {
  snippet.extend(obj, props.static);
  delete props.static;
}
...

클래스 정의하기

(ES2015: class 키워드)

snippet.defineClass()를 이용하면 클래스 정의 및 상속을 구현할 수 있다.

// 클래스 정의하기
const Person = snippet.defineClass({
  // 인스턴스 생성시 자동으로 실행되는 함수
  init: function(name) {
    this.name = 'Name:  ' + name;
    ...
  },

  // 생성자 클래스 정적 멤버 정의
  static: {
    company: 'NHN Cloud',
    getComany: function() {
       ...
    }
    ...
  },

  // 생성자 프로토타입에 들어갈 멤버 정의
  getName: function() {
    ...
  }
  ...
});

스크립트 코드에서 snippet.defineClass()에 클래스로 생성할 객체를 넘겨 클래스를 생성할 수 있다. 이때 init은 인스턴스가 생성될 때 실행되는 생성자 함수로 초기화 시 수행될 코드를 정의하고, static은 클래스의 정적 멤버를 정의한다. 그 외 메서드와 변수는 생성자 프로토타입에 들어간다.

// 인스턴스 생성하기
const hanjung = new Person('hanjung');

// 인스턴스 메서드 호출하기
hanjung.getName();

예제와 같이 new 키워드로 인스턴스를 생성할 수 있다.

상속 구현하기

(ES2015: extends 키워드)

상속은 클래스를 정의할때 이용했던 snippet.defineClass에 추가 파라미터를 넘겨 구현할 수 있다.

// 부모 클래스 정의하기
const Parent = snippet.defineClass({
  print: function() {
    console.log(this.name);
  }
});

우선 snippet.defineClass()를 이용하여 상속을 위한 부모 클래스를 먼저 정의한다. 부모 클래스를 정의하는 방법은 일반 클래스를 정의하는 방법과 동일하며, 클래스 정의하기에서 이미 다루었다.

// 자식 클래스 정의
const Child = snippet.defineClass(Parent, {
  init: function(name) {
    this.name = name;
  }
});

자식 클래스도 일반 클래스와 같은 방법으로 클래스를 정의하며, 파라미터만 다르다. 첫 번째 파라미터는 상속받을 대상이 되는 부모 클래스를 두 번째 파라미터에는 클래스로 생성할 객체를 넘긴다.

// 자식 인스턴스 생성
const child = new Child('fe');

// 인스턴스 메서드 호출
child.print();  // 'fe'

Child 클래스는 Parent 클래스를 상속받았기 때문에 자신에게는 없는 부모의 print()를 사용할 수 있다.

맺음말

지금까지 TOAST UI CodeSnippet에 대해 간단히 알아보았다. ES2015의 확산으로 과거에 비해 역할이 줄었지만, 여전히 CodeSnippet은 개발에 도움이 되는 많은 기능을 제공하고 있다. 이 가이드가 TOAST UI CodeSnippet을 보다 효율적으로 사용하는 데 도움이 되길 바란다.


이 문서는 NHN Cloud의 FE개발랩에서 작성하고 관리하는 공식 웹 프론트 개발 가이드이다. 가이드 적용 관련 문의나 문서의 오류, 개선 제안은 공식 문의 채널(dl_javascript@nhn.com)을 통해 할 수 있다.


Last Modified
2019. 03. 29
FE Development LabBack to list