자바스크립트를 더 잘 이해하기 위한 몇 가지 사실들

0

ECMAScript와 자바스크립트 엔진

자바스크립트는 높은 수준의 Just-In-Time 컴파일 및 다중 패러다임 프로그래밍 언어로, 처음에는 1995년 Netscape에서 LiveScript라는 이름으로 도입되었지만, 그 당시 가장 인기있던 프로그래밍 언어인 Java와 비슷한 자바스크립트라는 이름으로 바꿨는데, 이는 더 많은 사람들이 사용하길 바랬기 때문이라고 합니다. 하지만 자바스크립트와 Java는 구문과 원칙이 다른 독립적인 언어이고, 현재는 HTML, CSS와 함께 Web에서 가장 중요한 기술 중 하나가 되었습니다.

ECMA

Microsoft는 1996년에 자체 버전의 자바스크립트를 출시했는데, 같은 해에 사람들은 기존 버전의 자바스크립트와 Microsoft 버전의 자바스크립트의 두 버전이 표준화 문제를 일으킬 수 있음을 예상하고 제품 안전, 음향 및 기타 여러 표준과 같은 다양한 표준을 처리하는 그룹인 ECMA에 표준안을 제출했습니다.

자바스크립트는 지금도 계속해서 발전하고 있는데, ECMAScript라는 스크립팅 언어에 대한 표준을 개발하는 ECMA에는 TC39 라는 위원회가 있고, 이 위원회의 위원들은 일년에 여러번의 모임을 가지면서 제안된 변경 사항을 논의한다고 합니다.

ECMAScript는 언어가 어떻게 작동해야 하는지 설명하는 일련의 규칙을 정리한 문서로 참조의 역할을 할 뿐이지만, 브라우저를 공급하는 업체들은 ECMAScript의 사양을 참조하여 해당 규칙에 따라 엔진을 개발하기 때문에 중요한 역할을 하는데, 결국 자바스크립트는 ECMAScript의 가장 유명한 “구현체”라고 할 수 있는 것이죠.

ECMA의 위원회는 몇 년마다 모여서 기능을 추가하곤 하는데, 이들은 일련 번호로 버전의 이름을 지정하고 있습니다. ES5는 2009년에 출시되었고, 2015년에는 ES6라는 다른 버전을 출시, 이 후 부터는 매년 사양을 변경하기로 결정하고, 명명 규칙을 “ECMAScript-출시연도”로 변경하게 됩니다. 그래서 ES6ES2015가 되고, 다음 해 부터는 각각 ES2016, ES2017, ES2018, ES2019 등의 버전이 출시되고 있습니다.

자바스크립트 엔진

자바스크립트는 브라우저에서 실행되도록 만들어졌고, 주요 목적은 웹 페이지를 인터랙티브하게 만드는 것이었습니다. 브라우저에는 자바스크립트 가상 머신이라는 임베디드 엔진이 있는데, 이 엔진은 자바스크립트 코드를 구문 분석(읽기), 컴파일 및 실행하여 사용자가 웹사이트와 상호작용(버튼 클릭, 양식 채우기 등)을 할 수 있도록 하는 역할을 합니다.

자바스크립트 가상 머신은 브라우저의 엔진마다 다른 이름으로 불리는데, Chrome과 Opera의 자바스크립트 엔진은 V8, Firefox의 엔진은 SpiderMonkey, 다른 버전의 IE는 TridentChakra, Microsoft Edge는 ChakraCore, Safari는 NitroSquirrelFish라는 이름으로 불립니다.

자바스크립트의 엔진이 무엇인지 아는 것이 중요한 이유는 언어에 어떤 기능이 있더라도 그 기능이 해당 엔진에 구현되지 않는 경우라면 코드가 제대로 컴파일되지 않기 때문인데, 보통 자바스크립트 엔진은 ECMAScript 언어의 사양을 따르고 새로 추가된 기능을 구현하려고 하지만 모든 브라우저에서 일관성있게 실행될 수 있다는 “보장성”을 제공하는 것은 아닙니다.

