‘Node.js’ VS ‘Java Spring’

twkim913
NAVER Pay Dev Blog
Published in
14 min readDec 30, 2022

--

저는 네이버 파이낸셜 페이플랫폼에서 개발 업무를 담당하고 있는 김태우 입니다.

요즘 들어 (적어도 제가 예전에 이직을 준비하던 시점에는), Node.js 개발자 (+ kubernetes, MSA 경험자 우대) 와 같은 백엔드 개발자 채용 공고가 부쩍 늘어난 듯 합니다.

아마 부쩍 늘어난 그런 공고를 보며, 왜 하필 Node.js 가 유행인지, 또 왜 우대 사항 항목에는 언제나 k8s (kubernetes), MSA (Micro Service Architecture) 같은 항목이 함께 붙어 있는 건지 궁금하셨던 여러분을 위해 오늘은 Node.js 이야기를 해 보려고 합니다.

특히 2009년에 나왔던 Node.js가 왜 지금 유행을 타기 시작하는지, 기존의 Java Spring과 비교 했을 때 어떤 차이가 있는 것인지 등을 중점으로 이야기를 해 보겠습니다.

Node.js VS Java Spring!

하지만, Node.js의 장점부터 이야기하면 재미가 없을 듯 하니, Node.js의 약점부터 시작해 보겠습니다.

  1. Java와 비교 했을 때, Node.js 는 작고 느린 Single Thread 서버이다.

자세하게 설명하면 복잡한 이야기가 될 수 있지만, Node.js는 Single Thread ‘서버처럼’ 작동합니다.

Java가 각 Http 요청을 별도의 Thread에서 처리하는 것과는 다르게, Node.js는 모든 요청을 하나의 Thread로 처리합니다.

즉, 여러 개의 요청을 동시에 처리 할 수 있는 강력한 Java spring 서버와 비교하면 Node.js 서버는 한번에 하나의 요청만 처리할 수 있습니다.

또한, 한번에 하나의 요청만 처리한다는 말은 서버의 응답성이 떨어진다는 말이기도 합니다.

어떤 무거운 요청이 처리에 적어도 5초가 걸린다면, 해당 요청을 처리 중인 Java Spring 서버는 새로운 Thread를 만들어 바로 다른 요청을 처리 해 줄 수 있겠지만, Node.js 서버는 새로운 요청을 처리하기 위해 반드시 5초 동안 기존의 요청이 처리되기를 기다려야 합니다.

무엇보다 유사 Single Thread 구조를 가진 Node.js는 강력한 서버에 설치되었다고 하더라도, 해당 서버의 성능을 100% 활용 할 수가 없습니다. (CPU 코어 수가 아무리 많아도 Event Loop가 Single Threaded 이기 때문에)

다소 성급한 결론 일 수 있지만, 일정 부분에 있어 Node.js는 설계 단계부터 Java에 비해 처리 성능이 떨어집니다.

2. Node.js가 사용하는 언어인 JavaScript는 신기한(?) 언어다.

신성로마제국은 신성하지도 않고 로마도 아니며 제국도 아니다’ 프랑스의 철학자 볼테르가 한 말입니다.

자바스크립트 역시 자바도 아니고 (함수형 언어에 대한 이해가 필요하다는 점에서) 단순히 스크립트 언어도 아닙니다.

다른 고급 언어와는 다르게, 자바스크립트 단순히 웹 브라우저 화면을 컨트롤하는 스크립트 용도로 만들어진 언어입니다.

그렇기에 발전한 지금의 자바스크립트에는 얼핏 보기에는 간단해 보이는 코드 사이사이에 여러 혼란스러운 변화의 흔적들이 남아있습니다.

그 흔적은 정말 언어 자체의 결함이 아닌가 하는 부분부터 ( ‘this’ keyword 의 난해함, 혼란스러운 scope 개념)

단순한 스크립트 언어에 살을 붙이는 과정에서 생긴 부자연스러운 부분 (class를 지원하는 자바스크립트 언어에서 class는 사실 실체가 없는 syntax suger 라는 기묘한 사실, 모든 변수의 타입은 유동적),

그리고 정말 단순히 이해하기 어려운 개념들까지 (closure이나 prototype chain 등…) 정말 다양한 부분에 산재해 있습니다.

사실 가끔은, 변화의 흔적 같은 게 아니라 그냥 애초에 이상한 언어 아닌가 하는 생각도 듭니다.

