최신 JS프레임웍엔 최신 자바스크립트

JS 프레임웍을 위한 ES6

뷰, 리액트, 앵귤러등의 자바스크립트 프레임워크는 최신 자바스크립트 문법을 사용합니다. 따라서 자바스크립트 프레임워크를 학습하기 위해 알아야 할 최신 자바스크립트 문법을 따로 정리하도록 하겠습니다.

자바스크립트는 1995년에 탄생했습니다. 원래는 웹 페이지 상호작용을 손쉽게 추가하기 위해 만들어진 언어였는데 이후 DHTML, AJAX등의 기술과 함께 더욱 활발히 사용되는 언어가 되었습니다.

이 후 노드라는 자바스크립트 플랫폼이 등장하면서 풀스택 애플리케이션을 개발할 수 있는 언어가 되었는데, 이러한 자바스크립트의 변화를 주도하는 기관이 바로 ECMA입니다.

자바스크립트의 명세는 2015년 6월에 크게 개정되며 ECMAScript 6, ES6, ES2015등의 이름으로 불리게 되는데, 이 후부터는 ES2016, ES2017처럼 버전 대신 연도를 부여하여 새로운 명세를 추가하고 있습니다.

우리가 학습해야 할 최신 자바스크립트는 바로 이 2015년 6월 이후에 개정된 자바스크립트를 말합니다. 이 클래스에서는 앞으로 최신 자바스크립트를 ES6라고 부르도록 하겠습니다.

자바스크립트는 객체지향 보다는 함수형 프로그래밍에 적합한 언어입니다. 실제로 ES6에 포함된 대부분의 기능은 함수형 프로그래밍 기법에 최적화되어 있습니다.

함수형 프로그래밍은 코드를 여러 함수를 모아둔 것으로 생각하고, 이런 함수들을 서로 합성하고 조합하여 애플리케이션을 구축하는 방법입니다. 때문에 함수형 프로그래밍 기법에 익숙해지는 것이 최신 자바스크립트를 제대로 활용하는 방법이라고 할 수 있습니다.

변수 선언

이전의 자바스크립트에서는 var키워드를 사용하여 변수를 선언하였지만 ES6 부터는 더 나은 몇 가지 방법이 추가되었습니다.

상수

상수는 값을 변경할 수 없는 변수입니다. 상수로 설정된 변수의 값을 변경하려고 하는 경우에는 오류가 발생됩니다.

const es6 = 'javascript';
es6 = 'ECMAScript 6';
Uncaught TypeError: Assignment to constant variable.

let

ES6 부터는 렉시컬 스코프, 즉 구문적인 변수 영역 규칙을 지원합니다.

자바스크립트는 중괄호({})를 사용하여 코드 블록을 만드는데 다른 프로그래밍 언어와는 달리 if/else영역은 별도의 코드 블록으로 구성되지 않습니다. 그래서 이전의 자바스크립트에서는 글로벌 변수를 엉뚱한 값으로 덮어씌우는 상황도 만들어질 가능성이 있었죠.

var es6 = 'javascript';

if( es6 ) {
    var es6 = 'ECMASCript 2015';
    console.log('블록 변수: ', es6);
}

console.log('글로벌 변수: ', es6);
블록 변수:  ECMASCript 2015
글로벌 변수:  ECMASCript 2015

이전의 var 키워드와는 달리 let 키워드를 사용하면 변수의 영역을 코드 블록 안으로 한정시킬 수 있어 글로벌 변수를 보호할 수 있습니다.

var es6 = 'javascript';

if( es6 ) {
    let es6 = 'ECMASCript 2015';
    console.log('블록 변수: ', es6);
}

console.log('글로벌 변수: ', es6);
블록 변수:  ECMASCript 2015
글로벌 변수:  javascript

앞의 코드와 같이 let 키워드를 사용하면 똑같은 이름의 변수명을 사용해도 글로벌로 선언된 변수에는 영향을 주지 않습니다.

다음의 두 코드를 비교해보면 var 키워드와 let 키워드로 선언된 변수의 차이를 확실하게 느낄 수 있습니다.

var button,
    container = document.getElementById('container');

for(var i=0; i<5; i++) {
    button = document.createElement('button');
    button.innerText = '버튼' + (i + 1);
    button.addEventListener('click', function(){
        alert('이 버튼은 #' + (i + 1) + '번째 버튼입니다.');
    });
    container.appendChild(button);
}
버튼1부터 버튼5까지 모두 5번째 버튼으로 표시됩니다.

