# Box 시스템 자바스크립트 리팩토링하기

개인적 기록용 포스팅.

# 원래 함수

Get해오는 옵션이 다를때마다 새로운 함수를 만들어 춘추전국시대가 되었다. 이를 하나로 합치기로 한다.

var apiCRUD = {
    getList: function($http, tab, rc, tableType, pagination, params) { // TODO: tab과 pagination 통합하기
        rc.loaded = false;
        var url = pagination || '/api/' + tab + '/';

        $http.get(url, { params: params }).then(function(res) {
            var colHeaders, columns;
            if(/^invoice\/\w+$/.test(tab)) { /*-- operation의 로그 목록 다루기 --*/
                tab = 'operation';
            }
            if(pagination) {
                var thisPage = /\?page=\d+/.exec(pagination);
                rc.pageNum = thisPage? thisPage[0].split('=')[1] : 1;
            }
            $('ul.pagination li.prev').data('page', res.data.previous);
            $('ul.pagination li.next').data('page', res.data.next);
            switch(tab) {
                case "stock":
                    colHeaders = ['ID', 'UID', '이름', '가용재고', '불용재고', '전체수량'];
                    columns = [
                        {data: "id", renderer: "html"},
                        {data: "uid"},
                        {data: "title"},
                        {data: "available_remains"},
                        {data: "disable_remains"},
                        {data: "all_remains"},
                    ];
                    break;
                case "invoice":
                    data = $.map(res.data.results, function (obj) {
                        obj.ordered_time = moment(obj.ordered_time).format('YYYY년 MMMM Do dddd a h:mm');
                        return obj;
                    });
                    colHeaders = ['ID', '상태', '종류', '시간', '물품', '개수'];
                    columns = [
                        {data: "id", renderer: "html"},
                        {data: "status_ko"},
                        {data: "kind_ko"},
                        {data: "ordered_time"},
                        {data: "stock.title"},
                        {data: "quantity"}
                    ];
                    break;
                case "operation":
                case "close":
                case "sell":
                    if(!pagination) {
                        rc.last_log_idx = rc.last_log_idx2;
                        rc.last_log_idx2 = res.data.results[0]? res.data.results[0].id : 999999;
                    }
                    rc.list = res.data.results;
                    break;
            }
            if(tableType === 'handson') {
                var data = $.map(res.data.results, function (obj) { // id를 누르면 디테일 페이지로 가도록
                    var url = '#'+tab+'/'+ obj.id + '/';
                    obj.id = "<a href='" + url + "'>" + obj.id + "</a>";
                    return obj;
                });

                var listTable = new Handsontable(document.getElementById('listTable'), {
                    data: data,
                    rowHeaders: true,
                    colHeaders: colHeaders,
                    columns: columns
                });
            }
            rc.loaded = true;

        }, function() {
            alert('리스트를 불러오지 못했어요. 새로고침을 해주시겠어요?');
            rc.loaded = true;
        });
    },
    // TODO: 심플리스트랑 그냥리스트랑 합치기
    getSimpleList: function($http, tab, rc, uid) {
        $http.get('/api/' + tab + '/', {
            params: {
                simple: true
            }
        }).then(function(res) {
            console.log("SIMPLE LIST", tab, res.data);

            $("#table_search").DataTable(); /*-- DataTable 활성화 --*/
            switch(tab) {
                case "retailer":
                    rc.retailerList = res.data;
                    break;
                case "stock":
                    if(uid) {
                        rc.stockList = res.data; // operation의 작은 리스트
                    } else {
                        rc.list = res.data; // 물품 검색의 리스트
                    }
                    break;
            }
            var data = $.map(res.data, function (obj) {
                obj.text = obj.text || uid? obj.title+' '+obj.uid : obj.title; /*-- uid가 true면 title이랑 uid랑 합쳐 보여주기 --*/
                return obj;
            });
            $("."+tab+"-select").select2({
                placeholder: '제품 선택',
                data: data
            });
        }, function() {
            alert('리스트를 불러오지 못했어요. 새로고침을 해주시겠어요?');
        });
    },
    getListByDate: function(begin, end, $http, tab, rc, tableType) {
        var params =  {
            begin: begin,
            end: end
        };
        if(tableType === 'handson') {
            $('#listTable .handsontable').remove(); // TODO: 기존 테이블 지우지 않고 데이터 갈아채기
            apiCRUD.getList($http, tab, rc, 'handson', false, params);
        } else {
            apiCRUD.getList($http, tab, rc, 'simple', false, params);
        }
    },
    getItem: function($http, tab, rc, stockId) { // TODO: 이것도 합치기
        $http.get('/api/' + tab + '/'+stockId + '/').then(function(res) {
            var remains = res.data.retailer_remains;
            if(remains && remains.length !== 0) {
                remains.sum = remains.reduce(function(p, c) { return p.remains + c.remains; });
            }
            console.log("DETAIL", res.data);
            rc.detail = res.data;

        }, function() {
            alert('정보를 불러오지 못했어요. 새로고침을 해주시겠어요?');
        });
    },
};

