본문 바로가기
Study/JavaScript

You don't know JS yet(1.5~1.8)

by dailycoding777 2025. 1. 23.

1.5 하위 호환성과 상위 호환성

JS를 지탱하는 기본 원칙 중 하나는 하위 호환성 보장이다.

그런데 많은 개발자들이 하위호환성을 잘 모르고 상위호환성 개념과 혼동하기도 한다.

 

1. 하위호환성(Backward Compatibility)

  • 정의: 새로운 버전의 시스템이나 소프트웨어가 이전 버전과 호환되는 능력.
  • 특징:
    • 기존 코드나 기능이 새로운 환경에서도 문제 없이 작동함.
    • 사용자 입장에서 업데이트 시 추가 작업이 거의 없음.
    • 예시: JS는 과거에 작성된 코드가 최신 브라우저에서도 작동하도록 설계됨.

2. 상위호환성(Forward Compatibility)

  • 정의: 현재 버전의 시스템이나 소프트웨어가 미래의 버전과 호환되는 능력.
  • 특징:
    • 미래의 기능이나 표준을 대비해 설계됨.
    • 보통은 제한적이며, 완벽한 구현이 어려움.
    • 예시: 새로운 기능을 사용할 수 없지만, 데이터 구조는 인식 가능한 경우.

JS는 하위호환성을 보장하는 언어다. (상위 호환성은 보장하지 않는다.)

(+ HTML과 CSS는 상위 호환성 보장)

단 한번이라도 유효한 JS 문법이라고 인정되면 명세서가 변경되더라도 그 유효성이 깨지지 않는다.

미래에도 무조건 작동한다.

TC39 구성원들은 ‘우리는 절대 웹을 망가트리지 않는다!’ 라는게 하위호환성 지킬려고 노력해서 그렇다.

그치만 하위호환성을 유지보수 한다는 건 결고 쉬운 일이 아니다.

JS는 약 25년동안 하위호환성을 보장해왔는데 IT에서 이런 사례는 거의 없다.

reset이 거의 안되기 때문에 실수를 포함한 모든 결정이 영원히 박제되기에

결정할 때 기준이 매우 엄격하다.

다만 언제나 예외는 있다.

브라우저로 수집한 데이터를 통해 영향력이 작을 것이라고 판단 되면 투표를 통해 명세를 변경한다.

그치만 이 경우는 매우 드물다. 변경되더라도 사용자가 눈치 못챙정도만 영향을 준다.

1.5.1 간극을 줄이기 위한 노력

다시 말하지만 JS는 상위 호환성을 보장하지 않기에 아주 오래된 엔진에서는 유효한 문법으로 작성한 코드가 돌아가지 않을 가능성이 있다.

ex) 2003년에 ES6문법(2015)

// const,화살표 문법 인식 불가
const greeting = () => console.log("Hello, world!");

그렇다면 우리는 옛날 문법만 쓰면서 JS가 발전하는 속도를 따라가지 못해야하나..?

절대 아니다. 다만 이런 간극을 줄이기 위해 엄청난 노력이 필요하다.

명세서에 추가 됐지만 구엔진과 호환되지 않는 문법은 transpile을 통해 호환성 문제를 해결할 수 있다.

트랜스파일은 변환기다. 대부분 이거 쓰면 해결되고 주로 Babel을 사용한다.

기존코드 → 다른형태 코드로 바꿔서 배포한다.

// 기존
i f (something) {
let x = 3;
console. log(x);
}
else {
let x = 4;
console. 1og(x);
}
// 바벨로 변환 된 ES5 코드
var x$0, x$1;
i f (something) {
X$0 = 3;
console. log(x$0);
}
else {
X$1 = 4 ; console. log(x$1);
}

기존은 if,else 절에 각각 변수 x를 정의했다.

  • let은 블록스코프를 준수하고
  • var는 함수스코프여야 결계가 생기기 때문에(var는 스코프를 무시함) 스코프 밖에서도 x에 접근할 수 있다.

이를 보완하기에 두개의 별도 변수가 새로 생긴 것이다.

그럼 var는 결계가 없는 것인가? 있긴해. function으로 감싸면 결계가 생긴다.

  • var는 함수 스코프를 존중한다.
  • let,const는 블록 스코프를 존중한다.

위 예제처럼 레거시코드로 변환해주는 게 제일 일반적인 해결책이다.

이 해결책을 폴리필(polyfill) 또는 심(shim)이라고 한다.


1.6 인터프리터 이해하기

JS가 인터프리터 언어인지,컴파일 언어인지에 대한 논쟁은 아주 오래 이어졌다.

