본문 바로가기

개발

Prism + Node Proxy +Kafka Node 를 용한 Rest Mocking Server 구성하기

728x90
프로젝트에서 다른 모듈과 연계시 오래 걸리는 작업의 결과를 받아 오기 위해 Kafka 를 사용 하고 있다. 이번에도 Rest API 를 이용하여 연계를 하기로 하였다.이런 경우에는 언제나 그렇듯이 연계 시스템들이 잘 개발 되었있을 것이란 믿음은 깨졌다.

 

이런 이유로 Mock서버를 구성 하였는데, 여느 경우와 달리 연계 모듈에서 오래 걸리는 작업의 결과는 Kafka 로 보내 주어야 한다는 경우로 인해 조금 머리가 아팠다.

 

  • Rest API 로 연계

  • Rest API + Kafka 로 연계

 

Rest API 로만 연계 하는 경우는 PRISM을 이용하여Rest Mocking Server 를 구성하면 되지만, 응답을 KAFKA 로 넘겨줘야 하는 경우 때문에 어떻게 해야 할지 고민을 하게 되었다.

 

NODE 에 익숙하진 않았지만, 빠르게 구성 하기 위해 NODE 기반의 http-proxy, express, kafka-node 를 이용하기로 결정하였고 아래와 같이 동작 하도록 Mock Server 를 구성하였다.( 전체 코드 : https://github.com/jjeaby/JRestMockServer )

- Rest API 로 연계 : 1→2→3→4
- Rest API + Kafka 로 연계 : 1→2→3→4→5→6

 

제일 먼저 PRISM 으로 Mock Server를 실행하는 코드를 작성한다.

PRISM 은 Swagger Spec Document 를 기반으로 Mock Server 를 구성해 주는 도구로 자세한 설명은 여기(= PRISM 으로 REST API Mock Server 구성) 를 참고 하길 바란다.

 

PRISM 을 실행하는 것은 node 의 spawn 을 이용하여 cli 로 구동 하도록 하였다. 여기서 조금 고민한 부부은 kafka 에서 오류가 발생한 경우 PRISM 이 함께 종료되지 않는 경우가 있어서 prismMockServer.on('close', (code) .... 에서 Exception 메세지를 남기로 process.exit 를 이용해 PRISM process 를 종료 한 것이다.

/*
* Prism 으로 기본 Mock 서버 구동 하기
* */
let prismMockServer = () => {
    prismMockServer = spawn('prism', ['mock', '--host', '0.0.0.0', '-p', prismMockPort, '-d', prismMockSwaggerDoc]);

    prismMockServer.stdout.on('data', (data) => {
        data = data.toString().replace(/\r?\n|\r/g, " ");
        console.log(`stdout: ${data}`);
    });
    prismMockServer.stderr.on('data', (data) => {
        data = data.toString().replace(/\r?\n|\r/g, " ");
        console.log(`stderr: ${data}`);
    });
    prismMockServer.on('close', (code) => {
        throw {name: 'kafkaException', message: 'kafka 서버 연결 오류가 발생하였습니다.'}
        process.exit(3);
    });
    return prismMockServer
}

 

이제 http-proxy 를 이용하여 25001 port 로 요청되는 모든 데이터를 15001 port 로 요청하고, 특정 Rest API 의 경우 kafka 로 Produce 하는 코드를 작성한다.

 

kafka 로 Topic 을 Produce 하는 경우를 구분하기 위해 kafkaSendRestApi 를 마들고 METHOD REST Api를 key 로 갖는 JSON 을 생성한다.

 

const kafkaSendRestApi = {
    "GET /PETS?LIMIT=286": {
        "code": "200",
        "message": "success"
    }
};

 

이제 http-proxy 를 이용하여 25001 port 로 연결되는 모든 정보를 15001 port 로 보내는 코드를 작성한다.


여기서 잘 봐야 할 부부은 proxyRes.on('end', function 에서 res.end() 를 이용하여 response 값을 변경 시킬수 있다는 것과 kafkaSendRestApi 에 추가한 METHOD REST Ap 조건인 경우 sendKafka 함수를 이용하여 Topic 을 Produce 해준다는 것이다.

 

/*
* MOCK Proxy Server 구성
* */
let mockProxyServer = (mockServerName, mockServerUrl, mockWithKafkaServerUrl) => {
    let prismMockPort = parseInt(mockServerUrl.split(":")[2]);
    let proxyMockPort = parseInt(mockWithKafkaServerUrl.split(":")[2]);

    let option = {
        target: mockServerUrl,
        selfHandleResponse: true,
    };

    const proxyServer = createProxyServer({});
    proxyServer.on('proxyReq', function (proxyReq, req, res) {
        proxyReq.setHeader('Content-Type', 'application/json');
        if (req.body) {
            let bodyData = req.body;
            proxyReq.setHeader('Content-Length', Buffer.byteLength(JSON.stringify(bodyData)));
            proxyReq.write(JSON.stringify(bodyData));
        }
    });

    proxyServer.on('proxyRes', function (proxyRes, req, res) {
        let restApi = `${req.method} ${req.url}`;
        let body = [];
        res.setHeader('Content-Type', 'application/json');
        proxyRes.on('data', function (chunk) {
            body.push(chunk);
        });
        proxyRes.on('end', function () {
            body = Buffer.concat(body).toString();

            let bodyJson = JSON;
            bodyJson.status = "success";
            bodyJson.result = JSON.parse(body);

            if (kafkaSendRestApi[restApi.toUpperCase().trim()]) {
                sendKafka("topic", "messageKey", JSON.stringify(bodyJson));
                res.end(JSON.stringify(successResponse));
            } else {
                res.end(JSON.stringify(bodyJson));
            }
            // }
        });
    });


    const mockServer = express();
    mockServer.use(bodyParser.json());
    mockServer.use(bodyParser.urlencoded({extended: true}));
    mockServer.use(function (req, res) {
        proxyServer.web(req, res, option)
    });
    console.log('listening', '\x1b[31m', `${mockServerName}`, '\x1b[0m', '\x1b[36m', 'With Kafka on port,', '\x1b[31m', `${proxyMockPort}`, '\x1b[0m', ', only Mock on port', '\x1b[31m', `${prismMockPort}`, '\x1b[0m');
    mockServer.listen(proxyMockPort);
}

 

이제 kafka-node 를 이용하여 kafka 로 topic 을 produce 해주는 sendKafka 코드를 작성한다.

 

/*
* KAFKA 관련 설정
**/
const sendKafka = (topicName, messageKey, message) => {
    const client = new kafka.KafkaClient({kafkaHost});
    const producer = new kafka.Producer(client);
    const payloads = [
        {topic: topicName, key: messageKey, messages: message},
    ];
    producer.send(payloads, function (err, data) {
        if (data === undefined) {
            prismMockServer.kill('SIGHUP');
        }
    });
}

 

이렇게 코드를 모두 작성 하고 서버를 실행 시키면
  • 아래와 REST Api Mock Server 가 구동되고

  • kafkaSendRestApi 에 METHOD REST API 가 추가된 경우 Response 응답 Rest API + Kafka 로 연계 : 123456

 

  • kafkaSendRestApi 에 METHOD REST API 가 추가된 안된 경우 Response
    Rest API 로 연계 : 1234

 

 

전체 코드는 생각 보다 간단하게 작성 되었습니다. 개발시 급하게 사용하게 되어서 급하게 만들어 코드가 엉망이네요.(^^;; 사실 node 를 잘 몰라서)

 

REST API Mock Server 를 구성하여야 하는데 다른 서버를 거쳐야 한다는 조건이 있는 경우도 이 코드를 활용 할 수 있을 겁니다:) 그럼~~ 즐거운 개발 세발~~:)