모바일 사파리 , 즉 아이폰의 웹브라우저에서 input요소를 이용해 UI를 개발하다보면 가끔가다 input 안에 shadow가 보인다던지. 양옆에 border가 생긴다던지 하는 의도치 않은 화면이 보이는 문제가 있다.

이건 브라우저 버그라기보다는 webkit-appearance 가 동작하기 때문이다. ( 아이폰이 단말에 맞춰서 강조 또는 ui적으로 보여줌 )

이걸 해결하기 위해서는 css 에 아래 코드 한줄이면 끝난다.

input {
-webkit-appearance: none;
}

해당 css 는 모바일 사파리 즉 아이폰에서만 발생하므로, 안드로이드 브라우저에서는 발생하지 않는다.

금년도 들어서 가장 해결하기 어려웠고.. 왜 그렇게 동작하는지 이해가 안갔던 이슈다.

상황은 다음과 같다.
1.parent.html 에서 child.html 을 오픈 함
2.child.html 은 특정 동작을 취하면 parent.html에 저장된 window.setAction()호출
3.window.setAction( jSonData ) 와 같이 인자로 오브젝트 데이터를 전달하게 된다.
4.전달된 데이터는 전역 변수( 해당 스코프 안에서 ) parent.html 에서 저장하고 있다가. 특정 동작이 실행되면 ajax로 백엔드에 전달

언뜻보면 간단한 흐름을 가진 구조다. 하지만. 3번과 4번에서 IE8&9에서 문제가 발생한다. 즉 jsonData가 전달 당시에는 제대로 전달이 되는것 같지만, ( 디버그로 확인해보면 전달 당시에는 정상적인 값이 출력된다. ) 사용하려고 하면 null 도 아닌 null같은 쓰레기 값이 들어가 있어서 그 동작은 정상적으로 수행되지 않는다.

문제를 좀 객관화 하자면 IE 8&9에서는 부모페이지와 자식 페이지 상의 전역함수로 호출할경우 파라미터에 복잡한 JSON형태의 데이터가 들어가면 해당 데이터를 직접 저장하는게 아닌. 데이터가 생성되었을때 기준으로 shallow copy를 해오는것 같다. ( 이부분이 맞는지는 사실 정확히 모르겠다.. 자료를 찾아봐도 없다. )

이상황에서 자식창이 종료가 되면 shallow copy된 데이터도 같이 날아가버리는 현상이 발생한다.

문제에서 사용했던 data는 아래와 같다.

"data":[{
  "key1":"value",
  "key2":"value",
  "keyList":[{
    "key3":"value",
    "key4":"value"
   }],
  "key5":"value"
  },
  {
  "key1":"value",
  "key2":"value",
  "keyList":[{
    "key3":"value",
    "key4":"value"
  }],
  "key5":"value"
 }];

아무런 문제가 없는 일반적으로 사용하는 이중 배열 형태의 데이터다. 단지 data[0].keyList 가 한번더 배열인형태로 좀 복잡할 수가 있지만 (?) 트리 형태의 구조를 표현할때는 저렇게 구조를 잡는게 맞다.

당연히 해당 데이터는 circular-reference 로 만들어진 것도 아니였다. 그래서 해당 문제를 해결 하기 위해 일단 문제를 단순화 했다.

위의 과정중에
3. window.setAction( jSonData ) 와 같이 인자로 오브젝트 데이터를 전달하게 된다.
jsonData 부분에 위에 명시했던 데이터를 객체 형식으로 저장해서 과정4번 과정까지 진행해봤다. ( 즉 아래와 같이..)

var jsonData =[{  "key1":"value",
  "key2":"value",
  "keyList":[{
    "key3":"value",
    "key4":"value"
   }],
  "key5":"value"
  },
  {
  "key1":"value",
  "key2":"value",
  "keyList":[{
    "key3":"value",
    "key4":"value"
  }],
  "key5":"value"
 }];
window.setAction( jSonData );

정말로 신기한건 위와 같이 하면 문제가 해결 된다는 것이다.

하지만 실제로 웹서비스에서 저런 정적인 데이터는 불가능하므로, 다시 변수로 저장을 시도해봤다. 4번의 페이지에서 전달받았을 당시에는 IE8에서 디버깅을 해보면 문제없이 전달이 되므로.. 해당 변수 데이터를 클로저로 저장도 해보고. localStorage 에 저장도해보는 작업을 해본다.

하지만 결론은 모두 다 null도 아닌 쓰레기 값(?) 이 저장되며 여전히 4번 순서에서 오류가 났다..

