JS는 왜 싱글스레드인가

상태
진행 중
담당자
날짜
TMI

목차

알아두기

왜 JavaScript는 싱글 스레드 언어인가?

JS는 다른 언어들과는 다르게 싱글 스레드 언어입니다. 이는 JS 엔진이 한 번에 하나의 작업만 처리할 수 있음을 의미합니다. 이로 인해 비동기적으로 작업을 처리하는 이벤트 기반 모델이 필수적이며, 콜백 함수와 프로미스 등을 통해 비동기 코드를 관리합니다. 이러한 특성은 멀티스레드 환경에서의 복잡성을 줄이고, 단순한 동시성 제어를 가능하게 합니다. 지금부터 JS는 왜 싱글 스레드 언어로 발전했는지에 대해 살펴보겠습니다.

언어적인 설계 선택

JavaScript가 싱글 스레드인 주된 이유는 초기에 웹 브라우저 환경에서 실행되기 위해 설계되었기 때문입니다. 웹 페이지는 보통 단일 스레드인 메인 스레드에서 동작하며, 이러한 환경에서 여러 개의 스레드가 동시에 실행되면 복잡성과 동기화 문제가 발생할 수 있습니다. 따라서 JavaScript가 메인 스레드에서 단일 스레드로 동작하도록 설계된 것입니다.

비동기적 실행 모델

JavaScript는 비동기적 실행 모델을 채택하여 싱글 스레드에서도 비동기적으로 여러 작업을 처리할 수 있습니다. 이는 이벤트 루프(event loop)를 통해 구현됩니다. 비동기 API(예: setTimeout, XMLHttpRequest)를 통해 작업을 백그라운드에서 처리하고, 결과가 준비되면 콜백(callback)을 호출하여 처리하는 방식입니다. 이를 통해 JavaScript는 싱글 스레드에서도 효율적으로 동작하면서도 사용자 경험을 향상시킬 수 있습니다.

Node.js에서 JavaScript가 싱글 스레드인 이유

Node.js가 JavaScript를 싱글 스레드로 처리하는 이유는 초기의 웹 브라우저에서의 JavaScript 실행 환경과 이를 기반으로 한 설계 결정 때문입니다. 이를 통해 Node.js는 단순성과 효율성을 유지하면서도 비동기 처리를 통해 높은 성능을 제공할 수 있습니다.

결론

JavaScript가 싱글 스레드 언어인 이유는 초기 웹 환경에서의 설계 선택, 비동기적 실행 모델의 채택, 그리고 컴퓨터 시스템에서의 효율성을 고려한 결과입니다. 이러한 특성은 JavaScript를 웹 개발에서 강력한 도구로 만들어주며, 현대 웹 애플리케이션의 성능과 사용자 경험에 기여하고 있습니다.

부록

웹페이지는 단일 스레드로 동작하는 게 더 유리할까
JavaScript가 웹페이지에서 단일 스레드인 메인 스레드에서 동작하는 이유는 웹 브라우저의 동작 방식과 관련이 있습니다.

웹 브라우저의 단일 스레드 구조

1.
UI와 렌더링 동기화: 웹 페이지는 주로 사용자 인터페이스(UI)를 표시하고 사용자의 입력을 처리하는 역할을 합니다. 이러한 UI 작업들은 한 번에 하나씩 처리되어야 하며, 이를 위해 웹 브라우저는 단일 스레드인 메인 스레드를 사용합니다. 메인 스레드는 UI 렌더링과 관련된 작업을 순차적으로 처리하여 웹 페이지의 일관된 상태를 유지합니다.
2.
동기화 문제 회피: 여러 개의 스레드가 동시에 UI를 조작하면 상태 관리가 복잡해지고 예측할 수 없는 동작이 발생할 수 있습니다. 따라서 단일 스레드에서 UI와 관련된 작업을 처리함으로써 동기화 문제를 회피할 수 있습니다.

웹페이지의 단일 스레드 동작의 장점

1.
단순성과 예측 가능성: 단일 스레드 구조는 복잡성을 줄이고 예측 가능성을 높입니다. 웹 개발자는 메인 스레드에서 발생하는 작업의 순서와 타이밍을 명확히 이해할 수 있습니다.
2.
자원 관리: 단일 스레드로 동작하는 웹 페이지는 메모리와 CPU 자원을 효율적으로 관리할 수 있습니다. 여러 스레드가 동시에 실행되는 것보다 리소스 관리가 간단하고 효율적입니다.

결론

