LINE Corporation이 2023년 10월 1일부로 LY Corporation이 되었습니다. LY Corporation의 새로운 기술 블로그를 소개합니다. LY Corporation Tech Blog

Blog


Armeria를 소개합니다

LINE DEV Meetup #11 'LINE 서버 개발자들이 말한다! Armeria 아직도 안 써요?'에서 이희승 님이 발표하신 'Three Principles of a Good Framework' 세션 내용을 옮긴 글입니다.

안녕하세요. LINE에서 Armeria를 개발했고 현재는 Databricks로 옮겨 RPC 프레임워크 관련 작업을 진행하고 있는 이희승입니다. 이번 글에서는 Armeria 개발 뒤에 숨겨진 세 가지 원칙이 무엇인지 소개하려고 합니다. 많은 분들이 이 세 가지 원칙을 이해하시고 현재 진행하고 있는 혹은 진행하게 될 프로젝트에 적용하면 좋을 것 같다는 생각이 들어서 이렇게 자리를 마련했습니다.  

좋은 프레임워크의 세 가지 원칙

Armeria 홈페이지에 들어가면 Armeria의 슬로건과 함께 간단하게 프로젝트 설명이 나옵니다. 이 화면이 Armeria 프로젝트가 어떤 철학을 바탕으로 운영되고, 개발되고 있는지를 잘 나타냅니다.

그럼 각각의 원칙이 무엇인지 하나씩 살펴보겠습니다.

원칙 1: 각자의 페이스로 개발할 수 있게 

홈페이지에 들어가면 가장 먼저 'Build a reactive microservice at your pace, not theirs'라는 문구를 볼 수 있습니다. 문구에서는 'at your pace' 부분이 강조돼 있는데요. 이게 바로 Armeria의 첫 번째 철학이라고 할 수 있습니다. 개발할 때 다른 사람들의 페이스에 휘말리지 않고 본인의 페이스로 진행할 수 있게 해 준다는 것입니다. 

Armeria는 처음에 서버를 개발할 때 아주 작고 단순한 형태로 시작할 수 있게 디자인돼 있습니다. 아래 코드를 보시겠습니다.

Server라는 클래스가 있습니다. builder를 만든 다음에 포트 번호를 지정하고, service에서 'hello'와 경로 파라미터 name에 바인딩합니다. 요청이 들어오면 람다 표현식으로 응답을 생성합니다. 생성한 service를 앞선 경로에 바인딩한 뒤 서버를 build하면 서버가 생성됩니다. 단 몇 줄로 간단하게 RESTful API를 구현할 수 있습니다.

하지만 실제로 서비스를 개발하다 보면 이것만으로는 부족하지 않겠어요? 로깅과 같은 것을 추가하는 작업들이 필요하겠죠. Armeria는 위와 같은 간단한 코드에서 시작해 조금씩 조금씩 살을 붙여 가면서 서비스를 개발할 수 있도록 디자인돼 있습니다. 아래 코드를 보면 decorator라는 메서드를 이용해서 LoggingService라는 것을 추가했습니다. Decorator는 service가 호출되기 전에 공통으로 필요한 기능을 수행하는 역할을 합니다.

처음에 혼자 개발할 때는 로깅 정도만 추가해도 충분하겠지만 프로젝트가 커진다면 Prometheus 지표를 표시하거나 로드 밸런서가 헬스 체크 요청을 보냈을 때 응답하는 기능과 같은 더 다양한 기능이 필요하겠죠. 아래 코드를 보겠습니다. PrometheusExpositionService와 HealthCheckService 서비스를 추가했고 요청에 대한 지표를 수집하기 위해 MetricCollectingService도 추가했습니다.

처음에는 service만 있었지만 여기에 decorator를 추가했고, 다시 여러 개의 service와 decorator를 추가했습니다. 이 과정에서 근본적으로 바뀐 것은 없습니다. 사용자가 손쉽게 기능들을 하나씩 추가해 나갈 수 있도록 설계돼 있다는 것을 알 수 있습니다. 이 원칙을 확장해서 아래와 같이 인증이나 권한 관리 등 더 다양한 기능을 붙일 수 있습니다.

