게으른 개발자의 끄적거림

eval() vs new Function() 차이점

끄적잉 2024. 5. 29. 21:38
반응형

### JavaScript의 `eval()`과 `Function` 생성자: 차이점 및 상세 비교

JavaScript는 동적 코드 실행을 지원하는 두 가지 주요 방법을 제공합니다: `eval()` 함수와 `Function` 생성자. 이 두 방법 모두 문자열 형태로 전달된 코드를 실행할 수 있지만, 각각의 동작 방식, 사용 사례, 보안 및 성능에 대한 차이점이 존재합니다. 아래에서는 `eval()` 함수와 `Function` 생성자의 차이점을 상세히 비교하고, 각 방법의 장단점을 살펴보겠습니다.

 

 


#### 1. `eval()` 함수

`eval()` 함수는 JavaScript 코드 문자열을 인자로 받아 이를 실행하고, 실행 결과를 반환합니다. `eval()`은 전달된 문자열을 현재 스코프 내에서 실행하며, 이는 `eval()`이 호출된 위치의 변수와 함수에 접근할 수 있음을 의미합니다.

```javascript
let x = 10;
let y = 20;
let result = eval("x + y");
console.log(result); // 30
```

위 예제에서 `eval()` 함수는 `x + y`라는 문자열을 실행하여 `30`을 반환합니다. 이때 `x`와 `y`는 `eval()`이 호출된 스코프에서 정의된 변수입니다.

 

 


#### 2. `Function` 생성자

`Function` 생성자는 새로운 함수를 생성할 때 사용됩니다. `Function` 생성자는 함수의 매개변수와 함수 본문을 문자열로 받아들이며, 생성된 함수는 전역 스코프에서 실행됩니다. 이는 `Function` 생성자가 생성한 함수가 현재 스코프의 변수에 접근할 수 없음을 의미합니다.

```javascript
let x = 10;
let y = 20;
let func = new Function('a', 'b', 'return a + b');
let result = func(x, y);
console.log(result); // 30
```

위 예제에서 `Function` 생성자는 매개변수 `a`와 `b`를 받아들여 이 둘을 더하는 함수를 생성합니다. 이 함수는 전역 스코프에서 실행되기 때문에, 함수 내에서는 `x`와 `y`에 직접 접근할 수 없습니다. 대신, 매개변수로 전달된 값을 사용합니다.

 

반응형


#### 3. 주요 차이점

##### 스코프

- **`eval()`**: 현재 스코프에서 코드를 실행합니다. 이는 `eval()`이 호출된 위치의 변수와 함수에 접근할 수 있음을 의미합니다.
- **`Function` 생성자**: 전역 스코프에서 코드를 실행합니다. 함수 내부에서는 현재 스코프의 변수와 함수에 접근할 수 없습니다.

```javascript
let x = 10;
function testEval() {
    let y = 20;
    eval("console.log(x, y)"); // 10 20
}
testEval();

function testFunction() {
    let y = 20;
    let func = new Function('console.log(x, y)');
    func(); // ReferenceError: y is not defined
}
testFunction();
```

위 예제에서 `eval()`은 `testEval` 함수 내의 `y` 변수에 접근할 수 있지만, `Function` 생성자는 전역 스코프에서 실행되므로 `testFunction` 함수 내의 `y` 변수에 접근할 수 없습니다.

##### 보안

- **`eval()`**: 코드 인젝션 공격에 취약합니다. 사용자 입력을 `eval()`로 실행할 경우, 악의적인 코드를 실행할 위험이 있습니다.
- **`Function` 생성자**: 여전히 코드 인젝션의 위험이 있지만, 전역 스코프에서 실행되므로 상대적으로 더 안전합니다. 그러나 여전히 신뢰할 수 없는 소스에서 전달된 코드를 실행하는 것은 위험합니다.

```javascript
let userInput = "alert('Hacked!')";
eval(userInput); // 사용자 브라우저에서 alert 창을 띄움

let func = new Function(userInput);
func(); // 사용자 브라우저에서 alert 창을 띄움
```

위 예제에서 `eval()`과 `Function` 생성자 모두 사용자 입력을 실행하여 `alert` 창을 띄웁니다. 이는 두 방법 모두 신뢰할 수 없는 입력에 의해 악용될 수 있음을 보여줍니다.

##### 성능

