상세 컨텐츠

본문 제목

8. Javascript 환경과 모듈

Tech - 웹 개론

by Nested World 2021. 11. 1. 21:53

본문

Node.js

앞서 말한바와 같이 서버에서 애초에 html을 내려주기 전에 javascript를 실행하고 내려보내 줄 수 있다면, 서버사이드에서도 Vue.js를 사용할 수 있다. 그렇다면 더이상 SSR을 위해 Thymeleaf를 사용하고, 동시에 CSR을 위해 Vue.js를 사용해야 하는 불편함이 사라진다. 그냥 Vue.js만 사용하면 SSR와 CSR이 동시에 가능하기 때문이다.

이렇게 하기 위해서는, 브라우저가 아닌 서버사이드에서 javascript를 실행해줄 수 있어야 한다. 그리고 그렇게 해줄 수 있는 "javascript 실행기"가 바로 Node.js이다.

Node.js가 등장하기 전에, javascript는 오로지 웹 브라우저를 통해서만 사용되었고 다른 목적으로는 사용되지 않았다. 하지만 이제 Node.js를 통해 아무 곳에서나 javascript를 실행할 수 있게 되면서, javascript의 활용폭이 더 넓어졌다.

Node.js 사이트에 들어가면 윈도우의 경우 msi파일을 통해 쉽게 설치할 수 있으며, 이 경우 Program Files에 node.exe파일이 생긴다. 실행해보면 터미널이 뜨고, 여기에 자바스크립트를 입력해서 작동시킬 수 있다.

node.js를 통해, 브라우저로 한정되어 있던 javascript의 활용범위가 넓어졌다.

* Node.js를 무슨 '웹 서버'(WAS라던가..) 비슷한 것으로 알고 있는 사람이 많은데 이는 사실이 아니다. 이런 오해가 퍼진 것은 Node.js가 웹 분야에서 많이 쓰이고(애초에 자바 스크립트 실행기니까 당연한 일이다), 웹 서버 역할을 하는 자바스크립트 코드를 실행하는 경우가 많기 때문이다. Node.js는 웹 서버가 아니고 그냥 자바 스크립트 코드를 실행하는 인터프리터(실행기) 같은 것이다.

 

NPM

이렇게 환경이 자유로워 지면서, 당연하게도 다양한 프로그램들이 javascript로 개발되었다. 이제 실행환경이 자유로워 졌기 때문에, javascript는 다른 프로그래밍 언어들과 마찬가지로 범용 프로그래밍 언어가 된 것이다. 그리고 이렇게 javascript로 개발된 프로그램들을 손쉽게 다운로드할 수 있는 매니저가 있으니, 그것이 Node.js Package Manager(NPM)이다.

*보통 Node.js를 설치할때 NPM도 같이 설치된다.

NPM은 이름 그대로 Node.js에서 사용할 수 있는 패키지들을 관리해준다. 그런데 Node.js는 앞서 말했다시피 Javascript실행기이다. 따라서 Node.js에서 사용할 수 있는 패키지들이란, 결국 자바스크립트 코드를 의미한다. 결국 NPM을 사용해서 무언가를 install (명령어 : npm install) 하게 되면, 적절한 경로에서 자바스크립트 파일을 다운로드 받게 된다. 이렇게 다운로드 받은 자바스크립트들을 '라이브러리'로 사용해서, 여러분은 여러분의 비즈니스 로직에 집중할 수 있다.

*npm install을 실행하면 해당 명령을 실행한 경로에 node_modules 폴더를 생성하고 그 하위로 다운로드 된다.

 

다른 스크립트 가져와서 실행하기

Node.js를 설치해서 node.exe를 실행해보면, 마치 파이썬처럼 인터프리터가 뜨는것을 확인할 수 있다. 여기에 var a = 3, var b = 4이런식으로 코드를 바로 입력해서 실행할 수 있었다. 즉, Node.js를 통해 자바스크립트가 실행되는 경우 자바스크립트는 일반적인 C++이나 C#, Java 코드처럼 컴파일러에 의해 통째로 빌드되어 바이너리로 변해서 실행되는게 아니라, Node.js라는 '인터프리터'(실행기)를 통해 코드 자체가 한줄 한줄 실행된다.

