AWS lambda를 이용한 Proxy 서버 만들고 nodemodule을 추가하고 euc-kr 인코딩 xml 받아오기

ecof·2022년 11월 4일
0

Vue를 이용해 쇼핑몰의 껍데기 만들기 프로젝트를 진행중에 API를 받아오는 과정에서 CORS 오류를 만나 Vue 의 devserver 에서 제공하는 proxy를 이용해서 우회를 했었다.
당연히 임시방편으로 해놓은 것 이기 때문에 build를 한 후에는 사용 할 수 없는 방법이었다.
github page 를 이용해서 build 를 했기 때문에 github에 proxy를 적용 하는 방법이나 proxy가 적용 된 채로 github에 올리는 (기막힌 발상) 방법등을 강구했으나 찾을 수 없었다. 그 중 건진건 github page가 얼마전부터 업데이트가 되어 access-control-allow-headers 가 * 이 되었고 cors를 신경 쓸 필요 없다는 소식 이었지만 난 여전히 안된다는 사실이었다.
번역기를 돌려 github 형님들께 요청을 해보았지만 api를 주는 곳의 문제이고 내가 할 수 있는건 서버를 만드는거 말고는 없다는 답변을 들었고 proxy를 만들기로 결심하게 되었다.

-올렸던 질문 글
https://github.com/orgs/community/discussions/37317


aws 콘솔에 가입하고 lambda 에서 함수를 생성해준다.
aws 는 가입하고 하루 정도 뒤에 이용가능하다 해서 덕분에 하루 놀았다.

건드리는거 없이 함수이름을 정해준뒤 함수생성을 누른다. 여러가지 언어로 가능하지만 node.js 를 선택했다.

함수가 만들어지면 개요에서 트리거 추가를 누르고 API Gateway를 선택해준다.

create a new API를 선택하고 REST API, 보안은 Open 을 선택해준다.

이렇게 되면 API Gateway 가 생성되고 함수 개요에 생긴 API Gateway 를 눌러 보면 API endpoint를 확인 할 수 있는데

저 endpoint 를 이용해서 정보를 받아올수 있으며 뒤에 쿼리 방식을 이용하여 데이터를 전해줄 수 있다.
이제 lambda 에 내가 원하는 open API 에서 데이터를 받아오는 코드를 작성한다.

const https = require("https");

exports.handler = async (event) => {
    let key = '비밀이얌';
    let apiCode = encodeURI(event.queryStringParameters.apiCode);
    let searchTarget = apiCode === 'ProductSearch' ? 'keyword='+encodeURI(event.queryStringParameters.keyword) : 'productCode='+encodeURI(event.queryStringParameters.productCode)
    let etc = apiCode === 'ProductSearch' ? 'targetSearchPrd=KOR&pageSize=12&sortCd=A' : 'option=QAs,PostScripts,PdOption';
    
    const option = {
        protocol: "https:",
        hostname: "openapi.11st.co.kr",
        path: `/openapi/OpenApiService.tmall?key=${key}&apiCode=${apiCode}&${searchTarget}&${etc}`,
        encoding: null,
    }
    
    const resultObj = await new Promise((res, reject) => {
        https.get(option, (response)=>{
            var result = "";
            
            response.on('data', function (chunk) {
                result += chunk
            })
            
            response.on('end', function () {
                const body = {
                    headers: {
                        "Access-Control-Allow-Headers" : "Content-Type",
                        "Access-Control-Allow-Origin": "*",
                        "Access-Control-Allow-Methods": "OPTIONS,POST,GET",
                        "Content-Type" : "text/xml;charset=utf8"
                    },
                    statusCode: 200,
                    body: result,
                }
                res(body)
            })
        })
    })
    return resultObj;
};