노답 구조다...ㅎㅎㅎ;; getList, getSimpleList, getListByDate, getItem이라고 비슷한 일을 하는데 함수가 4개로 나뉘어져있다. 이걸 하나로 합치는걸 목표로 한다.

# 원래 하는 일 정리

먼저 거대한 apiCRUD 내부 함수들의 기능을 정리해본다.

  • getList
    • 인자: http, 탭이름, 앵귤러컨트롤러, 보여주고 싶은 테이블 타입(handson, datatables), 페이지네이션 url, 파라미터
    • 로딩 애니메이션을 위해 아직 리스트 불리기 전임을 알려줌
    • url 생성(페이지네이션 있으면 그걸로 갈아치고, 없으면 api/탭이름/으로 한다 )
    • url, 파라미터를 넣어서 ajax GET한다.
      • tab이름이 invoice/어쩌구 면 tab이름을 operation으로 갈아친다
      • pagination이 있으면 그거 잘라다가 현재페이지(rc.pageNum)에 넣어준다.
      • 페이지네이션 앞 뒤 넣어준다
      • 탭별로 스위치
        • stock, invoice는 handsone 옵션들 넣어준다
        • operation, close, sell은 페이지네이션으로 호출한게 아닌경우엔 로그에 노란거 표시 위한 짓들을 해준다.
      • 핸슨테이블이라면
        • id를 누르면 디테일 페이지로 가도록 한다.
    • 다 불러지면 로딩 애니메이션 true로 바꾸기
  • getSimpleList
    • 인자: http, tab, rc, uid
    • url, 파라미터를 넣어서 ajax GET한다.
      • datatable 활성화
      • res.data를 담을 곳에 넣는다.
      • uid가 true면 title이랑 uid합쳐 보여주기
      • tab이름으로 select2도 달아준다.
  • getListByDate
    • 인자: http, begin, end, tab, rc, tableType
    • 파라미터 넘기고, 테이블 타입별로 getList하기
  • getItem
    • 인자: http, tab, rc, stockId
    • rc.detail에 data GET해오기

# 새로운 Get 함수 만들기

  • 재사용을 고려해서 심플한 입출 함수를 생각한다.
    • $http는 apiCRUD.init같은데 한꺼번에 빼놓자
    • ngGetData(Get해올 URL, 앵귤러컨트롤러, 옵션)
    • 데이터 저장할 변수는 위를 불러온곳에서 한다.
    • 옵션
      • params: object
      • tableType: 'handsone' / 'datatables'
      • isPagination: True / False

# 결과물

ngGetData: function(url, dataContainer, options) {
        /*
         rc: 앵귤러 컨트롤러 이름
         url: Get해올 url
         options:
         - isPagination: true/false
         - tableType: 'handson' / 'datatables'
         - highlightNew: true / false
         - bindNameForSelect2: true / false (Select2라이브러리에서 이름, uid 합쳐주기)
         - params:
            - simple: true / false
         */
        var rc = this.rc;

        rc.loaded = false;
        this.$http.get(url, { params: options.params }).then(function(res) {
            rc.loaded = true;
            if(options.isNotPagination) { /* pagination 없는 리스트 */
                console.log("NEW LIST", url, res.data);
                rc[dataContainer] = res.data;

                if(options.params && options.params.simple) {
                    var data = $.map(res.data, function (obj) {
                        obj.text = obj.text || options.bindNameForSelect2 ? obj.title + ' ' + obj.uid : obj.title;
                        /*-- bindNameForSelect2가 true면 title이랑 uid랑 합쳐 보여주기 --*/
                        return obj;
                    });
                    $("." + dataContainer + "-select").select2({
                        data: data
                    });
                }
            } else { /* pagination 있는 일반 리스트 */
                console.log("NEW LIST", url, res.data.results);

                // pagination 처리
                rc.pageNum = res.data.next? res.data.next.split('=')[1] - 1 : 1;
                console.log("페이지:" , rc.pageNum);
                $('ul.pagination li.prev').data('page', res.data.previous);
                $('ul.pagination li.next').data('page', res.data.next);

                if(options.tableType === 'handson') {
                    apiCRUD.setHandsonData(options.tableName, res.data.results);
                } else {
                    rc[dataContainer] = res.data.results;
                    if(options.highlightNew) {
                        rc.last_log_idx = rc.last_log_idx2;
                        rc.last_log_idx2 = res.data.results[0]? res.data.results[0].id : 999999;
                    }
                }
            }
        }, function() {
            alert('리스트를 불러오지 못했어요. 새로고침을 해주시겠어요?');
            rc.loaded = true;
        });
    },

일단 하나로 합치는데 성공! 더이상의 리팩토링은 또 나중에 하기로 한다 ㅎㅅㅎ!