코드를 실행하고 각 버튼을 클릭해보면 루프 카운터의 변수 i에 var를 사용했을 때 글로벌 영역에 i가 생성되어 루프 카운터의 i 값이 덮어씌워져 버리는 것을 확인 할 수 있습니다. 그래서 버튼 1부터 버튼 5까지 어느 버튼을 클릭하더라도 5번째 버튼이라는 메세지만 출력됩니다.

그렇다면 변수 i에 var 대신 let 키워드를 사용하면 어떨까요? 동일한 코드를 let 키워드만 바꿔서 테스트해보세요.

var button,
    container = document.getElementById('container');

for(let i=0; i<5; i++) {
    button = document.createElement('button');
    button.innerText = '버튼' + (i + 1);
    button.addEventListener('click', function(){
        alert('이 버튼은 #' + (i + 1) + '번째 버튼입니다.');
    });
    container.appendChild(button);
}

위의 코드는 for 문의 변수 i의 키워드를 var에서 let으로만 바꾼 코드입니다. 루프 카운터의 변수 i를 let을 사용하여 선언하면 i의 영역이 for의 중괄호({})블록으로 제한되어 각각의 버튼을 클릭했을 때 루프를 생성할 때 사용한 i의 값이 정상적으로 표시됩니다.

코드를 실행해보면 각각의 버튼을 클릭했을 때 정상적인 버튼의 인덱스가 출력되는 것을 확인할 수 있습니다.

템플릿 문자열

템플릿 문자열`를 사용하는 문자열로 +보다 간단하고 편리하게 변수를 포함한 문자열 연결을 할 수 있습니다.

const first = '홍',
      middle = '길',
      last = '동'
console.log( first + ' ' + middle + ' ' + last );
console.log( `${first} ${middle} ${last}` ); // 템플릿 문자열
홍 길 동




홍 길 동

템플릿 문자열의 ${}의 내부에는 값을 만들어내는 모든 형태의 자바스크립트 식이 들어갈 수 있습니다.

또 템플릿 문자열은 모든 공백을 유지하기 때문에 이메일 템플릿이나 코드 템플릿 등 공백이 유지되어야 하는 문자열을 표시하는 경우에도 유용하게 사용될 수 있습니다.

const thankyou = `
안녕하세요. ${name}님.
${event} 티켓을 구매해주셔서 감사합니다.

[주문 정보]
${qty}매 X ${price}
total: ${qty * price}
${event} 공연

감사합니다.
`

const template = `
<div class="template">
    <h3>${template.title}</h3>
    <div class="body">
        ${template.body}
    </div>
