
JavaScript 개발을 하다가 "use strict"라는 구문을 한 번 쯤은 마주해본 적이 있을 것이다.
이는 코드에서 일명 "엄격 모드"라 불리는 JS의 메커니즘이 활성화 되었음을 의미하는 구문이다.
오늘은 엄격 모드가 무엇이고, 활성화 되었을 때 어떤 점이 달라지는지, 왜 사용해야 하는지에 대해 알아보려고 한다.
이 포스팅을 통해 엄격 모드가 단순하게 언어적으로 제약을 거는 게 아닌, 더 나은 JS 코드를 작성하기 위한 강력한 도구임을 깨달아 보자.
·
·
·
Strict Mode는 2009년 경 ECMAScript 5(ES5)가 공개되면서 도입되었다.
이 기능은 JavaScript에 좀 더 엄격한 구문 규칙과 오류 검사를 적용시키기 위한 모드이다.
어떠한 배경에서 등장하게 되었을까?
JavaScript라는 언어는 일명 "하위 호환성"을 보장하며 오래도록 발전해 온 언어다. 즉 새로운 기능이나 문법이 추가되더라도 기존에 작성했던 코드는 시간이 흘러도 새 기능/문법과 함께 잘 동작하는 걸 보장한다는 개념이다.
그런데 언어의 초기 설계 특성 상, JS만의 유연성이나 암묵적인 동작들로 인해 종종 예기치 못한 동작이 발생하곤 했다.
예를 들면 비-엄격 모드에서는 `foo = 'bar';` 와 같이
변수 선언 키워드,(var/let/const) 없이도 변수가 생성될 수 있는데,
만약 오타라도 나서 `doo = 'bar';` 라고 작성하더라도 오류가 발생하지 않으며
의도치 않게 새 전역 변수가 생성될 뿐 아니라 이는 디버깅도 어렵게 만든다.
이처럼 언어 특성으로 인해 개발자의 의도와 다르게 작동하거나, 디버깅의 어려움, 보안 취약점 등에서 문제되는 부분이 있었다. 다른 언어들과 비교해보면 '버그가 발생하기 쉬운' 언어라는 측면이 존재한다.
때문에 언어적인 차원에서 한 층 더 안전하고 예측하기 쉬운 코드를 작성하도록 돕고자 등장한 게 바로 "엄격 모드" 이다.
참고로, 엄격 모드가 아닌 비-엄격 모드(기본 값에 해당하는)에 대해서는 종종
sloppy mode라고도 불린다.영어 단어 sloppy는 '엉성한', '허술한', '부주의한' 등의 뜻을 가지고 있기 때문에 번역하자면 '느슨한, 허술한 모드' 라고 이해하면 될 것 같다. (규칙이 엄격하지 않고 잠재적으로 문제를 일으킬 수 있다는 의미 내포)
엄격 모드와 비-엄격 모드에서는 일부분이 서로 다르게 동작한다.
때문에 엄격 모드가 활성화되면 기존에 비-엄격 모드일 때는 무시되던(허용되던) 오류들이 검출된다거나, JS 엔진이 내부적으로 코드를 최적화하는 데 있어서 방해되는 요인들이 제거되는 등과 같은 변화가 생긴다.
그 외에도 잠재적으로 안전하지 않은 작업을 제한시키고, 보다 명확하고 안전한 방식으로 코드를 작성하도록 장려한다.
주요 내용 (in 엄격 모드):
var, let, const)와 함께 선언되어야 하며 그렇지 않으면 오류 발생this 값은 .call(), .apply(), .bind() 메서드로 명시적으로 지정하지 않는 한, undefinedarguments 객체 간 동기화 X (한 쪽을 수정한다고 다른 쪽이 변경되지 X) eval에서 생성하는 변수나 함수는 오직 평가되는 코드 안에서만 존재with 문 사용 불가이러한 엄격함 덕분에 개발자들은 비-엄격 모드에서 발생할 수 있는 실수나 문제들을 사전에 방지할 수 있다는 이점이 있다.
기술적으로 볼 때, 엄격 모드는 선택 사항이다. 즉 기본 값은 '비활성화'이며 개발자가 명시적으로 모드를 적용하지 않으면 사용되지 않는다는 뜻이다.
엄격 모드가 선택적으로 적용될 수 있도록 되어 있는 데에는 몇 가지 이유를 추측해 볼 수 있다.
JavaScript는 엄격 모드를 점진적으로 적용할 수 있도록 설계되었다. 이를 통해 개발자들은 기존 코드를 유지하면서도 새로운 코드나 리팩토링이 필요한 곳에 부분적으로 엄격 모드를 적용할 수 있다.
하지만 현대적인 JS 개발에서는 엄격 모드가 사실상 표준으로 자리잡았다고 해도 과언이 아니라고 본다. ES6 부터 도입된
모듈이나클래스같은 구문들에는 자동적으로 엄격 모드가 적용되기 때문이다. 따라서 많은 최신 프로젝트들에 대부분 해당 모드가 기본적으로 적용되고 있다.
먼저 엄격 모드를 활성화 하려면 "use strict" 구문을 첫 줄에 작성하면 된다.
이 때 JS 파일 첫 줄에 작성하느냐 함수 본문 첫 줄에 작성하느냐에 따라 엄격 모드가 적용되는 범위가 달라진다.
주의할 점은 해당 구문 이전에 공백이나 주석을 제외한 어떠한 코드라도 있으면 엄격 모드가 활성화 되지 않는다.
"use strict";
// 기존 자바스크립트 코드들..
function myFunc() {
"use strict";
// 기존 함수 본문 코드..
}
만약 파일 단위에 이미 적용이 되어 있는데, 동시에 함수에서 엄격 모드를 적용시키는 건 불가능하다.
파일에서만 적용시키든 함수에서만 적용시키든 둘 중 하나만 가능하다.
과연 지금부터 작성하는 코드든 이전에 작성한 코드든 모든 곳에 엄격 모드를 활성화하도록 바꿔야만 하는 것일까?
꼭 그래야만 하는 것은 아니다. 하지만 일반적인 스크립트라면 개별 .js 파일이나 <script> 태그 최상단에 "use strict";를 사용해 엄격 모드로 만들고, 레거시 코드나 기존 프로젝트라면 함수 단위에 "use strict";를 사용해 점진적으로 테스트 해가며 엄격 모드를 적용시키는 것이 권장되고 있다.
참고로 다음 목차에서 다루겠지만 JS의 일부 기능에서는 엄격 모드가 자동으로 적용되고 있다. 아래에서 이에 대해 알아보자.
대표적으로, ES6(ECMAScript 2015)에서 도입된 모듈과 클래스에서는 별도 설정 없이도 엄격 모드가 적용된다.
따라서 아래 소개되는 기능 내에서는 명시적으로 "use strict";를 선언할 필요가 없다.
모듈 내의 코드는 자동으로 엄격 모드가 적용된다.
참고로 <script> 태그에 type="module" 프로퍼티가 지정되었을 때도 동일하다.
// module.js
export default function strictFunc() {
// 자동으로 엄격 모드에서 실행된다
delete [].length;
}
// main.js
import strictFunc from './main.js'
strictFunc(); // 💥 TypeError: Cannot delete property 'length' of [object Array]
반면 이 코드를 CommonJS 방식으로 바꿔서 작성해보면 그때는 오류 없이 실행된다.
클래스 문법 내부에서도 역시 자동으로 엄격 모드가 적용된다.
이는 클래스 선언 혹은 클래스 표현식에 모두 해당된다.
// 클래스 선언
class C1 {
// 이 안의 모든 코드는 엄격 모드에서 평가된다
test() {
delete Object.prototype;
}
}
new C1().test(); // 💥 TypeError: Cannot delete property 'prototype' of function Object() { [native code] }
// 클래스 표현식
const C2 = class {
// 이 안의 모든 코드는 엄격 모드에서 평가된다
constructor() {
undeclaredVar = 10;
}
};
new C2(); // 💥 ReferenceError: undeclaredVar is not defined
이러한 동작은 JavaScript 언어의 발전 방향이나 보유하고 있는 철학을 잘 보여주는 사례라고 생각된다.
ES6 이후의 새로운 기능들에 대해 엄격 모드를 기본적으로 적용함으로써 여러 이점을 얻을 수 있었다.
예를 들면 잠재적으로 충돌 가능성이 있는 문법 같은 경우 사전에 제한하여, 예측 가능한 동작을 보장하면서도 언어가 향후 확장되더라도 호환성을 유지할 것으로 기대할 수 있다.
이미 많은 프로덕션 환경에서는 모듈 형식으로 작성된 파일이 많이 사용되고 있으며, 이에 따라 자동으로 엄격 모드가 준수되고 있다. 따라서 엄격 모드가 사실상 현대 JS 개발의 표준이 되어가고 있다고 보아도 손색이 없을 것 가다.
이러한 추세는 앞으로도 계속될 것으로 예상되고, 언어의 생태계 관점에서 보았을 때도 더 안전하고 효율적으로 코드를 작성하는 관행이 이어질 것 같다.
리액트에도 JavaScript의 엄격 모드와 비슷한 목적을 가지고 있는 <StrictMode>가 존재한다.
리액트 컴포넌트에 초점이 맞춰져 있으며, 개발 중에 애플리케이션에서 발생할 수 있는 잠재적인 문제들을 빠르게 식별할 수 있도록 도움을 준다.
아래는 전체 앱에 대해 엄격 모드를 활성화하는 방법이다.
해당 방식으로 사용하는 것이 권장되며, 루트 컴포넌트를 <StrictMode>로 감싸고 있다.
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(
<StrictMode>
<App />
</StrictMode>
);
활성화 되면, 컴포넌트 내부의 모든 하위 트리에 대해 추가적인 검사가 수행된다.
참고로 이 검사는 개발 환경에서만 실행되기 때문에 프로덕션 환경에서 제대로 재현하기 어려운 버그를 검출하는 데 도움이 된다. (프로덕션 빌드에는 영향 X)
발생하는 주요 변화로는 다음과 같다.
리액트에서도 제공되는 엄격 모드를 통해 개발자는 리액트의 모범 사례를 따르고 잠재적인 버그를 미연에 방지하는 데 도움을 받을 수 있다. 따라서 JS의 엄격 모드와 유사한 맥락으로, 더 나은 코드를 작성해 품질과 안정성을 높이기 위한 언어적 지원이라고 보면 될 것 같다.
JavaScript는 개발자들이 프로그램을 만들 때 버그를 최소화 할 수 있는 방법 중 하나로 일단 버그 자체가 발생하기 어려운 환경을 조성하기 위해 엄격 모드를 고안해 낸 것 같다.
이로써 이전보다는 신경 쓸 부분은 늘어났지만, 장기적으로 보았을 때 개발자들의 잠재적인 실수를 줄이고 더 안전한 방식으로 코드를 작성하도록 해 결과적으로 품질 높은 프로그램을 만들 수 있게 되었다고 생각한다.
그러니 특별한 이유가 있지 않은 이상에는 엄격 모드를 사용하는 것이 바람직하다 느껴진다. 단순히 '제약을 가한다'고 생각하기 보다는, 길게 보았을 때 전체 프로그램의 품질을 향상시키기 위한 수단이라고 보는 게 맞을 것 같다.
결론적으로 JavaScript를 오래도록 더 잘 사용하기 위해 앞으로도 엄격 모드를 적극 활용해보자.