Javascript 에서 지금도 보이는 개짓거리들
Tags: javascript, legacy, korean, webdev
Published: 2019-10-24 00:44:26

## `typeof(callback) === 'string'`

콜백을 문자열로 받는 미친놈들이 있다. 그리고 필연적으로 따라오는 함수. `eval`.
올해에 본 최고의 함수 내용을 지금 공개한다.

```js
function fn_exampleRequest(url, params, callback) {
  // ... 뭔가 좀 하냐?
  $.ajax({
    url: url,
    type: 'POST',
    data: fn_queryString(params),
    success: function(data){
        // ... data 에서 성공실패 대응
        eval(callback + '(data, something)');
    }, fail: function() {
       alert('넌 패배자야.');
    }, async: false
  });
}
```

그렇다. 내가 올해 본 함수 내용이 이렇다. 다시한번 강조하겠다. **올해**다.
게다가 이런 백투더 퓨처의 특징은 **비동기를 혐오하는 듯**한 메뉴얼이 눈에 띈다는 것.
이렇게 설계한 이유는 내 경험상 이렇다.

* 10여년 전으로 갑작스럽게 백투더퓨처 하신 공통모듈
* 지금도 X-Internet이라는 RIA 툴에서 스크립트 하신 공통모듈

아마 지금쯤 여러분은 `Arrow Function`을 아무렇지 않게 쓰며, `Promise`가 아예 문법으로 녹아들어 콜백 지옥은 이젠 역사가 됐을 것이라고 믿고 있을 것이다.
하지만 SI/SM 시장은 `Object.keys()` 함수만 써도 쓴소리가 날아오는 상황이다.

이런 꼬라지가 왜 발생했냐면, 역사는 이랬다.
옛날에는 버튼 클릭 이벤트를 거의 HTML 에서 정의했고

```html
<button onclick="someFunction">내 버튼<button>
```

그리고 이걸 함수로 구현했었으며, 게다가 IE 옛 버전 당시 `event`는 함수 인자가 아닌 전역 변수, 엄밀히 말하자면 전역(`window`)의 멤버 속성이었다는 것이다.
그렇다 보니, 지금은 구닥다리로 욕하고 있지만 당시엔 획기적인 개발 패턴이어서, XML 마크업으로 UI를 만들 때가 획기적이었던, 이걸 흉내낸 응용이나 RIA 툴이 꽤 있었으며, 이 옛날 답습을 그대로 따르는 것이다.

그리고 어자피 인터넷 브라우저는 IE가 아닌 브라우저를 쓰는 업무용 PC를 쓰는 건 드물기 때문에 안심하고 옛날 방식으로 코딩하는 이런 사단이 나고 있다.
참고로 필자 업무환경은 크롬만 쓴다. ㅋㅋ

이런 사단이 일어난 게 비단 `callback` 만은 아니다.
jQuery 초창기 획기적이었던 패턴인 **메소드 체인**을 모른다는 것이다. 이걸 또 굳이 프로시저 쓰듯이 순서대로 쓰는 개발자가 여전히 있다는 것.

## 전역 변수 `var`와 함께

요즘이야 전역 변수 사용을 거르고 전역 상수를 지정하려면 `const` 라도 쓰라고 가이드를 주는 시대다.

사실 단순히 `var` 로 전역변수 정의하는 것도 비추천인데, 이걸 심각하게 사용하는 예가 있다.
내가 간단한 예를 들도록 하겠다.

```js

var gridComp;

function fn_gridResize(elementId) {
  gridComp= $('#' + elementId);
  // ... 여기에 그리드 사이즈 초기화하는 코드
}

function fn_resize() {
  gridComp.width($(window).width());
  gridComp.height($(window).height());
}

$(document).ready(function(){
  $(window).resize(function(){
     fn_resize();
  });
});

```

내가 이런 코드를 봤다. 실제로 이딴 식의 코드다. 이게 공통 js 파일에 들은 내용의 일부다... 내용이야 인터넷 창 크기를 조절할 때마다 이벤트가 일어나서 그리드 사이즈도 따라서 조절하는 코드다.

내가 또 다시한번 강조해야 겠다. **올해 본 코드**다.

만약 여러분이라면 어떻게 할 것인가? 먼저 걷어내야 할 게 바로 전역 변수 `var`를 빼는 작업일 텐데, 그 전에 바로 업무상의 한계가 보일 것이다.
그렇다. 하나의 그리드만 적용 가능하고, 2개 이상의 그리드는 적용할 방법이 없다.
하지만 공통 개발자는 화면에 그리드 하나씩만 그릴 것으로 가정하고 이딴 식으로 코딩했다.
그리고 얼마 안 가 한계에 부딪히고, 나는 알바로 이걸 수정했다. 병신같은 패턴이긴 하지만 개발 중인 이 스크립트의 패턴을 함부로 깰 수는 없었고, array 로 일단 임시방편으로 그리드 2개 이상 대응은 가능하게 했다.

뭐 효율적인 대응은 개인마다 다르니 다양성을 존중하기 위해 언급하지 않겠다.
딱히 모던 코드 방식으로 어떻게 수정할 지 몰라서 그러는 게 아니다. 솔직히 귀찮다...

## 확장성이 없는 무작정 패턴

