← 글 목록

notes

20장. strict mode

모던 JS Deep Dive — strict mode의 오류 방지·문법 제한과 적용 기준 정리.

참고: 이웅모 《모던 자바스크립트 Deep Dive》 20장 · [MDN Strict mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode)

선언 없이 x = 10을 쳤는데 콘솔에 값이 찍혔다. “됐다”가 아니라 “전역에 구멍이 났다”였다. strict mode는 그 구멍을 일찍 에러로 바꿔 준다. 문법을 예쁘게 만드는 옵션이 아니라, 엔진이 관대해지던 길을 닫는 스위치다.

보강: ES 모듈·class 본문은 이미 strict. 모던 툴체인(번들러·"type":"module")을 쓰면 파일 단위로 이미 켜져 있는 경우가 많다. 그래도 레거시 스크립트·CDN·옛 문서를 만질 때는 여전히 이 장의 지도가 필요하다.

flowchart TB
  MISTAKE[선언 없는 할당 등] --> S{모드?}
  S -->|sloppy| QUIET[전역 오염·조용한 버그]
  S -->|strict| ERR[ReferenceError 등 조기 실패]
  MOD[ES Module / class 본문] --> STRICT[자동 strict]
  LEG[레거시 script] --> CHOICE["'use strict' 또는 슬롭"]

20.1 strict mode란?

슬롭 모드에서 함수 안에 x = 10만 쓰면, 엔진은 스코프 체인으로 x를 찾다 전역까지 올라간 뒤 암묵적 전역을 만든다. 선언을 깜빡한 건데 실행은 “성공”한다. 나중에 다른 파일이 같은 이름을 쓰면 충돌한다.

strict는 같은 코드를 ReferenceError로 끊는다. “관대함”을 포기하고 예측 가능성을 산다. ESLint 같은 정적 분석과 겹치는 영역이 많지만, 런타임 규칙이라 번들·동적 코드·평가된 문자열까지 영향을 준다.

20.2 어디에 어떻게 켜나

  • 스크립트 맨 앞에 'use strict'; → 그 스크립트 전체
  • 함수 몸체 맨 앞에 두면 → 그 함수와 중첩 함수

문자열 리터럴이어야 하고, 그 앞에 실행문이 있으면 무시되기 쉽다. 주석·빈 줄은 괜찮지만, "use strict"를 중간에 끼우면 기대한 범위가 안 열린다.

'use strict';

function foo() {
  // x = 10; // ReferenceError: x is not defined
}

function bar() {
  'use strict';
  // 이 함수(+중첩)만 strict
}

WHY: 범위를 명시하지 않으면 “이 파일이 슬롭인지 strict인지”가 팀마다 달라진다.

20.3 전역에 막 켜지 말자

<script> 태그마다 별도 스크립트다. A 스크립트에만 'use strict'를 넣으면 B 스크립트는 슬롭일 수 있다. 서드파티가 슬롭을 전제로 짜였다면 전역 strict가 깨뜨린다.

실무 패턴: 즉시 실행 함수로 한 칸을 만들고 그 선두에 strict를 건다. 또는 그냥 모듈로 옮긴다.

(function () {
  'use strict';
  // 이 IIFE 안만 strict. 바깥 슬롭 라이브러리와 격리
})();

20.4 함수마다 켜고 끄는 것도 피하자

어떤 함수만 strict, 어떤 함수는 슬롭이면 this·arguments·암묵 전역 규칙이 파일 안에서 갈린다. 디버깅 지옥이다. “전부 또는 모듈”이 기본선이다.

20.5 strict가 터뜨리는 대표 에러

20.5.1 암묵적 전역

(function () {
  'use strict';
  // undeclared = 1; // ReferenceError
})();

선언(let/const/var) 없이 식별자에 쓰면 바로 실패. 14장의 “로비 게시판” 사고를 런타임에서 차단한다.

20.5.2 변수·함수·매개변수 delete

delete는 객체 프로퍼티용이다. 변수/함수/매개변수에 delete를 치면 strict에서 SyntaxError.

(function () {
  'use strict';
  var x = 1;
  // delete x; // SyntaxError
  function foo(a) {
    // delete a; // SyntaxError
  }
  // delete foo; // SyntaxError
})();

20.5.3 매개변수 이름 중복

(function () {
  'use strict';
  // function foo(x, x) { return x + x; } // SyntaxError
})();

