JavaScript는 브라우저와 Node.js에서 어떻게 처음 실행하나요?
JavaScript는 브라우저 Console에 입력하거나 파일로 저장해 Node.js에서 실행합니다. 계산 문법은 같지만 document처럼 웹 페이지를 다루는 기능은 브라우저에서 사용합니다.
적용 조건·범위: 같은 JavaScript 문법을 사용해도 사용할 수 있는 API는 실행 환경에 따라 다릅니다.
본문 근거: Node.js 공식 사이트
독립 실습: 첫 파일과 실행 환경 확인
JavaScript 시작 가이드: DevTools와 Node.js로 첫 코드 실행하기 · 독립 실습
console.log로 출력되는 값과 typeof 결과를 확인하세요.
예제 크게 보기 (새 탭)아직 Node.js가 없다면 Node.js 공식 사이트에서 운영체제에 맞는 설치 프로그램을 설치하고 새 터미널에서 node --version으로 실행 파일 인식을 확인합니다. 브라우저 Console 실습에는 별도 설치가 없습니다.
빈 폴더에 hello.mjs를 만들고 아래 전체 코드를 저장하세요. 터미널에서 해당 폴더로 이동한 뒤 node hello.mjs를 실행합니다.
const message = 'JavaScript 준비 완료';
console.log(message);
console.log(2 + 3);
예상 출력은 JavaScript 준비 완료 다음 줄에 5입니다. 숫자 3을 8로 바꾸면 두 번째 출력은 10입니다. 같은 코드를 Console에 직접 입력해 결과가 같은지 확인하고, 출력 위치가 터미널인지 브라우저인지 설명하세요.
브라우저와 Node.js는 실행 환경이 다릅니다
같은 JavaScript 문법을 사용해도 사용할 수 있는 API는 실행 환경에 따라 다릅니다.
브라우저에서는 document, location, fetch처럼 웹 페이지와 연결된 API를 사용할 수 있습니다. Node.js에서는 파일 시스템이나 프로세스처럼 서버·도구 실행에 가까운 API를 사용할 수 있습니다.
JavaScript 언어
├─ 브라우저
│ ├─ document
│ ├─ location
│ └─ fetch
└─ Node.js
├─ process
└─ node:* 모듈
따라서 document.querySelector()가 Node에서 동작하지 않는 것은 JavaScript 문법 오류가 아니라 실행 환경에 해당 API가 없기 때문입니다.