그러다가 4번의 전달받은 데이터를 JSON.stringify( jsonData)로 해서 스트링으로 저장도 시도 해봤지만. 해당 데이터를 JSON.parse( )로 다시 JSON으로 만들때 circular reference 오류가 발생했다.
( circular reference가 나는 이유는.. 해당 데이터가 정상적이지 않기 때문에..)

정말 이리저리 삽질을 진행하다가 결국에는 문제를 해결했다.
아래는 해결한 방법이다.

var jsonData =[{  "key1":"value",
  "key2":"value",
  "keyList":[{
    "key3":"value",
    "key4":"value"
   }],
  "key5":"value"
  },
  {
  "key1":"value",
  "key2":"value",
  "keyList":[{
    "key3":"value",
    "key4":"value"
  }],
  "key5":"value"
 }];
jsonData = JSON.stringify( jsonData );
window.setAction( jSonData );

즉 자식 페이지에서 부모 페이지로 전달할때 JSON.stringify()로  스트링화 해서 전달한후 다시 JSON.parse() 해서 사용했더니 정보도 저장이 잘되었고 4번순서의 ajax 요청에도 정상적으로 활용할 수 있었다.

* 참고로 JSON관련 라이브러리는 jquery-json 을 사용했습니다. ( 그래서 문제없이 IE에서 JSON라이브러리를 사용가능)

뭔가 해결은 냈지만.. 원인을 정확하게 파악을 못해서 여전히 찝찝하다.

그 이후에 여러가지 방법으로 찾아봤는데.
https://support.microsoft.com/en-us/kb/2711084
위와 같은 내용이 존재한다. 요약하자면

IE 8에서는 윈도우창을 열었다 닫을때 참조하는 순환참조형태의 배열이 있다면 메모리 누수현상이 발생함” 이다.

물론 내가 만든 저 배열의 순환참조는 없었지만. IE 8에서 메모리 누수 현상이 있기때문에 내가 만든 패턴도 저 이슈에 걸렸다고 생각이되는데. 정확한 이해가 되지 않는데 다시 말하자면 나는 순환참조 배열이 아니다..

결국 의문점이 더 해소가 되지 않아서 더 찾아봤다. IE 에서 메모리 누수현상은 꾸준히 지적되어왔으니 내가 겪은 이슈도 분명히 있을것이라 생각하면서..
여러 검색 끝에 아래와 같이 결과물이 나왔다. (  MSDN..)

https://msdn.microsoft.com/en-us/library/bb250448(v=vs.85).aspx
해당 글에는 공식적으로 메모리 누수 패턴에 대해 설명하고 있다.

1.Circular References : 순환참조
2.Closures: 클로져
3.Cross-Page Leaks: 크로스 페이지 누수
4.Pseudo-Leaks : 슈도 누수(?)

드디어 찾았다. 3번째 크로스 페이지 누수다.

원문은 다음과 같다.

Cross-page leaks are often very small leaks of internal book-keeping objects as you move from site to site.

즉 사이트에서 사이트간의 이동 할때 내부적으로 저장된 객체해서 메모리 누수가 종종 발생한다고 한다.  이것을 스트링으로 저장하고 활용했기때문에 해결 가능했던 것이고 IE 8&9에서는 해당 이슈가 지적되며 개발자가 이런 객체로 저장하고 활용하는것을 어느정도 방어적으로 코드를 작성해야 한다고 나와있다.

많이 돌아와서.. 결국 이슈를 해결하고 원인도 알았다. ( 사실 이 포스팅을 준비하면서 더 찾아보다가여기까지 결론에 왔다.. 원래는 “~~해서 해결했다. 그래도 모르겠다. ” 였는데..)

한줄로 요약하자

IE 8&9 에서 사이트간 이동할때 객체형태의 데이터를 전달할때는 신중해야한다. (메모리누수 현상 발생할수도 있음 )

추가 : 포스팅 하고 해당 페이지의 3번째 이슈에 대한 상세 설명을 보면 DOM과 객체간의 이슈를 중점적으로 다루고 있다. 다만 내  이슈의 경우에는 자식창이 종료되면서 자식창이 가지고 있던 객체가 가비지 콜렉터에 의해 지워지면서 발생하는 이슈인것 같다.

이놈의 IE8 은 제발 없어져야 (.. ) 할 브라우저이다.

웹개발시에 이미지가  base64 encode되어서 들어가는 경우도 사실상 존재하긴한다.

(물론.. 절대경로 형태로 특정 이미지를 load하는게 더 좋은 방법이다. )

