template literal & 번외편(배열 메소드)
이 글은 [인프런] Javascript ES6+ 제대로 알아보기 - 초급(정재남)를 토대로 작성되었음을 알려드립니다.
ES5 에서는 홑따옴표, 쌍따옴표로 문자열을 표기(var a = 'abc';
)하였으나 ES6 에서 또 하나의 템플릿 문자열이 생겼습니다.
이번 장에서는 이 템플릿 문자열에 대해 알아보겠습니다.
템플릿 리터럴(Template Literal)
다음의 코드를 살펴보도록 합니다.
// 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
이라는 용어에 집중할 필요가 있는데 우선 다음의 코드를 살펴보고 왜 템플릿이라고 불리는 템플릿 리터럴을 만들었는지 살펴보자.
var a = 'abc\ndef'; // 기존의 문자열은 역슬래시를 사용하여 줄바꿈을 처리
var b = 'abc\n' + 'def';
console.log( a );
console.log( b );
var c = `abc
def`;
console.log( c );
출력 결과를 확인해 보면 템플릿 리터럴은 코드상의 줄바꿈 처리가 그대로 수행된다는 점입니다.
여기서 주의할 점은 빈 공백도 문자열이기 때문에 빈 공백도 모두 문자열로 인식하여 빈 공백이 생긴다는 아쉬운 점을 가지고 있습니다.
하지만 다음의 예제를 보면 템플릿으로서 중요한 기능을 가지고 있다는 것을 확인할 수 있습니다.
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) 의 값이 실제로 달러($)로 표기되어야 한다는 어떻게 해야할까요?
const str = `${a} + ${b} = $${ a + b }`;
중괄호({)가 바로 앞에 붙은 달러부터 인식하기 때문에 사용하는데 무리가 없을 것입니다.
그리고 이렇게 ${ }
사이에 값, 식을 정의할 수 있는 것을 string interpolation 이라고 합니다.
interpolation
이 단어를 찾아보면 [수학]보간법, 내삽법, 써넣음(써넣은 어구) 이라는 사전적 의미를 가지고 있습니다.
써넣음이라는 의미가 더 와닿겠지만 보간이란 뜻도 사이간, 보충할 보자로서 사이에 보충에서 넣는다는 의미를 가지고 있습니다.
즉, 글자 사이에 무엇인가를 보충해 넣겠다는 의미로 받아들 일 수 있습니다.
내삽 역시 안쪽에 삽입한다는 의미입니다.
string interpolation (문자열 보간법)
문자열(backtick) 안에 ${ } 로 변수를 삽입, 써넣겠다, 끼워넣겠다 라고 해석할 수 있습니다.
Detail Info
위에서 알아본 내용을 정리해 보고 추가적으로 더 살펴보도록 하겠습니다.
1) multi-line의 경우 들여쓰기에 주의
const f = function () {
const a = `abc
def
ghi`;
console.log(a);
};
f()
위 코드 출력을 확인해 보면 이쁘게 정렬되지 않으나 보간을 쓰기 위한 목적에 충실하다면 유용할 수 있습니다.
const f = function () {
const a = `abc ${10}\n` + `def\n` + `ghi\n`;
console.log(a);
};
f()
multi-line 을 사용하지 못하는 것이 아쉽지만 사용자 선택에 따라 활용하는 것이 나을 것입니다.
2) ${ }
내에는 `값` 또는 `식`이 올 수 있다.
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 처리가 된다.
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 처리
console.log(`Foo ${`Bar`}`); // ①
console.log(`Foo ${`Bar ${`Baz`}`}`); // ②
- ① 첫 백틱과 마지막 백틱이 쌍으로 이루고 ${`Bar`} 보간이 최우선되고 중괄호 안의 백틱이 처리된다.
- ② 보간 내에 보간으로 구성되어 있다.
5) 가독성을 위해 trim
처리
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 명세서의 메타 표기법이라고 합니다.
위에서 문법을 살펴본 후 다음의 예제를 보자.
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 객체
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 는 두번째 인자부터 배정된다.
다음의 예제를 통해 어떠한 동작을 하는지 알아보자.
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이 되는 것입니다.
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 문을 돌려서 무언가를 만드는 것이기 때문에 다음과 같은 문자열 조합도 가능할 수 있습니다.
const arr = [ 'a', 'b', 'c', 'd'];
const str = arr.reduce(function (res, item, index, array) {
return res + item;
});
console.log( str ); // abcd
좀더 응용해서 객체를 만들 수는 없을까?
예를 들어 reduce
를 활용하여 다음과 같은 객체를 만든다고 가정해 보자.
// 다음과 같은 배열이 있을 때
const arr = [ 'a', 'b', 'c', 'd'];
// 다음과 같은 객체를 만들고자 한다.
var obj = {
a : 'a',
b : 'b',
c : 'c',
d : 'd'
}
고민해 보고 실습해 보도록 하자.
reduce
메소드를 통해 다양한 조합이 가능하기 때문에 활용도가 높고 편하다고 하는데 예를 들어 다음과 같은 동작도 수행할 수 있습니다.
만약 배열에 1 ~10 까지의 숫자가 있을 경우에 모두 덧셈을 한다고 해보자.
아마도 for 문만 사용하는 개발자라면 다음과 같이 코드를 작성할 것입니다.
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 를 활용하면 훨씬 간단해 질 수 있습니다.
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) { });
생각해 보면 순서보다 항목이 더 중요하기 때문에 순수 자바스크립트는 배열의 순서보다 요소의 값을 먼저 사용하고 있지만,
제이쿼리는 혼동스럽게도 반대로 사용하고 있습니다.
Related Info