strict mode가 실행 컨텍스트에 미치는 영향은?

strict mode 활성화 시 실행 컨텍스트의 동작 방식이 어떻게 달라지며 this 바인딩과 변수 선언 규칙이 어떻게 엄격해지는지 학습합니다

중급 15분 strict mode 엄격 모드 this 바인딩 규칙

JavaScript의 strict mode는 실행 컨텍스트의 동작 방식을 근본적으로 변화시키는 중요한 메커니즘입니다. ‘use strict’ 선언을 통해 활성화되는 이 모드는 실행 컨텍스트 생성 시 더 엄격한 규칙을 적용하여 잠재적인 오류를 사전에 방지합니다. 특히 this 바인딩, 변수 선언, 함수 호출 등 실행 컨텍스트의 핵심 요소들이 strict mode 하에서 어떻게 달라지는지 이해하는 것은 현대 JavaScript 개발에서 필수적입니다. strict mode는 단순히 문법을 엄격하게 검사하는 것을 넘어 실행 컨텍스트의 생성과 관리 방식 자체를 변경하므로, 이로 인한 동작 차이를 정확히 파악해야 합니다.

🔒 strict mode의 핵심 영향

  • this 바인딩 변화: 전역 함수 호출 시 this가 전역 객체 대신 undefined로 바인딩되어 예기치 않은 전역 변수 오염을 방지합니다
  • 암묵적 전역 변수 금지: 선언하지 않은 변수에 값을 할당하면 즉시 ReferenceError가 발생하여 변수 선언 누락을 즉각 감지합니다
  • 함수 실행 컨텍스트 강화: 중복 매개변수, 읽기 전용 속성 할당, 삭제 불가 속성 삭제 등이 모두 에러로 처리되어 안전성이 크게 향상됩니다
  • eval 격리 강화: eval 함수가 자체 실행 컨텍스트에서 완전히 격리되어 실행되므로 외부 스코프 오염 위험이 제거됩니다
  • with 문 금지: 스코프 체인을 동적으로 변경하는 위험한 with 문이 완전히 금지되어 실행 컨텍스트의 예측 가능성이 보장됩니다

💼 실무에서의 영향

strict mode는 현대 JavaScript 개발 환경에서 사실상 표준으로 자리잡았습니다. React, Vue, Angular 등 주요 프레임워크의 빌드 도구들은 기본적으로 strict mode를 활성화하며, ES6 모듈 시스템은 자동으로 strict mode로 실행됩니다. 이는 실행 컨텍스트 생성 시 더 안전하고 예측 가능한 동작을 보장하여 런타임 에러를 개발 단계에서 조기에 발견할 수 있게 합니다. 특히 대규모 프로젝트에서 여러 개발자가 협업할 때 strict mode는 암묵적 전역 변수나 잘못된 this 바인딩 같은 실수를 즉시 차단하여 코드 품질을 크게 향상시킵니다. 또한 strict mode 하에서는 JavaScript 엔진이 더 공격적인 최적화를 수행할 수 있어 실행 성능도 개선됩니다. 레거시 코드를 유지보수하거나 strict mode와 non-strict mode가 혼재된 환경에서 작업할 때는 각 실행 컨텍스트가 어떤 모드로 동작하는지 정확히 파악하는 것이 디버깅의 핵심입니다.


핵심 개념

strict mode의 this 바인딩 변화

입문

strict mode에서는 함수를 호출할 때 this가 가리키는 대상이 달라져요. 이로 인해 예상치 못한 실수를 막을 수 있습니다.

🎯 일반 모드에서의 this는 어떻게 동작하나요? 일반 모드에서 함수를 그냥 호출하면 this는 자동으로 ‘전체 공간’(window 또는 global)을 가리켜요. 마치 편지에 받는 사람을 적지 않으면 자동으로 ‘우체국장님’께 배달되는 것과 비슷해요.

🔒 strict mode에서는 무엇이 달라지나요? strict mode에서는 받는 사람을 명확히 적지 않으면 편지가 ‘수신인 없음’으로 반송돼요. 즉, this가 자동으로 전체 공간을 가리키지 않고 undefined(정의되지 않음)가 됩니다.