일부 기능은 엔진에 의해 구현되지만 다른 기능은 구현되지 않을 수 있기 때문에, 모든 브라우저에서 모든 기능을 동일하게 사용할 수 있는 것은 아닙니다. 만약 어떤 기능을 현재 사용하는 브라우저의 엔진이 지원하는지 확인하고 싶다면 자바스크립트의 호환성 비교 표를 참고하세요.

참고로, 컴퓨터의 운영체제는 멀티 스레딩을 통해 동시에 여러 작업을 수행할 수 있지만 자바스크립트는 항상 코드가 단일 스레드에서 실행되는 단일 스레딩 언어입니다.

자바스크립트 환경

자바스크립트는 호스트 환경에서 실행되는 언어이고, 자바스크립트 엔진이 있는 모든 환경에서 실행될 수 있습니다. 가장 잘 알려진 자바스크립트 환경은 웹 브라우저로, 최신 브라우저에는 자체 자바스크립트 엔진이 내장되어 있고, 이 엔진을 통해 자바스크립트 코드를 컴파일 및 실행할 수 있습니다.

물론 브라우저 뿐만 아니라 서버와 같은 다른 환경에서도 자바스크립트를 실행할 수 있고, 자바스크립트를 사용하여 HTTP 요청을 보내고, 데이터를 가져올 수도 있습니다.

Node.js

Node.js는 브라우저의 외부에서 자바스크립트를 실행할 수 있는 런타임 환경인데, Node.js를 사용하면 클라이언트 및 서버 측 스크립트를 동일한 언어로 작성할 수 있는데, 단일 프로그래밍 언어를 중심으로 웹 개발을 통합할 수 있다는 장점이 있습니다.

브라우저 내에서 자바스크립트는 웹 페이지의 HTML 및 스타일 수정, 사용자 인터페이스 만들기, 원격 서버에 요청 보내기, 서버에 파일 다운로드 및 업로드하기, 쿠키 가져오기 및 설정하기, 사용자 조회하기, 로컬 스토리지를 이용한 일부 데이터 기억하기 등의 작업을 수행할 수 있는데, 디스크에 임의의 파일을 읽거나 쓸 수는 없고, 운영 체제의 기능에도 직접 액세스 할 수 없기 때문에 컴퓨터의 프로그램을 복사하거나 실행하는 것도 불가능합니다. 또 “동일 출처 정책”으로 인해 브라우저의 다른 창이나 탭에도 액세스 할 수 없습니다.

  • JavaScrit가 브라우저에서 할 수 있는 작업들

  • 웹 페이지의 HTML 및 스타일 수정
  • 사용자 인터페이스 만들기
  • 원격 서버에 요청 보내기
  • 서버에 파일 다운로드 및 업로드하기
  • 쿠키 가져오기 및 설정하기
  • 사용자 조회하기
  • 로컬 스토리지를 이용한 일부 데이터 기억하기

반면 Node.js는 서버와 같이 실행되는 시스템에서 로컬 파일 시스템에 액세스하고 운영 체제와 상호 작용을 할 수 있는데, 로드된 웹 페이지에 대해서는 액세스 권한을 가지고 있지 않아 HTML과 CSS를 직접 조작할 수는 없습니다.

Dynamic vs. Weak Typed

자바스크립트는 동적이고, 해석적이고, 약한 타입의 프로그래밍 언어로, 동적 및 해석은 코드가 미리 컴파일되지 않고 엔진에 의해 즉각적으로 구문 분석 및 컴파일됨을 의미합니다.

Java와 같은 다른 프로그래밍 언어의 경우에는 미리 컴파일된 코드가 있을 수 있기 때문에 최종 사용자와 공유하기 전에 코드가 컴파일되어 사용할 준비가 되는 반면, 자바스크립트는 코드를 런타임에서 즉시 평가하고 실행합니다.

자바스크립트에서 변수는 단순히 값을 담는 컨테이너의 역할을 하는데, 이 변수에 담기는 값은 숫자, 텍스트, Simbol 등이 될 수 있습니다. Java와 같은 프로그래밍 언어에서는 변수의 종류를 미리 선언해줘야 하는 반면 자바스크립트에서는 변수를 사용할 때 특정한 종류의 변수로 선언할 필요는 없습니다.