DevTools Console에서 첫 코드 실행
브라우저에서 아무 페이지나 빈 탭을 연 뒤 개발자 도구의 Console 탭을 엽니다. 이 단계에는 TaskFlow 프로젝트가 필요하지 않습니다.
다음 코드를 한 줄씩 실행해봅니다.
const serviceName = 'TaskFlow';
const taskCount = 3;
const isReady = true;
console.log(serviceName);
console.log(taskCount);
console.log(isReady);
console.log()는 현재 값과 실행 순서를 관찰할 때 가장 기본적인 도구입니다. 실제 기능 코드에 무분별하게 남기는 것은 피하지만 학습과 디버깅 단계에서는 적극적으로 사용합니다.
const와 let의 최소 기준
처음에는 다음 기준만 기억하면 충분합니다.
- 다시 다른 값을 대입하지 않을 이름은
const - 반복문 카운터처럼 값 자체를 다시 대입해야 하면
let var는 새 코드의 기본 선택으로 사용하지 않음
const title = 'JavaScript 시작';
let completedCount = 0;
completedCount += 1;
const로 선언한 객체나 배열 내부가 자동으로 읽기 전용이 되는 것은 아닙니다. 객체 참조와 불변 갱신은 5회차에서 별도로 다룹니다.
Node.js에서 첫 파일 실행
브라우저와 별개로 순수 JavaScript 문법을 빠르게 확인하고 싶다면 Node.js를 사용할 수 있습니다.
hello.mjs를 만듭니다.
const message = 'TaskFlow JavaScript 준비 완료';
console.log(message);
터미널에서 실행합니다.
node hello.mjs
다음 문장이 출력되면 됩니다.
TaskFlow JavaScript 준비 완료
이 과정의 브라우저 프로젝트는 .js 파일과 ES Module을 사용하고, Node에서 독립 예제를 실행할 때는 .mjs를 사용해 모듈 해석 차이를 줄입니다.
type="module"을 사용하면 import와 export를 사용할 수 있고 모듈 스크립트는 기본적으로 HTML 파싱 이후 실행됩니다.
브라우저 모듈은 file://로 직접 열었을 때 보안 정책 때문에 막힐 수 있습니다. VS Code의 로컬 서버 기능처럼 http://localhost... 주소로 실행하는 방식을 사용하세요.
자주 발생하는 오류
app.js를 찾지 못합니다
Network 또는 Console에서 404가 보이면 src 경로와 실제 폴더 위치를 비교합니다. HTML이 루트에 있고 app.js가 js 폴더 안에 있다면 ./js/app.js가 맞습니다.
ReferenceError가 발생합니다
선언하지 않은 이름을 사용했는지 확인합니다.
console.log(taskTitle);
taskTitle을 선언하지 않았다면 ReferenceError가 발생합니다.
SyntaxError가 발생합니다
괄호, 따옴표, 쉼표처럼 코드 자체를 해석할 수 없는 문제가 있는지 오류가 가리키는 줄 주변부터 확인합니다.
학습 완료 체크리스트
- 브라우저와 Node.js의 차이를 설명할 수 있습니다.
- DevTools Console에서 JavaScript를 실행할 수 있습니다.
const와let의 기본 기준을 구분할 수 있습니다.node hello.mjs로 파일을 실행할 수 있습니다.- HTML에
type="module"스크립트를 연결할 수 있습니다. - SyntaxError와 ReferenceError를 Console에서 확인할 수 있습니다.
혼자 확인하기: 브라우저와 Node 결과 구분
독자 질문: 같은 JavaScript 코드라도 어느 환경에서 실행했는지 어떻게 확인할까요? TaskFlow 파일 없이 다음 두 실습을 합니다.
- 브라우저의 빈 탭에서 개발자 도구 Console을 열고
console.log('browser', 2 + 3)을 직접 입력합니다. 예상 출력은browser 5입니다. - 앞에서 만든
hello.mjs파일의 메시지를console.log('node', 2 + 3)으로 바꿔 저장하고, 그 파일이 있는 폴더에서node hello.mjs를 실행합니다. 예상 출력은node 5입니다. - 일부러
console.log(missingName)을 각각 입력하면 ReferenceError가 나옵니다. 에러가 나타나는 곳은 브라우저 Console과 터미널로 다르지만 선언하지 않은 이름이라는 원인은 같습니다.
완료 기준: 두 출력을 직접 보여 주고 코드가 실행된 장소를 구분할 수 있습니다. 앞선 웹 퍼블리싱 프로젝트가 없어도 이 단계까지 확인 가능합니다. HTML에 모듈 파일을 연결하는 실습은 로컬 서버가 준비되면 이어서 진행합니다.
독립 실습은 여기서 마칠 수 있습니다. 아래 선택 실습은 TaskFlow 프로젝트 파일로 진행합니다.
선택 실습: 할 일 앱에 적용하기
TaskFlow는 작업을 추가·완료·삭제하는 할 일 앱입니다. 이 선택 실습에서는 JavaScript로 화면에 동작을 붙입니다.
HTML에 module script와 js/app.js를 추가하고 Console·Network에서 첫 실행을 확인합니다.
선택: 연재 프로젝트와 전체 자료
연재 프로젝트를 이어 만드는 분을 위한 자료입니다. 이 글의 개념과 예제만 찾으셨다면 필요한 파일과 실행 방법만 펼쳐보세요.
TaskFlow 프로젝트 적용
JavaScript 시작 가이드: DevTools와 Node.js로 첫 코드 실행하기 · 단계별 TaskFlow
TaskFlow JavaScript 1단계입니다. 기존 서비스 화면과 실제 콘솔 출력을 확인하세요.
예제 크게 보기 (새 탭)변경 설명과 부분 예제를 읽은 뒤, 시작·완료 파일과 적용 순서에서 필요한 선언·import·화면 연결을 포함한 전체 파일을 확인하세요. 이번 단계의 변경 이유와 적용 위치도 함께 확인합니다.
시작본과 이번 변경
웹 퍼블리싱 과정의 최종 publishing-step-15-final 파일을 가지고 있다면 이어서 진행합니다. 상단 첫 실행은 이 파일을 요구하지 않습니다.
기존 index.html의 body 끝에 module script를 한 번 추가하고 js/app.js를 만듭니다. 로컬 HTTP 서버로 열어 Console에서 로드 문구 한 번과 Network에서 app.js 응답을 확인합니다.
연속 프로젝트 선수 조건
- HTML과 CSS의 기본 역할을 알고 있으면 됩니다.
- 웹 퍼블리싱 과정을 따라왔다면
publishing-step-15-final을 시작점으로 사용합니다. - JavaScript 설치 프로그램이 따로 있는 것은 아닙니다. 브라우저에는 JavaScript 실행 엔진이 포함되어 있고, 터미널에서 실행하려면 Node.js 같은 런타임이 필요합니다.
이전 체크포인트
publishing-step-15-final
TaskFlow에는 이미 hero, 기능 카드, 요금표, 문의 폼, FAQ, 반응형·접근성·SEO 구조가 있습니다. 기존 HTML/CSS는 그대로 유지합니다.
이번 변경
js/app.js추가index.html에 모듈 스크립트 연결- 브라우저 Console에서 로드 확인
변경 파일
index.html
js/app.js
전체 파일·코드 보기
index.html 변경 부분
<!-- 기존 footer와 help link는 그대로 유지 -->
<script type="module" src="./js/app.js"></script>
</body>
js/app.js 전체 코드
console.log('TaskFlow JavaScript loaded');
script를 HTML에 연결하기
웹 퍼블리싱 최종 index.html의 </body> 바로 앞에 다음 한 줄을 추가합니다.
<script type="module" src="./js/app.js"></script>
첫 app.js
프로젝트에 js 폴더를 만들고 app.js를 추가합니다.
index.html
style.css
images/
js/
app.js
js/app.js:
console.log('TaskFlow JavaScript loaded');
페이지를 새로고침했을 때 Console에 문장이 한 번만 출력되면 연결에 성공한 것입니다.
이번 회차 변경 코드와 전체본 연결
변경 없는 style.css와 기존 정적 section은 publishing-step-15-final을 그대로 사용합니다.
실행 확인
Console
> TaskFlow JavaScript loaded
같은 문장이 새로고침 한 번에 여러 번 출력된다면 script가 중복으로 들어갔는지 확인합니다.
중간부터 시작하는 전체 파일과 적용 순서
시작 ZIP은 웹 퍼블리싱 publishing-step-15-final의 실제 완료 파일입니다. 프로젝트 순서를 이어가는 기준이며 사이트의 기술 읽기 순서와는 구분합니다.
- 시작 ZIP을 새 폴더에 풉니다. 이 폴더가 작업 루트이며 기존 체크포인트 파일을 별도로 찾을 필요가 없습니다.
- index.html의 body 끝에 모듈 script를 연결하고 js/app.js를 만듭니다.
- 아래 변경 파일을 같은 상대 경로에 추가하거나 전체 교체합니다. 표시하지 않은 파일과 images는 시작본 그대로 유지합니다.
- 작업 루트에서 명령을 실행하고 예상 결과를 확인합니다. 완료 ZIP은 직접 적용한 결과를 비교하는 기준입니다.
추가·교체 파일
index.html— 전체 교체package.json— 추가server.mjs— 추가tests/core.test.mjs— 추가js/app.js— 추가
실행과 확인
npm start
npm test
브라우저 실습에는 개발 서버를 실행 상태로 두세요. npm test는 별도 터미널에서 실행하며 이 단계에서는 서버를 요구하지 않습니다. 2026-10-12에 Node.js 24.18.0과 공개 완료 ZIP으로 아래 검사를 실행했습니다. JavaScript 실습 주소는 http://127.0.0.1:4173입니다.
예상 결과: Console에 TaskFlow JavaScript loaded가 한 번 출력됩니다.
실제 실행 확인(2026-10-12): 공개 완료 ZIP에서 npm test를 실행해 1개 검사가 통과했습니다. 현재 tests/core.test.mjs는 js/app.js의 종료 코드와 TaskFlow JavaScript loaded 로그를 확인한 뒤 종료합니다. 이 결과는 DOM 조작·경계값·실제 브라우저 화면·기기·외부 CDN 검사를 포함하지 않습니다.
파일 대조: 아래에 제공하는 전체 코드 5개는 공개 완료 ZIP의 해당 파일과 줄바꿈(LF/CRLF)을 제외하고 일치합니다. 시작 ZIP에 이 5개를 적용하면 실행 파일과 유지 파일을 재현할 수 있습니다. 완료 ZIP의 README.md·CHANGES.md는 별도 설명 파일이며, 모든 파일이 시작본과 같다는 뜻은 아닙니다. 완료 ZIP의 공통 README에 적힌 DOM/React 검증 문구도 이 첫 단계의 실제 검사 범위로 읽지 않습니다.
이 절은 변경하는 실행 파일 5개의 전체 코드를 제공합니다. 위쪽 원문의 짧은 예제나 생략 표시가 있는 부분 코드는 개념·변경 위치 설명용입니다. 서버 저장과 인증은 제공하지 않으며 JSON fixture는 로컬 읽기 실습용입니다.
복사할 변경 파일 전체 코드
index.html — 전체 파일
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>
TaskFlow - 간단한 작업 정리 서비스
</title>
<link href="./style.css" rel="stylesheet"/>
<meta content="TaskFlow의 주요 기능과 요금제, 문의 방법을 확인할 수 있는 작업 관리 서비스 소개 페이지입니다." name="description"/>
</head>
<body>
<a class="skip-link" href="#main">
본문으로 바로가기
</a>
<header class="site-header">
<div class="page site-header__inner">
<a class="brand" href="#home">
TaskFlow
</a>
<nav aria-label="주요 메뉴" class="nav">
<a href="#features">
기능
</a>
<a href="#pricing">
요금제
</a>
<a href="#contact">
문의
</a>
<a href="#faq">
FAQ
</a>
</nav>
</div>
</header>
<main class="page" id="main" tabindex="-1">
<div class="service-layout">
<div>
<section class="hero" id="home">
<div>
<h1>
해야 할 일을 한눈에 정리하세요
</h1>
<p>
TaskFlow는 오늘의 작업을 빠르게 정리하는 서비스입니다.
</p>
<a class="button-link" href="#contact">
시작 문의하기
</a>
</div>
<img alt="TaskFlow 서비스 소개 페이지의 기능·문의 화면" class="hero__image" height="800" src="./images/hero.webp" width="1200"/>
</section>
<section id="features">
<h2>
주요 기능
</h2>
<div class="feature-grid">
<article class="card feature-card">
<span class="feature-card__badge">
NEW
</span>
<h3>
빠른 작업 추가
</h3>
<p>
해야 할 일을 바로 기록할 수 있습니다.
</p>
<img alt="TaskFlow 기능 카드 세 개" class="feature-card__image" height="450" loading="lazy" src="./images/feature-board.webp" width="800"/>
</article>
<article class="card">
<h3>
간단한 분류
</h3>
<p>
해야 할 일을 구분합니다.
</p>
</article>
<article class="card">
<h3>
진행 확인
</h3>
<p>
완료 여부를 확인합니다.
</p>
</article>
<article class="card task-preview">
<div class="task-preview__content">
<h3>
아주 긴 작업 제목과 URL
</h3>
<p>
https://example.com/very-long-task-url-without-normal-break-points-123456789
</p>
</div>
<a href="#contact">
문의
</a>
</article>
</div>
</section>
<section class="faq" id="faq">
<h2>
자주 묻는 질문
</h2>
<details>
<summary>
TaskFlow는 어떤 서비스인가요?
</summary>
<p>
해야 할 일을 빠르게 정리하는 작업 관리 서비스 예제입니다.
</p>
</details>
<details>
<summary>
모바일에서도 볼 수 있나요?
</summary>
<p>
작은 화면부터 설계한 반응형 레이아웃을 사용합니다.
</p>
</details>
<details>
<summary>
문의는 어디서 하나요?
</summary>
<p>
아래 문의 폼을 이용할 수 있습니다.
</p>
</details>
</section>
<section id="pricing">
<h2>
요금제
</h2>
<div class="pricing-grid">
<article class="card">
<h3>
Basic
</h3>
<p>
개인 학습용
</p>
</article>
<article class="card">
<h3>
Pro
</h3>
<p>
더 많은 프로젝트
</p>
</article>
</div>
<div aria-label="요금제 비교표 가로 스크롤" class="table-scroll" role="region" tabindex="0">
<table class="pricing-table">
<caption>
TaskFlow 요금제 기능 비교
</caption>
<thead>
<tr>
<th scope="col">
항목
</th>
<th scope="col">
Basic
</th>
<th scope="col">
Pro
</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">
프로젝트
</th>
<td>
3개
</td>
<td>
무제한
</td>
</tr>
<tr>
<th scope="row">
작업 보관
</th>
<td>
30일
</td>
<td>
무제한
</td>
</tr>
<tr>
<th scope="row">
팀 공유
</th>
<td>
없음
</td>
<td>
지원
</td>
</tr>
</tbody>
</table>
</div>
</section>
<section id="contact">
<h2>
문의하기
</h2>
<form>
<div class="form-field">
<label for="name">
이름
</label>
<input autocomplete="name" id="name" name="name" type="text"/>
</div>
<div class="form-field">
<label for="email">
이메일
</label>
<input autocomplete="email" id="email" name="email" type="email"/>
</div>
<div class="form-field">
<label for="message">
문의 내용
</label>
<textarea id="message" name="message" rows="5"></textarea>
</div>
<button type="submit">
문의 보내기
</button>
</form>
</section>
<div class="feature-slot">
<article class="card feature-card">
<img alt="TaskFlow 기능 카드 세 개" class="feature-card__image" height="450" loading="lazy" src="./images/feature-board.webp" width="800"/>
<div>
<h3>
작업 카드
</h3>
<p>
넓은 공간에서는 이미지와 설명을 나란히 배치합니다.
</p>
</div>
</article>
</div>
</div>
<aside aria-label="기능 미리보기">
<div class="feature-slot">
<article class="card feature-card">
<img alt="TaskFlow 기능 카드 세 개" class="feature-card__image" height="450" loading="lazy" src="./images/feature-board.webp" width="800"/>
<div>
<h3>
작업 카드
</h3>
<p>
넓은 공간에서는 이미지와 설명을 나란히 배치합니다.
</p>
</div>
</article>
</div>
</aside>
</div>
</main>
<footer class="site-footer">
<div class="page">
<p>
© TaskFlow
</p>
</div>
</footer>
<a class="help-button" href="#contact">
문의
</a>
<script type="module" src="./js/app.js"></script></body>
</html>
package.json — 전체 파일
{
"name": "taskflow-javascript-checkpoints",
"private": true,
"type": "module",
"scripts": {
"start": "node server.mjs",
"test": "node --test tests/*.test.mjs"
}
}
server.mjs — 전체 파일
import { createServer } from 'node:http';
import { readFile } from 'node:fs/promises';
import { resolve, extname, sep } from 'node:path';
const root = process.cwd();
const types = {'.html':'text/html; charset=utf-8','.js':'text/javascript; charset=utf-8','.css':'text/css; charset=utf-8','.json':'application/json; charset=utf-8','.svg':'image/svg+xml'};
createServer(async (req,res) => {
try {
const pathname = decodeURIComponent(new URL(req.url, 'http://localhost').pathname);
const file = resolve(root, '.' + (pathname === '/' ? '/index.html' : pathname));
if (!file.startsWith(root + sep)) { res.writeHead(403).end(); return; }
const data = await readFile(file);
res.writeHead(200, {'Content-Type':types[extname(file)] || 'application/octet-stream'}).end(data);
} catch { res.writeHead(404).end('Not found'); }
}).listen(4173, '127.0.0.1', () => console.log('http://127.0.0.1:4173'));
tests/core.test.mjs — 전체 파일
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { existsSync } from 'node:fs';
import { spawnSync } from 'node:child_process';
test('TaskFlow checkpoint modules', async () => {
if (!existsSync(new URL('../js/task-store.js', import.meta.url))) {
const result=spawnSync(process.execPath,['js/app.js'],{encoding:'utf8'});
assert.equal(result.status,0);assert.match(result.stdout,/TaskFlow JavaScript loaded|전체: 3/);return;
}
const ns = await import('../js/task-store.js');
const store = ns.createTaskStore ? ns.createTaskStore() : ns;
const before = store.getTasks().length;
const a = store.addTask('경계값 확인');
const b = store.addTask('연속 추가');
assert.notEqual(a.id, b.id);
assert.equal(store.getTasks().length, before + 2);
assert.equal(store.toggleTask(a.id).completed, true);
assert.equal(store.toggleTask('missing'), null);
store.deleteTask(a.id); store.deleteTask(b.id);
assert.equal(store.getTasks().length, before);
if (store.getTaskSummary) {
store.getTasks().forEach(task => store.deleteTask(task.id));
assert.deepEqual(store.getTaskSummary(), {total:0,completed:0,pending:0});
}
});
js/app.js — 전체 파일
console.log('TaskFlow JavaScript loaded');
완료 체크포인트
javascript-step-01-runtime
다음 학습
원문에서 안내한 다른 학습 순서 (선택)
다음 글부터 실제 데이터를 다룹니다. TaskFlow 작업 배열을 만들고 조건문과 반복문으로 완료 상태와 개수를 직접 추적합니다.
다음 글: JavaScript 조건문과 반복문: 변수 값의 흐름부터 추적하기
글 소개·학습 준비·전체 목차

JavaScript · 1 / 15 · 필수 길잡이 · DevTools와 Node.js로 첫 코드 실행하기
브라우저에서는 DevTools Console에 JavaScript를 입력하고, Node.js에서는 저장한 .mjs 파일을 터미널에서 실행합니다. 두 환경의 계산 결과는 같아도 document 같은 브라우저 API를 Node.js에서 그대로 사용할 수는 없습니다. 첫 실행과 오류 확인을 별도 예제로 연습합니다.
이번 글에서 배우는 내용
- 브라우저에서 JavaScript가 실행되는 위치
- DevTools Console 사용법
script와type="module"const와let의 최소 기준- Node.js에서
.mjs파일 실행 - SyntaxError와 ReferenceError 확인
- TaskFlow에 첫 JavaScript 파일 연결
먼저 알아야 할 내용
코드 파일을 저장하고 폴더를 찾을 수 있으면 됩니다. 브라우저 실습에 필요한 HTML/CSS 역할은 아래 설명에서 확인합니다.
목차
이 글이 도움이 되었나요?
읽음 기록
읽음 — / 전체 15개 · 필수 14개
새 글 받아보기
RSS 리더에서 BlogFlow의 새 글을 확인할 수 있습니다.