태그드 템플릿 리터럴
태그드 템플릿 리터럴이란?
간단히 말해, 템플릿 리터럴 앞에 함수(예: myTag)를 붙여서 문자열을 가공할 수 있는 기능
const name = "철수";
console.log(myTag`${name}입니다.`);
위 코드에서 myTag는 태그 함수이다.
즉, 템플릿 리터럴을 가공할 함수를 지정하는 것이다.
2. 태그드 템플릿 리터럴 동작 방식
일반적인 템플릿 리터럴(`${name}입니다.`)을 쓰면, 그냥 변수가 문자열에 삽입된다.
하지만 태그 함수가 붙으면, 문자열과 변수를 따로 전달받아 가공할 수 있다.
function myTag(strings, ...values) {
console.log(strings); // ['','입니다.']
console.log(values); // ['철수']
}
const name = "철수";
myTag`${name}입니다.`;
- string: 고정된 문자열 부분이 배열로 전달된다. [' ', '입니다.']
- values : 삽입된 값들이 배열로 전달된다. ['철수']
즉, 템플릿 리터럴이 문자열 조각(strings) + 변수 값(values)으로 나뉘어 태그 함수로 전달된다.
3. 예제 코드
function highlight(strings, ...values) {
return strings.reduce((prev, curr, i) =>
`${prev}<b>${values[i - 1] || ""}</b>${curr}`
);
}
const name = "철수";
console.log(highlight`${name}입니다.`);
highlight 함수 실행 과정
1) highlight(strings, ...values) 실행
템플릿 리터럴 ${name}입니다.가 highlight 함수에 전달된다.
이때, 문자열 부분(strings)과 변수 값(values)이 따로 전달된다.
전달되는 값
strings = ["", "입니다."]; // 템플릿 리터럴의 고정된 문자열 부분
//변수를 기준으로 앞 뒤로 구분 앞에 문자가 없으므로 첫번째 배열은 빈값
values = ["철수"]; // 변수 부분 (name의 값)
이렇게 분리되는 이유는 템플릿 리터럴이 태그드 템플릿 함수(highlight)를 만나면, 변수를 따로 추출하기 때문
2) string.reduce()실행
reduce()는 string배열을 순회하면서, 문자열을 조립하는 역할을 한다.
첫 번째 반복 (i = 0)
prev = "" // 초기값
curr = "" // 첫 번째 strings 요소 ("" - 비어 있음)
i = 0
values[i - 1] = values[-1] // values의 -1번째 배열은 존재하지 않으므로 undefined → 빈 문자열 "" 처리됨
두 번째 반복 (i = 1)
prev = "<b></b>" //누적 값
curr = "입니다." // 두 번째 strings 요소 ("입니다.")
i = 1
values[i - 1] = values[0] = "철수"
- 최종 결과: "<b></b><b>철수</b>입니다."
결과적으로 <b>철수</b>입니다. 가 출력된다.
즉, 템플릿 리터럴에서 변수 부분을 <b></b>태그로 감싸주는 코드이다.
여기서 앞부분에 <b></b>가 나오는 이유는 첫 번째 string 요소가 빈 문자열("")이기 때문이다.
즉, 변수 앞에 있는 빈 문자열도 <b></b>로 감싸지면서 처음에는 빈<b></b>가 추가된 것이다.
올바르게 동작하도록 수정하기
불필요한 빈<b></b>를 없애려면 첫 번째 루프에서 <b></b>태그를 붙이지 않으면 된다.
수정된 코드
function highlight(strings, ...values) {
return strings.reduce((prev, curr, i) =>
`${prev}${values[i] !== undefined ? `<b>${values[i]}</b>` : ""}${curr}`
);
}
const name = "철수";
console.log(highlight`${name}입니다.`);
출력
<b>철수</b>입니다.
4. 태그드 템플릿 리터럴의 활용
보다 일반적인 활용 예제를 보면 이해하기 쉽다.
1) HTML 생성
템플릿 리터럴을 이용해 HTML을 동적으로 생성할 수 있다.
function html(strings, ...values) {
return strings.reduce((prev, curr, i) =>
`${prev}<span style="color: blue;">${values[i - 1] || ""}</span>${curr}`
);
}
const user = "영희";
const age = 25;
console.log(html`이름: ${user}, 나이: ${age}`);
출력
<span style="color: blue;">영희</span>이름: , <span style="color: blue;">25</span>나이:
값에만 색상스타일 변경
2) SQL Injection 방지
태그드 템플릿을 활용해 SQL Injection 방지가 가능
function safeQuery(strings, ...values) {
return strings.reduce((prev, curr, i) =>
`${prev}${encodeURIComponent(values[i - 1] || "")}${curr}`
);
}
const userInput = "DROP TABLE users;";
console.log(safeQuery`SELECT * FROM users WHERE name = '${userInput}'`);
3) 다국어 지원 (i18n)
태그드 템플릿을 이용하면 다국어 지원을 쉽게 처리할 수 있다.
const translations = {
en: { hello: "Hello", world: "World" },
ko: { hello: "안녕하세요", world: "세계" },
};
function i18n(strings, ...values) {
const lang = "ko"; // 현재 언어 설정 (예: 'en' / 'ko')
return strings.reduce((prev, curr, i) =>
`${prev}${translations[lang][values[i - 1]] || values[i - 1]}${curr}`
);
}
console.log(i18n`${"hello"} ${"world"}!`);
// 출력: "안녕하세요 세계!"