본문 바로가기

Issue

CODEF를 이용한 API 일괄처리 시스템을 만들다 생긴 문제들 (OAUTH 2.0 인증, 간편인증)

최근 CODEF API를 이용해 여러 개의 금융정보와 관련된 API를 일괄적으로 받아 관리하는 웹페이지를 만드는 작업을 진행하였습니다.

요청 API 리스트
[고용산재]
 - 근로자고용정보현황조회
 - 보수총액신고서
[국세청]
 - 신고서 법인세 과세표준 및 세액신고서
 - 종합소득세, 농어촌특별세, 주민세 과세표준확정신고 및 납부계산서
 - 신고서 표준대차대조표
 - 신고서 표준손익계산서
 - 신고서 법인세 과세표준 및 세액조정신고서
 - 신고서 세액공제명세서
 - 신고서 사업소득명세서
 - 신고서 공제감면세액 및 추가납부세액합계표(갑)
 - 증명발급 사업자등록 증명

 

이렇게 총 11개의 API를 카카오를 이용한 간편 인증을 통해 회원정보를 등록해 관리할 수 있도록 하는 애플리케이션인데
나는 카카오계정 간편 인증 연동과 API를 일괄적으로 불러와 회원정보에 저장 또는 갱신하여 이후 원하는 데이터를 애플리케이션에서 활용할 수 있게끔 만드는 작업을 맡았다. (백엔드 쪽 작업 - PHP와 MYSQL을 이용해 작업하였습니다.)

 

1) 페이지를 그려서 구성해 보자

초기에 생각해두었던 구상도? 구조도? 를 최대한 그려보았습니다 ( 프로젝트 설계할때 다이어그램 잘 활용하는 법도 공부하고싶네요..ㅠ)

초기에 구상해 두었던 개발 해야 될 로드맵입니다.
카카오계정을 이용한 회원 DB 생성과 세션을 통해 회원별로 API 데이터를 일괄적으로 받아오는 부분들은 구성을 완료해 두었지만,
테스팅 버전에서 동작하였던 API의 요청이 동작하지 않는 문제가 발생하기 시작했습니다.

 

2) 문제를 찾아보자

문제를 찾기 위해 CODEF의 개발가이드를 다시 한번 정독하기 시작했고, 먼저 CODEF API 흐름도를 살펴보았다.

CODEF의 API 흐름도

어쨌든 'API 요청' 부분에서 에러가 확인되었으니 accessToken 세팅, connectedID 세팅, publicKey로 RSA 암호화, URL Encoding

이 4가지 항목 중에서 에러가 나는 것이라 생각하였다.

 

문제해결과정 1) 4가지 항목들을 하나하나 살펴보자는 의미에서 publicKey로 인증서 비밀번호를 암호화시켜 보거나 요청 데이터 URL Encoding, connectedID는 CODEF 측에 문의해 본 결과 필수적인 것은 아니라고 하여 문제의 원인을 찾지 못한 채 계속 삽질만 했다..

 

문제해결과정 2) CODEF에서 API를 제공받기 위해서는 입력부가 필요하고 해당 입력부에 확인하고 싶은 API에 해당하는 json 형식의 데이터를 ENDPOINT로 보내 출력부에 해당 API를 제공받을 수 있는 방식이다. 개발하고자 하는 방향은 하나의 페이지에서 다양한 API 데이터를 일괄적으로 발급받아 회원 DB에 각 API 항목별로 데이터를 저장하고 관리하는 것이 목적이었기 때문에 하나의 입력폼으로 다양한 정보들을 처리하기 위해 무리하게 다양한 값들을 받아오는 과정에서 실수가 있다고 생각하였다.

// 근로자고용정보현황조회를 위한 입력부
{
    "organization": "0001",
    "loginType": "0 또는 5",
    "certType": "1",
    "certFile": "BASE64로 Encoding된 인증서 der파일 문자열",
    "keyFile": "BASE64로 Encoding된 인증서 key파일 문자열",
    "certPassword": "RSA암호화된 비밀번호",
    "userType": "0",
    "identity": "9**********",
    "loginTypeLevel": "5",
    "loginUserName": "****",
    "phoneNo": "010********",
    "manageNo": "*********",
    "insuranceType": "0",
    "userIdentity": "*********",
    "state": "0",
    "type": "1",
    "inquiryType": "0",
    "startDate": "20220101",
    "endDate": "20200330",
    "userName": "차**",
    "applicationYN": "0",
    "corporateNo": "********",
    "approvalNo": "*********",
    "telecom": "",
    "id":""
}

하나의 API를 제공받기 위해서 입력부에 많은 양의 정보가 필요로 했고 총 11개의 API를 모두 제공받는 과정을 하나의 입력 폼에서 처리하는 과정에서 값을 처리하는 방식에 문제가 있다고 생각하여 또 한참을 삽질했다.. (결국 값들을 처리하는 코드 상에는 문제가 없었다)

 

최종문제해결 - AccessToken/OAUTH 2.0)

