JavaScript/ES6+

template literal & 번외편(배열 메소드)

jaiyah 2021. 2. 26. 18:12

이 글은 [인프런] Javascript ES6+ 제대로 알아보기 - 초급(정재남)를 토대로 작성되었음을 알려드립니다.


ES5 에서는 홑따옴표, 쌍따옴표로 문자열을 표기(var a = 'abc';)하였으나  ES6 에서 또 하나의 템플릿 문자열이 생겼습니다.

이번 장에서는 이 템플릿 문자열에 대해 알아보겠습니다.

 

 

템플릿 리터럴(Template Literal)

다음의 코드를 살펴보도록 합니다.

js
// ES5
var a = 'abc';
var b = "abc";

// ES6(es2015)
var c = `abc`;

console.log( a === b );
console.log( a === c );
console.log( b === c );
console.log( a === b && b === c );

위 코드에서 확인했다시피 키보드의 숫자키 1 바로 좌측에 있는  backtick ( ` ) 이라고 불리는 문자로 문자열을 정의할 수 있습니다.
ES6 에서 왜 굳이 기존의 문자열 리터럴(string literal) 방법을 두고 하나를 더 추가했을까요?

일단 template 이라는 용어에 집중할 필요가 있는데 우선 다음의 코드를 살펴보고 왜 템플릿이라고 불리는 템플릿 리터럴을 만들었는지 살펴보자.

js
var a = 'abc\ndef'; // 기존의 문자열은 역슬래시를 사용하여 줄바꿈을 처리
var b = 'abc\n' + 'def';
console.log( a );
console.log( b );

var c = `abc
def`;
console.log( c );

출력 결과를 확인해 보면 템플릿 리터럴은 코드상의 줄바꿈 처리가 그대로 수행된다는 점입니다.
여기서 주의할 점은 빈 공백도 문자열이기 때문에 빈 공백도 모두 문자열로 인식하여 빈 공백이 생긴다는 아쉬운 점을 가지고 있습니다.

하지만 다음의 예제를 보면 템플릿으로서 중요한 기능을 가지고 있다는 것을 확인할 수 있습니다.

js
const a = 10;
const b = 20;

// 기존 문자열 접합(What the..., Hell.. 드럽게 복잡합 ㄷㄷ)
const beforeStr = a + ' + ' + b + ' = ' + (a + b);
console.log( beforeStr );

// ES6 문자열 접합
const str = `${a} + ${b} = ${ a + b }`;
console.log(str)

템플릿 리터럴에서는 첫 번째  backtick 과 마지막  backtick 사이에  ${ } 에 변수를 정의할 수 있습니다.
즉,  backtick 사이에는 값이나 식을 정의(if 문과 같은 것은 제외)할 수 있습니다.
다시 말해, 값이 될 수 있는 것은 식이나 문이기 때문에 값, 식을 정의할 수 있으나 문 종류는 값이 될 수 없기 때문에 사용할 수 없습니다.
다시 생각해 보면 값을 출력하는 목적이 강한 측면이 있는 것입니다.
그래서  ${ } 에는 변수, 식, 함수의 호출, 삼항 연산자 등을 정의할 수 있습니다.

그런데 만약 위 코드에서 (a + b) 의 값이 실제로 달러($)로 표기되어야 한다는 어떻게 해야할까요?

js
 const str = `${a} + ${b} = $${ a + b }`;

중괄호({)가 바로 앞에 붙은 달러부터 인식하기 때문에 사용하는데 무리가 없을 것입니다.

그리고 이렇게  ${ } 사이에 값, 식을 정의할 수 있는 것을  string interpolation 이라고 합니다.

 

interpolation

이 단어를 찾아보면  [수학]보간법, 내삽법, 써넣음(써넣은 어구) 이라는 사전적 의미를 가지고 있습니다.
써넣음이라는 의미가 더 와닿겠지만  보간이란 뜻도 사이간, 보충할 보자로서 사이에 보충에서 넣는다는 의미를 가지고 있습니다.
즉,  글자 사이에 무엇인가를 보충해 넣겠다는 의미로 받아들 일 수 있습니다.
내삽 역시 안쪽에 삽입한다는 의미입니다.

 

string interpolation (문자열 보간법)

문자열(backtick) 안에 ${ } 로 변수를 삽입, 써넣겠다, 끼워넣겠다 라고 해석할 수 있습니다.

 

 

Detail Info

위에서 알아본 내용을 정리해 보고 추가적으로 더 살펴보도록 하겠습니다.

1)  multi-line의 경우 들여쓰기에 주의

js
const f = function () {
    const a = `abc
      def
      ghi`;

    console.log(a);
};
f()

위 코드 출력을 확인해 보면 이쁘게 정렬되지 않으나  보간을 쓰기 위한 목적에 충실하다면 유용할 수 있습니다.

js
const f = function () {
    const a = `abc ${10}\n` + `def\n` + `ghi\n`;

    console.log(a);
};
f()

multi-line 을 사용하지 못하는 것이 아쉽지만 사용자 선택에 따라 활용하는 것이 나을 것입니다.

 

2)  ${ } 내에는  `값` 또는  `식`이 올 수 있다.

js
 const counter = {
    current: 0,
    step: 1,
    count: function() { return this.current += this.step },
    reset: function() { return this.current = 0 }
};
console.log(`${counter.count()} ${counter.count()};
${counter.reset()} $${counter.count()}
${counter.count()}$`);

 

3) 결국 문자열이므로,  자동으로 toString 처리가 된다.

js
console.log(`${ [0, 1, 2] }`); // ①
console.log(`${ {a:1, b:2} }`);
console.log(`${ function(){ return 1 } }`);
console.log(`${ (function(){ return 1;})() }` + 1);

① 보간안에 값으로 배열이 올 경우 문자열로 표현하기 위해  toString 이 자동 호출된다.

 

4} 중첩된  backtick 처리

js
console.log(`Foo ${`Bar`}`); // ①
console.log(`Foo ${`Bar ${`Baz`}`}`); // ② 
  • ① 첫 백틱과 마지막 백틱이 쌍으로 이루고  ${`Bar`} 보간이 최우선되고 중괄호 안의 백틱이 처리된다.
  • ② 보간 내에 보간으로 구성되어 있다.

 

5) 가독성을 위해  trim 처리

js
function a () {
    return `
<div>
    <h1>Lorem ipsum.</h1>
</div>
  `.trim();
}

console.log(a());
console.log(a().replace(/\n/g, ''));

 

 

 

 

번외편 : forEach, map, reduce 메소드

배열을 다룰 때 유용한 메소드에 대해 알아보도록 하겠습니다.

 

forEach method

MDN - Array.prototype.forEach 는 반복문으로 아래 문법을 살펴보겠습니다.

 

Array.prototype.forEach(callback [, thisArg])

  • callback : function (currentValue[, index[, originalArray]])
    • currentValue : 현재값
    • index : 현재 인덱스
    • originalArray : 원본 배열
  • thisArg : this 에 할당할 대상. 생략시 global 객체

callback,  thisArg 라는 인자를 두 개 받는데  대괄호([])가 표기되어 있으면 생략이 가능하다는 의미로 즉, 선택적으로 사용할 수 있다는 뜻입니다.
이 말은  대괄호([])가 아닌  callback 은 필수적으로 사용해야 한다는 것입니다.

callback 인자를 살펴보면, 콜백은 함수를 사용하라고 정의되어 있으며 함수의 인자로는  currentValue,  index,  originalArray 를 사용할 수 있으며,  currentValue는 대괄호에 포함되어 있지 않으므로 필수적으로 사용해야 하며, 이 외의  index,  originalArray은 선택적으로 사용해도 되고 사용하지 않아도 된다는 의미입니다.
이러한 표기를  API 명세서의 메타 표기법이라고 합니다.

 

위에서 문법을 살펴본 후 다음의 예제를 보자.

js
const a = [ 1, 2, 3 ];
a.forEach(function (v, i, arr) {
    console.log(v, i, arr, this)
}, [ 10, 11, 12 ]);

const b = [ 1, 2, 3 ];
b.forEach(function (v, i, arr) {
    // 기타 목적으로 this 를 사용할 필요가 있을 경우에 활용
    // 일반적으로 많이 사용하지는 않음
    console.log( this[i] );
}, [ 10, 11, 12 ]);

 

 

map method

forEach 는  for 문을 사용해서 순회하는 것처럼 비슷한 개념이지만  map 은  for 문을 돌려서 새로운 배열을 만드는 목적을 가지고 있습니다.

[MDN - Array.prototype.map] 참고

 

Array.prototype.map(callback[, thisArg])

  • callback : function (currentValue[, index[, originalArray]])
    • currentValue : 현재값
    • index : 현재 인덱스
    • originalArray : 원본 배열
  • thisArg : this 에 할당할 대상. 생략시 global 객체

 

js
 const a = [ 1, 2, 3 ]
const b = a.map(function (v, i, arr) {
    console.log(v, i, arr, this);
    return this[0] + v
}, [ 10 ]);

map 은 새로운 배열, 아래에서 살펴볼 reduce 는 새로운 무언가를 만드는 것이기 때문에 반드시 반환(return)을 해주어야 합니다.

위 코드는 배열의 요소를 하나하나 순회하면서 즉, 함수가 세 번 실행되면서 반환된 값을 가지고 새로운 배열을 생성하게 됩니다.

 

 

reduce

reduce 는  for 문을 돌려서 최종적으로 다른 무엇인가를 만드를 목적을 가지고 있을 경우에 사용합니다.

[MDN - Array.prototype.map] 참고

 

Array.prototype.reduce(callback[, initialValue])

  • callback : function (accumulator, currentValue[, currentIndex[, originalArray]])
    • accumulator : 누적된 계산값
    • currentValue : 현재값
    • currentIndex : 현재 인덱스
    • originalArray : 원본 배열
  • initialValue : 초기값. 생략시 첫번째 인자가 자동 지정되며, 이 경우 currentValue 는 두번째 인자부터 배정된다.

 

다음의 예제를 통해 어떠한 동작을 하는지 알아보자.

js
const arr = [ 1, 2, 3 ];
const res = arr.reduce(function (p, c, i, arr) {
    console.log(p, c, i, arr, this);
    return p + c;
}, 10)

accumulator 는 누적된 계산값으로 위 예제에서  p 에는 처음 10이 들어오고  c 에는 1 이 들어오게 됩니다. 그에 대한 결과값이  return p + c 으로 반환되어 11 이 되고 다음 순회할 때  accumulator 인  p 로 들어가게 되고 11 과 다음  currentValue 인 2 와 더해서 13, 13 이 다시  p 로 들어가서  (13 + 3) 으로 최종 결과값은 16이 되는 것입니다.

 

js
 const arr = [ 1, 2, 3 ];
const res = arr.reduce(function (p, c, i, arr) {
    console.log(p, c, i, arr);
    return p + c;
});

console.log( res );

만약에 위 코드와 같이  initialValue 가 없다면  accumulator에 들어갈 첫 번째 값에 배열의 첫 번째 인덱스가 자동으로 들어가게 됩니다. 여기선 1 이 될 테고, 그러고 나서 순회는 2 번째 인덱스부터 순회하여 콘솔 결과가  1 2 1,  3 3 2가 출력되는 것입니다.

위에서 언급했 듯이  reduce 는  for 문을 돌려서 무언가를 만드는 것이기 때문에 다음과 같은  문자열 조합도 가능할 수 있습니다.

js
 const arr = [ 'a', 'b', 'c', 'd'];
const str = arr.reduce(function (res, item, index, array) {
    return res + item;
});

console.log( str ); // abcd

 

좀더 응용해서 객체를 만들 수는 없을까?

예를 들어  reduce 를 활용하여 다음과 같은 객체를 만든다고 가정해 보자.

js
 // 다음과 같은 배열이 있을 때
const arr = [ 'a', 'b', 'c', 'd'];

// 다음과 같은 객체를 만들고자 한다.
var obj = {
        a : 'a',
        b : 'b',
        c : 'c',
        d : 'd'
    }

고민해 보고 실습해 보도록 하자.

 

reduce 메소드를 통해 다양한 조합이 가능하기 때문에 활용도가 높고 편하다고 하는데 예를 들어 다음과 같은 동작도 수행할 수 있습니다.

만약 배열에  1 ~10 까지의 숫자가 있을 경우에 모두 덧셈을 한다고 해보자.

아마도  for 문만 사용하는 개발자라면 다음과 같이 코드를 작성할 것입니다.

js
const a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let res = 0;
for (let i = 0; i < a.length; i++) {
    res += a[i];
}
console.log( res );

위 코드는 reduce 를 활용하면 훨씬 간단해 질 수 있습니다.

js
const a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

const res = a.reduce(function (acc, crrValue) { return acc + crrValue; });
console.log( res );

 

 

순수 자바스크립트는 중요성이 높은 것 부터 사용합니다.

  • [1, 2, 3].forEach( function(item, index) { });
  • $('li').each( function(index, item) { });

생각해 보면 순서보다 항목이 더 중요하기 때문에 순수 자바스크립트는 배열의 순서보다 요소의 값을 먼저 사용하고 있지만,
제이쿼리는 혼동스럽게도 반대로 사용하고 있습니다.

 

 

 

 

Jaehee's WebClub