Preview
https://sieon-dev.tistory.com/130에서는 prototype, constructor, __proto__ 를 중점으로 살펴봤다. 객체와 객체지향을 시작하기 위한 첫 번째 단계라고 생각한다. 이번에는 자바스크립트가 동작하는 방식과 이 방식으로 인한 장단점 등을 살펴보려고 한다.
Methodology
Javascript의 특징
- Non-blocking IO 시스템
Non-Blocking 은 처리가 순서대로 이루어짐을 의미한다. 즉 이전 작업이 끝나지 않았더라도 기다려주지 않는다.
대부분의 프로그래밍 언어는 Blocking IO 시스템으로 되어있지만 왜 Javascript Engine(V8)은 왜 Non-Blocking IO일까?
그전에 Javascript 는 싱글 스레드 기반의 언어임을 알아야 한다.
왜 싱글 스레드인지는 개발자가 그 이유를 말하진 않았지만 대다수의 사람이 말하는 원인에는 "쉬워서"이다.
멀티 스레드 기반의 언어였으면 웹에서 이루어지는 동시성에 대한 엄격하고 구조화된 처리방식을 가지고 있어야 하지만 싱글 스레드인 경우 하나의 스레드에서 모든 처리를 처리하면 되기에 쉽다고 한다.
만약 하나의 스레드에서 모든 프로세스를 순서대로 처리하고 있으면 답답할 것이다.
이를 해결하기 위해 브라우저 엔진 및 NodeJS는 효율적으로 프로세스를 처리할 수 있도록 WebAPI와 event loop, queue stack을 활용해 Non-Blocking IO 시스템으로 구축한 것이다.
어쨌든 이벤트 루프는 무엇입니까? | Philip Roberts | JSConf EU
- 비동기 프로그래밍 방식
비동기 프로그래밍은 작업이 완료될 때까지 기다리는 대신, 다른 작업을 동시에 실행하도록 하는 프로그래밍 방식이다. 앞서 설명한 Non-Blocking과 같은 개념인 듯 보인다. 하지만 엄밀히 따지면 같은 개념은 아니다.
ChatGPT에게 물어봤다.
비동기 작업은 작업을 시작하고 결과를 기다리지 않고 다른 작업을 계속할 수 있는 방식입니다. 일반적으로 콜백, 프라미스, async/await 등의 메커니즘을 사용하여 구현됩니다.
non-blocking은 주로 I/O 작업이나 네트워크 호출과 관련이 있습니다. 블로킹 I/O에서는 한 작업이 완료될 때까지 다음 작업을 수행할 수 없지만, 논블로킹 I/O에서는 작업이 완료될 때까지 기다리지 않고 다른 작업을 계속할 수 있습니다.
즉, 비동기는 작업의 동작 방식을 설명하는 데 사용되고, "non-blocking"은 주로 I/O 작업이나 네트워크 호출과 같은 특정 상황에서 작업이 수행되는 방식을 설명하는 데 사용됩니다.
웹 브라우저나 NodeJS는 Non-blocking 시스템이기 때문에 자동적으로 비동기 처리 방식을 따른다. 실행하는데 오래 걸리는 함수를 기다려주지 않는다. 따라서 순서가 중요한 프로세스에는 그에 맞는 동기 처리 방식에 따라 개발해줘야 한다.
예전 처음 개발을 시작할 때 JS의 비동기처리에 대한 글을 블로그에 남긴적이 있다.
- 일급객체
일급객체(First-class object) 개념은 Javascript에 국한되지 않는 개념이다. 그렇기에 특별한 개념은 아니지만 꽤나 중요하다.
일급객체는 함수와 객체를 변수에 할당하거나 함수의 매개변수로 전달하고, 함수에서 반환할 수 있는 것을 의미한다.
const foo = function () {
return "hello, js";
};
이와 같이 변수에 함수를 할당하거나
function foo(boo) {
boo();
}
foo(() => {
return "hello, js";
});
이와 같이 다른 함수에 파라미터로 함수를 전달할 수도 있다.
function foo() {
return () => {
return "hello,js";
};
}
const boo = foo();
boo();
또한 이와 같이 함수에서 함수를 반환할 수 있다.
- 스코프 체이닝
스코프는 일종의 범위이다. 함수는 중괄호 { } 라는 범위 안에서 정의된다. 함수뿐만 아니라 조건문(if), 반복문(for), 객체(object) 등 중괄호를 사용해 하나의 스코프로 정의할 수 있다.
앞선 일급객체 설명에서 다룬 예제처럼 다른 함수에 파라미터로 전달된 함수의 스코프는 어떻게 될까?
function foo(boo) {
const myName = "bono";
boo();
}
foo(function boo() {
console.log(myName);
});
위 예제처럼 하면 myName이라는 변수는 boo 함수의 스코프에 없기 때문에 에러가 발생한다. 즉 boo가 foo의 스코프에 있다고 하더라도 파라미터로 전달된 함수이기 때문에 같은 스코프로 정의되지 않는다.
function foo() {
const myName = "bono";
function boo() {
console.log(myName);
}
boo();
}
이처럼 전달받지 않고 한 스코프 안에 정의된 함수 boo는 foo 스코프안에 있는 myName 변수를 사용 가능하다. 이게 가능한 이유는 스코프 체이닝 때문이다.
스코프 체이닝은 본인의 스코프보다 상위에 있는 스코프까지 탐색하기 때문에 체인처럼 엮여있다고 표현할 수 있다.
첫 번째 예제에서 boo 함수의 상위 스코프는 전역공간이다. 전역공간에도 myName변수가 없기에 에러가 난 것이다.
function foo() {
const myName = "bono";
return () => {
return myName;
};
}
const boo = foo();
boo();
이 경우 boo 에는 foo함수가 반환한 foo 스코프에 있는 변수 myName 이 담긴다. 쉽게 말해 myName이라는 변수를 밖으로 빼돌린 셈이다. 변수뿐만 아니라 함수도 가능하다. 중요한 건 반환될 때 상위의 스코프에 선언된 정보(콘텍스트) 도 같이 반환한다는 점이다. 이런 개념을 클로저라고 한다.
클로저를 잘 활용하면 비공개 메서드(private method)를 흉내 낼 수도 있다.
- JS의 장점과 단점, 그리고 한계점
JS는 쉽다. 쉽다라는 점이 장점이다. 동적타입언어이기 때문에 자료형에 대한 고민을 하지 않아도 된다.
가령 a라는 변수에는 숫자도, 문자열도, 함수도 자유롭게 담을 수 있다. 자유롭다.
사용자가 많다. 웹 브라우저에서 실행되는 스크립트 언어인 만큼 모든 개발자는 Javascript라는 언어를 한 번씩은 거치게 되어있다. 그렇다 보니 자료가 방대하다.
자유롭기 때문에 단점이 된다. 엄격하고 체계적인 프로그래밍 규칙이 정해져있지 않으면 프로젝트는 산으로 간다. 물론 다른 언어 기반의 프로젝트도 마찬가지겠지만 JS는 동적타입언어인 점과 런타임 환경에서 발생하는 예기치 못한 에러가 특히나 많기 때문에 유지보수에 어려움을 겪을 수 있다.
그래서 등장한 정적타입언어인 Typescript는 Javascript의 자유로움을 어느 정도 억제하고 타입을 강제해 JS의 아쉬운 점을 보완했다.
하지만 개인적으로 조금 모순적이라고 생각이 드는 점은 자유롭고 쉽다는 점 때문에 Javascript를 선택해 개발하는데, 다시 그 자유로움을 억제해야 한다는 점이 좀 웃겼다. 하지만 JS 기반의 프로젝트를 진행해 보니 TS의 필요성을 절실히 느끼고 있는 중이다.
'코드 기록' 카테고리의 다른 글
[Pyqt5] QMainWindow 배경 이미지 설정하기 (0) | 2024.04.14 |
---|---|
Why & What (is) Spidergen ? (0) | 2024.03.30 |
Restudy Series. Javascript (1) (0) | 2024.03.30 |
[개역개정 한글 성경] JSON 파일 다운로드 (0) | 2023.11.29 |
Javascript console.log 조심해야겠어요 (0) | 2023.11.02 |