‘스크립트 언어다’라는 의견도 있지만 실상은 더 복잡하다.

인터프리터 언어는 컴파일 언어에 비해 열악하다는 평가를 받음

  • 성능 최적화가 잘 안된다는 인식
  • 일부 스크립트 언어에(java,C++)서 동적타입 대신 정적 타입을 사용하는 등의 이유 때문

동적 : 타입이 실행 시점에 결정되고 변경 | 정적 : 선언 시점

 

구분 동적 타입 정적 타입

타입 선언 필요 없음 명시적 또는 추론
타입 검사 런타임에 수행 컴파일 시점에 수행
유연성 높음 (다양한 타입 사용 가능) 낮음 (타입이 고정됨)
안정성 낮음 (런타임 에러 가능성 있음) 높음 (컴파일 시 에러 미리 발견)
예시 언어 JavaScript, Python TypeScript, Java, C++

스크립트 언어나 인터프리터 언어는 대개 위에서 아래로 한 줄씩 코드가 실행되는 방식으로 만 들어진다.

보통 실행이 시작되기 전에 거치는 사전단계가 없다.

 

예제1. 오류를 무조건 바로 터트리지 말고 실행을 계속 진행해도 문제가 없을 때

function processUserInput(input) {
  try {
    let parsed = JSON.parse(input); // 잘못된 JSON이면 여기서 에러 발생
    console.log(parsed.name); // 정상적으로 처리
  } catch (e) {
    console.error("JSON 파싱 실패:", e.message); // 오류 메시지만 출력
  }
  console.log("다음 작업 진행 중..."); // 프로그램은 계속 실행
}

JSON 파싱에 실패해도 영향을 주지 않는다. → 에러처리 해줬으니 문제 없다.

만약 catch문을 작성 안했으면 런타임 에러가 생겼을 것.

 

예제2.오류 처리를 미루면 안되는 경우

function calculateTotal(price, quantity) {
  if (typeof price !== "number" || typeof quantity !== "number") {
    throw new Error("입력 값이 잘못되었습니다!"); // 즉시 오류 발생
  }
  return price * quantity;
}

 

계산 결과 자체가 말이 안되는 상황이니까. 예외처리가 없으니까 js에서 바료 오류를 띄운다.

3. 결론: "괜찮을 수도 있고, 안 괜찮을 수도 있다"의 의미

이 말은 상황에 따라 오류를 처리하는 시점이 달라져야 한다는 걸 의미한다.

  • 괜찮을 때: 오류가 발생해도 다른 부분에 치명적인 영향을 주지 않을 때
  • 안 괜찮을 때: 오류가 프로그램 로직이나 전체 흐름에 심각한 문제를 유발할 때

실행 전에 파싱을 먼저 거치는 언어

파싱(Parsing)이란?

  • 파싱소스 코드를 읽고 분석하는 과정
  • 즉, **"코드가 올바른 문법을 따르고 있는지 확인"**하는 과정
  • 문법 확인 + 구조 해석

(1) 컴파일 언어 (Parsing을 실행 전에 수행)

  • 대표 언어: C, C++, Java
  • 프로그램 실행 전에 컴파일 단계에서 파싱을 수행한다.
  • 코드가 실행되기 전에 문법 오류나 타입 에러를 모두 잡아낸다.
  • 모든 컴파일 언어는 파싱을 거친다.

예시 (C 코드):

int main() {
    int x = "Hello"; // 컴파일 오류: 타입이 맞지 않음
    return 0;
}
  • 이 코드는 컴파일 단계에서 오류가 발생하므로, 실행 자체가 안된다.

순서 : 파싱 →파싱결과인 추상구문트리(abstract syntax tree [AST])를 컴퓨터가 실행할 형태로 바꿔줌

 

 

(2) 인터프리터 언어 (Parsing을 실행 중에 수행)

  • 대표 언어: JavaScript, Python
  • 코드를 한 줄씩 읽어가면서 런타임에 파싱을 수행한다.
  • 문법 오류나 타입 에러는 해당 줄이 실행될 때까지 알 수 없다.

예시 (JavaScript 코드):

console.log("Hello, world");
console.log(unknownVar); // 여기서 ReferenceError 발생
  • 첫 번째 줄은 정상 실행되고, 두 번째 줄에서야 오류가 발생한다.

요약

  • "사전에 파싱"은 컴파일 언어의 특징이고, 코드를 실행하기 전에 오류를 미리 잡는 과정이다.
  • 인터프리터 언어는 실행 중에 오류를 잡으니, 런타임 에러가 더 자주 발생할 수 있다.

 

