회사에서 맡고 있는 서비스 중, 회원이 구매를 신청하면 정부24에서 그 컨텐츠를 구매하여 제공을 하는 서비스가 있다. (참고로, 구매를 한다고 바로 완료되는게 아니라 구매 로직을 태운 후 실제로 컨텐츠를 가져오기까지 어느정도 시간이 소요된다.)
서비스 내에서 회원이 구매를 결정하여 정부24에서 그 컨텐츠를 구매하는 로직을 실행할 수 있는 곳은 서비스에서 총 4군데가 있는데, Header, Footer, 그리고 2개의 검색탭이다.
그런데 정부24에서 컨텐츠들을 구매할 때 너무 많은 양을 한 번에 구매하면, 다량의 신청건이 한꺼번에 신청 Queue에 몰리고, 또 입수 Queue에 몰리게 되므로, 과부하가 걸리곤 했다.
회원은 유료 서비스를 이용하고 있지만 실제 컨텐츠를 제공받기까지 너무 시간이 오래 걸려서 직접 정부기관에서 구매를 하여 이중으로 컨텐츠 이용 금액을 사용하게 되거나, 사업실로 컴플레인을 거는 일이 많았다고 한다.
따라서 사업실에서는 많은 구매건이 진행중이거나 신규 신청건이 너무 많을 때 이에 대한 안내 팝업이 노출되도록 하여 어느정도 시간을 벌고, 또 이 팝업에서 다른 정부24 아이디를 입력하도록 하여 하나의 정부24 계정으로 감당 불가능한 양의 컨텐츠를 발급하는 것을 막고자 했다.
서비스 관련 배경 지식
이 서비스의 회원 구조는 본사 - 지점 - 내부 사용자의 3 depth로 되어있다.
회원이 로그인을 하고 나면, 정부24에서 발급하는 몇가지 서류를 발급할 때 이 회원의 정부24 아이디로 로그인을 하게 되어 있다.
개발 요건 파악
요청사항 파악
세션의 정부24 아이디로 발급 진행중인 컨텐츠의 수와 새로 신청하는 정부24 컨텐츠의 수를 합쳐 50건이 넘으면 팝업이 뜬다.(참고로, 실제로는 50건이 아니다. 업무 내용을 모두 솔직히 블로그에는 적을 수 없어서 약간 각색했다.)
이 팝업에서는 하나의 정부24 계정으로 신청하려는 or 진행중인 컨텐츠의 건수가 50건이 넘으니 시간이 오래 소요된다는 점을 안내하고, 다른 정부24 계정을 사용하도록 유도한다.
‘계정 변경’ 버튼과 ‘취소’ 버튼이 존재한다.
‘계정 변경’을 누르면 새로운 계정을 사용하는 것이다. (로그인 로직이 실행되도록 해야한다.)
‘취소’ 버튼을 클릭하면 이 팝업을 닫는다.
업무 수행 계획
구매 버튼을 누를 때, 세션에 있는 정부24 아이디로 발급중인 컨텐츠의 건수를 세는 API를 개발한다. 혹은 기존 API를 사용할 수 있는지 찾아본다.
‘계정 변경’을 눌렀을 때, 새로운 계정으로 로그인 되고, 그 이후에 구매 버튼을 클릭하게 하면 다시 50건 락 팝업을 띄울지 말지 (건수 카운팅) 로직을 실행해야 하므로, 어디서 이 로직을 타게할 것인지 결정해야 한다.
새로운 계정으로 변경했을 때, 구매가 가능한 경우 구매 데이터를 적재하는 테이블에, 변경된 계정 정보가 잘 들어갔는지 확인해야 한다.
회원이 구매하는 컨텐츠에서 정부24에서 구매하는 컨텐츠와 그렇지 않은 컨텐츠가 섞여있는 경우 이를 어떻게 처리할지 결정한다.
회원이 체크리스트에서 체크한 건수를 카운트하고, 진행중인 건수를 더하여 50건이 넘는지 확인하는 로직을 프론트(JavaScript)에서 처리할지, 백(Java)에서 처리할지 생각해본다.
기존에 정부24 관련 모달은 공통 파일로 관리되고 있고, 신청 버튼을 처리하는 로직은 4군데에 나누어 각각 처리되고 있다. 이 로직은 건수 제한 팝업과 관련된 로직들은 어디에서 처리해야할지 정해야한다.
물론, 공통 로직으로 빼서 필요한 순간에 불러오는 식이 코드의 중복을 피할 수 있어 가장 효율적이겠지만, 서비스 이미 정상적으로 잘 작동하고 있는 코드를 최소한으로 수정하는 식으로 코드를 작성하는 것이 좋다.
해결 과정
API 수정하기
기존에 구매 테이블(table240)에는 사용자가 구매한 컨텐츠에 대한 정보가 쌓이고 있는데, 이 테이블의 데이터를 SELECT하는 몇 개의 API 중 사용 빈도가 적고, 쿼리 실행 속도가 빠른 API를 찾아내었다.
이 API는 회원이 서비스를 이용하면서 ‘마이페이지’에서 자신의 구매 기록을 확인할 때 데이터를 처리하기 위해 고안된 API이다.
하지만 문제점은, 기존의 API에는 ‘정부24 계정’과 ‘처리상태’를 where절에 받고 있지는 않다. 시니어분께 조언을 구한 결과 새로운 API를 개발하는 것보다는 기존의 API 활용하도록 권고를 받아, 이 API를 수정하기로 결정했다.
이 API를 호출할때 정부24 계정(gov24_id)과 처리상태(processCode)를 함께 넘겨줄 것이므로, Service에서 이 Parameter를 받아 SQL문을 실행할 수 있도록 코드를 수정했다.
publicMap<String,Object>()SampleAPI(Map<String,Object>()paramMap){Map<String,Object>()inputMap=newHashMap<>();Map<String,Object>()resultMap=newHashMap<>();// 이번 업무와 무관한 코드 생략inputMap("gov24_id",paramMap.get("gov24_id"));inputMap("processCode",paramMap.get("processCode"));apiService.getXxxx(inputMap);returnresultMap;}
publicStringcontroller(ElementelRoot){Map<String,Object>inputMap=newHashMap<>();Map<String,Object>resultMap=newHashMap<>();//정부24 컨텐츠가 아닌 것은 제외intexclude_cnt=0;if(StringUtils.isNotBlank(form.getXxxContentNum)){exclude_cnt=form.getXxxContentNum().get(",").length;}inputMap.put("svc_id",CommonController.SERVICE_ID);inputMap.put("usr_id",CommonController.USER_ID);inputMap.put("process_code","P");//진행중inputMap.put("gov24_id",getSessionValue("GOV24_ACT"));//정부24 계정List<Map<String,Object>>gov24_in_progress=commonService.getGov24ProcessListCnt(inputMap);intgov24_cnt=govgov24_in_progress.size()-exclude_cnt;resultMap.put("count",gov24_cnt);if(gov24_cnt>=50){resultMap.put("code","gov24Over50");elRoot.addCntent(newElementBuilder().createRootElement("gov24_id_cnt_result",resultMap).toElement());returnJSON;}resultMap.put("code","SUCCESS");elRoot.addCntent(newElementBuilder().createRootElement("gov24_id_cnt_result",resultMap).toElement());returnJSON;}
Service
publicList<Map<String,Object>>getGov24ProcessListCnt(Map<String,Object>apiParams){List<Map<String,Object>>resultList=newArrayList<>();Stringapi_code=XXX;try{//api 호출 및 데이터 처리}catch(Exceptione){//예외 발생시 예외 처리}returnresultList;}
모달 팝업 만들기
이 서비스에서 직접 새로운 팝업창을 사용하지 않고, 모달 팝업을 사용하고 있어 나도 비슷한 스타일의 모달 팝업을 생성했다.
<divclass="modal"id="gov24_processChk"style="z-index:1080; width:600px;"><divclass="header"><h1class="title">알림</h1><div><buttontype="button"class="btn_close"><iname="modal_close"class="icon_close"></i></button></div></div><divclass="container"><div><pclass="list1">* 기존의 정부24 계정으로 신청하신 건수가 50건이 초과되어 컨텐츠 제공에 많은 시간이 소요됩니다.</p><pclass="list2">- 정부24 계정을 변경하시겠습니까? (정부24 계정을 변경하길 원하시면 변경하실 정부24 계정 정보를 입력하여 주시길 바랍니다. '취소' 버튼 클릭시 기존의 정부24 계정을 사용합니다.</p></div><div>
정부24 아이디: <inputid="newID"type="text"name="newID">
정부24 비밀번호: <inputid="newPW"type="password"name="newPW"></div><div><ahref="javaScript:void(0)"id="newIDPW_Submmit"class="btn_b1"><span>계정 변경</span></a><ahref="javaScript:void(0)"id="newIDPW_Cancel"class="btn_b2"><span>취소</span></a></div></div></div>
$('#newIDPW_Submmit').on('click',function(){//계정 변경 버튼 클릭시 발생하는 이벤트 코드 작성});$('#newIDPW_Cancel').on('click',function(){//취소 버튼 클릭시 발생하는 이벤트 코드 작성});
모달 팝업 띄우기
사용자가 ‘구매’ 버튼을 클릭했을 때, 그 시점에 기존에 처리중인 정부24 컨텐츠가 몇 건인지 카운트하는 메서드를 선언한다.그리고 컨텐츠를 카운팅하는 로직이 실행되어야 하는 시점에 이 메서드가 실행되도록 추가한다.
/**
* 세션의 정부24 아이디로 진행중인 건수 조회
*/inqGov24ProcessCnt:function(clbck){//page context와 각종 parameter 세팅//controller에서의 데이터를 가져온다.vargov24_chk_result=XXX.getObejctInto("gov24_id_cnt_result");if(gov24_chk_result.code!==undefined&&gov24_chk_result.code!==''){if(gov24_chk_result.code=="gov24Over50"||Number(gov24_chk_result.count)+Number($('#selectedNew').val())>=50){$('#gov24_processChk').show();return;}}if($.isFunction(clbck)){clbck();}};
회원이 구매를 결정할 수 있는 4군데에서 위의 로직이 실행되도록 모두 작업한다. 이제 회원에 구매 결정을 하게 되면, 그 시점에 사용자 정부24 계정으로 발급중인 프로세스의 건수를 조회할 것이고 이 건수에 따라, 그리고 새로 구매하는 컨텐츠의 건수에 따라 위의 모달이 노출될 것이다.
UAT
API 정상 실행 여부 확인
Local 서버를 가동하여 API를 호출해본 결과, API는 나의 의도대로 정상적으로 작동하고 있다. 또한 응답 시간도 빠른 편이다.
팝업 노출 정상 여부 확인
일부러 팝업이 노출되도록 result code를 "gov24Over50"으로 넘겨 팝업이 뜨는지 확인했다. 또한 result count를 50으로 넘겨 확인했다.
실제 환경과 동일하게 테스트하기 위하여 10건이 진행중인 상황에서 50건을 새로 구매하려고 시도했다. 팝업이 정상적으로 노출된다.
버튼 클릭시 발생하는 이벤트 정상 여부 확인
팝업이 노출되었을 때 팝업의 버튼을 클릭하여 이벤트가 정상적으로 발생하는지 확인했다. ‘계정 변경’버튼을 눌렀을 때 발생하는 이벤트도 정상아고, ‘취소’ 버튼을 눌렀을 때 발생하는 이벤트도 정상적이다.
요청자에게 UAT 요청
개발자 테스트가 완료되어 요청자에게 UAT를 요청했다.
그리고, 새로운 버튼 추가 요청이 들어왔다. (이와 관련된 TIL at work는 다음 포스팅으로 작성 예정!)