본문으로 건너뛰기

웹 페이지에서 돌리기

자바스크립트 목차웹 페이지 움직이기레슨 65 / 76

목차

지금까지는 터미널에서 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'도 서로 다른 규칙 번호입니다.

4단계 중 1단계. 브라우저는 index.html을 읽어 요소 나무를 만드는 동안 defer가 붙은 app.js를 미리 받아 둡니다.

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"> 안에 선언한 변수는 콘솔창에서 직접 안 보임
맨 위 thisundefined고전 스크립트 맨 위의 this는 브라우저 창 객체인 window

특히 고전 스크립트(app.js) 맨 위에서 await fetch(...)를 바로 적으면 SyntaxError(await is only valid...) 오류가 나며 파일 전체가 돌지 않습니다. 그래서 13단원에서 오늘의 한마디를 받아 올 때는 async function loadQuote()⁠로 감싼 뒤 loadQuote();를 부릅니다.

예제

결과를 먼저 생각한 뒤 펼쳐 보세요. 맞혀 보기는 점수에 들지 않습니다.

브라우저에서 버튼 클릭 리스너를 등록해 두었습니다. 첫 번째 클릭 때 리스너 안에서 오류가 나면, 두 번째 클릭 때는 어떻게 될까요?

결과 보기

첫 번째 클릭에서 오류가 나면 그 클릭의 리스너 실행만 멈추고 콘솔에 빨간 오류가 찍힙니다. 하지만 웹 페이지와 리스너 등록은 그대로 살아 있으므로, 버튼을 두 번째로 다시 누르면 리스너가 처음부터 다시 실행됩니다.

자주 틀리는 곳

  1. <head> 안에 defer 없이 <script src="app.js">⁠를 넣는다: 브라우저는 HTML을 위에서 아래로 읽다가 defer 없는 <script>를 만나면 그 자리에서 바로 실행합니다. 아직 <body> 안의 요소들이 만들어지기 전이라 요소를 찾으면 모두 null이 나옵니다.
  2. 브라우저용 app.js를 터미널에서 node app.js로 실행한다: document나 localStorage가 들어 있는 코드는 Node에 없는 브라우저 전용 객체라 ReferenceError(document is not defined)⁠가 납니다. 브라우저는 index.html을 열어 확인합니다.
  3. 고전 스크립트 맨 위에서 await를 바로 쓴다: <script src="app.js" defer>⁠는 모듈이 아닌 고전 스크립트이므로 맨 위 await가 금지되어 있습니다. 반드시 async function 안에 넣어 부릅니다.

확인 문제

  1. <script src="app.js" defer>⁠에서 defer 속성이 하는 일을 한 문장으로 쓰세요.
정답

브라우저가 index.html을 끝까지 읽어 요소 나무(DOM)를 다 만든 뒤에 app.js를 실행하게 미뤄 줍니다.

  1. 터미널에서 node app.js로 document.querySelector⁠가 들어 있는 파일을 실행하면 어떤 이름의 오류가 나나요?
정답

ReferenceError(document is not defined)⁠가 납니다.

  1. 고전 스크립트 app.js에서 서버 데이터를 await fetch(url)로 받아 오려고 합니다. SyntaxError가 나지 않게 하려면 코드를 어떤 모양으로 감싸야 하나요?
정답

async function load() 안에 await fetch(url)를 넣고 밖에서 load();를 호출합니다.

끝까지 내려오면 저절로 표시됩니다. 이 기기에만 저장합니다.

자바스크립트 목차로