Typescript와 AWS Lambda로 모니터링에 유용한 API 패턴 구성하기

Harry The Great
해리의 유목코딩
6 min readMay 22, 2019

이전에… 작성했던 괴랄한 Typescript 패턴글을 반성하며 조금은 자세히 현재 사용하고있는 패턴에대해 조금은 자세히 적어보려합니다.

람다를 크게 세 부분으로 나눕니다.

  • 사용자로부터 받은 데이터를 검증해주는 컨트롤러
  • 비즈니스로직을 구성하는 서비스
  • API나 데이터베이스와 연결되는 레파지토리

그럼 상품을 구매하는 Shop 예제를 통해 차근차근 보겠습니다.

Shop예제

위 이미지처럼 컨트롤러는 생성자로 서비스를 , 서비스는 생성자로 레파지토리를 가지고있고 handler 메서드를 export하는형태로 구성되어있습니다.

컨트롤러에서 검증하기

Lambda에서 최상위 Handler의 역활을 하는것이 컨트롤러입니다. 요청받은 queryString이나 JSON데이터가 올바른 형식인지를 검증하고 문제가 없다면 서비스에 파라미터로 전달합니다.

PurchaseItem 메서드로 event객체를 통해 queryStringParamter를 받습니다. 이때 요청한 형식이 올바르게 요청했는지를 검증한 후 그렇지 않다면 400에러를 요청을 제대로 하였다면 생성자로 가지고있던 Service의 purchaseItem으로 전달됩니다.

서비스로 로직 처리하기

Controller로부터 인자를 받은 서비스에서는 데이터를 레포지토리를 통해 검증합니다. 예를들어 요청한 사용자가 실제 존재하는 사용자인지 혹은 실제 상품정보가 있는지 그리고 구매할려는 상품과 비교하여 포인트가 충분한지등입니다.

레파지토리로 데이터 가져오기

데이터베이스 혹은 API와 통신하는 Repository는 되도록 작은 단위로 쪼개는편이 재사용성에 좋습니다. 예를들어 유저의 정보를 가져오고 업데이트한다면 유저정보를 가져와 업데이트 하는것보다 유저정보를 가져오는 부분 업데이트 하는부분을 쪼개어 다른 서비스 메서드 등에서도 사용할 수 있도록하면 좋습니다. (물론 트랜잭션처리가 필요하다면 묶어서 작업합니다)

컨트롤러 에러처리

조금 전 서비스 코드를보면 데이터에 문제가 있을때 Throw Error를통해 컨트롤러의 Catch에서 처리하도록 위임합니다. 이때 받은 에러의 유형을 Enum값으로 분류하여 에러의 유형에 맞게 상태코드와 메세지를 리턴합니다. 만약 핸들링할 수 없는 에러라면 에러로그를 Console등으로 기록하여 클라우드와치에서 관리할 수 있어야합니다.

에러모니터링

상태코드를 잘활용하면 X-RAY를 통해서도 모니터링 할 수 있고 VPC FLows 와 같은 서비스를 통해서도 문제상황을 알 수 있습니다. 예를들어 실수로 상품정보나 유저정보등이 삭제되었지만 배너를 통해서 계속 요청이 들어올때 간헐적으로 발생한다면 운영할때 문제를 알아차리기 쉽지 않지만 StatusCode별로 모니터링한다면 갑작스레 404에러나 500에러등이 급증하는것을 기반으로 요청에 문제가 생겼다는것을 알 수 있습니다.

시중에 많은 서비스가있지만 규모가 크지 않은 경우라면 AWS Quicksight과 AWS Flowlogs를 연동시켜 갑작스래 400번대 혹은 500번대 에러가 급증하는경우 이메일로 알림을보내어 상황을 모니터링할 수 있습니다.

테스트코드작성

레파지토리 테스트코드 작성

레파지토리는 서비스나 컨트롤러와 같은 의존성을 가지고있지않기때문에 현재 사용하는 데이터베이스에 맞게 작성합니다.

서비스 테스트코드 작성

서비스에서는 ts-mockito라는 테스트라이브러리를 사용하였습니다.

아무래도 자바테스트코드를 짜다보면 mochito라는 테스트라이브러를 많이사용해서 선택하게되었는데 컨트롤러를 테스트할때는 몇가지 문제가 있습니다. 이부분은 차후 설명하겠습니다. 레파지토리 클래스를 Mocking한 후 인스턴스를 만들어 서비스에 주입해줍니다. 서비스를 테스트할때 Repository에 대한 의존성 없이 하기 위함인데 만약 Repository에서 리턴해야하는 데이터가 필요하다면 아래와같은 형태로 미리 정의해줍니다.

when(repository.getUser).thenReturn(리턴할데이터);

반대로 repository로 전달한 인자에대한 데이터를 검증하고싶다면 capture를 사용합니다.

capture(repository.getUser).last()

위 코드는 Service에서 Repository의 getUser로 전달한 파라미터의 마지막값을 가져옵니다.

컨트롤러 테스트코드 작성

ts-mochito는 interface를 mocking할 수 없는데 콜백등을 활용하여 비슷하게 구현하거나 혹은 Lambda-mock등과같은 라이브러를 사용해볼 수 있지만 필자는 TypeMoq이라는 라이브러를 이용하였습니다.

typemoq는 interface를 사용하여 Mocking할 수 있는 오브젝트를 생성해줍니다. 또한 미리 원하는값으로 오버라이딩할 수 있습니다.

마치며

Lambda의 ProxyHander혹은 DynamoDB Stream Handler를 Mocking하는 방법에 대해서는 되도록 위처럼 여러가지의 라이브러리를 혼용하는방법은 유지보수면에서 좋지 않을것같습니다. 타입스크립트에대한 내공이 많이 부족하여 조금 더 매끄럽게 테스트코드를 처리할 수 있는 방법에대한 공부가 필요할것같습니다.

위와같이 되도록 StatusCode로 에러를 분기하고 StatusCode로 모니터링하는 방법은 문제가 생기고있지만 에러가 나지않아 모니터링되지않는 문제점들을 찾는데 유용합니다.

--

--

Harry The Great
해리의 유목코딩

Android & IOS Developer 😀 미디움 이외에 스니펫이나 디버그노트로 활용하는 https://www.harrymikoshi.com/ 블로그도 운영하고있습니다.