금년도 진행했던 프로젝트에서 텍스트 에디터로 summernote 라는 오픈소스를 사용했는데. ( http://summernote.org/  ) 다른 텍스트 에디터에 비해 최신 브라우저 지원이 잘되어있으며, bower  와 같은 도구도 지원하고 ui도 필요한것만 있어서 아주 유용하게 사용했다. (라이센스도 MIT이고.)

다만 이미지 업로드를 붙히는 과정에서. 여력이 안되서. 초기버전에는 summernote에서 지원하는 base64 encode 방식으로 이미지 삽입 기능을 사용했는데.

해당 기능을 사용하면 아래와 같이 HTML에 삽입이 된다.

<img style=”width: 275px;” src=”data:image/jpeg;base64,/9j/…..M0gH//2Q==” data-filename=”3.jpg”>

물론 간단한 이미지를 올리고 표시하는 경우에는 별도의 이미지 업로드 서버가 없이 사용할 수 있어, 상당히 유용한데.

문제는 이 데이터가 증가하면 DB가 감당하기도 어렵고, 조회 할때 너무 많은 데이터를 불러와서 API형태의 웹페이지에서는 그렇게 좋은 방법 같지는 않다.

게다가 여기서 문제가 발생했다.

QA를 통해 IE8에서 해당 이미지가 안보인다고 이슈라이징이 되었는데..

문제는 내 개발 PC 는 IE10이고. 개발자도구로 IE8로 렌더링/브라우저 모드를 변경해도 해당 이슈는 재현되지 않았다.

그래서 여러방면으로 찾아본 결과 아래의 글을 찾기에 이른다.

http://stackoverflow.com/questions/10159500/internet-explorer-and-base64-image-display

즉 IE8 에서는 32KB이상의 base64 encode 이미지를 지원 하지 않는 다는 것이다.

base64이미지를 사용하려면 32KB보다 낮은 이미지를 사용해야 하는데 사실상 안전성을 위해 사용하지 않는 편이 더 좋은것 같다. (급할땐 어쩔수 없지만..)

이후에 해당 기능은 별도의 이미지 서버에 올려서 URL을 리턴받는 방식으로 개발 교체 되었고, 지금은 문제가 발생하지 않는다..(하지만 이미이전에 만들어놓은 데이터가 속을 썪이고.. 있다.. )

만약 base64 인코딩된 이미지로 HTML에 삽입해서 페이지를 개발했는데 IE8에서 이슈가 발생한다면 빨리 이미지업로드 서버구축후 ( 아니면 오픈된 다른것을 사용하던가. ) 해당기능은 교체해야 할 것이다.

 

 

자바스크립트 개발을 하다가 일정 수준 이상이 되면, 코드 가독성 및 더 좋은 패턴에 대해 연구 하게 되고, 널리 쓰이는 패턴을 내 코드에 사용하곤 한다.

물론 책으로도 대부분 접할수가 있으며JSHINT 나, JSLINT와 같은 정적 검사 도구로 체크하여 잘못된 패턴을 지양하고, 좋은 패턴을 사용한다.

그 중  자바스크립트에서 가장 중요한 패턴이라고 알려진 Literal 표기법에 대해서 알아보자.

본 포스팅에서는 우선 객체 선언에서만 다룰 예정이다.

우선 자바스크립트의 DATA Type은 총 7개가 있으며 아래와 같다.

  1. Boolean
  2. Null
  3. Undefined
  4. Number
  5. String
  6. Symbol (ES6 에서 추가)
  7. Object

위 리스트에서 6번까지는 primitive type 이며  Object 는 primitive가 아니다.  여기서 각 type별로 선언하는 여러가지 방법이 있지만 Object는 다음과 같이 주로 선언한다.

var obj1 = {};
var obj2 = new Object();

위 코드에서 obj1 과 obj2는 객체는 동일한 역할을 하게 된다. 즉 아무것도 없는 빈 객체를 생성해서, 향후 프로퍼티 또는 메소드를 추가할 수 있는 객체가 된다.

하지만 위 코드에서 좋은 패턴은 어떤 패턴일까?

주로 대부분의 포스팅이며, 책, 자료에서는 아래와 같은 방식을 좋은 패턴이라고 말한다.

var obj1 = {}; //good pattern

위 코드를 바로 literal 표기법이라고 한다. 간단하게 객체를 선언할 수 있으며 아래와 같은 코드에서는 가독성 또한 좋아진다.

//객체 생성과 할당 
var obj1 = {
  a: 1,
  b: 2
};
//객체 생성한 후 할당 
var obj2 = new Object();
obj2.a = 1;
obj2.b = 2;

그렇다면 단순히 가독성을 위해서 {} 를 new Object 에 비해 권장 하는 것일까?

이제 본론으로 들어가자

이 포스팅을 작성하면서, 갖고 있던 근원적인 물음은 다음과 같다.

1. {} 과  new Object()는  동일한 객체를 생성할까?

우선 Literal 기법과 new 기법으로 생성된 객체는 동일한 객체이다. ( 의미적으로 동일하다는 말이다. 참고로 String 은 다르다. ). 즉 같다. 

[참고]

var str1 = "";
var str2 = new String();
console.log( typeof str1 ); // "string"
console.log( typeof str2 ); // "object"

위 코드에서  str1 과 str2 는 다르다. str1 은 type이 string 이지만 str2는 object 이다.

[/참고]

1번의 물음이 해결되고 나서 다시 궁금한점이 생겼다.

2. 그렇다면 {}를 권장하는 이유는 무엇인가? ( 단순히 가독성때문에?)

사실 2번에 대한 이유는 몇가지 알고있었다. (대체적으로 가독성, 속도 정도 였고,  자바스크립트에서 내장함수가 override  되는 버그(?) )

하지만 뭔가 다른 이유가 있을 것이라 생각하며 문제를 하나씩 찾아보았다.

첫번째로 가독성이다. 

Literal 기법이 대체적으로 더 짧고 직관적이며 객체를 생성하기도 더욱 용이하다. 위에서 살펴 보았듯이 생성과 할당에 더 적합하다. 또한 가장 기본적인 데이터를 선언하는데 있어서 new 표현을 덜 쓰면서, 가독성도 더 좋아진다.

두번째는 속도다. 

이미 여러가지 실험을 통해 Literal기법이 속도가 더 빠르다는것이 증명되었다.  ( 약 12.14% 빠르다 )

http://jsperf.com/new-array-vs-literal/26

하지만 사실상 미미하며, 정말 복잡도가 높은 어플리케이션을 개발할 경우만 필요할 수도 있다고 생각한다. ( 개인적 의견 ) . 이전 브라우저에서는 많이 차이가 있었지만, 최근에는 그 격차가 많이 좁혀진게 아닐까 생각한다.

* 속도가 왜 빠른지는 나중 포스팅에서 다뤄보자.. 중요하다. 

마지막으로 세벗째는 Overriden에 따른 예방이다. 

우선 자바스크립트는 기본 함수 조차도 Overriden이 된다.  overriden이란 재정의란 의미로 Object 도 어떻게 보면 자바스크립트에서 함수이기도 한다. 하지만 이 함수를 재정의 할수 있다. 예제를 한번 만들어보자

Object = function(){ 
 alert("재정의"); 
};
var obj1 = new Object(); //alert 발생

즉 Object 란  내장함수 조차도 재정의 되어서 전혀 예상치 못한 결과를 초래할 수 있다.

이렇게 literal기법으로 선언 하는것이 더욱 효과적이며 해당 근거는 위에 3가지 정도로 요약할 수 있다.

별거 아닌거지만, 가장 기본적이며, 향후에 Array, String, Number 에대해서도 다뤄볼 예정이다. ( String은 또 다르다. )

 

자바스크립트를 공부하다보면, 변수, 함수, 객체, 프로퍼티 와 같은 단어를 많이 사용하고 접하게 된다. 처음 접할때는 변수 == 프로퍼티 라고 이해를 하게 되지만, 시간이 지나고 조금더 고민하다보면 뭔가 미묘한 차이들이 있다.

그래서 누군가 이 함수에서 변수는 무엇이고 프로퍼티는 무엇이냐 ? 라고 물어보게 된다면 더욱 더 헷갈리게 된다. 그래서 이번기회를 통해 조금 정리해보았다.

예를들어 아래와 같은 코드가 있다고 하자.

var a = "test";

일반적으로 변수는 var 로 선언을 하게 된다. 여기서 a 는 해당 스코프 안의 변수 이다. 절대 해당 스코프에서 프로퍼티라고는 하지 않는다.

하지만 만약에 다음과 같이 작성한다고 하자.

this.b = "test";

this를 통해서 b를 선언하게 되면, b 는 해당 객체의 프로퍼티가 되게 된다. 단순하게 var로 선언하면 변수, this 로 선언하면 프로퍼티 라고한다.

설명을 좀더 자세히 하기 위해 전체 코드를 작성해보겠다.

var setMyName = function( value ){
  var name = value;
  this.name = value;
};
var setName = new setMyName( "kazikai" );
console.log( setName.name ); //kazikai

위 코드에서 발생하는 name 은 var name 으로 저장된 값일까? this.name 으로 저장된 값일까?

정답은 this.name 으로 저장된 값이다.  이유는 setName 이란 변수는 setMyName의 인스턴스를 통해 만들어진 객체이며, 해당 객체에서 this.name 이라고 선언된 name은 인스턴스로 만들어진 객체의 프로퍼티가 된다.

조금 더 추가 설명을 하자면,

JavaScript에서는 private public 의 개념이 따로 존재하지 않는다.

Public이 외부에서 접근 가능하고 private이 내부에서만 접근가능한 변수라고 한다면,

this 는 public 이고, var는 private 한 변수라고 할 수 있다.

아래는 더글라스 크락포트가 작성한 private 에 관한 개념. 이 개념을 이해하면, 변수와 프로퍼티의 차이점을 더욱 더 자세히 이해할 수 있다.

http://javascript.crockford.com/private.html

배열 메소드 들중에.. 평소에 잘 사용하지 않지만, 사용하면 생산성이 많이 올라가는 메소드 들이다.

하지만 다시 리마인드 하는 의미로 정리하면, 도움이 될것 같다. 익숙한놈도 있고.. 초기에 JS 공부할때 알아뒀지만, 한번도 안써봤던,,(reduce) 같은 놈들은 다시 보니 어색 하기도 하고. 이번기회에 정리를 해보자

아래는 이 포스팅의 원문이 되는 블로그

http://colintoh.com/blog/5-array-methods-that-you-should-use-today?utm_source=javascriptweekly&utm_medium=email

 

1. 배열에서특정값이 있는지 찾을때는 indexOf 사용해라 

대부분의 배열에서 indexOf 를 사용 하지 않으면, for loop 를 돌려서 찾을수 밖에 없다. 즉 아래와 같은 코드를 사용 하게 되는데.

var isExist =false;
for( var i = 0; max = array.length; i += 1 ) {
 if( array[i] === "특정값" ){
   isExist = true;
 }
}

위와 같은 코드는 아래와 같이 간단하게 사용할 수 있다.

var isExist = (array.indexOf(“특정값”)!== -1 )

즉  간단하게 위의 for loop 를 한줄로 표현할 수 있으며, 이부분이 생산성이나, 코드 가독성에 도 옳은 영향을 끼친다.

2. filter 사용해보세요:)

