티스토리 뷰

Backend

성능 테스트를 위한 격리 - 시뮬레이션

지마켓 선현상 2022. 12. 21. 14:35

시뮬레이션

이전 글을 통해 테스트에 사용할 기록들을 캡처하여 확보했습니다. 이렇게 캡처한 내용을 바탕으로 mock service 를 띄워 이제는 시뮬레이션할 수 있습니다. 이제 테스트를 수행해 봅시다. hoverfly dashboard 에서 simulate 모드로 변경하거나 기록한 파일을 import 하여 hoverfly 를 재기동하면 테스트 환경으로 활용할 수 있습니다. 하지만 몇 가지 좀 더 수월한 테스트 조력을 원하였습니다.

 

  • hoverfly 는 매우 디테일하게 기록하지만 실제 테스트에 적합한 동작은 덜 디테일하거나 추가적인 디테일이 필요할 수 있다.
  • mocking 하는 서비스의 특징을 흉내 낼 때, 각 config 가 서비스 단위로 관리할 수 있는 게 편했다.
  • hoverfly 는 가볍고 빠르다고 하지만 실제 성능 테스트에 필요한 부하 환경에서 쓰기엔 부족했다.

이 점들을 하나씩 살펴보겠습니다.

관련 글

테스트에 적합한 동작

mock service 는 기본적으로 요청에 반응하는 응답을 바랍니다. hoverfly 역시 요청-응답 쌍으로 캡쳐하므로 mock service 로써 역할을 충분히 수행할 수 있습니다.

 

여기서 꽤 자주 마주하는 상황이 있습니다. 보통 매 요청마다 새로 생성하는 값들입니다. 만약 요청에 timestamp 성격의 데이터나 혹은 요청번호 같은 sequence 성격의 데이터가 있다면 단순한 캡쳐 결과로는 예상하는 응답을 매칭해주지 않을 것입니다. 기록했던 요청의 데이터와 해당 부분이 다르기 때문이죠. 이런 데이터는 매칭에서 가볍게 무시해주면 됩니다.

 

물론, hoverfly 가 제공하는 simulation 문법에 이럴 경우를 위한 매칭 문법들을 제공합니다. 하지만, 말하자면 capture 방식에서는 이를 아주 멋지게 추론해 주지는 않습니다. 캡쳐 결과를 바탕으로 필요한 매칭 문법으로의 수정을 약간 해야 합니다.

 

예를 들어, body 에 대한 기본 결과가 다음과 같을 때,

{
  "matcher": "exact",
  "value": "{\"requestKey\":\"1000\",\"itemNo\":\"10000001\",\"sellerNo\":\"1000001\",\"qty\":1,\"price\":10000}"
}

이 요청은 부하 시, 매 요청마다 바뀌는 requestKey 를 대응하지 못합니다. 이 경우는 다음 방식으로 대응할 수 있습니다.

{
  "matcher": "jsonpartial",
  "value": "{\"itemNo\":\"10000001\",\"sellerNo\":\"1000001\",\"qty\":1,\"price\":10000}"
}

이제 requestKey 에 대한 매칭은 무시하고 요청의 내용이 동일하면 원하는 응답으로 대응할 수 있습니다.

 

그런데 해당 요청의 응답 모양은 다음과 같았습니다.

{
  "status": 200,
  "body": "{\"requestKey\":\"1000\",\"itemNo\":\"10000001\",\"fee\":50}"
}

요청 시 requestKey 는 무시하였지만 요청에서 들어온 값과 상관없이 응답에서는 해당 값으로 1000 을 보낼 것입니다. 이 또한 hoverfly 에서 제공하는 문법이 있습니다.

{
  "status": 200,
  "body": "{\"requestKey\":{{Request.Body 'jsonpath' '$.[0].requestKey'}},\"itemNo\":\"10000001\",\"fee\":50}"
}

이렇게 request 와 response 의 스펙을 변경하면 우리가 원하는 방식으로 동작합니다. 이 작업은 사실 충분히 자동화 도구로 대체 가능한 작업입니다. 따라서, 단순한 설정을 통해 원하는 문법 변경을 적용하는 도구를 활용합니다.

{
  "ignoreProperties": [
    {
      "path": "/v1/fee",
      "position": "requestKey"
    }
  ],
  "templatedProperties": [
    {
      "path": "/v1/fee",
      "position": "[0].requestKey",
      "from": "[0].requestKey"
    }
  ]
}

이런식으로 hoverfly 가 남겨준 아주 충분한 기록을 바탕으로 테스트에 적합하도록 수정하는 도구를 만들었습니다. 제가 사용한 도구는 JSON 편집에 용이한 javascript 를 돌릴 수 있는 node.js 어플리케이션으로 개발하여 사용하였습니다. 이를 통해 매번 동일한 요구사항을 담은 문법 변경을 자동으로 수행하면 테스트 시나리오를 다양하게 준비하는 과정이 훨씬 수월합니다.

서비스의 특징