그럼 자바스크립트는 뭔가요?

 

의견1. 컴파일러 언어가 아니다.

컴파일을 거치면 보통 분산 시스템에서 언제든 배포할 수 있는 바이너리 파일이 생성된다.

그런데 JS는 소스코드 자체를 배포하지, 바이너리 파일을 배포하지 않기 떄문이다.

스크립트 언어나 인터프리터 언어는 대개 위에서 아래로 한줄씩 실행되는 방식이다.

그리고 실행 시작전에 거치는 사전단계가 없다. ( 과거 )

컴파일언어의 순서

파싱 →파싱결과인 추상구문트리(abstract syntax tree [AST])를 컴퓨터가 실행할 형태로 바꿔줌

JS로 작성한 코드도 실행전에 파싱을 거친다.

// 현대 엔진 V8 예시
// 전체 파싱 후 실행한다.
1. 소스 코드
    ↓
2. 파싱 // 소스 코드를 읽고 분석하는 과정
    ↓
3. AST 생성 // 코드를 읽어서 추상구문트리로 만듦
    ↓
4. 바이트코드 생성 //js ,기계어의 중간인 코드로 만듦
    ↓
5. 실행 (JIT 컴파일로 최적화된 기계어 사용(이진코드))

JS 명세서 왈 : 가능하면 프로그램 실행 전에 초기오류(정적으로 탐지가 가능한 오류)를 잡아야 한다.

그래서 답은 ‘컴파일 언어에 가깝다’ (필자 의견)

그림 1-1 : 위에서 아래로 한줄씩 실행,실행 시작 전 사전단계 X
그림 1-2 : 코드 전체 읽고 분석 → 컴파일 → 실행

JS에서 파싱이 끝난 코드는 그림1-1처럼 한줄씩 처리 X

컴파일러를 거쳐 최적화된 이진코드로 변환된 후 그림 1-2처럼 실행 된다.

+대부분 언어나 엔진은 비효율성 떄문에 그림 1-1처럼 실행하지 않는다.

 

 

의견2. JS는 인터프리터 언어다.

스탠포드 대학에서 발췌

필자랑 내가 알고 있는 지식이 달라서 스탠포드대학 논문도 봤다.

밑줄 친 부분 보이는가?

JS는 인터프리터 언어다. 컴파일 언어가 아니다.

그럼 나는 어떻게 알고 있어야 하는가..?

왜 이렇게 부르는지 너무 궁금했다. 이유는 다음과 같다.

1.역사적인 이유

  • JS는 원래 인터프리터 언어로 시작했다.
  • 초기에는 소스코드 한줄씩 읽고 바로 실행하는 방식으로 동작했다.(전통적인 인터프리터 방식)

2.실행 특성 때문에

  • 지금도 JS는 JIT(Just-In-Time) 컴파일을 사용하기 전에는 인터프리터처럼 런타임에 코드를 바로 사용하는 느낌이 있다.
  • 특히 동적타입과 런타임평가(예:eval)같은 특성 떄문에, 인터프리터 언어의 성격을 많이 가지고 있다.

3.ECMAScript에선 뭐라고 하는가?

“컴파일 언어”, “인터프리터 언어”로 규정하지 않는다.