이건.. JSON 오브젝트로 이루어진 배열중에 특정 값만 filter 시킬때 유용하다.

var arr = [    
  {"name":"apple", "count": 2},    
  {"name":"orange", "count": 5},    
  {"name":"pear", "count": 3},    
  {"name":"orange", "count": 16}
];    
var newArr = arr.filter(function(item){    
  return item.name === "orange";
});  
console.log("Filter results:",newArr);

위 값의 newArr 은 name이 orange 인 놈만 필터 되서 나온다.

3. forEach()쓰세요

사실 개인적으로 for 루프가 더 성능 향상에 좋다고 해서..( 자바스크립트 성능 최적화에 나온다. ).for 로 풀어서 쓰는데  1ms 도 차이 안나니 그냥 forEach를 쓰라고 가이드 한다.

사용법은 아래와 같다.

var array = [1,2,3,4,5];
array.forEach( function( v, i ){
  if( v === 3 ){
    console.log( v + ":" + i); // 3:2 가 나온다.
  }
});

4. map을 잘 활용하자. 

이전에 jsperf 에서 map이 forEach 보다 더 성능이 빨라서. 개인적으로 array에서 map을 주로 써왔다. 하지만 … map과 forEach는 용도가 미세하게 다른데.. map은 각 루프에서return을 할수있는 것이 특징이다.