애초부터 SI/SM 시장에 공통모듈은 확장성을 생각하지 않고 단적인 목적으로 개발한다. 왜냐? 확장성을 고려할 시간이 주어지지 않기 때문이다. 물론 그렇다. 하지만 문제가 생기거나 한계에 부딪히면 땜빵으로 고치고, 또 수정하고, 추가하다 보면 결국엔 누더기 코드가 되기 마련이다.

그렇다 보니, 남에게 자신의 패턴을 강요하는 모듈이 나오기도 한다. 올해 본 코드 중 예제를 작성하도록 하겠다.

```js
// common.js script

var paramObj;

// 공통 ajax 요청 스크립트로 실무 개발자는 이거 사용.
function fn_commonRequest(varyObj, varyId, formId) {
  paramObj = {};
  paramObj.url = varyObj[varyId].url;
  paramObj.callback = varyObj[varyId].callback;
  paramObj.data = $('#' + formId).serialize();
  _fn_commonRequest();
}

// 공통 ajax 요청 내부 함수.
function _fn_commonRequest() {
  var paramUrl = paramObj.url;
  var paramCallback = paramObj.callback ;
  var paramData = paramObj.data ;
  // ...
  
  $.ajax({
    url: paramUrl, type: 'POST', dataType: 'json',
    data: paramData,
    success: function(data) {
      if(data.code == 'success') {
        eval(paramCallback + '(data.detail, "success")');
      } else {
        eval(paramCallback + '(data.detail, "fail")');
      }
      
    }, fail: function() {
       alert('오류. 담당자 확인 요망');
    }
  });
}

```

```js
// page.html script

// 페이미 마다 정의해야 할 전역변수
var pageObj = {

  memberList: {url: '/path/to/memberList.do', callback: 'listCallback', ...},
  memberView: {url: '/path/to/memberView.do', callback: 'viewCallback', ...},
  insertMember: {url: '/path/to/insertMember.do', callback: 'saveCallback', ...},
  updateMember: {url: '/path/to/updateMember.do', callback: 'saveCallback', ...}

};

// 초기화 함수
function fn_init() {

  $('#btnView').click(function(){
    // ...
    fn_commonRequest(pageObj, 'memberView', param);
  });

  $('#btnInsert').click(function(){
    // ...
    if(fn_commonValidate('frmInsert') == true) {
      if(confirm('저장하시겠습니까?')) {
        fn_commonRequest(pageObj, 'memberView', param);
      }
    }
    
    
  });

  // ...

}

// 콜백 함수들...

function listCallback(data, result) {
  // ...
}
function viewCallback(data, result) {
  // ...
}
function saveCallback(data, result) {
  // ...
}

```

내가 만약 이딴 프로젝트를 뛴다면, 공통 개발자 명치 한방 날리고 튀고싶을 정도의 공통모듈이다. 아니면 먹고살기 궁하다면 뭐... 나만의 공통 모듈을 만들던가...

사실 프레임워크라 함은, 공통모듈이라 함은, 개발자에게 생산성 향상을 위해 최대한 많은 경우의 수를 생각하고 공통적으로 업무에 대한 설계를 하는 중요한 역할이다. 그렇다 보니 실무 개발자들이 보기엔 공통은 언뜻 보면 땡보 개발자로 보이지만, 초창기엔 개고생하고 야근한다. 물론 후반엔 공통 오류수정만 하는 좀 땡보 비슷해서 PM 같은 인력관리 인사들이 대가리가 비면 프로젝트 막바지 다가갈 때 쫓아내는 경우도 있다. 하지만 공통 개발자라도 백단도 건드리는 풀스택이라... 아무리 공통이라도 구조조정이 필요할 정도로 심각한 상황이 아닌 이상 함부로 내쫓지 않는 인력관리자가 되도록 하자. 만약 쫓아내고 공통모듈 문제생기면 누가 수습하리? 스크립트만 문제생기나? 아니다. 백단도 문제생기기 마련. 실제 올해 내 지인 프로젝트에 공통 개발자 부재인 상태에 공통모듈 때문에 업무시험 때 실제로 사건 터지고 말아서 실무 개발자들이 밤샘으로 간신히 찾아냈다고 한다. 나는 10분만에 찾아낸 걸 그들은 3시간만에... 불쌍하기도 해라... (날짜계산 쪽에 원인이 있었다고)

하지만 상기 방식으로 제발 하지 말아야 할 패턴을 응용하여 공통모듈을 구성했다. 보기만 해도 암걸리지 않는가? 게다가 공통 개발자도 갔다? 암울하다.

제발 이렇게 개발할 거면, 공통은 그냥 업무에 도움을 주는 유틸리티 함수로 끝냈으면 좋겠다.

ECMA 5도 바라지도 않는다. 자바스크립트에 비동기 혐오하지? 이놈의 콜백. 응용은 더 심할텐데.
휴...

여러분, 이렇게 하지 마세요! 나중에 디버깅과 시점 맞추기 힘듭니다~ 메모리 누수는 덤.
그럼 대안을 줘야지? 하아... 자바스크립트 가이드 책도 있고... 최적화 가이드도 구글 검색하면 많다. 스코프와 클로저 등을 안다면 다행이지만 모른다면... 애도.

생각나면 이걸 좀 재대로 정리하도록 하겠다.
끗.