예를 들어 CORS(Cross-Origin Resource Sharing)나 SAML(Security Assertion Markup Language)을 이용한 인증, SSO(single sign-on), 속도 제한(rate-limiting), 마이크로서비스 환경에서 필요한 분산 추적(distributed tracing), 디버깅 기능, 대시보드로 서버 상황을 확인하는 기능 등을 붙일 수 있습니다. 직접 decorator를 구현해서 여러 공통 기능을 붙일 수도 있습니다.

이와 같이 Armeria는 몇 가지 간단한 핵심 개념을 이해하면 서비스를 점진적으로 발전시켜 나갈 수 있도록 설계돼 있습니다. 

개발자가 자신의 페이스에 맞게 속도 조절을 잘 할 수 있도록 API를 설계하는 것이 가장 중요한 원칙입니다. 사용자가 단순하게 시작해서 조금씩 살을 붙여 나가며 서비스를 만들 수 있게 해야 합니다. 또한 살을 붙여 만든 코드가, 물론 예전만큼 아주 단순할 수는 없겠지만, 처음의 단순한 형태를 최대한 유지할 수 있도록 설계한다는 것이 Armeria의 첫 번째 원칙이라고 할 수 있습니다.

원칙 2: 어떤 상황에서도 사용할 수 있게

Armeria 홈페이지의 두 번째 문장은 'your go-to microservice framework for any situation'입니다. 어떤 상황에서도 '내가 마이크로서비스 하나 만들어야겠네'라는 생각이 들면 찾아가게 되는 프레임워크라는 것이죠.

예를 들어 '뭐 먹지?'라는 생각이 들 때 '아, 오늘은 그 백반집에 가야겠다'하고 딱 떠오르며 언제든지 쉽게 가서 먹을 수 있는 그런 식당처럼, 서비스를 구현하고 싶을 때 편하게 찾아가면 쉽게 서비스를 구현할 수 있게 해 주는 그런 프레임워크를 지향했습니다. 앞서 소개한 문구 뒤를 보면 gRPC를 포함해 이것저것 다 지원한다고 나옵니다. 사용자가 선호하는 기술을 마음대로 조합해서 쓸 수 있게 해 준다는 이야기입니다.

gRPC 같은 경우를 보면 앞서 소개했던 builder 패턴으로 서버를 만들고 서비스를 바인딩하는데요. 여기서는 기존의 HTTP 스타일의 API가 아니라 GrpcService라는 걸 만들어서 넣습니다. 이렇게 넣으면 gPRC 서버가 됩니다. GrpcHelloService라는 것은 바로 gRPC 컴파일러, 프로토콜 버퍼 컴파일러가 생성해 준 것을 확장해서 만든 실제 서비스 구현체가 됩니다.

Thrift도 마찬가지라서 Thrift 컴파일러로 컴파일해서 생성한, 인터페이스를 구현한 ThriftHelloService라는 것을 THttpService라고 하는 것으로 감싸서 바인딩하면 자연스럽게 Thrift 서버가 됩니다.

마지막으로 이런 것들을 믹스 앤 매치할 수 있습니다. 서비스를 여러 개 붙였는데 하나는 REST 스타일의 API, 하나는 Thrift, 하나는 gRPC, 이렇게 다양한 프로토콜, 다양한 스타일의 통신 방식을 섞어서 하나의 서버를 구성할 수 있습니다. Thrift라는 기술이나 gRPC라는 기술을 손쉽게 서버에 통합할 수 있는 것이죠.

그 밖에도 아래와 같이 다양한 것들을 통합할 수 있습니다.