그렇다면, npm install 명령을 통해 가져온 자바스크립트를 어떻게 실행할 수 있을까? 여러분이 작성하는 javascript 파일의 내용은 결국 위의 이미지와 같은 인터프리터에 한줄한줄 복사되어 실행된다. 그러면 여러분이 직접 작성한 코드에서, 여러분이 npm install을 통해 가져온 스크립트를 실행할 수 있어야 할 것이다. 당연히 그럴 방법이 있다.

결론부터 말하자면, 위와 같이 require 메소드를 통해 실행할 수 있다. 조금 전에 THREE라는 스크립트를 다운로드 받았었다. 이것을 가져다가 사용하기 위해서, const THREE = require('three');라는 코드를 입력해 주었다. 그리고 THREE를 출력해보면 위와 같이 정상적으로 뭔가 객체가 생성된것을 확인할 수 있다.

이렇게 THREE 자바스크립트가 실행 되었기 때문에(그 결과로 THREE라는 객체가 생성되었다), 이제 THREE스크립트에서 제공하는 메소드 등을 여러분의 자바스크립트에서 호출할 수 있다. "THREE.메소드" 이런식으로 접근해서 사용할 수 있을 것이다. 바꿔서 말해보자면, THREE 자바스크립트는 스스로가 실행되면 하나의 THREE인스턴스를 생성해서 반환하도록 작성되어 있어야 할 것이다.

 

모듈과 빌드

위 예시에서 THREE처럼, 다른곳에서도 가져다 사용할 수 있는 자바스크립트를 '모듈'이라고 한다. 즉, 모듈이란 그냥 자바스크립트일 뿐이다. npm을 통해 이런것들을 다운로드 받을 수 있고, 이렇게 모듈(패키지)을 관리해준다는 의미에서 npm이라는 이름이 붙은 것이다.

그런데 위의 이미지를 자세히 보면, 맨 위에서 여러분의 자바스크립트가 a라는 변수를 선언하고 3으로 초기화 한 것을 볼 수 있다. 그런데 만약, 공교롭게도 THREE 모듈에서도 a라는 변수를 사용했다면 어떻게 될까? 당연히 THREE 모듈의 스크립트를 실행하는 도중 "a라는 변수가 이미 존재한다"는 에러가 터질 것이고, 따라서 THREE 스크립트가 끝까지 실행되지 못하기 때문에 THREE객체도 비정상적으로 생성되거나 아예 생성되지 못할 것이다.

위와 같은 문제를 방지하기 위해 THREE 스크립트의 변수 이름들을 다른것과 겹치지 않도록 변환해주는 작업이 필요하다. 예를 들어 변수명 뒤에 GUID를 추가한다면, 어떤 변수와도 겹치지 않을 것이다. 이러한 작업을 자바스크립트의 '빌드'라고 한다. 상술했듯이, javascript는 원래 '빌드'가 따로 필요한 언어가 아니다. 브라우저나 Node.js라는 실행기(인터프리터)를 통해 한줄 한줄 실행되기 때문이다. 하지만 어쨌든 코드를 조작해서 실행가능한(충돌이 없는) 형태로 만든다는 의미에서 빌드라고 부른다.

 

모듈의 구현

상술 했듯이 모듈은 단지 하나의 자바스크립트 파일에 불과하다. 하나의 모듈에서 import 키워드를 통해 다른 모듈을 불러와서 사용할 수 있고, export키워드를 통해 현재 모듈을 다른 모듈에서 사용할 수 있도록 오픈할수 있다. 모듈단위로 변수의 스코프가 존재하며, 하나의 모듈이 여러 곳에서 사용되더라도 최초 호출시 한번만 실행된다.

