본문 바로가기

Javascript/Javascript

[Javascript] eval() 바로 알기

728x90
반응형

 

 

eval은 evaluate의 약자로 문자열이 코드라고 가정하고 평가해서 실행한다는 의미를 가지고 있다. 그 의미대로 문자열로 된 표현식을 파라미터로 받아 코드로 해석한 후 실행하는 역할을 한다.

 

따라서 내장 함수 eval()을 사용하면 문자열 형식으로 표현된 코드를 실행할 수 있다. 

let code = 'console.log("Hello World")';
eval(code); // Hello World

let value = eval('1+1');
console.log(value); // 1

let value2 = eval('let i=0; ++i');
console.log(value2) // 1

 

 value 의 예와 같이 길이가 긴 문자열이 코드가 될 수 있는데 여기에는 줄바꿈, 함수 선언, 변수 등이 포함될 수도 있다. 또한  value2 를 보면 마지막 구문의 결과가 eval의 결과가 되는 것을 알 수 있다.

 

 

eval로 둘러싼 코드는 현재 렉시컬 환경에서 실행되므로 외부 변수에 접근할 수 있다. 또한 외부 변수를 수정하는 것도 가능하다.

// 외부 변수 접근
let a = 1;

function fn() {
  let a = 2;
  eval('console.log(a)'); // 2
}

fn();


// 외부 변수 수정
let x = 5;
eval("x = 10");
console.log(x); // 10, 변경된 값

 

엄격 모드(use strict)에서 eval은 자체 렉시컬 환경을 가지고 있다. 따라서 eval 내부에 선언된 함수와 변수는 외부에서 읽을 수 없다.

'use strict'	// 엄격 모드

eval("let x = 5; function fn() {}");

console.log(typeof x); // undefined (없는 변수)
// 함수 fn도 읽을 수 없음

 

use strict가 적용되어 있지 않은 경우라면 eval은 자체 렉시컬 환경을 갖지 않기 때문에 외부에 있는 x와 f를 읽을 수 있다.

 

 

 


 

 

 

"eval is evil"

 

eval은 모던 프로그래밍에서 잘 사용되지 않는다. 오죽하면 "eval is evil"이라는 말이 있을 정도.

 

과거에는 자바스크립트가 지원하는 기능이 제한적이었기 때문에 eval을 사용해야만 처리할 수 있는 것들이 많았다. 하지만 그 이후로 시간이 흐름에 따라 자바스크립트는 강력한 언어로 변모해 왔다. 지금은 eval을 사용할 이유가 없다.

 

cf) 과거의 자바스크립트는 JSON.parse()처럼 문자열을 JSON 객체(혹은 객체)로 만드는 방법이 없었다. 이때 eval()을 사용하면 손쉽게 JSON 객체를 만들 수 있었던 것이다.

 

 

아니, eval은 사용하지 말아야 한다. eval을 사용하지 말아야 할 이유는 너무나도 많다.

 

 

1. 느리다

자바스크립트는 인터프리터 언어로 태어났다. 하지만 거듭된 개선을 통해 지금 현재 대부분의 자바스크립트 엔진은 내부에서 컴파일 과정을 거치게 되었고 엄청난 속도 개선을 이루어냈다. 이렇듯 최신 자바스크립트 엔진에서 여러 코드 구조를 최적화하는 것과는 달리 eval()을 사용하면 실행할 때마다 컴파일이 일어나고 무조건 인터프리터로 동작하게 되기 때문에 느릴 수밖에 없다.

또 한 가지. 최신 자바스크립트 인터프리터는 코드를 기계 코드로 변환한다. 즉 변수명의 개념이 완전히 사라지는 것이다. eval()을 사용하면 브라우저는 기계 코드에 해당 변수가 있는지 확인하고 값을 대입하기 위해 길고 무거운 변수명 검색을 수행해야 한다. 따라서 eval()의 영향으로 자료형 변경 등 변수에 변화가 일어날 수 있으며, 브라우저는 이에 대응하기 위해 기계 코드를 재작성해야 한다. 

 

 

