aboutsummaryrefslogtreecommitdiff
path: root/files/ko/web/javascript/reference/global_objects/eval/index.html
blob: 000c8b2228bd8aa543dcadd17cafafa8deda1c09 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
---
title: eval()
slug: Web/JavaScript/Reference/Global_Objects/eval
tags:
  - JavaScript
  - Method
  - Reference
translation_of: Web/JavaScript/Reference/Global_Objects/eval
---
<div>{{jsSidebar("Objects")}}</div>

<div class="blockIndicator warning">
<p><strong>주의:</strong> 문자열로부터 <code><strong>eval()</strong></code>을 실행하는 것은 엄청나게 위험합니다. <code><strong>eval()</strong></code>을 사용하면 해커가 위험한 코드를 사용할 수 있습니다. 아래에 <a href="#eval을 절대 사용하지 말 것!">eval을 절대 사용하지 말 것!</a>을 확인하세요.</p>
</div>

<p><code><strong>eval()</strong></code>은 문자로 표현된 JavaScript 코드를 실행하는 함수입니다.</p>

<div>{{EmbedInteractiveExample("pages/js/globalprops-eval.html")}}</div>

<h2 id="구문">구문</h2>

<pre class="syntaxbox notranslate"><code>eval(<em>string</em>)</code></pre>

<h3 id="매개변수">매개변수</h3>

<dl>
 <dt><code>string</code></dt>
 <dd>자바스크립트 표현식, 명령문, 또는 연속되는 다수의 명령문을 나타내는 문자열. 표현식은 이미 존재하는 객체의 변수나 속성을 포함할 수 있습니다.</dd>
</dl>

<h3 id="반환값">반환값</h3>

<p>주어진 코드를 평가하여 얻은 값. 값이 없다면 {{jsxref("undefined")}}를 반환합니다.</p>

<h2 id="설명">설명</h2>

<p><code>eval()</code>은 전역 객체의 함수 속성입니다.</p>

<p><code>eval()</code>의 인자는 문자열입니다. 인자가 표현식을 나타낸다면 <code>eval()</code>은 표현식을 평가합니다. 인자가 하나 이상의 JavaScript 명령문을 나타낸다면 모두 실행합니다. 연산식을 계산하기 위해 <code>eval()</code>을 호출하지 마세요. 자바스크립트는 연산식을 알아서 계산합니다.</p>

<p>문자열로 연산식을 구성하면 나중에 <code>eval()</code>로 계산할 수 있습니다. <code>x</code> 라는 변수가 있다고 가정하면 <code>x</code>가 포함된 연산식을 문자열로, 예를 들어 "<code>3 * x + 2</code>"로 나타내고 나중에 <code>eval()</code>을 호출해서 계산을 연기할 수 있습니다.</p>

<p><code>eval()</code>의 인자가 문자열이 아니면 <code>eval()</code>은 인자를 그대로 반환합니다. 다음 예시에서, <code>String</code> 생성자가 명시된 경우 문자열을 계산하는 대신 <code>String</code> 객체를 반환합니다.</p>

<pre class="brush:js notranslate">eval(new String("2 + 2")); // "2 + 2"를 포함한 String 객체를 반환
eval("2 + 2");             // 4를 반환
</pre>

<p><code>toString()</code>을 사용하는 일반적인 방식으로 제약을 피할 수 있습니다.</p>

<pre class="brush:js notranslate">var expression = new String("2 + 2");
eval(expression.toString());            // 4를 반환
</pre>

<p><code>eval</code>을 직접 호출하지 않고 참조를 통해 <em>간접적으로</em> 사용한다면 <a href="http://www.ecma-international.org/ecma-262/5.1/#sec-10.4.2">ECMAScript 5부터는</a> 지역 범위가 아니라 전역 범위에서 동작합니다. 예를 들어 <code>eval()</code>로 함수를 선언하면 전역 함수가 되고, 실행되는 코드는 실행되는 위치의 지역 범위에 접근할 수 없습니다.</p>

