지난번 nextJS 포스팅에서 @vercel/postgres의 sql에 직접적으로 템플릿 리터럴을 넣으면
오류가 발생하는 부분에 대해 작성했었다.
일단 템플릿 리터럴도 그냥 되고, 편하니까 사용하고 있었는데 이왕 정식 명칭을 알았으니
이제부턴 있어보이게 템플릿 리터럴이라고 표현해야겠다.
(그전까진 그냥 백틱이라고만 표현했음..)
템플릿 리터럴
자바스크립트에서 문자열을 생성하는 방법중 하나.
``으로 감싸진 문자열로, 변수, 표현식을 ${}의 형태로 사용할 수 있다.
const name = 'John';
const greeting = `Hello, ${name}!`;
console.log(greeting); // 출력: "Hello, John!"
그렇다면 sql`템플릿 리터럴`의 형태로 사용하던 함수는 뭘까?(애초에 함수인지도 모르겠음)
일전에 styled-component를 사용할때도 styled.div`~`하는 식으로 사용을 했었었는데
그때는 딱히 이 표현에대해 궁금해하지 않았는데,
이번에 한번 더 마주치면서 궁금증이 생겨서 찾아보게 되었다
태그드 템플릿(Tagged Template Literals)
애초에 용어 자체를 모르니 검색도 못했던게 당연했고, MDN을 찾아보니 태그드 템플릿은,
template literals 의 더욱 발전된 한 형태는 tagged templates 입니다. 태그를 사용하면 템플릿 리터럴을 함수로 파싱 할 수 있습니다. 태그 함수의 첫 번째 인수는 문자열 값의 배열을 포함합니다. 나머지 인수는 표현식과 관련됩니다. 결국 함수는 조작 된 문자열을 반환 할 수 있습니다 (또는 다음 예제에서 설명하는 것과 완전히 다른 결과를 반환 할 수 있습니다). function 이름은 원하는 어떤 것이든 가능합니다.
아래와 같은 구조를 갖는다.
function myTag(strings, ...values) {
// strings: 템플릿 리터럴의 문자열 부분을 배열로 받음
// values: 템플릿 리터럴의 인터폴레이션된 값들을 배열로 받음
// 원하는 로직을 구현하여 처리
}
테스트를 위해 간단히 예제를 작성하고 각각의 인자가 어떤식으로 사용되는지 확인해보았다.
const test = (string, ...values) => {
console.log(string);
console.log(values);
}
let name = '은옥'
console.log(test`저는 ${name}입니다.`);
//['저는 ', '입니다.', raw: Array(2)]
//['은옥']
//undefined
문자열 부분과 변수 부분이 나뉘어서 나오는걸 확인 할 수 있었고,
함수 자체에서 리턴해주는게 없었기 때문에 undefined가 나오는걸 확인할 수 있었다.
변수 하나 더 추가한 뒤 기존 템플릿 리터럴이 반환하는것과 동일하게 만들어 보았다.
const test = (string, ...values) => {
return string.map((str, index) => {
return `${str}${values[index] || ''}`;
}).join('');
}
const name = '은옥';
const age = 33
console.log(test`저는 ${name}입니다. 나이는 ${age}살 입니다.`);
//저는 은옥입니다. 나이는 33살 입니다.
제법.. 번거롭다.
찾아보니 템플릿 함수 자체에서 원본 템플릿 리터럴 자체를 읽거나 접근하는 방법은 없다고 한다.
지난번 sql`${a}`의 형태를 테스트 해보았다.
const test = (string, ...values) => {
return string.map((str, index) => {
return `${str}${values[index] || ''}`;
}).join('');
}
const name = '은옥';
const age = 33
const text = `저는 ${name}입니다. 나이는 ${age}살 입니다.`;
console.log(test`${text}`);
//저는 은옥입니다. 나이는 33살 입니다.
?.. 잘되는걸.. 아 string과 values를 확인해보자.
const test = (string, ...values) => {
console.log(string);
console.log(values);
return string.map((str, index) => {
return `${str}${values[index] || ''}`;
}).join('');
}
const name = '은옥';
const age = 33
const text = `저는 ${name}입니다. 나이는 ${age}살 입니다.`;
console.log(test`${text}`);
//['', '', raw: Array(2)]
//['저는 은옥입니다. 나이는 33살 입니다.']
//저는 은옥입니다. 나이는 33살 입니다.
아하.. `${text}`자체가 values의 하나로 통채로 인식된듯하다.
이러면 지난번 sql`${query}`가 왜 오류가 났는지 알 것 같다.
이제 어느정도 알았으니 응용을 한번 해보자
const test = (string, ...values) => {
let result = string.map((str, index) => `${str}${values[index] || ''}`).join('');
let addText = values[1] > 19 ? '\n성인입니다.' : '\n미성년자입니다.';
return result + addText;
}
const personA = { name: '은옥', age: 33 }
const personB = { name: '옥', age: 13 }
console.log(test`저는 ${personA.name}입니다. 나이는 ${personA.age}살 입니다.`);
console.log(test`저는 ${personB.name}입니다. 나이는 ${personB.age}살 입니다.`);
// 저는 은옥입니다. 나이는 33살 입니다.
// 성인입니다.
// 저는 옥입니다. 나이는 13살 입니다.
// 미성년자입니다.
아하.. 잘된다.
굳이 ...values의 형태가 아니라 name, age의 형태로 인자를 넣어도 잘되는걸 확인했지만,,
굳이 이런식으로 할바엔 태그드 템플릿을 사용할 이유는 없어보인다.
음.. 추가적인 활용 예시는 현재 진행하고 있는 프로젝트들에 적용 해보면서
장단점을 한번 정리해봐야겠다.
끝.