대신 “동적 프로그래밍 언어(Dynamic Programing Language”로 정의한다.

Dynamic의 의미

  • 동적 타입(dynamic typing) : 타입이 런타임에 결정
  • 동적 실행(dynamic evbaluation) : eval이나 Function 생성자처럼 실행 중에 코드를 평가 (현대 엔진은 꼭 그렇지 않는다. 하위호환성 보장때문에 꼭 그렇지 않다고 말한거다.)

왜 인터프리터라고 했냐?

  • 자바스크립트는 역사적으로 인터프리터 방식으로 시작했고, 지금도 인터프리터적인 특성을 가지고 있기 때문이다.

현대적으로 보면?

  • JIT 컴파일러를 사용하는 하이브리드 언어에 가깝다.

공식적으로는?

  • 자바스크립트는 ECMAScript 명세에 따라 동적 프로그래밍 언어로 정의된다.

 

📊 책의 설명 vs V8 흐름 + 바벨/웹팩 과정

단계 책의 설명 V8 흐름

1. 개발자의 코드 작성 소스 코드를 작성한 후, 바벨 트랜스파일링 및 웹팩 번들링을 거쳐 브라우저로 전달. 개발자가 작성한 소스 코드를 작성.
**2. 바벨 트랜스파일링    
(npm run build)** 최신 JS(ES6+) 코드를 ES5 코드로 변환. 최신 JS(ES6+) 코드를 바벨을 통해 트랜스파일링.
**3. 웹팩 번들링    
(npm run build)** 웹팩이 여러 파일을 묶어 최적화된 번들 파일을 생성하여 브라우저에 전달. 웹팩이 트리 쉐이킹, 코드 분할 등을 수행하며 번들링 완료.
4. JS 엔진 파싱 JS 엔진이 소스 코드를 읽어들여 추상 구문 트리(AST) 생성. V8 엔진이 전체 코드를 파싱하여 AST 생성.
5. AST → 바이트코드 변환 엔진이 AST를 바탕으로 바이트코드 생성, 이 과정에서 JIT 컴파일러가 작동하며 성능 최적화. V8 엔진이 AST를 바이트코드로 변환하고, 자주 실행되는 코드를 JIT 컴파일로 최적화.
6. 프로그램 실행 JS 가상 머신이 바이트코드와 최적화된 기계어를 실행. V8 엔진이 최적화된 기계어 또는 바이트코드를 실행.
  1. 바벨
    • 최신 문법 → 이전 문법(ES5) 변환.
    • 브라우저 호환성을 위한 작업.
  2. 웹팩
    • 여러 파일을 번들링, 트리 쉐이킹, 코드 분할.
  3. V8(크롬,노드의 JS엔진)
    • AST 생성 → 바이트코드 변환 → 실행.
    • JIT 컴파일로 성능 최적화.

1.6.1 웹 어셈블리

웹 어셈블리를 알기전에 ASM.js 먼저 봐야한다.

ASM.js란?

  • 자바스크립트 성능 극대화를 위해 설계한 subset이다. 자바스크립트를 마치 저수준언어(예: C언어)처럼 사용할 수 있게 해준다.
  • Mozilla에서 개발한 자바스크립트의 하위 집합(subset)
  • JS를 정적 타입 언어처럼 작성해서, 실행 속도를 기계어에 가깝게 최적화할 수 있도록 설계되어 있다.

특징

1. 정적 타입 기반

  • JS는 원래 동적타입언어지만, ASM.js는 동적 타입을 강제한다.
  • 예) 변수타입 명확히 정의,타입 안정성 보장

2. 저수준 명령 지원

  • 숫자,메모리,포인터 같은 저수준 지원
  • 성능을 위해 주로 32비트 정수와 부동소수점 연산을 사용.

3. 컴파일러 출력 코드

  • ASM.js는 사람이 직접 작성하기 보단 C/C++ 코드를 컴파일러로 변환해 생성

4. ASM.js의 활용

(1) 게임

  • Unreal Engine 같은 게임 엔진을 브라우저에서 실행.
  • 성능이 중요한 실시간 그래픽 작업.

(2) 고성능 계산

  • 과학 시뮬레이션, 데이터 분석, 머신 러닝.

(3) C/C++ 코드 변환

  • Emscripten 컴파일러로 C/C++ 코드를 ASM.js로 변환하여 실행.

현재는 webAssembly로 대체되었다.


1. 웹어셈블리란?

  • 브라우저에서 네이티브 성능에 가까운 실행 속도를 제공하는 바이너리 실행 포맷이다.
  • 바이너리 실행 포맷 : 이진코드 0,1로 된 데이터를 담고 있는 파일 형식
  • 브라우저뿐만 아니라 **서버 환경(Node.js)**에서도 실행 가능하다.
  • 자바스크립트와 함께 동작하며, 서로 상호작용이 가능하다.

2. 왜 등장했나?

(1) 자바스크립트의 한계

  • 자바스크립트는 성능이 중요한 애플리케이션(예: 게임, 영상 처리, 머신 러닝)에서 한계가 있다.
  • 특히 C/C++ 같은 저수준 언어로 작성된 고성능 코드를 웹에서 실행하기 어려웠다.

(2) 고성능 애플리케이션 요구

  • Unreal Engine 게임 엔진이나 과학적 계산 프로그램 등에서 네이티브 성능이 필요했기 때문에 WebAssembly가 설계되었다.

3. 웹어셈블리의 특징

  1. 바이너리 포맷
    • 사람이 읽기 어려운 이진 코드로 작성되어, 자바스크립트보다 빠르게 실행된다.
    • 예: 0x6D736100 (WASM의 매직 넘버)
  2. 플랫폼 독립적
    • 브라우저나 운영체제에 구애받지 않고 실행된다.
    • 모든 주요 브라우저(Chrome, Firefox, Safari, Edge)에서 지원.
  3. 자바스크립트와 통합 가능
    • WebAssembly는 자바스크립트와 상호작용하며, 필요한 작업만 WASM에서 처리.
  4. 보안
    • 샌드박스 환경에서 실행되므로 브라우저의 보안을 유지한다.

