JavaScript · 2장 기본
스크립트를 페이지에 넣는 법과 기본 규칙
자바스크립트 파일을 페이지에 연결하는 방법은 간단하지만, 어디에 어떻게 넣느냐에 따라 페이지가 늦게 뜨거나 멈춰요. 스크립트를 넣는 자리, 스크립트와 화면의 관계, 그리고 코드를 쓸 때 늘 따라오는 문장 · 세미콜론 · 엄격 모드 규칙을 정리했어요.
01. 페이지에 스크립트 넣기
HTML 페이지에서 자바스크립트를 실행하려면 <script> 태그를 써요. 코드를 태그 안에 바로 적을 수도 있지만, 실무에서는 거의 항상 별도 파일로 두고 src로 연결해요. 파일로 나눠 두면 여러 페이지가 같은 코드를 쓰고, 브라우저가 한 번 받은 파일을 다시 쓸 수 있어요(캐시).
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<title>할 일 목록</title>
<script src="app.js" defer></script>
</head>
<body>
<ul id="todos"></ul>
</body>
</html>
src가 있는 <script>는 태그 안에 적은 코드를 무시해요. 파일도 연결하고 코드도 쓰고 싶다면 태그를 두 개로 나눠요.
defer를 붙이는 이유
브라우저는 HTML을 위에서부터 읽어 가며 화면을 만들어요. 그런데 defer 없는 <script>를 만나면 읽기를 멈추고, 파일을 내려받아 실행할 때까지 기다려요. 스크립트가 크거나 네트워크가 느리면 그동안 화면이 비어 있어요. 게다가 그 시점에는 아래쪽 HTML이 아직 없어서, 스크립트가 #todos를 찾아도 null이 나와요.
defer를 붙이면 브라우저는 HTML을 계속 읽으면서 파일을 함께 내려받고, HTML을 다 읽은 뒤에 실행해요(그때까지 파일을 다 못 받았으면 마저 받고 실행해요). 그래서 스크립트는 <head>에 두고 defer를 붙이는 게 기본이에요. type="module"로 넣은 스크립트(13장)는 따로 적지 않아도 defer처럼 동작해요.
02. 스크립트가 도는 동안 화면은 기다려요
브라우저 탭 하나에서 자바스크립트 실행, 화면 그리기, 클릭 · 키 입력 처리는 대부분 메인 스레드(main thread)라는 한 줄에서 차례로 일어나요. 보통의 화면은 1초에 60번쯤 새 그림을 그리니까, 한 번에 쓸 수 있는 시간은 약 16.7밀리초(ms)예요. 자바스크립트가 그보다 오래 붙잡고 있으면 그동안 화면은 새로 그려지지 않고 입력도 처리되지 않아요.
아래에서 직접 확인해 보세요. 버튼을 누르면 자바스크립트가 정해진 시간 동안 메인 스레드에서 계산만 해요. 그동안 공 두 개와 입력 칸이 어떻게 되는지 보세요. 입력 칸에 글자를 치면서 버튼을 눌러 보면 더 잘 느껴져요.
위 공은 자바스크립트가 매 프레임 위치를 바꿔서 움직이고, 아래 공은 브라우저에 맡긴 transform 애니메이션으로 움직여요.
막는 동안 위 공은 그 자리에 멈추고, 친 글자는 막힘이 끝난 뒤에 한꺼번에 나타나요. 반면 아래 공은 대부분의 브라우저에서 계속 움직여요. transform · opacity 애니메이션은 브라우저가 메인 스레드와 따로(컴포지터 스레드) 처리할 수 있기 때문이에요.
이 글의 실행 블록은 페이지와 분리된 Web Worker에서 돌아요. 그래서 끝나지 않는 반복을 실행해도 페이지는 멈추지 않고, 1초가 지나면 엔진이 실행을 끊어요. 무거운 계산을 메인 스레드 밖으로 보내는 것도 실무에서 쓰는 방법이에요.
let i = 0;
while (true) {
i++;
}
03. 문장과 세미콜론
자바스크립트 코드는 문장(statement)의 연속이에요. 문장 끝에는 세미콜론 ;을 붙여요. 줄이 바뀌면 엔진이 세미콜론을 알아서 넣어 주기도 하는데(자동 세미콜론 삽입), 이 규칙이 생각과 다르게 동작하는 자리가 두 곳 있어요.
첫째, return 바로 뒤에서 줄을 바꾸면 엔진은 거기서 문장이 끝났다고 봐요. 아래 함수는 객체를 돌려주려 했지만 undefined를 돌려줘요.
function makeUser() {
return
{ name: '하늘' };
}
console.log(makeUser()); // undefined
둘째, 줄이 [나 (로 시작하면 엔진은 앞줄에 이어지는 코드로 읽어요. 아래 코드는 0[1, 2].forEach(…)로 읽혀서 오류가 나요.
let sum = 0
const start = 0
[1, 2].forEach(n => { sum += n })
console.log(sum)
코드에 설명을 남길 때는 주석을 써요. //는 그 줄 끝까지, /* … */는 여러 줄을 감싸요. 주석은 엔진이 무시하고 실행하지 않아요.
04. 엄격 모드: 조용한 실수를 오류로
자바스크립트는 오래된 코드와의 호환 때문에, 실수인데도 오류 없이 넘어가는 동작이 몇 가지 남아 있어요. 파일이나 함수 맨 위에 'use strict';를 적으면 엄격 모드가 켜져서 이런 실수를 오류로 알려 줘요. 대표적인 게 선언하지 않은 변수에 값을 넣는 경우예요.
function setScore() {
scroe = 90; // score라고 쓰려다 오타가 났어요
}
setScore();
console.log(scroe); // 90 — 오타가 새 전역 변수가 됐어요
같은 코드를 엄격 모드로 돌리면 오타가 난 그 자리에서 멈춰요. 버그를 일찍 찾을수록 고치기 쉬워요.
'use strict';
function setScore() {
scroe = 90;
}
setScore();
console.log('여기까지 오지 않아요');
다행히 요즘 코드는 대부분 이미 엄격 모드예요. 모듈(type="module", import · export)과 클래스 본문은 자동으로 엄격 모드로 실행되거든요. 모듈을 쓰지 않는 오래된 스크립트 파일에서만 'use strict';를 직접 적으면 돼요.
참고로 브라우저에는 alert · prompt · confirm이라는 기본 대화 상자도 있어요. 하지만 열려 있는 동안 페이지 전체가 멈추고 모양을 바꿀 수 없어서, 실제 서비스에서는 직접 만든 대화 상자(예: <dialog> 요소)를 써요. 이 노트의 실행 블록에서는 이 함수들이 결과 칸에 기록만 남겨요.
아래 페이지를 열면 콘솔에 TypeError: Cannot read properties of null (reading 'append')가 나요. app.js는 document.querySelector('#todos').append(…)로 목록에 항목을 더하는 코드예요. HTML에서 한 곳만 고쳐 오류를 없애 보세요.
<head>
<script src="app.js"></script>
</head>
<body>
<ul id="todos"></ul>
</body>
해답 보기
defer가 없어서 app.js가 <ul id="todos">를 읽기 전에 실행됐어요. 그 순간에는 요소가 없어서 querySelector가 null을 돌려줬어요. <script src="app.js" defer></script>로 고치면 HTML을 다 읽은 뒤에 실행돼요.
아래 코드는 무엇을 출력할까요? 먼저 예상해 보고 실행해 보세요. 그다음 원래 뜻대로 안녕과 ['사과', '배']가 각각 출력되게 고쳐 보세요.
const greet = '안녕'
console.log(greet)
const fruits = ['사과', '배']
;(function show() { console.log(fruits) })()
const label = '과일'
(function () { console.log(label) })()
해답 보기
안녕과 ['사과', '배']까지는 잘 나오고, 마지막에 TypeError: "과일" is not a function이 나요. 넷째 줄은 앞에 ;를 붙여 두어서 괜찮지만, 마지막 줄은 앞줄의 '과일'에 이어져 '과일'(function …), 즉 문자열을 함수처럼 부른 코드가 됐어요. 다섯째 줄 끝에 ;를 붙이면 과일도 출력돼요.
05. 실전 포인트
- 언제 쓰나요? 스크립트는
<head>에defer를 붙여 넣거나type="module"로 넣어요. Vite 같은 번들러를 쓰는 프로젝트(13장)는 도구가 이 태그를 알아서 만들어 줘요. - 대신 쓰는 것:
defer없는<head>속 스크립트, HTML 속성에 코드를 적는onclick="…"은 피해요. 몇 백 ms씩 걸리는 계산은 잘게 나누거나(6장 · 11장) Web Worker로 보내서 메인 스레드를 비워 둬요. - 세미콜론: 붙이는 스타일이든 빼는 스타일이든, 팀에서 하나로 정하고 Prettier 같은 도구(3장)가 맞추게 해요. 직접 신경 쓰는 것보다 정확해요.
- 흔한 실수:
return다음 줄바꿈, 세미콜론 없이[·(로 시작하는 줄, 그리고defer를 빼먹어 요소를 못 찾는(null) 오류예요.
정리
- 스크립트는 파일로 두고
<script src="…" defer>로 연결해요.defer는 HTML을 다 읽은 뒤에 실행해요. - 자바스크립트와 화면 그리기는 같은 메인 스레드를 써요. 오래 걸리는 코드는 화면과 입력을 멈춰요.
return뒤 줄바꿈,[·(로 시작하는 줄은 자동 세미콜론 삽입의 함정이에요.- 엄격 모드는 조용한 실수를 오류로 바꿔요. 모듈과 클래스는 자동으로 엄격 모드예요.