💡 왜 이게 좋은가요? 만약 여러분이 실수로 전체 공간에 무언가를 쓰려고 했다면 큰일이에요! 다른 프로그램들도 쓰는 공간이니까요. strict mode는 이런 실수를 바로 알려줘서 문제를 미리 막아줍니다.

🚨 실제로 어떤 차이가 생기나요? 일반 모드에서는 조용히 잘못된 곳에 값을 쓸 수 있지만, strict mode에서는 “잠깐! 여기에 쓸 수 없어요!”라고 즉시 에러를 발생시켜요. 마치 금지구역에 들어가려 할 때 경비원이 막아주는 것과 같아요.

중급

strict mode는 함수 호출 시 this 바인딩 규칙을 근본적으로 변경합니다. 일반 모드(sloppy mode)에서는 암묵적으로 전역 객체를 바인딩하지만, strict mode에서는 undefined로 유지됩니다.

일반 모드의 암묵적 전역 바인딩 일반 모드에서 함수를 단독으로 호출하면 this는 자동으로 전역 객체(브라우저: window, Node.js: global)를 가리킵니다. 이는 의도하지 않은 전역 변수 오염을 일으킬 수 있습니다.

// 일반 모드
function normalMode() {
  console.log(this); // window (브라우저) 또는 global (Node.js)
}
normalMode();

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

실행 컨텍스트 생성 시 차이 함수 실행 컨텍스트 생성 단계에서 strict mode 여부에 따라 ThisBinding 컴포넌트의 값이 달라집니다. strict mode에서는 호출 방식에 따라 this가 명시적으로 전달된 값 또는 undefined가 되므로 예측 가능성이 높아집니다.

'use strict';
const obj = {
  method: function() {
    console.log(this); // obj (메서드로 호출되었으므로)
  }
};

obj.method(); // obj
const fn = obj.method;
fn(); // undefined (단독 함수로 호출되었으므로)

심화

strict mode의 this 바인딩 메커니즘은 ECMAScript 명세의 Function Call 알고리즘(Abstract Operation)에서 정의된 ThisBinding 결정 규칙을 통해 구현됩니다.

ECMAScript 명세 기반 ThisBinding 메커니즘 ECMAScript 2023 Section 10.2.1.1 (PrepareForOrdinaryCall)에 따르면, 함수 실행 컨텍스트 생성 시 ThisBinding은 다음과 같이 결정됩니다:

  1. 일반 모드: thisArgument가 undefined 또는 null이면 전역 객체로 강제 변환(coercion)
  2. strict mode: thisArgument를 그대로 사용 (undefined, null 포함)

이는 10.2.1.2 (OrdinaryCallBindThis)에서 구체적으로 명시됩니다. strict mode 플래그는 함수 객체의 내부 슬롯 [[Strict]]에 저장되며, 실행 컨텍스트의 코드 평가 기록(Code Evaluation State)에 영향을 미칩니다.

V8 엔진의 strict mode This 최적화 V8 엔진은 strict mode 함수에 대해 TurboFan 최적화 컴파일러에서 특별한 처리를 수행합니다:

ThisBinding 특수화(Specialization): strict mode에서는 this가 undefined일 가능성이 높으므로, 컴파일러가 this 체크 분기를 제거하고 직접 undefined를 사용하는 코드로 최적화합니다. 이는 분기 예측(Branch Prediction) 비용을 줄입니다.

Hidden Class 안정성: 일반 모드에서는 this가 전역 객체로 강제 변환되면서 타입 변환 오버헤드가 발생합니다. strict mode는 이를 제거하여 Hidden Class 전환이 없어지고 Inline Cache 히트율이 향상됩니다(벤치마크: 약 3-5% 성능 개선).

보안 측면의 의미: strict mode의 this 격리는 단순한 버그 방지를 넘어 보안 샌드박싱(Security Sandboxing)의 핵심입니다. 제3자 코드가 전역 객체에 접근하지 못하도록 차단하여 XSS(Cross-Site Scripting) 공격 벡터를 감소시킵니다.

암묵적 전역 변수 방지