4. 웹어셈블리와 ASM.js 비교

특징 ASM.js WebAssembly

형식 텍스트 기반의 자바스크립트 서브셋 이진 포맷 (바이너리 코드)
성능 네이티브 성능에 가까움 네이티브 성능과 거의 동일
브라우저 지원 자바스크립트 엔진 필요 모든 주요 브라우저에서 지원
용도 고성능 작업(게임, 영상 처리 등) ASM.js보다 더 범용적이고 효율적

5. WebAssembly의 활용

(1) 게임

  • 브라우저에서 고성능 3D 게임을 실행.
  • 예: Unreal Engine 게임 엔진.

(2) 머신 러닝

  • TensorFlow.js 같은 라이브러리가 WebAssembly로 가속화된 모델을 실행.

(3) 영상 처리

  • 웹캠이나 동영상에서 실시간 필터를 적용.

(4) 기존 네이티브 코드의 활용

  • C, C++, Rust로 작성된 코드를 WebAssembly로 컴파일하여 웹에서 실행.

현재 V8엔진에서 완벽하게 WebAssembly를 지원한다.


1.7 엄격모드(strict mode)

1. Strict Mode란?

  • *ES5(ECMAScript 5)**에서 도입된 기능으로, 잠재적인 에러를 미리 잡아주는 모드다.
  • "use strict";라는 키워드를 코드의 맨 위나 함수 내부에 작성하면 활성화된다.
  • 느슨한 문법을 사용하지 못하게 막아서 코드 품질을 향상시킨다.

2. Strict Mode 활성화 방법

(1) 전역 스코프에서 활성화

"use strict";
x = 3; // ReferenceError: x is not defined
  • 전체 코드에서 Strict Mode가 활성화

(2) 함수 스코프에서 활성화

function myFunction() {
  "use strict";
  y = 4; // ReferenceError: y is not defined
}
myFunction();
  • 특정 함수에서만 Strict Mode를 활성화

3. Strict Mode의 주요 기능

  • 암묵적 전역 변수를 방지 (선언되지 않은 변수에 값 할당 X)
"use strict";
x = 3; // ReferenceError: x is not defined
  • 변수 삭제 금지(delete 키워드로 변수나 함수 삭제를 막음)
"use strict";
let a = 5;
delete a; // SyntaxError: Delete of an unqualified identifier in strict mode

  • **중복 매개변수 금지 (**함수 선언 시 중복된 매개변수 이름을 사용 X)
"use strict";
function sum(a, a) { // SyntaxError: Duplicate parameter name not allowed
  return a + a;
}
  • 객체의 읽기 전용 속성에 쓰기 금지(읽기 전용 속성에 값을 할당하면 오류가 발생)
"use strict";
const obj = Object.freeze({ x: 1 });
obj.x = 2; // TypeError: Cannot assign to read only property 'x'
  • this의 암묵적 바인딩 금지(함수에서 this가 암묵적으로 전역객체에 바인딩 X)
"use strict";
function test() {
  console.log(this); // undefined
}
test();

Strict Mode는 **"자바스크립트를 더 엄격하게 사용하도록 강제하는 모드다.

장점 : 암묵적 에러 방지, 안정성 향상, 최적화 가능성 증가.

단점 : 기존 코드와의 호환성 문제.


1.8 정리

  • JS 는 ECMA 주최하에 TC39 위원회가 결정하는 ECMAScript 표준 ( 집필 시점에는 ES2019) 을 구현한 언어다.
  • JS 는 브라우저를 비롯한 Node.js 등의 다양한 환경에서 실행된다. JS 는 다중 패러다임 언어다. 개발자는 JS 문법이나 여러 기능을 사용해 절차적으로도 코드 를 작성할 수 있고 객체 지향 방식으로도 , 함수형으로도 코드를 작성할 수 있다.
  • JS 는 컴파일 처리되는 언어다. (그치만 동적 프로그래밍 언어임) JS 엔진을 비롯한 여러 도구가 JS 로 작성한 코드를 처리하고 , 코드를 실행하기 전에 코드를 점검한다. 이때 오류가 있으면 오류도 보고한다.

'Study > JavaScript' 카테고리의 다른 글

You don't know JS (1.1~1.4)  (0) 2025.01.16