우형의 새로운 백엔드 개발 표준을 시청했다. TS + nestjs를 사용하고 있는데 유튜브만 틀어놓으니까 머리에 잘 안들어오길래 블로그를 켰다. 여전히 모르는 개념들이 많은데 이는 빨간색으로 표시했다. 일단 정리 기기
우형은 현재 자바의 스프링을 사용하고 있다.
스프링의 가장 큰 장점은 아무래도 많은 레퍼런스를 꼽을 수 있다.
자바 스프링은 멀티 스레드 기반으로, 하나의 세션에 대해 신뢰성이 높은 상태로 처리가 가능한 대신,
온디멘드 트래픽이 많은 경우, 이를 위해 준비해야 하는 시스템 자원의 낭비가 클 때가 있다.
하지만 서버리스 환경이나, 빠르게 스케일 아웃을 해야할 때 느린 cold start time이 대표적인 단점 중 하나다.
왜 많은 언어와 프레임워크 중 우형은 TS + NEST를 선택했을까?
복잡한 현대 아키텍처에서는 하나의 도메인을 한 가지 방식으로만 제공할 경우, 요구사항을 달성하는데 어려운 문제가 있다. REST API 환경에서도 배치 작업이 필요할 수가 있고, 비동기이면서도 안정성을 보장해야하는 환경이 필요할 수도 있듯이 말이다. 이를 위해 각각의 서비스를 환경별로 구성할 경우 하나의 도메인을 공유하기 때문에 비즈니스 로직이나 인터페이스가 중복되게 된다. 따라서 이렇게 구성한 서비스들은 높은 의존성을 갖게 된다. 도메인이 하나이기 때문에 어디서부터 어디까지 나눌지가 모호해진다. 그래서 시스템의 안정성과 생산성을 위해 Nestjs를 도입했다고 한다.
안정성과 생산성을 고려했고, 또 자바 스프링 만큼의 단단하고 빠른 서비스 개발환경을 선호한다고 함.
대중적인 기술을 선호했고, 일정 수준의 생태계가 구축된 언어와 프레임워크여야한다.
TS는 5년간 3배 가량 발전했고, JS보다 안정적 & 효율적으로 개발할 수 있다.
JS는 약타입 언어다.
자바는 강타입 언어로, 결과의 일관성을 보장하고 컴파일러를 통해 개발단계에서 에러를 감지할 수 있지만
약타입 언어인 JS는 그렇지 못하다. 개발 초기에는 약타입이 빠르지만 코드의 규모가 커질수록 강타입이 유리해진다.
약타입 JS에 정적 타입 시스템을 도입한게 TS다. TS는 코드 작성 단계에서 정적 타입으로 관리할 수 있도록 하여 코드 레벨에서의 문제를 조기에 발견할 수 있다. 컴파일 시점에 오류 확인이 가능하지만, 자바와 같은 강타입 언어와 다르게 런타임 레벨에서는 여전히 JS로 동작하기 때문에 타입이 엄격하게 관리되지는 않는다. 따라서 사용하는 프레임워크에서 TS를 완벽하게 제공하지 않거나, 외부에서 전달받는 리퀘스트나 서드파티 저장소로부터 전달받는 데이터의 타입이 명확하게 관리되지 않는다면 TS로 작성된 코드일지라도, 실제 런타임에서는 코드와 다르게 다른 타입으로 동작할 수 있는 문제가 있다.
반대로 외부에서 들어오는 데이터의 타입이 엄격하게 관리될 수만 있다면? 런타임에서 동작하지 않는 TS라고 하더라도 강타입 언어만큼의 단단한 코드를 유지할 수 있다는 뜻이 된다.
NodeJS는 2009년도에 공개되서 벌써 10년 넘게 사용되고 있다. 이벤트 루프를 통해 시스템 커널에 작업을 넘겨서, 싱글 스레드임에도 불구하고 논블로킹 I/O 작업을 손쉽게 수행할 수 있도록 디자인 되어 있다. 커널 환경은 멀티 스레드이기 때문에 백그라운드에서 다수의 작업을 실행할 수 있고, 이러한 작업 중 하나가 완료되면 커널이 NodeJS에게 알려줘서 적절한 콜백을 큐에 추가함으로써 실행되는 구조를 가지고 있다.
이러한 방식은 단일스레드 기반으로 동작하기 때문에 CPU 집약적인 작업에는 적합하지 않지만, 많은 I/O 작업을 처리하는 데에는 멀티 스레드에 비해 작은 작원을 사용하여 많은 양을 처리할 수 있게 한다. 특히 요청이 많고 가벼운 REST API 환경에서는 굉장히 높은 효율을 달성할 수 있게 된다.
우형은 하나의 큰 서비스가 아니라 여러개의 작은 서비스로 나뉘어져 있고, 이러한 서비스는 조직적으로도 분리가 되어있기 때문에 많은 I/O 처리의 효율적인 NodeJS 특성은 우형에 적합한 환경이라 결론지었다.
기존에 사용중인 자바 스프링은 CPU 집약적인 처리에 능숙하다. NodeJS는 정반대의 장점과 단점을 가지고 있기 때문에, 이 둘을 같이 사용하여 서로의 장단점을 보완해주도록 했다.
NodeJS는 많은 I/O 작업을 처리하는 데 효율적이기 때문에 웹에서도 사용한다. 웹 프레임워크에는 여러가지가 있지만, 내가 들어본 프레임워크는 express, nestJS가 있다.
express는 2010년도부터 개발되어서 nodejs와 역사가 비슷하다. 확장성을 지향하기 때문에 자유도가 높은 편이라서 빠르고 간편하다는 장점이 있다. 하지만 자유로운만큼 제한이 없다. 시스템이 커지고 협업이 많아질수록 컨벤션 통합, 의견 충돌 등에 더 많은 비용을 쏟아야하고 이는 제한이 너무 없다는 부채로 다가오게 된다. 자유도가 높다보니 서비스가 커지게 되면 코드의 복잡도가 지나치게 높아져서 서비스를 다시 다른 프레임워크로 바꾸게 되는 경우도 발생하게 된다. 또 공식적으로 TS를 지원하지 않는다. 내가 필요한 로깅 시스템 뭐 기타등등 필요한 라이브러리를 입맛에 맞게 골라서 사용할 수 있다, 커스텀이 가능하다는 장점이 있지만, 여러 라이브러리간의 호환성 여부를 지속적으로 확인해야하고 deprecated 될 때 만료된 라이브러리로 인해 발생할 수 있는 문제 역시 운영중인 서비스에 영향을 끼치게 될 수도 있다.
가장 많이 그리고 오래 사용된 건 express이지만 앵귤러 기반의 nestjs는 스프링 프레임워크에 조금 더 가까운 모습으로 구현이 되어있다.nestjs는 스프링과 같은 opinionated 프레임워크이기 때문이다. express는 un-opinionated 프레임워크다.
opinionated 프레임워크는 엔지니어의 작성 패턴을 강제하는 대신 다양한 기능을 제공하는, 완전 관리형 프레임워크를 뜻한다. 이러한 프레임워크는 굉장히 많은 규칙과 기능이 존재하기 때문에 개발자가 자유롭게 만들기보다는 정해진 규칙을 따르도록 하고 있다. 이미 아키텍처 결정에 의해 잘 다져진 경로를 제공하니, 예측가능하고 일관성을 띄고 있어서, 대규모 인원에 의해 관리되더라도 이해하기 쉬운 코드 베이스로 작성되어있어서 관리에 용이하다는 장점이 된다. 하지만 프레임워크의 목적과 요구사항이 일치하지 않게 되면, 강제된 규칙을 벗어나기 어렵다는 것이 단점이다.
반대로 un-opinionated 프레임워크인 express는, 자유도가 높기 때문에 내 입맛대로 구현할 수는 있지만, 개발할 때마다 수많은 시작 코드와 테스트 그리고 최적화와 검증이 필요하게된다. 스프링과 nestJS는 비슷한 구석이 많다. 스프링 개발자라면 큰 러닝커브없이 쉽게 익힐 수 있다. 코드레벨에서도 작성법이 많이 비슷함.
nestJS는 스프링과 비슷하지만 또 다른 점들도 있다. 미드레어로 시작해서 필터로 끝나는 nestJS만의 라이프 사이클은 하나의 요청에 대해 각 단계별로 관리할 수 있게 되어있다. 복잡한 비즈니스 로직을 각 단계별로 분리해서 관리할 수 있게 되는거다. 인증이나 validation error에 대한 처리를 각각의 단계별로 분리해서 관리할 수 있도록 했기 때문에, 가독성과 유지 보수성을 높여줄 수 있었다.
class validation과 class transformer를 통해 제공되는 validation은, 요청하는 리퀘스트에 대해서 검증함으로써 외부에서 전달되는 데이터의 타입을 안전하게 관리할 수 있다. 런타임 레벨에서 동작하지 않는 TS라고 하더라도 강타입 언어만큼의 안전한 서비스를 유지할 수 있도록 해준다.
Modular Layered Architecture를 기본으로 사용하고 있어서 어플리케이션에서 모듈 단위로 도메인과 서비스를 분리하고, 모듈 내에서 레이어를 분리함으로써 확장에 유리하면서도 복잡한 비즈니스를 쉽게 관리할 수 있게 되어있다. 라이브러리도 모듈 단위로 관리가 가능하다. 새로운 모듈을 쉽게 추가할 수 있기 때문에 안정적이고 빠르게 요구사항을 구현할 수 있게 된다. like 레고!
단순히 http 통신하는 어플리케이션이 아닌, message queue, grpc, socket io뿐만 아니라 자신이 구성하는 다양한 형태의 통신 프로토콜을 쉽게 적용할 수 있게 되어있다. 프레임워크에서 리퀘스트 리스폰스가 비즈니스 로직까지 전달되지 않고, 명확하게 통신 레이어에서만 관리된다는 것을 의미한다. 그 덕분에 비즈니스 로직이 트랜스포트 레이어의 영향을 받지 않게 되고, 결과적으로 하나의 비즈니스 로직을 다양한 프로토콜을 통해 처리할 수 있다는 것이 된다. 트랜스포트 레이어의 변경이 아니더라도 스탠드 어론으로 동작해야하는 람다 형태 또한 제공할 수 있게 되어있다??
opinionated 프레임워크인 nestJS는, 이렇게 다양한 확장 패턴 또한 이미 프레임워크 레벨에서 고민이 되어있기 때문에 다양한 요구사항에 맞게 활용할 수 있다는 장점이 있다.
처음 들어본 단어였고 아직 찾아봤는데 잘 정리가 안됐지만.. 지금 이해하기론, 최소한의 변경으로 여러곳에서 사용될 수 있도록 하는.. 무슨 틀? 같은 템플릿? 같은 형태인가 보다 하고 이해했다. 다시 정리되면, 그때 재정리하도록 하겠다!