</div>
`

디폴트 파라미터

C++이나 파이썬과 같은 언어에서는 함수의 인자로 디폴트 값을 선언할 수 있습니다.

기존의 자바스크립트에서는 함수의 인자에 디폴트 값을 선언 할 수 없었지만, 최신 자바스크립트의 ES6 명세에서는 디폴트 파라미터가 추가되어 함수의 인자에서 직접 디폴트 값을 설정할 수 있게 되었습니다.

const person = {
    name: '홍길동',
    favorite: '축구'
}

function likeActivity(a=person) {
    console.log( `${a.name}은 ${a.favorite}을 좋아합니다.` );
}

likeActivity();
홍길동은 축구을 좋아합니다.

화살표 함수

화살표 함수를 사용하면 function 키워드 없이도 함수를 만들 수 있고, return을 사용하지 않아도 식을 계산한 값이 자동으로 반환됩니다.

var citizen = function(name) {
    return `나는 대한민국의 시민 ${name}입니다.`;
}

console.log( citizen('홍길동') );
나는 대한민국의 시민 홍길동입니다.

기존의 자바스크립트에서는 위와 같은 형태로 익명함수를 사용했습니다. 최신 자바스크립트에서는 화살표 함수를 사용하여 다음과 같이 간단하게 만들 수 있습니다.

var citizen = name => `나는 대한민국의 시민 ${name}입니다.`;
console.log( citizen('홍길동') );
나는 대한민국의 시민 홍길동입니다.

기존의 코드에 비해 훨씬 간단해진 것을 알 수 있습니다.

2개 이상의 파라미터와 멀티 라인 처리하기

앞의 예제를 수정하여 2개 이상의 파라미터를 사용하는 경우를 만들어 보겠습니다. 우선 기존 방식의 자바스크립트 코드입니다.

var citizen = function(country, name) {
    return `나는 ${country}의 시민 ${name}입니다.`;
}

console.log( citizen('대한민국', '홍길동') );
나는 대한민국의 시민 홍길동입니다.

위의 예제와 같이 함수의 프로퍼티를 2개 이상 사용해야 하는 경우를 화살표 함수에서는 어떻게 표현할까요? 이런 경우에는 화살표 함수의 파라미터를 괄호로 감싸주면 됩니다.

var citizen = (country, name) => `나는 ${country}의 시민 ${name}입니다.`;
console.log( citizen('대한민국', '홍길동') );
나는 대한민국의 시민 홍길동입니다.

사실 함수는 일반적으로 위의 예제와 같이 간단한 구조로 만드는 경우는 많지 않습니다. 명령 처리를 위해 여러 줄로 코드를 작성하는 경우가 대부분이죠.

이런 경우에는 함수와 같이 중괄호({})로 코드를 감싸주면 됩니다. 먼저 기존 방식의 함수 형태로 작성해보겠습니다.

var citizen = function(country, name) {

    if( !country ) {
        throw new Error('국가명을 입력해주세요.');
    }

    if( !name ) {
        throw new Error('이름을 입력해주세요.');
    }

    return `나는 ${country}의 시민 ${name}입니다.`;
}

console.log( citizen('대한민국', '홍길동') );
나는 대한민국의 시민 홍길동입니다.

이제 기존 방식으로 작성된 위의 함수를 화살표 함수로 바꿔 보겠습니다.

var citizen = (country, name) => {

    if( !country ) {
        throw new Error('국가명을 입력해주세요.');
    }

    if( !name ) {
        throw new Error('이름을 입력해주세요.');
    }

    return `나는 ${country}의 시민 ${name}입니다.`;
}

console.log( citizen('대한민국', '홍길동') );
javascript_test.html:24 나는 대한민국의 시민 홍길동입니다.

어떤가요? 기존의 함수와 크게 다르지는 않죠? 하지만 한가지 주의해야 할 점이 있습니다.

기존 함수와는 달리 화살표 함수는 새로운 this 영역을 만들지 않습니다.다음과 같은 코드의 경우 기존의 문법으로 함수를 작성하면 에러가 발생됩니다.

var sports = {
    games: ['야구', '농구', '축구', '배구', '태권도'],
    print: function(delay=1000) {
        setTimeout(function() {
            console.log(this.games.join(', '));
        }, delay);
    }
}

sports.print();
Uncaught TypeError: Cannot read property 'join' of undefined

위 코드의 경우 this.resortsjoin 메서드를 호출하려고 시도하다가 에러가 발생됩니다. 그 이유는 함수에서 호출되는 this가 window 객체이기 때문에 gamesundefined로 출력되기때문이죠.

이 코드를 화살표 함수로 바꿔보겠습니다.

var sport = {
    games: ['야구', '농구', '축구', '배구', '태권도'],
    print: function(delay=1000) {
        setTimeout(() => {
            console.log(this.games.join(', '));
        }, delay);
    }
}

sport.print();
야구, 농구, 축구, 배구, 태권도

이 경우에는 에러가 나지 않고 함수가 정상적으로 작동합니다. 이 때 함수에서 호출되는 thissport입니다. 그렇다면 만약 print에도 화살표 함수를 사용하면 어떻게 될까요?이 경우에는 thiswindow 객체가 됩니다.

var sport = {
    games: ['야구', '농구', '축구', '배구', '태권도'],
    print: (delay=1000) => {
        setTimeout(() => {
            console.log( this === window );
        }, delay);
    }
}

sport.print();
true

아직은 잘 이해가 되지 않을 수도 있지만 두 가지만 기억하세요. 그러면 큰 어려움없이 화살표 함수를 사용하실 수 있습니다.

  • 첫번째, 화살표 함수를 사용할 때는 항상 영역에 유의하세요.
  • 두번째, 화살표 함수는 새로운 this 영역을 만들지 않아요.

객체와 배열

ES6는 객체와 배열을 다루는 방법, 객체와 배열 안에서 변수의 영역을 제한하는 방법을 다양하게 제공합니다. 대표적으로 구조 분해, 객체 리터럴 개선, 스프레드 연산자 등이 있습니다.

구조 분해

ES6의 구조 분해는 객체 안에 있는 필드 값을 원하는 변수에 대입할 수 있게 해주는 기능으로 이 기능을 사용하면 객체에 있는 필드를 같은 이름의 변수에 넣어줍니다.

var subway = {
        bread: '위트',
        topping: '아보카도',
        source: '랜치 드레싱',
        vegetable: ['양상추', '토마토', '오이']
    }

var {bread, source} = subway;

console.log( bread, source );
위트 랜치 드레싱

위 코드는 ES6의 구조 분해 기능을 사용하여 subway 객체를 분해해 bread와 source 필드를 같은 이름의 변수에 넣어줍니다.

이 때 두 변수의 값은 subway에 있는 같은 이름의 필드 값으로 초기화되지만, 두 변수를 변경해도 subway에 있는 원래의 필드 값은 변경되지 않습니다.

var subway = {
        bread: '위트',
        topping: '아보카도',
        source: '랜치 드레싱',
        vegetable: ['양상추', '토마토', '오이']
    };

var {bread, source} = subway;

bread = '화이트';
source = '허니 머스타드';

console.log( bread, source );
console.log( subway.bread, subway.source );
화이트 허니 머스타드
위트 랜치 드레싱

구조 분해 변수는 함수의 인자로 넘길 수도 있습니다.

var fullName = {
    firstName: '길동',
    lastName: '홍'
}

var citizen = fullName => console.log(`나는 대한민국의 ${fullName.firstName}입니다.`);

citizen(fullName);
나는 대한민국의 길동입니다.

위의 코드는 구조 분해를 사용하여 아래와 같이 더 간단하게 변경할 수 있습니다.

var fullName = {
    firstName: '길동',
    lastName: '홍'
}

var citizen = ({firstName}) => console.log(`나는 대한민국의 ${firstName}입니다.`);

citizen(fullName);
나는 대한민국의 길동입니다.

그렇다면 구조 분해의 장점은 무엇일까요? 구조 분해는 기존의 코드 보다 더 선언적입니다. 즉 코드를 작성한 사람의 의도를 더 잘 설명해줄 수 있습니다.

위의 코드에서는 구조 분해를 사용하여 fullName객체의 firstName만을 가져옴으로써 객체의 필드 중에서 firstName만을 사용한다는 사실을 직관적으로 알 수 있습니다.

또 객체 뿐만 아니라 배열에도 구조 분해를 사용할 수 있습니다. 아래와 같이 배열에는 대괄호([])를 사용하여 첫 번째 원소를 변수에 대입할 수 있습니다.

var [country] = ['대한민국', '미국', '영국', '이탈리아'];
console.log(country);
대한민국

위 코드는 지정된 변수에 배열의 첫 번째 원소를 대입합니다. 그러면 다른 위치에 있는 원소를 대입하려면 어떻게 해야 할까요? 그런 경우에는 콤마(,)를 사용하면 됩니다. 즉 무시하고 싶은 원소의 위치에 콤마를 넣어주는 방법으로, 이런 방식을 리스트 매칭이라고 합니다.

var [country, , , country2] = ['대한민국', '미국', '영국', '이탈리아'];
console.log(country, country2);
대한민국 이탈리아

객체 리터럴 개선

객체 리터럴 개선은 구조 분해와는 반대되는 기능을 제공합니다. 즉 객체 리터럴 개선은 구조를 다시 만들거나 여러 내용을 하나의 객체로 만들어줍니다.

객체 리터럴 개선을 사용하면 현재의 영역에 있는 변수를 객체의 필드로 묶을 수 있습니다.

var country1 = '대한민국',
    country2 = '이탈리아';

var countries = {country1, country2};

console.log(countries);
{country1: "대한민국", country2: "이탈리아"}

위의 코드를 실행해보면 countries라는 객체에 country1country2라는 필드가 들어있는 것을 확인할 수 있습니다. 다음과 같이 객체 내에 메서드를 만들 수 도 있습니다.

var country1 = '대한민국',
    country2 = '이탈리아',
    match = function() {
        console.log(`${this.country1}과 ${this.country2}는 2002년 월드컵 16강전에서 만났습니다.`);
    };

var countries = {country1, country2, match};

console.log(countries.match);
ƒ () {
        console.log(`${this.country1}과 ${this.country2}는 2002년 월드컵 16강전에서 만났습니다.`);
    }

이 때 위의 코드에서 match메서드를 정의할 때 변수에 this 키워드를 사용했다는 사실에 유의할 필요가 있습니다.

객체 메서드 선언 방법

객체의 내부에 메서드를 선언할 때 예전에는 function 키워드를 사용했습니다. 하지만 새로운 ES6 문법에서는 굳이 function 키워드를 사용할 필요가 없습니다.

// 예전 방식의 객체 메서드 선언
var car = {
    name: name,
    sound: sound,
    accelerator: function() {
        var accel = this.sound.toUpperCase();
        console.log(`${accel} ${accel} ${accel}`);
    },
    speed: function(mph) {
        this.speed = mph;
        console.log('속도(mph): ', mph);
    }
}
// 새로운 문법의 객체 메서드 선언
var car = {
    name,
    sound,
    accelerator() {
        var accel = this.sound.toUpperCase();
        console.log(`${accel} ${accel} ${accel}`);
    },
    speed(mph) {
        this.speed = mph;
        console.log('속도(mph): ', mph);
    }
}

어떤가요? 객체 리터럴 개선으로 인해 코드가 매우 간단해지고 입력해야 할 코드의 양도 줄어든 것을 확인할 수 있죠?

자바스크립트 프레임워크를 학습하다보면 이런 형태의 코드를 매우 자주 접하게 됩니다. 잘 익혀두시면 나중에 분명히 도움이 되겠죠?

스프레드 연산자

스프레드 연산자는 세계의 점()으로 이루어진 연산자로 몇 가지의 다른 역할을 담당합니다.

먼저 스프레드 연산자를 사용하여 배열의 내용을 조합해보겠습니다. 아래와 같이 두 개의 배열이 있을 경우 스프레드 연산자를 사용하여 두 배열의 모든 원소가 포함된 세 번째 배열을 만들 수 있습니다.

var arr1 = ['콜라', '사이다', '환타'];
var arr2 = ['맥주', '소주', '양주'];
var arr3 = [...arr1, ...arr2];

console.log(arr3.join(', '));
콜라, 사이다, 환타, 맥주, 소주, 양주

위의 코드처럼 스프레드 연산자를 사용하면 쉽게 배열에 포함된 모든 원소를 합칠 수 있습니다. 그리고 이때 스프레드 연산자는 원본 배열을 수정하지 않고 복사본을 만들기 때문에 원본을 재활용할 수 있다는 이점을 누릴 수 있습니다.

var arr1 = ['콜라', '사이다', '환타'];
var [last] = arr1.reverse();

console.log(last);
console.log(arr1.join(', '));
환타
환타, 사이다, 콜라

위 코드는 앞에서 학습한 구조 분해와 배열의 순서를 뒤집는 reverse 메서드를 사용하여 배열의 순서를 바꾸는 코드입니다. 그런데 문제는 reverse원본 배열을 변경해버리는 메서드라는 것입니다.

결과를 보면 arr1의 요소 순서가 바뀌어져 있다는 것을 알 수 있습니다. 만약 원본 배열을 유지해야 하는 경우라면 기존의 배열을 복사하고 복사한 배열을 조작하는 귀찮은 과정을 거쳐야 하겠죠? 하지만 스프레드 연산자를 사용하면 쉽게 해결할 수 있습니다.

var arr1 = ['콜라', '사이다', '환타'];
var [last] = [...arr1].reverse();

console.log(last);
console.log(arr1.join(', '));
환타
환타, 사이다, 콜라

기존의 배열을 대입하는 대신 스프레드 연산자로 복사본을 만드는 것으로 해결했습니다. 결과를 보면 원본인 arr1의 요소들이 그대로 있는 것을 확인할 수 있습니다.

앞에서 학습한 배열의 구조 분해와 스프레드 연산자를 사용하여 배열의 나머지 원소들을 얻을 수 도 있습니다.

var arr1 = ['콜라', '사이다', '환타'];
var [fist, ...rest] = arr1;

console.log(arr1.join(', '));
콜라, 사이다, 환타

스프레드 연산자와 배열의 구조 분해를 활용하면 다음과 같이 다소 복잡해보이는 로직도 간단하게 만들 수 있습니다.

아래의 코드는 열차의 시간표를 표시해주는 함수로 이 함수는 스프레드 연산자를 사용하여 배열을 인자로 받고 구조 분해 기능으로 각각의 변수를 저장한 뒤 각 변수와 배열의 정보를 표시해주는 기능을 구현하고 있습니다.

코드의 흐름을 잘 따라가면서 분석해보세요.

function trainTimeTable(...args) {
    var [start, ...remaining] = args;
    var [finish, ...stops] = remaining.reverse();

    console.log(`이 열차는 ${args.length}개의 도시를 운행합니다.`);
    console.log(`이 열차는 ${start}애서 출발하는 열차입니다.`);
    console.log(`이 열차의 목적지는 ${finish}입니다.`);
    console.log(`이 열차는 ${stops.length}개의 도시를 통과합니다.`);
}

trainTimeTable(
    '서울',
    '수원',
    '천안',
    '대전',
    '대구',
    '부산'
);
이 열차는 6개의 도시를 운행합니다.
이 열차는 서울애서 출발하는 열차입니다.
이 열차의 목적지는 부산입니다.
이 열차는 4개의 도시를 통과합니다.

 

스프레드 연산자를 객체에 사용하기

스프레드 연산자는 객체에도 사용할 수 있는데 객체에 사용하는 방법도 스프레드 연산자를 배열에 사용하는 방법과 비슷합니다.

var meal = {
    breakfast: '씨리얼',
    lunch: '햄버거'
}

var dinner = '스테이크';

var meals = {
    ...meal,
    dinner
}

console.log(meals);
{breakfast: "씨리얼", lunch: "햄버거", dinner: "스테이크"}

프라미스

프라미스는 비동기적인 이벤트를 다루는 방법입니다.

일반적으로 프로그램에서 비동기 요청을 보내면 둘 중의 하나의 결과를 얻게 됩니다. 바로 ‘원하는 방식 대로’ 완벽하게 동작하거나 ‘알 수 없는’ 오류를 발생시키는 것입니다.

프로그램은 다양한 유형으로 성공하거나 실패할 수 있고, 여러 유형의 오류가 발생될 수 있는데, 프라미스는 이렇게 여러가지 성공이나 실패의 경우를 보다 단순한 성공이나 실패로 환원시킬 수 있습니다.

프라미스 구현하기

우선 randomuser.me라는 api로 부터 데이터를 가져오는 비동기 프라미스 예제를 하나 만들어보겠습니다.

randomuser.merandomuser.me에서 배포하는 api로 가상 멤버의 이름, 전화번호, e-mail등의 더미 정보를 제공해주어 테스트를 위한 사이트 등을 만들때 유용하게 활용할 수 있습니다.

아래는 새로운 프라미스를 반환하고, 반환된 프라미스가 randomuser.me api에 요청을 보내는 코드입니다.

const getRandomUser = count => new Promise((resolves, rejects) => {
    const api = `https://randomuser.me/api/?nat=us?results=${count}`;
    const request = new XMLHttpRequest();
    request.open('GET', api);
    request.onload = () =>
        (request.status === 200) ?
            resolves(JSON.parse(request.response).results) :
            reject(Error(request.statusText));
    request.onerror = (err) => rejects(err);
    request.send();
});