2. 유지보수가 힘들어진다

위 예시들에서 보았듯 일단 eval을 사용하면 가독성이 많이 떨어진다. 뿐만 아니라 eval() 내부에서 외부 지역 변수를 사용하는 것은 코드 유지보수를 더 어렵게 만든다.

 

 

3. 보안상 문제

eval()을 사용하지 말아야 할 가장 큰 이유는 보안에 있다. eval()은 인자로 받은 코드를 호출부의 권한으로 수행하는 위험한 함수다. 이를 불순한 목적으로 사용한다면 어떤 문제를 일으킬지 예상할 수 없다.

// (A)
eval('location.href = "http://www.sniffing.com";')

// (B)
eval('ale' + 'r' + 't(\'' + 'hi there!' + '\')');

 

(A)와 같이 특정 웹 사이트로 이동하는 코드를 사용한다면? 정상적인 환경에서는 동작할 수 없는 코드지만 실행할 수 있는 환경이라고 가정한다면 큰 문제가 된다. 또한 eval()을 사용해 제한된 환경에서도 코드를 막 실행할 수 있는데 예를 들어 alert()를 차단하는 사이트가 있다고 한다면 이는 (B)와 같이 eval 블록으로 분리해 속일 수도 있다고 한다. 

 

또한 eval()은 실행할 코드가 신뢰할 수 없는 소스에서 제공되는 경우 보안 취약점을 초래할 수 있다. 외부입력으로부터 받은 문자열을 직접 eval()에 전달하면 악의적인 코드가 실행될 가능성이 있는 것이다.



4. 디버깅의 어려움

eval()을 사용하면 코드의 가독성이 떨어지고 디버깅이 어려워진다. String으로 취급되기 때문에 ide에서 제공하는 여러 문법 체크 기능에 걸리지 않을 것이고, 실행 시에 동적으로 생성된 코드는 정적인 코드와는 달리 디버깅 도구에서 추적에 어려움이 있다.

 

 

5. 가비지 컬렉션 관련

가비지 컬렉션은 eval로 쓰인 코드를 메모리에서 제거해도 되는지 모른다. 따라서 페이지가 새로고침되기 전까지 그 코드들을 처리하지 않고 계속 가지고 있게 된다.

 

 

6. 코드 압축률

eval()을 사용할 때는 항상 외부 변수 접근 시 사이드 이펙트가 발생할 수 있다는 우려가 있다. 보통 프로덕션 환경에서 애플리케이션이 배포되면, 자바스크립트로 구현한 코드는 코드 압축기 (minifier) 또는 번들러와 같은 도구에 의해 스크립트 크기를 작게 만든다. 이 과정에서 개발자가 선언한 변수명이 a, b와 같이 짧게 변환되는데 eval로 감산 코드에서 이러한 지역 변수에 접근할 수 있기 때문에 이러한 압축 방식은 eval()이 스크립트 내부에 있다면 안전하지 않다. 따라서 이러한 위험을 방지하기 위해 압축기는 eval() 내부 코드에서 접근할 가능성이 있는 모든 변수의 이름을 변경하지 않는다. 결국 코드 압축률에 부정적인 영향을 미치게 된다.

 

 

7. 굳이 쓸 필요가 없다

말 그대로 쓸 이유가 없다. 보안에 취약하고 성능도 저하시키는 데다 가독성도 떨어져 유지보수까지 어렵게 하고... 더군다나 일부 브라우저에서는 eval()을 사용하는 것에 대한 제약이 있을 수 있다(크로스 브라우징 이슈). 이렇게 위험 요소가 많은데 다른 로직으로 구현이 가능하다. eval()은 쓸 이유가 없다.

 

 

 

 

참고

https://velog.io/@modolee/javascript-eval-is-evil

https://blog.naver.com/dlaxodud2388/222164268980

https://ko.javascript.info/eval

728x90
반응형