먼저 언어를 보면, JVM 기반의 근본인 Java부터 시작해서 Kotlin, Scala와 연동할 수 있는 통합 모듈을 제공합니다. 예를 들어 Kotlin 같은 경우에는 Kotlin Coroutine과 손쉽게 연동할 수 있습니다. 다음으로 프레임워크를 보면, 의존성 주입 프레임워크(dependency injection framework)인 Spring이나 SpringBoot, 그리고 Dropwizard 등 다른 프레임워크와 연동할 수 있도록 연동 모듈을 제공합니다. 앞서 소개했던 것처럼 gRPC나 Thrift, GraphQL과 같이 다양한 프로토콜과 연동할 수 있고요. 마이크로서비스 환경에서 중요한 서비스 디스커버리를 수행하기 위한 Consul이나 ZooKeeper, Eureka 같은 것들도 지원합니다. 아주 간단하게 쓸 수 있는 DNS 같은 것도 DNS Resolution을 직접 구현해서 잘 작동합니다. 또 마이크로서비스에서 분산 트레이싱이 빠질 수 없죠. 분산 트레이싱을 제공하는 여러 가지 구현체들이 있는데요. 그중에서 대표적인 구현체인 OpenTelemetry나 SkyWalking, Zipkin 같은 것들도 지원하고 있습니다. 또 흥미로운 것은, 서블릿(servlet)을 지원하지 않는 새로운 형태의 웹 프레임워크 같은 경우에는 서블릿 같은 것들을 직접 구동할 수 없는 문제가 있는데요. Armeria 같은 경우에는 Apache Tomcat이나 Jetty 같은 서블릿 컨테이너와도 연동이 되기 때문에 이런 서비스들, 예를 들어 gRPC와 Thrift, 그리고 Apache Tomcat과 Jetty, GraphQL을 한꺼번에 작동시킬 수 있다는 특별한 장점이 있습니다.

Armeria의 통합 철학

이와 같은 통합이 가능한 이유는 Armeria를 설계하고 구현하면서 어떤 특정한 방식으로 통합해야 한다고 고집하지 않았기 때문입니다. 

보통 어떤 기술과 통합할 때 한 가지 기술만 통합하는 것이 아니라 두 가지 혹은 세 가지 기술과 통합하게 됩니다. 이때 통합하는 기술 간에 사이드 이펙트가 발생하거나 의존성 관계가 복잡해지면서 관리 측면에서 불편함이 발생해서는 안 된다고 생각했습니다. 이에 앞서 말씀드렸던 것처럼 아주 간단한 핵심 개념을 설정하고 모든 것을 그 개념을 기반으로 개발해서 service나 decorator를 추가하는 방법을 알고 있다면 어떤 특정 기술과 통합하기 위해 따로 지식이 필요하지 않게 개발했습니다. 예를 들어 앞서 보여드린 것처럼 Prometheus와 연동하려고 한다면 그냥 PrometheusExpositionService를 추가하고 MetricCollectingService를 decorator로 추가하는 작업만으로 연동이 완료됩니다.

그런데 이와 같은 방식의 통합이 왜 중요할까요? 예를 들어 Thrift에서 gPRC로 마이그레이션한다고 생각해 보겠습니다.

예전에는 RPC 프로토콜로 Thrift를 많이 사용했는데 요즘에는 gRPC로 많이들 넘어가는 추세잖아요. 이와 같이 Thrift 서비스가 존재하는 상황에서 gRPC 서비스를 추가한다고 했을 때 만약 서버를 별도로 만들어서 운영하면 관리 복잡도가 증가하겠죠. 하지만 Armeria로 통합하면 Thrift와 gRPC를 하나의 서버에서 작동시킬 수 있습니다. 이후 마이그레이션이 완료되면, 즉 어떤 클라어인트도 더 이상 Thrift로 통신을 하지 않는다면 Thrift를 삭제하면 됩니다. 하나의 서버로 손쉽게 마이그레이션 작업을 완료할 수 있습니다. 작업하기 위해 앞단에 리버스 프락시 같은 것을 두지 않아도 되고, 포트를 두 개 연다거나 서버를 두 대 마련할 필요도 없어집니다. 관리 복잡도가 내려가는 것이죠.

마이그레이션 측면의 장점만 있는 것은 아닙니다. 요즘 프로토콜을 한 가지만 지원하는 서비스가 많지 않습니다. gRPC도 지원하고, REST API도 지원하고, 최근에는 GraphQL을 지원하는 경우도 있습니다.

게다가 gRPC만 지원한다고 하더라도 로드 밸런서에서 헬스 체크를 보냈을 때 어떻게 응답할지 고려해야 하고 또한 웹 소켓은 어떻게 응답할지, REST API를 사용하면서 가끔 정적 자원을 서빙해야 하는 경우는 어떻게 해야 할지 고민해야 합니다. 그런 경우에 많은 분들이 NGINX를 사용합니다. 프락시를 하나 붙여서 정적 리소스는 NGINX가 서빙하고 REST API는 컨테이너가 서빙하는 방식으로 해결하는데요. Armeria를 사용하면 이런 것들을 하나로 합칠 수 있습니다. 하나의 서비스가 하나의 포트에서 하나의 JVM으로 멀티 프로토콜을 수행할 수 있게 되는 것이죠. 이를 통해 관리 복잡도는 물론 코드와 빌드 측면의 복잡도도 상당히 줄일 수 있습니다.

