옛날 코드를 생각하며.... 예전에 eval을 다루었던 적이 있었다. 뭐 대부분의 상황에서 eval은 백엔드에서 script를 전달하여, 클라이언트에서 사용 할 수 있게 만드는 역할을 하였다. 하지만, eval을 듣기만 해도 몸서리치는 사람들이 주변에 한 두명 정도는 있을 것이다.
가장 큰 문제는
코드 인젝션 : eval
은 전달된 문자열을 코드로 실행하기 때문에, 외부 입력을 eval
에 전달할 경우, 악의적인 사용자가 임의의 코드를 실행할 수 있는 보안 취약점이 생깁니다. 이는 XSS(Cross-Site Scripting)와 같은 공격에 취약하게 만듭니다.
신뢰할 수 없는 소스 : eval
에 의해 실행되는 코드가 신뢰할 수 없는 소스에서 온다면, 시스템이나 데이터를 손상시킬 수 있습니다.
위와 같은 문제를 꼽을 수 있다.
그렇다면, 어떤 대안이 있을까? 서버가 클라이언트에 어떤 행동을 하게 하기 위한 대안 말이다. 문자열로 스크립트를 바로 실행 하지 않고, 함수를 만들어내는 것이다.
당연하게도, new Function 키워드의 경우도 완벽한 보안을 제공하지는 않는다. 하지만, eval의 경우, 현재 스코프의 변수들을 접근 할 수 있는 반면, new Function은 글로벌함수만 접근 할 수 있고, 함수를 만들어내는 코드일 뿐, 실행에 대한 제어는 프런트에서 진행을 하게 된다.
일단 오해를 풀고가야 하는 부분이 있다. eval < new Function 상대적으로 보안에 좋다는 거지, 당연하게도 매우 취약하다. 과거의 우리는 어쩔 수 없이 사용하였을 뿐, 대체할만한 코드는 너무나 많고 많다.
하지만, 현대에 new Function이 갖는 의의는 서버에서 제공하는 문자열을 클라이언드에서 함수로 만들어서 실행 하는 것이 아니다!
템플릿 엔진
우리가 사용하는 .jsx에 대해 생각해보자, 자바스크립트에 그러한 문법이 있는가?
1번
const dom = <div>test</div>
자 위의 코드를 생각해보자, <div>태그로 시작하는 값이 있는가이다?
우리는 위의 코드를 보며, 아래의 내용으로 해석되어야 함을 알 수 있다.
2번
const dom = document.createElement('div');
dom.innerText = 'test'
자 그러면 loader는 1번 "const dom = <div>test</div>"문자열을 받아, 2번으로 만들어내어야 한다.
function createDOMFunction(code) {
// 정규식을 사용하여 태그와 내용을 추출
const regex = /<(\w+)>([^<]*)<\/\1>/;
const match = code.match(regex);
if (match) {
const tag = match[1]; // 태그명
const content = match[2]; // 태그 내용
// 동적 함수를 생성
const functionBody = `
const dom = document.createElement('${tag}');
dom.innerText = '${content}';
return dom;
`;
return new Function(functionBody);
} else {
throw new Error('Invalid code format');
}
}
// 사용 예제
const domFunction = createDOMFunction('<div>test</div>');
const dom = domFunction();
물론, 위의 예제만 가지고는 정확한 예제가 되지는 않을 수 있다. 내가 하고 싶은 말은 문자열을 가지고 어떠한 함수를 만들어내어야 한다. 이 관점을 보이고 싶은 부분이다.
function createTemplateFunction(code) {
// 정규식을 사용하여 {{test}}와 같은 패턴을 추출
const regex = /{{(.*?)}}/g;
const matches = code.matchAll(regex);
let interpretedCode = code;
// 각 매치에 대해 변수 값을 해석하여 코드를 변경
for (const match of matches) {
const variableName = match[1].trim(); // {{test}}에서 'test' 추출
interpretedCode = interpretedCode.replace(match[0], '${' + variableName + '}');
}
// 동적 함수를 생성
const functionBody = `
const template = \`${interpretedCode}\`;
return template;
`;
return new Function(functionBody);
}
function createDOMFunction(code) {
// 정규식을 사용하여 태그와 내용을 추출
const regex = /<(\w+)>([^<]*)<\/\1>/;
code = createTemplateFunction(code)();
const match = code.match(regex);
if (match) {
const tag = match[1]; // 태그명
const content = match[2]; // 태그 내용
// 동적 함수를 생성
const functionBody = `
const dom = document.createElement('${tag}');
dom.innerText = '${content}';
return dom;
`;
return new Function(functionBody);
} else {
throw new Error('Invalid code format');
}
}
let test = 1
// 사용 예제
const domFunction = createDOMFunction('<div>{{test}}</div>');
const dom = domFunction();
console.log('dom', dom)
조금만 더 추가를 해보면, 위의 머스타치 부분을 해석을 해낼 수 있게 만들어 낼 수 있다.
댓글
댓글 쓰기