아마존에서의 첫 작업

내 이 세상 도처에서 쉴 곳을 찾아보았으나, 마침내 찾아낸, 컴퓨터가 있는 구석방보다 나은 곳은 없더라.

아마존에서의 첫 작업

입사한 지 두 달 만에 의미 있는 일을 하게 되었다. 새로운 프로젝트에서 사용할 메트릭 라이브러리를 만드는 작업이었다. 서비스가 호출될 때 또는 서비스 내에서 다른 다운스트림 서비스를 호출할 때 메트릭 정보를 남겨두면 모니터링 도구를 이용해 서비스가 호출된 횟수, 요청을 처리하는 데 걸린 시간, 다운스트림 서비스를 호출하는 데 걸린 시간, 오류 발생 빈도 등을 확인할 수 있다. 아마존은 이미 이런 작업을 위한 내부 도구를 갖추고 있다. 메트릭 정보를 기록하는 라이브러리도 있다. 내가 할 일은 우리 팀에서 새로 개발할 서비스에서 메트릭 정보를 쉽게 남길 수 있게 하는 라이브러리를 만드는 일이었다.

메트릭이라 하니까 뭔가 어려운 것 같지만 사실 로깅과 비슷하다. 이 작업에 대한 이야기를 듣는 순간 AOP(Aspect Oriented Programming)를 활용해야겠다고 생각했다. 메트릭 정보를 기록하는 것은 핵심 비즈니스 로직이 아니지만, 서비스 전반에 걸쳐 사용될 것이다. AOP에 딱 맞는 작업이다. AOP와 어노테이션을 활용하면 아주 편리하게 사용할 수 있는 라이브러리를 만들 수 있을 것 같았다. 메트릭 정보를 기록하기 위해 매번 메서드를 호출하는 대신 필요한 곳에 어노테이션을 달아주면 자동으로 메트릭 정보가 기록될 것이다.

아마존 내부에도 선언적 메트릭을 지원하는 라이브러리가 있긴 했다. 그러나 다음과 같은 문제로 사용하지 못했다. 첫째, 라이브러리에 문서가 제대로 업데이트되어 있지 않아 적용하기가 어려웠다. 팀 안의 누구도 그 라이브러리를 성공적으로 적용할 수 없었다. 둘째, 팀의 요구사항을 모두 만족하지 못했다. 이 문제는 조금 미묘하다. 요구사항이 명확하게 정리된 게 아니었고 개발자마다 생각하고 있는 게 달랐다. 새로 만든다고 해도 모든 사람이 만족하는 공통 라이브러리는 아마도 불가능할 것이다. 그렇지만 우리가 만들었다면 우리 요구사항에 맞춰 수정할 수 있을 것이다.

AOP와 어노테이션을 이용해 선언적 메트릭 라이브러리를 만드는 데 약간의 문제가 있었다. Spring을 제대로 보기 시작한 지도 얼마 되지 않았고 어노테이션은 사용하기만 했을 뿐 직접 만들어본 적이 없었다. AOP를 들은 지는 10년도 넘었지만 써본 적은 한 번도 없었다. Spring in Action 4판에서 AOP 부분을 읽으며 조금 맛본 게 내가 아는 전부였다. 처음 맡은 작업인데 잘 알지 못하는 기술을 사용해 버벅거리다가 망하면 어쩌나 걱정도 됐지만, AOP를 적용하는 게 옳다는 생각은 변하지 않았다.

AOP를 적용하겠다고 하자 팀원 중 한 명이 'AOP는 안 좋다'고 말했다. 그러나 그 이상 말하지는 않았다. 뒤에서 AOP가 안 좋다고 궁시렁 거리기만 했을 뿐 왜 그렇게 생각하는지 설명하지 않았다. 그의 의견을 무시하기로 했다. 내게 직접 말한 것도 아니고, 합당한 이유도 제시하지 않았기 때문이다. 예전에 내가 Lombok을 사용하면 보일러플레이트 코드를 줄일 수 있다고 했을 때도 그는 '그런 식의 마법을 사용하느니 그냥 보일러플레이트 코드를 작성하는 게 낫다'고 했었다. 그렇게 생각하는 이유를 물었지만 답이 없었다. 그때는 Lombok을 사용하자고 강하게 주장하지 못했지만, 이번에는 AOP로 밀어붙일 작정이었다. 다른 동료는 내 의견을 지지해 주었다. 게다가 알고 보니 팀 내에서 이미 AOP를 사용하는 사람도 있었다. 그에게 도움을 받을 수 있겠다고 생각하니 마음이 조금 편해졌다.

작업을 시작하니 기술적인 부분은 큰 문제가 아니었다. 구글과 스택오버플로우, 그리고 동료의 도움으로 문제를 하나씩 해결해가며 라이브러리의 모양을 갖춰가기 시작했다. 어노테이션을 정의하고 AOP를 적용하는 것은 예상했던 것보다 쉬웠다. ajc로 컴파일하는 부분에서 조금 헤맸지만 동료의 도움으로 무사히 넘어갈 수 있었다. 뜻밖에 나를 애먹인 것은 불확실한 요구사항이었다. 동료와 논의하며 요구사항을 구체화할 수 있다고 생각했으나, 그들도 자기 일로 바빴다. 나는 원하는 정보를 제때 얻을 수 없었고 개발은 조금씩 지체되었다.

