Java.. 그러니까 서버용 프래임워크로써 Spring(추후 Spring Boot로 이어지는) 오래 전 부터에 전성기를 누린 언어이고, 지금 들어서는 조금 옛스러운 것으로 취급하는 시선도 있다. 또한 그동안 말 많았던 Javascript가 Typescript 등 착실히 단점을 보완하기 시작하면서, 점유율을 점차 늘리고 있는 추세.. 비전공자가 백엔드를 입문할 때, Node.js의 상대적으로 가벼움, 배포의 용이성, 완만한 학습커브에 매력을 느껴 Node.js를 선택을 한다.
저 또한 그러한 장점이 매력적으로 끌려와 백엔드 입문을 Node.js로 선택을 하였다. 하지만... 공부를 하면서, 점점 이러한 고민을 하게 되었다. 왜 엔터프라이즈 급의 기업에서는 Spring Boot를 사용하는 걸까? 혹은 금융권 같이 보수성을 지향하는 곳에서도 Spring 계열을 안정적으로 고집하는 이유가 뭘까? 이런 질문들 말이다.

네카라쿠배..라고 불리는 우리나라 IT공룡 뿐만 아니라, 해외의 주요 기업들도 Spring Boot 프래임워크를 사용한다. 공고를 보면 Java(Spring Boot) 혹은 이와 생태계를 공유하는 Koltin 능숙 여부를 요구하는 경우가 짙다. 서론이 너무 길었다. 그럼 먼저 Node.js와 Spring Boot을 간단히 비교하는 표를 보고 다음 이야기로 넘어가겠다.
| 구분 | Node.js | Spring Boot |
| 스레드 구조 | 이벤트 루프 내 싱글 스레드 | 멀티 스레드 |
| 요청 처리 방식 | 비동기, 논 블로킹 | 기본적으로 동기, 비동기 옵션 가능 |
| CPU 부하 작업 | 싱글 스레드로 제약 | 멀티 스레드로 유연하게 대응 |
Node.js의 단점은 곧 Javascript의 단점이다
Javascript은 배우기 편하고 숙달이 쉽게 가능하지만, Type Safe하지 못하다는 한계점이 있다.
1 + 2
// 3
"1" + "2"
//12
102 - 2
// 100
"102" - "2"
// 100
위의 예시와 같이, 숫자와 문자가 구분되지 않고, 제 멋대로(?) 계산이 된다는 점이다. 초반에 이러한 점이 용이하다고 생각하여, 본인은 코딩테스트에 자주 사용하였는데, 추후에 이런 방식이 안전하지 않다라는 것을 깨닳은 이후 지양하게 되었다. 이러한 Type Safe문제는 대규모 트래픽을 감당해야 하는 큰 프로젝트 규모에서는 특히나 치명적인데, 런타임 에러가 발견되더라도, 이를 일일이 탐색하여 발견해야 한다는 점, 마찬가지로, IDE 툴에서 자동완성, 오류탐지 기능도 제약이 따라오기 때문이기도 한다.

또한, Javascript가 싱글 스레드라는 점도 일종의 한계점으로 작용하기도 한다. 정확히 말하자면, 주 일처리를 하는 메인 이벤트 루프는 싱글스레드이며, 기타 작업은 Web APIs 등에서 비동기+논블로킹으로 처리한다. 왜 이런 모델을 차용을 하였나하면, Javascript는 원래 브라우저용 언어에서 출발하였기 때문이다.
만약 당신이 브라우저에서 이미지 파일을 다운로드를 클릭했다고 가정해보자. 파일을 다운로드를 처리하는 동안, 아무런 작업을 하지 못하고 대기해야 한다면 얼마나 답답할 것인가?! 이때 Javascript는 파일 다운로드와 같은 기타 작업을 외부의 스레드, WebAPIs에 이양하고, 메인 스레드가 여러분의 입력 요청을 처리한다. 대신 WebAPIs에처 작업이 완료되면, 이벤트 루프를 통해 메인 스레드(싱글)에 옮겨주는 방식이다.
이러한 방식은 Node.js에서도 기본적으로 동일하다. 브라우저에 특화된 I/O 요청 처리에 용이하다는 장점도 가져온 동시에, 싱글스레드라는 한계점도 동일하게 작용한다는 것!
싱글스레드라 함은 (아주 정확한 해석은 아니지만), 프로세스 하나가 CPU 코어 하나만 활용해서 처리를 한다는 것을 의미한다. 즉 I/O 처리가 강하지만, CPU를 많이 사용하는 작업의 경우 병목현상이 발생할 수 있다는 것이 Node.js의 특징이다. 물론, Node.js를 이용해서 멀티 스레드를 사용하는 방법이 있긴 하다. Node.js 프로세스 자체를 여러 개 띄워서 직접 돌리는 방식이다. (최근에는 worker_threads 기능이 추가되어 필요시 병렬로 가능)
반면 Java의 경우 멀티 스레드 프로그래밍을 전제로 만들어진 언어인 만큼, CPU 부하 요청에 최적화되어 있다고 생각하면 되겠다. 아래 그림과 같이, 여러 스레드, 코어를 사용하는 작업일 수록, Spring의 작업효율이 더 높아지는 것을 볼 수 있다.

