본문 바로가기

Java

전자세금계산서 웹 스크래핑

반응형

앞장에서 국세청 홈텍스 시스템으로 공인인증서 로그인 과정을 성공한 이후에 이번 장에서는 국세청 홈텍스에 저장된 전자세금계산서 데이터를 조회하기 위해서 SSO 로그인 및 데이터 요청 및 결과 확인에 대해서 알아본다.

이번 장의 내용은 앞장의 연속이기 때문에 공인인증서 로그인 과정에서 얻었던 WMONIDTXPPsessionID 항목의 값은 이번에도 계속 사용된다.

로그인을 성공하고 나면 현재 로그인한 사용자에 대한 여러가지 정보를 얻을 수 있는데 세금계산서 조회, 세금계산서 XML 다운로드 등의 요청 정보 중에 반드시 필요한 항목이 이 정보에 포함되어 있으므로 이 값을 미리 얻어 놓도록 하자.

먼저 아래의 권한요청 페이지로 간단한 정보를 송신해보자.
https://www.hometax.go.kr/permission.do?screenId=index_pp

Request Cookies는 아래와 같이 넘긴다.

 항목명 비고
WMONID 앞 장에서 얻은 WMONID 값
nts_homtax:zoomVal ‘100’으로 하드코딩
nts_hometax:pkckeyboard ‘none’으로 하드코딩
nts_hometax:userId ‘’으로 하드코딩
NTS_LOGIN_SYSTEM_CODE_P ‘TXPP’로 하드코딩
TXPPsessionID 앞 장에서 얻은 TXPPsessionID 값
NTS_REQUEST_SYSTEM_CODE_P 변수에 ‘TXPP’를 담아서 넣는다.

Request Body는 아래와 같이 넘긴다.

<map id='postParam'><popupYn>false</popupYn></map>

이렇게 POST 방식으로 송신하면 응답으로 XML을 수신 받는데, 그 중 전자세금계산서 데이터를 조회하기 위한 Tin 값을 아래의 XML을 참고하여 얻는다.

<?xml version="1.0" encoding="UTF-8"?>
<map id="">
   <map id="resultMsg">
      <map id="sessionMap">
         …
         <tin>000000************</tin>

차후 전자세금계산서 XML 파일을 다운로드 받을 경우에는 sessionMap 항목 중 mpbNo의 값을 얻어 두었다가 사용한다.

Request Cookies 항목에서 보면 NTS_REQUEST_SYSTEM_CODE_P 이란 항목이 새롭게 추가된 것을 볼 수 있는데 기존에 NTS_LOGIN_SYSTEM_CODE_P 이란 항목이 있었다는 것을 기억해서 추측을 해보면 로그인할 때 시스템은 TXPP였고, 현재 Request를 날리는 시스템도 TXPP라는 것을 추측할 수 있을 것 같다.

앞장의 마지막 설명에서 공인인증서 로그인은 포탈 시스템에 로그인한 것에 불과하고 전자세금계산서나 현금영수증 같은 업무는 별도의 시스템이라서 SSO 로그인을 해야 한다고 설명을 했었다.
그렇다면 현재 Request 시스템은 TXPP였지만 전자세금계산서 시스템이나 현금영수증 시스템은 별도의 코드 값이 있다고 추론을 한다면 정답이다.

전자세금계산서 시스템은 TEET라는 코드 값을, 현금영수증 시스템은 TECR라는 코드 값을 가지고 있다.
진행했던 업무가 2개의 시스템만 다뤄서 다른 시스템의 코드 값을 알지 못하지만 크롬의 개발자 도구를 실행하여 다른 메뉴를 클릭하면 어떤 시점부터 NTS_REQUEST_SYSTEM_CODE_P 값이 해당 시스템에 맞게 변경되는 것을 볼 수 있을 것이다.

이제 우리는 전자세금계산서 시스템에 SSO 로그인을 해야 하기 때문에 TEET 시스템에 접근하기 위한 TEETsessionID 값을 얻도록 하자.

TEETsessionID 값을 얻기 위해서는 TEET시스템의 특정 페이지에 대한 권한 요청을 수행한다.
https://teet.hometax.go.kr/permission.do?screenId=UTEETBDA01