입문

strict mode는 변수를 선언하지 않고 사용하려는 실수를 즉시 잡아줘요. 이는 프로그램의 안전을 크게 높여줍니다.

📦 변수 선언이 뭔가요? 변수 선언은 ‘이름표 붙인 상자’를 만드는 것이에요. “이 상자에는 숫자를 담을 거야”라고 미리 알려주는 거죠. JavaScript에서는 let, const, var 같은 키워드로 선언합니다.

🚨 선언 없이 사용하면 어떻게 되나요? 일반 모드에서는 선언하지 않고 값을 넣으면 JavaScript가 자동으로 ‘전체 공간’에 상자를 만들어버려요. 마치 여러분의 물건을 공동 창고에 무단으로 놓는 것과 같아요. 다른 사람 물건과 섞일 수 있어요!

🔒 strict mode는 어떻게 막나요? strict mode에서는 선언하지 않은 변수에 값을 넣으려고 하면 즉시 “안 돼요! 먼저 선언하세요!”라고 에러를 발생시켜요. 마치 출입증 없이 건물에 들어가려 할 때 경비실에서 막는 것과 같아요.

💡 왜 이게 중요한가요? 만약 다른 프로그램이 똑같은 이름의 변수를 쓰고 있다면? 여러분의 값이 그 프로그램을 망가뜨릴 수 있어요. strict mode는 이런 충돌을 미리 막아서 안전하게 프로그래밍할 수 있게 해줍니다.

✅ 어떻게 해야 하나요? 항상 변수를 사용하기 전에 let이나 const로 선언하세요. 그러면 여러분만의 ‘개인 상자’가 만들어져서 다른 곳과 섞이지 않아요!

중급

strict mode는 선언되지 않은 변수에 대한 할당을 ReferenceError로 처리하여 암묵적 전역 변수(implicit global variable) 생성을 방지합니다.

일반 모드의 암묵적 전역 변수 생성 일반 모드에서는 선언하지 않은 식별자에 값을 할당하면 JavaScript 엔진이 자동으로 전역 객체의 프로퍼티로 생성합니다. 이는 스코프 체인을 따라 식별자를 찾지 못했을 때 발생하는 “조용한 실패(silent failure)“입니다.

// 일반 모드
function createGlobal() {
  undeclaredVar = 100; // 선언 없이 할당
  console.log(window.undeclaredVar); // 100 (전역 객체에 생성됨)
}
createGlobal();
'use strict';
function preventGlobal() {
  undeclaredVar = 100; // ReferenceError: undeclaredVar is not defined
}
preventGlobal();

실행 컨텍스트 변수 환경 탐색 strict mode에서는 실행 컨텍스트의 Lexical Environment를 따라 스코프 체인을 탐색한 후, 식별자를 찾지 못하면 전역 객체 폴백(fallback) 없이 즉시 ReferenceError를 발생시킵니다.

'use strict';
function correctWay() {
  let declaredVar = 100; // 명시적 선언
  console.log(declaredVar); // 100
}
correctWay();

심화

암묵적 전역 변수 방지는 ECMAScript 명세의 PutValue 추상 연산(Abstract Operation)과 Reference 타입의 strict reference 플래그를 통해 구현됩니다.

ECMAScript 명세 기반 식별자 해석 메커니즘 ECMAScript 2023 Section 6.2.5 (The Reference Record Specification Type)에 따르면, 식별자 참조는 다음 필드를 가진 Reference Record로 표현됩니다:

  • [[Base]]: 참조의 기반 값 (Environment Record 또는 Object)
  • [[ReferencedName]]: 식별자 이름
  • [[Strict]]: strict mode 여부 (boolean)

Section 6.2.5.5 (PutValue)에서는 Reference의 [[Base]]가 unresolvable일 때:

  • 일반 모드: 전역 객체에 새 프로퍼티 생성 (CreateDataProperty)
  • strict mode: ReferenceError 발생

이는 실행 컨텍스트의 스코프 체인 탐색 실패 시 동작을 명확히 구분합니다.