자체 제작한 강력한 런타임

이와 같이 멀티 프로토콜을 지원하고 관리 복잡도를 낮추는 다양한 통합 기능을 제공할 수 있었던 건 HTTP 런타임을 Netty에 기반해서 처음부터 다시 만들어냈기 때문입니다.

Armeria는 자체 제작한 강력한 런타임을 바탕으로 다른 프레임워크에서는 찾아볼 수 없는 통합 기능을 제공합니다. 앞서 말씀드렸던 것처럼 gRPC와 Thrift, GraphQL, REST, 심지어 Tomcat이나 Jetty는 물론 모던한 SpringBoot의 WebFlux 같은 프레임워크에 이르기까지, 다양한 엔드포인트를 하나의 포트에서 거의 오버헤드 없이 작동시킬 수 있게 된 것이죠. 

또한 decorator라는 추상적 개념을 통해 여러 서비스를 동일한 방법으로 붙일 수 있습니다. 지표를 추가하거나 분산 트레이싱, 속도 제한 추가를 손쉽게 할 수 있는 것입니다. 예를 들어 Thrift로 개발했는데 아직 Thrift에서 분산 트레이싱을 지원하지 않는다면 Armeria에서 Thrift 서비스를 실행하면서 decorator를 붙이면 자연스럽게 분산 트레이싱이 추가됩니다.

코어에는 아래와 같이 좀 더 강력한 기능들도 내장돼 있습니다.

예를 들어 구조화된 로깅(Structured logging)이라는 개념이 있습니다. 기존 서버들은 텍스트 형태여서 별도로 파싱해야 하는, 제한적인 정보만 담고 있는 접속 로그만 남겼는데요. 구조화된 로깅을 이용하면 코드를 이용해서 요청과 관련된 모든 정보에 접근할 수 있습니다. 이렇게 얻은 정보를 프로토콜 버퍼나 JSON 같은 형태로 직렬화한 다음 Kafka나 Elasticsearch 같은 것으로 넘기면 Kibana나 Logstash 등을 통해서 분석하거나 사용자가 쉽게 열람해서 검색할 수 있게 되는 것이죠.

또한, HTTP 런타임을 개발할 때 HTTP/1과 2를 모두 고려해서 추상 레이어를 설계했기 때문에 Armeria로 서비스를 실행하면 그 서비스는 HTTP/1과 2를 자연스럽게 잘 지원하게 됩니다. 예를 들어 업스트림의 gRPC를 사용할 경우 HTTP/2만 지원할 수 있지만 Armeria와 통합하면 HTTP/1도 지원할 수 있고, 오래된 버전의 Tomcat이나 Jetty, 또는 Thrift를 사용하면 HTTP/1만 지원할 수 있지만 Armeria와 통합하면 HTTP/2도 지원할 수 있는 것이죠.

그 밖에도 다양한 프로토콜을 구현해 놓았습니다. 예를 들어 HAproxy를 구현했기 때문에 Elastic 로드 밸런서와 연동할 때 편리합니다. 또한 SOCKS 같은 프로토콜도 구현했고, 하나의 포트에서 여러 개의 프로토콜을 지원할 수도 있습니다. 예를 들어 플레인 텍스트 HTTP와 HTTPS, SSL 연결을 하나의 포트에서 동시에 다루는 것이 가능합니다. 이와 같은 런타임 덕분에 복잡하고 다양한 통합이 가능한 것입니다.

원칙 3: 친근한 커뮤니티 형성

마지막 세 번째 원칙도 아주 중요합니다. Armeria 홈페이지에는 아래와 같이 큼지막하게 Community 링크가 걸려 있습니다. 그만큼 프로젝트를 개발하고 운영하는 데 있어서 커뮤니티를 중요하게 여기고 있다는 뜻입니다. 

Armeria는 커뮤니티 친화적으로 운영함으로써 프로젝트 운영이 선순환 구조를 이룰 수 있도록 신경을 쓰고 또 쓰고 있습니다.