*위에서는 require메소드를 통해 모듈을 가져왔었다. require는 코드 중간에서도 사용할 수 있지만, import는 모듈의 맨 위에서만 가능하다. 하지만 외부의 모듈을 굳이 실행중에 갑자기 가져올 이유가 없고, 더구나 require는 현재 표준(ES6)이 아니다. 따라서 import를 사용하도록 하자.

*초기의 자바스크립트에는 이렇게 모듈을 사용할 수 있는 import나 export 키워드가 없었다. 지금도 대부분의 웹 브라우저와 Node.js가 모듈을 지원하지만, 인터넷 익스플로러 같은 구시대의 브라우저는 지원하지 않는다. 그래서 모듈을 사용한 자바스크립트를 '모던 자바스크립트'라고 구분해서 부르기도 한다. 하지만 큰 의미는 없다. 오늘날은 모듈이 표준이다.

//testModule.js
export function test(str){
	alert("hello " + str)
}
export const PI = 3.141592

//main.js
import {test, PI} from './testModule.js'

test('Lee'); // --> alert으로 "hello Lee"가 뜬다.
test(PI); // --> alert으로 3.141592가 뜬다.

 

위와 같이 하나의 모듈에서 다른 모듈을 import해서 사용할 수 있다. testMoudle에서는 하나의 메소드와 하나의 상수를 export했고, main.js에서는 그 둘을 import받아서 사용하고 있다.

testModule에서 여러가지를 export하고 있기 때문에, 반드시 가져오는 쪽에서는 이름을 중괄호로 명시해서 가지고 와야 한다. 그리고 이 때 이름이 다르다면 당연히 해당 메소드나 상수를 찾을 수 없으므로 가져올 수 없다. 이런식으로 export하는 것을 named export라고 한다.

하지만 실제로는 이렇게 메소드나 상수만을 사용하지 않고, 하나의 객체 자체를 export하고 이 안에 상수나 메소드들을 포함시키는 경우가 많다. 그러면 사용하는 쪽에서도 해당 객체 하나만 import받으면 내부의 기능을 모두 사용할 수 있기 때문이다.

이런 경우에는 export할 것이 하나밖에 없으므로, default 키워드를 사용할 수 있다. 그리고 이렇게 export된 모듈을 가져올때는 중괄호로 이름을 명시할 필요가 없다. 이렇게 export하는 것을 default export라고 한다.

*객체를 export할때는 default export, 메소드 같은것만 export할때는 named export를 사용한다고 오해하지 말자. 이들은 export 하는 방식을 뜻하는 말이다. 객체도 여러개를 export할때는 named export를 사용해야 하며, 메소드 하나만 export하더라도 default export를 사용할 수 있다.

*심지어, 하나의 모듈에서 named export와 default export를 동시에 사용해도 문제는 없다. 하지만 굳이 그렇게 하지는 말자.

 

이것은 약간 번외의 이야기인데, 이제 javascript를 꼭 브라우저나 Node.js뿐 아니라 그냥 아무 환경에서나 사용할 수 있다. javascript 어셈블리가 등장했기 때문인데, 이는 자바스크립트를 마치 다른 언어들(C++, C#, Java등)처럼 어셈블리어로 빌드해서 실행되도록 한다. 따라서 Javascript로 작성한 코드를 .exe파일로 빌드해서 윈도우에서 실행하는것도 가능하다.

하지만 이렇다고 해서 node.js가 무의미해지는 것은 아니다. node.js를 없애기 위해서는 브라우저 이외에서 실행되는 모든 자바스크립트를 어셈블리로 빌드해서 CPU에 따라 구분해 배포하고, 기존의 시스템들에서 이것을 받아서 분기해가며 사용해야 하는데 당연히 불가능한 일이다. 그렇다고 자바스크립트 자체를 배포하고 이를 각 사용처에서 빌드해서 사용하자는건 웃긴 소리다. node.js는 그냥 배포받은 자바스크립트를 실행할 수 있는데 말이다.

관련글 더보기