완전 초심, HTML, CSS, JavaScript를 사용해서 웹을 만들어야한다.
오늘은 이러한 세 가지를 기본적인 관점에서 다시 돌아보는 시간이다.
HTML(HyperText Markup Language)은 문서의 구조를 정의하는 마크업 언어로 웹 페이지의 기본 뼈대이다.
로직이 아닌 웹 페이지 내부 컨텐츠의 의미와 계층을 전달하는 역할을 한다.
기본적인 문서 구조는
<!DOCTYPE html>
<html>
<head>
...
...
...
</head>
<body>
...
...
...
</body>
</html>
의 형태를 띄고 있다.
해당 컨텐츠가 어떤 의미를 가지고 있는지, 이 컨텐츠들이 어떤 관계를 띄고 있는지를 명확히 해줄 필요가 있기 때문이다.
이는 시멘틱 태그와 웹 접근성의 중요성을 기반으로 설명할 수 있겠다.
웹 접근성이 프론트 서비스에서는 중요하다.
의미를 가지고 있는 태그를 부여함으로 해당 컨텐츠가 어떤 내용을 띄고 있을 지, 컴퓨터가 이해할 수 있어야 한다.
ex) <nav> - 내비게이션 메뉴, <article> - 독립형 컨텐츠 단위, <section> - 특정 컨텐츠의 그룹화
그래야 스크린 리더나 키보드만 사용하는 사용자 등 여러 다양성을 가진 유저들이 컨텐츠의 구조를 정확하게 전달받고 사용이 가능하기 때문이다.
CSS(Cascading Style Sheet)는 HTML의 요소의 색상, 위치, 폰트, 애니메이션 등을 정의하여 웹 컨텐츠를 시각적으로 꾸며준다.
어떻게 보일 것인가를 결정하며 기능에 대한 필수 사항은 아니므로 완전한 사용자 경험의 영역이다.
기본적인 구성은
선택자 {
속성 : 값
}
의 형태를 띄고 있다.
우선 스타일을 적용할 HTML 요소를 정확하게 짚어주어야 한다.
이를 선택자라고 한다.
해당 HTML의 태그 그 자체가 선택자가 될 수 있고 태그 내부에 들어있는 속성 class 및 id가 선택자가 될 수 있다.
또한 스타일 적용에도 우선순위가 있는데 그 순서는 이러하다.
인라인 스타일 > id 선택자 > class 선택자 > 태그 선택자
CSS의 일부 속성(color,font 등)은 부모의 스타일을 자식이 그대로 물려받는다. 이때 부모 자식은 HTML 태그의 관계를 뜻 한다.
CSS를 공부하는 가장 큰 이유이지 않을까 싶다.
세상에 존재하는 디바이스의 화면은 모두 일정하지 않다. 그렇기에 어떠한 디바이스의 크기라도 모두 동일한 스타일을 제공해야 사용자 경험을 고려했다 말할 수 있다.
%, vw(Viewport Width), vh(Viewport Height), min/max 등 CSS가 기본적으로 지원하는 태그들을 잘 사용해보자.
또한 flexbox나 grid를 사용하면 구현 가능하다.
더 깊게 보자면 media query나 container query를 사용해서 상위 요소의 크기에 따라 본인의 크기를 조절할 수 있다.
그렇다면 브라우저는 이 HTML과 CSS를 어떻게 화면에 렌더링 시켜주나?
간단하게 요약하자면 이렇다.
단순히 보자면 이런 렌더링 과정을 거친다.
JavaScript는 HTML을 조작하고 사용자와 상호작용하기 위한 동적 프로그래밍 언어로 HTML구조와 CSS 스타일에 행동과 동작을 부여한다.
JS의 기본 문법과 사용 방법을 정리하면 해당 포스팅이 어마무시하게 길어지기에 그 부분은 건너뛰겠다.
DOM을 조작하는 동적 페이지의 필요성이 대두 되었기 때문이다.
HTML 문서에 기입해둘 수 없는 서버의 데이터를 AJAX로 데이터를 주고 받아 요소를 동적으로 렌더링 해야한다.
JS는 브라우저의 렌더링 과정에서 이기적이다.
HTML을 파싱하여 DOM Tree를 생성하는 도중, JS를 불러들인다는 <script>를 만나는 순간 DOM 생성을 막고 본인이 렌더링 되기 때문이다.
이 때문에 defer를 사용해서 HTML 파싱 후 실행하는 순서를 보장해주거나 HTML의 <body>태그 밑에 사용한다. 아니면 async를 사용하여 순서 보장 없이 파싱과 실행을 병렬적으로 수행할 수 있다.
JS는 DOM에 접근하기 위해서 document.querySelector()등 DOM API를 사용하거나 innerText, style, classList, appendChild 등 조작 메서드로 DOM에 접근하여 조작한다.
JavaScript의 역사에는 jQuery는 빼놓을 수 없다.
순수 JS는 DOM 셀렉터와 이벤트 바인딩이 복잡하고 크로스 브라우징에서 문제가 생길 수 있기 때문에 jQuery로 이를 해결해왔다.
$()의 문법으로 document.querySelector()등을 획기적으로 줄였고 .on(), .addClass()등의 메서드로 직관적인 관계를 걸어주었고 AJAX 통신 기능까지 제공해주었다.
하지만 이런 jQuery도 번들이 무거워 성능이 다소 아쉽고, 추상화가 심해 상태관리를 복잡하게 하기 어려웠다.
그래서 개발된 혁신이 React이다.
jQuery 기반의 동적 페이지는 코드가 점점 복잡해지고, 상태 관리가 어려워졌다.
이를 해결하는 Meta(구 Facebook)에서 자체 개발한 React는 .jsx 문법을 통한 선언형 UI & 컴포넌트 기반 설계, hooks를 통한 상태가 바뀌면 UI도 바뀐다는 철학 아래 Virtual DOM을 통해 최소한의 DOM 변경을 이루어내었다.
JS는 싱글 스레드로 동시에 1개의 작업만 처리가 가능하다.
하지만 직접 JavaScript를 사용해 보았다면 조금 의아할 것이다.
분명 비동기도 지원하며 여러 코드가 거의 동시에 작동하는것 같던데??
이는 Event Loop의 구성 요소를 분해해서 설명할 수 있겠다.
예시 코드를 들어 설명해보겠다.
console.log('start');
setTimeout(() => console.log('timeout'), 0);
Promise.resolve().then(() => console.log('promise'));
console.log('end');
이러한 코드가 있다고 해보자. 출력 순서가 어떻게 될 것 같은가?
단순하게 생각하면 모두 딜레이가 없기에 start > timeout > promise > end로 위에서 아래로 무난하게 실행 되지 않을까?
하지만 실제로는 start > end > promise > timeout 순서대로 실행된다.
이를 알기 전에 키워드 몇 개만 짚고 넘어가겠다.
1. Call Stack (콜스택)
- 함수 실행 컨텍스트가 쌓이는 스택 구조
- LIFO(Last In First Out) 구조
- 현재 실행 중인 작업을 나타냄
2. Web API (브라우저 API)
- setTimeout, fetch, DOM 이벤트, Promise 등의 비동기 API는 실행 시 Web API 영역
- 완료되면 밑 두개의 Queue에 콜백이 들어감
3. Task Queue
- setTimeout, UI 이벤트 등을 저장
- Call Stack이 비면, Event Loop는 이 큐에서 하나씩 꺼내 실행
4. MicroTask Queue
- Promise.then, Promise.catch 등을 저장
- Task Queue 보다 우선순위가 높음
- Call Stack이 비는 시점마다 저장된 모든 MicroTask를 우선 실행
이러한 키워드가 정리되면 위의 코드가 어떤 단계별로 진행되는지 느낌이 올 것이다.
start, end는 Call Stack에서 바로 실행
setTimeout은 Task Queue에 저장.
Promise는 MicroTask Queue에 저장.
Call Stack이 비었으니 MicroTask Queue를 모두 실행해 Promise 실행
그 후 Task Queue에 저장된 timeout을 출력
이러한 Event Loop 단계를 통해 JS가 싱글스레드이지만 비동기 처리가 가능한 이유를 알 수 있다.
JS는 메모리에 관련해서도 자동으로 관리해주는 특징이 있는데 이 요소까지 다루면 너무 긴 포스팅이 될 수 있기에 간단히만 짚고 가겠다.
JS는 객체나 배열 같은 참조형 데이터를 Memory Heap에 저장한다.
하지만 이 메모리가 무한하지 않다, 그렇기 때문에 JS 엔진은 Garbage Collector를 통해 사용하지 않는 데이터를 자동으로 탐지하고 제거한다.
대표적으로 Mark & Sweep 알고리즘을 통해 더 이상 접근 불가능한 데이터를 수거한다.
이러한 메커니즘 때문에 개발자는 메모리 관리를 할 필요가 없다는 생각을 심어주지만 클로저, 전역 참조 등 메모리 누수를 주의해야한다.
...
...
...
프론트엔드는 결국 기초적인 HTML, CSS, JavaScript에서 부터 시작되었다. 뿌리를 잃지 않는 것이 최신 기술을 사용함에 있어 더욱 이점이 된다 생각되기 때문에 계속 공부해 나아가야겠다.