구직 사이트를 돌아보다 가장 먼저 깨닫게 된 게 있다.
python-django 보다 javascript-node.js를 사용하는 회사가 더 많다는 것이다.
취준생으로써 node 찍먹정돈 해볼 필요성을 깨달았다.
Node.js(이하 노드)를 정의할 때 3가지 키워드를 많이 얘기한다. 1. 비동기(Asynchronous), 2. 이벤트 기반(event-driven), 3. Javascript 런타임 환경 이다. 하나씩 차례대로 알아보자.
위 사진을 보면 동기와 비동기의 차이를 직관적으로 볼 수 있다. 동기(synchronous)는 어떤 API가 한 번의 요청(request)와 응답(response)을 처리할 때 서버는 다른 일을 할 수 없는 상태에 놓이게 된다. 즉 1번 task를 모두 끝낸 뒤 2번, 3번, 이후 task 순서대로 request를 처리(sequential I/O)하는 것이다.
하지만 이에 반해 비동기(Asynchronous)는 동기방식과는 다르게 API에 순서에 상관없이 일처리를 할 수 있는 비순차 입출력(non-sequential I/O) 환경이 마련된다. 그렇기 때문에 일의 처리(완료) 순서도 빨리 끝나는 작업의 결과가 먼저 출력되는 것을 알 수 있다.
// task 1
console.log("Sync node.js run start")
const meSync = {
laundry: () => {
console.log('세탁기 돌리기 시작')
let percentage = 25
while (percentage !== 100) {
console.log(`${percentage}% 완료`)
percentage += 25
}
console.log(`${percentage}% 빨래 완료`)
},
dishes: () => console.log('설거지 하기'),
toilet: () => console.log('화장실 청소 하기'),
}
const doWorkSync = () => {
meSync.laundry()
meSync.dishes()
meSync.toilet()
}
// task 2
doWorkSync()
// task 3
console.log("Sync node.js run end")
동기적 처리 방식의 쉬운 예시는 다음과 같다. meSync라는 object를 만들고 실행하면 결과는 다음과 같다.
Sync node.js run start
세탁기 돌리기 시작
25% 완료
50% 완료
75% 완료
100% 빨래 완료
설거지 하기
화장실 청소 하기
Sync node.js run end
task 1, 2, 3순서대로 코드가 실행된 것을 확인할 수 있다. 세탁기 작업을 모두 완료한 후에 설거지를 하고 화장실 청소도 수행하는 것이다. 세탁기를 돌리는 도중에 설거지도 하고 화장실 청소도 할 수 있는데도 이렇게 일을 처리하는 것은 정말이지 얼마나 비효율 적이란 말인가?! 그렇다면 비동기 처리는 어떻게 다를까?
//task 1
console.log("Async node.js run start")
const meAsync = {
laundry: () =>
new Promise((resolve, reject) => {
console.log('세탁기 돌리기 시작')
let percentage = 25
const intervalId = setInterval(() => {
console.log(`${percentage}% 완료`)
percentage += 25
if (percentage === 100) {
clearInterval(intervalId)
console.log(`${percentage}% 빨래 완료`)
resolve()
}
}, 100)
}),
dishes: () => console.log('설거지 하기'),
toilet: () => console.log('화장실 청소 하기'),
}
const doWorkAsync = async () => {
await meAsync.laundry()
meAsync.dishes()
meAsync.toilet()
}
// task 2
doWorkAsync()
// task 3
console.log("Async node.js run end")
코드 실행 결과는 다음과 같다.
Async node.js run start
세탁기 돌리기 시작
Async node.js run end
25% 완료
50% 완료
75% 완료
100% 빨래 완료
설거지 하기
화장실 청소 하기
Promise나 async, awaitm setInterval 등 위의 동기 처리 방식에서는 보지 못한 코드가 조금 있다. 이런 함수들이 만들어낸 가장 큰 차이점은 결과에서 볼 수 있듯이 코드 가장 밑 줄에 표현해둔 console.log
(task 3)가 먼저 실행되고 doWorkAsync()
(tast 2)함수가 실행된 것이다!! 이 점이 동기식과 비동기 방식의 차이이다. promise나 async, await와 같은 것은 다음에 자세히 공부해봐야겠다.
(잠깐! 그럼 이건 세탁기를 돌리면서 동시에 설거지, 화장실 청소한건 아니잖아!! 참고로 meAsync.dishes()
와 meAsync.toilet()
를 doWorkAsync
함수 밖으로 빼면 다른 결과가 도출된다. 결과는 직접 확인!)
사실 나에게는 비동기보다 더 이해하기 어려운 키워드가 이벤트 기반
이었다. 하지만 스스로 내린 결론을 얘기해보자면,
- Evevt
이벤트에는 클릭(.on("click")
)이나 네트워크 요청(request
) 등이 있다.- Event-driven
이벤트 기반 시스템에서는 특정 이벤트에 대한 콜백 함수(callback function)을 지정한다.
결국 이벤트 기반이란 키워드는 비동기 방식과 떨어질 수 없는 것 같다. 하나의 이벤트를 비동기적으로 처리하는 것이 노드의 아이덴티티인 것이다. 쉬운 예시를 들어보자면 클라이언트 1과 2가 순차적으로 request
를 보냈을 때 클라이언트 1과 2 중 먼저 동작이 종료되는 request
에 대한 response
를 전송하는 것이다.
여담으로 사용하는 모듈과 코딩 방식에 따라 싱글 스레드로도 충분히 멀티 스레드와 같은 환경을 만들어줄 수 있고, 멀티 스레드를 싱글 스레드처럼 사용할 수도 있다.(다만 성능의 차이가 있겠지만) 여기서 노드는 싱글 스레드 환경에서 비동기 이벤트 기반의 처리 방식으로 빠른 앱을 설계할 수 있게 된다.
자바스크립트로 만든 프로그램을 컴퓨터에서 실행할 수 있는 환경
그렇다면 자바스크립트 런타임 환경이란 어떤 의미일까? 먼저 런타임은 특정 언어로 만든 프로그램을 실행할 수 있는 환경을 의미한다. 구글의 V8 엔진이 등장하기 전까지 기존의 자바스크립트는 브라우저 단에서 구동되는 언어였기 때문에 컴퓨터가 이해할 수 있는 언어가 아니었다. V8 엔진의 등장과 함께 자바스크립트는 더이상 브라우저 뿐만아니라 컴퓨터가 이해할 수 있게 컴파일되어 브라우저를 벗어나 사용할 수 있게 되었다.
Chrome V8 엔진은 자바스크립트를 바이트코드(bytecode)로 컴파일하고 실행하는 방식을 사용하는 C++ 오픈 소스 자바스크립트 엔진이다. 이름에 걸맞게 구글에서 시작되었고, 지금은 우리의 자랑스러운 삼성 전자도 개발 그룹에 포함되어있다.(국뽕 충전!)
노드(Node.js)는 비동기(Asynchronous) 이벤트 기반(event-driven) Javascript 런타임 환경 이다