자바스크립트에서는 데이터의 유형이 자동으로 유추되는데, 유형은 변수가 아니라 값과 연관됩니다. 즉 자바스크립트는 처음부터 데이터 유형을 할당하지 않기 때문에 변수의 첫 번째 값은 텍스트가 될 수 있고, 재할당되는 두 번째 값은 숫자가 될 수도 있습니다. 이는 변수에 저장된 데이터가 런타임에서 변경될 수 있다는 의미입니다.

이것이 자바스크립트를 ‘느슨한 형식’ 또는 ‘동적 형식’이라고 부르는 이유로, ‘엄격한 형식’의 언어에서는 변수가 보유할 데이터 유형을 미리 정의해야 하고, 이를 변경할 수 없습니다. 만약 자바스크립트를 사용하면서 ‘엄격한‘한 변수를 사용해야 한다면 TypeScript를 고려해 볼 수 있습니다.

noscript, defer, async, strict 모드

<noscript>

클라이언트 측에서는 자바스크립트의 실행을 비활성화할 수 있는데, 자바스크립트의 실행을 비활성화하면 브라우저는 자바스크립트 파일을 실행하지 않습니다. 이런 경우 만약 운영하고 있는 웹 사이트가 자바스크립트에 크게 의존하는 경우라면 ‘더 나은 UI/UX를 위해 자바스크립트를 켜주세요.’라는 메세지를 남겨야 할 필요가 있습니다.

바로 이런 경우에 <noscript> 태그를 사용할 수 있는데, <noscript> 태그 안에 작성한 텍스트나 HTML은 스크립트 유형이 지원되지 않거나 브라우저에서 스크립팅이 비활성화된 경우에만 사용자에게 해당 내용을 표시해 줍니다.

<noscript>
  더 나은 UI/UX를 위해 자바스크립트를 켜주세요.
</noscript>

defer와 async

deferasync 태그는 외부 스크립트를 불러오는 경우에만 사용할 수 있습니다.

일반적으로 HTML 문서의 헤드 섹션에 스크립트 가져오기를 추가하면 브라우저는 HTML 콘텐츠보다 먼저 스크립트 태그를 구문 분석하게 되는데, 해당 태그에 src 이외의 속성이 없으면 스크립트가 다운로드될 때까지 HTML 구문 분석을 차단하고, HTML 콘텐츠가 로드되기 전에 자바스크립트도 실행하기 때문에 로딩 시간도 길어집니다.

defer 속성 을 사용하면 브라우저는 HTML이 로드되기 전에 자바스크립트 파일을 다운로드하지만 HTML 구문 분석을 중단하지 않고 다른 모든 작업이 완료된 후에만 스크립트를 실행하게 되는데, 이 때 스크립트 실행 순서는 defer로 보장되고, 자바스크립트 파일은 가져온 순서대로 실행이 됩니다.

<script defer src="defer-script.js"></script>

만약 HTML 구문 분석이 중지되고 스크립트가 다운로드된 직후에 실행되는 것을 원하지 않는 경우에는 async 태그를 추가하면 되는데, 스크립트 실행 순서는 비동기식으로 스크립트는 다운로드가 완료된 직후에 실행되고, 스크립트를 가져오는 순서와 같지 않을 수 있다는 것에 주의해야 합니다.

<script async src="async-script.js"></script>

즉, async 태그는 스크립트의 실행 순서를 보장하지 않기 때문에 여러 개의 외부 스크립트를 사용하는 경우에는 주의할 필요가 있습니다.

마지막으로 외부 스크립트를 가져오는 또 다른 방법은 <body> 태그의 마지막에 스크립트를 추가하는 겁니다. 이렇게 하면 브라우저가 HTML을 로드한 후에 스크립트를 실행하기 때문에, HTML이 로드된 후 확실히 실행된다는 장점이 있습니다.

하지만 일반적으로 asyncdefer 태그를 사용하여 head 섹션에서 스크립트를 가져오는 것이 body 태그 하단에 추가하는 것보다 빠르고, 스크립트 다운로드와 HTML 파싱 및 로드를 동시에 수행할 수 있기 때문에 스크립트를 최대한 빨리 실행해야 한다면 deferasync를 사용하는 것이 좋습니다.