겨우 제대로 동작하는 버전을 만들어내긴 했는데, 일부 클래스가 정적 유틸리티 클래스가 된 게 마음에 들지 않았다. Aspect 클래스에 스프링 빈을 넣어줘야 하는데 그게 잘 안 됐다. 스프링 빈으로 만들어 Aspect 객체에 종속성을 주입하기 위해 여러 가지 시도를 했지만 모두 실패했다. 정적 유틸리티 클래스로 된 부분이 외부 라이브러리와 엮이는 부분이라 단위 테스트를 만들 수가 없었다.

며칠을 고민하며 구글로 검색하고 스택오버플로우를 뒤지다 마침내 방법을 찾았다. 정적 유틸리티 클래스를 스프링 빈으로 만들었다. 외부 라이브러리와 엮이는 부분을 모킹(mocking)할 수 있게 되어 단위 테스트를 작성할 수 있었고, 테스트 커버리지도 90%를 넘길 수 있었다. 설정 방법, 사용법 등을 위키 문서로 작성해 공유했다. 이제 다음과 같이 원하는 메서드에 어노테이션을 추가해 메서드가 호출된 횟수, 수행하는 데 걸린 시간을 기록할 수 있게 되었다. 어노테이션에 속성을 지정해 메트릭 키를 원하는 대로 바꿀 수도 있고, 리턴 값을 기록하게 할 수도 있다.

  @Counting
  public void doSomething1(...) {
    // ...
  }

  @Timing
  public void doSomething2(...) {
    // ...
  }

1:1 면담에서 매니저는 '라이브러리도 잘 만들었고 문서 작업도 환상적으로 잘했다'는 과분한 칭찬을 했다. 행복한 결말이다. 그렇지 않은가.

라이브러리는 만들었다고 일이 끝나는 게 아니다. 라이브러리가 사용되지 않는다면 의미가 없다. 다음 할 일은 새로 만든 서비스에 메트릭 라이브러리를 시범 적용하는 것이었다. 여기서 지저분한 현실에 직면했다. 어노테이션 만으로는 그 서비스에서 메트릭을 사용하는 방식을 맞출 수가 없었다. 어쩔 수 없이 인라인 메트릭이란 이름으로 어노테이션 대신 메트릭 정보를 남기는 메서드를 직접 호출할 수 있게 API를 추가해야 했다. 여기까지는 어느 정도 예상했었다. 메트릭을 남길 메서드에 어노테이션을 붙이는 대신 메서드 안에서 메트릭 메서드를 직접 호출할 수 있게 되었다. 아름다움에 약간 흠이 가긴 했지만, 아름답기만 하고 실제로 적용할 수 없다면 아름답다고 할 수도 없지 않겠는가. 밖으로 노출되지 않던 메서드를 노출하기만 했을 뿐 내 라이브러리가 지저분해진 것도 아니니까 그렇게 나쁜 상황도 아니었다.

그런데 라이브러리를 사용하는 개발자가 내가 열어준 API만으로 만족하지 못했다. 이건 어떻게 보면 메트릭을 어떻게 남길까에 대한 문제다. 메트릭 정보를 어떤 구조로 남길 것인지, 어떻게 정규화할 것인지에 대한 고민 없이 과거에 남기던 방식으로 그대로 아무런 일관성 없이 사용하려다 보니 문제가 생긴 것이다. 그러나 그런 논의를 할 만큼 시간이 충분하지도 않았다. 팀 전체가 모여 토론해야 하는데 다들 바쁘다 보니 그렇게 하지 못했다. 메트릭 라이브러리를 만든 내가 회의를 소집하고 논의를 주도해야겠지만 영어가 딸리다 보니 그렇게도 할 수 없었다.

메서드에 가변인자가 추가되어 시그너처가 복잡해졌다. 조금 지나니 그것도 충분하지 않다며 더 복잡한 시그너처를 가지는 메서드를 추가하자고 했다. 그게 정말 필요한 것일까? 메트릭을 꼭 그런 식으로 남겨야 할까? 메트릭을 잘 남겨놔야 나중에 모니터링 도구에서 서비스의 각종 지표를 원하는 대로 조합해 확인할 수 있다. 나는 메트릭 체계와 모니터링 시스템에 대한 이해가 부족한 상태였다. 그래서 메트릭 라이브러리를 강력하게 통제할 수 없었던 것이다.

라이브러리가 망가지기 시작했다. 나는 소스 코드의 모양에 신경을 많이 쓰는데 그는 소스 코드 모양보다는 일을 일찍 마치는 쪽에 우선순위를 두었다. 잘못이라 할 수는 없다. 일을 끝마치는 것도 중요하니까. 그러나 메트릭 라이브러리는 여러 서비스에서 사용할 공통 라이브러리다. 명확하고 일관성 있고 사용하기 쉬운 모습을 유지해야 한다. 태어나자마자 망가지기 시작하는 모습을 보니 마음이 불편했다.

아마존에서는 일을 마치는 것 못지않게 만들어낸 결과물의 품질도 중요하게 여긴다. 코드를 엉성하게 작성해 빨리 배포하는 것보다는 시간이 걸리더라도 제대로 만들어낼 것을 요구한다. 다른 한 편으로 일의 속도도 중요하게 여긴다. 비즈니스의 세계에서는 속도도 중요하니까. 즉, 훌륭한 품질의 결과물을 빠른 속도로 만들어 내야 한다. 내가 이해한 아마존 리더십에 따르면, 품질과 속도를 모두 만족시킬 수 없다면 품질을 우선시해야 한다. 그러나 어떻게 하는 것이 옳은지, 옳은 방법으로 하려면 얼마나 시간이 필요한지를 모르는 상태에서 내 의견만 고집할 수는 없었다.