그림을 보면 알 수 있듯 문제해결과정 1)에서 해결하고자 했던 문제 3가지는 모두 옵션 항목이었고 필수적인 조건이 아니었다.
정작 필수적인 조건인 AccessToken을 발급받지 않고 API 요청을 보냈기 때문에 생긴 문제였는데..

어째서 왜 필수조건을 당연히 처리했다고 생각하고 계속하지 않았는지 참 갑갑하다 (분명 운전면허를 통한 테스트 API 요청을 보낼 땐 AccessToken의 발급 없이 API를 제공받을 수 있어서 당연히 발급받아 문제는 다른 곳에서 발생한다고 생각한 것 같다는 건 추한 변명일 뿐이겠죠,,?)

CODEF의 Access Token 발급 방식 (OAUTH 2.0 표준을 따른다)

OAUTH 2.0 표준을 서비스 구조에 맞게 변경하여 인증을 진행한다고 나와있는데, 개발가이드의 다른 곳을 살펴보아도 OAUTH 2.0 표준에 대한 설명이나 API에 대한 설명만큼 자세하게 ENDPOINT, 입력부, 출력부 형식으로 구성되어 있는 이미지는 볼 수 없었다.

아주 간단하게 짜잔. 하는 느낌의 흐름도만 보여주고 있었다.

그래서 일단 OAuth 2.0의 개념과 작동방식을 공부하고, CODEF에 적용하여 해당 accessToken을 발급받을 수 있도록 하고자 하였다.

 

 

3) 원인을 찾았다! 근데 먼저 OAuth 2.0부터 알아보자..

웹 서핑을 하다 보면 구글, 페이스북, 카카오 간편 인증을 통한 로그인 서비스를 여럿 본 적이 있다. OAuth 2.0은 이런 연동되는 외부 웹 애플리케이션에서 구글, 페이스북 등이 제공하는 기능을 간편하게 사용할 수 있게 인증해 주는 프로토콜이다.

OAuth에 대한 정의) Third-Party 프로그램에게 리소스 소유자를 대신하여 리소스 서버에서 제공하는 자원에 대한 접근 권한을 위임하는 방식을 제공하는 개방형 표준

출처: PAYCO 개발자 센터

전체적인 OAuth 2.0의 진행 흐름도이다. 흐름도를 살펴보기 이전에 각각의 구성요소에 대한 설명부터 진행하자면

 - 사용자(Resource Owner) : 웹서비스를 이용하려는 유저, 사용자
 - Client: 애플리케이션 서버
 - Authorization Server: 권한을 부여해 주는 서버 (인증에 사용할 정보를 제공)
 - Resource Server: 사용자의 개인정보를 가지고 있는 애플리케이션 회사 서버 (구글, 페이스북, 카카오)
 - Access Token: 자원에 대한 접근 권한을 나타내는 자격증명


따라서 여기서 Client는 개발을 진행한 웹 애플리케이션으로 볼 수 있고, 사용자는 내가 구성한 애플리케이션에서 API를 제공받고자 사용하는 회원으로 볼 수 있다. Authorization Server는 CODEF API로 이곳에서 Access Token을 발급받아 CODEFD API에서 Client의 Access Token 값을 제공해주고자 하는 API Resources Server(내가 개발하는 애플리케이션에서는 [고용산재], [국세청]이 될 수 있겠다)으로 보내주어 사용자에 대한 인증을 통해 API 제공을 해준다~라는 방식이었다

 

OAuth 2.0은 프로토콜을 4가지 종류로 구분하여 제공하는 것으로 보이는데,
CODEF에서 사용 중인 방식은 Client Credentials Grant(클라이언트 자격증명 승인 방식)으로 보인다.

 

Client Credentials Grant(클라이언트 자격증명 승인 방식)이란?

클라이언트의 자격증명만으로 Access Token을 획득하는 방식이다.

OAuth 2.0의 권한 부여 방식 중 가장 간단한 방식으로 클라이언트 자신이 관리하는 리소스 혹은 권한 서버에 해당 클라이언트를 위한 제한된 리소스 접근 권한이 설정되어 있는 경우 사용된다. 자격증명을 안전하게 보관할 수 있는 클라이언트에서만 사용되어야 한다.

 

Client Credentials Grant(클라이언트 자격증명 승인 방식)의 Request body

// Request body
(POST) /token
Authorization: (API서버에 신원 증명)
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials

 

이런 개념을 통해 CODEF API에서 Access Token을 제공받는 방법에 대해 알아보자면

CODEF API의 자격증명 방식

서비스 신청과 심사를 통해 clientId, clientSecret, publicKey를 발급해 주는 방식을 통해 Client(현재 개발 중인 애플리케이션)에 자격증명을 부여해 주게 된다. 발급받은 값들을 이용해 Request body에 Authorization: 값을 설정하여 보내 자격증명을 확인받아 Access Token을 요청, 발급받는 방식이었던 것이다.

따라서, Client에서 자격증명을 받은 뒤 Client(개발 중인 애플리케이션)에 사용자가 로그인 시 Access Token을 부여해 주는 방식을 이용해 사용자가 Access Token값을 지닌 API 요청을 보낼 수 있도록 수정하였다.