Java(Spring Boot)의 동시성 제어
위에서 말했듯, Java는 멀티 스레드 환경을 전제로 만들어졌다. 즉, 여러 작업이 동시에 효율적으로 처리가 가능한 만큼, 이에 대한 제어 기능도 마련되어 있다는 것. 만약 여러 스레드가 동시에 같은 데이터를 다룬다고 생각해보자. 아래 코드 예시:
int count = 0;
Thread t1 = new Thread(() -> { count++; });
Thread t2 = new Thread(() -> { count++; });
t1.start();
t2.start();
이 코드에서 count는 1이 될지, 2가 될지 정확히 모른다. 각각 스레드가 count가 0인 것을 참고하였지, 혹은 다른 하나가 이미 count가 1이 된 것을 참조하여 +1을 추가하였는지, 예측할 수 없기 때문...
그렇기 때문에, Java에서는 다양한 동시성 제어 기능이 제공된다. 이 중 대표 3가지를 표로 요약하자면 다음과 같다.
| 방법 | 특징 |
| synchronized | 특정 스레드가 객체에 접근하면 Lock획득하여 타 스레드 접근 Block |
| ReentrantLock | 명시적으로 Lcok 해제/제어가 가능, 세밀하게 lock.lock()호출로 제어 가능 |
| AtomicInteger | 논 블로킹(Lock-Free)로 동시성 제어, CAS 알고리즘으로 작동 |
위 방법을 사용하면, count는 안전하게 2로 변경이 된다.
그 외에도 Spring의 장점들...
예전에 기술한 글에서도, Java에는 폭 넓은 생태계가 구축되어 있고, 그 중 보안에 속하는 Spring security에서 더욱 강점을 발휘한다. 물론, Node.js에서도 보안 기능을 구현할 수 있는 패키지가 있다. 하지만 이 패키지를 일관성 있게 운영하기 위해 서로 조율하여 구축을 해야 하는데, 조직 규모가 큰 엔터프라이즈의 경우, 보안 체계를 표준화할 필요성이 더욱 요구된다.
또한 Spring은 컴파일 언어라는 점이 대규모 엔터프라이즈에게 장점으로 작용한다. 컴파일이란, 코드가 한 번에 컴파일되어 바이트코드로 변환되는 과정을 말한다. 그리고 실제 실행이 될 때는, 바이트 코드가 실행된다. 또한 컴파일 과정에서 모든 설정, 타입, 의존성, 설정 등이 한번 체크가 되는데, 오류 발생 시 즉시 검증이 된다는 장점이 있다. 따라서, 협업과 유지보수 시, 정형화된 코드를 통해 협력에 용이하게 해준다.
물론, 아직은 이러한 멀티 스레드나 스프링 시큐리티 등을 아직 본인 혹은 팀 프로젝트에 적용시킬 수준으로 성장을 하진 못했다. 하지만, 미래의 성장 가능성을 보고, 추후 스레드 관리/트래픽 처리하는 개발자로 성장하기 위해 이와 같은 방향을 정했다고 생각하면 좋겠다.
'개발자의 하루' 카테고리의 다른 글
| 크래프톤 정글 (SW-AI Lab 12기) 최종 합격 후기 (0) | 2026.02.10 |
|---|---|
| SQLD 합격 후기: 별도 강의 구매없이 독학으로 공부하는 법 (16) | 2025.06.27 |
| SQLD 시험 후기, 문제 분석 (10) | 2025.06.02 |