<pre class="notranslate"><code>function test() {
  var x = 2, y = 4;
  console.log(eval('x + y'));  // 직접 호출, 지역 범위 사용, 결과값은 6
  var geval = eval; // eval을 전역 범위로 호출하는 것과 같음
  console.log(geval('x + y')); // 간접 호출, 전역 범위 사용, `x`가 정의되지 않았으므로 ReferenceError 발생
  (0, eval)('x + y'); // 다른 방식으로 간접 호출
}</code></pre>

<h2 id="eval을_절대_사용하지_말_것!"><a name="dont-use-it"><code>eval</code>을 절대 사용하지 말 것!</a></h2>

<p><code>eval()</code>은 인자로 받은 코드를 caller의 권한으로 수행하는 위험한 함수입니다. 악의적인 영향을 받았을 수 있는 문자열을 <code>eval()</code>로 실행한다면, 당신의 웹페이지나 확장 프로그램의 권한으로 사용자의 기기에서 악의적인 코드를 수행하는 결과를 초래할 수 있습니다. 또한, 제3자 코드가 <code>eval()</code>이 호출된 위치의 스코프를 볼 수 있으며, 이를 이용해 비슷한 함수인 {{jsxref("Global_Objects/Function", "Function")}}으로는 실현할 수 없는 공격이 가능합니다.</p>

<p>또한 최신 JS 엔진에서 여러 코드 구조를 최적화하는 것과 달리 <code>eval()</code>은 JS 인터프리터를 사용해야 하기 때문에 다른 대안들보다 느립니다.</p>

<p>추가로, 최신 JavaScript 인터프리터는 자바스크립트를 기계 코드로 변환합니다. 즉, 변수명의 개념이 완전히 없어집니다. 그러나 <code>eval</code>을 사용하면 브라우저는 기계 코드에 해당 변수가 있는지 확인하고 값을 대입하기 위해 길고 무거운 변수명 검색을 수행해야 합니다. 또한 <code>eval()</code>을 통해 자료형 변경 등 변수에 변화가 일어날 수 있으며, 브라우저는 이에 대응하기 위해 기계 코드를 재작성해야 합니다. 그러나, (다행히) <a href="/ko/docs/Web/JavaScript/Reference/Global_Objects/Function">window.Function</a>이라는 <code> eval</code>보다 훨씬 나은 대안이 있습니다. <code>eval()</code>을 사용하는 코드를 <code>Function()</code>으로 바꾸는 방법에 대해서는 아래를 참조하세요.</p>

<p><code>eval</code>을 사용하는 나쁜 코드:</p>

<pre class="brush:js notranslate">function looseJsonParse(obj){
    return eval(obj);
}
console.log(looseJsonParse(
   "{a:(4-1), b:function(){}, c:new Date()}"
))
</pre>

<p><code>eval</code>이 없는 코드:</p>

<pre class="brush:js notranslate">function looseJsonParse(obj){
    return Function('"use strict";return (' + obj + ')')();
}
console.log(looseJsonParse(
   "{a:(4-1), b:function(){}, c:new Date()}"
))
</pre>

<p>위의 두 코드는 얼핏 보면 같은 방식으로 실행되는 것처럼 보이지만, <code>eval</code>이 있는 코드가 훨씬 느립니다. 평가되는 객체의 <code>c: new Date()</code>를 주목하세요. <code>eval</code>이 없는 함수의 경우 이 객체는 전역 범위에서 평가되기 때문에 브라우저에서는 <code>Date</code>를 같은 이름의 지역 변수가 아니라 <code>window.Date</code>로 취급할 수 있습니다. 그러나 <code>eval()</code>을 사용하는 코드에서는 아래와 같은 경우도 존재할 수 있기 때문에 <code>Date</code>를 이렇게 취급할 수 없습니다.</p>

<pre class="brush:js notranslate">function Date(n){
    return ["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"][n%7 || 0];
}
function looseJsonParse(obj){
    return eval(obj);
}
console.log(looseJsonParse(
   "{a:(4-1), b:function(){}, c:new Date()}"
))
</pre>