( return된 결과의 동일한 배열을 원하는것이 아니라면 forEach가 더 적합하다. )

즉 map을 사용하면, 리턴값으로 같은 길이의 배열이 나오게 된다.

예를들어 …

var a = [1,2,3,4,5];
var b = a.forEach( function(v, i){ console.log(v); return v+1;}});
var c = a.map( function(v, i){ console.log(v); return v+1;});

를 실행해보면차이점이 다르다.  b는 undefined 가 나오며 c 는 [2,3,4,5,6] 이라는 배열이 나온다.즉 배열을 가지고, 연산을 하고 길이가 같은 배열로 결과를 리턴받고 싶을때는 map을 쓰는것이 훨씬 유리하다.

forEach와는 용도가 서로 다르다는것을 기억하자

마지막으로 5..

5.reduce!!  사용하자 

이놈은.. 인자를 어떻게 주냐에 따라 용도가 달라진다.

array.reduce(f,n)
var a= [1,2,3,4,5] //f는 함수 n은 두번째 인자 라고 가정
n이 없을경우 array.reduce(f);를 살펴 보자 
f의 
(0) iteration 
- 첫번째 인자 : 1
- 두번째 인자 : 2
(1) iteration
- 첫번째 인자 : 1,2
- 두번째 인자 : 3
(2) iteration
- 첫번째 인자 : 1,2,3
- 두번째 인자 : 4
(3) iteration
- 첫번째 인자 : 1,2,3,4
- 두번째 인자 : 5
로 나오게 된다.
즉 f 의 인자가 f( a,b,c,d) 라면
a=이전값
b= 현재값
c= 인덱스
d= 배열

