지금까지는 터미널에서 node main.js로 코드를 돌렸지만, 웹 페이지를 움직이는 코드는 브라우저가 index.html을 열 때 함께 읽어서 실행합니다. HTML 파일에 <script src="app.js" defer> 태그를 넣어 자바스크립트 파일을 연결하고, 브라우저 개발자 도구 콘솔에서 출력을 확인하는 법을 알아봅니다.
HTML·CSS 과목을 거쳐 왔다면 용어 두 가지를 구분해 두세요.
- HTML·CSS 과목에서
id·class를 부르던 '이름표'와 이 과목에서 변수 이름을 부르는 '이름표'(규칙 3)는 다른 말입니다. 이 단원부터 HTML의id·class는 그대로 **id·class(속성)**라고 부릅니다. - HTML·CSS 과목의 '브라우저 규칙 1~6'과 이 과목의 '규칙 1~8'도 서로 다른 규칙 번호입니다.
1<script src="app.js" defer>와 'use strict'
작업 폴더에 index.html, style.css, app.js 세 파일을 같은 자리에 둡니다. 짧은 길로 왔다면 완성 style.css를 받아 같은 폴더에 둡니다(index.html 받기 · style.css 받기).
index.html의 닫는 </body> 바로 위에 <script> 태그를 넣어 app.js를 연결합니다.
<footer class="footer">
<p id="count">
전체 3개 중 2개 남음
</p>
</footer>
</main>
<script
src="app.js"
defer
></script>
</body>src="app.js": 같은 폴더의app.js파일을 불러옵니다.defer: 브라우저가index.html을 끝까지 읽어 요소 나무(DOM)를 다 만든 뒤에app.js를 실행하게 합니다.defer를 빼먹고<head>위쪽에서 실행하면 아직 요소들이 만들어지기 전이라 아무 요소도 찾지 못합니다.
type="module" 없이 <script src="app.js" defer>로 불러오는 고전 스크립트는 기본적으로 느슨한 모드로 돕니다. 따라서 app.js의 **첫 줄에는 늘 'use strict';**를 적어, 선언하지 않은 변수 대입 같은 실수를 바로 잡게 합니다.
// app.js
'use strict';
console.log('할 일 앱 준비 완료');
Console 탭: 할 일 앱 준비 완료컴퓨터 탐색기(Finder · 파일 탐색기)에서 index.html을 더블클릭하면 브라우저 주소창에 file:///.../index.html이 열립니다. 키보드에서 F12(맥은 Cmd+Option+J)를 눌러 개발자 도구의 Console 탭을 열면 할 일 앱 준비 완료가 찍혀 있습니다.
2왜 브라우저 코드를 node app.js로 돌리면 멈출까?
브라우저는 창(window)과 문서 요소 나무(document) 객체를 기본으로 만들어 주지만, 터미널의 Node에는 화면 문서가 없습니다. 그래서 document를 쓰는 코드를 node app.js로 실행하면 첫 줄에서 바로 ReferenceError가 납니다.
console.log(document);
main.js:1console.log(document); ^ ReferenceError: document is not defined at main.js:1:13반대로 지금까지 1~11단원에서 배운 값, 배열, 객체, 함수, Promise, fetch 문법은 브라우저에서도 똑같이 동작합니다.
3Node와 브라우저가 다른 곳
같은 자바스크립트라도 터미널(Node 모듈)과 브라우저(고전 스크립트)는 여섯 군데가 다릅니다.
| 항목 | Node (main.js 모듈) | 브라우저 (<script src="app.js" defer>) |
|---|---|---|
| 오류가 나면 | 그 자리에서 프로그램 전체가 끝남 | 그 스크립트·그 리스너 한 번만 멈추고 페이지는 산다 (다음 클릭에 리스너가 다시 돎) |
| 잡지 않은 거부 | 거부된 Promise를 안 잡으면 프로세스가 멈춤 | 콘솔에 빨간 경고만 찍히고 타이머와 다른 이벤트 리스너도 계속 돎 |
맨 위 await | 파일 맨 위에서 바로 await 사용 가능 | 고전 스크립트에서는 SyntaxError로 파일 전체가 돌지 않음 (async function 안에서만 사용) |
| 콘솔 입력창 | 파일 단위로만 실행 | 개발자 도구 콘솔창은 느슨한 모드이며 편의상 let 재선언도 허용함 |
| 모듈 안 이름 | 파일 밖에서 접근 불가 | <script type="module"> 안에 선언한 변수는 콘솔창에서 직접 안 보임 |
맨 위 this | undefined | 고전 스크립트 맨 위의 this는 브라우저 창 객체인 window |
특히 고전 스크립트(app.js) 맨 위에서 await fetch(...)를 바로 적으면 SyntaxError(await is only valid...) 오류가 나며 파일 전체가 돌지 않습니다. 그래서 13단원에서 오늘의 한마디를 받아 올 때는 async function loadQuote()로 감싼 뒤 loadQuote();를 부릅니다.
예제
결과를 먼저 생각한 뒤 펼쳐 보세요. 맞혀 보기는 점수에 들지 않습니다.
브라우저에서 버튼 클릭 리스너를 등록해 두었습니다. 첫 번째 클릭 때 리스너 안에서 오류가 나면, 두 번째 클릭 때는 어떻게 될까요?
결과 보기
첫 번째 클릭에서 오류가 나면 그 클릭의 리스너 실행만 멈추고 콘솔에 빨간 오류가 찍힙니다. 하지만 웹 페이지와 리스너 등록은 그대로 살아 있으므로, 버튼을 두 번째로 다시 누르면 리스너가 처음부터 다시 실행됩니다.
자주 틀리는 곳
<head>안에defer없이<script src="app.js">를 넣는다: 브라우저는 HTML을 위에서 아래로 읽다가defer없는<script>를 만나면 그 자리에서 바로 실행합니다. 아직<body>안의 요소들이 만들어지기 전이라 요소를 찾으면 모두null이 나옵니다.- 브라우저용
app.js를 터미널에서node app.js로 실행한다:document나localStorage가 들어 있는 코드는 Node에 없는 브라우저 전용 객체라ReferenceError(document is not defined)가 납니다. 브라우저는index.html을 열어 확인합니다. - 고전 스크립트 맨 위에서
await를 바로 쓴다:<script src="app.js" defer>는 모듈이 아닌 고전 스크립트이므로 맨 위await가 금지되어 있습니다. 반드시async function안에 넣어 부릅니다.
확인 문제
<script src="app.js" defer>에서defer속성이 하는 일을 한 문장으로 쓰세요.
정답
브라우저가 index.html을 끝까지 읽어 요소 나무(DOM)를 다 만든 뒤에 app.js를 실행하게 미뤄 줍니다.
- 터미널에서
node app.js로document.querySelector가 들어 있는 파일을 실행하면 어떤 이름의 오류가 나나요?
정답
ReferenceError(document is not defined)가 납니다.
- 고전 스크립트
app.js에서 서버 데이터를await fetch(url)로 받아 오려고 합니다.SyntaxError가 나지 않게 하려면 코드를 어떤 모양으로 감싸야 하나요?
정답
async function load() 안에 await fetch(url)를 넣고 밖에서 load();를 호출합니다.