<p>그러므로 <code>eval()</code>이 있는 코드의 경우 브라우저는 <code>Date()</code>라는 지역 변수의 존재를 확인하기 위해 무거운 변수명 탐색을 수행해야 하며, 이는 <code>Function()</code>과 비교하면 매우 느립니다.</p>

<p>만약 위의 상황에서 실제로 새로 선언한 <code>Date</code> 함수를 <code>Function()</code>에서 실행해야 하는 상황을 생각해 봅시다. 이렇게 되면 <code>eval()</code>로 돌아가야 할까요? 물론 아닙니다. 아래의 접근을 시도해 보세요.</p>

<pre class="brush:js notranslate">function Date(n){
    return ["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"][n%7 || 0];
}
function runCodeWithDateFunction(obj){
    return Function('"use strict";return (' + obj + ')')()(
        Date
    );
}
console.log(runCodeWithDateFunction(
   "function(Date){ return Date(5) }"
))
</pre>

<p>위 코드는 삼중 중첩 함수를 사용하기 때문에 매우 비효율적으로 보일 수 있지만, 이 방법의 이점을 우선 살펴봅시다.</p>

<p>1. <code>runCodeWithDateFunction</code>에 문자열로 전달된 코드를 최소화<sup>minify</sup>할 수 있다.</p>

<p>2. Function call overhead is minimal, making the far smaller code size well worth the benefit</p>

<p>3. <code>Function()</code><code>"use strict";</code>를 사용함으로써 코드의 성능을 최적화할 수 있다.</p>

<p>4. <code>eval()</code>을 사용하지 않으므로 실행 속도가 훨씬 빠르다.</p>

<p>마지막으로 코드 최소화의 측면에서 살펴보면, 위와 같이 <code>Function()</code>을 사용했을 때는 아래의 최소화된 코드와 같이 함수의 인자 이름 역시 짧게 줄일 수 있으므로 runCodeWithDateFunction에 전달하는 코드 문자열을 더욱 효율적으로 줄일 수 있습니다.</p>

<pre class="brush:js notranslate">console.log(Function('"use strict";return(function(a){return a(5)})')()(function(a){
return"Monday Tuesday Wednesday Thursday Friday Saturday Sunday".split(" ")[a%7||0]}));</pre>

<p>자주 쓰이는 용례에 대해서는 <code>eval()</code>이나 <code>Function()</code>보다 안전하고 빠른 대안도 존재합니다.</p>

<h3 id="객체의_속성에_접근하기">객체의 속성에 접근하기</h3>

<p>속성명으로 속성을 찾는 데 <code>eval()</code>을 사용해서는 안 됩니다. 다음 예제와 같이 코드를 실행하기 전까지는 접근할 속성을 알 수 없는 상황을 생각해 봅시다. 이 상황은 <code>eval</code>로 처리할 수 있습니다.</p>

<pre class="brush:js notranslate">var obj = { a: 20, b: 30 };
var propname = getPropName();  // "a" 또는 "b"를 반환

eval( "var result = obj." + propname );
</pre>

<p>그러나 여기에서 <code>eval()</code>을 쓸 필요가 없고, 지양되어야 합니다. 그 대신 훨씬 빠르고 안전한 <a href="/ko/docs/Web/JavaScript/Reference/Operators/Property_Accessors" title="JavaScript/Reference/Operators/Member_Operators">속성 접근자</a>를 사용하여야 합니다.</p>

<pre class="brush:js notranslate">var obj = { a: 20, b: 30 };
var propname = getPropName();  // "a" 또는 "b"를 반환
var result = obj[ propname ];  //  obj[ "a" ]는 obj.a와 동일함
</pre>

<p>이 방법으로 더 깊은 속성에도 접근할 수 있습니다. <code>eval()</code>을 사용한다면 다음과 같을 것입니다.</p>

<pre class="notranslate"><code>var obj = {a: {b: {c: 0}}};
var propPath = getPropPath();  // "a.b.c"를 반환한다고 가정

eval( 'var result = obj.' + propPath );</code></pre>

