(기본기) 노드 런타임과 브라우저 런타임
같은 자바스크립트인데 실행 환경이 다릅니다. Node.js와 브라우저가 각각 무엇을 제공하는지 정리합니다.
같은 언어, 다른 환경
자바스크립트는 하나의 언어지만, 실행되는 곳이 두 군데입니다.
- 브라우저: 크롬, 파이어폭스 같은 웹 브라우저 안
- Node.js: 서버, 로컬 컴퓨터의 터미널 안
자바로 비유하면 이렇습니다.
자바 코드는 JVM 위에서 돌아갑니다.
자바스크립트 코드는 자바스크립트 엔진 위에서 돌아가는데, 그 엔진을 감싸고 있는 환경이 브라우저일 수도 있고 Node.js일 수도 있습니다.
엔진(V8)은 같은데, 그 위에 어떤 API가 올라가느냐가 다릅니다.
브라우저 런타임
브라우저는 화면을 그리는 환경입니다.
자바스크립트가 브라우저에서 실행되면, 엔진 외에 이런 것들을 쓸 수 있습니다.
| 제공하는 것 | 설명 |
|---|---|
document | HTML 요소를 읽고 조작 (DOM) |
window | 브라우저 창 정보, 전역 객체 |
fetch | 서버에 HTTP 요청 |
localStorage | 브라우저에 데이터 저장 |
alert, confirm | 팝업 대화상자 |
addEventListener | 클릭, 키보드 등 이벤트 감지 |
투두리스트에서 localStorage를 썼던 게 기억나실 겁니다.
그건 브라우저가 제공하는 API이기 때문에 가능한 것이고, Node.js에서는 localStorage가 없습니다.
document.getElementById('app'); // 브라우저에서만 동작
window.innerWidth; // 브라우저에서만 동작
localStorage.getItem('todos'); // 브라우저에서만 동작Node.js 런타임
Node.js는 브라우저 바깥에서 자바스크립트를 실행하는 환경입니다.
2009년에 V8 엔진을 브라우저에서 꺼내서, 서버에서도 돌릴 수 있도록 만든 프로젝트입니다.
자바 진영에서 톰캣이 HTTP 요청을 받아서 자바 코드를 실행하는 것처럼,
Node.js는 HTTP 요청을 받아서 자바스크립트 코드를 실행할 수 있습니다.
| 제공하는 것 | 설명 |
|---|---|
fs | 파일 읽기/쓰기 |
path | 파일 경로 조작 |
http | HTTP 서버 생성 |
process | 실행 중인 프로세스 정보, 환경 변수 |
__dirname | 현재 파일의 디렉토리 경로 |
const fs = require('fs');
const data = fs.readFileSync('./data.json', 'utf-8'); // 파일 읽기
console.log(process.env.NODE_ENV); // 환경 변수브라우저에서는 fs가 없고, Node.js에서는 document가 없습니다.
같은 자바스크립트인데 쓸 수 있는 도구가 다릅니다.
비교 정리
| 브라우저 | Node.js | |
|---|---|---|
| 목적 | 화면을 그리고 사용자와 상호작용 | 서버 실행, 파일 처리, 빌드 도구 |
| 전역 객체 | window | global (또는 globalThis) |
| DOM 접근 | document로 HTML 조작 | 없음 |
| 파일 시스템 | 없음 (보안상 차단) | fs로 파일 읽기/쓰기 |
| 모듈 시스템 | <script> 태그, ES Modules | CommonJS (require), ES Modules |
| 패키지 관리 | 없음 | npm, pnpm, yarn |
개발할 때 두 환경이 섞이는 순간
React 개발을 하다 보면, 코드를 작성하는 곳과 실행되는 곳이 다른 상황이 계속 생깁니다.
Vite 개발 서버
리액트 기초 트랙에서 pnpm dev로 개발 서버를 띄웠습니다.
이 개발 서버는 Node.js에서 돌아갑니다.
pnpm dev
→ Node.js가 Vite 서버를 실행
→ .tsx 파일을 변환해서 브라우저에 전달
→ 브라우저가 받아서 화면을 그림.tsx 파일을 저장하면 화면이 바로 바뀌는 것(HMR)도, Node.js가 파일 변경을 감지해서 브라우저에 알려주기 때문입니다.
npm 스크립트
package.json의 scripts에 들어있는 명령들은 전부 Node.js에서 실행됩니다.
{
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
}
}pnpm build를 실행하면 Node.js가 TypeScript를 컴파일하고, Vite가 번들링해서 순수한 HTML/CSS/JS 파일을 만들어냅니다.
그 결과물이 브라우저에서 실행됩니다.
환경 변수
Node.js에서는 process.env로 환경 변수를 읽습니다.
하지만 브라우저에는 process가 없습니다.
Vite는 이걸 해결하기 위해, .env 파일에서 VITE_로 시작하는 변수만 골라서 빌드 시점에 코드에 주입합니다.
# .env
VITE_API_URL=https://api.example.com
DB_PASSWORD=secret123console.log(import.meta.env.VITE_API_URL); // 빌드 시 값이 치환됨
// DB_PASSWORD는 VITE_ 접두사가 없으므로 브라우저에 노출되지 않음VITE_ 접두사가 없는 변수는 브라우저 번들에 포함되지 않습니다.
DB 비밀번호 같은 것이 프론트엔드 코드에 실수로 들어가는 걸 방지하는 장치입니다.
왜 이걸 알아야 할까
React 코드를 쓰다 보면 이런 에러를 만날 수 있습니다.
ReferenceError: document is not defined이건 Node.js 환경에서 실행된 코드가 document에 접근하려 할 때 나옵니다.
서버 사이드 렌더링(SSR)이나 빌드 시점에 코드가 실행되면 브라우저 API가 없기 때문입니다.
반대로 Node.js의 fs를 브라우저에서 쓰려고 하면 동작하지 않습니다.
이런 문제를 피하려면 지금 이 코드가 어디서 실행되는지를 항상 인식하고 있어야 합니다.
다음에 나올 서버 사이드 렌더링, 서버 컴포넌트 문서를 이해하려면 이 감각이 필수입니다.
직접 해보기
- 브라우저 개발자 도구(F12) 콘솔에서
window,document,localStorage를 입력해보기 - 터미널에서
node를 실행한 뒤process.version,__dirname을 입력해보기 - 두 환경에서
fetch가 되는지 확인해보기 (Node 18+ 부터는 fetch 지원)