로 생각하면 된다.

하지만 reduce 메소드를 불러올때  n을 주게 되면, f의 첫번째 인자가 n이 되게 되는데, 즉 이전초기값이 n으로 대체 된다. 

var a= ["a","b","c","d","e","a","b","a","c","c","c"];
var b = a.reduce(function(x,y){
  console.log("x:", x);
  console.log("y:", y);
  x[y] = ++x[y]|| 1;
  return x;
},{});
console.log(b);

위 예제를 돌려보면 B 는 최종적으로

Object {a: 3, b: 2, c: 4, d: 1, e: 1}

로 리턴된다. 

설명한 5개의 메소드들은  사용하면 개발 생산성 및 가독성이 좋아질 것 같다.

블로그를 드디어 워드프레스로 이사중인데.. 새로운 포스팅을 하기는 시간적 여건이 여의치 않고. 나름 해외 아티클을 정리해본 포스팅을 옮겨왔다.

——————————

첫번째 포스팅은 이전 티스토리에서 블로그를 운영중일 경우 썼던 포스팅이다.

javaScript를 배우고 사용하면서,  가장 신기했던 내장 함수중에 하나인 eval 에 대한 논의를 다룬  해외 아티클인

http://www.nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood/

에대한 내용을  참고 하여 정리 해보았다.

자바스크립트에서 대부분 eval 함수는 계륵 같은 존재 인것 같다. 이 함수에  대해서 “evil”이라고 비방 하고 있는데 .. 과연 이 놈이 evil 일까??(* 즉 eval함수는 쓰지 않는것이 보안 문제 에 좋다고 알고있으며 본인도 대부분 지양 해서 프로그래밍 중이다. )

이 간단한 함수는  string 으로 자바스크립트 코드를 실행 할수 있게 설계 되었다. 일반적으로 아래와 같은 내용을 eval로 처리할 수 있다.

ex:)

var arrayList = ["first","second", "third"];
var foo = {};
eval( "foo." + arrayList[0] + "=1" );
eval( "foo." + arrayList[1] + "=2" );
eval( "foo." + arrayList[2] + "=3" );
원하는 결과 
foo.first = 1;
foo.second = 2;
foo.third = 3;

물론 위와 같은 코드는 간단히 아래와 같은 코드 들로 해결할 수 있다.

foo[ arrayList[0] ] = 1;

다만 지금은 eval 로 위와 같은 처리를 할수도 있다는 걸로 넘어가자. 즉 스크립트 실행 단에서 변수를 선언하거나, 함수를 선언하는 동작을 할 수 있으므로, 상당히 강력한 기능이라고 할 수있다. 

하지만  “더글라스 크락포드” 에 의해 “eval() is evil ” 이란 구절이 생기면서 대부분의 자바스크립트 개발자는 (최근들어.._) 사용을 지양하고 있으며, eval 을 남발하는 개발자는 당연히 실력이 낮은 개발자 취급을 받고 있다.

우선 위에서 더글라스 크락포드가 뭐라고 말했는지 살펴보자

—————————————————————–

eval() 함수는 자바스크립트 컴파일러에 접근하는것을 제공 해준다( Function, setTimeout, setInterval 과 비슷함 ).하지만 대부분의 경우에서 나쁜 코딩에 존재 한다.eval 함수의 대부분은 javaScript 의 특징을 오용 하고 있다.

——————————————————————

즉 String 으로 자바스크립트를 작성한다음 eval( string ); 을 하면해당 자바스크립트가 실행 되는 아주 고마운(?) api라고 할 수있다.하지만 이 eval  이 잘못 사용 되고 있다는 말인데…그러면 여기서 의문점이 하나있다.

” 응 ? 그러면 eval을 잘 사용 하면 사용 해도 된다는 말인가? ”