Request Cookies 값은 먼저 권한요청 했을 때 사용했던 내용을 그대로 사용하여 송신하면 응답이 오는데 응답 본문은 무시하고 Response Cookies에서 TEETsessionID 값을 얻는다.

TEETsessionID 값을 얻었으면 TEET 시스템에 SSO 과정에 사용할 토큰 값을 얻어야 한다.

// _ + 20자리로 된 랜덤 값
String nts_generateRandomString = "_" + this.getNtsGenerateRandomString(20);
url = "https://hometax.go.kr/token.do?query=" + nts_generateRandomString + "&postfix=" + formatter.formatDate("yyyy_MM_dd", new Date());

이때 20자리로 된 랜덤 스트링을 GET 방식으로 넘겨야 하는데 아래 함수를 사용하여 얻을 수 있다.

private String getNtsGenerateRandomString(int length) {
	String seed = "qwertyuiopasdfghjklzxxcvbnm0123456789QWERTYUIOPASDDFGHJKLZXCVBNBM";
	String result = "";
	for (int i = 0; i < length; i++) {
		Double d = Math.floor(Math.random() * seed.length());
		result += seed.charAt(d.intValue());
	}
	return result;
}

Request Cookies는 아래와 같이 넘긴다.

항목명 비고
WMONID 앞 장에서 얻은 WMONID 값
nts_homtax:zoomVal ‘100’으로 하드코딩
nts_hometax:pkckeyboard ‘none’으로 하드코딩
NTS_LOGIN_SYSTEM_CODE_P ‘TXPP’로 하드코딩
NTS_REQUEST_SYSTEM_CODE_P 아직까지는 변수에 ‘TXPP’를 담아서 넣는다.
TXPPsessionID 앞 장에서 얻은 TXPPsessionID 값
TEETsessionID 앞에서 얻은 TEETsessionID 값

이렇게 GET 방식으로 서버로 전송하면 클라이언트 페이지의 콜백 함수를 호출하기 위한 문자열을 받을 수 있는데 그 값 중에서 nts_reqPortalCallback("와 "); 사이의 값이 SSO 토큰 값이다.

nts_reqPortalCallback("<ssoToken>Vy3zFyENGINEx5F1zTyNTS1zCy1579495544zPy86400zAy23zEykx78REm0MuRx78BmqHVnx7ALx78OcUgOx2FYZTcQCx7AsQNLoBAw6hWtIlD3wMmAh51QoaBBOckRdCMx2B29x2BDdS9CatJ1DSwkwewL6x78sc0ZShLHEsSN7GAmc33LHfCu5aEx78pcl7O5Eh6bx2FS38PU18gi8hnTsx78dhUl3x790v1i4deFVrY17er2PtgLiZpugOE514QvOQOgmEQBbaN2rlqHamQCBjux79MXGjVu8Lw1cCoLDDXTBOfXx7AN7DkJgP9qOTK0x2BmwY2Swp7pBCJvCkOupcCAeKI2JZkWXdddfHVSRNFiojdsgx79lDDq4x786k6fK38m7x2Fx78YUYcsIZp6x79esAix2Bx7ASeDVgDBx7Ajx2Ft9uBR69gnbwJx78v8BIqNx2BFeM1n4JOZsD8JEltx78uArFx78GoMHA9nK6v5WnO9qX6CYFK3H09CeABdaAARbXshB54v1x7AY8e4Rx2F0ERoJuew7e9eFAx79H7x7Ax79mHx2FKw2x78M7w8Lx79SrH5Px2FUi2Px79nrJ7WAdGHwjMCwUc0x7ANDZsx2Fx7AvquPx78LXwEGgJWShwQlx79Zj6bpupviIdHAs4x2BCx784x2Bwt0H1es8lTj1uBijvDFx79SofnKcHeJ1TsGuj0sPD50MPHVgn5VuSBqpLcdTwLq0iCIHrhaIWHwGBSvGRX6IYZtL3nseg1GG9ki8oCoDFax7A8YUfGx2BvJ5x7ACFjx78348u37H4b4GjXL066YWQja2JbEIPCnqsZ5rwfg3D4bCLGKP8TNFLnx2Bhjm2lABvQ4Isx7AOCQUCLf5ZlpTNhHb1x2FRn9lx7ArkeSAUsXWcjVIDP09WD2wOi9A8SzKyfwKCF3D0qOQBbT1DoIM7sWInEax79oqhlASVpe7NKVlZSJLTB6XWpW1p0k4PMUvlXZzSSy00000000112zUURy05c4a18533261da6zMysKi6RoPlNx788x3Dz</ssoToken><userClCd>02</userClCd>");