strict 모드

자바스크립트의 문법 규칙은 엄격하지 않기 때문에, 세미콜론을 사용하지 않고 줄 바꿈만 사용하거나, var 또는 let을 선언하고 변수를 사용하는 경우에도 스크립트는 정상적으로 작동할 수 있습니다. 하지만 이렇게 너무 느슨한 규칙으로 자바스크립트를 사용하는 것은 좋은 방법이 아니기 때문에 'use strict'를 추가하여 조금 더 엄격한 모드로 스크립트를 작성할 수 있습니다.

'use strict';
let hi = "Hi!  I'm a strict mode script!";

위 코드와 같이 스크립트의 첫 번째 코드 행에 'use strict'를 추가하면 보다 조금 더 엄격한 문법 규칙에 따라 코드를 작성해야 하는데, strict 모드에서는 에러가 발생할 가능성이 더 줄어들고, 더 쉽게 디버깅을 할 수 있는 최적화된 코드를 작성할 수 있습니다. 단, ‘strict’ 모드를 사용하여 작성한 코드에서 ‘strict’ 모드를 사용하지 않는 라이브러리를 사용하는 경우에는 오류가 발생할 수 있어 주의해야 합니다.

Scope와 호이스팅

Scope

Scope는 변수가 등록되는 장소로 생각할 수 있는데, Scope는 변수에 대한 접근성을 결정하기 때문에 중요한 개념입니다. 자바스크립트에는 다음과 같이 2가지의 주요 Scope가 있습니다.

전역 Scope
기본 범위로 브라우저에서 전역 Scope는 window 객체를 나타낸다.(브라우저에 있는 코드의 최상위 수준은 window 객체)
전역 변수는 블록 외부에서 선언되며 응용 프로그램 전체에서 사용할 수 있다.
로컬 Scope
전역 Scope 내에 중첩되며, 지역 변수는 블록 내부에서 선언된다.
블록은 주로 중괄호 안에 있는 0개 이상의 명령문 그룹을 의미하며, 'if...else''for' 문 내부에 있는 모든 항목은 로컬로 범위가 지정된다.
function은 로컬로 범위가 지정된다.(변수를 var로 선언하는 경우에는 전역 변수가 되기 때문에 주의)

Hoisting

Hoisting은 코드를 실행하기 전에 변수 및 함수 선언을 해당 범위의 맨 위로 이동하는 자바스크립트의 메커니즘입니다. 이것은 함수와 변수가 선언된 위치에 관계없이 불가피하게 범위의 맨 위로 이동 됨을 의미하는데, 이를 위해 컴파일 단계에서 함수와 변수 선언이 메모리에 추가됩니다.

브라우저는 스크립트를 두 번 실행하는데, 첫 번째 실행에서는 모든 선언을 가져와 메모리에 추가하게 되고, 두 번째 턴에서는 모든 변수를 사용하려고 시도합니다.

자바스크립트는 “선언”만 호이스트하는데, 변수 선언은 호이스트되지만 초기화는 호이스팅되지 않고, 함수 선언도 호이스팅되지만 함수 표현식은 호이스팅되지 않습니다. 즉 변수 let a;를 선언하거나, let a = 3;와 같이 초기화를 하는 경우 변수를 선언한다는 것은 변수가 존재한다는 것을 말하고, 초기화하는 것은 그 변수에 값을 주는 것을 말합니다.

그런데 호이스팅은 “선언”만을 위한 것이라고 했죠? 즉 값을 유지하지 않고 스크립트에 존재하는 변수를 기록하기 때문에, 두 경우 모두 let a라는 선언만 호이스팅되는 겁니다.

// First case:
let a
console.log(a) // undefined
a = 3

// Second case:
console.log(a) // undefined
let a = 3

// Third case:
let a = 3
console.log(a) // 3

자바스크립트에서는 호이스팅 때문에 항상 해당 범위의 맨 위에 변수를 선언하는 것을 모범 사례로 간주하는데, 변수를 선언할 때는 항상 초기화를 해주는 것이 좋습니다. 이것은 더 깔끔한 코드를 제공하고 정의되지 않은 변수를 피하는 데 도움이 될 수 있습니다.

답글 남기기