'전체 글'에 해당되는 글 32건

  1. 2018.10.28 Vanilla JS .. ?
  2. 2018.10.28 ES6 문법 정리 (3) – template, arrow function, module

자바스크립트 3대장 (React, Angular, Vue)을 익히다 보면 Vanilla JS라는 말이 자주 등장합니다. 그러나 어디에서도 그 실체는 드러나지가 않죠. 도데체 뭐가 Vanilla JS 라는겨?? 저도 처음엔 이랬습니다만.. 더 이상 바보가 되지 않도록 정리 한 해보죠~

 

※ vanilla 의 어원

이거 하나로 정리가 되죠? 형용사로 특별한 점이 없는, 일반적인 이라는 뜻입니다. 명사로서의 뜻도 있지만, 바닐라 열매에서 추출한 원료라는 뜻이어서 큰 특징은 없습니다.

곰곰이 생각해보면 아이스크림에서 바닐라 맛은 미각적인 특징이 들어가지 않은 기본적인 맛이잖아요? 이걸 따와서 특별한게 없는 것의 특징으로 vanilla 라고 부르기 시작한게 아닐까 싶어요. 실제로 영어에서 vanilla는 별 특징 없는 걸 표현할때 쓰는 slang이라고 하네요~

 

※ Vanilla JS

즉, Vanilla JS는 라이브러리나 프레임워크 등의 특징을 타지 않는 순수 자바스크립트 문법을 가리키는 또 하나의 slang입니다. 아마 프로그래밍 기본기의 중요성을 망각하고, 무조건 프레임워크나 라이브러리 같이 포장되서 나오는 것들만 좋아하는 사람들을 골려주기 위해 나온 말 같은데요..ㅋㅋㅋ 아래 동영상에 자세히 설명이 나옵니다~

 

ps. 근데 한편으로는 씁쓸한게... 뭔가 우리나라 현실을 너무 잘 비꼬는 거 같은...?

Posted by kevin.jeong.
,

이번에 다룰 내용은 React.js, Vue.js 등의 최신 자바스크립트 라이브러리 및 프레임워크에서 꼭 보게되는 내용입니다. 잘 이해하면 최신 자바스크립트 트렌드를 따라가는데 수월해지죠.


※ Template

template 문법은 기존 자바스크립트에서 문자열 사이에 값을 넣을때 .. + yourName + .. 같은 처리로 코드가 지저분해지는걸 막고 가독성을 높여주는 효과가 있습니다. 간단하므로 예제로 설명드리겠습니다.

const bodyTag = document.getElementsByTagName('body')[0]
const myName = 'kevin'
bodyTag.innerHTML = `<div id="myDiv">${myName}'s Home</div>`