https를 받아온뒤 그것을 이용하여 API에게 요청한다.
encodeURI(event.queryStringParameters.apiCode) 를 이용해서 endpoint 뒤에 붙는 '?쿼리이름=쿼리내용&두번째꺼=두번째' 형태의 쿼리를 받아 올 수 있다.
아마 다른 형태로도 요청을 할 수 있겠지만 참고할 만한 곳이 얼마 없었어서 그 중 되는걸 했기 때문에 코드의 작성 이유등을 정확히 정의하지 못했다.
설상가상으로 요즘 티스토리 블로그들이 터져서 글들을 참고하지 못해 더욱 정보가 한정되었다.
option 에 담긴 정보를 이용해 https.get 을 이용해 정보를 요청하고 end 부분에서 Access-Control-Allow-Origin 이나 인코딩 및 형태를 전해주어 xml을 받아 올 수 있게 했다.
여기서 생긴 문제는 내가 받아오는 11번가의 api 정보는 euc-kr 형태의 인코딩을 제공하는데 node가 그런건지 lambda 가 그런건지 euc-kr 인코딩이 안된다는 것이다.
버퍼에 넣은 뒤에 인코딩을 하든 받아온걸 utf-8로 바꾸는 binary로 바꾸든 무엇을 해도 한글은 깨졌다.
역발상으로 인코딩 자체를 안하고 proxy에서 vue로 전해준뒤 vue 에서 인코딩을 하려고 했지만 받아올때 아무리 encoding 이 null 이라도 lambda 에서 출력할때 utf-8로 바뀌는건지 (뇌피셜) 그것 또한 불가능 했다.

이틀간 삽질끝에 lambda 에 nodemodule을 넣어 iconv-lite를 lambda 에서 사용하는 형태를 사용했다.
lambda 에서 nodemodule 을 사용하기 위해서는 layer 를 사용해야 한다.


추가 리소스에 계층을 눌러 layer 로 들어간다.

계층 생성을 누르고

이름 을 정한뒤 호환런타임에서 node.js 를 선택하고 nodemodule 을 압축하여 업로드해주면 되는데 중요한 점은 nodemoule 을 그대로 압축해서 넣으면 안된다는 점이다.

aws 에서 제공하는 가이드 대로 nodejs 폴더 안에 nodemodule 이 있는 형태로 압축을 해서 넣어주어야만 한다.

그 뒤 코드를 작성 할 수 있는 페이지에서 가장 아래로 내리면 계층 부분이 있다. Add a layer 를 눌러 준다.

사용자 지정 계층을 선택하고 내가 추가했던 계층을 골라준다. 수정사항이 있을시 같은 이름의 계층으로 업로드 하면 버전이 쌓이게 된다. 사용하고픈 버전으로 골라 주면 된다.
이렇게 nodemodule 안에 iconv-lite 를 넣고 layer에 추가 했다면 코드 내에서 사용해주면 된다.

const https = require("https");
const iconv = require('iconv-lite')

exports.handler = async (event) => {
    let key = '비밀이얌';
    let apiCode = encodeURI(event.queryStringParameters.apiCode);
    let searchTarget = apiCode === 'ProductSearch' ? 'keyword='+encodeURI(event.queryStringParameters.keyword) : 'productCode='+encodeURI(event.queryStringParameters.productCode)
    let etc = apiCode === 'ProductSearch' ? 'targetSearchPrd=KOR&pageSize=12&sortCd=A' : 'option=QAs,PostScripts,PdOption';
    
    const option = {
        protocol: "https:",
        hostname: "openapi.11st.co.kr",
        path: `/openapi/OpenApiService.tmall?key=${key}&apiCode=${apiCode}&${searchTarget}&${etc}`,
        encoding: null,
    }
    
    const resultObj = await new Promise((res, reject) => {
        https.get(option, (response)=>{
            var result = "";
            
            response.on('data', function (chunk) {
                result += iconv.decode(chunk, 'euc-kr')
            })
            
            response.on('end', function () {
                const body = {
                    headers: {
                        "Access-Control-Allow-Headers" : "Content-Type",
                        "Access-Control-Allow-Origin": "*",
                        "Access-Control-Allow-Methods": "OPTIONS,POST,GET",
                        "Content-Type" : "text/xml;charset=utf8"
                    },
                    statusCode: 200,
                    body: result,
                }
                res(body)
            })
        })
    })
    return resultObj;
};

response 에서 data를 result 에 추가하는 부분에서 iconv를 이용해 euc-kr로 디코드하는 부분을 추가했다.
이렇게 build 된 프로젝트에서도 proxy를 이용해 api를 받아올 수 있게 되었다.

++참고한 글
https://nookpi.tistory.com/99
(근데 티스토리 터져서 언제 보일지는 모름)

추가로 내 lambda 함수를 아무나 사용하지 못하도록 api 키를 추가하거나 Access-Control-Allow-Origin 설정을 따로 해 줄 수 있는데 현재 티스토리가 터져 보이지도 않고 지금은 잘 돌아가는 상황이라 나중에 추가할 예정이다.

profile
프론트엔드 개발 지망

0개의 댓글