무엇보다도 가장 큰 문제는 저런 개념에 대해 이해를 하지 못하고 그냥 문법이 좀 다른 java를 작성한다고 생각하고 javascript 코드를 작성하더라도, 타입과 문법이 비교적 자유로운 javascript 특성 상 코드는 런타임 시에 큰 ‘폭발’이 일어날 때까지 실행이 됩니다.

Javascript는 좋은 부분이 많은 언어이지만, 위험하고 나쁜 부분은 더 많습니다.

C++만큼 두꺼운 Javscript 책과, 그렇지 않은 ‘The good Parts’

그 좋은 부분을 잘 쓰기 위해서는 Javascript를 ‘잘’ 알아야 할 필요가 있습니다.

그렇다면 Node.js 에는 어떤 장점이 있는 것일까요?

  1. Node.js는 (작고 가벼운) Single Thread 서버이다.

아 다르고 어 다르다는 말처럼, Node.js 서버는 Java와 비교 했을 때, 작고 느린 것은 맞지만, 작고 가볍다고 볼 수도 있습니다.

서버는 보통 대규모 트래픽을 받아주기 위해 존재하고, 그렇기에 작고 가볍다는 node.js의 장점은 크게 유용하게 생각되지는 않았습니다.

하지만, 작은 서버(Micro Server) 여러 대를 사용해 거대한 서버를(Monolithic Architecture) 대체 하겠다는 MSA (Micro Service Architecture) 구조와 서버 배포와 관리를 간단하고 빠르게 해 줄 수 있는 k8s (kubernetes)가 유행을 탄 후, 상황은 조금 달라졌습니다.

MSA는 거대한 서버를 서비스 기능 별로 나눌 뿐 만 아니라, 트래픽을 나눕니다.

예를 들어, 기존에 음식 배달 회사에서 거대한 서버 2대로 모든 서비스 요청을 처리하고 있었다 치면, MSA 구조에서는 이벤트 서버 2대, 음식점 리스트/추천 서버 2대, 주문/배달 처리 서버 3대 등으로 나눠 질 수 있습니다. (주문과 배달에 이벤트보다는 많은 요청이 있을 거라고 고려해 3대로 설정 했습니다.)

위에 예에서 볼 수 있듯이, 거대한 서버 2대를 MSA 구조로 바꾸었을 때는 7대, 트래픽이 적더라도(이중화를 고려 했을 때) 적어도 6대는 있어야 합니다.

물론 기존의 트래픽을 7대의 서버가 나눠서 받기에 각 서버의 성능은 기존의 2대 보다는 조금 떨어져도 무방합니다.