첫 번째와 두 번째 원칙을 바탕으로 잘 디자인한 API와 구현체를 제공해서 개발자들이 Armeria를 사용할 때 좋은 경험을 얻게 하고, 그 경험을 바탕으로 모인 개발자들이 다양한 아이디어나 피드백을 남겨 주면 다시 이를 바탕으로 커뮤니티 친화적인 환경 속에서 사용자들과 협업해 더 나은 구현체를 만들어 내고, 이게 또다시 더 나은 개발자 경험으로 이어지는 선순환 구조를 만들어 내는 데 많은 노력을 기울이고 있습니다.

이런 사이클이 잘 굴러가기 위해서는 아래와 같은 세 가지 요소가 필요하다고 생각합니다.

먼저 친근한 분위기, 그러면서도 할 말은 하는 분위기를 형성하는 게 중요합니다. 소위 '팩트 폭력'이라고 말하는 것처럼 너무 거칠게 하는 것보다는 친근한 분위기에서 하되, 반대로 너무 친근하게 하려다 보니 의견을 제대로 피력하지 못하는 분위기가 되는 것은 지양해야 합니다. 다음으로 어떤 사람이 기여할 때 그 과정이 너무 힘들지 않도록 계속 도와주고 이끌어 주는 것이 중요합니다. 마지막으로 어떤 기여가 들어왔거나 어떤 변경 작업을 진행한다고 할 때 프로젝트의 목표와 원칙에 맞도록 일관성 있게 기여를 처리하고 또 개선해 나가는 것이 중요합니다. 이것이 결국 일관성을 유지하면서 더 나은 API, 또 더 나은 개발자 경험을 가져오기 때문입니다.

이와 같은 세 가지 요소들이 앞서 말씀드렸던 선순환 구조의 모든 측면에서 상당히 중요한 역할을 합니다. 사람들이 질문을 하거나 어떤 아이디어를 피력하거나 피드백을 남겼을 때 혹은 기여가 들어와서 코드 리뷰를 했을 때 위 세 가지 요소를 잘 생각해서 진행해야 선순환 구조가 더욱 강화될 수 있겠죠.

Armeria 프로젝트를 시작한 뒤 수년의 시간이 흐른 지금 커뮤니티는 상당히 크게 성장했는데요. 150명이 넘는 기여자 분들이 다양한 PR을 보내 주셨고, 피드백도 많이 남겨 주신 덕분에 지금까지 잘 성장할 수 있었다고 생각합니다. 기여자 중 많은 분들이 회사에 다니면서 일을 하시는 분들이었는데요. 이렇게 커뮤니티를 통해 소통하셨던 분들이 직장에서 Armeria를 전파하고, 이것이 실제 프로덕션에서 사용하는 것으로 이어졌습니다. 몇몇 사례를 살펴보겠습니다.

먼저 Slack은 거의 첫 번째 외부 고객이라고 할 수 있는데요. Armeria를 이용해 앞서 보여드렸던 사용 예시와 같이 Thrift에서 gRPC로 성공적으로 마이그레이션한 경우입니다. 또한 주로 영국에서 많이 사용하는 Doordash라는 배달 플랫폼 서비스에서는 Armeria와 Kotlin을 이용해서 기존의 Python 기반 스택을 Armeria와 gRPC, 그리고 Kotlin으로 성공적으로 마이그레이션했습니다. 현재 제가 재직하고 있는 Databricks에서도 기존의 레거시 RPC 프레임워크를 Armeria로 변환하는 작업을 계속 진행 중입니다. 일부 서비스는 이미 마이그레이션이 완료됐고요. 그다음 호주에서 많이 사용하는 Afterpay 같은 서비스도 Armeria로 개발됐습니다. 물론 이 웨비나를 주최해 주신, Armeria를 낳은 LINE에서도 Armeria를 아주 많이 사용하고 있고요. 인테리어로 유명한 오늘의집(참고), 그리고 네이버와 네이버 웹툰, 네이버 클로바 같은 곳에서도 Armeria를 잘 쓰고 있다고 합니다. 그 밖에도 카카오페이를 포함해 이 글에서 언급하지 않은 다른 회사들도 많이 있으리라고 생각합니다.

지금까지의 내용을 정리해 보겠습니다.