V8 엔진의 strict mode 식별자 최적화 V8 엔진은 strict mode 함수에 대해 컴파일 타임 분석을 통해 최적화를 수행합니다:

정적 분석(Static Analysis): Ignition 인터프리터가 바이트코드 생성 시 선언되지 않은 식별자 사용을 감지하면, 즉시 컴파일 에러로 처리합니다. 이는 런타임 체크 비용을 제거합니다.

Lexical Scope 폐쇄성(Closure): strict mode에서는 전역 객체 폴백이 없으므로, 스코프 체인 탐색이 현재 함수의 스코프 체인 길이로 제한됩니다. 이는 최대 탐색 깊이를 줄여 O(n)에서 O(k)로 개선합니다 (k: 함수 중첩 깊이).

메모리 안전성: 일반 모드의 암묵적 전역 변수는 전역 객체를 오염시켜 메모리 누수(Memory Leak)를 유발할 수 있습니다. strict mode는 이를 차단하여 가비지 컬렉션(Garbage Collection) 효율성을 향상시킵니다.

타입 시스템 통합: TypeScript 등 정적 타입 시스템은 strict mode를 전제로 설계되었습니다. 암묵적 전역 변수 방지는 타입 추론(Type Inference)의 정확성을 보장하는 핵심 메커니즘입니다.

함수 매개변수와 객체 조작 제한

입문

strict mode는 함수의 입력값과 객체를 다룰 때 더 엄격한 규칙을 적용해서 실수를 방지해요.

🎁 함수 매개변수가 뭔가요? 함수 매개변수는 함수에 전달하는 ‘입력값’이에요. 마치 자판기에 동전을 넣는 것처럼, 함수에 정보를 넣어주는 거죠.

🚫 중복된 이름은 왜 문제인가요? 일반 모드에서는 함수가 같은 이름의 입력값을 여러 개 받을 수 있어요. 예를 들어 “사과, 사과, 바나나”처럼요. 그러면 어느 사과를 써야 할지 헷갈리겠죠? strict mode는 “같은 이름 두 번 쓰지 마세요!”라고 에러를 냅니다.

🔐 읽기 전용 속성이란? 어떤 객체의 속성은 ‘읽기만 가능’으로 설정되어 있어요. 마치 박물관 유리 케이스 안의 유물처럼 보기만 하고 만지면 안 되는 거죠. 일반 모드에서는 이를 바꾸려 해도 조용히 무시하지만, strict mode는 “건드리지 마세요!”라고 에러를 발생시켜요.

🗑️ 삭제할 수 없는 것들 어떤 속성은 ‘삭제 불가’로 만들어져요. 마치 건물의 기둥처럼 없애면 안 되는 것들이죠. strict mode는 이런 것을 지우려고 하면 즉시 막아줍니다.

✨ 왜 이렇게 엄격한가요? 이런 규칙들은 프로그램의 중요한 부분을 실수로 망가뜨리는 것을 막아줘요. 마치 중요한 문서에 실수로 커피를 쏟지 않도록 덮개를 씌우는 것과 같아요.

중급

strict mode는 함수 매개변수와 객체 프로퍼티 조작에 대해 추가적인 제약을 가하여 프로그래밍 실수를 방지합니다.

중복 매개변수 금지 일반 모드에서는 같은 이름의 매개변수를 여러 번 선언할 수 있지만, strict mode에서는 SyntaxError가 발생합니다.

// 일반 모드
function duplicate(a, a, b) {
  console.log(a); // 두 번째 a 값 (마지막 값이 우선)
}
duplicate(1, 2, 3); // 2

// strict mode
'use strict';
function noDuplicate(a, a, b) { // SyntaxError: Duplicate parameter name not allowed
  console.log(a);
}

읽기 전용 프로퍼티 할당 방지 Object.defineProperty로 writable: false로 설정된 프로퍼티에 값을 할당하려 하면 TypeError가 발생합니다.

'use strict';
const obj = {};
Object.defineProperty(obj, 'readOnly', {
  value: 42,
  writable: false
});

obj.readOnly = 100; // TypeError: Cannot assign to read only property

삭제 불가 프로퍼티 삭제 방지 configurable: false로 설정된 프로퍼티를 삭제하려 하면 TypeError가 발생합니다.