코드를 작성하고 randomuser.me api에서 가져오고 싶은 멤버수getRandomUser에 전달하여 호출하면 프라미스를 사용할 수 있습니다.

프라미스가 성공한 경우 실행해야 할 작업은 then함수를 이용하여 프라미스의 뒤에 체이닝시켜주고 오류를 처리하기 위한 콜백도 함께 제공해줍니다.

getRandomUser(10).then(
    members => console.log(members),
    err => console.error(new Error('멤버 정보를 가져올 수 없습니다.'))
);
[{gender: "female", name: {…}, location: {…}, email: "charlotte.hughes@example.com", login: {…}, …}]

자바스크립트에서는 비동기로 데이터를 처리하는 경우가 많은데 프라미스는 이런 비동기 요청을 더 쉽게 처리할 수 있게 해줍니다.

특히 최근에는 노드를 이용한 자바스크립트 프레임워크를 많이 사용하는데, 노드에서도 프라미스 기능을 많이 사용하고 있습니다. 자바스크립트 프레임워크를 사용해야 하는 상황이라면 프라미스의 사용 방법을 정확하게 이해하고 있는 것이 좋습니다.

클래스

이전의 자바스크립트는 클래스가 존재하지 않았기 때문에 아래와 같이 함수를 정의하고 그 함수의 객체에 있는 프로토타입을 사용해 메서드를 정의하는 방법으로 클래스를 구현했습니다.