슬롭에선 조용히 마지막 쪽이 이기고, strict에선 문법 에러. 실수 은닉을 끊는다.

20.5.4 with 문

with (obj) { ... }는 스코프 체인에 객체를 끼워 넣어 식별자 해석을 흐리게 한다. 성능·가독성 모두 나쁘고, strict에선 SyntaxError. 쓰지 않는 게 답이다.

20.6 에러는 안 나도 바뀌는 동작

20.6.1 일반 함수의 this

strict에서 foo()처럼 일반 호출하면 this는 undefined다. 슬롭에선 전역 객체(브라우저 window)로 붙던 길이다. 생성자 new Foo()는 여전히 새 인스턴스.

(function () {
  'use strict';
  function foo() {
    console.log(this); // undefined
  }
  foo();

  function Foo() {
    console.log(this instanceof Foo); // true
  }
  new Foo();
})();

WHY: “메서드가 아닌데 this를 쓴다”가 전역 오염으로 안 가고, 바로 드러난다. 22장 this와 직결.

20.6.2 arguments와 매개변수의 연결 끊김

슬롭에선 매개변수에 재할당하면 arguments 쪽도 같이 바뀌는 식이었다. strict에선 매개변수 재할당이 arguments 스냅샷을 따라가지 않는다.

(function (a) {
  'use strict';
  a = 2;
  console.log(arguments[0]); // 1 — 전달값 유지
})(1);

모던 코드는 rest (...args)·명시 파라미터를 쓰고 arguments 의존을 줄인다(18장).

표로 한눈에

예제 — 왜 이렇게 쳐 봤냐

// 레거시 CDN과 내 코드 격리
(function () {
  'use strict';
  const config = { debug: true };
  function boot() {
    // config.dbg = true; // 오타면 ReferenceError 대신 프로퍼티 추가 — 별 이슈
    // dbg = true; // ReferenceError — 암묵 전역 차단
  }
  boot();
})();

WHY: IIFE + strict는 “전역에 켜기”와 “함수마다 켜기” 사이의 실무 타협이었다. 지금은 ESM이 더 깔끔하다.

// 모듈은 파일 전체가 strict
// // math.mjs
export function add(a, b) {
  // this는 모듈 평가 맥락에서 undefined에 가깝게 동작하는 경우가 일반적
  return a + b;
}

헷갈리던 장면 / 함정

  • “strict 켰으니 안전” → 논리 버그·비동기 레이스는 그대로다. 문법·바인딩만 빡세진다.
  • HTML에 스크립트 여러 개 + 일부만 strict → 혼종 상태가 더 무섭다.
  • 슬롭 라이브러리를 전역 strict 페이지에 그대로 꽂기
  • this가 undefined인 줄 모르고 this.x = … → TypeError (조기 발견이긴 함)
  • 클래스/모듈만 쓰면서도 옛 튜토리얼의 슬롭 예제를 복붙

질문 → 자답

Q. 지금 프로젝트에 'use strict'를 일일이 넣어야 하나? A. ESM이면 이미 켜져 있다. CommonJS/레거시 번들이면 번들 설정·린트로 통일. 파일마다 손대는 건 최후.

Q. ESLint랑 뭐가 다르냐? A. 린트는 작성 시, strict는 실행 시. 동적 eval/옛 엔진 경로까지 막으려면 런타임 규칙이 남는다.

Q. 화살표 함수와 strict this? A. 화살표는 자기 this가 없다(렉시컬). strict의 “일반 호출 this=undefined”는 일반 함수 이야기다.

웹 보강 메모

MDN Strict mode 목록(옥탈 리터럴, arguments.callee 등)을 훑고, 우리 레포에서 arguments·with·암묵 전역 검색을 돌렸다. 빌드가 모듈이면 “이미 켜짐”을 전제로 문서에 한 줄만 남겼다.

실무로 바로

  • 새 파일은 ESM 또는 번들러 strict 전제
  • 레거시 진입점만 IIFE+'use strict'로 감싸기
  • PR 체크: 선언 없는 할당, with, 중복 파라미터
  • this를 쓰는 함수는 메서드/바인딩/new 경로를 테스트에 넣기

여기서 잠깐 💁‍♂️

다음으로 이어지는 감각: 21장 빌트인·전역 객체, 22장 this. strict는 “전역과 this가 어떻게 새는지”를 막는 안전벨트다.