'use strict';
const obj = {};
Object.defineProperty(obj, 'permanent', {
  value: 42,
  configurable: false
});

delete obj.permanent; // TypeError: Cannot delete property

심화

함수 매개변수와 객체 조작 제한은 ECMAScript 명세의 Property Descriptor 메커니즘과 Declarative Environment Record의 Immutable Binding을 통해 구현됩니다.

ECMAScript 명세 기반 프로퍼티 조작 제어 ECMAScript 2023 Section 6.2.6 (The Property Descriptor Specification Type)은 프로퍼티 속성을 다음과 같이 정의합니다:

  • [[Writable]]: 값 변경 가능 여부
  • [[Configurable]]: 속성 변경 및 삭제 가능 여부
  • [[Enumerable]]: 열거 가능 여부

Section 9.1.9 (Set)에서는 프로퍼티 할당 시 strict mode 플래그에 따라 동작이 달라집니다:

  • [[Writable]] = false이고 strict mode → TypeError
  • [[Writable]] = false이고 일반 모드 → 조용히 실패 (silent failure)

중복 매개변수의 Environment Record 충돌 Section 10.2.1.1 (PrepareForOrdinaryCall)에서 함수 실행 컨텍스트 생성 시, 중복 매개변수는 동일한 식별자에 대해 여러 바인딩을 시도합니다. strict mode는 파싱 단계에서 이를 감지하여 SyntaxError를 발생시킵니다 (Section 15.2.1 Static Semantics: Early Errors).

V8 엔진의 프로퍼티 속성 최적화 V8 엔진은 프로퍼티 속성을 Elements Kind와 Properties Backing Store로 관리합니다:

Fast Properties vs Slow Properties: writable/configurable이 false인 프로퍼티는 Fast Properties (in-object storage)에서 Slow Properties (dictionary mode)로 전환됩니다. strict mode의 조기 에러 발생은 이러한 전환 비용을 줄입니다.

Inline Cache 무효화: 프로퍼티 속성 변경은 Inline Cache를 무효화하여 성능 저하를 유발합니다. strict mode는 불법 조작을 사전에 차단하여 IC 안정성을 유지합니다.

보안 측면: Frozen Objects (Object.freeze)와 Sealed Objects (Object.seal)는 strict mode와 결합하여 불변성(Immutability)을 보장합니다. 이는 보안 민감 데이터 보호와 함수형 프로그래밍의 핵심 메커니즘입니다.

eval과 with의 스코프 격리

입문

strict mode는 eval과 with라는 특별한 기능들이 주변 환경을 마음대로 바꾸지 못하도록 막아요.

🎭 eval이 뭔가요? eval은 문자열로 된 코드를 실행할 수 있는 특별한 함수예요. 마치 종이에 적힌 마법 주문을 읽으면 실제로 마법이 일어나는 것과 같아요. 하지만 이게 위험할 수 있어요!

🌪️ eval이 왜 위험한가요? 일반 모드에서 eval은 주변의 변수들을 마음대로 만들고 바꿀 수 있어요. 마치 손님이 우리 집에 와서 가구 배치를 마음대로 바꾸는 것과 같아요. 나중에 찾으려던 물건이 어디 갔는지 모르게 되죠!

🔒 strict mode는 어떻게 막나요? strict mode의 eval은 자기만의 ‘방’에서 실행돼요. 그 방 안에서 무슨 일이 일어나든 밖의 우리 집에는 영향을 주지 않아요. 마치 방문객에게 게스트룸을 주는 것과 같아요.

🚫 with는 뭐고 왜 금지되나요? with는 객체의 속성을 마치 변수인 것처럼 쓸 수 있게 해주는 기능이에요. 편리해 보이지만 실제로는 엄청 혼란스러워요. 이 변수가 어디서 온 건지 알 수가 없거든요! 그래서 strict mode는 아예 with를 쓰지 못하게 막아버려요.