먼저 페이스 조절이 중요합니다. 사용자가 쉽게 배울 수 있게 하고 또 작게 시작해서 점점 프로젝트를 키워나갈 수 있도록 하는 게 중요합니다. 또한 통합은 특정 방식을 고집하거나 특정 의존성을 너무 불필요하게 끌어오는 것보다는 'unopinionated' 방식으로 통합하는 게 중요합니다. Armeria의 경우 자체적으로 개발하고 커스터마이징한 HTTP 런타임 덕분에 이것이 가능했습니다. 마지막으로 아무리 프로젝트가 좋아도 커뮤니티 친화적인 환경에서 선순환 구조를 만들지 않으면 임팩트를 만들기 어렵습니다. 여러분들께서도 프로젝트를 진행할 때 이런 부분을 한 번씩 생각해 보시면 좋을 것 같습니다. Armeria는 커뮤니티 친화적인 분위기를 계속 이어 나가겠습니다. 더욱 많은 분들께서 Armeria 프로젝트에 참여해 더욱더 많은 피드백을 주시면 감사하겠습니다.

Armeria의 향후 계획

그럼 이제 앞으로 무엇이 필요할까요? 현재 다양한 기능과 통합을 제공하고 있지만 앞으로는 지금까지 한 것보다 더욱 많은 할 일이 남아 있습니다. Armeria에서도 물론 노력하겠지만, 여러분들의 기여도 아주 중요합니다. 단순히 PR을 보내는 것뿐 아니라 필요한 기능을 요구하거나 피드백을 남기는 것도 아주 중요한 기여가 됩니다. 또한 사용하는 것 그 자체만으로도 큰 힘이 되는 기여라고 생각하고 있습니다.

현재 어떤 기능들을 고민하고 있는지 말씀드리겠습니다.

먼저 서버에 어떤 문제가 생기거나 현재 서버의 상태가 궁금할 때, 물론 이때 지표 같은 것을 보는 방법도 있겠지만, 해당 서버의 웹 서버에 접속해서 로컬에서 현재 서버 상황이 어떤지 확인할 수 있는 웹 기반 대시보드가 있으면 좋을 것 같다고 생각하고 있습니다. 다음으로 요즘 서비스 메시(service mesh)가 한창 유행이죠. 서비스 메시의 핵심이 되는 프로토콜이 xDS라는 프로토콜입니다. 서비스 메시 컨트롤러와 서비스 메시의 역할을 하는 사이드카 간의 통신을 xDS 프로토콜로 하는데요. 만약 Armeria가 xDS 프로토콜도 지원하면 사이드카 없이도 서비스 메시의 역할을 수행할 수 있을 것입니다. 아주 흥미로운 기능이 될 것으로 예상하고 있습니다. 그 밖에도 OpenAPI와의 상호 운영성이라든지 최신 프로토콜인 HTTP/3, 사이드카와 통신할 때 필요한 UNIX Domain Sockets, 또 WebSocket이나 Socket.io 지원도 고민하고 있습니다. 그리고 현재 여러 언어와 통합하고 있긴 하지만 아직 그 언어에 맞는 DSL을 제공하지는 않고 있는데요. Kotlin DSL이나 Scala DSL 같은 것들도 제공하면 좋을 것 같습니다. 다음으로 서버를 재기동하지 않고도 설정을 변경하거나 라이브 리로딩해서 개발 사이클을 좀 더 단축시킬 수 있다면 좋을 것 같습니다. 또한 GraalVM 기반으로 네이티브 이미지를 빌드해서 메모리 풋프린트(memory footprint)나 기동 시간을 줄여서 람다 같은 곳에서 쉽게 사용할 수 있게 된다면 좋겠죠. 다음으로 요즘 프레임워크에서 CLI(command line interface)를 이용해서 특정 기능을 켜고 끄는 기능들이 제공되는 경우가 많은데요. Armeria에서도 구현되면 좋을 것 같습니다. 그 밖에도 Armeria가 더욱 다양한 프레임워크와 연동할 수 있도록 준비하고 있습니다.

Q&A

Q: 사이드카 없이 동작하면 지연 시간(latency) 문제는 없을까요?