template 문법을 지정하기 위해서는 기존에 문자열을 나타내는 따옴표나 쌍따옴표 대신 보통 ESC 키 옆에 있는 ` 문자를 사용해야 합니다. 문자열을 ` ... `로 감싸면 자바스크립트가 템플릿으로 처리할 준비를 합니다. 그 다음에 '..' + 문자열변수명 + '..' 대신 ${문자열변수명 혹은 삽입할 객체의 프로퍼티명} 으로 템플릿에 삽입할 변수와 위치를 지정해주고 실행하면, 자바스크립트는 템플릿 문법을 처리해서 변수의 문자열 값을 지정된 위치에 넣은 문자열을 반환해주죠. 실행 결과는 다음과 같습니다.

크롬에서 새탭 열고 바로 개발자 도구 열어서 실행했습니다. 크롬 고유의 홈 화면은 온데간데 없이 사라지고 예제로 지정한 템플릿만 나오네요. 이처럼 템플릿 문법은 최신 자바스크립트 프레임워크에서 웹페이지 뷰를 동적으로 로드할때 많이 씁니다.


※ Tagged Template literals

템플릿으로 문자열을 동적으로 할당해주는건 좋지만, 만약 JSON 객체로 반복되는 템플릿을 실행하다가 중간에 비어서 선언이 안되어 있는 프로퍼티가 있으면 어떻게 해야 할까요? 대상이 없다고 안내하는 대체 문구가 있으면 좋겠죠? 그런걸 처리하는 방법이 있습니다. 템플릿에서 삽입할 값을 검사하는 함수를 만드는 건데요. 감이 잘 안오실테니 예제로 보여드리겠습니다.

let jsonData = [
  {
    title: "ES6 문법 정리 (1) – let, const 키워드"
    ,author: "kevini"
    ,category: "IT관련, Javascript"
    ,keywords: "const, ES6, let, 자바스크립트"
    ,publishDate: "2018-03-02"
  }
  ,{
    title: "ES6 문법 정리 개요"
    ,author: "kevini"
    ,category: "IT관련, Javascript"
    ,keywords: "ECMAScript, ES6"
    ,publishDate: "2018-03-01"
  }
  ,{
    title: "자바스크립트 개념 정리 (3) – prototype 3탄 (자바스크립트 내장 메소드, 상속구조 구현을 위한 ES6 문법)"
    ,author: "kevini"
    ,category: "IT관련, Javascript"
    ,keywords: "ES6 class, Object.스태틱메소드, 자바스크립트"
    ,publishDate: "2018-03-01"
  }
  ,{
    title: "자바스크립트 개념 정리 (3) – prototype 2탄 (prototype chaining)"
    ,author: "kevini"
    ,category: "IT관련, Javascript"
    ,keywords: "prototype chaining, 자바스크립트, 클로저"
    ,publishDate: "2018-03-01"
  }
  ,{
    title: "자바스크립트 개념 정리 (3) – prototype 1탄"
    ,author: "kevini"
    ,category: "IT관련, Javascript"
    ,keywords: "prototype, 생성자함수, 자바스크립트"
    ,publishDate: "2018-02-25"
  }
  ,{
    title: "자바스크립트 개념 정리 (2) – context & scope"
    ,author: "kevini"
    ,category: "Javascript"
    ,keywords: "context, javascript, this"
    ,publishDate: "2018-02-24"
  }
  ,{
    title: "자바스크립트 개념 정리 (1) – 변수 타입"
    ,author: "kevini"
    ,category: "Javascript"
    ,keywords: "==, ===, 변수타입, 자바스크립트"
    ,publishDate: "2018-02-03"
  }
  ,{
    title: "Atom 에디터에서 오류 발생할때 대처방법"
    ,author: "kevini"
    ,category: "개발툴관련"
    ,publishDate: "2018-01-27"
  }
  ,{
    title: "자바스크립트 표준 ES6 문법 체킹을 위한 JSHint"
    ,author: "kevini"
    ,category: "Javascript"
    ,keywords: "ES6, JSHint, 자바스크립트"
    ,publishDate: "2018-01-27"
  }
];

const bodyTag = document.getElementsByTagName('body')[0];
bodyTag.innerHTML = `<h2>블로그 목록</h2><div id="myDiv" style="text-align:left"></div>`;
const divTag = document.querySelector('#myDiv');

function tagFn(tags, title, author, category, keywords){
  if(typeof category === 'undefined') category = '카테고리 미지정';
  if(typeof keywords === 'undefined') keywords = '태그 미지정';

  return (tags[0] + title + tags[1] + author + tags[2] + category + tags[3] + keywords + tags[4]);
}

jsonData.forEach((v) => {
  const liTags = tagFn`
  <li>제목 : ${v.title}</li>
  <li>작성자 : ${v.author}</li>
  <li>카테고리 : ${v.category}</li>
  <li>태그 : ${v.keywords}</li>`;

  divTag.innerHTML += `
    <ul>
      ${liTags}
    </ul>
  `;
});

역시 크롬에서 새 탭 띄우신 다음 개발자 도구 열고 콘솔에서 예제 돌리면 아래와 같이 페이지가 바뀝니다.

자세히 보시면 밑에서 두번째 목록의 태그는 '태그 미지정' 으로 입력되어 있네요. 그 부분의 JSON 데이터를 보면 keywords 프로퍼티가 비어있습니다. 즉 비어있는 값들에 대해 처리를 할수 있도록 tagFn이라는 템플릿 검사 함수를 만든것입니다.


※ Arrow function

Arrow function은 함수를 정의하는 새로운 방식입니다. 보통 콜백으로 넘기는 함수를 정의할때 많이 쓰입니다. 물론 일반적인 함수 정의를 대체할수도 있습니다.

//일반적인 함수 정의
function(value1, value2, ...) {
  ...
}

//Arrow function 정의
let fn1 = (value1, value2, ...) => {
  ...
}

//Arrow function 사용 예
setTimeout(() => {
  console.log("check!!");
}, 200)

소스를 보면 function() {} 에서 () => {} 로 바뀐게 전부입니다. () 부분은 똑같이 파라미터를 정의해주고, {} 부분에서 함수의 기능을 정의하는 거죠. 다만 function 키워드로 함수를 선언할때와 arrow function으로 선언할때 실행컨텍스트가 다르다는 차이가 있습니다. 예제를 더 보도록 하죠.

const myObj = {
  timeoutTest1(){
    setTimeout(function(){
      console.log(this);
    }, 200);
  },
  timeoutTest2(){
    setTimeout(() => {
      console.log(this);
    }, 200);
  }
}

myObj.timeoutTest1();
myObj.timeoutTest2();

실행 결과를 보겠습니다.

myObj 에서 timeoutTest1 메소드로 setTimeout을 실행하면서 실행컨텍스트를 확인해봤습니다. 예전에 함수의 실행컨텍스트는 global 객체가 된다고 했었죠? 메소드로 실행해도 출력하는 부분은 function으로 둘러싸여있어서 Window 객체가 출력되었네요.

하지만 timeoutTest2 메소드로 setTimeout을 실행할때는 실행컨텍스트가 myObj 객체가 되네요. 즉, arrow function이 실행될때 실행컨텍스트는 자기를 부르는 메소드를 포함하는 객체가 됩니다. 이 차이만 주의하시면 arrow function은 그렇게 두려워하실 필요는 없으실거 같습니다.


※ module 정의

module을 구성하려면 우선 자바스크립트 소스를 여러 파일로 정의해야 합니다. 순서대로 방법을 적어보면,

(1) 자바스크립트 파일마다 모듈화할 객체를 구현한다.

(2) 파일마다 export 혹은 export default 키워드로 외부에서 접근 가능한 객체를 지정한다.

(3) 최종적으로 html에서 <script> 태그로 로드할 파일에서 import 문으로 다른 파일에서 정의한 객체를 로드해서 사용한다.

정도가 되겠습니다.

module을 테스트 하려면 웹 서버가 구성되있어야 하므로 저는 nodeJS 기반의 lite-server 에서 테스트 하겠습니다.

우선 예제 코드 올립니다.

/* CalendarObj.js */
class CalendarObj{
  constructor(year, month){
    this.year = year
    this.month = month;
  }

  checkParams(){
    if(this.year * 1 === NaN){
      console.log("년도의 입력 값이 올바르지 않습니다.", this.year);
      return false;
    }
    if(this.month < 1 || this.month > 12){
      console.log("월의 입력 값이 올바르지 않습니다.", this.month);
      return false;
    }
    return true;
  }

  getCalendarForm(){
    if(!this.checkParams()){
      alert("입력 값을 다시 확인하세요.");
      return false;
    }

    let month_str = this.month > 9 ? this.month : '0' + this.month;
    let max_day = 31;
    switch(this.month){
      case 4:
      case 6:
      case 9:
      case 11:
        max_day = 30;
        break;
      case 2:
        max_day = 28;
        break;
    }

    let day_of_week = new Date(`${this.year}-${month_str}-01`).getDay();

    let in_tr_arr = [];
    let tmp_tr_arr = '';

    for(let k = 0; k < day_of_week; k++){
      tmp_tr_arr += '<td></td>';
    }

    for(let i  = 1; i <= max_day; i++){
      if(day_of_week == 0){
        in_tr_arr.push(tmp_tr_arr);
        tmp_tr_arr = '';
      }

      let color_style = day_of_week === 0 || day_of_week === 6 ? 'style="color:red;"' : '';
      tmp_tr_arr += `<td ${color_style}>${i}</td>`;

      day_of_week = (day_of_week + 1) % 7;
    }

    in_tr_arr.push(tmp_tr_arr);
    let in_template = in_tr_arr.join('</tr><tr>');
    return `
      <table>
        <tr>
          <th>일</th>
          <th>월</th>
          <th>화</th>
          <th>수</th>
          <th>목</th>
          <th>금</th>
          <th>토</th>
        </tr>
        <tr>
          ${in_template}
        </tr>
      </table>
    `;
  }
}

export default CalendarObj;
/* app.js */
import CalendarObj from './CalendarObj.js'

document.querySelector("input[type=button]").addEventListener("click", (evt)=>{
  let year_input = document.querySelector("#year_input");
  let month_input = document.querySelector("#month_input");

  let cal_obj = new CalendarObj(year_input.value * 1, month_input.value * 1);
  if(!cal_obj.checkParams()){
    alert("입력값을 다시 확인하세요!");
  } else {
    console.dir(document.querySelector("#calFrame"));
    document.querySelector("#calFrame").innerHTML = cal_obj.getCalendarForm();
  }
})
<!-- index.html -->
<!doctype html>
<html>
  <head>
    <title>module test page</title>
  </head>
  <body>
    <div id="appContents">
      <input type="text" id="year_input" /><br />
      <input type="text" id="month_input" /><br />
      <input type="button" value="달력보기" />
      <div id="calFrame">

      </div>
    </div>
    <script type="module" src="./scripts/app.js"></script>
  </body>
</html>

js 파일은 scripts 디렉토리 밑에 두었습니다. 주의하실 점은 html에서 script 태그를 붙일때 type="module" 로 꼭 되어있어야 한다는 점입니다. 이렇게 안하면 module import 를 못합니다.

실행 결과는 다음과 같습니다.

이 부분은 비슷하게 예제 만들어서 lite-server 에서 테스트 해보시면 이해가 빠르실겁니다. 당장 이해하려고 하지 않으셔도 React.js, Angular.io, Vue.js 같은데서 항상 등장하는 거라서 지금은 가볍게 이해만 하고 넘어가셔도 되고요~


※ 레퍼런스

이 글은 인프런 사이트의 ‘모던자바스크립트(javascript) 개발을 위한 ES6 강좌’ 강좌의 내용을 토대로 예제코드를 작성하여 설명하였습니다. ES6 자바스크립트의 기능을 좀 더 알기를 원하시면 해당 강좌를 수강하시는 것을 권장합니다. 비용 결재 후 수강 가능합니다만 인프런 사이트 내 상당수의 강좌가 무료이며, 유료강좌도 비용이 강좌당 몇만원 수준이어서 크게 부담은 안되실겁니다. 그리고 인프런과 협약을 맺은 대학교의 학생들은 이 유로강좌도 무료로 들을 수 있으니 인프런 사이트를 참고하시기 바랍니다.

 

 

 

'Web 개발 (업데이트 없음)' 카테고리의 다른 글

Socket.IO 이해하기  (0) 2018.10.28
Vanilla JS .. ?  (0) 2018.10.28
ES6 문법 정리 (2) – Destructuring 기법  (0) 2018.10.28
ES6 문법 정리 (1) - let, const 키워드  (0) 2018.10.28
ES6 문법 정리 개요  (0) 2018.10.28
Posted by kevin.jeong.
,