- **`eval()`**: JavaScript 엔진의 최적화를 방해합니다. `eval()`은 문자열을 실행하기 위해 파싱하고 컴파일해야 하므로 추가적인 성능 부담이 발생합니다.
- **`Function` 생성자**: `eval()`보다 약간 더 나은 성능을 제공합니다. 그러나 여전히 문자열을 파싱하고 컴파일해야 하므로 성능 부담이 있습니다.

JavaScript 엔진은 정적 코드 최적화를 통해 성능을 향상시키지만, `eval()`이나 `Function` 생성자는 이러한 최적화를 방해합니다. 엔진이 코드 내용을 미리 알 수 없기 때문에 최적화 작업을 수행하기 어렵습니다.

##### 디버깅

- **`eval()`**: 디버깅이 어렵습니다. 문자열로 전달된 코드가 실행되므로, 디버거가 해당 코드를 추적하고 분석하는 데 어려움이 있습니다.
- **`Function` 생성자**: 디버깅이 비교적 쉬운 편입니다. 함수로 생성된 코드가 명확히 분리되어 있어, 디버거가 이를 인식하고 분석하기가 더 쉽습니다.

```javascript
try {
    eval("invalid code");
} catch (e) {
    console.log(e.message); // Unexpected identifier
}

try {
    let func = new Function("invalid code");
    func();
} catch (e) {
    console.log(e.message); // Unexpected identifier
}
```

위 예제에서 `eval()`과 `Function` 생성자 모두 잘못된 코드를 실행하려 하면 예외를 발생시킵니다. 그러나 `Function` 생성자로 생성된 함수는 명확히 구분되므로, 디버깅 도구가 이를 인식하고 분석하는 데 더 유리합니다.

 

 


#### 4. 사용 사례 및 대안

##### `eval()` 사용 사례

- **기존 레거시 코드**: 오래된 코드베이스에서 동적 코드를 실행해야 하는 경우.
- **동적 데이터 파싱**: JSON 포맷이 도입되기 전, 서버에서 전달된 데이터를 객체로 변환하기 위해 사용.

##### `Function` 생성자 사용 사례

- **동적 함수 생성**: 동적으로 함수를 생성하여 실행해야 하는 경우. 예를 들어, 템플릿 엔진이나 코드 생성기에서 사용될 수 있습니다.
- **독립된 코드 실행**: 전역 스코프에서 실행되어야 하거나, 다른 스코프와의 충돌을 피하기 위한 코드 실행.

##### 대안

- **정적 코드 사용**: 동적 코드를 피하고, 가능한 정적 코드를 사용하여 코드의 예측 가능성을 높입니다.
- **함수 분기**: 객체나 함수를 사용하여 동작을 분기합니다.

```javascript
let actions = {
    add: (a, b) => a + b,
    subtract: (a, b) => a - b,
};

let operation = "add";
let result = actions[operation](10, 20);
console.log(result); // 30
```

- **JSON 처리**: JSON 데이터를 파싱할 때는 `JSON.parse()`를 사용합니다.

```javascript
let jsonData = '{"name": "John", "age": 30}';
let obj = JSON.parse(jsonData);
console.log(obj.name); // John
```

#### 5. 결론

`eval()` 함수와 `Function` 생성자는 모두 JavaScript에서 동적 코드를 실행할 수 있는 강력한 도구입니다. 그러나 이 두 방법은 각각의 특성과 제한 사항을 가지고 있습니다. `eval()`은 현재 스코프에서 코드를 실행하는 반면, `Function` 생성자는 전역 스코프에서 코드를 실행합니다. 이로 인해 보안, 성능, 디버깅 측면에서 차이가 발생합니다.

일반적으로, 보안 및 성능 문제를 고려할 때 `eval()`과 `Function` 생성자의 사용은 피하는 것이 좋습니다. 대신, 정적 코드 사용, 함수 분기, JSON 파싱 등의 안전한 대안을 사용하는 것이 권장됩니다. 동적 코드 실행이 불가피한 경우에도, 입력 검증과 필터링을 통해 보안 위험을 최소화하는 것이 중요합니다.

이러한 이유로 대부분의 JavaScript 개발자들은 `eval()`과 `Function` 생성자를 신중하게 사용하며, 가능한 한 대체 방법을 통해 보다 안전하고 효율적인 코드를 작성하는 것을 선호합니다.

반응형