취준생이 작성한 글입니다. 틀린 부분있으면 언제든지 지적부탁 드립니다.
IO 와 언어에 대한 이해와 활용
불필요하다고 생각했던 이유
- 프로그램의 동작 방식을 활용하는 방법에 대해서 몰랐다.
- 성능 최적화는 비즈니스 규모에 따라서 필요할 거라고 생각했다.
→ 유효하지 않은 비즈니스에서도 충분히 경험할 수 있고, 학습할 수 있다
계기
- 기술 블로그에서 해당 개념들을 어떻게 활용한 지에 대해서 기술되어 있다.
- 코어 부분에 대해서 알지 못해, 프레임워크나 라이브러리를 적당히 조합할 수 있는 능력이 떨어짐.
→ 백지부터 작성할 수 있는 주관이 없다.
다룰 개념
- IO 란
- Spring 과 Node 로 보는 동기, 블럭 / 비동기, 논블럭
- Node js 와 Spring 프레임워크가 가지는 차이점과 활용방안
- 멀티 프로세스와 멀티 스레드는 언제 활용해야 할까?
- 우리가 나아가야 할 방향
- 최종 정리
IO 란?
- 웹 어플리케이션의 구조 상 IO 작업의 비용이 가장 많이 차지한다.
- 병목현상의 주 원인은 IO 이며, IO 의 성능개선에 주목해야 한다.
- 웹 에서는 Network, Disk 관련 IO 가 높은 비중을 가지고 있다. (해당 작업들이 어플리케이션의 전부)
- 해당 파트에서는 IO 관점에 초점을 맞춘다.
→ 우리가 개발하는 어플리케이션은 항상 IO 와 밀접한 관련이 있고, 이런 IO 에 대한 처리에 대한 문제점으로 다양한 프로그래밍 방식이 발전해왔다는 것을 알아야한다.
Spring 과 Node 로 보는 동기, 블럭 / 비동기, 논블럭
→ 웹 어플리케이션은 접근성이 높고, 많은 유저의 요청에 이루어진 작업을 주로 수행한다.
→ IO 관련 작업이 많은 분야이기 때문에, 해당 섹션을 IO 의 관점으로 분류해서 보려고 한다.
Spring Servlet Container(Tomcat) 의 동기 / 블럭킹
- 스프링에서는 하나의 요청 (요청 당) 당 하나의 스레드를 할당한다.
- 동기 방식의 성능상 문제점을 해결하기 위해서 Multi-Thread 개념이 생겼다.
- 스프링의 WAS 에서는 서블릿 컨테이너 (톰캣) 의 스레드 수가 정해져있다.
- 해당 스레드 수를 넘는 유저의 요청이 발생하면 어떻게 대처해야할까?
동기 방식의 문제점과 해결방안
- 해당 스레드에서 처리할 수 있는 양의 범위를 넘어선다면?
→ 물리적인 서버를 업그레이드 시킨다. (스프링의 관점에서는 서블릿 컨테이너 (톰캣))
→ 로드 밸런싱을 통한 서버의 트래픽을 나눠서 처리한다.
- 로드 밸런싱이란?
- 클라이언트로부터 오는 요청을 여러 서버로 분배 해주는 것
- 리퀘스트를 동일하게 분배하기 때문에, 세션관리에서 문제가 생길 수 있다.
- 개념에 대해 정확히 알지 못한다면, 해당 포스트를 참고하라.

