JavaScript 템플릿 리터럴(Template Literals)과 태그드 템플릿(Tagged Templates)

Kingmo·2022년 3월 14일

ES6(ES2015)에서 도입된 템플릿 리터럴(Template Literals)은 자바스크립트의 문자열 처리 방식을 완전히 바꿨다.

1. 템플릿 리터럴 기본 문법과 표현식 삽입

템플릿 리터럴은 작은따옴표(')나 큰따옴표(") 대신 백틱(backtick, `)으로 감싸서 정의한다.

문자열 보간(String Interpolation)

${expression} 구문을 사용해 변수, 연산, 함수 호출 결과를 문자열 중간에 바로 삽입할 수 있다.

// ES5 이전: 더하기 연산자로 문자열 연결
var a = 20;
var b = 8;
var lang = "자바스크립트";
var str = "저는 " + (a + b) + "살이고 " + lang + "를 좋아합니다.";

// ES6 템플릿 리터럴: 표현식 삽입법
const strLiteral = `저는 ${a + b}살이고 ${lang}를 좋아합니다.`;
console.log(strLiteral); // "저는 28살이고 자바스크립트를 좋아합니다."

여러 줄 문자열(Multi-line Strings)

개행 문자(\n)와 + 연결 연산자 없이, 백틱 내부에서 엔터를 입력하면 줄바꿈이 그대로 문자열에 반영된다.

// 기존 방식
console.log("string text line 1\n" + "string text line 2");

// 템플릿 리터럴
console.log(`string text line 1
string text line 2`);

2. 템플릿 중첩(Nesting Templates)

삼항 연산자 등으로 조건에 따라 다른 문자열을 조합해야 할 때, 템플릿 리터럴 내부에 또 다른 템플릿 리터럴을 중첩하면 가독성을 크게 높일 수 있다.

// 1. ES5 방식: 복잡한 조건문과 문자열 연결
var classes = 'header';
classes += (isLargeScreen() ? '' : item.isCollapsed ? ' icon-expander' : ' icon-collapser');

// 2. 템플릿 중첩 미사용
const classes2 = `header ${isLargeScreen() ? '' : (item.isCollapsed ? 'icon-expander' : 'icon-collapser')}`;

// 3. 템플릿 중첩 활용: 공통 접두사 'icon-'을 유지하며 조건 분기
const classes3 = `header ${isLargeScreen() ? '' : `icon-${item.isCollapsed ? 'expander' : 'collapser'}`}`;

3. 태그드 템플릿 (Tagged Templates)

태그드 템플릿은 템플릿 리터럴을 함수의 인자로 파싱하여 전달하는 발전된 문법이다. 함수 이름 뒤에 소괄호 () 없이 백틱을 바로 붙여서 호출한다.

styled-component에서 사용하는 `styled.button...
문법이 태그드 템플릿으로 작동한다.

동작 원리와 인자 구조

태그 함수는 템플릿 리터럴을 두 가지 종류의 인자로 분해해서 받는다.

  1. 첫 번째 인자 (strings): ${} 표현식을 기준으로 쪼개진 순수 정적 문자열들의 배열
  2. 나머지 인자들 (...expressions): ${} 안에 들어간 동적 표현식의 평가값들
function myTag(strings, ...expressions) {
  console.log("문자열 배열:", strings);
  console.log("표현식 값들:", expressions);
}

const html = 5;
const css = 3;
const js = 'ES10';

myTag`HTML: ${html}, CSS: ${css}, JS:${js}`;
// 문자열 배열: ["HTML: ", ", CSS: ", ", JS: ", ""] (길이: 4)
// 표현식 값들: [5, 3, "ES10"] (길이: 3)

특징: 문자열 배열의 길이는 항상 표현식 개수 + 1이 된다. 마지막 템플릿 뒤에 아무 글자가 없더라도 끝에 빈 문자열("")이 포함된다.

실전 활용 예제: 데이터 유무에 따른 커스텀 파싱

태그 함수 내부에서 인자들을 직접 가공하여 조건부 텍스트 치환이나 데이터 검증을 일괄 처리할 수 있다.

const ramenList = [
  { brand: '농심', items: ['신라면', '짜파게티'] },
  { brand: '삼양', items: ['삼양라면', '불닭볶음면'] },
  { brand: '오뚜기', items: undefined } // 데이터 미입력 상태
];

function formatRamen(strings, brand, items) {
  // items가 없을 때 대체 문구 출력
  if (!items) {
    return `${brand}의 라면은 현재 재고가 없습니다!`;
  }
  return `${strings[0]}${brand}${strings[1]}${items.join(', ')}`;
}

console.log(formatRamen`구매 가능한 ${ramenList[0].brand} 제품: ${ramenList[0].items}`);
// 출력: "구매 가능한 농심 제품: 신라면, 짜파게티"

console.log(formatRamen`구매 가능한 ${ramenList[2].brand} 제품: ${ramenList[2].items}`);
// 출력: "오뚜기의 라면은 현재 재고가 없습니다!"

4. 원시 문자열 (Raw Strings)

\n, \t와 같은 이스케이프 시퀀스를 해석(줄바꿈 등)하지 않고, 작성된 문자 그대로 출력하고 싶을 때 사용한다.

String.raw 내장 태그 함수

const rawStr = String.raw`xy\n${1 + 1}z`;
console.log(rawStr); // "xy\n2z" (줄바꿈되지 않고 \n 문자 그대로 출력됨)

커스텀 태그 함수에서 raw 속성 접근

태그 함수의 첫 번째 인자인 문자열 배열에는 .raw 프로퍼티가 포함되어 있어, 해석되지 않은 원시 텍스트를 그대로 꺼내 쓸 수 있다.

function rawTag(strings) {
  return strings.raw[0];
}

const str = rawTag`Hello\nWorld`;
console.log(str); // "Hello\nWorld"
profile
Developer

0개의 댓글