그렇다 해당 블로그를 읽어 보면 eval() 함수는 “악” 이 아니라 단지 오해 일 뿐이라고 한다.

그렇다면 일단 eval() 함수가 “악” 처럼 보이는 이유에 대해서 알아보자 .

1. misuse : 오용/남용 

eval 함수를 나쁘다(악을 풀어서 나쁘다라고 적겠다.) 라고 말한 유래에는 대부분 자바스크립트란 언어에 대해서 잘 이해 하지 못한 사람들이 잘못 사용 하는것에 있다고 한다.

이것은 성능이나 보안 이슈와는 관계가 없다.

단지 자바스크립트에서 참조를 사용 하고 설계 하는것에 대해 잘 이해 하지 못해서 발생하는 현상이라고 한다.

##예제 코드 1 //잘못된 사용의 예

function isChecked( optionNumber ) {
  return eval ( "forms[0].option" + optionNumber + ".checked:" );
}
var result = isCheced( 1 );

위 코드를 보게 되면 개발자는 forms[0].options1.checked  란 것을 최종적으로 반환 받고 싶어 한하지만 eval()을 사용 하지 않고 이 값을 알수 있는 방법이 있다. 하지만 위와 같이 작성 대부분 eval()을 사용 하지 않고 알수있는 법을 모른다고 보여진다.

(블로그저자는 실제로 10년차 이상의 개발자 중에 저런 패턴의 코드를 쓰는 경우를 많이 볼수있다고 한다. ) ( 하긴 .. JavaScript 를 대충 사용 하다보면 저런 패턴을 사용하는 사람들이 보이기도 한다. )

eval()의 사용은 위 코드에서 적절치 못하며 불필요하다. 즉 위 코드는 나쁘다고 할수있다.

위 코드는 아래와 같이 쉽게 리팩토링이 가능하다.

##예제 코드  2 // 예제코드 1리팩토링

function isChecked( optionNumber ) {
  return forms[0]["options" + optionNumber].checked;
}
var result = isChecked(1);

 즉 eval()이 사용된 코드중에 대부분은 괄호표기법을 사용해서 대체가 가능하다. 위의 잘못된 사용이 대부분 크락포드가 잘못된 사용이라고 포함 했던 것들이다.

2. Debugability : 디버깅 

자 이제 eval()  의을 피해야 하는 좋은 이유중에 하나중 디버깅에 대해서 이야기를 꺼내보자

지금까지  eval()  코드 안에서 어떤 부분이 잘못되었는지 디버깅 하는것은 불가능해왔다.

즉 해당 코드는 블랙박스와 같다는 말이다.

크롬 개발자 툴에서는 eval() 코드를 현재 디버깅 할수있다고 하지만 ( 이부분은 반성해야겠다.. 몰랐다..).. 여전히 그 디버깅 과정이 고통 스럽다고 한다. ( 나중에 예시를 만들어봐야 하나..)

소스 패널에서 코드가 보이기 전에 한번 코드가 실행 될때까지 기다려야 한다.

(풀어서 말하자면 코드가 한번 실행 된다음 소스패널에 해당 코드가 보이고나서 디버깅 할수있다는말 )

즉 결론적으로 eval()된 코드를 사용 하지 않는것이 소스코드를좀더 단계적으로 쉽게 보일수있다는 말이다.  이것은 eval() 이 evil이란 말이 아니다. 단지 일반적인 개발 과정에서 있는 사소한 문제다

eval debug problem !== eval is evil

: 즉 위와 같다는 말이다. :)

3. Performance : 성능 

그다음 문제는.. 좀 중요한 문제라고 할수있다.

바로 eval()의 성능에 대한 문제다.

예전 버전의 브라우저에서는 eval()은 중복 Interpretation 패널티를 가졌다. 즉 코드가 한번 interpret 되고, eval() 이 한번더 interpret 가 된다는 말이다.

eval()이 한번더 interpret될때는 자바스크립트 컴파일링 엔진 없이 되기 때문에 브라우저에서 10배 정도 느려진다

최근의 자바스크립트 컴파일 엔진에서도 eval()은 여전히 문제를 내포 하고있다.

대부분의 엔진들은 2가지 방법으로 코드를 실행 하는데. “Fast path “, Slow path ” 로 돈다.

fast path코드는 안정적이고 예측 가능한 코드들이고 빠르게 실행을 위해 컴파일 된다. 하지만 slow path의 경우 예측 하기 힘들기 때문에 컴파일 하기 어렵기 때문에 여전히 interpreter에 의해 실행 된다.

(이부분은 아래의 링크를 봐두면 좋을것 같다. )