수정된 웹 어플리케이션의 흐름도

이렇게 OAuth 2.0에 대한 개념과 작동방식에 대해 알아보며 카카오 간편 인증 로그인 방식에도 OAuth 2.0 방식이 사용된다는 것을 알게 되었고, 카카오의 OAuth 2.0은 가장 기본적인 방식인 Authorization Code Grant - 권한 부여 승인 코드 방식을 통해 진행되며 해당 코드의 구조도 새로 분석하게 되었다.


매번 오픈소스를 활용해 해당 코드의 동작방식이나 원리들을 제대로 모르고 개발을 진행했는데 요즘 이런 방식을 통해서만 개발을 진행하기엔 많은 한계점이 있다고 스스로 뉘우치고 많이 깨닫는 중이다. 앞으로의 개발 방식을 바꾸고 코드를 깔끔하게 구성하여 완성하는 방법을 공부하고자 생각한다. 이후 간편 인증을 통해 API 요청을 보내는 방식에서도 많은 것을 느끼게 되었는데 개인적으로 이런 방식 말고 더 깔끔하게 코드와 프로젝트를 구성하는 방식이 있는지 궁금하여 앞으로는 프로젝트의 설계와 계획단계에서 어떤 방식으로 진행되는지 알아보고자 한다.

4) 간편인증을 통해 CODEF의 API요청을 보내보자

에러? 는 아니지만 코드를 짜는 것에 있어서 요즘 굉장히 고민이 많은 나에게 딱 알맞은 주제의 문제를 겪게 했던 사항이다.
내가 만들었던 프로젝트들을 나중에 다시 살펴보면 유지보수는 모르겠다~ 하는 습관, 코드는 오픈소스에서 잔뜩 긁어와 입맛에 맞게 활용하기만 하여 여러 코드가 덕지덕지 붙어 굉장히 더러워지는 모습들이 굉장히 많이 보여 결국 이후에 유지보수를 할 엄두도 내지 못하고 어떤 방식으로 코드를 짰던 건지 이게 무슨 코드인지 참 알기 어려웠던 경우가 많다.

이번 상황도 이런 내 문제를 정확히 짚어주기라도 하는 듯 생긴듯했다.

 

간편 인증을 통해 CODEF에 API 요청을 보낼 때 애플리케이션(Client)에서 Access Token을 발급해 주기 위한 회원 DB와 해당 관련 코드들을 수정하는 경우도 생겼지만 부가적인 것들은 제쳐두고 코드의 문제점이 있다고 느꼈던, 하지만 해결방법이 이거 말고는 생각나지 않았던 상황 하나에 대해서 얘기해보고자 한다.

기존의 페이지 구조

위 그림은 API 반환값이 돌아왔을 때 처리하는 부분을 구조도로 나타낸 것이다.
3개의 페이지 이외에도  종합소득세, 농어촌특별세, 주민세 과세표준확정신고 및 납부계산서, 신고서 표준대차대조표, 신고서 표준손익계산서, 신고서 법인세 과세표준 및 세액조정신고서 등등.. 총 11개의 페이지로 구분되어 있고 데이터를 각각 별개의 페이지에서 처리하도록 설계해 두었다.


문제는 여기서 발생했다. 간편 인증을 통한 개발도 진행해 달라는 유지보수 요청이 들어왔고, 해당 기능을 구현하기 위해선 API요청 이후 반환값을 이용해 다시 한번 API 요청을 해야 최종적으로 원하는 값을 전달받을 수 있는 방식이었다. 때문에 페이지의 구조를 밑의 그림과 같이 수정했다.

간편인증 진행 시 페이지 구조

전달받은 값들을 데이터 가공 페이지로 보내주기 이전, 각각의 추가인증 페이지로 값을 넘겨주고 해당 페이지에선 API 재요청을 처리하여 반환받은 값들을 그때서야 데이터 가공 페이지로 보내주며 DB에 저장할 수 있도록 처리해 주었다. 그림으로 보면 간단하지만 각각의 11개의 페이지를 구성해 두었던 상황이라, 11개의 페이지를 새로 만들고 재요청 입력부와 반환부 코드 구성 반환값을 다시 데이터 가공 페이지에 전달해 주는 과정을 수정하는 데는 굉장히 번거로웠다.

 

데이터 가공 와 DB에 입력되는 부분을 하나의 함수로 만들어 전달받은 값에 따라 각각의 DB로 입력되도록 코드를 구성해 두었다면 이번 유지보수 작업을 처리할 때 조금 수월하지 않았을까.. 하는 아쉬움이 남는다.
또는 다른 방식으로 처리할 수 있었다면 어떤 좋은 방법이 있었을까 하는 아쉬움이 많이 남았던 개발과정이었다.
개발 방식이나 코드 구성 방법에 대해 앞으로 나아가야 할 좋은 방향이 무엇인가 많은 조언을 받고 싶고 앞으로 많이 공부해야겠다고 다짐했다