JVM위에서 구동 되어야 하는 Java Spring는 node.js에 비해 무겁습니다. 조금 구체적인 예를 들어 설명하자면, 아무것도 하지 않는 Java Spring은 400MB 가량의 Memory를 사용하는데 반해, Node.js는 25MB 정도의 Memory 만을 사용합니다. (참조: https://medium.com/maestral-solutions/alternative-package-managers-for-node-js-f52805b98064)

그렇기에 7대의 서버가 떠 있다고 했을 때, JVM과 spring의 구동만으로 약 3GB의 메모리를 사용해야 하는 데 비해, node.js의 경우 200MB 정도면 충분합니다.

얼핏 보았을 때, 비효율적으로 보이는 MSA 구조는 트래픽이 많이 몰릴 때 빛을 발합니다.

위의 예시에서 만약 어떤 시간 한정 이벤트가 발생해서 이벤트에만 잠시 사람이 몰린다면 이벤트 서버만 증설 해 주면 됩니다. 또한, 이런 증설은 트래픽을 감지한 k8s 시스템이 자동으로 진행해 줍니다.

11월 11일 11시 11분부터 21분까지 10분 간 빼빼로 무료 쿠폰을 나눠주는 이벤트를 한다면, 이벤트 서버에 극단적으로 트래픽이 많이 몰려, k8s가 자동으로 해당 트래픽을 감지하고 이벤트 서버를 10~20대 까지도 늘릴 수 있을 것이고, MSA 구조에서 (실제로 돈이 되는) 주문이나 배달 서비스는 늘어난 이벤트 트래픽의 영향 없이 잘 실행이 됩니다.

또한, node.js의 서버 가동 시간은 1초 이내이기에 (java spring 서버 가동 시간은 10 ~ 20 초 사이) 갑작스러운 트래픽에 즉각적으로 대응하기에도 유리합니다.

이야기가 MSA와 k8s 쪽으로 빠진 것 같아 본론으로 돌아오자면, MSA와 k8s가 유행하며 서버 수 자체도 늘고, 서버 수가 트래픽에 맞춰 유동적으로 바뀌는 상황이 많아졌으며, 크고 강력한 한 대의 서버보다는 빠르고 가벼운 서버가 여러 대 있는 쪽이 유리한 상황이 많아졌습니다.

이러한 상황에서 빠른 서버 가동 시간과 적은 memory 사용량 등 node.js의 가벼움은 빛을 발하고 있습니다.

처리 능력이 떨어지거나 하나의 thread만 사용해 응답성이 떨어지는 단점들도, 서버를 여러 대 두면 무마 가능한 단점이 되었습니다. (Single Thread 서버 여러 대 ≈ Multi Thread 서버)

또한, Single Thread 서버가 더 이상 단점은 아니지만, Single Thread 서버 개발에는 장점이 남아 있습니다.

Single Thread 서버에는 오로지 하나의 work thread만이 존재하기 때문에, Single Thread 서버를 개발하는 개발자들은 더 이상 동시성(concurrency) 지옥에서 고통 받을 필요가 없습니다.

그리고 프로그램에도 철학자들이 포크를 차지하려고 싸우는 걸 막기 위해 자물쇠를 잠글 필요가 없습니다.

우리를 괴롭혀온 식사하는 철학자 문제 ( https://en.wikipedia.org/wiki/Dining_philosophers_problem )

node.js를 통해 개발을 한다면, 동시성 이슈를 해결할 시간을 더 생산적인 개발에 사용할 수 있습니다!

2. Javascript 언어는 나아지고(!) 있다.

카일 심슨이 쓴 ‘You Don’t know JS’ 이나, 더글라스 클락포드가 쓴 ‘자바스크립트는 왜 그 모양일까?’ 같은 javascript 관련 서적들의 제목부터, ‘열혈 C 프로그래밍’이나 ‘이팩티브 Java’ 와 같은 다른 책들과는 온도 차가 있어 보입니다.

장점을 소개하는 단락을 이렇게 시작하기는 조심스럽지만 자바스크립트는 (적어도 제가 느끼기에는) 호불호가 갈리는 언어입니다.

제가 생각하는 Javascript의 가장 큰 단점 중 한 가지는 타입이 없기에, 타입을 강제할 방법이 없었다는 것입니다.

예를 들어 자바스크립트로 아래와 같이 함수를 짰다면

function plus(number1, number2)
{
return number1 + number2;
}

우리는 함수를 사용하는 사람한테 number1, number2 를 숫자로 쓰라고 강제할 수 없습니다.

함수 사용자가 아래와 같은 악랄한(!) 짓을 해도 딱히 막을 방법은 없습니다.

var result = plus('1', '2') // 3...! 다행이 이건 의도대로 작동합니다.
var result2 = plus('2', 'number') // '2number'? 우리가 원했던 결과는 아니네요
var result3 = plus(1, '2') // 12, 여기부터 심각하게 잘못된 결과가 나오기 시작합니다.

우리가 할 수 있는 최선은 주석을 길게 달아 사용자에게 제발 이 함수를 정확히 써 달라고 읍소(!) 하는 것 뿐입니다.

/**
* @param {number} number1- 더하는 첫번째 수, 제발 숫자만 넣어주세요 ㅜㅜ
* @param {number} number2- 더하는 두번째 수, 꼭 무조건 숫자만 ㅠㅠ;
*
* WARNING!!!! 숫자가 아닌 param을 넣으면 오작동함!!!!
*/
function plus(number1, number2) {
// ...
}

물론 그런 주석 속의 울부짖음을 무시할 수 있는 사람은 너무나 많습니다. (주석을 읽기에는 너무 피곤한 프로그래머, 내가 만든 코드를 까먹은 3개월 뒤의 나, 주석은 바보나 읽는 거라는 자신감에 차있는 신입, 한글 주석은 잘 못 읽는 인도인 프로그래머)

소규모 프로젝트에서는 그렇게 큰 문제가 되지 않았지만, javascript에도 많은 대규모 프로젝트들이 생기며, 이 문제는 심해져 갔습니다.

다행히, 지금은 Typescript가 나왔습니다.

Typescript는 아주 기발한 방법으로(솔직히 말하면 조금 꼼수를 써서) Javascript를 Type이 존재하는 언어로 재탄생 시켰습니다.

아무튼, Typescipt의 탄생으로 인해 Javascript도 type이 있는 interface를 개발자들에게 제공해 줄 수 있게 되었고, javascript는 대규모 개발에는 어울리지 않는다는 오명도 조금은 벗을 수 있게 되었습니다.

Nest.Js 의 출시 역시 놓칠 수 없는 포인트입니다.

Java Spring이라는 강력한 Framework가 제공하는 DI, IoC 기반의 개발은 Java를 포기하기 힘들게 만드는 Spring의 큰 장점입니다.

하지만, 지금은 Nest.Js의 출시로 인해, Javascript에서도 마치 Java Spring를 쓰는 것처럼 annotation을 통해 편리하게 DI, IoC 기반의 개발을 할 수 있습니다.

그리고 Nest.Js는 Typeorm 이라는 ORM 프레임워크 역시 적극적으로 지원하고 있기에, 더더욱 Java Spring + Hibernate 조합을 쓰는 느낌으로 Javascript(Nest.Js + Typeorm)개발을 할 수 있습니다.

Nest.Js에 대해 조금 더 자세히 알고 싶으시다면 주도현 님께서 작성하신 글을 참조해 보셔도 좋습니다😉 (Nest.Js 사용 소감: https://medium.com/naverfinancial/nestjs-%EC%82%AC%EC%9A%A9-%EC%86%8C%EA%B0%90-f851527f7922)

또한, Javascript 진영에도 Jest나 Mocha 같은 좋은 테스트툴이 나왔기에 DI, IoC 구조가 제공하는 장점 중 하나인 ‘유닛테스트의 편리함’ 역시 잘 활용할 수 있게 되었습니다.

이야기가 조금 길어졌지만, 제 결론은 간단합니다.

서버 개발 언어로서의 Javascript는 발전하고 있습니다.

부족한 부분은 채워지고 있고 (Typescript), 다른 프레임워크의의 장점이나 (Nest.Js) 부족했던 개발 툴 역시 보강되고 있습니다. (Typeorm, Jest, Mocha)

위에서도 말했지만, ‘Javascript는 좋은 부분이 많은 언어이지만, 위험하고 나쁜 부분은 더 많습니다’.

그렇지만 좋은 부분도 천천히, 하지만 확실하게 많아지고 있습니다.

위에서 다소 길고 장황하게 백엔드 영역에서의 Javascript의 장점을 이야기 했습니다.

그렇다면, 이렇게 긴 설명의 결론이 무엇일까요?

Javascript가 Java보다 좋은 언어다?

이제 백엔드 개발에 Java Spring은 버리고 Node.js로 갈아탈 시점이다?

아닙니다, 오히려 지금은 Javascript로 넘어갈 때가 아니라고 생각합니다.

Java Spring은 10년 넘게 현장에서 활용되며 그 안정성을 (개발자들의 야근과 눈물로) 검증 받아 왔습니다.

그렇게 쌓아온 안정성을, 적어도 지금 시점에서, Node.js가 대체 할 수는 없습니다.

특히 네이버 파이낸셜 같은 금융 회사에는 더더욱 그렇습니다.

하지만 저는 PHP가 이끌던 백엔드 개발 트렌드를 Java Spring이 가져갔던 것처럼, 언젠가 Node.Js 역시 개발 트렌드를 주도 할 수 있다고 생각합니다.

그렇기에 저희 네이버 파이낸셜 ‘페이플랫폼 직속 팀’ 에서는 미래에 대비하며, 적극적으로 Node.Js를 도입하고 Node.js 활용의 노하우를 쌓아가고 있습니다.

페이플랫폼 직속 팀에서는 이미

  • 네이버 쇼핑 구매 목록 API를 제공하는 타임라인 서버
  • 혜택 광고 API를 제공하는 리워드 서버
  • 카드 정보 API를 제공하는 간편 입력 서버
  • 마이데이터 API를 제공하는 마이데이터 서버

… 외 다양한 서버들을 Node.Js (+ MSA, k8s)로 개발하고 운영하고 있습니다.

또한 대규모의 CPU 연산이 필요한 신규 DB 구축을 위한 마이그레이션 작업 역시 MSA 구조의 Node.js 서버로 처리해서, Node.js 서버로도 대규모 CPU 연산 작업이 가능하다는 것을 검증했고 이 부분의 노하우 역시 쌓아가고 있습니다.

이 글을 보시고 Node.Js 도입에 흥미가 생기셨거나, 궁금하신 부분이 있으시다면 티타임은 언제나 환영입니다! (물론 커피는 사 주셔야 합니다 ☕)

감사합니다.

--

--