Know Your Engines by David Mandelin (SlideShare)

즉 eval()부분은 최근의 빠른  자바스크립트 엔진을 사용 하지 못하고 예전 브라우저 속도로 실행 된다는 말이다. ( 다시 말하자면 10 배 느리다. )그리고 또 문제가 있다. eval() 은 YUI Compressor 를 통해 변수 이름을 변경 하는게 불가능 하다.

eval()은 변수에 바로 접근 할 것이고 이것은 오류로 나타난다.다른 툴들도 마찬가지다 Closure Compiler나 UglifyJS 에서도 이 문제는 나타난다.그래서 eval()을 사용 할때는 성능에 관해서 여전히 심각한 문제들이 존재하고 있다.

하지만… 이렇다고  evil은 아니다. ( 거의 evil일듯…)

4. Security : 보안 

마지막으로 eval논쟁의 꽃 !! 보안 문제이다.이런 논쟁의 화두는 XSS 공격 에 취약하다는 것인데. eval() 이 코드를 오픈 시킬수있다는 것이다.표면적으로는 eval()이 정의한 코드가 제멋대로 실행 되는것에대한 우려는 있을만 하다고 보여진다.

(걱정 하는게 이해가 간다.)

이것은 만약에 사용자 입력을 개발자가 다루고 eval() 에 의해 이게 실행 된다면 위험하다고 할수있다. 그렇지만 만약에 사용자에 대한 입력을 다루지 않는다면 진짜 위험 할까?

ib/blob/master/src/css/PropertyValuePart.js#L145위 참조한 블로그의 저자는 최근에 eval()을 사용한 CSS Parser의 코드에 대한 complain을 받았다고 한다. ( 링크 : https://github.com/nzakas/parser-lib/blob/master/src/css/PropertyValuePart.js#L145 )

이 부분은 css 에 문자열 토큰을 변환 하기 위해 eval()을 사용 한 것인데  이 경우 어떤 사용 자도 해당 코드를 공격 하고나 문제를 일으킬수 없다고 판단 된다고 한다.

그렇게 주장하는 이유는 다음과 같다고 한다.

1. tokenizer 를 통해 전달된 값이 eval()된다.

2. tokenizer는 이미 해당 string이 유효한 값이라고 판단 한다.

3. 해당 코드는 커맨드 라인에서 자주 실행 되는 코드다.

4. 브라우저안에서 실행 될때 해당 코드는 클로저를 통해 보호되고 직접 호출이 불가능하다.

해당 코드를 직접 본게 아니기 때문에 맞는지 확신은 못하겠지만  어쨌든 보안에 대한 문제는 없다는 것으로 보인다.

결론적으로 eval()을 사용할때 사용자 입력에 관한  보안 이슈가 있는것은 맞다. 하지만 개발자가 사용자 입력을 eval에서 활용 하지 않고 사용자에 의해 해당 코드가(eval 에서 도는 코드)  변하지 않는다면 보안 위험은 없다고 판단된다.

(그리고 위 블로그 저자는 보안 이슈에 대한 우려가 있다면 외부 자바스큽트 로딩 코드에 대한 보안을 더 신경 써야 된다고 한다…)

5.Conclusion

결론적으로 eval()을 언제 어디에서나 사용하는것은 적절치 않다.그리고 eval()을 사용한 좋은 케이스도 있다!!( 물론, 여기에 code 투명성, 디버깅, 성능에대해서는 간과 하지 말아야 한다.)

그렇기 때문에 개발자는 eval()을 사용 하는것을 두려워 하지 말아야 한다.eval()을 쓰지않는것이 최선이 아니라 eval()을 적절하게 사용 하는것이 최선이라고 말할수있다.

 

- 개인적인 결론-

위에 모든 이슈를 다 따지면서 코딩하기는  초급 개발자 또는 중급 개발자도 힘든것 같다.가능한 eval()을 안쓰면서 구현 하도록 버릇이 들이고, eval()에 대한 예제를 몇개 만들어 보면서 정말 필요할때는 같이 프로젝트를 진행 하는 사람들의 동의를 얻어서 code에 추가하는게 좋을것같다.

게다가 eval 사용을 허용하게 되버리면, 해당 포스팅의 첫번째에 있는 말도안되는(?) 코드가 발생하기도 한다. ( 실제로 현업에서.. 같이 협력했던 외부직원의 코드에서 저런양식이 발견되어서 경악을 하기도 하였다.. 우리는 JSHint로 eval 사용을 막고있는데.. jshint 무력화 커멘트를 추가하여 eval을 사용하는 세심함까지도…)