- 이대로 정말 괜찮을까?
→ Thread 들은 서로 데이터를 공유할 수 있기 때문에, 동기화를 관리해줘야 한다. (개발자의 능력)
→ 물리적인 서버의 비용은 트래픽에 비례해서 상승할 것이다.
→ 물리적인 단순한 로드밸런싱은 스케일 아웃의 관점에서 봤을 때 상태를 유지할 수 없다.
→ 상태를 유지하기 위해 Sticky Session, Session Clustering, Session Server 유지
→ 블록킹 언어의 근본적인 문제는 비즈니스가 커지면 커질수록 좋지 못할 것이다.
→ 결론 : 언어적 특성을 해결하기 위해 밸런싱, 세션서버를 독립적으로 관리해야 할 것이다.
→ 물론, 비동기 언어를 사용한다고 모든 문제를 해결 할 수는 없을 것이다.
추가
→ 스프링은 싱글 프로세스에서 멀티 스레드를 이용하여 처리한다.
→ 스프링의 프로세스와 스레드와 흐름에 대한 기본적인 개념은 해당 포스트에서 공부하도록 하자.
Thread 와 빈의 연관관계에 대해서
- 하나의 요청 → 하나의 스레드 → 빈 (Controller, Service ...) 을 공유하여 사용하는 환경
- 빈은 JVM 의 Heap 영역에 할당되는데, 하나의 싱글턴 인스턴스를 모두 공유하여 처리하지 않는다.
- 기본적인 구조에서 Controller 의 Mppaing 메소드를 참조하는 순서로 간단하게 전개해보자면
- 컨트롤러의 메소드를 스택이 할당 → 메소드 로직을 실행하는데, 여기서 힙에 공유된 객체들이 상태가 있으면 문제가 생긴다는 원리이다.
- 자세히 보기
- 컨트롤러의 인스턴스는 힙에 생성된다.
- 요청에 대한 처리를 위한 **스레드**는 힙의 **인스턴스를 공유**하는 것이 아니라, 해당 인스턴스의 메소드 정보를 가지고 있는 Method area 에서 메소드 정보를 참조한다.
- 메소드 영역의 메소드 코드를 실행 → 메소드 코드 중 힙에 있는 객체가 있다 → 상태를 공유 → 문제가 생긴다.
흐름을 도식화 해보자.@Controller public class memberController { private final memberService; @GetMapping("/") public String getMember(){ memberService.searchMember(); } }- 요청 발생 → 스레드 풀에서 스레드 할당 → **getMember()** 메소드 실행
- 메소드에 memberService.searchMember() 실행 → 해당 블럭은 스택에서 실행되므로 공유되지 않는다.
- memberService 는 싱글턴 (호출해도 같은 객체를 리턴한다)
- 해당 **빈의 메소드를 실행(searchMember())**시킨다.
- 멤버변수 빈 (싱글턴) 들은 **안전**한가? → 안전하지 않음. **세이프티 하게 설계해야할 의무**가 존재한다.
- 스프링의 모든 요청을 하나의 컨트롤러가 모두 처리하는 게 맞을까?
톰캣 구조와 스레드를 보며 얻을 수 있는 가치
- 스프링의 빈은 **Thread-safety** 하지 않다.
→ 멀티 스레드 프로그래밍에서 공유되는 객체는 싱글턴 임에도 **Thread-safety** 하지 않다. 라는 말과 같지 않나?
- 빈을 설계할 땐, **stateless** 하게 작성하자.
- 멀티 스레딩 프로그래밍에서는 **동시성** 문제가 생기지만, 반대로 **데이터를 공유**할 수 있다는 점은 장점이다.
→ 이 부분은 뒷장에서 컨텍스트 스위칭과 프로세스 관련 개념에서 한번 더 정리한다.
논블럭킹의 장점을 보여주는 Node Js
동기 블락킹 방식의 대표인 Spring 은 싱글 프로세스 에서 멀티스레드를 사용한다.
Node Js 는 싱글 스레드 임에도 고효율의 IO 처리를 보장한다.
싱글 스레드로 동작하는 Node Js 는 어떻게 고효율의 IO 처리를 보장할까?
처리 흐름에 대한 모든 부분은 생략하고, 핵심만 애기한다.
- **Call Stack** 에서 일반 동기코드를 처리한다.
- IO 에 관련된 로직 ( addEventListener , setTimeout, Ajax ) 은 Web APIS 에서 처리한다.
- APIS 에서 처리하고 난 뒤 CallBack 들은 CallBack Queue 에 담긴다.
- Event Loop 는 Callback Queue 에 있는 콜백들을 Call Stack 에 올려서 실행한다.
- Call Stack 이 비어있는 경우에만 Event Loop 는 콜스택에 콜백 큐의 로직들을 올려 실행하는 것이 핵심.
→ Call Stack 이 비어있을 때만 → 콜 스택에서 작업을 하고 있다면, 블럭킹이 되버린다는 것!
요약
- IO 에 관한 작업들은 Web Apis 에 맡겨서 처리하기 때문에 IO 처리에 대한 고성능을 보장한다.
- Event Loop 는 싱글 스레드 이기 때문에, 콜백 큐의 내용들은 잘게 짤라서 넣어주자.
- CallStack, CallBack Queue 모두 연산의 크기가 무거워지면 좋지않다.
엔진 구조를 보고 Node Js 를 활용할 때 얻을 수 있는 가치
- 콜백 큐에 들어가는 로직 (콜백 로직 역시 너무 많은 양을 할당하지 말자)
- 코드를 작성할 때, Call Stack 에 오래걸리는 작업을 넣지말자. ( 콜백큐가 처리안될 것은 당연하기 때문)
- 콜스택에서 오래 걸리는 작업 → CPU 연산과 같은 작업들일 것이다. → 즉 V8 은 비동기 IO 에 최적화된 엔진이다.
→ 물론 최근 Worker Thread 를 이용한 CPU 연산이 가능하긴 하다.
Spring, Node Js 을 정리하며 조금 더 알아보자.
위 섹션에서 우리는 Spring, Node js 의 구조에 대해서 알아보았다.
조금 더 이론적인 부분을 더 붙여 정리해보자.
2 섹션의 마지막 정리를 위해서 프로세스, 스레드, 코어 관련 개념이 꼭 필요하다.
프로세스
- 프로그램이 메모리에 올라간 상태
- 하나의 프로세스에 **여러개의 스레드**가 포함될 수 있다.
- 프로세스 끼리 자원을 공유하지 못한다. (통신을 이용하여여 공유할 수 있다.)
- 여러 개의 프로세스 (멀티 프로세스) 를 시분할 처리할 때, Context-switching 이 일어나는데 비용이 높다.
- 프로세스는 독립적인 단위이기 때문에, 하나의 프로세스에서 생긴 문제는 다른 프로세스에서 문제가 없다.
- 구글 크롬의 탭들은 하나의 **프로세스로** 이루어져 있다.
- 프로세스를 운영하는 비용은 비싸지만, 서버의 안정성을 위해서 사용할 수 있다.
스레드
- 하나의 프로세스 안에 여러개의 스레드가 포함될 수 있다.
- 프로세스에 비해 가볍다.
- 스레드들은 자원을 **공유**할 수 있다.
- 개발자가 동기화 처리를 잘 해줘야 한다.
- 하나의 스레드가 프로세스에 영향을 준다. (안정성이 떨어진다.)
코어
- 예전에는 싱글코어를 활용했다.
- 최근에는 멀티코어를 활용한다.
- 코어의 수 만큼 프로세스를 만들어서 활용하는 방식을 Node JS 에서 사용한다. (클러스터)
Node Js 를 더 의미있게 사용하기
- 멀티 프로세스를 지원하기 때문에 규모가 큰 서비스에서는 사용하자.
- 이벤트 루프와 콜 스택에 오는 태스크의 단위를 항상 **슬라이싱** 하여 사용하자.
- **Cpu-Intensive** : Worker-Thread 를 활용, **IO-Intensive** 는 Event Loop 에게 위임하자.
- 모든 로직을 항상 비동기 방식으로 처리하도록 노력하고, 처리하라. (엔진의 특성)
Spring Tomcat 구조에서 의미있게 사용하기
- 싱글 프로세스 위의 멀티 스레드 구조이다.
- 컨텍스트 스위칭의 비용이 없고, 동시성 문제를 해결해야 한다.
- 빈은 **stateLess 하게 관리하자.**
모든 어플리케이션 관점에서의 마지막 정리
- Node JS 에서는 멀티 프로세스, 멀티 스레드를 모두 사용할 수 있다.
- CPU-intensive 는 멀티 스레딩 IO 는 멀티 프로세스 를 사용하자.
- 연산 작업이냐, IO 작업이냐에 대해서 구분하자.
- 여러개의 프로세스가 처리되어야 할 때 동일한 데이터를 사용하고, 이러한 데이터를 하나의 디스크에 두고 모든 프로세서(CPU)가 이를 공유하면 비용적으로 저렴하다.
멀티 프로세스와 멀티 스레드는 언제 활용해야 할까?
언어의 구조적 특성에 의한 활용
Node Js
- 싱글 스레드 이지만, 해당 서버의 스펙(**cpu 코어 수**)에 따라서 멀티 프로세싱 처리를 하여 서버를 구성하면 더 좋을 것이다.
- 코어의 수에 프로세스 수를 맞춰 프로세스를 할당하는 게 보편적이다.
- 코어의 수에 비례하여 사용하지 않아도 되지만, 일반적으로 위와 같이 활용하더라.
- **pm2**같은 라이브러리를 활용하여 멀티 프로세싱을 조금 더 간편하게 구현할 수 있다.
Spring
- 서블릿 컨테이너의 디폴트 인 ‘톰캣’ 이 싱글 프로세스 이기 때문에 서버를 바꾸지 않는 이상 멀티 프로세싱은 불가능 할 것이다.
- 컨테이너는 언더토, 제티, 네티와 같은 서버들을 지원하고 있기 때문에, 해당 서버와 **WebFlux** 에 대해서 알아보자.
사례로 찾아보는 멀티 프로세스와 멀티 스레드
- 우아한 형제들 실시간 배달 서비스
→ 멀티 프로세스와 워커를 활용하여 진행된 우아한 형제들의 기술블로그
시나리오
- IO 에 대한 작업이 많고, 멀티 프로세스를 Node 에서 지원하기 때문에, 프로세싱을 했다.
문제점
- 스케일 아웃한 서버와 기존의 서버에 worker 사이에 통신을 할 수 없는 문제가 생겼다.
- Redis 서버를 중간에 넣어 통신을 수행시켰다.
- 물리적인 로드 밸런싱은 문제가 없으나, 서버안의 worker 에 세션관리에 문제가 생긴다.
→ 일반적으로로 세션관리는 세션 서버, 클러스터링 하여 처리할 수 있는데 프로세싱 한 경우에 대해서는 어떤 해법들이 존재할까?
개선 구조도
- 요청이 들어온 프로세스에서 세션을 관리하기 위해서 sticky session 처리는 오픈소스 모듈을 활용하였다고 한다.
- 구글 크롬과 인터넷 익스플로러
구글크롬
- 크롬의 탭 하나는 하나의 프로세스이다.
- 탭 하나에 문제가 생겨도 다른 탭에는 문제가 생기지 않는다.
인터넷 익스플로러
- 익스플로러의 탭 하나는 하나의 스레드이다.
- 하나의 탭에 문제가 생기면 모두 종료가 되버리곤 한다.
→ 예전에 익스플로러에서 흔히 마주하던 상황
- Node 에서 CPU 연산이 필요할 때
시나리오
- Node js 에서 Cpu-Intensive 가 필요하다.
- 알고리즘을 활용한 계산 등
해결방안
- Worker Thread 를 활용하여 연산한다.
참고자료
기타 시나리오
- 기타 시나리오에 대해 알고 계신 부분이 있다면 연락해주세요 ( 010 - 9830 - 5559 )
- 작성중 ....
우리가 나아가야 할 방향
해당 섹션은 개인적인 가정과 질문을 순서로 나눠 질답을 하며 정답을 찾아볼 것이다.
동기 블럭킹이 별로 안좋은 거 같은데 기존 프로젝트들은 바꿀 수 있으면 다 바꾸면 좋을까?
→ 절대 아니다. 기본적으로 우리가 개발했던 스프링 프로젝트들의 구조를 다시 한번 살펴보자.
- 비관계형 DB를 사용할때는 async보다는 Spring Webflux 사용
- 관계형 DB에서는 Async가 의미가 없다. DB단에서는 트랜젝션 때문에 동기로 통신을 처리하기 때문이다.
- 물론, 최근 비동기 DB 접근 방법이 있다. 프로덕션 레벨에서 사용해본 경험이 없으니, 잘 생각해보아야 한다.
비동기 논블럭킹 / 동기 블럭킹 말고는 굳이 구분해야 할 필요가 있나요?
→ 있다. Polling 과 같은 개념을 이해하려면 꼭 필요하고, 이 개념을 활용하여 아키텍쳐를 설계할 때 사용할 수 있다.
풀링을 이용한 아키텍쳐를 우아한 형제들에서 사용했다.
결국 비즈니스 규모가 큰 회사들은 비동기 / 논블럭킹 을 사랑할 것이고, 내가 원하는 회사나 기술과 맞다면 투자하라.
섞인 개념들 덜어내지 못한 부분 (읽지 말 것)
Node js 와 Spring 프레임워크가 가지는 차이점과 활용방안
Node Js 관점에서의 결론과 활용방안
- 비동기 IO의 처리에 특화된 엔진의 특성을 항상 기억하라. (CPU 를 많이 사용 X)
- 루프의 구조를 이해하여 태스크를 적절히 분리 하자.
- 루프의 구조를 이해하여 항상 비동기적 로직을 작성 하자.
- 서버는 항상 Stateless 하게 설계되어야 한다.
- Node Js 에서 멀티 프로세스를 활용할 수 있어야 한다.
- 멀티 프로세스는 “데이터 공유” 가 불가능 하다.
- 스케일 아웃이 될 때, 워커들간의 데이터 공유는 어떻게 해결할까?
- 아웃 된 서버와 기존 서버안에 있는 워커들간의 데이터 공유는 어떻게 해결할까?
멀티 프로세스와 워커를 활용하여 진행된 우아한 형제들의 기술블로그
→ 이벤트 정보를 공유하는 것과 유지되어야 할 로그인 정보는 격리되어야 한다.
Spring 관점에서의 결론과 활용방안
- 스프링의 기본 Servlet Container는 Process 하나에 배정된다. (즉 싱글프로세스)
- 싱글 프로세스 에서 멀티스레딩을 이용하여 동작한다.
- Spring 에서 빈 역시 StateLess 하게 설계되어야 한다.
- 빈을 공유해서 사용한다는 의미는 “오브젝트 직접참조” 가 아니다.
(빈 클래스의 정보를 가진 Method Area 에서 객체의 Binary Code 정보를 공유)
- 상태가 필요할 때, 다양한 방식으로 해결할 수 있고., 방어적인 프로그래밍으로 작성할 수 있다.
- (ThreadLocal, Bean scopes = Prototype, Stack Programming 등)
- 스케일 아웃이 되었을 때, 단일 서버안에서 데이터를 자유롭게 교환할 수 있는 이점이 있다.
- 스케일 아웃된 서버 전체에서 값을 참조하여 변경해야 할 경우, 특별한 처리가 더 필요할 것.
토큰 값을 공유하는 형태와 세션을 공유하는 형태에 대한 차이점
추가
→ V8 엔진과 노드의 엔진에 대한 자세한 매커니즘은 해당 포스트를 참고하세요.
→ 비동기 처리에 대한 개념은 해당 포스트를 참고하세요.
→ Worker Thread : CPU-Intensive 에 적합하다. IO-Intensive 는 loop 가 처리하는 게 좋다.
워커 스레드 관련 포스팅은 여기
해결해야 될 의문
- Redis 서버를 중간에 두어 활용한 사례말고, Kafka 를 이용하여 처리할 순 없을까?
- Worker 들 간의
IO 참고
로드밸런싱, 세션, 클러스터링, 캐시서버에 대한 개념
Sticky Session과 Session Clustering
스프링의 서블릿 컨테이너와 동작, 그리고 동기 블락킹 흐름
ServletContainer와 SpringContainer는 무엇이 다른가?
자바스크립트와 비동기
JavaScript 비동기 핵심 Event Loop 정리
NodeJS의 Non-Blocking IO와 멀티코어 프로세싱
NodeJS의 Non-Blocking IO와 멀티코어 프로세싱
멀티 프로세스와 멀티 스레딩의 장단점
멀티 프로세스(Multi Process)와 멀티 스레드(Multi Thread)
멀티 프로세싱 개념과 활용
PM2를 활용한 Node.js 무중단 서비스하기 - LINE ENGINEERING
스프링
스프링 구조
ServletContainer와 SpringContainer는 무엇이 다른가?
스프링의 비동기 처리의 유효성
일반적으로 async말고 일반적인 스프링 MVC패턴을 추천한다.
async를 써도 문제는 없지만, 사실 여러개의 API를 동시에 전송해서 그 결과를 JOIN할때 일반적인 메소드 내에서
CompletableFuture를 여러개 만든다음 전송시키고 합쳐서 응답을 내려주는게 제일 좋은 방안이다.
비관계형 DB를 사용할때는 async보다는 Spring Webflux 사용을 권장하며,
관계형 DB에서는 Async가 의미가 없다. DB단에서는 트랜젝션 때문에 동기로 통신을 처리하기 때문이다.
[Spring, Android] 스프링 안드로이드 JWT 활용한 WebView 모바일 유저 인증처리 (#업무)
IO 와 언어에 대한 이해와 활용
불필요하다고 생각했던 이유
- 프로그램의 동작 방식을 활용하는 방법에 대해서 몰랐다.
- 성능 최적화는 비즈니스 규모에 따라서 필요할 거라고 생각했다.
→ 유효하지 않은 비즈니스에서도 충분히 경험할 수 있고, 학습할 수 있다
계기
- 기술 블로그에서 해당 개념들을 어떻게 활용한 지에 대해서 기술되어 있다.
- 코어 부분에 대해서 알지 못해, 프레임워크나 라이브러리를 적당히 조합할 수 있는 능력이 떨어짐.
→ 백지부터 작성할 수 있는 주관이 없다.
다룰 개념
- IO 란
- Spring 과 Node 로 보는 동기, 블럭 / 비동기, 논블럭
- Node js 와 Spring 프레임워크가 가지는 차이점과 활용방안
- 멀티 프로세스와 멀티 스레드는 언제 활용해야 할까?
- 우리가 나아가야 할 방향
- 최종 정리
IO 란?
- 웹 어플리케이션의 구조 상 IO 작업의 비용이 가장 많이 차지한다.
- 병목현상의 주 원인은 IO 이며, IO 의 성능개선에 주목해야 한다.
- 웹 에서는 Network, Disk 관련 IO 가 높은 비중을 가지고 있다. (해당 작업들이 어플리케이션의 전부)
- 해당 파트에서는 IO 관점에 초점을 맞춘다.
→ 우리가 개발하는 어플리케이션은 항상 IO 와 밀접한 관련이 있고, 이런 IO 에 대한 처리에 대한 문제점으로 다양한 프로그래밍 방식이 발전해왔다는 것을 알아야한다.
Spring 과 Node 로 보는 동기, 블럭 / 비동기, 논블럭
→ 웹 어플리케이션은 접근성이 높고, 많은 유저의 요청에 이루어진 작업을 주로 수행한다.
→ IO 관련 작업이 많은 분야이기 때문에, 해당 섹션을 IO 의 관점으로 분류해서 보려고 한다.
Spring Servlet Container(Tomcat) 의 동기 / 블럭킹
- 스프링에서는 하나의 요청 (요청 당) 당 하나의 스레드를 할당한다.
- 동기 방식의 성능상 문제점을 해결하기 위해서 Multi-Thread 개념이 생겼다.
- 스프링의 WAS 에서는 서블릿 컨테이너 (톰캣) 의 스레드 수가 정해져있다.
- 해당 스레드 수를 넘는 유저의 요청이 발생하면 어떻게 대처해야할까?
동기 방식의 문제점과 해결방안
- 해당 스레드에서 처리할 수 있는 양의 범위를 넘어선다면?
→ 물리적인 서버를 업그레이드 시킨다. (스프링의 관점에서는 서블릿 컨테이너 (톰캣))
→ 로드 밸런싱을 통한 서버의 트래픽을 나눠서 처리한다.
- 로드 밸런싱이란?
- 클라이언트로부터 오는 요청을 여러 서버로 분배 해주는 것
- 리퀘스트를 동일하게 분배하기 때문에, 세션관리에서 문제가 생길 수 있다.
- 개념에 대해 정확히 알지 못한다면, 해당 포스트를 참고하라.

- 이대로 정말 괜찮을까?
→ Thread 들은 서로 데이터를 공유할 수 있기 때문에, 동기화를 관리해줘야 한다. (개발자의 능력)
→ 물리적인 서버의 비용은 트래픽에 비례해서 상승할 것이다.
→ 물리적인 단순한 로드밸런싱은 스케일 아웃의 관점에서 봤을 때 상태를 유지할 수 없다.
→ 상태를 유지하기 위해 Sticky Session, Session Clustering, Session Server 유지
→ 블록킹 언어의 근본적인 문제는 비즈니스가 커지면 커질수록 좋지 못할 것이다.
→ 결론 : 언어적 특성을 해결하기 위해 밸런싱, 세션서버를 독립적으로 관리해야 할 것이다.
→ 물론, 비동기 언어를 사용한다고 모든 문제를 해결 할 수는 없을 것이다.
추가
→ 스프링은 싱글 프로세스에서 멀티 스레드를 이용하여 처리한다.
→ 스프링의 프로세스와 스레드와 흐름에 대한 기본적인 개념은 해당 포스트에서 공부하도록 하자.
Thread 와 빈의 연관관계에 대해서
- 하나의 요청 → 하나의 스레드 → 빈 (Controller, Service ...) 을 공유하여 사용하는 환경
- 빈은 JVM 의 Heap 영역에 할당되는데, 하나의 싱글턴 인스턴스를 모두 공유하여 처리하지 않는다.
- 기본적인 구조에서 Controller 의 Mppaing 메소드를 참조하는 순서로 간단하게 전개해보자면
- 컨트롤러의 메소드를 스택이 할당 → 메소드 로직을 실행하는데, 여기서 힙에 공유된 객체들이 상태가 있으면 문제가 생긴다는 원리이다.
- 자세히 보기
- 컨트롤러의 인스턴스는 힙에 생성된다.
- 요청에 대한 처리를 위한 **스레드**는 힙의 **인스턴스를 공유**하는 것이 아니라, 해당 인스턴스의 메소드 정보를 가지고 있는 Method area 에서 메소드 정보를 참조한다.
- 메소드 영역의 메소드 코드를 실행 → 메소드 코드 중 힙에 있는 객체가 있다 → 상태를 공유 → 문제가 생긴다.
흐름을 도식화 해보자.@Controller public class memberController { private final memberService; @GetMapping("/") public String getMember(){ memberService.searchMember(); } }- 요청 발생 → 스레드 풀에서 스레드 할당 → **getMember()** 메소드 실행
- 메소드에 memberService.searchMember() 실행 → 해당 블럭은 스택에서 실행되므로 공유되지 않는다.
- memberService 는 싱글턴 (호출해도 같은 객체를 리턴한다)
- 해당 **빈의 메소드를 실행(searchMember())**시킨다.
- 멤버변수 빈 (싱글턴) 들은 **안전**한가? → 안전하지 않음. **세이프티 하게 설계해야할 의무**가 존재한다.
- 스프링의 모든 요청을 하나의 컨트롤러가 모두 처리하는 게 맞을까?
톰캣 구조와 스레드를 보며 얻을 수 있는 가치
- 스프링의 빈은 **Thread-safety** 하지 않다.
→ 멀티 스레드 프로그래밍에서 공유되는 객체는 싱글턴 임에도 **Thread-safety** 하지 않다. 라는 말과 같지 않나?
- 빈을 설계할 땐, **stateless** 하게 작성하자.
- 멀티 스레딩 프로그래밍에서는 **동시성** 문제가 생기지만, 반대로 **데이터를 공유**할 수 있다는 점은 장점이다.
→ 이 부분은 뒷장에서 컨텍스트 스위칭과 프로세스 관련 개념에서 한번 더 정리한다.
논블럭킹의 장점을 보여주는 Node Js
동기 블락킹 방식의 대표인 Spring 은 싱글 프로세스 에서 멀티스레드를 사용한다.
Node Js 는 싱글 스레드 임에도 고효율의 IO 처리를 보장한다.
싱글 스레드로 동작하는 Node Js 는 어떻게 고효율의 IO 처리를 보장할까?
처리 흐름에 대한 모든 부분은 생략하고, 핵심만 애기한다.
- **Call Stack** 에서 일반 동기코드를 처리한다.
- IO 에 관련된 로직 ( addEventListener , setTimeout, Ajax ) 은 Web APIS 에서 처리한다.
- APIS 에서 처리하고 난 뒤 CallBack 들은 CallBack Queue 에 담긴다.
- Event Loop 는 Callback Queue 에 있는 콜백들을 Call Stack 에 올려서 실행한다.
- Call Stack 이 비어있는 경우에만 Event Loop 는 콜스택에 콜백 큐의 로직들을 올려 실행하는 것이 핵심.
→ Call Stack 이 비어있을 때만 → 콜 스택에서 작업을 하고 있다면, 블럭킹이 되버린다는 것!
요약
- IO 에 관한 작업들은 Web Apis 에 맡겨서 처리하기 때문에 IO 처리에 대한 고성능을 보장한다.
- Event Loop 는 싱글 스레드 이기 때문에, 콜백 큐의 내용들은 잘게 짤라서 넣어주자.
- CallStack, CallBack Queue 모두 연산의 크기가 무거워지면 좋지않다.
엔진 구조를 보고 Node Js 를 활용할 때 얻을 수 있는 가치
- 콜백 큐에 들어가는 로직 (콜백 로직 역시 너무 많은 양을 할당하지 말자)
- 코드를 작성할 때, Call Stack 에 오래걸리는 작업을 넣지말자. ( 콜백큐가 처리안될 것은 당연하기 때문)
- 콜스택에서 오래 걸리는 작업 → CPU 연산과 같은 작업들일 것이다. → 즉 V8 은 비동기 IO 에 최적화된 엔진이다.
→ 물론 최근 Worker Thread 를 이용한 CPU 연산이 가능하긴 하다.
Spring, Node Js 을 정리하며 조금 더 알아보자.
위 섹션에서 우리는 Spring, Node js 의 구조에 대해서 알아보았다.
조금 더 이론적인 부분을 더 붙여 정리해보자.
2 섹션의 마지막 정리를 위해서 프로세스, 스레드, 코어 관련 개념이 꼭 필요하다.
프로세스
- 프로그램이 메모리에 올라간 상태
- 하나의 프로세스에 **여러개의 스레드**가 포함될 수 있다.
- 프로세스 끼리 자원을 공유하지 못한다. (통신을 이용하여여 공유할 수 있다.)
- 여러 개의 프로세스 (멀티 프로세스) 를 시분할 처리할 때, Context-switching 이 일어나는데 비용이 높다.
- 프로세스는 독립적인 단위이기 때문에, 하나의 프로세스에서 생긴 문제는 다른 프로세스에서 문제가 없다.
- 구글 크롬의 탭들은 하나의 **프로세스로** 이루어져 있다.
- 프로세스를 운영하는 비용은 비싸지만, 서버의 안정성을 위해서 사용할 수 있다.
스레드
- 하나의 프로세스 안에 여러개의 스레드가 포함될 수 있다.
- 프로세스에 비해 가볍다.
- 스레드들은 자원을 **공유**할 수 있다.
- 개발자가 동기화 처리를 잘 해줘야 한다.
- 하나의 스레드가 프로세스에 영향을 준다. (안정성이 떨어진다.)
코어
- 예전에는 싱글코어를 활용했다.
- 최근에는 멀티코어를 활용한다.
- 코어의 수 만큼 프로세스를 만들어서 활용하는 방식을 Node JS 에서 사용한다. (클러스터)
Node Js 를 더 의미있게 사용하기
- 멀티 프로세스를 지원하기 때문에 규모가 큰 서비스에서는 사용하자.
- 이벤트 루프와 콜 스택에 오는 태스크의 단위를 항상 **슬라이싱** 하여 사용하자.
- **Cpu-Intensive** : Worker-Thread 를 활용, **IO-Intensive** 는 Event Loop 에게 위임하자.
- 모든 로직을 항상 비동기 방식으로 처리하도록 노력하고, 처리하라. (엔진의 특성)
Spring Tomcat 구조에서 의미있게 사용하기
- 싱글 프로세스 위의 멀티 스레드 구조이다.
- 컨텍스트 스위칭의 비용이 없고, 동시성 문제를 해결해야 한다.
- 빈은 **stateLess 하게 관리하자.**
모든 어플리케이션 관점에서의 마지막 정리
- Node JS 에서는 멀티 프로세스, 멀티 스레드를 모두 사용할 수 있다.
- CPU-intensive 는 멀티 스레딩 IO 는 멀티 프로세스 를 사용하자.
- 연산 작업이냐, IO 작업이냐에 대해서 구분하자.
- 여러개의 프로세스가 처리되어야 할 때 동일한 데이터를 사용하고, 이러한 데이터를 하나의 디스크에 두고 모든 프로세서(CPU)가 이를 공유하면 비용적으로 저렴하다.
멀티 프로세스와 멀티 스레드는 언제 활용해야 할까?
언어의 구조적 특성에 의한 활용
Node Js
- 싱글 스레드 이지만, 해당 서버의 스펙(**cpu 코어 수**)에 따라서 멀티 프로세싱 처리를 하여 서버를 구성하면 더 좋을 것이다.
- 코어의 수에 프로세스 수를 맞춰 프로세스를 할당하는 게 보편적이다.
- 코어의 수에 비례하여 사용하지 않아도 되지만, 일반적으로 위와 같이 활용하더라.
- **pm2**같은 라이브러리를 활용하여 멀티 프로세싱을 조금 더 간편하게 구현할 수 있다.
Spring
- 서블릿 컨테이너의 디폴트 인 ‘톰캣’ 이 싱글 프로세스 이기 때문에 서버를 바꾸지 않는 이상 멀티 프로세싱은 불가능 할 것이다.
- 컨테이너는 언더토, 제티, 네티와 같은 서버들을 지원하고 있기 때문에, 해당 서버와 **WebFlux** 에 대해서 알아보자.
사례로 찾아보는 멀티 프로세스와 멀티 스레드
- 우아한 형제들 실시간 배달 서비스
→ 멀티 프로세스와 워커를 활용하여 진행된 우아한 형제들의 기술블로그
시나리오
- IO 에 대한 작업이 많고, 멀티 프로세스를 Node 에서 지원하기 때문에, 프로세싱을 했다.
문제점
- 스케일 아웃한 서버와 기존의 서버에 worker 사이에 통신을 할 수 없는 문제가 생겼다.
- Redis 서버를 중간에 넣어 통신을 수행시켰다.
- 물리적인 로드 밸런싱은 문제가 없으나, 서버안의 worker 에 세션관리에 문제가 생긴다.
→ 일반적으로로 세션관리는 세션 서버, 클러스터링 하여 처리할 수 있는데 프로세싱 한 경우에 대해서는 어떤 해법들이 존재할까?
개선 구조도
- 요청이 들어온 프로세스에서 세션을 관리하기 위해서 sticky session 처리는 오픈소스 모듈을 활용하였다고 한다.
- 구글 크롬과 인터넷 익스플로러
구글크롬
- 크롬의 탭 하나는 하나의 프로세스이다.
- 탭 하나에 문제가 생겨도 다른 탭에는 문제가 생기지 않는다.
인터넷 익스플로러
- 익스플로러의 탭 하나는 하나의 스레드이다.
- 하나의 탭에 문제가 생기면 모두 종료가 되버리곤 한다.
→ 예전에 익스플로러에서 흔히 마주하던 상황
- Node 에서 CPU 연산이 필요할 때
시나리오
- Node js 에서 Cpu-Intensive 가 필요하다.
- 알고리즘을 활용한 계산 등
해결방안
- Worker Thread 를 활용하여 연산한다.
참고자료
기타 시나리오
- 기타 시나리오에 대해 알고 계신 부분이 있다면 연락해주세요 ( 010 - 9830 - 5559 )
- 작성중 ....
우리가 나아가야 할 방향
해당 섹션은 개인적인 가정과 질문을 순서로 나눠 질답을 하며 정답을 찾아볼 것이다.
동기 블럭킹이 별로 안좋은 거 같은데 기존 프로젝트들은 바꿀 수 있으면 다 바꾸면 좋을까?
→ 절대 아니다. 기본적으로 우리가 개발했던 스프링 프로젝트들의 구조를 다시 한번 살펴보자.
- 비관계형 DB를 사용할때는 async보다는 Spring Webflux 사용
- 관계형 DB에서는 Async가 의미가 없다. DB단에서는 트랜젝션 때문에 동기로 통신을 처리하기 때문이다.
- 물론, 최근 비동기 DB 접근 방법이 있다. 프로덕션 레벨에서 사용해본 경험이 없으니, 잘 생각해보아야 한다.
비동기 논블럭킹 / 동기 블럭킹 말고는 굳이 구분해야 할 필요가 있나요?
→ 있다. Polling 과 같은 개념을 이해하려면 꼭 필요하고, 이 개념을 활용하여 아키텍쳐를 설계할 때 사용할 수 있다.
풀링을 이용한 아키텍쳐를 우아한 형제들에서 사용했다.
결국 비즈니스 규모가 큰 회사들은 비동기 / 논블럭킹 을 사랑할 것이고, 내가 원하는 회사나 기술과 맞다면 투자하라.
섞인 개념들 덜어내지 못한 부분 (읽지 말 것)
Node js 와 Spring 프레임워크가 가지는 차이점과 활용방안
Node Js 관점에서의 결론과 활용방안
- 비동기 IO의 처리에 특화된 엔진의 특성을 항상 기억하라. (CPU 를 많이 사용 X)
- 루프의 구조를 이해하여 태스크를 적절히 분리 하자.
- 루프의 구조를 이해하여 항상 비동기적 로직을 작성 하자.
- 서버는 항상 Stateless 하게 설계되어야 한다.
- Node Js 에서 멀티 프로세스를 활용할 수 있어야 한다.
- 멀티 프로세스는 “데이터 공유” 가 불가능 하다.
- 스케일 아웃이 될 때, 워커들간의 데이터 공유는 어떻게 해결할까?
- 아웃 된 서버와 기존 서버안에 있는 워커들간의 데이터 공유는 어떻게 해결할까?
멀티 프로세스와 워커를 활용하여 진행된 우아한 형제들의 기술블로그
→ 이벤트 정보를 공유하는 것과 유지되어야 할 로그인 정보는 격리되어야 한다.
Spring 관점에서의 결론과 활용방안
- 스프링의 기본 Servlet Container는 Process 하나에 배정된다. (즉 싱글프로세스)
- 싱글 프로세스 에서 멀티스레딩을 이용하여 동작한다.
- Spring 에서 빈 역시 StateLess 하게 설계되어야 한다.
- 빈을 공유해서 사용한다는 의미는 “오브젝트 직접참조” 가 아니다.
(빈 클래스의 정보를 가진 Method Area 에서 객체의 Binary Code 정보를 공유)
- 상태가 필요할 때, 다양한 방식으로 해결할 수 있고., 방어적인 프로그래밍으로 작성할 수 있다.
- (ThreadLocal, Bean scopes = Prototype, Stack Programming 등)
- 스케일 아웃이 되었을 때, 단일 서버안에서 데이터를 자유롭게 교환할 수 있는 이점이 있다.
- 스케일 아웃된 서버 전체에서 값을 참조하여 변경해야 할 경우, 특별한 처리가 더 필요할 것.
토큰 값을 공유하는 형태와 세션을 공유하는 형태에 대한 차이점
추가
→ V8 엔진과 노드의 엔진에 대한 자세한 매커니즘은 해당 포스트를 참고하세요.
→ 비동기 처리에 대한 개념은 해당 포스트를 참고하세요.
→ Worker Thread : CPU-Intensive 에 적합하다. IO-Intensive 는 loop 가 처리하는 게 좋다.
워커 스레드 관련 포스팅은 여기
해결해야 될 의문
- Redis 서버를 중간에 두어 활용한 사례말고, Kafka 를 이용하여 처리할 순 없을까?
- Worker 들 간의
IO 참고
로드밸런싱, 세션, 클러스터링, 캐시서버에 대한 개념
Sticky Session과 Session Clustering
스프링의 서블릿 컨테이너와 동작, 그리고 동기 블락킹 흐름
ServletContainer와 SpringContainer는 무엇이 다른가?
자바스크립트와 비동기
JavaScript 비동기 핵심 Event Loop 정리
NodeJS의 Non-Blocking IO와 멀티코어 프로세싱
NodeJS의 Non-Blocking IO와 멀티코어 프로세싱
멀티 프로세스와 멀티 스레딩의 장단점
멀티 프로세스(Multi Process)와 멀티 스레드(Multi Thread)
멀티 프로세싱 개념과 활용
PM2를 활용한 Node.js 무중단 서비스하기 - LINE ENGINEERING
스프링
스프링 구조
ServletContainer와 SpringContainer는 무엇이 다른가?
스프링의 비동기 처리의 유효성
일반적으로 async말고 일반적인 스프링 MVC패턴을 추천한다.
async를 써도 문제는 없지만, 사실 여러개의 API를 동시에 전송해서 그 결과를 JOIN할때 일반적인 메소드 내에서
CompletableFuture를 여러개 만든다음 전송시키고 합쳐서 응답을 내려주는게 제일 좋은 방안이다.
비관계형 DB를 사용할때는 async보다는 Spring Webflux 사용을 권장하며,
관계형 DB에서는 Async가 의미가 없다. DB단에서는 트랜젝션 때문에 동기로 통신을 처리하기 때문이다.
[Spring, Android] 스프링 안드로이드 JWT 활용한 WebView 모바일 유저 인증처리 (#업무)
IO 와 언어에 대한 이해와 활용
불필요하다고 생각했던 이유
- 프로그램의 동작 방식을 활용하는 방법에 대해서 몰랐다.
- 성능 최적화는 비즈니스 규모에 따라서 필요할 거라고 생각했다.
→ 유효하지 않은 비즈니스에서도 충분히 경험할 수 있고, 학습할 수 있다
계기
- 기술 블로그에서 해당 개념들을 어떻게 활용한 지에 대해서 기술되어 있다.
- 코어 부분에 대해서 알지 못해, 프레임워크나 라이브러리를 적당히 조합할 수 있는 능력이 떨어짐.
→ 백지부터 작성할 수 있는 주관이 없다.
다룰 개념
- IO 란
- Spring 과 Node 로 보는 동기, 블럭 / 비동기, 논블럭
- Node js 와 Spring 프레임워크가 가지는 차이점과 활용방안
- 멀티 프로세스와 멀티 스레드는 언제 활용해야 할까?
- 우리가 나아가야 할 방향
- 최종 정리
IO 란?
- 웹 어플리케이션의 구조 상 IO 작업의 비용이 가장 많이 차지한다.
- 병목현상의 주 원인은 IO 이며, IO 의 성능개선에 주목해야 한다.
- 웹 에서는 Network, Disk 관련 IO 가 높은 비중을 가지고 있다. (해당 작업들이 어플리케이션의 전부)
- 해당 파트에서는 IO 관점에 초점을 맞춘다.
→ 우리가 개발하는 어플리케이션은 항상 IO 와 밀접한 관련이 있고, 이런 IO 에 대한 처리에 대한 문제점으로 다양한 프로그래밍 방식이 발전해왔다는 것을 알아야한다.
Spring 과 Node 로 보는 동기, 블럭 / 비동기, 논블럭
→ 웹 어플리케이션은 접근성이 높고, 많은 유저의 요청에 이루어진 작업을 주로 수행한다.
→ IO 관련 작업이 많은 분야이기 때문에, 해당 섹션을 IO 의 관점으로 분류해서 보려고 한다.
Spring Servlet Container(Tomcat) 의 동기 / 블럭킹
- 스프링에서는 하나의 요청 (요청 당) 당 하나의 스레드를 할당한다.
- 동기 방식의 성능상 문제점을 해결하기 위해서 Multi-Thread 개념이 생겼다.
- 스프링의 WAS 에서는 서블릿 컨테이너 (톰캣) 의 스레드 수가 정해져있다.
- 해당 스레드 수를 넘는 유저의 요청이 발생하면 어떻게 대처해야할까?
동기 방식의 문제점과 해결방안
- 해당 스레드에서 처리할 수 있는 양의 범위를 넘어선다면?
→ 물리적인 서버를 업그레이드 시킨다. (스프링의 관점에서는 서블릿 컨테이너 (톰캣))
→ 로드 밸런싱을 통한 서버의 트래픽을 나눠서 처리한다.
- 로드 밸런싱이란?
- 클라이언트로부터 오는 요청을 여러 서버로 분배 해주는 것
- 리퀘스트를 동일하게 분배하기 때문에, 세션관리에서 문제가 생길 수 있다.
- 개념에 대해 정확히 알지 못한다면, 해당 포스트를 참고하라.

- 이대로 정말 괜찮을까?
→ Thread 들은 서로 데이터를 공유할 수 있기 때문에, 동기화를 관리해줘야 한다. (개발자의 능력)
→ 물리적인 서버의 비용은 트래픽에 비례해서 상승할 것이다.
→ 물리적인 단순한 로드밸런싱은 스케일 아웃의 관점에서 봤을 때 상태를 유지할 수 없다.
→ 상태를 유지하기 위해 Sticky Session, Session Clustering, Session Server 유지
→ 블록킹 언어의 근본적인 문제는 비즈니스가 커지면 커질수록 좋지 못할 것이다.
→ 결론 : 언어적 특성을 해결하기 위해 밸런싱, 세션서버를 독립적으로 관리해야 할 것이다.
→ 물론, 비동기 언어를 사용한다고 모든 문제를 해결 할 수는 없을 것이다.
추가
→ 스프링은 싱글 프로세스에서 멀티 스레드를 이용하여 처리한다.
→ 스프링의 프로세스와 스레드와 흐름에 대한 기본적인 개념은 해당 포스트에서 공부하도록 하자.
Thread 와 빈의 연관관계에 대해서
- 하나의 요청 → 하나의 스레드 → 빈 (Controller, Service ...) 을 공유하여 사용하는 환경
- 빈은 JVM 의 Heap 영역에 할당되는데, 하나의 싱글턴 인스턴스를 모두 공유하여 처리하지 않는다.
- 기본적인 구조에서 Controller 의 Mppaing 메소드를 참조하는 순서로 간단하게 전개해보자면
- 컨트롤러의 메소드를 스택이 할당 → 메소드 로직을 실행하는데, 여기서 힙에 공유된 객체들이 상태가 있으면 문제가 생긴다는 원리이다.
- 자세히 보기
- 컨트롤러의 인스턴스는 힙에 생성된다.
- 요청에 대한 처리를 위한 **스레드**는 힙의 **인스턴스를 공유**하는 것이 아니라, 해당 인스턴스의 메소드 정보를 가지고 있는 Method area 에서 메소드 정보를 참조한다.
- 메소드 영역의 메소드 코드를 실행 → 메소드 코드 중 힙에 있는 객체가 있다 → 상태를 공유 → 문제가 생긴다.
흐름을 도식화 해보자.@Controller public class memberController { private final memberService; @GetMapping("/") public String getMember(){ memberService.searchMember(); } }- 요청 발생 → 스레드 풀에서 스레드 할당 → **getMember()** 메소드 실행
- 메소드에 memberService.searchMember() 실행 → 해당 블럭은 스택에서 실행되므로 공유되지 않는다.
- memberService 는 싱글턴 (호출해도 같은 객체를 리턴한다)
- 해당 **빈의 메소드를 실행(searchMember())**시킨다.
- 멤버변수 빈 (싱글턴) 들은 **안전**한가? → 안전하지 않음. **세이프티 하게 설계해야할 의무**가 존재한다.
- 스프링의 모든 요청을 하나의 컨트롤러가 모두 처리하는 게 맞을까?
톰캣 구조와 스레드를 보며 얻을 수 있는 가치
- 스프링의 빈은 **Thread-safety** 하지 않다.
→ 멀티 스레드 프로그래밍에서 공유되는 객체는 싱글턴 임에도 **Thread-safety** 하지 않다. 라는 말과 같지 않나?
- 빈을 설계할 땐, **stateless** 하게 작성하자.
- 멀티 스레딩 프로그래밍에서는 **동시성** 문제가 생기지만, 반대로 **데이터를 공유**할 수 있다는 점은 장점이다.
→ 이 부분은 뒷장에서 컨텍스트 스위칭과 프로세스 관련 개념에서 한번 더 정리한다.
논블럭킹의 장점을 보여주는 Node Js
동기 블락킹 방식의 대표인 Spring 은 싱글 프로세스 에서 멀티스레드를 사용한다.
Node Js 는 싱글 스레드 임에도 고효율의 IO 처리를 보장한다.
싱글 스레드로 동작하는 Node Js 는 어떻게 고효율의 IO 처리를 보장할까?
처리 흐름에 대한 모든 부분은 생략하고, 핵심만 애기한다.
- **Call Stack** 에서 일반 동기코드를 처리한다.
- IO 에 관련된 로직 ( addEventListener , setTimeout, Ajax ) 은 Web APIS 에서 처리한다.
- APIS 에서 처리하고 난 뒤 CallBack 들은 CallBack Queue 에 담긴다.
- Event Loop 는 Callback Queue 에 있는 콜백들을 Call Stack 에 올려서 실행한다.
- Call Stack 이 비어있는 경우에만 Event Loop 는 콜스택에 콜백 큐의 로직들을 올려 실행하는 것이 핵심.
→ Call Stack 이 비어있을 때만 → 콜 스택에서 작업을 하고 있다면, 블럭킹이 되버린다는 것!
요약
- IO 에 관한 작업들은 Web Apis 에 맡겨서 처리하기 때문에 IO 처리에 대한 고성능을 보장한다.
- Event Loop 는 싱글 스레드 이기 때문에, 콜백 큐의 내용들은 잘게 짤라서 넣어주자.
- CallStack, CallBack Queue 모두 연산의 크기가 무거워지면 좋지않다.
엔진 구조를 보고 Node Js 를 활용할 때 얻을 수 있는 가치
- 콜백 큐에 들어가는 로직 (콜백 로직 역시 너무 많은 양을 할당하지 말자)
- 코드를 작성할 때, Call Stack 에 오래걸리는 작업을 넣지말자. ( 콜백큐가 처리안될 것은 당연하기 때문)
- 콜스택에서 오래 걸리는 작업 → CPU 연산과 같은 작업들일 것이다. → 즉 V8 은 비동기 IO 에 최적화된 엔진이다.
→ 물론 최근 Worker Thread 를 이용한 CPU 연산이 가능하긴 하다.
Spring, Node Js 을 정리하며 조금 더 알아보자.
위 섹션에서 우리는 Spring, Node js 의 구조에 대해서 알아보았다.
조금 더 이론적인 부분을 더 붙여 정리해보자.
2 섹션의 마지막 정리를 위해서 프로세스, 스레드, 코어 관련 개념이 꼭 필요하다.
프로세스
- 프로그램이 메모리에 올라간 상태
- 하나의 프로세스에 **여러개의 스레드**가 포함될 수 있다.
- 프로세스 끼리 자원을 공유하지 못한다. (통신을 이용하여여 공유할 수 있다.)
- 여러 개의 프로세스 (멀티 프로세스) 를 시분할 처리할 때, Context-switching 이 일어나는데 비용이 높다.
- 프로세스는 독립적인 단위이기 때문에, 하나의 프로세스에서 생긴 문제는 다른 프로세스에서 문제가 없다.
- 구글 크롬의 탭들은 하나의 **프로세스로** 이루어져 있다.
- 프로세스를 운영하는 비용은 비싸지만, 서버의 안정성을 위해서 사용할 수 있다.
스레드
- 하나의 프로세스 안에 여러개의 스레드가 포함될 수 있다.
- 프로세스에 비해 가볍다.
- 스레드들은 자원을 **공유**할 수 있다.
- 개발자가 동기화 처리를 잘 해줘야 한다.
- 하나의 스레드가 프로세스에 영향을 준다. (안정성이 떨어진다.)
코어
- 예전에는 싱글코어를 활용했다.
- 최근에는 멀티코어를 활용한다.
- 코어의 수 만큼 프로세스를 만들어서 활용하는 방식을 Node JS 에서 사용한다. (클러스터)
Node Js 를 더 의미있게 사용하기
- 멀티 프로세스를 지원하기 때문에 규모가 큰 서비스에서는 사용하자.
- 이벤트 루프와 콜 스택에 오는 태스크의 단위를 항상 **슬라이싱** 하여 사용하자.
- **Cpu-Intensive** : Worker-Thread 를 활용, **IO-Intensive** 는 Event Loop 에게 위임하자.
- 모든 로직을 항상 비동기 방식으로 처리하도록 노력하고, 처리하라. (엔진의 특성)
Spring Tomcat 구조에서 의미있게 사용하기
- 싱글 프로세스 위의 멀티 스레드 구조이다.
- 컨텍스트 스위칭의 비용이 없고, 동시성 문제를 해결해야 한다.
- 빈은 **stateLess 하게 관리하자.**
모든 어플리케이션 관점에서의 마지막 정리
- Node JS 에서는 멀티 프로세스, 멀티 스레드를 모두 사용할 수 있다.
- CPU-intensive 는 멀티 스레딩 IO 는 멀티 프로세스 를 사용하자.
- 연산 작업이냐, IO 작업이냐에 대해서 구분하자.
- 여러개의 프로세스가 처리되어야 할 때 동일한 데이터를 사용하고, 이러한 데이터를 하나의 디스크에 두고 모든 프로세서(CPU)가 이를 공유하면 비용적으로 저렴하다.
멀티 프로세스와 멀티 스레드는 언제 활용해야 할까?
언어의 구조적 특성에 의한 활용
Node Js
- 싱글 스레드 이지만, 해당 서버의 스펙(**cpu 코어 수**)에 따라서 멀티 프로세싱 처리를 하여 서버를 구성하면 더 좋을 것이다.
- 코어의 수에 프로세스 수를 맞춰 프로세스를 할당하는 게 보편적이다.
- 코어의 수에 비례하여 사용하지 않아도 되지만, 일반적으로 위와 같이 활용하더라.
- **pm2**같은 라이브러리를 활용하여 멀티 프로세싱을 조금 더 간편하게 구현할 수 있다.
Spring
- 서블릿 컨테이너의 디폴트 인 ‘톰캣’ 이 싱글 프로세스 이기 때문에 서버를 바꾸지 않는 이상 멀티 프로세싱은 불가능 할 것이다.
- 컨테이너는 언더토, 제티, 네티와 같은 서버들을 지원하고 있기 때문에, 해당 서버와 **WebFlux** 에 대해서 알아보자.
사례로 찾아보는 멀티 프로세스와 멀티 스레드
- 우아한 형제들 실시간 배달 서비스
→ 멀티 프로세스와 워커를 활용하여 진행된 우아한 형제들의 기술블로그
시나리오
- IO 에 대한 작업이 많고, 멀티 프로세스를 Node 에서 지원하기 때문에, 프로세싱을 했다.
문제점
- 스케일 아웃한 서버와 기존의 서버에 worker 사이에 통신을 할 수 없는 문제가 생겼다.
- Redis 서버를 중간에 넣어 통신을 수행시켰다.
- 물리적인 로드 밸런싱은 문제가 없으나, 서버안의 worker 에 세션관리에 문제가 생긴다.
→ 일반적으로로 세션관리는 세션 서버, 클러스터링 하여 처리할 수 있는데 프로세싱 한 경우에 대해서는 어떤 해법들이 존재할까?
개선 구조도
- 요청이 들어온 프로세스에서 세션을 관리하기 위해서 sticky session 처리는 오픈소스 모듈을 활용하였다고 한다.
- 구글 크롬과 인터넷 익스플로러
구글크롬
- 크롬의 탭 하나는 하나의 프로세스이다.
- 탭 하나에 문제가 생겨도 다른 탭에는 문제가 생기지 않는다.
인터넷 익스플로러
- 익스플로러의 탭 하나는 하나의 스레드이다.
- 하나의 탭에 문제가 생기면 모두 종료가 되버리곤 한다.
→ 예전에 익스플로러에서 흔히 마주하던 상황
- Node 에서 CPU 연산이 필요할 때
시나리오
- Node js 에서 Cpu-Intensive 가 필요하다.
- 알고리즘을 활용한 계산 등
해결방안
- Worker Thread 를 활용하여 연산한다.
참고자료
기타 시나리오
- 기타 시나리오에 대해 알고 계신 부분이 있다면 연락해주세요 ( 010 - 9830 - 5559 )
- 작성중 ....
우리가 나아가야 할 방향
해당 섹션은 개인적인 가정과 질문을 순서로 나눠 질답을 하며 정답을 찾아볼 것이다.
동기 블럭킹이 별로 안좋은 거 같은데 기존 프로젝트들은 바꿀 수 있으면 다 바꾸면 좋을까?
→ 절대 아니다. 기본적으로 우리가 개발했던 스프링 프로젝트들의 구조를 다시 한번 살펴보자.
- 비관계형 DB를 사용할때는 async보다는 Spring Webflux 사용
- 관계형 DB에서는 Async가 의미가 없다. DB단에서는 트랜젝션 때문에 동기로 통신을 처리하기 때문이다.
- 물론, 최근 비동기 DB 접근 방법이 있다. 프로덕션 레벨에서 사용해본 경험이 없으니, 잘 생각해보아야 한다.
비동기 논블럭킹 / 동기 블럭킹 말고는 굳이 구분해야 할 필요가 있나요?
→ 있다. Polling 과 같은 개념을 이해하려면 꼭 필요하고, 이 개념을 활용하여 아키텍쳐를 설계할 때 사용할 수 있다.
풀링을 이용한 아키텍쳐를 우아한 형제들에서 사용했다.
결국 비즈니스 규모가 큰 회사들은 비동기 / 논블럭킹 을 사랑할 것이고, 내가 원하는 회사나 기술과 맞다면 투자하라.
섞인 개념들 덜어내지 못한 부분 (읽지 말 것)
Node js 와 Spring 프레임워크가 가지는 차이점과 활용방안
Node Js 관점에서의 결론과 활용방안
- 비동기 IO의 처리에 특화된 엔진의 특성을 항상 기억하라. (CPU 를 많이 사용 X)
- 루프의 구조를 이해하여 태스크를 적절히 분리 하자.
- 루프의 구조를 이해하여 항상 비동기적 로직을 작성 하자.
- 서버는 항상 Stateless 하게 설계되어야 한다.
- Node Js 에서 멀티 프로세스를 활용할 수 있어야 한다.
- 멀티 프로세스는 “데이터 공유” 가 불가능 하다.
- 스케일 아웃이 될 때, 워커들간의 데이터 공유는 어떻게 해결할까?
- 아웃 된 서버와 기존 서버안에 있는 워커들간의 데이터 공유는 어떻게 해결할까?
멀티 프로세스와 워커를 활용하여 진행된 우아한 형제들의 기술블로그
→ 이벤트 정보를 공유하는 것과 유지되어야 할 로그인 정보는 격리되어야 한다.
Spring 관점에서의 결론과 활용방안
- 스프링의 기본 Servlet Container는 Process 하나에 배정된다. (즉 싱글프로세스)
- 싱글 프로세스 에서 멀티스레딩을 이용하여 동작한다.
- Spring 에서 빈 역시 StateLess 하게 설계되어야 한다.
- 빈을 공유해서 사용한다는 의미는 “오브젝트 직접참조” 가 아니다.
(빈 클래스의 정보를 가진 Method Area 에서 객체의 Binary Code 정보를 공유)
- 상태가 필요할 때, 다양한 방식으로 해결할 수 있고., 방어적인 프로그래밍으로 작성할 수 있다.
- (ThreadLocal, Bean scopes = Prototype, Stack Programming 등)
- 스케일 아웃이 되었을 때, 단일 서버안에서 데이터를 자유롭게 교환할 수 있는 이점이 있다.
- 스케일 아웃된 서버 전체에서 값을 참조하여 변경해야 할 경우, 특별한 처리가 더 필요할 것.
토큰 값을 공유하는 형태와 세션을 공유하는 형태에 대한 차이점
추가
→ V8 엔진과 노드의 엔진에 대한 자세한 매커니즘은 해당 포스트를 참고하세요.
→ 비동기 처리에 대한 개념은 해당 포스트를 참고하세요.
→ Worker Thread : CPU-Intensive 에 적합하다. IO-Intensive 는 loop 가 처리하는 게 좋다.
워커 스레드 관련 포스팅은 여기
해결해야 될 의문
- Redis 서버를 중간에 두어 활용한 사례말고, Kafka 를 이용하여 처리할 순 없을까?
- Worker 들 간의
IO 참고
로드밸런싱, 세션, 클러스터링, 캐시서버에 대한 개념
Sticky Session과 Session Clustering
스프링의 서블릿 컨테이너와 동작, 그리고 동기 블락킹 흐름
ServletContainer와 SpringContainer는 무엇이 다른가?
자바스크립트와 비동기
JavaScript 비동기 핵심 Event Loop 정리
NodeJS의 Non-Blocking IO와 멀티코어 프로세싱
NodeJS의 Non-Blocking IO와 멀티코어 프로세싱
멀티 프로세스와 멀티 스레딩의 장단점
멀티 프로세스(Multi Process)와 멀티 스레드(Multi Thread)
멀티 프로세싱 개념과 활용
PM2를 활용한 Node.js 무중단 서비스하기 - LINE ENGINEERING
스프링
스프링 구조
ServletContainer와 SpringContainer는 무엇이 다른가?
스프링의 비동기 처리의 유효성
일반적으로 async말고 일반적인 스프링 MVC패턴을 추천한다.
async를 써도 문제는 없지만, 사실 여러개의 API를 동시에 전송해서 그 결과를 JOIN할때 일반적인 메소드 내에서
CompletableFuture를 여러개 만든다음 전송시키고 합쳐서 응답을 내려주는게 제일 좋은 방안이다.
비관계형 DB를 사용할때는 async보다는 Spring Webflux 사용을 권장하며,
관계형 DB에서는 Async가 의미가 없다. DB단에서는 트랜젝션 때문에 동기로 통신을 처리하기 때문이다.
[Spring, Android] 스프링 안드로이드 JWT 활용한 WebView 모바일 유저 인증처리 (#업무)
IO 와 언어에 대한 이해와 활용
불필요하다고 생각했던 이유
- 프로그램의 동작 방식을 활용하는 방법에 대해서 몰랐다.
- 성능 최적화는 비즈니스 규모에 따라서 필요할 거라고 생각했다.
→ 유효하지 않은 비즈니스에서도 충분히 경험할 수 있고, 학습할 수 있다
계기
- 기술 블로그에서 해당 개념들을 어떻게 활용한 지에 대해서 기술되어 있다.
- 코어 부분에 대해서 알지 못해, 프레임워크나 라이브러리를 적당히 조합할 수 있는 능력이 떨어짐.
→ 백지부터 작성할 수 있는 주관이 없다.
다룰 개념
- IO 란
- Spring 과 Node 로 보는 동기, 블럭 / 비동기, 논블럭
- Node js 와 Spring 프레임워크가 가지는 차이점과 활용방안
- 멀티 프로세스와 멀티 스레드는 언제 활용해야 할까?
- 우리가 나아가야 할 방향
- 최종 정리
IO 란?
- 웹 어플리케이션의 구조 상 IO 작업의 비용이 가장 많이 차지한다.
- 병목현상의 주 원인은 IO 이며, IO 의 성능개선에 주목해야 한다.
- 웹 에서는 Network, Disk 관련 IO 가 높은 비중을 가지고 있다. (해당 작업들이 어플리케이션의 전부)
- 해당 파트에서는 IO 관점에 초점을 맞춘다.
→ 우리가 개발하는 어플리케이션은 항상 IO 와 밀접한 관련이 있고, 이런 IO 에 대한 처리에 대한 문제점으로 다양한 프로그래밍 방식이 발전해왔다는 것을 알아야한다.
Spring 과 Node 로 보는 동기, 블럭 / 비동기, 논블럭
→ 웹 어플리케이션은 접근성이 높고, 많은 유저의 요청에 이루어진 작업을 주로 수행한다.
→ IO 관련 작업이 많은 분야이기 때문에, 해당 섹션을 IO 의 관점으로 분류해서 보려고 한다.
Spring Servlet Container(Tomcat) 의 동기 / 블럭킹
- 스프링에서는 하나의 요청 (요청 당) 당 하나의 스레드를 할당한다.
- 동기 방식의 성능상 문제점을 해결하기 위해서 Multi-Thread 개념이 생겼다.
- 스프링의 WAS 에서는 서블릿 컨테이너 (톰캣) 의 스레드 수가 정해져있다.
- 해당 스레드 수를 넘는 유저의 요청이 발생하면 어떻게 대처해야할까?
동기 방식의 문제점과 해결방안
- 해당 스레드에서 처리할 수 있는 양의 범위를 넘어선다면?
→ 물리적인 서버를 업그레이드 시킨다. (스프링의 관점에서는 서블릿 컨테이너 (톰캣))
→ 로드 밸런싱을 통한 서버의 트래픽을 나눠서 처리한다.
- 로드 밸런싱이란?
- 클라이언트로부터 오는 요청을 여러 서버로 분배 해주는 것
- 리퀘스트를 동일하게 분배하기 때문에, 세션관리에서 문제가 생길 수 있다.
- 개념에 대해 정확히 알지 못한다면, 해당 포스트를 참고하라.

- 이대로 정말 괜찮을까?
→ Thread 들은 서로 데이터를 공유할 수 있기 때문에, 동기화를 관리해줘야 한다. (개발자의 능력)
→ 물리적인 서버의 비용은 트래픽에 비례해서 상승할 것이다.
→ 물리적인 단순한 로드밸런싱은 스케일 아웃의 관점에서 봤을 때 상태를 유지할 수 없다.
→ 상태를 유지하기 위해 Sticky Session, Session Clustering, Session Server 유지
→ 블록킹 언어의 근본적인 문제는 비즈니스가 커지면 커질수록 좋지 못할 것이다.
→ 결론 : 언어적 특성을 해결하기 위해 밸런싱, 세션서버를 독립적으로 관리해야 할 것이다.
→ 물론, 비동기 언어를 사용한다고 모든 문제를 해결 할 수는 없을 것이다.
추가
→ 스프링은 싱글 프로세스에서 멀티 스레드를 이용하여 처리한다.
→ 스프링의 프로세스와 스레드와 흐름에 대한 기본적인 개념은 해당 포스트에서 공부하도록 하자.
Thread 와 빈의 연관관계에 대해서
- 하나의 요청 → 하나의 스레드 → 빈 (Controller, Service ...) 을 공유하여 사용하는 환경
- 빈은 JVM 의 Heap 영역에 할당되는데, 하나의 싱글턴 인스턴스를 모두 공유하여 처리하지 않는다.
- 기본적인 구조에서 Controller 의 Mppaing 메소드를 참조하는 순서로 간단하게 전개해보자면
- 컨트롤러의 메소드를 스택이 할당 → 메소드 로직을 실행하는데, 여기서 힙에 공유된 객체들이 상태가 있으면 문제가 생긴다는 원리이다.
- 자세히 보기
- 컨트롤러의 인스턴스는 힙에 생성된다.
- 요청에 대한 처리를 위한 **스레드**는 힙의 **인스턴스를 공유**하는 것이 아니라, 해당 인스턴스의 메소드 정보를 가지고 있는 Method area 에서 메소드 정보를 참조한다.
- 메소드 영역의 메소드 코드를 실행 → 메소드 코드 중 힙에 있는 객체가 있다 → 상태를 공유 → 문제가 생긴다.
흐름을 도식화 해보자.@Controller public class memberController { private final memberService; @GetMapping("/") public String getMember(){ memberService.searchMember(); } }- 요청 발생 → 스레드 풀에서 스레드 할당 → **getMember()** 메소드 실행
- 메소드에 memberService.searchMember() 실행 → 해당 블럭은 스택에서 실행되므로 공유되지 않는다.
- memberService 는 싱글턴 (호출해도 같은 객체를 리턴한다)
- 해당 **빈의 메소드를 실행(searchMember())**시킨다.
- 멤버변수 빈 (싱글턴) 들은 **안전**한가? → 안전하지 않음. **세이프티 하게 설계해야할 의무**가 존재한다.
- 스프링의 모든 요청을 하나의 컨트롤러가 모두 처리하는 게 맞을까?
톰캣 구조와 스레드를 보며 얻을 수 있는 가치
- 스프링의 빈은 **Thread-safety** 하지 않다.
→ 멀티 스레드 프로그래밍에서 공유되는 객체는 싱글턴 임에도 **Thread-safety** 하지 않다. 라는 말과 같지 않나?
- 빈을 설계할 땐, **stateless** 하게 작성하자.
- 멀티 스레딩 프로그래밍에서는 **동시성** 문제가 생기지만, 반대로 **데이터를 공유**할 수 있다는 점은 장점이다.
→ 이 부분은 뒷장에서 컨텍스트 스위칭과 프로세스 관련 개념에서 한번 더 정리한다.
논블럭킹의 장점을 보여주는 Node Js
동기 블락킹 방식의 대표인 Spring 은 싱글 프로세스 에서 멀티스레드를 사용한다.
Node Js 는 싱글 스레드 임에도 고효율의 IO 처리를 보장한다.
싱글 스레드로 동작하는 Node Js 는 어떻게 고효율의 IO 처리를 보장할까?
처리 흐름에 대한 모든 부분은 생략하고, 핵심만 애기한다.
- **Call Stack** 에서 일반 동기코드를 처리한다.
- IO 에 관련된 로직 ( addEventListener , setTimeout, Ajax ) 은 Web APIS 에서 처리한다.
- APIS 에서 처리하고 난 뒤 CallBack 들은 CallBack Queue 에 담긴다.
- Event Loop 는 Callback Queue 에 있는 콜백들을 Call Stack 에 올려서 실행한다.
- Call Stack 이 비어있는 경우에만 Event Loop 는 콜스택에 콜백 큐의 로직들을 올려 실행하는 것이 핵심.
→ Call Stack 이 비어있을 때만 → 콜 스택에서 작업을 하고 있다면, 블럭킹이 되버린다는 것!
요약
- IO 에 관한 작업들은 Web Apis 에 맡겨서 처리하기 때문에 IO 처리에 대한 고성능을 보장한다.
- Event Loop 는 싱글 스레드 이기 때문에, 콜백 큐의 내용들은 잘게 짤라서 넣어주자.
- CallStack, CallBack Queue 모두 연산의 크기가 무거워지면 좋지않다.
엔진 구조를 보고 Node Js 를 활용할 때 얻을 수 있는 가치
- 콜백 큐에 들어가는 로직 (콜백 로직 역시 너무 많은 양을 할당하지 말자)
- 코드를 작성할 때, Call Stack 에 오래걸리는 작업을 넣지말자. ( 콜백큐가 처리안될 것은 당연하기 때문)
- 콜스택에서 오래 걸리는 작업 → CPU 연산과 같은 작업들일 것이다. → 즉 V8 은 비동기 IO 에 최적화된 엔진이다.
→ 물론 최근 Worker Thread 를 이용한 CPU 연산이 가능하긴 하다.
Spring, Node Js 을 정리하며 조금 더 알아보자.
위 섹션에서 우리는 Spring, Node js 의 구조에 대해서 알아보았다.
조금 더 이론적인 부분을 더 붙여 정리해보자.
2 섹션의 마지막 정리를 위해서 프로세스, 스레드, 코어 관련 개념이 꼭 필요하다.
프로세스
- 프로그램이 메모리에 올라간 상태
- 하나의 프로세스에 **여러개의 스레드**가 포함될 수 있다.
- 프로세스 끼리 자원을 공유하지 못한다. (통신을 이용하여여 공유할 수 있다.)
- 여러 개의 프로세스 (멀티 프로세스) 를 시분할 처리할 때, Context-switching 이 일어나는데 비용이 높다.
- 프로세스는 독립적인 단위이기 때문에, 하나의 프로세스에서 생긴 문제는 다른 프로세스에서 문제가 없다.
- 구글 크롬의 탭들은 하나의 **프로세스로** 이루어져 있다.
- 프로세스를 운영하는 비용은 비싸지만, 서버의 안정성을 위해서 사용할 수 있다.
스레드
- 하나의 프로세스 안에 여러개의 스레드가 포함될 수 있다.
- 프로세스에 비해 가볍다.
- 스레드들은 자원을 **공유**할 수 있다.
- 개발자가 동기화 처리를 잘 해줘야 한다.
- 하나의 스레드가 프로세스에 영향을 준다. (안정성이 떨어진다.)
코어
- 예전에는 싱글코어를 활용했다.
- 최근에는 멀티코어를 활용한다.
- 코어의 수 만큼 프로세스를 만들어서 활용하는 방식을 Node JS 에서 사용한다. (클러스터)
Node Js 를 더 의미있게 사용하기
- 멀티 프로세스를 지원하기 때문에 규모가 큰 서비스에서는 사용하자.
- 이벤트 루프와 콜 스택에 오는 태스크의 단위를 항상 **슬라이싱** 하여 사용하자.
- **Cpu-Intensive** : Worker-Thread 를 활용, **IO-Intensive** 는 Event Loop 에게 위임하자.
- 모든 로직을 항상 비동기 방식으로 처리하도록 노력하고, 처리하라. (엔진의 특성)
Spring Tomcat 구조에서 의미있게 사용하기
- 싱글 프로세스 위의 멀티 스레드 구조이다.
- 컨텍스트 스위칭의 비용이 없고, 동시성 문제를 해결해야 한다.
- 빈은 **stateLess 하게 관리하자.**
모든 어플리케이션 관점에서의 마지막 정리
- Node JS 에서는 멀티 프로세스, 멀티 스레드를 모두 사용할 수 있다.
- CPU-intensive 는 멀티 스레딩 IO 는 멀티 프로세스 를 사용하자.
- 연산 작업이냐, IO 작업이냐에 대해서 구분하자.
- 여러개의 프로세스가 처리되어야 할 때 동일한 데이터를 사용하고, 이러한 데이터를 하나의 디스크에 두고 모든 프로세서(CPU)가 이를 공유하면 비용적으로 저렴하다.
멀티 프로세스와 멀티 스레드는 언제 활용해야 할까?
언어의 구조적 특성에 의한 활용
Node Js
- 싱글 스레드 이지만, 해당 서버의 스펙(**cpu 코어 수**)에 따라서 멀티 프로세싱 처리를 하여 서버를 구성하면 더 좋을 것이다.
- 코어의 수에 프로세스 수를 맞춰 프로세스를 할당하는 게 보편적이다.
- 코어의 수에 비례하여 사용하지 않아도 되지만, 일반적으로 위와 같이 활용하더라.
- **pm2**같은 라이브러리를 활용하여 멀티 프로세싱을 조금 더 간편하게 구현할 수 있다.
Spring
- 서블릿 컨테이너의 디폴트 인 ‘톰캣’ 이 싱글 프로세스 이기 때문에 서버를 바꾸지 않는 이상 멀티 프로세싱은 불가능 할 것이다.
- 컨테이너는 언더토, 제티, 네티와 같은 서버들을 지원하고 있기 때문에, 해당 서버와 **WebFlux** 에 대해서 알아보자.
사례로 찾아보는 멀티 프로세스와 멀티 스레드
- 우아한 형제들 실시간 배달 서비스
→ 멀티 프로세스와 워커를 활용하여 진행된 우아한 형제들의 기술블로그
시나리오
- IO 에 대한 작업이 많고, 멀티 프로세스를 Node 에서 지원하기 때문에, 프로세싱을 했다.
문제점
- 스케일 아웃한 서버와 기존의 서버에 worker 사이에 통신을 할 수 없는 문제가 생겼다.
- Redis 서버를 중간에 넣어 통신을 수행시켰다.
- 물리적인 로드 밸런싱은 문제가 없으나, 서버안의 worker 에 세션관리에 문제가 생긴다.
→ 일반적으로로 세션관리는 세션 서버, 클러스터링 하여 처리할 수 있는데 프로세싱 한 경우에 대해서는 어떤 해법들이 존재할까?
개선 구조도
- 요청이 들어온 프로세스에서 세션을 관리하기 위해서 sticky session 처리는 오픈소스 모듈을 활용하였다고 한다.
- 구글 크롬과 인터넷 익스플로러
구글크롬
- 크롬의 탭 하나는 하나의 프로세스이다.
- 탭 하나에 문제가 생겨도 다른 탭에는 문제가 생기지 않는다.
인터넷 익스플로러
- 익스플로러의 탭 하나는 하나의 스레드이다.
- 하나의 탭에 문제가 생기면 모두 종료가 되버리곤 한다.
→ 예전에 익스플로러에서 흔히 마주하던 상황
- Node 에서 CPU 연산이 필요할 때
시나리오
- Node js 에서 Cpu-Intensive 가 필요하다.
- 알고리즘을 활용한 계산 등
해결방안
- Worker Thread 를 활용하여 연산한다.
참고자료
기타 시나리오
- 기타 시나리오에 대해 알고 계신 부분이 있다면 연락해주세요 ( 010 - 9830 - 5559 )
- 작성중 ....
우리가 나아가야 할 방향
해당 섹션은 개인적인 가정과 질문을 순서로 나눠 질답을 하며 정답을 찾아볼 것이다.
동기 블럭킹이 별로 안좋은 거 같은데 기존 프로젝트들은 바꿀 수 있으면 다 바꾸면 좋을까?
→ 절대 아니다. 기본적으로 우리가 개발했던 스프링 프로젝트들의 구조를 다시 한번 살펴보자.
- 비관계형 DB를 사용할때는 async보다는 Spring Webflux 사용
- 관계형 DB에서는 Async가 의미가 없다. DB단에서는 트랜젝션 때문에 동기로 통신을 처리하기 때문이다.
- 물론, 최근 비동기 DB 접근 방법이 있다. 프로덕션 레벨에서 사용해본 경험이 없으니, 잘 생각해보아야 한다.
비동기 논블럭킹 / 동기 블럭킹 말고는 굳이 구분해야 할 필요가 있나요?
→ 있다. Polling 과 같은 개념을 이해하려면 꼭 필요하고, 이 개념을 활용하여 아키텍쳐를 설계할 때 사용할 수 있다.
풀링을 이용한 아키텍쳐를 우아한 형제들에서 사용했다.
결국 비즈니스 규모가 큰 회사들은 비동기 / 논블럭킹 을 사랑할 것이고, 내가 원하는 회사나 기술과 맞다면 투자하라.
섞인 개념들 덜어내지 못한 부분 (읽지 말 것)
Node js 와 Spring 프레임워크가 가지는 차이점과 활용방안
Node Js 관점에서의 결론과 활용방안
- 비동기 IO의 처리에 특화된 엔진의 특성을 항상 기억하라. (CPU 를 많이 사용 X)
- 루프의 구조를 이해하여 태스크를 적절히 분리 하자.
- 루프의 구조를 이해하여 항상 비동기적 로직을 작성 하자.
- 서버는 항상 Stateless 하게 설계되어야 한다.
- Node Js 에서 멀티 프로세스를 활용할 수 있어야 한다.
- 멀티 프로세스는 “데이터 공유” 가 불가능 하다.
- 스케일 아웃이 될 때, 워커들간의 데이터 공유는 어떻게 해결할까?
- 아웃 된 서버와 기존 서버안에 있는 워커들간의 데이터 공유는 어떻게 해결할까?
멀티 프로세스와 워커를 활용하여 진행된 우아한 형제들의 기술블로그
→ 이벤트 정보를 공유하는 것과 유지되어야 할 로그인 정보는 격리되어야 한다.
Spring 관점에서의 결론과 활용방안
- 스프링의 기본 Servlet Container는 Process 하나에 배정된다. (즉 싱글프로세스)
- 싱글 프로세스 에서 멀티스레딩을 이용하여 동작한다.
- Spring 에서 빈 역시 StateLess 하게 설계되어야 한다.
- 빈을 공유해서 사용한다는 의미는 “오브젝트 직접참조” 가 아니다.
(빈 클래스의 정보를 가진 Method Area 에서 객체의 Binary Code 정보를 공유)
- 상태가 필요할 때, 다양한 방식으로 해결할 수 있고., 방어적인 프로그래밍으로 작성할 수 있다.
- (ThreadLocal, Bean scopes = Prototype, Stack Programming 등)
- 스케일 아웃이 되었을 때, 단일 서버안에서 데이터를 자유롭게 교환할 수 있는 이점이 있다.
- 스케일 아웃된 서버 전체에서 값을 참조하여 변경해야 할 경우, 특별한 처리가 더 필요할 것.
토큰 값을 공유하는 형태와 세션을 공유하는 형태에 대한 차이점
추가
→ V8 엔진과 노드의 엔진에 대한 자세한 매커니즘은 해당 포스트를 참고하세요.
→ 비동기 처리에 대한 개념은 해당 포스트를 참고하세요.
→ Worker Thread : CPU-Intensive 에 적합하다. IO-Intensive 는 loop 가 처리하는 게 좋다.
워커 스레드 관련 포스팅은 여기
해결해야 될 의문
- Redis 서버를 중간에 두어 활용한 사례말고, Kafka 를 이용하여 처리할 순 없을까?
- Worker 들 간의
IO 참고
로드밸런싱, 세션, 클러스터링, 캐시서버에 대한 개념
Sticky Session과 Session Clustering
스프링의 서블릿 컨테이너와 동작, 그리고 동기 블락킹 흐름
ServletContainer와 SpringContainer는 무엇이 다른가?
자바스크립트와 비동기
JavaScript 비동기 핵심 Event Loop 정리
NodeJS의 Non-Blocking IO와 멀티코어 프로세싱
NodeJS의 Non-Blocking IO와 멀티코어 프로세싱
멀티 프로세스와 멀티 스레딩의 장단점
멀티 프로세스(Multi Process)와 멀티 스레드(Multi Thread)
멀티 프로세싱 개념과 활용
PM2를 활용한 Node.js 무중단 서비스하기 - LINE ENGINEERING
스프링
스프링 구조
ServletContainer와 SpringContainer는 무엇이 다른가?
스프링의 비동기 처리의 유효성
일반적으로 async말고 일반적인 스프링 MVC패턴을 추천한다.
async를 써도 문제는 없지만, 사실 여러개의 API를 동시에 전송해서 그 결과를 JOIN할때 일반적인 메소드 내에서
CompletableFuture를 여러개 만든다음 전송시키고 합쳐서 응답을 내려주는게 제일 좋은 방안이다.
비관계형 DB를 사용할때는 async보다는 Spring Webflux 사용을 권장하며,
관계형 DB에서는 Async가 의미가 없다. DB단에서는 트랜젝션 때문에 동기로 통신을 처리하기 때문이다.
[Spring, Android] 스프링 안드로이드 JWT 활용한 WebView 모바일 유저 인증처리 (#업무)
IO 와 언어에 대한 이해와 활용
불필요하다고 생각했던 이유
- 프로그램의 동작 방식을 활용하는 방법에 대해서 몰랐다.
- 성능 최적화는 비즈니스 규모에 따라서 필요할 거라고 생각했다.
→ 유효하지 않은 비즈니스에서도 충분히 경험할 수 있고, 학습할 수 있다
계기
- 기술 블로그에서 해당 개념들을 어떻게 활용한 지에 대해서 기술되어 있다.
- 코어 부분에 대해서 알지 못해, 프레임워크나 라이브러리를 적당히 조합할 수 있는 능력이 떨어짐.
→ 백지부터 작성할 수 있는 주관이 없다.
다룰 개념
- IO 란
- Spring 과 Node 로 보는 동기, 블럭 / 비동기, 논블럭
- Node js 와 Spring 프레임워크가 가지는 차이점과 활용방안
- 멀티 프로세스와 멀티 스레드는 언제 활용해야 할까?
- 우리가 나아가야 할 방향
- 최종 정리
IO 란?
- 웹 어플리케이션의 구조 상 IO 작업의 비용이 가장 많이 차지한다.
- 병목현상의 주 원인은 IO 이며, IO 의 성능개선에 주목해야 한다.
- 웹 에서는 Network, Disk 관련 IO 가 높은 비중을 가지고 있다. (해당 작업들이 어플리케이션의 전부)
- 해당 파트에서는 IO 관점에 초점을 맞춘다.
→ 우리가 개발하는 어플리케이션은 항상 IO 와 밀접한 관련이 있고, 이런 IO 에 대한 처리에 대한 문제점으로 다양한 프로그래밍 방식이 발전해왔다는 것을 알아야한다.
Spring 과 Node 로 보는 동기, 블럭 / 비동기, 논블럭
→ 웹 어플리케이션은 접근성이 높고, 많은 유저의 요청에 이루어진 작업을 주로 수행한다.
→ IO 관련 작업이 많은 분야이기 때문에, 해당 섹션을 IO 의 관점으로 분류해서 보려고 한다.
Spring Servlet Container(Tomcat) 의 동기 / 블럭킹
- 스프링에서는 하나의 요청 (요청 당) 당 하나의 스레드를 할당한다.
- 동기 방식의 성능상 문제점을 해결하기 위해서 Multi-Thread 개념이 생겼다.
- 스프링의 WAS 에서는 서블릿 컨테이너 (톰캣) 의 스레드 수가 정해져있다.
- 해당 스레드 수를 넘는 유저의 요청이 발생하면 어떻게 대처해야할까?
동기 방식의 문제점과 해결방안
- 해당 스레드에서 처리할 수 있는 양의 범위를 넘어선다면?
→ 물리적인 서버를 업그레이드 시킨다. (스프링의 관점에서는 서블릿 컨테이너 (톰캣))
→ 로드 밸런싱을 통한 서버의 트래픽을 나눠서 처리한다.
- 로드 밸런싱이란?
- 클라이언트로부터 오는 요청을 여러 서버로 분배 해주는 것
- 리퀘스트를 동일하게 분배하기 때문에, 세션관리에서 문제가 생길 수 있다.
- 개념에 대해 정확히 알지 못한다면, 해당 포스트를 참고하라.

- 이대로 정말 괜찮을까?
→ Thread 들은 서로 데이터를 공유할 수 있기 때문에, 동기화를 관리해줘야 한다. (개발자의 능력)
→ 물리적인 서버의 비용은 트래픽에 비례해서 상승할 것이다.
→ 물리적인 단순한 로드밸런싱은 스케일 아웃의 관점에서 봤을 때 상태를 유지할 수 없다.
→ 상태를 유지하기 위해 Sticky Session, Session Clustering, Session Server 유지
→ 블록킹 언어의 근본적인 문제는 비즈니스가 커지면 커질수록 좋지 못할 것이다.
→ 결론 : 언어적 특성을 해결하기 위해 밸런싱, 세션서버를 독립적으로 관리해야 할 것이다.
→ 물론, 비동기 언어를 사용한다고 모든 문제를 해결 할 수는 없을 것이다.
추가
→ 스프링은 싱글 프로세스에서 멀티 스레드를 이용하여 처리한다.
→ 스프링의 프로세스와 스레드와 흐름에 대한 기본적인 개념은 해당 포스트에서 공부하도록 하자.
Thread 와 빈의 연관관계에 대해서
- 하나의 요청 → 하나의 스레드 → 빈 (Controller, Service ...) 을 공유하여 사용하는 환경
- 빈은 JVM 의 Heap 영역에 할당되는데, 하나의 싱글턴 인스턴스를 모두 공유하여 처리하지 않는다.
- 기본적인 구조에서 Controller 의 Mppaing 메소드를 참조하는 순서로 간단하게 전개해보자면
- 컨트롤러의 메소드를 스택이 할당 → 메소드 로직을 실행하는데, 여기서 힙에 공유된 객체들이 상태가 있으면 문제가 생긴다는 원리이다.
- 자세히 보기
- 컨트롤러의 인스턴스는 힙에 생성된다.
- 요청에 대한 처리를 위한 **스레드**는 힙의 **인스턴스를 공유**하는 것이 아니라, 해당 인스턴스의 메소드 정보를 가지고 있는 Method area 에서 메소드 정보를 참조한다.
- 메소드 영역의 메소드 코드를 실행 → 메소드 코드 중 힙에 있는 객체가 있다 → 상태를 공유 → 문제가 생긴다.
흐름을 도식화 해보자.@Controller public class memberController { private final memberService; @GetMapping("/") public String getMember(){ memberService.searchMember(); } }- 요청 발생 → 스레드 풀에서 스레드 할당 → **getMember()** 메소드 실행
- 메소드에 memberService.searchMember() 실행 → 해당 블럭은 스택에서 실행되므로 공유되지 않는다.
- memberService 는 싱글턴 (호출해도 같은 객체를 리턴한다)
- 해당 **빈의 메소드를 실행(searchMember())**시킨다.
- 멤버변수 빈 (싱글턴) 들은 **안전**한가? → 안전하지 않음. **세이프티 하게 설계해야할 의무**가 존재한다.
- 스프링의 모든 요청을 하나의 컨트롤러가 모두 처리하는 게 맞을까?
톰캣 구조와 스레드를 보며 얻을 수 있는 가치
- 스프링의 빈은 **Thread-safety** 하지 않다.
→ 멀티 스레드 프로그래밍에서 공유되는 객체는 싱글턴 임에도 **Thread-safety** 하지 않다. 라는 말과 같지 않나?
- 빈을 설계할 땐, **stateless** 하게 작성하자.
- 멀티 스레딩 프로그래밍에서는 **동시성** 문제가 생기지만, 반대로 **데이터를 공유**할 수 있다는 점은 장점이다.
→ 이 부분은 뒷장에서 컨텍스트 스위칭과 프로세스 관련 개념에서 한번 더 정리한다.
논블럭킹의 장점을 보여주는 Node Js
동기 블락킹 방식의 대표인 Spring 은 싱글 프로세스 에서 멀티스레드를 사용한다.
Node Js 는 싱글 스레드 임에도 고효율의 IO 처리를 보장한다.
싱글 스레드로 동작하는 Node Js 는 어떻게 고효율의 IO 처리를 보장할까?
처리 흐름에 대한 모든 부분은 생략하고, 핵심만 애기한다.
- **Call Stack** 에서 일반 동기코드를 처리한다.
- IO 에 관련된 로직 ( addEventListener , setTimeout, Ajax ) 은 Web APIS 에서 처리한다.
- APIS 에서 처리하고 난 뒤 CallBack 들은 CallBack Queue 에 담긴다.
- Event Loop 는 Callback Queue 에 있는 콜백들을 Call Stack 에 올려서 실행한다.
- Call Stack 이 비어있는 경우에만 Event Loop 는 콜스택에 콜백 큐의 로직들을 올려 실행하는 것이 핵심.
→ Call Stack 이 비어있을 때만 → 콜 스택에서 작업을 하고 있다면, 블럭킹이 되버린다는 것!
요약
- IO 에 관한 작업들은 Web Apis 에 맡겨서 처리하기 때문에 IO 처리에 대한 고성능을 보장한다.
- Event Loop 는 싱글 스레드 이기 때문에, 콜백 큐의 내용들은 잘게 짤라서 넣어주자.
- CallStack, CallBack Queue 모두 연산의 크기가 무거워지면 좋지않다.
엔진 구조를 보고 Node Js 를 활용할 때 얻을 수 있는 가치
- 콜백 큐에 들어가는 로직 (콜백 로직 역시 너무 많은 양을 할당하지 말자)
- 코드를 작성할 때, Call Stack 에 오래걸리는 작업을 넣지말자. ( 콜백큐가 처리안될 것은 당연하기 때문)
- 콜스택에서 오래 걸리는 작업 → CPU 연산과 같은 작업들일 것이다. → 즉 V8 은 비동기 IO 에 최적화된 엔진이다.
→ 물론 최근 Worker Thread 를 이용한 CPU 연산이 가능하긴 하다.
Spring, Node Js 을 정리하며 조금 더 알아보자.
위 섹션에서 우리는 Spring, Node js 의 구조에 대해서 알아보았다.
조금 더 이론적인 부분을 더 붙여 정리해보자.
2 섹션의 마지막 정리를 위해서 프로세스, 스레드, 코어 관련 개념이 꼭 필요하다.
프로세스
- 프로그램이 메모리에 올라간 상태
- 하나의 프로세스에 **여러개의 스레드**가 포함될 수 있다.
- 프로세스 끼리 자원을 공유하지 못한다. (통신을 이용하여여 공유할 수 있다.)
- 여러 개의 프로세스 (멀티 프로세스) 를 시분할 처리할 때, Context-switching 이 일어나는데 비용이 높다.
- 프로세스는 독립적인 단위이기 때문에, 하나의 프로세스에서 생긴 문제는 다른 프로세스에서 문제가 없다.
- 구글 크롬의 탭들은 하나의 **프로세스로** 이루어져 있다.
- 프로세스를 운영하는 비용은 비싸지만, 서버의 안정성을 위해서 사용할 수 있다.
스레드
- 하나의 프로세스 안에 여러개의 스레드가 포함될 수 있다.
- 프로세스에 비해 가볍다.
- 스레드들은 자원을 **공유**할 수 있다.
- 개발자가 동기화 처리를 잘 해줘야 한다.
- 하나의 스레드가 프로세스에 영향을 준다. (안정성이 떨어진다.)
코어
- 예전에는 싱글코어를 활용했다.
- 최근에는 멀티코어를 활용한다.
- 코어의 수 만큼 프로세스를 만들어서 활용하는 방식을 Node JS 에서 사용한다. (클러스터)
Node Js 를 더 의미있게 사용하기
- 멀티 프로세스를 지원하기 때문에 규모가 큰 서비스에서는 사용하자.
- 이벤트 루프와 콜 스택에 오는 태스크의 단위를 항상 **슬라이싱** 하여 사용하자.
- **Cpu-Intensive** : Worker-Thread 를 활용, **IO-Intensive** 는 Event Loop 에게 위임하자.
- 모든 로직을 항상 비동기 방식으로 처리하도록 노력하고, 처리하라. (엔진의 특성)
Spring Tomcat 구조에서 의미있게 사용하기
- 싱글 프로세스 위의 멀티 스레드 구조이다.
- 컨텍스트 스위칭의 비용이 없고, 동시성 문제를 해결해야 한다.
- 빈은 **stateLess 하게 관리하자.**
모든 어플리케이션 관점에서의 마지막 정리
- Node JS 에서는 멀티 프로세스, 멀티 스레드를 모두 사용할 수 있다.
- CPU-intensive 는 멀티 스레딩 IO 는 멀티 프로세스 를 사용하자.
- 연산 작업이냐, IO 작업이냐에 대해서 구분하자.
- 여러개의 프로세스가 처리되어야 할 때 동일한 데이터를 사용하고, 이러한 데이터를 하나의 디스크에 두고 모든 프로세서(CPU)가 이를 공유하면 비용적으로 저렴하다.
멀티 프로세스와 멀티 스레드는 언제 활용해야 할까?
언어의 구조적 특성에 의한 활용
Node Js
- 싱글 스레드 이지만, 해당 서버의 스펙(**cpu 코어 수**)에 따라서 멀티 프로세싱 처리를 하여 서버를 구성하면 더 좋을 것이다.
- 코어의 수에 프로세스 수를 맞춰 프로세스를 할당하는 게 보편적이다.
- 코어의 수에 비례하여 사용하지 않아도 되지만, 일반적으로 위와 같이 활용하더라.
- **pm2**같은 라이브러리를 활용하여 멀티 프로세싱을 조금 더 간편하게 구현할 수 있다.
Spring
- 서블릿 컨테이너의 디폴트 인 ‘톰캣’ 이 싱글 프로세스 이기 때문에 서버를 바꾸지 않는 이상 멀티 프로세싱은 불가능 할 것이다.
- 컨테이너는 언더토, 제티, 네티와 같은 서버들을 지원하고 있기 때문에, 해당 서버와 **WebFlux** 에 대해서 알아보자.
사례로 찾아보는 멀티 프로세스와 멀티 스레드
- 우아한 형제들 실시간 배달 서비스
→ 멀티 프로세스와 워커를 활용하여 진행된 우아한 형제들의 기술블로그
시나리오
- IO 에 대한 작업이 많고, 멀티 프로세스를 Node 에서 지원하기 때문에, 프로세싱을 했다.
문제점
- 스케일 아웃한 서버와 기존의 서버에 worker 사이에 통신을 할 수 없는 문제가 생겼다.
- Redis 서버를 중간에 넣어 통신을 수행시켰다.
- 물리적인 로드 밸런싱은 문제가 없으나, 서버안의 worker 에 세션관리에 문제가 생긴다.
→ 일반적으로로 세션관리는 세션 서버, 클러스터링 하여 처리할 수 있는데 프로세싱 한 경우에 대해서는 어떤 해법들이 존재할까?
개선 구조도
- 요청이 들어온 프로세스에서 세션을 관리하기 위해서 sticky session 처리는 오픈소스 모듈을 활용하였다고 한다.
- 구글 크롬과 인터넷 익스플로러
구글크롬
- 크롬의 탭 하나는 하나의 프로세스이다.
- 탭 하나에 문제가 생겨도 다른 탭에는 문제가 생기지 않는다.
인터넷 익스플로러
- 익스플로러의 탭 하나는 하나의 스레드이다.
- 하나의 탭에 문제가 생기면 모두 종료가 되버리곤 한다.
→ 예전에 익스플로러에서 흔히 마주하던 상황
- Node 에서 CPU 연산이 필요할 때
시나리오
- Node js 에서 Cpu-Intensive 가 필요하다.
- 알고리즘을 활용한 계산 등
해결방안
- Worker Thread 를 활용하여 연산한다.
참고자료
기타 시나리오
- 기타 시나리오에 대해 알고 계신 부분이 있다면 연락해주세요 ( 010 - 9830 - 5559 )
- 작성중 ....
우리가 나아가야 할 방향
해당 섹션은 개인적인 가정과 질문을 순서로 나눠 질답을 하며 정답을 찾아볼 것이다.
동기 블럭킹이 별로 안좋은 거 같은데 기존 프로젝트들은 바꿀 수 있으면 다 바꾸면 좋을까?
→ 절대 아니다. 기본적으로 우리가 개발했던 스프링 프로젝트들의 구조를 다시 한번 살펴보자.
- 비관계형 DB를 사용할때는 async보다는 Spring Webflux 사용
- 관계형 DB에서는 Async가 의미가 없다. DB단에서는 트랜젝션 때문에 동기로 통신을 처리하기 때문이다.
- 물론, 최근 비동기 DB 접근 방법이 있다. 프로덕션 레벨에서 사용해본 경험이 없으니, 잘 생각해보아야 한다.
비동기 논블럭킹 / 동기 블럭킹 말고는 굳이 구분해야 할 필요가 있나요?
→ 있다. Polling 과 같은 개념을 이해하려면 꼭 필요하고, 이 개념을 활용하여 아키텍쳐를 설계할 때 사용할 수 있다.
풀링을 이용한 아키텍쳐를 우아한 형제들에서 사용했다.
결국 비즈니스 규모가 큰 회사들은 비동기 / 논블럭킹 을 사랑할 것이고, 내가 원하는 회사나 기술과 맞다면 투자하라.
섞인 개념들 덜어내지 못한 부분 (읽지 말 것)
Node js 와 Spring 프레임워크가 가지는 차이점과 활용방안
Node Js 관점에서의 결론과 활용방안
- 비동기 IO의 처리에 특화된 엔진의 특성을 항상 기억하라. (CPU 를 많이 사용 X)
- 루프의 구조를 이해하여 태스크를 적절히 분리 하자.
- 루프의 구조를 이해하여 항상 비동기적 로직을 작성 하자.
- 서버는 항상 Stateless 하게 설계되어야 한다.
- Node Js 에서 멀티 프로세스를 활용할 수 있어야 한다.
- 멀티 프로세스는 “데이터 공유” 가 불가능 하다.
- 스케일 아웃이 될 때, 워커들간의 데이터 공유는 어떻게 해결할까?
- 아웃 된 서버와 기존 서버안에 있는 워커들간의 데이터 공유는 어떻게 해결할까?
멀티 프로세스와 워커를 활용하여 진행된 우아한 형제들의 기술블로그
→ 이벤트 정보를 공유하는 것과 유지되어야 할 로그인 정보는 격리되어야 한다.
Spring 관점에서의 결론과 활용방안
- 스프링의 기본 Servlet Container는 Process 하나에 배정된다. (즉 싱글프로세스)
- 싱글 프로세스 에서 멀티스레딩을 이용하여 동작한다.
- Spring 에서 빈 역시 StateLess 하게 설계되어야 한다.
- 빈을 공유해서 사용한다는 의미는 “오브젝트 직접참조” 가 아니다.
(빈 클래스의 정보를 가진 Method Area 에서 객체의 Binary Code 정보를 공유)
- 상태가 필요할 때, 다양한 방식으로 해결할 수 있고., 방어적인 프로그래밍으로 작성할 수 있다.
- (ThreadLocal, Bean scopes = Prototype, Stack Programming 등)
- 스케일 아웃이 되었을 때, 단일 서버안에서 데이터를 자유롭게 교환할 수 있는 이점이 있다.
- 스케일 아웃된 서버 전체에서 값을 참조하여 변경해야 할 경우, 특별한 처리가 더 필요할 것.
토큰 값을 공유하는 형태와 세션을 공유하는 형태에 대한 차이점
추가
→ V8 엔진과 노드의 엔진에 대한 자세한 매커니즘은 해당 포스트를 참고하세요.
→ 비동기 처리에 대한 개념은 해당 포스트를 참고하세요.
→ Worker Thread : CPU-Intensive 에 적합하다. IO-Intensive 는 loop 가 처리하는 게 좋다.
워커 스레드 관련 포스팅은 여기
해결해야 될 의문
- Redis 서버를 중간에 두어 활용한 사례말고, Kafka 를 이용하여 처리할 순 없을까?
- Worker 들 간의
IO 참고
로드밸런싱, 세션, 클러스터링, 캐시서버에 대한 개념
Sticky Session과 Session Clustering
스프링의 서블릿 컨테이너와 동작, 그리고 동기 블락킹 흐름
ServletContainer와 SpringContainer는 무엇이 다른가?
자바스크립트와 비동기
JavaScript 비동기 핵심 Event Loop 정리
NodeJS의 Non-Blocking IO와 멀티코어 프로세싱
NodeJS의 Non-Blocking IO와 멀티코어 프로세싱
멀티 프로세스와 멀티 스레딩의 장단점
멀티 프로세스(Multi Process)와 멀티 스레드(Multi Thread)
멀티 프로세싱 개념과 활용
PM2를 활용한 Node.js 무중단 서비스하기 - LINE ENGINEERING
스프링
스프링 구조
ServletContainer와 SpringContainer는 무엇이 다른가?
스프링의 비동기 처리의 유효성
일반적으로 async말고 일반적인 스프링 MVC패턴을 추천한다.
async를 써도 문제는 없지만, 사실 여러개의 API를 동시에 전송해서 그 결과를 JOIN할때 일반적인 메소드 내에서
CompletableFuture를 여러개 만든다음 전송시키고 합쳐서 응답을 내려주는게 제일 좋은 방안이다.
비관계형 DB를 사용할때는 async보다는 Spring Webflux 사용을 권장하며,
관계형 DB에서는 Async가 의미가 없다. DB단에서는 트랜젝션 때문에 동기로 통신을 처리하기 때문이다.
[Spring, Android] 스프링 안드로이드 JWT 활용한 WebView 모바일 유저 인증처리 (#업무)
IO 와 언어에 대한 이해와 활용
불필요하다고 생각했던 이유
- 프로그램의 동작 방식을 활용하는 방법에 대해서 몰랐다.
- 성능 최적화는 비즈니스 규모에 따라서 필요할 거라고 생각했다.
→ 유효하지 않은 비즈니스에서도 충분히 경험할 수 있고, 학습할 수 있다
계기
- 기술 블로그에서 해당 개념들을 어떻게 활용한 지에 대해서 기술되어 있다.
- 코어 부분에 대해서 알지 못해, 프레임워크나 라이브러리를 적당히 조합할 수 있는 능력이 떨어짐.
→ 백지부터 작성할 수 있는 주관이 없다.
다룰 개념
- IO 란
- Spring 과 Node 로 보는 동기, 블럭 / 비동기, 논블럭
- Node js 와 Spring 프레임워크가 가지는 차이점과 활용방안
- 멀티 프로세스와 멀티 스레드는 언제 활용해야 할까?
- 우리가 나아가야 할 방향
- 최종 정리
IO 란?
- 웹 어플리케이션의 구조 상 IO 작업의 비용이 가장 많이 차지한다.
- 병목현상의 주 원인은 IO 이며, IO 의 성능개선에 주목해야 한다.
- 웹 에서는 Network, Disk 관련 IO 가 높은 비중을 가지고 있다. (해당 작업들이 어플리케이션의 전부)
- 해당 파트에서는 IO 관점에 초점을 맞춘다.
→ 우리가 개발하는 어플리케이션은 항상 IO 와 밀접한 관련이 있고, 이런 IO 에 대한 처리에 대한 문제점으로 다양한 프로그래밍 방식이 발전해왔다는 것을 알아야한다.
Spring 과 Node 로 보는 동기, 블럭 / 비동기, 논블럭
→ 웹 어플리케이션은 접근성이 높고, 많은 유저의 요청에 이루어진 작업을 주로 수행한다.
→ IO 관련 작업이 많은 분야이기 때문에, 해당 섹션을 IO 의 관점으로 분류해서 보려고 한다.
Spring Servlet Container(Tomcat) 의 동기 / 블럭킹
- 스프링에서는 하나의 요청 (요청 당) 당 하나의 스레드를 할당한다.
- 동기 방식의 성능상 문제점을 해결하기 위해서 Multi-Thread 개념이 생겼다.
- 스프링의 WAS 에서는 서블릿 컨테이너 (톰캣) 의 스레드 수가 정해져있다.
- 해당 스레드 수를 넘는 유저의 요청이 발생하면 어떻게 대처해야할까?
동기 방식의 문제점과 해결방안
- 해당 스레드에서 처리할 수 있는 양의 범위를 넘어선다면?
→ 물리적인 서버를 업그레이드 시킨다. (스프링의 관점에서는 서블릿 컨테이너 (톰캣))
→ 로드 밸런싱을 통한 서버의 트래픽을 나눠서 처리한다.
- 로드 밸런싱이란?
- 클라이언트로부터 오는 요청을 여러 서버로 분배 해주는 것
- 리퀘스트를 동일하게 분배하기 때문에, 세션관리에서 문제가 생길 수 있다.
- 개념에 대해 정확히 알지 못한다면, 해당 포스트를 참고하라.

- 이대로 정말 괜찮을까?
→ Thread 들은 서로 데이터를 공유할 수 있기 때문에, 동기화를 관리해줘야 한다. (개발자의 능력)
→ 물리적인 서버의 비용은 트래픽에 비례해서 상승할 것이다.
→ 물리적인 단순한 로드밸런싱은 스케일 아웃의 관점에서 봤을 때 상태를 유지할 수 없다.
→ 상태를 유지하기 위해 Sticky Session, Session Clustering, Session Server 유지
→ 블록킹 언어의 근본적인 문제는 비즈니스가 커지면 커질수록 좋지 못할 것이다.
→ 결론 : 언어적 특성을 해결하기 위해 밸런싱, 세션서버를 독립적으로 관리해야 할 것이다.
→ 물론, 비동기 언어를 사용한다고 모든 문제를 해결 할 수는 없을 것이다.
추가
→ 스프링은 싱글 프로세스에서 멀티 스레드를 이용하여 처리한다.
→ 스프링의 프로세스와 스레드와 흐름에 대한 기본적인 개념은 해당 포스트에서 공부하도록 하자.
Thread 와 빈의 연관관계에 대해서
- 하나의 요청 → 하나의 스레드 → 빈 (Controller, Service ...) 을 공유하여 사용하는 환경
- 빈은 JVM 의 Heap 영역에 할당되는데, 하나의 싱글턴 인스턴스를 모두 공유하여 처리하지 않는다.
- 기본적인 구조에서 Controller 의 Mppaing 메소드를 참조하는 순서로 간단하게 전개해보자면
- 컨트롤러의 메소드를 스택이 할당 → 메소드 로직을 실행하는데, 여기서 힙에 공유된 객체들이 상태가 있으면 문제가 생긴다는 원리이다.
- 자세히 보기
- 컨트롤러의 인스턴스는 힙에 생성된다.
- 요청에 대한 처리를 위한 **스레드**는 힙의 **인스턴스를 공유**하는 것이 아니라, 해당 인스턴스의 메소드 정보를 가지고 있는 Method area 에서 메소드 정보를 참조한다.
- 메소드 영역의 메소드 코드를 실행 → 메소드 코드 중 힙에 있는 객체가 있다 → 상태를 공유 → 문제가 생긴다.
흐름을 도식화 해보자.@Controller public class memberController { private final memberService; @GetMapping("/") public String getMember(){ memberService.searchMember(); } }- 요청 발생 → 스레드 풀에서 스레드 할당 → **getMember()** 메소드 실행
- 메소드에 memberService.searchMember() 실행 → 해당 블럭은 스택에서 실행되므로 공유되지 않는다.
- memberService 는 싱글턴 (호출해도 같은 객체를 리턴한다)
- 해당 **빈의 메소드를 실행(searchMember())**시킨다.
- 멤버변수 빈 (싱글턴) 들은 **안전**한가? → 안전하지 않음. **세이프티 하게 설계해야할 의무**가 존재한다.
- 스프링의 모든 요청을 하나의 컨트롤러가 모두 처리하는 게 맞을까?
톰캣 구조와 스레드를 보며 얻을 수 있는 가치
- 스프링의 빈은 **Thread-safety** 하지 않다.
→ 멀티 스레드 프로그래밍에서 공유되는 객체는 싱글턴 임에도 **Thread-safety** 하지 않다. 라는 말과 같지 않나?
- 빈을 설계할 땐, **stateless** 하게 작성하자.
- 멀티 스레딩 프로그래밍에서는 **동시성** 문제가 생기지만, 반대로 **데이터를 공유**할 수 있다는 점은 장점이다.
→ 이 부분은 뒷장에서 컨텍스트 스위칭과 프로세스 관련 개념에서 한번 더 정리한다.
논블럭킹의 장점을 보여주는 Node Js
동기 블락킹 방식의 대표인 Spring 은 싱글 프로세스 에서 멀티스레드를 사용한다.
Node Js 는 싱글 스레드 임에도 고효율의 IO 처리를 보장한다.
싱글 스레드로 동작하는 Node Js 는 어떻게 고효율의 IO 처리를 보장할까?
처리 흐름에 대한 모든 부분은 생략하고, 핵심만 애기한다.
- **Call Stack** 에서 일반 동기코드를 처리한다.
- IO 에 관련된 로직 ( addEventListener , setTimeout, Ajax ) 은 Web APIS 에서 처리한다.
- APIS 에서 처리하고 난 뒤 CallBack 들은 CallBack Queue 에 담긴다.
- Event Loop 는 Callback Queue 에 있는 콜백들을 Call Stack 에 올려서 실행한다.
- Call Stack 이 비어있는 경우에만 Event Loop 는 콜스택에 콜백 큐의 로직들을 올려 실행하는 것이 핵심.
→ Call Stack 이 비어있을 때만 → 콜 스택에서 작업을 하고 있다면, 블럭킹이 되버린다는 것!
요약
- IO 에 관한 작업들은 Web Apis 에 맡겨서 처리하기 때문에 IO 처리에 대한 고성능을 보장한다.
- Event Loop 는 싱글 스레드 이기 때문에, 콜백 큐의 내용들은 잘게 짤라서 넣어주자.
- CallStack, CallBack Queue 모두 연산의 크기가 무거워지면 좋지않다.
엔진 구조를 보고 Node Js 를 활용할 때 얻을 수 있는 가치
- 콜백 큐에 들어가는 로직 (콜백 로직 역시 너무 많은 양을 할당하지 말자)
- 코드를 작성할 때, Call Stack 에 오래걸리는 작업을 넣지말자. ( 콜백큐가 처리안될 것은 당연하기 때문)
- 콜스택에서 오래 걸리는 작업 → CPU 연산과 같은 작업들일 것이다. → 즉 V8 은 비동기 IO 에 최적화된 엔진이다.
→ 물론 최근 Worker Thread 를 이용한 CPU 연산이 가능하긴 하다.
Spring, Node Js 을 정리하며 조금 더 알아보자.
위 섹션에서 우리는 Spring, Node js 의 구조에 대해서 알아보았다.
조금 더 이론적인 부분을 더 붙여 정리해보자.
2 섹션의 마지막 정리를 위해서 프로세스, 스레드, 코어 관련 개념이 꼭 필요하다.
프로세스
- 프로그램이 메모리에 올라간 상태
- 하나의 프로세스에 **여러개의 스레드**가 포함될 수 있다.
- 프로세스 끼리 자원을 공유하지 못한다. (통신을 이용하여여 공유할 수 있다.)
- 여러 개의 프로세스 (멀티 프로세스) 를 시분할 처리할 때, Context-switching 이 일어나는데 비용이 높다.
- 프로세스는 독립적인 단위이기 때문에, 하나의 프로세스에서 생긴 문제는 다른 프로세스에서 문제가 없다.
- 구글 크롬의 탭들은 하나의 **프로세스로** 이루어져 있다.
- 프로세스를 운영하는 비용은 비싸지만, 서버의 안정성을 위해서 사용할 수 있다.
스레드
- 하나의 프로세스 안에 여러개의 스레드가 포함될 수 있다.
- 프로세스에 비해 가볍다.
- 스레드들은 자원을 **공유**할 수 있다.
- 개발자가 동기화 처리를 잘 해줘야 한다.
- 하나의 스레드가 프로세스에 영향을 준다. (안정성이 떨어진다.)
코어
- 예전에는 싱글코어를 활용했다.
- 최근에는 멀티코어를 활용한다.
- 코어의 수 만큼 프로세스를 만들어서 활용하는 방식을 Node JS 에서 사용한다. (클러스터)
Node Js 를 더 의미있게 사용하기
- 멀티 프로세스를 지원하기 때문에 규모가 큰 서비스에서는 사용하자.
- 이벤트 루프와 콜 스택에 오는 태스크의 단위를 항상 **슬라이싱** 하여 사용하자.
- **Cpu-Intensive** : Worker-Thread 를 활용, **IO-Intensive** 는 Event Loop 에게 위임하자.
- 모든 로직을 항상 비동기 방식으로 처리하도록 노력하고, 처리하라. (엔진의 특성)
Spring Tomcat 구조에서 의미있게 사용하기
- 싱글 프로세스 위의 멀티 스레드 구조이다.
- 컨텍스트 스위칭의 비용이 없고, 동시성 문제를 해결해야 한다.
- 빈은 **stateLess 하게 관리하자.**
모든 어플리케이션 관점에서의 마지막 정리
- Node JS 에서는 멀티 프로세스, 멀티 스레드를 모두 사용할 수 있다.
- CPU-intensive 는 멀티 스레딩 IO 는 멀티 프로세스 를 사용하자.
- 연산 작업이냐, IO 작업이냐에 대해서 구분하자.
- 여러개의 프로세스가 처리되어야 할 때 동일한 데이터를 사용하고, 이러한 데이터를 하나의 디스크에 두고 모든 프로세서(CPU)가 이를 공유하면 비용적으로 저렴하다.
멀티 프로세스와 멀티 스레드는 언제 활용해야 할까?
언어의 구조적 특성에 의한 활용
Node Js
- 싱글 스레드 이지만, 해당 서버의 스펙(**cpu 코어 수**)에 따라서 멀티 프로세싱 처리를 하여 서버를 구성하면 더 좋을 것이다.
- 코어의 수에 프로세스 수를 맞춰 프로세스를 할당하는 게 보편적이다.
- 코어의 수에 비례하여 사용하지 않아도 되지만, 일반적으로 위와 같이 활용하더라.
- **pm2**같은 라이브러리를 활용하여 멀티 프로세싱을 조금 더 간편하게 구현할 수 있다.
Spring
- 서블릿 컨테이너의 디폴트 인 ‘톰캣’ 이 싱글 프로세스 이기 때문에 서버를 바꾸지 않는 이상 멀티 프로세싱은 불가능 할 것이다.
- 컨테이너는 언더토, 제티, 네티와 같은 서버들을 지원하고 있기 때문에, 해당 서버와 **WebFlux** 에 대해서 알아보자.
사례로 찾아보는 멀티 프로세스와 멀티 스레드
- 우아한 형제들 실시간 배달 서비스
→ 멀티 프로세스와 워커를 활용하여 진행된 우아한 형제들의 기술블로그
시나리오
- IO 에 대한 작업이 많고, 멀티 프로세스를 Node 에서 지원하기 때문에, 프로세싱을 했다.
문제점
- 스케일 아웃한 서버와 기존의 서버에 worker 사이에 통신을 할 수 없는 문제가 생겼다.
- Redis 서버를 중간에 넣어 통신을 수행시켰다.
- 물리적인 로드 밸런싱은 문제가 없으나, 서버안의 worker 에 세션관리에 문제가 생긴다.
→ 일반적으로로 세션관리는 세션 서버, 클러스터링 하여 처리할 수 있는데 프로세싱 한 경우에 대해서는 어떤 해법들이 존재할까?
개선 구조도
- 요청이 들어온 프로세스에서 세션을 관리하기 위해서 sticky session 처리는 오픈소스 모듈을 활용하였다고 한다.
- 구글 크롬과 인터넷 익스플로러
구글크롬
- 크롬의 탭 하나는 하나의 프로세스이다.
- 탭 하나에 문제가 생겨도 다른 탭에는 문제가 생기지 않는다.
인터넷 익스플로러
- 익스플로러의 탭 하나는 하나의 스레드이다.
- 하나의 탭에 문제가 생기면 모두 종료가 되버리곤 한다.
→ 예전에 익스플로러에서 흔히 마주하던 상황
- Node 에서 CPU 연산이 필요할 때
시나리오
- Node js 에서 Cpu-Intensive 가 필요하다.
- 알고리즘을 활용한 계산 등
해결방안
- Worker Thread 를 활용하여 연산한다.
참고자료
기타 시나리오
- 기타 시나리오에 대해 알고 계신 부분이 있다면 연락해주세요 ( 010 - 9830 - 5559 )
- 작성중 ....
우리가 나아가야 할 방향
해당 섹션은 개인적인 가정과 질문을 순서로 나눠 질답을 하며 정답을 찾아볼 것이다.
동기 블럭킹이 별로 안좋은 거 같은데 기존 프로젝트들은 바꿀 수 있으면 다 바꾸면 좋을까?
→ 절대 아니다. 기본적으로 우리가 개발했던 스프링 프로젝트들의 구조를 다시 한번 살펴보자.
- 비관계형 DB를 사용할때는 async보다는 Spring Webflux 사용
- 관계형 DB에서는 Async가 의미가 없다. DB단에서는 트랜젝션 때문에 동기로 통신을 처리하기 때문이다.
- 물론, 최근 비동기 DB 접근 방법이 있다. 프로덕션 레벨에서 사용해본 경험이 없으니, 잘 생각해보아야 한다.
비동기 논블럭킹 / 동기 블럭킹 말고는 굳이 구분해야 할 필요가 있나요?
→ 있다. Polling 과 같은 개념을 이해하려면 꼭 필요하고, 이 개념을 활용하여 아키텍쳐를 설계할 때 사용할 수 있다.
풀링을 이용한 아키텍쳐를 우아한 형제들에서 사용했다.
결국 비즈니스 규모가 큰 회사들은 비동기 / 논블럭킹 을 사랑할 것이고, 내가 원하는 회사나 기술과 맞다면 투자하라.
섞인 개념들 덜어내지 못한 부분 (읽지 말 것)
Node js 와 Spring 프레임워크가 가지는 차이점과 활용방안
Node Js 관점에서의 결론과 활용방안
- 비동기 IO의 처리에 특화된 엔진의 특성을 항상 기억하라. (CPU 를 많이 사용 X)
- 루프의 구조를 이해하여 태스크를 적절히 분리 하자.
- 루프의 구조를 이해하여 항상 비동기적 로직을 작성 하자.
- 서버는 항상 Stateless 하게 설계되어야 한다.
- Node Js 에서 멀티 프로세스를 활용할 수 있어야 한다.
- 멀티 프로세스는 “데이터 공유” 가 불가능 하다.
- 스케일 아웃이 될 때, 워커들간의 데이터 공유는 어떻게 해결할까?
- 아웃 된 서버와 기존 서버안에 있는 워커들간의 데이터 공유는 어떻게 해결할까?
멀티 프로세스와 워커를 활용하여 진행된 우아한 형제들의 기술블로그
→ 이벤트 정보를 공유하는 것과 유지되어야 할 로그인 정보는 격리되어야 한다.
Spring 관점에서의 결론과 활용방안
- 스프링의 기본 Servlet Container는 Process 하나에 배정된다. (즉 싱글프로세스)
- 싱글 프로세스 에서 멀티스레딩을 이용하여 동작한다.
- Spring 에서 빈 역시 StateLess 하게 설계되어야 한다.
- 빈을 공유해서 사용한다는 의미는 “오브젝트 직접참조” 가 아니다.
(빈 클래스의 정보를 가진 Method Area 에서 객체의 Binary Code 정보를 공유)
- 상태가 필요할 때, 다양한 방식으로 해결할 수 있고., 방어적인 프로그래밍으로 작성할 수 있다.
- (ThreadLocal, Bean scopes = Prototype, Stack Programming 등)
- 스케일 아웃이 되었을 때, 단일 서버안에서 데이터를 자유롭게 교환할 수 있는 이점이 있다.
- 스케일 아웃된 서버 전체에서 값을 참조하여 변경해야 할 경우, 특별한 처리가 더 필요할 것.
토큰 값을 공유하는 형태와 세션을 공유하는 형태에 대한 차이점
추가
→ V8 엔진과 노드의 엔진에 대한 자세한 매커니즘은 해당 포스트를 참고하세요.
→ 비동기 처리에 대한 개념은 해당 포스트를 참고하세요.
→ Worker Thread : CPU-Intensive 에 적합하다. IO-Intensive 는 loop 가 처리하는 게 좋다.
워커 스레드 관련 포스팅은 여기
해결해야 될 의문
- Redis 서버를 중간에 두어 활용한 사례말고, Kafka 를 이용하여 처리할 순 없을까?
- Worker 들 간의
IO 참고
로드밸런싱, 세션, 클러스터링, 캐시서버에 대한 개념
Sticky Session과 Session Clustering
스프링의 서블릿 컨테이너와 동작, 그리고 동기 블락킹 흐름
ServletContainer와 SpringContainer는 무엇이 다른가?
자바스크립트와 비동기
JavaScript 비동기 핵심 Event Loop 정리
NodeJS의 Non-Blocking IO와 멀티코어 프로세싱
NodeJS의 Non-Blocking IO와 멀티코어 프로세싱
멀티 프로세스와 멀티 스레딩의 장단점
멀티 프로세스(Multi Process)와 멀티 스레드(Multi Thread)
멀티 프로세싱 개념과 활용
PM2를 활용한 Node.js 무중단 서비스하기 - LINE ENGINEERING
스프링
스프링 구조
ServletContainer와 SpringContainer는 무엇이 다른가?
스프링의 비동기 처리의 유효성
일반적으로 async말고 일반적인 스프링 MVC패턴을 추천한다.
async를 써도 문제는 없지만, 사실 여러개의 API를 동시에 전송해서 그 결과를 JOIN할때 일반적인 메소드 내에서
CompletableFuture를 여러개 만든다음 전송시키고 합쳐서 응답을 내려주는게 제일 좋은 방안이다.
비관계형 DB를 사용할때는 async보다는 Spring Webflux 사용을 권장하며,
관계형 DB에서는 Async가 의미가 없다. DB단에서는 트랜젝션 때문에 동기로 통신을 처리하기 때문이다.
[Spring, Android] 스프링 안드로이드 JWT 활용한 WebView 모바일 유저 인증처리 (#업무)