A: 사이드카를 두면 사이드카를 한 번 거치기 때문에 오히려 지연 시간이 소폭 증가하는 걸로 알고 있습니다. 또한 파일 서빙이나 SSL 때문에 사이드카를 붙이는 경우도 사실 많습니다. 예를 들어 앞단의 NGINX나 Envoy가 SSL 처리 혹은 정적 파일 처리를 담당하고 그 뒤에 Java 기반의, SSL을 사용하지 않는 애플리케이션이 동작하는 경우가 많은데요. Armeria 같은 경우에는 Netty에 JNI 기반의 네이티브, Google에서 개발한 OpenSSL 기반의 BoringSSL이라고 하는 SSL 엔진을 사용해서 SSL을 처리하고 있습니다. 또한 파일 전송 같은 경우에도 메모리 복사를 최소화해서 파일 전송을 하도록 구현돼 있기 때문에 성능 측면에서는 크게 문제가 없는 걸로 알고 있으며, 만약 성능 이슈가 발생한다면 저희 커뮤니티에서 잘 처리할 것으로 믿고 있습니다.

Q: Spring WebFlux와 Armeria의 관계가 흥미롭습니다. 비슷한 기능을 제공하는 것 같으면서도 서로에게 없는 점을 상호 보완해 주는 것 같은데요. 이 둘을 현명하게 함께 사용하는 방법(예를 들어, Spring WebFlux를 사용하고 있는데 Armeria를 추가해 부족한 점을 보완하는 시나리오)을 추천해 주실 수 있나요?

A: 앞서 'Unopinionated integration' 부분에서 언급했던 것처럼 Armeria와 WebFlux는 함께 사용할 수 있습니다. Armeria가 Spring WebFlux의 HTTP 엔진을 교체해서 Armeria가 WebFlux의 트래픽도 서빙하고 Armeria의 gRPC나 Thrift 같은 다른 서비스도 서빙하는 방식으로 통합합니다. 즉 Armeria와 SpringBoot WebFlux를 통합하면 Armeria가 통신을 전부 수행하면서 WebFlux 코드로 들어온 요청을 전달해 주는 역할을 하는 것이죠. 따라서 gRPC도 서빙하면서 WebFlux로 REST API도 구현하는 것이 가능합니다. 다양하게 믹스 앤 매치할 수 있습니다. 만약 업무를 담당하고 개발하시는 분들이 'Web API를 개발할 때는 WebFlux가 Armeria보다 익숙하고 편리하다'라고 한다면 WebFlux를 사용하면서 gRPC나 GraphQL 같은 부분만 Armeria를 사용하는 것도 가능하고요. 또는 Armeria의 강력한 SSL 연동이나 HTTP/1과 2를 잘 지원하는 것, 혹은 하나의 포트에서 여러 개의 프로토콜을 지원하는 것들에만 관심이 있을 수도 있겠죠. 그런 경우에도 Armeria와 SpringBoot WebFlux를 통합하면 WebFlux는 그대로 사용하면서 필요한 기능을 추가로 사용할 수 있게 되는 장점이 있습니다. 이와 같이 원하는 방식으로 통합할 수 있다고 말씀드리고 싶습니다.

Q: BoringSSL을 선택하신 이유가 궁금합니다.

A: Netty에서는 네이티브 코드에 기반한 SSL 모듈을 제공합니다. 현재 그걸 사용하고 있는데요. 기본적으로 Netty에서 바닐라 OpenSSL과 BoringSSL을 OpenSSL 모듈로 제공합니다. 여기서 BoringSSL은 정적으로 링크된 라이브러리를 제공하기 때문에 대부분의 환경에서 별문제 없이 네이티브 라이브러리를 로딩할 수 있다는 장점이 있습니다. 또한 Google에서 관리하고 있기 때문에 퀄리티도 어느 정도 보장되고 메인터넌스도 보장됩니다. 이와 같은 장점 때문에 BoringSSL을 기본으로 선택하고 있는데요. 필요하다면 의존성을 조금 변경해서 운영체제에서 제공하는 OpenSSL을 사용하는 것도 가능하긴 합니다. 

마치며

이번 글에서는 Armeria를 좋은 프레임워크로 만들기 위해 세운 세 가지 원칙과 그 원칙을 어떻게 구현해 왔는지 말씀드렸습니다. Armeria에 관심이 생기셨다면 Armeria 웹사이트에 방문하시면 다양한 정보와 관련 링크를 확인하실 수 있습니다. 또한 이번 글을 읽고 공감하셨다면 Armeria GitHub에 방문하셔서 'Star'해 주시면 감사하겠습니다.