[angularJS] interceptor 활용하기


작성일자 : 2019-04-08

요구사항 발생 원인

더블 클릭으로 인한 POST 중복 호출로 동일 데이터가 여러개 생성

해결 방안 모색

버튼에서 더블 클릭을 방지하는 방안

방안비고
기존 화면에 있는 기본 버튼을 custom 한 directive로 대체테스트 시간 소요, 누락 가능성
버튼 외에 키보드 이벤트 등 으로도 api 가 호출 가능모든 경우의 수 고려 필요, 누락 가능성
위 경우의 수 누락을 방지하기 위한 api 중복 호출을 막기결국 중복 기능이 되어버림

api 호출 관련이 아닌 중복 동작 처리에 사용하는 것이 적합해 보임

api 호출 직전에 중복 호출 막는 방안

방안비고
interceptor 를 이용하여 api 호출 직전, 이전 api를 체크interceptor request
다른 directive 에서 동일한 api를 거의 동시에 호출하는 경우 문제 발생POST, PUT, DELETE 만 대상
코드로 인한 연속 호출인 경우에 문제가 발생할 수 있음필요시 예외 flag
화면에서 사람에 의한 중복 호출만 있다는 가정

인터페이스를 통한 막기 보다는, 최종 단계 직전에서 막는 것이 더 심플할 것으로 판단
interceptor 사용하는 것이 더 효과적이라고 생각 됌

interceptor

angularJS $http 에서 제공함.
현재 개발중인 프로젝트는 1.6.10
1.6.10 버전은 $resource interceptor 는 response, responseError 만 처리할 수 있음.
(1.6.10 버전 $http는 request, requestError 제공)

프로젝트 angularJS 버전을 올려야 함 > 1.7.8 로 올림 > 모든 기능 테스트 필요

참조

migrating-from-1.6-to-1.7
$resource:1.6.10
$resource:latest
$http#interceptors:1.6.10
$http#interceptors:latest

api 중복 호출 처리

    /**
     * 더블 클릭 방지 코드
     * 목적: 더블 클릭 방지
     *      동일 url 이 response 되지 않은 상황에서 재호출 막기
     * 동일 url, 동일 method 을 호출할 경우 중복으로 처리
     * response, responseError 로 반드시 종료 처리가 되기 때문에, 요청 시간 정보는 불필요
     * url을 key 로 하면 탐색시간이 O(1) 이므로 검색 속도가 빠르므로 simple 하게 
     * {
     *   'GET': {
     *     url1: true,
     *     url2: true
     *   },
     *   'POST': {
     *     url1: true,
     *     url2: true
     *   }
     * }
     * 위 형태로 요청 중인 정보를 가지고, response(Error) 시 delete 처리
     * @author heather
     */
    var requestingUrlData = {};

    function addRequestData(config) {
      if (config.method === 'GET') { return; }
      requestingUrlData[config.method] = requestingUrlData[config.method] || {};
      requestingUrlData[config.method][config.url] = true;
    }

    function removeRequestData(config) {
      if (config.method === 'GET') { return; }
      var methodData = requestingUrlData[config.method] || {};
      delete methodData[config.url];
    }

    function isDoingSameRequest(config) {
      var methodData = requestingUrlData[config.method] || {};
      return methodData[config.url];
    }
    /** - 더블 클릭 방지 코드 끝 */

    return {
      request: function(config) {
        if (!config.passSameCall && isDoingSameRequest(config)) { 
          // '중복 허용이 아님' && '현재 동일 api 호출 후 리턴이 오지 않음'
          // passSameCall 는 resource 호출 시 설정 함.
          return $q.reject({ 
            error: 'same request called', 
            pass: true, 
            config: config 
          });
        }
        // 호출 시작
        addRequestData(config);
        return config;
      },
      requestError: function(rejection) {
        // 호출 완료
        removeRequestData(rejection.config);
        return $q.reject(rejection);
      },
      response: function (response) {
        // 호출 완료
        removeRequestData(response.config);
        return response.data;
      },
      responseError: function (error) {
        // 호출 완료
        removeRequestData(error.config);
        if (error.pass) {
          return $q.reject(error);
        } 

        return error.data;
      }
    };
// $resouce 호출
doApi : function() {

  var url = 'url';
  var action = actionFactory.defaultAction;
  var params = {};

  // 중복호출 허용 설정
  action.create.passSameCall = true;

  return $resource(url, params, action);
},

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다