이제 이 값을 SSO 토큰을 전송하기 위한 본문의 XML로 만들어야 하는데 아래와 같은 형태로 구성한다.

sb.append("<map id='postParam'>");
sb.append(ssoToken);
sb.append("<popupYn>false</popupYn>");
sb.append("</map>");

SSO 토큰을 전송하기 위한 주소는 아래와 같다.
https://teet.hometax.go.kr/permission.do?screenId=UTEETBDA01&domain=hometax.go.kr

Request Cookies는 아래와 같이 넘긴다.

항목명 비고
WMONID 앞 장에서 얻은 WMONID 값
NTS_LOGIN_SYSTEM_CODE_P ‘TXPP’로 하드코딩
NTS_REQUEST_SYSTEM_CODE_P 아직까지는 변수에 ‘TXPP’를 담아서 넣는다.
TXPPsessionID 앞 장에서 얻은 TXPPsessionID 값
TEETsessionID 앞에서 얻은 TEETsessionID 값
NetFunnel_ID ‘’으로 하드코딩

SSO 토큰 값을 POST 방식으로 송신을 하면 Response Cookies에서 아래의 값을 얻는다.

항목명 비고
NTS_REQUEST_SYSTEM_CODE_P ‘TEET’
TEETsessionID TEET 시스템의 세션 ID

우리는 이번 장의 2번째 단계에서 TEETsessionID 값을 얻었었지만 SSO 토큰 값을 송신하여 TEET 시스템에 정상적으로 로그인이 되면 TEETsessionID 값이 바뀌게 되고 이후 바뀐 값으로 TEET 시스템에 Request Cookies에 값을 넘겨야 정상적인 데이터를 얻을 수 있다.

그리고 SSO 토큰 값을 넘기기 전에 NTS_REQUEST_SYSTEM_CODE_P에 해당하는 값이 ‘TXPP’ 였다가 SSO 토큰 값을 송신한 이후 Response Cookies에 담긴 값으로 변경하여 ‘TEET’가 되는 것을 알 수 있다.

이 단계까지 왔다면 전자세금계산서 데이터를 조회하기 위해 TEET 시스템에 SSO 로그인이 성공하였을 것이다.

그런데 전자세금계산서 데이터 조회를 요청하기 앞서 마지막 단계가 하나 더 남았다.
바로 NetFunnel_ID 값을 얻어야 하는데 긴 말이 필요 없이 어떻게 얻는지 알아보자.

요청 주소는 아래와 같다.
https://apct.hometax.go.kr/ts.wseq

위의 주소로 GET 방식의 데이터를 송신해야 하는데 항목은 아래와 같다.

항목명 비고
opcode ‘5101’ 으로 하드코딩
nfid ‘0’ 으로 하드코딩
prefix NetFunnel.gRtype=5101’ 으로 하드코딩
sid ‘service_2’ 으로 하드코딩
aid ‘UTEETBDA01’ 으로 하드코딩
js ‘yes' 으로 하드코딩

Request Cookies는 아래와 같이 넘긴다.

항목명 비고
NTS_LOGIN_SYSTEM_CODE_P ‘TXPP’로 하드코딩
NTS_REQUEST_SYSTEM_CODE_P 앞 단계에서 ‘TEET’로 바뀐 변수 값
TXPPsessionID 앞 장에서 얻은 TXPPsessionID 값
TEETsessionID 앞 단계에서 얻은 TEETsessionID 값

이렇게 GET 방식으로 서버로 전송하면 문자열을 받을 수 있는데 그 값 중에서 NetFunnel.gRtype=5101NetFunnel.gControl.result='와 '; NetFunnel.gControl._showResult(); 사이의 값이 NetFunnel_ID 이다.