function Travel(destination, length) {
    this.destination = destination;
    this.length = length;
}

Travel.prototype.guide = function(){
    console.log( '이번 ' + this.destination + ' 여행은 ' + this.length + '일 간의 일정으로 진행됩니다.');
}

var europe = new Travel('유럽', 15);

europe.guide();
이번 유럽 여행은 15일 간의 일정으로 진행됩니다.

하지만 이런 방법은 전통적인 객체 지향 프로그래밍과는 다소 차이가 있는 낯선 방식이죠. 위의 방식에서 함수는 객체의 역할을 하고 상속은 프로토타입을 통해서 처리합니다.

다행히도 ES6에서는 클래스의 개념이 추가되었는데 아래와 같이 구현할 수 있습니다. 참고로 일반적인 클래스의 네이밍 규칙은 첫 글자를 대문자로 시작합니다.

class Travel {
    constructor(destination, length) {
        this.destination = destination;
        this.length = length
    }

    guide() {
        console.log( '이번 ' + this.destination + ' 여행은 ' + this.length + '일 간의 일정으로 진행됩니다.');
    }
}

어떤가요? 기존의 방식보다 더 가독성이 좋아지고 이해할 수 있는 코드가 되었죠? 이제 정의된 클래스를 새로운 인스턴스로 만들어서 사용하면 됩니다. 새로운 인스턴스를 만드는 것은 new 키워드를 사용합니다.

