
템플릿 엔진을 사용하지 않고 html에 데이터를 넣는 상황이다.
const indexTemplate = async (request: HttpRequest) => {
let contents = (await getFileContents(VIEW_PATH, INDEX_HTML)).toString();
if (isAuth(request)) {
const sid = getSID(request);
contents = contents.replace(
/\{\{user\}\}/g,
sessionStorage.storage[sid].nickname,
);
contents = contents.replace(/\{\{authBtn\}\}/g, logoutBtn);
} else {
contents = contents.replace(/{{user}}/g, 'Web');
contents = contents.replace(/\{\{authBtn\}\}/g, loginBtn);
}
return contents;
};
처음에 위와 같은 코드를 작성했는데 모든 변수를 일일이 작성해서 replace해주어야 해서 지저분해 보인다. 뿐만 아니라 html을 여러 번 읽고 다시 contents에 새로운 문자열을 만들어 저장해야 하므로 시간과 메모리 낭비가 될 수 있다.
위의 상황을 정규표현식의 캡쳐 그룹을 이용하여 html을 한 번만 읽고 데이터를 넣을 수 있도록 바꿔보았다.
const putTemplateData = (
fileContent: string,
data: Record<string, string>,
) => {
return fileContent.replace(/\{\{([^}]+)\}\}/g, (_: string, token: string) => {
if (token in data) {
return data[token];
} else {
return '';
}
});
};
const indexTemplate = async (request: HttpRequest) => {
const contents = (await getFileContents(VIEW_PATH, INDEX_HTML)).toString();
const data: Record<string, string> = {};
if (isAuth(request)) {
const sid = getSID(request);
data[templateVars.user] = sessionStorage.storage[sid].nickname;
data[templateVars.authBtn] = logoutBtn;
} else {
data[templateVars.user] = 'Web';
data[templateVars.authBtn] = loginBtn;
}
return putTemplateData(contents, data);
};
일단 'putTemplateData' 함수를 정의하여 인자로 file 내용과 data를 받았다. data는 html에 쓰여진 변수를 key로 하고 들어갈 데이터를 value로 하여 object를 만들었다.
replace 부분을 자세히 봐보자.
fileContent.replace(/\{\{([^}]+)\}\}/g, (_: string, token: string) => {
if (token in data) {
return data[token];
} else {
return '';
}
});
정규표현식의 캡쳐 그룹을 사용하여 일치하는 문자를 'token'으로 받고 있다. token을 이용하여 data의 값을 꺼내어 대체 문자열로 return해준다.
이렇게 하면 indexTemplate에서는 data object만 만들어주면 되고 putTemplateVars를 이용해 html에 한 번에 원하는 데이터를 넣을 수 있다. 여기서 포인트였던 캡쳐 그룹과 비캡쳐 그룹에 대해 알아보겠다!
캡쳐 그룹은 소괄호로 둘러쌓인 부분의 패턴이며 일치하는 텍스트의 일부를 추출하거나 그룹화하는 데 사용된다. 정규표현식을 사용했을 때 반환되는 값은 배열 형태이며 첫 번째 인자는 전체 문자열, 두 번째 부터 캡쳐되어 추출된 문자열들이 나열된다. 예시를 보자.
// generated by chatGPT
const text = "날짜는 2023-10-20입니다.";
const regex = /(\d{4})-(\d{2})-(\d{2})/;
const match = regex.exec(text); // 배열 반환
if (match) {
console.log("전체 매치: " + match[0]);
console.log("년: " + match[1]);
console.log("월: " + match[2]);
console.log("일: " + match[3]);
} else {
console.log("일치하는 패턴을 찾을 수 없습니다.");
}
위에서는 소괄호로 연도, 월, 일을 추출하고 있다. index 0은 전체 문자열, 이후 부터 추출된 문자열이 나열되어 있음을 알 수 있다.
비캡쳐 그룹은 그룹화를 수행하지만 일치된 부분을 캡쳐하지 않는다. 즉, 문자열을 추출하여 반환하지 않는다. 캡쳐 그룹과 같이 소괄호를 사용하지만 안에 ?:를 넣어 구분한다. 아래 예시를 살펴보자.
// generated by chatGPT
const text = "Hello World";
const regex = /(?:Hello) (World)/;
const match = regex.exec(text);
if (match) {
// 첫 번째 그룹은 캡처되지 않으므로, match 배열의 두 번째 요소에 "World"가 저장됨
console.log("전체 매치: " + match[0]);
console.log("두 번째 그룹: " + match[1]);
} else {
console.log("일치하는 패턴을 찾을 수 없습니다.");
}
정규표현식을 보면 캡쳐 그룹과 비캡쳐 그룹을 둘 다 사용하였다. 'Hello'는 비캡쳐 그룹이므로 배열에 반환되지 않고, 'World'는 캡쳐 그룹이므로 반환된 것을 확인할 수 있다.
https://blog.rhostem.com/posts/2018-11-11-regex-capture-group