NetFunnel.gRtype=5101NetFunnel.gControl.result='5002:200:key=69189F7B2AD5F389E372DF1939BEA2C3D48914F4629B33E3BA98A8E0A8CA1544EFA7912B4EEC4CE4F6AB63EFF0AEA7694FAE7B5998DE73556D65D26FD905FE258756372D4829DB04185ECAA79AB45EC5361230C4B1DA94A86030DC0DE8D9B0A7714FC9AAE4CAB1472150053A5B61D679312C302C342C312C30&nwait=0&nnext=0&tps=0&ttl=0&ip=apct.hometax.go.kr&port=80'; NetFunnel.gControl._showResult();

지금까지 드래곤볼을 모으듯이 전자세금계산서 데이터를 요청하기 위한 필수 값을 모두 모았으니 용신을 부를 차례가 되었다.

전자세금계산서 데이터를 요청하기 위한 주소는 아래와 같다.
https://teet.hometax.go.kr/wqAction.do?actionId=ATEETBDA001R01&screenId=UTEETBDA01&popupYn=false&realScreenId=

Request Cookies는 아래와 같이 넘긴다.

항목명 비고
WMONID 앞 장에서 얻은 WMONID 값
NTS_LOGIN_SYSTEM_CODE_P ‘TXPP’로 하드코딩
NTS_REQUEST_SYSTEM_CODE_P 앞 단계에서 ‘TEET’로 바뀐 변수 값
TXPPsessionID 앞 장에서 얻은 TXPPsessionID 값
TEETsessionID 앞 단계에서 얻은 TEETsessionID 값
NetFunnel_ID 앞 단계에서 얻은 NetFunnel_ID 값

전자세금계산서 데이터를 요청하기 위해서는 검색조건을 XML로 송신을 해야 하는데 각 항목의 값은 전자세금계산서 조회 페이지의 조회조건에 따라 변경되므로 가장 기본 상태의 검색 값을 예를 들었고 대략 아래와 같다.

<map id="ATEETBDA001R01">
	<icldLsatInfr>N</icldLsatInfr>
	<resnoSecYn>Y</resnoSecYn>
	<srtClCd>1</srtClCd>
	<srtOpt>01</srtOpt>
	<map id="pageInfoVO">
		<pageSize>10</pageSize>
		<pageNum>1</pageNum>
	</map>
	<map id="excelPageInfoVO" />
	<map id="etxivIsnBrkdTermDVOPrmt">
		<tnmNm />
		<prhSlsClCd>01</prhSlsClCd>
		<dtCl>01</dtCl>
		<bmanCd>00</bmanCd>
		<etxivClsfCd>all</etxivClsfCd>
		<isnTypeCd>all</isnTypeCd>
		<pageSize>10</pageSize>
		<splrTin>sessionMap의 tin 번호</splrTin>
		<dmnrTin></dmnrTin>
		<cstnBmanTin></cstnBmanTin>
		<splrTxprDscmNo></splrTxprDscmNo>
		<dmnrTxprDscmNo></dmnrTxprDscmNo>
		<splrMpbNo></splrMpbNo>
		<dmnrMpbNo></dmnrMpbNo>
		<cstnBmanMpbNo></cstnBmanMpbNo>
		<etxivClCd>01</etxivClCd>
		<etxivKndCd>all</etxivKndCd>
		<splrTnmNm></splrTnmNm>
		<dmnrTnmNm></dmnrTnmNm>
		<inqrDtStrt>조회기간 시작일(yyyyMMdd)</inqrDtStrt>
		<inqrDtEnd>조회기간 종료일(yyyyMMdd)</inqrDtEnd>
	</map>
</map>

위의 항목 중 splrTin 항목의 값은 이 장의 처음 단계에서 sessionMap에 저장되어 있던 Tin 값 얻었던 것을 여기에 넣어야 한다.

이렇게 POST 방식으로 서버로 전송하면 검색조건에 해당하는 전자세금계산서 데이터를 XML 형태로 받을 수 있고, 현금영수증 시스템의 데이터도 지금까지 살펴본 방법과 거의 유사한 방법으로 데이터를 얻을 수 있다.

반응형