const myTrip = new Travel('태국', 7);
myTrip.guide();
이번 태국 여행은 7일 간의 일정으로 진행됩니다.

클래스는 만들고 나면 new를 호출하여 새로운 객체를 원하는 만큼 생성할 수 있고 다른 객체지향 프로그래밍 처럼 클래스를 확장할 수도 있습니다.

기존의 클래스인 부모 클래스또는 상위 클래스는 확장하여 새로운 자식 클래스또는 하위 클래스를 만들 수 있습니다.

확장한 새로운 클래스는 상위 클래스의 모든 프로퍼티 메서드를 상속합니다. 그리고 이렇게 상속한 프로퍼티나 메서드는 하위 클래스의 선언 안에서 변경이 가능합니다.

기존의 클래스를 추상 클래스로 사용할 수도 있습니다. 예를 들어 앞서 만들었던 Travel 클래스는 extends 키워드를 사용하여 프로퍼티를 추가하여 추상 클래스로 만들 수 있습니다.

class Expedition extends Travel {
    constructor(destination, length, tool) {
        super(destination, length);
        this.tool = tool
    }

    guide() {
        super.guide();
        console.log(`이번 여행에는 ${this.tool.join('와 ')}가 필요합니다.`);
    }
}

위의 코드에서 하위 클래스가 상위 클래스의 프로퍼티를 상속하는 간단한 상속 관계를 확인 할 수 있습니다. guide()는 상위 클래스에 있는 guide 메서드를 호출한 후 Expedition에 있는 추가 정보를 출력합니다.

