프로젝트하면서 알게 된 것들과 코딩테스트를 통해 배운 것들, 자바스크립트&제이쿼리: 인터랙티브 프론트엔드 웹 개발 교과서(Jon Duckett)와 JavaScript: The Good Parts(Douglas Crockford)를 통해 공부한 것들, 그리고 Front-end Job Interview Questions에 나와있는 질문들에 대한 대답을 간략하게 정리했습니다. 정보의 출처는 글마다 참고 링크를 걸어두었습니다. 잘못된 내용이 있다면 댓글로 달아주세요!
일반적인 것들
헝가리안 표기법
변수 이름에 데이터 타입을 접두어로 붙이는 표기법입니다. 숫자라면 nVar, 배열이라면 aVar 이런 식이으로 붙입니다. 2015년 네이버에서 인턴십을 할 때 코딩컨벤션이 헝가리언 표기법 + 캐멀케이스여서 그때 처음 접하고 사용하기 시작했는데, 요즘에는 헝가리언 표기법을 지양한다고 합니다. 가독성과 유지보수 효율성이 떨어진다네요. (참고)
블록 안에서 변수 선언
Java의 경우 한 블록 안에서만 쓰이는 변수를 코드 위쪽에 선언하지 않고 블록 안에 변수를 선언하면 코드를 파악하기 더욱 쉬워지고, 스코프를 제한할 수 있어 안전합니다. 자바스크립트의 var
타입은 블록 스코프가 아닌 함수 스코프를 적용하기 때문에 어디에 선언하든 아무런 차이가 없습니다. 하지만 let
이나 const
를 사용한다면 얘기가 달라집니다. 이 둘은 블록 스코프입니다. (참고)
점진적 향상법(Progressive enhancement)과 우아한 성능저하법(Graceful degradation)
사용자가 항상 최신 기술을 사용할 수 있는 환경에서 서비스를 사용하지는 않습니다. 특히 웹사이트를 만들 때는 최신 브라우저와 구식 브라우저를 모두 신경써야 합니다. 점진적 향상법과 우아한 성능저하법은 최신과 구식에 대응하기 위한 방법을 말합니다. (참고)
점진적 향상법은 기본적으로 구식 기술 환경에서 동작할 수 있는 기능을 구현하고, 최신 기술을 사용할 수 있는 환경에서는 더 나은 사용자 경험을 제공할 수 있는 최신 기술을 제공하는 방법입니다. 즉, 구식 환경에서도 충분히 서비스를 사용할 수 있고, 최신 환경이라면 더 나은 기능들을 사용할 수 있도록 만드는 것입니다. 구식 브라우저를 사용하는 사용자에게 100만큼의 기능을 제공하고, 최신 브라우저를 사용하는 사용자에게는 130정도의 기능을 제공하도록 웹사이트를 만든다고 보면 됩니다.
우아한 성능저하법은 점진적 향상법과 반대입니다. 이는 최신 기술을 기반으로 기능을 구현한 뒤, 구형 기술에 기반한 환경에서도 유사하게 동작하도록 만드는 방법입니다. 최신 브라우저에서는 100만큼의 기능을 제공하고, 구식 브라우저에서는 50정도의 기능만을 제공하게 웹사이트를 만드는 것입니다. img 태그에 alt 속성을 지정함으로써 이미지를 보여주지 못하는 환경에서 이미지를 텍스트로 대체하는 것이 대표적인 예시입니다.
FOUC(Flash Of Unstyled Content)
FOUC는 외부의 CSS 코드를 불러오기 전 스타일이 적용되지 않은 페이지가 잠시 나타나는 현상입니다. 특히 IE에서 자주 발생하는 현상으로, 사옹자 경험을 저하하는 요인이 됩니다. FOUC가 발생하는 주요 원인은 브라우저가 마크업에 참조된 파일들을 모아 DOM을 생성할 때 가장 빠르게 분석할 수 있는 부분(HTML)을 먼저 화면에 표시한 뒤 화면에 출력된 마크업 순서에 따라 스타일(CSS)을 적용하고 스크립트(Javascript)를 실행하기 때문입니다.
FOUC를 해결하기 위해서는 head 태그 안에 CSS를 링크하고, @import의 사용을 자제해야 합니다. 또한 자바스크립트를 head 태그 안에서 로드하는 것도 방법될 수 있습니다. (하지만 별로 추천하지 않습니다.) 어떤 방법으로도 해결되지 않는다면 FOUC가 발생하는 구역을 숨겼다가 브라우저가 준비됐을 때 다시 보여주는 방법이 있습니다. (참고)
ARIA(Accessible Rich Internet Applications)와 스크린리더
ARIA는 접근가능한 인터넷 어플리케이션을 의미합니다. 이는 웹 콘텐츠를 개발할 때 장애인을 위한 접근성 향상 방법을 정의합니다. ARIA를 사용해 웹사이트여러 곳의 접근성을 향상할 수 있습니다. ARIA는 html 태그에 role 속성을 지정하는 방식으로 사용할 수 있으며, 대부분의 브라우저와 스크린리더가 ARIA를 지원하고 있습니다. 스크린리더는 웹사이트의 구성 요소들을 읽어주는 프로그램으로, 시각장애를 가진 사용자가 컴퓨터 화면에 무엇이 있는지 인지할 수 있게 돕습니다. (참고1) (참고2)
CSS 애니메이션과 Javascript 애니메이션의 차이
자바스크립트는 메인 쓰레드가 무거운 작업을 하고 있을 때 애니메이션 처리의 우선순위를 미뤄두는 반면 CSS는 독립적인 쓰레드가 애니메이션을 처리해줍니다. 때문에 CSS 애니메이션은 최적화가 쉽습니다. 하지만 항상 CSS 애니메이션이 우수한 것은 아닙니다. UI 요소가 작은 경우 CSS를 사용하고, 애니메이션을 세밀하게 제어해야 하는 경우 자바스크립트를 사용하는 것이 좋습니다. (참고1) (참고2)
동일출처정책(Same-origin policy)과 CORS(Cross-origin resource sharing)
동일출처정책은 한 출처의 문서가 다른 출처의 문서와 상호작용하지 못하도록하는 정책입니다. 두 문서의 프로토콜, 포트, 호스트가 같으면 동일출처라고 합니다. CORS는 어떤 웹페이지에 있는 자바스크립트가 해당 도메인이 아닌 다른 도메인에 XMLHttpRequests 요청을 허용하는 기법을 말합니다. 브라우저는 서버와 통신할 때마다 서로에 대한 정보를 HTTP 헤더를 이용해 전달합니다. CORS는 HTTP 헤더에 정보를 추가해 브라우저와 서버가 서로 통신해야 한다는 사실을 알게 합니다. CORS를 통하지 않을 경우, Cross-domain 요청은 동일출처정책에 의해 브라우저가 금지합니다. CORS는 다른 출처에서 온 요청을 허용할 지 결정하기 위해 브라우저와 서버가 상호교류하는 방법을 정의한 것입니다. CORS는 W3C 명세에 포함되어 있지만 IE의 경우에는 비표준 XDomainRequest 객체를 사용하여 CORS 요청을 처리합니다. (참고1) (참고2)
코드 의존성과 DRY원칙
어떤 스크립트는 다른 추가적인 스크립트를 필요로 하기도 합니다. 이때 다른 스크립트에 의존하는 스크립트를 작성할 때 해당 스크립트에 의존성이 있다고 표현합니다. jQuery를 활용하는 스크립트는 의존성이 있다고 할 수 있습니다. 이런 경우 주석을 통해 의존성을 명시하는 것이 좋습니다.
DRY원칙은 Don't Repeat Yourself의 줄임말로, 소프트웨어 개발 원칙을 말합니다. 한국어로는 중복배제라고 합니다. 같은 작업을 수행하는 코드를 두 번 작성했다는 이를 코드 중복이라고 하는데, DRY원칙은 이러한 코드 중복을 지양하자는 원칙입니다. 코드 중복의 반대 개념으로는 코드 재사용(Code reuse)이 있습니다. 코드 재사용은 같은 코드가 스크립트의 다른 곳에서 한 번 이상 사용되는 것을 말합니다. 함수를 활용하는 것은 코드 재사용의 좋은 예로, 재사용 가능한 함수를 헬퍼 함수(Helper function)라고 합니다. 코드 재사용을 권장하기 위해 개발자들은 작은 스크립트를 작성하는데, 이때문에 코드 재사용은 코드 사이에 더 많은 의존성을 만듭니다.
HTML
DOCTYPE
DOCTYPE은 문서형을 말하며, 해당 문서가 어떤 버전의 어떤 마크업 언어로 구성되어있는지를 의미합니다. DOCTYPE을 선언하는 것을 DTD(Dodumenttation Type Declaration)라고 한다. DTD는 각 마크업의 각 버전에서 사용가능한 태그나 속성 등을 정의하기 때문에 문서의 최상단에 위치해야 합니다. HTML5 이전 버전은 SGML(Standard Generalized Markup Language)에 기반했기 때문에 DTD 참조가 필수적입니다. HTML5는 DTD 참조가 필요하지 않으며, 하위 호환을 위해 <!DOCTYPE html>
만으로 선언합니다. (참고)
표준모드(Standard mode)와 호환모드(Quirks mode)
표준모드와 호환모드는 브라우저가 가진 두 가지 렌더링 모드입니다. 브라우저는 DTD에 따라 렌더링할 모드를 선택하는데, 이 과정을 Doctype sniffing또는 Doctype switching이라고 합니다. 브라우저가 출력하고자 하는 문서가 최신이라면 W3C나 IETF의 표준을 엄격히 준수하는 표준모드로 렌더링을 합니다. 반면 문서가 오래된 버전이라면 호환모드로 렌더링합니다. 호환모드는 이전 세대의 브라우저에 맞는 비표준 규칙을 문서에 적용해 오래된 웹페이지들이 최신 브라우저에서 깨져보이지 않게 합니다. 브라우저는 문서가 최신인지 아닌지를 DTD로 판단한다. 만약 DTD가 존재하지 않거나 일부가 누락된 경우 호환모드로 문서를 해석합니다. 또한 IE의 경우 DTD 앞에 주석이나 다른 문자가 들어갔을 때도 문서를 호환모드로 해석한다. (참고)
XML과 XHTML
XML과 XHTML모두 웹 문서 규격입니다. XML은 W3C에서 여러 특수 목적의 마크업 언어를 만드는 용도에서 권장되는 다목적 마크업 언어입니다. XML은 문서 상의 데이터 이름과 값 등을 구분하기 위해 만들어졌는데요, XML은 SGML(참고로 SGML은 인터넷이 등장하기 이전에 만들어졌다.)을 기반으로한 HTML의 한계를 극복하여 XHTML을 이끌었습니다. XML을 기반으로 한 HTML을 만든 셈입니다. XHTML은 XML의 문법을 따르며, HTML 문법과 매우 유사하지만 더 엄격합니다.
XHTML이 더 표준인 것처럼 보이지만 사실 그렇게 권장하지 않는 사람들도 있습니다. 일단 HTML의 호환성이 더 높기 때문입니다. XHTML은 1.1버전부터 비표준이나 비권장 태그를 호환하지 못하게 되면서 지나치게 엄격하다는는 비판을 받았습니다. IE가 XHTML을 해석하지 못하는 것 역시 XHTML의 호환성을 떨어뜨리는 요인이 됐습니다. 또한 XHTML과 HTML의 요소와 속성에는 차이가 거의 없습니다. 단지 HTML에서는 <br>
로 써도 되지만 XHTML에서는 반드시 <br/>
로 써야한다는 정도의 문법이 다를 뿐입니다. 이런 점에서 굳이 XHTML을 써야할 기술적 이유는 없다는 것입니다. 한편 HTML5가 발표되면서 XHTML은 거의 사용되지 않고 있습니다. (참고)
data-* 속성
HTML5에서 새로 추가된 data- 속성은 커스텀 데이터 속성으로, 개발자가 임의로 이름을 붙일 수 있는 속성입니다. data- 속성은 html 태그 상에서 별다른 작용을 하지 않습니다. 자바스크립트가 DOM의 데이터에 접근하거나 서버에서 받아온 데이터를 활용해야 할 때 사용됩니다. (참고)
Cookie, sessionStorage, localStorage
쿠키(Cookie), 세션 저장소(sessionStorage), 로컬 저장소(localStorage)는 브라우저에 데이터를 저장하기 위한 공간들입니다. HTML5 이전에는 쿠키를 주로 사용했습니다. 하지만 쿠키는 많은 양의 데이터를 저장할 수 없고, 동일한 도메인에 페이지를 요청할 때마다 서버로 함께 전송되며, 변조가 쉬워 보안이 취약합니다. 그래서 HTML5부터는 저장소 객체(Storage object)를 정의하고 있습니다. 저장소 객체는 세션 저장소와 로컬 저장소 두 가지를 제공합니다. 로컬 저장소에 데이터를 자장하면 창이나 탭을 닫아서 세션이 종료돼도 데이터가 보존되고, 열려 있는 모든 창이나 탭이 데이터를 공유하게 됩니다. 세션 저장소는 반대입니다. 일반적으로 브라우저는 저장소에 5mb 정도의 공간을 할당하며, 데이터는 키-값 쌍(KVP; Key-Value Pair)을 이용하는 저장소 객체의 속성으로 저장됩니다. 또한 브라우저는 데이터를 보호하기 위해 동일출처정책에 의거, 서로 다른 페이지는 같은 도메인에 저장된 데이터에만 접근이 가능하도록 제한하고 있습니다. (참고1) (참고2) (참고3)
script 태그의 async와 defer
script 태그에 async 속성과 defer 속성이 추가된 것은 HTML5부터입니다. 브라우저가 웹 문서에서 외부 스크립트를 불러오는 script 태그를 만나면 해당 스크립트를 내려받아 해석하고 실행할 때까지 HTML 코드 파싱 작업을 뒤로 미룹니다. 그래서 무거운 스크립트 문서를 해석할 때는 페이지 전체가 느려지는 현상이 발생합니다. 그런데 script 태그에 async나 defer 속성을 지정하면 마크업 코드 작업을 중단하지 않고 스크립트를 동시에 내려받게 됩니다. defer는 마크업 파싱을 마친 다음 스크립트를 실행하며, async는 스크립트를 내려받는 즉시 스크립트를 실행합니다. (참고)
Progressive rendering
Progressive rendering은 콘텐츠를 빠르게 화면에 렌더링하는 기법입니다. 브로드밴드 인터넷이 등장하기 전에 매우 중요한 기술이었고, 모바일 플랫폼을 고려한다면 여전히 무시할 수 없는 부분입니다. 가시영역의 이미지만 로딩해주는 jQuery 플러그인 Lazy loading이 좋은 예입니다. (참고)
CSS
Reset CSS
모든 브라우저에서 통일된 화면을 볼 수 있도록 CSS의 기본값을 초기화하는 것을 말합니다. Eric Meyer's Reset과 Normalize.css가 주로 쓰입니다.
BFC(Block Formatting Context)와 IFC(Inline Formatting Context)
모든 HTML 요소는 사각형 박스 형태를 취하고 있습니다. 박스는 Box-model이라는 모델을 가지고 있습니다. CSS요소에는 display가 존재하는데, 이는 블록(Block)과 인라인(Inline) 두 가지 값을 가집니다. block은 블록 레벨 요소(Block-level elements)를 의미하며, 블록 레벨 요소는 BFC에 속하는 박스입니다. 블록 레벨 요소 박스는 수직으로 계속 쌓입니다. inline은 인라인 레벨 요소(Inline-level elements)를 의미합니다. 인라인 레벨 요소는 인라인 레벨 박스를 생성하며, 이는 IFC에 속합니다. 인라인 레벨 요소 박스는 수평으로 계속 쌓입니다. 또한 인라인 레벨 박스에 border나 padding이 눈에 보이더라도 사실은 line-height에 의해 높이가 조절됩니다. inline-block는 특이한데요, 이 요소는 인라인 요소처럼 수평으로 쌓이지만 블록 레벨 요소의 박스처럼 높이를 계산합니다. 즉 line-height에 의존하지 않습니다. (참고)
클리어링(Clearing) 기술
클리어링는 float 속성이 주변 요소의 배치에 영향을 미치지 않도록하는 것입니다. float 속성을 가진 요소는 자신의 위치를 주변 콘텐츠로부터 상대적으로 배치하기 때문에 다른 콘텐츠가 그 주위로 흐르게 됩니다. 클리어링를 통해 이를 방지할 수 있는데요, 여기에는 4가지 방법이 있습니다.
첫 번째는 float에 float으로 대응하는 것입니다. 자식 요소 뿐만 아니라 부모 요소에게도 float 속성을 지정하면 부모 요소가 자식 요소의 높이를 반영합니다. 단, 이렇게 하면 부모 요소의 너비가 자식 요소를 담을 정도로 줄어듭니다.
두 번째는 float에 overflow 속성으로 대응하는 것입니다. 자식 요소의 높이를 부모에게 반영하는 방법으로, 부모 요소의 overflow 속성에 auto 또는 hidden 값을 부여합니다. 하지만 auto 값의 경우 자식 요소가 부모 요소보다 클 때 스크롤바가 생기며, hidden 값의 경우 넘치는 부분이 잘려버립니다.
세 번째로 float을 빈 엘리먼트로 클리어링할 수도 있습니다. float이 지정된 요소 뒤에 빈 요소를 추가하고 빈 요소의 clear 속성에 both 값을 부여하는 것인데, 의미없는 요소를 사용하게 되기 때문에 권장하지 않습니다.
마지막으로 float을 가상 선택자 :after
로 클리어링하는 방법이 있습니다. 가상 선택자는 가상 클래스(pseudo-class)와 가상 요소(pseudo-element)로 나뉩니다. 가상 클래스는 특정 요소에 아무런 class를 부여하지 않았지만 마치 class를 변경한 것과 같은 역동적인 효과를 낼 수 있는 것들입니다. :hover
, :active
, :focus
등이 여기에 속합니다. 가상 요소는 존재하지 않는 요소를 가상으로 생성하는 선택자입니다. :first-line
, :before
, :after
가 여기에 속합니다. 가상 요소는 HTML 문서에 존재하지 않는 콘텐츠를 출력하기도 합니다. 이렇게 가상 요소를 생성한 다음 display: block; clear: both;
처리를 하면 깔끔하게 클리어링을 할 수 있습니다. 가장 권장하는 방법입니다. (참고)
Image Replacement
요소를 이미지로 교체하는 기법입니다. 기본적으로 다음과 같이 하면 됩니다:
.elements {
background-image: url("image.png");
}
그리고 A History of CSS Image Replacement에서 더 많은 예시를 볼 수 있습니다.
IE box model과 W3C box model의 차이
브라우저에 따라 박스 모델이 달라 요소에 지정된 너비와 높이가 같아도 서로 다르게 렌더링됩니다. 박스 모델은 기본 적으로 margin, border, padding, content로 구성되어 있습니다.
W3C의 표준 박스 모델은 콘텐츠의 너비, 높이만을 width와 height로 계산하는 반면, IE의 박스 모델은 콘텐트와 padding, border를 포함한 너비와 높이를 width, height로 계산합니다. 이를 위한 해결책은 (1)DTD를 통해 브라우저가 쿽스 모드로 동작하지 않도록 하거나 (2)wrapper 요소를 사용해 wrapper 요소에 width, height 값을 할당하고 내부 엘리먼트에 padding, border 값을 할당하거나 (3)Conditional comment를 추가하거나 (3)css hack을 사용하는 방법이 있습니다. (참고)
그리드 시스템 (Grid system)
반응형 웹페이지를 만들기 위해 자주 쓰이는 기술입니다. 페이지 위에 격자를 그리고 그 위에 요소를 그 위에 배치하는 방법으로, 기술적인 이유만이 아니라 디자인적인 이유로도 사용합니다. 보통 그리드 시스템을 위해 부트스트랩을 사용합니다.
CSS 전처리기
CSS 문서가 방대해짐에 따라 작업 효율을 높이기 위해 등장한 기술입니다. 전처리기를 사용하면 CSS 상의 반복적인 부분을 스크립트나 변수로 처리할 수 있고, 다양한 연산이 가능해집니다. Less나 Sass가 대표적인 CSS 전처리기입니다.
@bgColor: #DFDFDF;
body {
background-color: @bgColor;
}
위는 Less의 예시입니다. 컴파일을 하면 body의 background-color 값이 #DFDFDF로 치환된 CSS 코드를 얻을 수 있습니다.
반응형 디자인(Responsive design)과 적응형 디자인(Adaptive design)
반응형 디자인은 디스플레이의 너비에 따라 레이아웃을 변형시키고, 적응형 디자인은 고정적 레이아웃을 가집니다. 반응형웹이 미디어쿼리를 사용해 스타일 분기를 나누는 방법이라면 적응형 웹은 디바이스를 체크해 그 디바이스에 최적화된 마크업을 호출하는 방법입니다. (참고)
Javascript
추상적 같음 비교(Abstract equality comparison)와 엄격한 같음 비교(Strict equality comparison)
추상적 같음 비교(==
)는 두 변수를 같은 데이터 타입으로 변환한 다음 값을 비교하는 반면, 엄격한 같음 비교(===
)는 두 변수의 값과 데이터 타입을 함께 비교합니다. 따라서 값과 타입이 완전히 일치해야 true를 반환합니다. 엄격한 같음 비교를 사용한 비교 결과는 예측이 쉽고 타입 강제가 일어나지 않기 때문에 추상적 같음 비교를 사용하는 것보다 낫습니다. (참고)
고급 예외처리(Advanced exception handling)
try...catch
와 throw
로 고급 예외처리를 할 수 있습니다. 참고로 프로그램 실행 중에 발생하는 오류는 예외(Exception)이라고 하며, 코드의 문법적 오류는 에러(Error)라고 한다. try...catch
는 구동 중 코드상에서 발생할 수 있는 오류들을 잡아준다.
try {
// try 블록은 예외가 발생할 수도 있는 부분
} catch(e) {
// catch 블록은 try 블록에서 예외가 발생했을 때 호출되는 부분
// 객체 e는 name과 message 속성을 가짐
} finally {
// 선택적으로 finally 블록을 추가할 수 있음
// 예외 발생 여부를 떠나 무조건 실행되어야 할 부분
}
throw
문은 강제로 예외를 발생시킬 때 사용합니다. throw
의 문법은 다음과 같습니다:
throw expression;
표현식의 값은 숫자, 문자 등 어떤 것이든 상관없습니다. throw
가 발생하면 가장 가까운 catch
블록으로 이동합니다. 다음과 같이 사용하면 됩니다:
try {
if(a > 100) {
throw "Value too high";
}
} catch(e) {
alert(e);
}
Event delegation
이벤트 리스너를 등록하는 것은 DOM 입장에서는 부담스러운 일이며, 리소스를 상당히 잡아먹는 작업입니다. 그래서 요소 하나하나에 이벤트를 등록하는 대신 위임을 하는 것이 바람직합니다. 자식 요소들을 감싼 부모 요소에 이벤트를 등록해 이벤트를 위임하면 각각의 자식 요소들에 이벤트를 등록하는 효과를 낼 수 있습니다. 이벤트를 위임하면 리소스를 절약할 수 있을 뿐만 아니라 DOM 트리에 새로운 요소를 추가했을 때 코드를 추가할 필요가 없어지고, 코드의 양을 줄일 수도 있습니다. 다음은 ul 요소의 자식 요소인 li 요소를 클릭하면 해당 li 요소가 사라지는 코드입니다.
<ul id="todoList">
<li>TODO: A</li>
<li>TODO: B</li>
<li>TODO: C</li>
</ul>
아주 평범한 리스트입니다.
function getTarget(e) {
if(!e) { // event 객체가 존재하지 않으면
e = window.event; // IE의 event 객체를 사용
}
return e.target || e.srcElement; // 이벤트가 발생한 요소를 가져옴
}
function itemDone(e) {
var elTarget, elParent;
elTarget = getTarget(e); // 이벤트가 발생한 요소 가져옴 (li)
elParent = target.parentNode; // 해당 요소의 부모 요소를 가져옴 (ul)
elParent.removeChild(elTarget); // 이벤트가 발생한 요소를 제거함 (li)
}
(function(){
var el = document.getElementById('todoList');
if(el.addEventListener) { // 이벤트 리스너가 지원되면
el.addEventListener('click', function(e) { // 클릭 이벤트에 리스너를 지정
itemDone(e);
}, false); // 이벤트 버블링을 사용
} else { // 이벤트 리스너가 지원되지 않으면
el.attachEvent('onclick', function(e) { // IE의 onclick 이벤트를 사용
itemDone(e);
}
});
})();
IE까지 고려한 코드입니다. 하나의 이벤트 리스너로 요소 3개를 제어하고 있습니다다. jQuery는 보다 편하게 이벤트를 바인딩할 수 있도록 .delegate()
메소드를 제공하고 있습니다.
Prototype 기반 상속
자바스크립트는 클래스라는 개념이 없기때문에 프로토타입(Prototype)이 클래스를 대신해 객체지향 프로그래밍의 핵심을 맡습니다. (ES6부터 클래스 문법이 추가됐기 때문에 프로토타입에 직접 접근할 필요가 없어졌습니다. 그래도 프토로타입은 자바스크립트의 기반이기 때문에 알아두는 것이 좋습니다.) 자바스크립트에서는 이 프로토타입을 통해 다른 객체지향 언어에서 쓰이는 클래스를 구현할 수 있습니다.
function Player() {
this.hp = 100;
this.mp = 50;
}
var kim = new Player();
var park = new Player();
console.log(kim.hp); // 100
console.log(kim.mp); // 50
console.log(park.hp); // 100
console.log(park.mp); // 50
kim
과 park
은 hp
와 mp
를 각각 100, 50씩 가지고 있습니다. 만약 객체를 100개 만들면 200개의 변수가 메모리에 할당됩니다. 프로토타입을 활용하면 메모리를 아낄 수 있습니다.
function Player() {}
Player.prototype.hp = 100;
Player.prototype.mp = 50;
var kim = new Player();
var park = new Player();
kim
과 park
은 프로토타입에 연결된 Player
객체의 값을 가져다 쓸 수 있습니다. hp
와 mp
를 kim
과 park
이 공유하는 것입니다. 이러한 매커니즘으로 프로토타입을 이용해 상속을 구현할 수 있습니다. (참고1) (참고2)
null, undefined, undeclared의 차이
이건 종종 헷갈리는 경우가 있습니다. null은 어떠한 값도 가리키지 않는다는 의미의 원시값입니다. 변수를 선언하고 null을 할당한 경우 해당 변수가 어떠한 값도 가지지 않았다는 의미가 됩니다. undefined는 변수가 선언됐지만 값이 할당되지 않았다는 것을 의미하는 값입니다. 즉, null은 개발자로부터 할당되는 값이고, undefined는 아예 할당을 하지 않은 상태입니다. undeclared는 변수 자체가 선언되지 않았다는 의미입니다. 콘솔에서는 undefined와 똑같이 표기되기 때문에 변수가 초기화되지 않았다는 것인지, 아예 선언되지 않았다는 것인지 확인할 필요가 있습니다.
클로저(Closure)
자바스크립트에서는 함수 안에 또 다른 함수를 선언할 수 있는데, 함수 안의 함수인 내부함수(inner function)는 외부함수(outer function)의 지역변수에 접근할 수 있습니다. 외부함수의 실행이 끝나서 외부함수가 소멸된 이후에도 내부함수는 외부함수에 접근할 수 있습니다. 이러한 메커니즘을 클로저라고 합니다. MDN에 클로저를 활용한 재밌는 예제가 있습니다:
function makeAdder(x) {
return function(y) {
return x + y;
};
}
var add5 = makeAdder(5);
var add10 = makeAdder(10);
console.log(add5(2)); // 7
console.log(add10(2)); // 12
여기서 makeAdder(x)
는 x
를 인자로 받아서 새로운 함수를 반환합니다. 그리고 반환되는 내부 함수는 y
를 인자로 받아 x + y
를 반환합니다. (참고1) (참고2)
자바스크립트 모듈 패턴(Module pattern)
모듈 패턴(Module pattern)은 코드 설계 방법을 말합니다. 여기서는 객체를 public과 private으로 나누는 캡슐화가 핵심입니다. 자바스크립트는 public이나 private와 같은 접근 제한자를 제공하지 않지만, 클로저를 이용해 구현할 수 있습니다. private method는 코드 접근을 제한할 수 있을뿐만 아니라 추가적인 자바스크립트가 다른 스크립트와 이름이 충돌하는 것을 막을 수 있습니다. 매우 기본적인 방법은 모든 코드를 익명함수 안에 집어넣어 private 스코프로 만드는 것입니다. 하지만 이렇게 하면 코드를 재사용하기 불편해지기 때문에 별도의 네임스페이스를 적용해야 합니다. (참고1) (참고2
네이티브 객체(Native object), 호스트 객체(Host object), Built-in 객체
객체는 크게 3가지로 구분됩니다. 네이티브 객체는 ECMAScript 명세에 정의된 객체로, 자바스크립트의 모든 엔진에 구현된 표준객체입니다. BOM(Browser Object Model)과 DOM 등은 모두 네이티브 객체며, 자바스크립트 엔진을 구동하는 측에서 빌드되는 객체입니다. 호스트 객체는 개발자가 정의한 객체입니다. 마지막으로 Built-in 객체는 자바스크립트 엔진을 구성하는 기본 객체들을 포함합니다. Number, String, Array, Date 등의 내장객체들이 있습니다. (참고)
기능 검출(Feature detection)과 기능 추론(Feature inference)
기능 검출이란 스크립트가 호출하는 기능을 사용자의 브라우저가 지원하는지 체크하는 것을 말합니다. 다음은 브라우저가 GPS를 지원하는지 확인하는 코드입니다.
if(navigator.geolocation) {...}
기능 추론도 기능 검출처럼 브라우저가 특정 기능을 지원하는지 체크하는 것입니다. 하지만 'A기능을 지원하면 B기능도 지원할 것이다.'라는 추론이 바탕이 됩니다. 별로 좋지 않은 방법입니다. (참고)
호이스팅(Hoisting)
호이스팅은 인터프리터가 스크립트를 해석할 때, 변수의 정의가 스코프에 따라 선언과 할당으로 분리되어 선언을 항상 컨텍스트의 최상위로 끌어올리는 것을 의미합니다. 즉, 변수를 어디에서 선언하든 인터프리터는 최상단에서 선언한 것으로 해석합니다. 이는 변수 뿐 아니라 함수에도 적용됩니다. 따라서 상단에서 함수를 호출하고, 하단에서 함수를 정의해도 기능적인 문제는 없습니다. (참고)
이벤트 흐름(Event flow)
HTML 요소가 다른 요소의 내부에 중첩되어 있을 때 자식 요소를 클릭하면 부모 요소를 클릭한 셈이 됩니다. 이처럼 이벤트는 흐름을 가지고 있으며, 이것을 이벤트 흐름이라고 부릅니다. 이벤트 흐름에는 두 가지 방식이 있습니다. 먼저 이벤트 버블링(Event bubbling)은 이벤트가 직접적으로 발생한 노드로부터 시작해 바깥 노드로 이벤트가 퍼져 나가는 방식을 말합니다. 대부분의 브라우저가 기본적으로 이 방식을 지원합니다. 반대로 이벤트 캡쳐링(Event capturing)은 바깥 노드부터 시작해서 안쪽으로 퍼지는 방식입니다. IE8 혹은 그 이전 버전에서는 지원되지 않습니다.
document load event와 DOMContentLoaded event
DOM을 제어하는 스크립트는 마크업의 모든 요소에 대한 처리가 끝난 뒤에 로드되어야 합니다. 그래서 보통 body 태그 최하단에서 스크립트를 불러오도록합니다. 또 다른 방법은 이벤트를 이용하는 것입니다. document load event는 페이지의 모든 리소스가 로드된 이후에 실행됩니다. 때문에 구동이 지연되어 사용자 경험을 저하할 수 있습니다. 반면 DOMContentLoaded event는 스크립트 로드를 마치고 실행이 가능한 시점에 바로 실행됩니다. (참고)
조건부 삼항 연산자(Conditional ternary operator)
보통 그냥 '삼항 연산자'이라고 부릅니다. if문을 축약해서 쓸 수 있는 유용한 연산자이지만, 과하게 사용하면 코드의 가독성을 떨어뜨릴 수 있습니다.
var a = 1, b = 2;
console.log(a < b ? "True" : "False"); // logs "True"
console.log(a > b ? "True" : "False"); // logs "False"
a < b ? (
console.log("True");
alert("True");
) : (
console.log("False");
alert("False");
); // logs "True"
조건이 true면 전자의 값을 반환하고, false면 후자의 값을 반환합니다.
use strict
ES6에서 새로 추가된 기능으로, 스크립트에 "use strict;"
구문을 추가하면 strict mode에서 실행하게 됩니다. strict mode는 코딩 실수를 찾아 예외를 발생시키고, 전역 객체에 접근하는 것과 같은 위험한 액션을 막습니다. 스크립트 전체에 적용할 수도 있고 특정 함수에만 적용할 수도 있습니다. (참고)
Call stack과 Task queue
자바스크립트 엔진은 요청이 들어올 때마다 요청을 순차적으로 call stack에 담아 하나씩 처리합니다. call stack은 하나만 존재하기 때문에 요청도 하나씩만 처리할 수 밖에 없습니다. task queue는 처리해야 하는 task를 임시로 저장해두는 큐입니다. call stack이 비워지면 task queue에 있던 task들이 순서대로 call stack에 push됩니다. 가령 setTimeout()
함수로 10초의 딜레이를 둔다면, 그동안 setTimeout()
이 처리할 task는 task queue에 쌓이고 다른 부분의 스크립트들이 실행됩니다. 따라서 딜레이를 0으로 줘도 setTimeout()
의 task는 다른 것들보다 나중에 처리됩니다. (참고)