<p>여기서 <code>eval()</code>의 사용을 피하려면 속성 경로를 <code><a href="/ko/docs/Web/JavaScript/Reference/Global_Objects/String/split">split</a></code>한 다음 순서대로 접근할 수도 있습니다.</p>

<pre class="notranslate"><code>function getDescendantProp(obj, desc) {
  var arr = desc.split('.');
  while (arr.length) {
    obj = obj[arr.shift()];
  }
  return obj;
}

var obj = {a: {b: {c: 0}}};
var propPath = getPropPath();  // "a.b.c"를 반환한다고 가정
var result = getDescendantProp(obj, propPath);</code></pre>

<p>속성에 값을 대입하는 것도 비슷하게 할 수 있습니다.</p>

<pre class="notranslate"><code>function setDescendantProp(obj, desc, value) {
  var arr = desc.split('.');
  while (arr.length &gt; 1) {
    obj = obj[arr.shift()];
  }
  return obj[arr[0]] = value;
}

var obj = {a: {b: {c: 0}}};
var propPath = getPropPath();  // "a.b.c"를 반환한다고 가정
var result = setDescendantProp(obj, propPath, 1);  // test.a.b.c의 값은 1로 지정됨</code>
</pre>

<h3 id="단편적인_코드_수행_대신_함수_사용하기">단편적인 코드 수행 대신 함수 사용하기</h3>

<p>JavaScript의 <a href="http://en.wikipedia.org/wiki/First-class_function">함수는 1급 객체</a>이므로 다른 API에 함수를 인자로 전달할 수도 있고, 변수나 객체의 속성으로 대입할 수도 있습니다. 다수의 DOM API는 이 점을 염두에 두고 설계되므로, 다음과 같이 코드를 짜야 합니다.</p>

<pre class="brush: js notranslate">// setTimeout(" ... ", 1000) 대신에
setTimeout(function() { ... }, 1000);

// elt.setAttribute("onclick", "...") 대신에
elt.addEventListener("click", function() { ... } , false); </pre>

<p>또한 <a href="/en-US/docs/Web/JavaScript/Closures" title="JavaScript/Guide/Closures">클로저</a>를 이용해 문자열을 합치는 등의 연산 없이 매개변수화된 함수를 생성할 수 있습니다.</p>

<h3 id="JSON_파싱_문자열을_JavaScript_객체로_변환">JSON 파싱 (문자열을 JavaScript 객체로 변환)</h3>

<p><code>eval()</code>을 호출하려는 문자열에 코드가 아니라 데이터가 포함되어 있다면(예를 들어 <code>"[1, 2, 3]"</code>과 같은 배열), 대신 JavaScript의 문법 일부를 이용해 문자열로 데이터를 표현할 수 있는 <a href="/ko/docs/Web/JavaScript/Reference/Global_Objects/JSON" title="JSON">JSON</a>을 사용하는 것을 고려해 보세요. <a href="/en-US/docs/Downloading_JSON_and_JavaScript_in_extensions" title="Downloading_JSON_and_JavaScript_in_extensions">Downloading JSON and JavaScript in extensions</a>도 참고해 보세요.</p>

<p>JSON 문법은 JavaScript 문법에 비해 제약이 있기 때문에, 유효한 JavaScript 리터럴이 JSON으로 변환되지 않는 경우도 있습니다. 예를 들어, JSON에서는 배열이나 객체를 콤마로 끝낼 수 없고, 객체 리터럴에서 속성명(키)은 반드시  따옴표로 감싸야 합니다. 나중에 JSON으로 파싱할 문자열을 생성할 때는 JSON 직렬 변환기<sup>JSON serializer</sup>를 사용하여야 합니다.</p>

<h3 id="코드_대신_데이터_전달하기">코드 대신 데이터 전달하기</h3>

<p>예를 들어, 웹 페이지의 내용을 추출하는 확장 프로그램은 JavaScript 코드 대신 <a href="/ko/docs/XPath" title="XPath">XPath</a>에 스크랩 규칙을 정의할 수 있습니다.</p>