위와 같이 상속을 사용해 만든 하위 클래스의 인스턴스도 new 키워드를 사용하여 인스턴스를 생성합니다.

const myTrip2 = new Expedition('에베레스트', 30, ['등산장비', '카메라']);
myTrip2.guide();
에베레스트 여행은 30일 간의 일정으로 진행됩니다.
이번 여행에는 등산장비와 카메라가 필요합니다.

클래스 상속과 프로토타입 상속

사실 ES6의 객체지향 프로그래밍은 완벽한 객체지향 프로그래밍이라고 할 수는 없습니다. ES6에서 class 키워드를 사용해도 실제적으로는 prototype을 사용하기 때문이죠.

앞에서 만든 Travel 클래스에 prototype을 연결하여 콘솔에 출력해보세요. 프로토타입에 포함되어 있는 생성자와 guide() 메서드를 확인할 수 있습니다.

console.log(Travel.prototype);
{constructor: ƒ, guide: ƒ}

ES6 모듈

ES6에서 모듈은 다른 자바스크립트 파일에서 불러와 활용할 수 있는 ‘재사용이 가능한 코드 조각’을 의미합니다.

최근까지 자바스크립트를 모듈화하기 위해서는 모듈의 임포트 익스포트를 처리할 수 있는 라이브러리를 활용해야 했습니다. 하지만 ES6에서 부터는 자바스크립트 자체에서 모듈을 지원하고 있어 더욱 편리하게 모듈을 활용할 수 있게 되었습니다.