JavaScript가 웹페이지에서 단일 스레드인 메인 스레드에서 동작하는 이유는 웹 브라우저의 단일 스레드 구조와 UI 동기화의 필요성 때문입니다. 이러한 설계 선택은 웹 개발의 단순성과 예측 가능성을 높이며, 웹 페이지의 성능과 사용자 경험을 향상시키는 데 기여합니다.
js에서 a(), b() 두 개의 작업을 비동기로 동작시키는 예시

비동기 작업 요청

먼저, a()와 b() 작업을 비동기 함수로 요청합니다. 여기서는 setTimeout을 사용하여 간단한 예로 설명하겠습니다.
javascript코드 복사 setTimeout(() => console.log('a 작업 완료'), 1000); setTimeout(() => console.log('b 작업 완료'), 500);
JavaScript
복사
위 코드에서는 각각 1초와 0.5초 후에 실행될 두 개의 비동기 작업을 요청하고 있습니다.

백그라운드 처리

비동기 함수는 요청된 후 백그라운드에서 타이머에 의해 관리됩니다. 이 예제에서는 b() 작업이 500ms 후에 완료되고, a() 작업은 1000ms 후에 완료됩니다.

작업 완료 후 콜백 큐에 추가

작업이 완료되면 해당 작업의 콜백 함수가 콜백 큐에 추가됩니다. b() 작업은 500ms 후에, a() 작업은 1000ms 후에 각각 콜백 큐에 추가됩니다.

이벤트 루프

이벤트 루프는 콜백 큐에서 대기 중인 작업을 하나씩 가져와 실행합니다. 먼저 b() 작업의 콜백을 가져와 실행한 후, a() 작업의 콜백을 가져와 실행합니다.

콜백 함수 실행

메인 스레드에서 콜백이 실행됩니다. 먼저 b() 작업의 콜백이 실행되어 "b 작업 완료"가 출력되고, 이후 a() 작업의 콜백이 실행되어 "a 작업 완료"가 출력됩니다.

시각화

아래의 시각화는 이 과정이 어떻게 이루어지는지 쉽게 이해할 수 있도록 도와줍니다.
yaml코드 복사 | JavaScript 코드 || a() 작업 요청 | | b() 작업 요청 | ↓ ↓ | 백그라운드 타이머 설정 | | 백그라운드 타이머 설정 | ↓ ↓ | 대기 (1000ms) | | 대기 (500ms) | ↓ ↓ | 콜백 큐에 추가 | | 콜백 큐에 추가 | ↓ ↓ | 이벤트 루프가 처리 | | 이벤트 루프가 처리 | ↓ ↓ | 콜백 실행 (a) | | 콜백 실행 (b) | ↓ ↓ | "a 작업 완료" 출력 | | "b 작업 완료" 출력 |
YAML
복사
이 방식으로 JavaScript는 싱글 스레드 환경에서도 비동기 작업을 효율적으로 처리합니다. 각 작업이 완료되는 시점에 따라 순차적으로 콜백을 실행하여 여러 작업을 동시에 처리하는 것처럼 보이게 합니다. 이러한 비동기 처리 모델 덕분에 JavaScript는 웹 애플리케이션에서 높은 성능과 유연성을 제공할 수 있습니다.
nodeJS의 설계 원칙과 특징
초기 설계 결정
Node.js는 초기에 V8 엔진을 사용하여 JavaScript를 서버 측 애플리케이션 개발에 적합하게 만들기 위해 개발되었습니다. JavaScript는 웹 브라우저에서 주로 단일 스레드로 동작하는 것을 반영하여, Node.js 역시 단일 스레드로 동작하도록 설계되었습니다.
이벤트 기반 비동기 모델
Node.js는 비동기 이벤트 기반 모델을 채택하여 단일 스레드에서도 많은 I/O 작업을 효율적으로 처리할 수 있습니다. 이 모델은 이벤트 루프를 통해 비동기 작업을 관리하고, 작업 완료 시 콜백을 실행하여 다음 작업을 수행합니다. 이러한 방식으로 Node.js는 여러 클라이언트 요청을 동시에 처리할 수 있습니다.
자원 관리와 단순성
싱글 스레드 구조는 메모리 사용을 줄이고 자원 관리를 간소화합니다. 여러 스레드가 동시에 접근할 때 발생할 수 있는 동기화 문제를 회피할 수 있으며, 코드 작성과 디버깅을 단순화시킵니다.
멀티 스레드 대신 클러스터링
Node.js는 멀티 스레딩이 아닌 클러스터링을 통해 성능을 확장할 수 있습니다. 클러스터링은 여러 개의 프로세스를 생성하고 각각을 단일 스레드로 동작하게 만들어 멀티 코어 CPU를 효율적으로 활용할 수 있습니다.