Q "광화문 어디에 있어요?"
A "서울특별시 종로구 세종로 사직로 161이요"
어디에 있든지 한 방에 가는 고유한 경로(주소)를 말한다.
ex) C:\users\document\untitled.jpg, http://www.google.com
Q "(지금 광화문 역인데...) 광화문 어디에 있어요?"
A "여기(광화문역)에서 쭉 직진하면 보이는 세종대왕 동상 바로 뒤요"
'현재 내가 위치한 곳을 기준'으로 한 '그곳의 위치'이다.
다시한번 설명하자면 내가 작업중인 소스코드 파일이 위치한 폴더를 기준으로 작성한 상대적인 경로라는 것. (VSC 경우도 모든 것을 작업하는 폴더를 기준으로 한다)
/ 루트 (가장 최상의 디렉토리로 이동 / Web root)
./ 현재 위치 (파일의 현재 디렉토리를 의미)
../ 현재 위치의 상단 폴더 (상위 디렉토리로 이동 - 한 칸 상위로 이동)
수도 없이 사용했던 document.ready, getElementById, location.href 등등
'이런 접근은 어떻게 가능한 것일까?'에 대한 대답이 될 수 있는 주제이다.
◽ DOM(Document Object Model)
단어 그대로 '도큐먼트를 객체로 표현하는 모델', 문서 객체을 의미한다.
문서 객체란 HTML 문서의 태그들을 JavaScript가 읽을 수 있는 객체(object)로 만든 것이다.
HTML, XML 문서의 프로그래밍 인터페이스이다.
DOM은 수정사항이 있을 때 처음부터 렌더링을 거치는데, 스케일이 커질수록 노드가 증가하여 브라우저의 속도가 느려진다.
◽ DOM에 접근하기
document 객체를 통하여 접근할 수 있다.
getElementById()
getElementByTagName()
getElementByClassName()
querySelector() - (보통은 쿼리 셀렉터를 자주 사용한다. css 선택자를 그대로 쓸 수 있기 때문)
// 해당 div를 클릭했을 때 hide라는 클래스로 변경하기
<div id="here" class="show">
<p>안녕!</p>
</div>
<script>
let target = document.getElementById("here");
target.addEventListener("click", function() {
target.removeClass("show").addClass("hide");
});
</script>
◽ 가상 DOM(Virtual DOM)
Vue.js는 가상 DOM(Virtual DOM) 기반의 렌더링 시스템으로 빠른 렌더링 속도를 자랑한다.
가상 DOM은 실제 DOM의 복사본을 메모리 내에 저장하여 사용한다.
수정이 될 때 마다 전체가 다시 렌더링 되어 속도의 단점이 있는 진짜 DOM과 달리, 가상 DOM은 변경될 때마다 진짜 DOM과 비교해서 차이를 찾아 그 부분만 수정하는 효율적인 동작을 하여 속도가 빠르다.
◽ BOM(Browser Object Model)
브라우저에 접근 할 수 있는 객체의 모음이다.
window 객체를 통해 접근할 수 있다.
DOM은 현재 눈에 보이는 웹문서에 대한 제어와 변경이라면 BOM은 window를 제어한다.
◽ BOM에서 제공하는 객체
window 객체를 통하여 접근할 수 있다.
window: 가장 최상위 객체로, 아래 서술된 모든 객체는 이 객체 아래에 존재한다.
location
document
navigator
history
screen
백엔드(서버)를 구성하는데 사용되는 기술은 Node.js 외에도
Java의 Spring
Python의 Django
PHP의 Laravel -
Ruby의 Rails
등 다양한 프레임워크가 존재한다. 그럼에도 Nodejs로 서버를 구성한다면 그 선택에 이유가 있어야한다. 무지성으로 기술을 선택하지 말자.
먼저, 위 질문에 대해서 어떻게 대답해야할까?
가장 대표적으로 잘못된 대답은 자바스크립트로 만드는 백엔드/서버라는 대답이다. 말도 안 되는 소리는 아니지만, Nodejs가 뭐냐는 질문에는 명백히 잘못된 대답이다. 그렇다면 Node.js는 뭘까??
쉬운 말로 표현하면 자바스크립트를 브라우저가 아닌 다른 환경에서 사용할 수 있게 해주는 무언가이다.
좀 더 제대로 표현하자면 V8 엔진으로 빌드된 JavaScript의 새로운 런타임이다. 기존 런타임은 Web Browser만 존재했다.
즉, 자바스크립트로 만드는 백엔드/서버는 Nodejs라는 런타임 환경에서 할 수 있는 일의 일부이기에, Node.js가 뭐죠? 라는 질문의 대답이 될 수는 없다. 과일이 뭐냐는 질문에 사과요라고 답할 수는 없지 않은가.
백엔드에 국한된 이야기는 아니지만, 런타임이라는 용어가 나온 김에 정리해보겠다. 런타임이란 프로그램이 실행되고 있는 환경이다. 쉽게 말하면 그 코드가 지금 어디에서 실행되고 있는가 생각하면 된다.
프로그래밍 언어는 주로 고급 언어로, 기계(컴퓨터)가 바로 이해할 수 없다. 따라서 고급 언어로 작성된 소스 코드를 기계어로 변환하는 과정이 필요한데, 이 행위를 컴파일이라 한다. 이렇게 컴파일 과정을 마친 프로그램이 사용자에 의해 실행되어 응용프로그램이 동작되는 순간을 런타임이라 한다. 이 컴파일/런타임에 따라 발생하는 에러도 다르다. (링크 참고),
흔히 자바스크립트는 인터프리터 언어라서 컴파일 타임이 없다는 이야기를 하곤 하는데, 이 이야기는 컴파일을 하지 않는 다는게 아니라 컴파일 타임이 없다는 것이다. 컴파일 과정이 없는 고급 언어는 존재할 수 없다. 다만 컴파일을 언제 하는가 방식의 차이일 뿐이다. (링크 참고)
지금까지 자바스크립트는 브라우저만이 자바스크립트의 유일한 런타임이었다. 하지만 Nodejs가 브라우저 밖에서 자바스크립트를 사용할 수 있는 환경을 제공한다. 이로 인해 자바스크립트로 데스크탑 앱도 만들고, 서버도 만들고 할 수 있다.
자바스크립트를 사용한다
단일 스레드 이벤트 루프 기반
Non-blocking I/O (비동기)
방대한 모듈 생태계(NPM)
백엔드 로직도 직접 작성하고 싶은 프론트엔드 개발자에게 큰 장점이 된다. 새로운 언어를 배우지 않아도 된다는 건 크나큰 장점이다.
또한 이후 설명할 특징은 모두 자바스크립트를 사용하기에 나타나는 특징이다
오해하지 말아야 하는 것은, Nodejs 자체는 멀티 쓰레드이다.
자바스크립트 엔진에는 외부 요청(입력)에 대한 처리를 하는 단일 호출 스택이 존재하는데, 이 단일 호출 스택과 Node.js의 다양한 쓰레드를 연동하기 위해 사용하는 장치가 이벤트 루프이며, 이 이벤트 루프가 단일 쓰레드인 것이다.
장점
수 많은 요청에 대해 순차적으로 처리할 필요 없이 워커 쓰레드에 작업 처리를 위임하고, 작업이 끝나는 순서대로 이벤트를 받아서 응답한다. => 대규모 네트워크 프로그램을 개발하기에 적합
요청의 수와 상관 없에 메인 쓰레드는 하나이기 때문에 메모리 사용량과 시스템 리소스 사용량에 변화가 거의 없다
따라서 서버에 부하가 적다. (가볍다!)
단점
context switching 비용이 크다.
많은 요청을 처리하기엔 적합하지만, 큰 단일 처리가 필요한 요청에는 적합하지 않다(콜백지옥에 빠질 수 있음)
메인 스레드가 무너지면 프로그램 전체에 문제가 발생할 수 있다.
위에서 언급한 워커 쓰레드는 비동기 방식으로 인풋/아웃풋을 관리한다
따라서 프로그램의 흐름을 막지 않고(Non-blocking) 동시에 여러 작업을 수행하는게 가능하다
npm에 등록된 수 많은 라이브러리, 패키지를 사용할 수 있기에 개발 효율성이 올라간다.
앞서 설명한 Node.js의 특징을 기반으로 요약, 정리하면서 글을 마무리하도록 하겠다.
단일 스레드이기 때문에 하나의 커다란 요청보다 간단한 요청 처리에 어울린다
예를 들어 네트워크 스트리밍, 채팅 등 작고 빈번한 요청의 서비스들.
또한 비동기로 요청을 처리하기 때문에 처리가 끝나면 바로 응답한다. 즉, 응답 속도가 빠르다
심지어 async, await의 등장으로 비동기 처리 로직을 작성하는 난이도도 쉬워졌다.
Nodejs는 런타임에 에러가 발생할 수 있기 때문에 프로그램 복잡도와 위험도가 비례한다.
또한 서버에 체크 로직이 많으면 callback 지옥에 빠질 수도 있다. (async, await로 어느정도 해결 가능!)
Node.js가 원인을 알 수 없는 이유로 종료되는 경우는 없다. 주로 예외처리를 하지 않은 개발자의 실수가 원인이기 때문에, 개발복잡도가 올라가면 실수할 가능성이 높아진다.
NPM 생태계에서 다른 패키지 도움을 받아 개발 효율을 높일 수 있으며
개발 환경 자체가 그리 복잡하지 않기 때문에 빠르게 개발을 진행할 수 있다.
웬만한 기능은 이미 NPM 패키지에 존재한다.
데이터 포맷으로 JSON을 사용할 때
자바스크립트 자체가 JSON을 지원하기에 적합하다.
데이터베이스로 MongoDB / Elasticsearch 등을 사용한다면 시너지가 더 발생한다.
Java, C++과 같은 클래스 기반 객체지향 프로그래밍 언어와 달리 자바스크립트는 프로토타입 기반 객체지향 프로그래밍 언어이다. 따라서 자바스크립트의 동작 원리를 이해하기 위해서는 프로토타입의 개념을 잘 이해하고 있어야 한다.
클래스 기반 객체지향 프로그래밍 언어는 객체 생성 이전에 클래스를 정의하고 이를 통해 객체(인스턴스)를 생성한다. 하지만 프로토타입 기반 객체지향 프로그래밍 언어는 클래스 없이(Class-less)도 (ECMAScript 6에서 클래스가 추가되었다) 객체를 생성할 수 있다.
자바스크립트의 객체 생성 방법
자바스크립트의 모든 객체는 자신의 부모 역할을 담당하는 객체와 연결되어 있다. 그리고 이것은 마치 객체 지향의 상속 개념과 같이 부모 객체의 프로퍼티 또는 메소드를 상속받아 사용할 수 있게 한다. 이러한 부모 객체를 Prototype(프로토타입) 객체 또는 줄여서 Prototype(프로토타입)이라 한다.
Prototype 객체는 생성자 함수에 의해 생성된 각각의 객체에 공유 프로퍼티를 제공하기 위해 사용한다.
var student = {
name: 'Lee',
score: 90
};
// student에는 hasOwnProperty 메소드가 없지만 아래 구문은 동작한다.
console.log(student.hasOwnProperty('name')); // true
console.dir(student);
ECMAScript spec에서는 자바스크립트의 모든 객체는 [[Prototype]]이라는 인터널 슬롯(internal slot)를 가진다. [[Prototype]]의 값은 null 또는 객체이며 상속을 구현하는데 사용된다. [[Prototype]] 객체의 데이터 프로퍼티는 get 액세스를 위해 상속되어 자식 객체의 프로퍼티처럼 사용할 수 있다. 하지만 set 액세스는 허용되지 않는다. 라고 되어있다.
[[Prototype]]의 값은 Prototype(프로토타입) 객체이며 proto accessor property로 접근할 수 있다. proto 프로퍼티에 접근하면 내부적으로 Object.getPrototypeOf가 호출되어 프로토타입 객체를 반환한다.
student 객체는 proto 프로퍼티로 자신의 부모 객체(프로토타입 객체)인 Object.prototype을 가리키고 있다.
var student = {
name: 'Lee',
score: 90
}
console.log(student.__proto__ === Object.prototype); // true
객체를 생성할 때 프로토타입은 결정된다. 결정된 프로토타입 객체는 다른 임의의 객체로 변경할 수 있다. 이것은 부모 객체인 프로토타입을 동적으로 변경할 수 있다는 것을 의미한다. 이러한 특징을 활용하여 객체의 상속을 구현할 수 있다.