카테고리 없음

API Response Data 처리의 3R

유지남 2020. 1. 3. 04:23

들어가며

프로젝트 진행하며 가장 많은 시간이 소요되는 부분은 설계 Logic을 세워 개발을 하는 것보다 함수의 이름을 정할 때다.
짧은 일정 내에 결과물을 내놓는 것도 어려운데, 산출물 인계를 위해 함수/변수 이름도 잘 정해야 한다.

 

특히 API 개발할 때 변수 이름이 얼마나 중요한지 드러난다.
API 개발은 Business logic의 따라 복잡도가 다르지만, 단순하게 보면 Query를 질의하여 얻은 Data를 요구 사항에 맞게 가공하여 출력하는 것이 전부다.

 

문제 파악

기능에 대한 이름 짓기도 어렵고, 일정도 촉박하니 함수 안에 2개 이상의 기능을 구현할 때가 많다.

이렇게 되면 추가/수정 시 다량의 Source code를 수정하게 되거나, 예상하지 못한 문제가 발생한다.

이 문제를 해결하기 위해 여러 사례를 훑어보고, 여러 가지 시도를 해보고, 실제로 적용을 해보면서 나름 규칙을 만들어 보았다.

 

먼저 함수형 프로그래밍을 배워보자.

사실 함수형 프로그래밍은 아주 오래전부터 나온 이론이다.
새삼스럽게 이런 방법론이 다시 거론된다는 것은 아직도 많은 사람들이 여러 기능을 하는 수행하는 함수를 작성하고,

문제가 발생했을 때 Code refactoring의 방향성으로 다시금 화두 되는 것이 아닐까 한다.

슬프게도 이 글을 쓰는 나 역시 “아직도 많은 사람들“ 중 하나이다.

 

자바스크립트 개발자라면 Lodash/fp, Ramda를 참고하면 되고, 개인적으로는 Ramda를 선호한다.

PHP는 Laravel의 Collect를 사용하면 된다.

이 밖에 다른 언어들도 함수형 프로그래밍 Library가 있으니 꼭 숙지하고 연습하길 바란다.

 

3R(Run, Reduce, Render)

Data 처리 순서를 정하고, 함수의 주요 Parameter와 역할로 구분 지었다.

  1. Run(id = null, where = []) : items
    기본적인 Primary key 검색과 추가 조건을 Parameter로 받아 단순히 Query를 질의하는 용도로 사용한다.
  2. Reduce(items) : items
    권한에 따라 달라지는 내용이나 외부 Interface와 연동하여 혼합한 내용을 가공 및 불필요한 내용을 여과한다.
  3. Render(items) : Response
    대부분의 경우 API Data는 공통으로 사용하고, Service type(쇼핑몰, 여행 서비스, 세무/회계 등)에 따라 출력되는 내용이 달라지는데, 이런 과정에서 약속된 Model에 맞게 다시 한번 가공하여 출력한다.

    그리고 XML, JSON 등의 API 문서 형식을 정하여 출력한다.

 

활용 사례

Nodejs로 실제 사용 했던 일부를 수정한 내용이니 결과를 보지 말고,

단순히 흐름이 어떻게 되는지만 참고하도록 하자.

function run({
    id = null,
    where = []
} = {}) {
    // 실제 Code에서는 ORM을 이용 했다.
    return Query("Select * from User");
}

function reduce(items) {
    // Data를 가공 한다.
    return items.filter(v => v.id > 0).reduce((a, b) => {
        a.push({
            id: b.id
        });
    }, []);
}

function render(items) {
    // 약속된 Model 정의에 맞게 가공 한다.
    return items.map(v => {
        return {
            primary: v.id,
        }
    })
}

function getUser() {
    // 단순한 쿼리를 질의 하고,
    let result = run();

    // 필요하다면 다른 기능을 처리 한 후에
    // 가공을 하고
    result = reduce(result);

    // 최종적으로 재가공 후 출력 한다.
    return render(result).toJSON();

    /* Ramda 이용시
    return R.pipe(
    	something_method,
    	reduce,
        render,
        toJSON
    )(run());
    */
}

function index() {
    return getUser();
}

 

마무리

RDB의 Query caching 장점 때문에 질의 내용이 조금 길어지더라도, Sub query, join 등을 사용하여 문법을 복잡하게 구현했었고,

이런 Query는 작성한 사람 외에는 Debug가 쉽지 않았다.

Query의 복잡도를 줄이고 Serverside에서 처리를 하니, Code가 조금 더 직관적으로 구현이 되고, Dubug가 수월했다.


아무래도 규칙이 생기니 유지보수가 쉬워졌지만,

아직도 “Query를 조금 더 잘하면 다른 더 좋은 대안이 있지 않을까?”의 대한 의문은 남아 있다.
결과적으론 만족하고, “서버사이드 연산속도는 생각보다 빠르다”라는 결론을 내렸다.