<h3 id="제한된_권한으로_코드_실행하기">제한된 권한으로 코드 실행하기</h3>

<p>제3자 코드를 실행해야 할 때는 제한된 권한으로 실행하는 것을 고려해야 합니다. 이는 주로 확장 프로그램이나 XUL 어플리케이션에 적용되며, 이때 <a href="/en-US/docs/Components.utils.evalInSandbox" title="Components.utils.evalInSandbox">Components.utils.evalInSandbox</a>를 사용할 수 있습니다.</p>

<h2 id="예제">예제</h2>

<h3 id="eval_사용하기"><code>eval</code> 사용하기</h3>

<p>아래 코드에서 <code>eval()</code>를 포함하는 문장은 모두 42를 반환합니다. 전자는 문자열 "<code>x + y + 1</code>"을, 후자는 문자열 "<code>42</code>"를 평가합니다.</p>

<pre class="brush:js notranslate">var x = 2;
var y = 39;
var z = "42";
eval("x + y + 1"); // 42를 반환
eval(z);           // 42를 반환
</pre>

<h3 id="eval을_사용해서_JavaScript_코드_문자열_평가하기"><code>eval</code>을 사용해서 JavaScript 코드 문자열 평가하기</h3>

<p>다음 예제에서는 <code>eval()</code>을 사용하여 <code>str</code> 문자열을 평가합니다. 이 문자열은 <code>x</code>가 5이면 로그를 출력한 다음 <code>z</code>에 42를 할당하고, 그렇지 않으면 <code>z</code>에 0 을 할당하는 JavaScript 코드입니다. 두 번째 문장이 실행되면, <code>eval()</code>은 이 문장의 집합을 수행하고, <code>z</code>에 할당된 값을 반환합니다.</p>

<pre class="brush:js notranslate">var x = 5;
var str = "if (x == 5) {console.log('z is 42'); z = 42;} else z = 0; ";

console.log("z is ", eval(str));</pre>

<p>여러 값을 정의할 경우 마지막 값을 반환합니다.</p>

<pre class="notranslate"><code>var x = 5;
var str = "if (x == 5) {console.log('z is 42'); z = 42; x = 420; } else z = 0;";

console.log('x is ', eval(str)); // z는 42, x는 420</code></pre>

<h3 id="마지막_표현식이_수행된다">마지막 표현식이 수행된다</h3>

<p><code>eval()</code> 은 마지막 표현식의 평가값을 반환합니다.</p>

<pre class="brush:js notranslate">var str = "if ( a ) { 1+1; } else { 1+2; }";
var a = true;
var b = eval(str);  // 2를 반환

console.log("b is : " + b);

a = false;
b = eval(str);  // 3을 반환

console.log("b is : " + b);</pre>

<h3 id="함수_정의_문자열로서의_eval_은_앞뒤를_와_로_감싸야_한다">함수 정의 문자열로서의 <code>eval</code> 은 앞뒤를 "("와 ")"로 감싸야 한다</h3>

<pre class="brush:js notranslate">var fctStr1 = "function a() {}"
var fctStr2 = "(function a() {})"
var fct1 = eval(fctStr1)  // undefined를 반환
var fct2 = eval(fctStr2)  // 함수를 반환
</pre>

<h2 id="sect1"></h2>

<h2 id="브라우저_호환성"><span style="">브라우저 호환성</span></h2>

<div class="hidden">The compatibility table on this page is generated from structured data. If you'd like to contribute to the data, please check out <a href="https://github.com/mdn/browser-compat-data">https://github.com/mdn/browser-compat-data</a> and send us a pull request.</div>

<p>{{Compat("javascript.builtins.eval")}}</p>

<h2 id="참고">참고</h2>

<ul>
 <li>{{jsxref("Global_Objects/uneval", "uneval()")}}</li>
 <li><a href="/ko/docs/Web/JavaScript/Reference/Operators/Property_Accessors">속성 접근자</a></li>
 <li><a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Content_scripts#Using_eval()_in_content_scripts">WebExtensions: Using eval in content scripts</a></li>
</ul>