캡쳐 시에는 하나의 hoverfly 로도 요구사항이 충분합니다. hoverfly 는 destination 도 꼼꼼히 기록해주기 때문에 하나의 시나리오에 포함한 모든 네트워크 내역을 모두 하나의 hoverfly proxy 에 흘려 한꺼번에 기록하는 편이 관리가 편했습니다.

 

하지만 시뮬레이션은 하나의 hoverfly 에서 할 필요는 없습니다. 특히나 각 서비스의 특징들을 적용하려고 할 때는 서비스 별로 시뮬레이션 서버의 인스턴스를 따로 하면 좋습니다.

 

예를 들어 네트워크 내역은 서비스를 막론하고 테스트 시나리오 별로 모아두는 편이 편하지만 특정 서비스의 응답시간을 적용하는 것은 테스트 시나리오보다는 해당 서비스를 기준으로 설정을 관리하는 것이 편합니다. hoverfly 는 여러 단계의 delay 에 관한 설정을 할 수 있습니다. 다음과 같이 서비스 전체가 15ms 의 지연응답을 하도록 할 수도 있죠.

{
  "globalActions": {
    "delays": [
      {
        "urlPattern": ".",
        "delay": 15
      }
    ],
    "delaysLogNormal": []
  }
}

이때, 각 시뮬레이션을 위한 hoverfly 는 모든 네트워크 기록을 들고 올라갈 필요가 없습니다. 따라서, 테스트 시나리오 기록 중 해당 서비스 도메인과 관련한 요청-응답 기록만 필터링하면서 적용하도록 도구를 사용하기로 하였습니다.

 

이 도구는 위의 문법 변경을 위한 도구와 협력할 수 있습니다. 실제로도 이 두 도구를 협력하여 좀 더 매끄러운 테스트 준비과정을 마련하였습니다.

테스트에 충분한 부하를 견디기

지금까지의 준비를 토대로 실제 부하 테스트를 진행하는 과정에서 사실상 hoverfly 가 성능 테스트를 도울만한 도구는 아니라는 걸 확인하였습니다. 우리는 테스트 시나리오를 마련하는 과정에 테스트 경계 밖의 요청-응답을 기록하고 테스트 용이한 행위로 시뮬레이션을 적용하는 것이 큰 도움을 받았지만 그것으로는 부족하였습니다.

 

결국 부하를 충분히 받을 수 있는 mock server 어플리케이션을 따로 도입하기로 하였습니다. 대신, 앞서 마련한 hoverfly 문법을 지원하여 지금까지 활용한 방법과 도구들을 그대로 재사용할 수 있는 환경을 원했습니다.

 

지금까지의 도구들은 node.js 로 만들었으며 역시 지연시간을 흉내 내면서 부하를 견디기에는 node.js 만한 방식이 없었습니다. reactor 패턴으로 non-blocking I/O 를 사용하기 때문에 지연시간을 흉내 낼 때 전혀 CPU 에 부담을 주지 않기 때문입니다.

 

물론 역시, 위에 언급한 도구들과의 협력 또한 매끄러웠습니다. hoverfly 는 JSON 을 문법으로 쓰기에 JSON 친화적인 node.js 는 매우 적합했습니다. 아예 mock server 어플리케이션에 이 도구들을 포함하여 캡쳐 원본과 서비스 설정을 전달하면 대상 서비스를 특정한 mock server 로 기동할 수 있도록 작성하였습니다.

 

node.js 를 기반으로 한 mock server 는 아주 훌륭하게 부하를 받아주었으며, hoverfly 도구들을 재사용한 방식은 여전히 매끄러운 테스트 준비과정을 제공하였습니다.

정리

이 글에서는 테스트 환경에 필요한 격리를 위해 hoverfly 를 활용하는 방안을 소개하였습니다. 래리 월이 말한 프로그래머가 가져야 할 세가지 덕목 에는 "나태"가 있습니다. 테스트를 반복적으로 수행하는 일은 제품의 품질에 더없이 영향이 큽니다. 하지만 테스트의 반경이 커질수록 준비하는 것도 수행하는 것도 부담이 늘죠. 나태한 개발자는 이때 도구를 만들 것입니다. 어제보다 조금 더 나태해지기 위해서 테스트를 위한 데이터를 일괄 캡쳐하고 이를 테스트 성격에 맞게 변형하여 테스트 변수들을 적용한 mock service 를 올리는 과정을 위한 도구를 만들면 이 과정들이 모두 수월해집니다.

 

가장 중심적인 자동화는 hoverfly 입니다. 이를 위해 테스트 데이터를 수집, 기록, 재현하는 과정에 상당 부분 손을 덜었습니다. 약간 미진한 부분은 작고 잘 동작하는 도구들을 만들어 추가적으로 손이 덜 가도록 하였죠.

 

지금까지에 걸쳐 조금 완벽하지 않더라도 오히려 부담이 적은 성능 테스트를 위한 노력들을 소개해 보았습니다. 이 과정을 통해서 자동화 또는 부담을 낮춘 반복적인 테스트 수행이 제품의 품질과 개발 경험에 얼마나 많은 도움을 주는지 다시 한번 확인한 계기로 삼았습니다.

 

긴 글 읽어주셔서 감사합니다.

댓글