💡 왜 이렇게 제한하나요? 프로그램이 예측 가능하게 동작하려면 각 코드가 어디에 영향을 주는지 명확해야 해요. eval과 with는 이걸 애매하게 만들어서 버그를 찾기 어렵게 만들어요. strict mode는 이런 불확실성을 제거해줍니다.

중급

strict mode는 eval 함수와 with 문에 대해 스코프 격리를 강제하여 실행 컨텍스트의 예측 가능성을 보장합니다.

일반 모드의 eval 스코프 오염 일반 모드에서 eval은 호출된 스코프의 Environment Record를 직접 수정할 수 있어 외부 변수에 영향을 줍니다.

// 일반 모드
function normalEval() {
  eval('var x = 10');
  console.log(x); // 10 - eval이 외부 스코프에 변수 생성
}
normalEval();
'use strict';
function strictEval() {
  eval('var x = 10');
  console.log(x); // ReferenceError - eval의 변수는 격리됨
}
strictEval();

with 문 완전 금지 strict mode에서는 with 문이 SyntaxError를 발생시킵니다. with는 스코프 체인을 동적으로 변경하여 성능과 예측 가능성을 저하시킵니다.

'use strict';
const obj = { x: 10 };
with (obj) { // SyntaxError: Strict mode code may not include a with statement
  console.log(x);
}

실행 컨텍스트 스코프 체인 보호 strict mode에서 eval은 자체 Lexical Environment를 생성하여 외부 스코프와 완전히 격리됩니다. 이는 실행 컨텍스트의 Environment Record가 오염되지 않음을 보장합니다.

심화

eval과 with의 스코프 격리는 ECMAScript 명세의 Direct Eval과 Lexical Environment 동적 확장 메커니즘을 통해 구현됩니다.

ECMAScript 명세 기반 eval 스코프 메커니즘 ECMAScript 2023 Section 19.2.1 (eval)은 Direct Eval과 Indirect Eval을 구분합니다:

Direct Eval: eval(code) 형태로 직접 호출

  • 일반 모드: 호출자의 Lexical Environment에 직접 바인딩 생성
  • strict mode: 새로운 Declarative Environment Record 생성 (Section 19.2.1.3)

Indirect Eval: (0, eval)(code) 또는 변수 참조를 통한 호출

  • 항상 전역 스코프에서 실행 (strict/일반 모드 무관)

Section 10.2.1.3 (NewDeclarativeEnvironment)에서 strict mode eval은 별도의 Environment Record를 생성하여 [[OuterEnv]]로만 외부 스코프와 연결됩니다. 내부에서 생성된 바인딩은 외부로 누출되지 않습니다.

with 문의 Object Environment Record 문제 Section 9.1.1.2 (Object Environment Records)에서 with는 객체를 Environment Record로 래핑하여 스코프 체인에 삽입합니다. 이는 다음 문제를 야기합니다:

  1. 동적 프로퍼티 조회: 스코프 체인 탐색이 런타임에 결정되어 정적 분석 불가
  2. 성능 저하: 객체 프로퍼티 조회는 Environment Record 바인딩보다 느림
  3. 최적화 방해: Inline Cache와 Hidden Class 최적화 무효화

strict mode는 Section 14.11.1 (Static Semantics: Early Errors)에서 with를 파싱 단계에 차단합니다.

V8 엔진의 eval 최적화 차단 V8 엔진은 함수가 eval을 사용하면 최적화를 포기합니다:

Deoptimization 트리거: eval 사용 감지 시 TurboFan 최적화 컴파일러가 해당 함수를 최적화 대상에서 제외합니다. strict mode eval은 스코프 격리로 인해 이 영향을 최소화합니다 (eval 내부만 최적화 제외).

Lexical Scope 분석 불가: 일반 모드 eval은 정적 스코프 분석(Static Scope Analysis)을 불가능하게 하여 Escape Analysis와 Dead Code Elimination을 차단합니다. strict mode는 eval 스코프를 격리하여 외부 함수의 최적화를 보존합니다.

CSP(Content Security Policy) 통합: 최신 웹 보안 정책에서는 eval 사용을 제한합니다. strict mode의 eval 격리는 CSP ‘unsafe-eval’ 지시어와 결합하여 보안 샌드박싱을 강화합니다.