ES6에서는 각각의 모듈을 별도의 파일로 저장하는데 저장된 모듈을 외부에 익스포트하는 방법은 두 가지가 있습니다.

첫번째 방법인 한 모듈에서 여러 자바스크립트 객체를 외부에 노출시키는 방법은 위와 같이 export를 사용하여 여러개의 객체를 외부에 노출시키는 방법으로 export를 사용하여 다른 모듈에서 사용할 수 있도록 함수 또는 객체, 변수 등을 외부에 익스포트 합니다.

// msg.js
export const print(msg) => log(msg, new Date());
export const log(msg, timeStamp) => console.log(`${timeStamp.toString()}: ${msg}`);

위의 코드는 print()함수와 log()함수를 외부에 익스포트하는 예제입니다. 이 경우 helper.js에 정의된 다른 선언은 로컬, 즉 해당 모듈의 내부에 한정됩니다.

두번째 방법은 모듈에서 단 하나의 타입(변수, 객체, 배열, 함수 등)만 노출시키는 경우입니다. 단 하나의 객체만 노출시키는 경우에는 export default를 사용합니다.

// trip.js
const myTrip = new Expedition('에베레스트', 30, ['등산장비', '카메라']);
export default myTrip;

위의 코드와 같이 export default는 오직 하나의 이름만 노출시키는 모듈에서 사용할 수 있습니다.

앞에서 살펴본 것 처럼 exportexport default모두 자바스크립트의 변수나 객체, 배열, 함수 등의 모든 타일을 외부에 노출 시킬 수 있으며, 노출된 타입은 import 명령을 사용하여 불러와 사용할 수 있습니다.

외부에 여러 타입을 노출한 모듈을 임포트하는 경우에는 아래와 같이 객체 구조 분해를 활용하여 불러올 수 있고, export default를 사용하여 하나의 타입만 노출하는 경우에는 이름을 사용하여 불러올 수 있습니다.

import {print, log} from './msg.js';
import myTrip from './trip.js'

print('메시지 print');
log('메시지 log');

myTrip.guide();

모듈을 임포트할 때 as 키워드를 사용하면 다른 이름을 부여할 수 있습니다.

import {print as msg, log as logMsg} from './msg.js';

msg('메시지 print');
logMsg('메시지 log');

만약 모든 모듈을 가져오고 싶다면 *를 사용합니다. *를 사용하면 다른 모듈에서 가져온 모든 타입을 사용자의 로컬 객체로 가져옵니다.

import * as funcs from './msg.js';

커먼JS

커먼JS노드에서 지원하는 일반적인 모듈 패턴입니다. 커먼JS를 사용하면 module.exports를 사용하여 자바스크립트 객체를 익스포트할 수 있습니다.

// msg.js
const print(msg) => log(msg, new Date());
const log(msg, timeStamp) => console.log(`${timeStamp.toString()}: ${msg}`);

module.exports = {print, log};

참고로 커먼JS는 import 문을 지원하지 않습니다. 대신 require함수를 사용하여 모듈을 임포트할 수 있습니다.

const {print, log} = require('./msg.js');

여기까지 최신 자바스크립트에서 지원하는 주요 기능들을 정리해보았습니다. 이미 기존에 자바스크립트를 사용하고 있었지만 아직 최신 자바스크립트 문법에는 익숙하지 않은 분들이라면 이번 기회에 최신 자바스크립트 문법을 사용해보시길 강력히 권해드립니다.

최근에는 이미 대부분의 브라우저에서 최신 자바스크립트를 지원하고 있고, 대표적인 자바스크립트 프레임워크인 뷰, 리액트, 앵귤러 등에서도 아무런 제한없이 사용할 수 있습니다.

최신 자바스크립트의 새로운 문법은 대부분 더 쉽고 간단한 코딩을 할 수 있도록 지원하는 문법입니다. 이를 통해 더 효율적이고 빠른 코딩을 할 수 있겠죠? 코딩 생산성이 획기적으로 상승하는 경험을 하게 될지도 모르겠네요. ^^

댓글 남기기