https://frontendmasters.com/guides/front-end-handbook/2024/
The Front End Developer/Engineer Handbook 2024
The handbook provides an in-depth overview of the skills, tools, and technologies necessary to excel as a front-end developer / engineer.
frontendmasters.com:443
이 포스트는 위 링크에 해당하는 콘텐츠를 읽고 개인적으로 정리하고 싶은 내용들을 추려내어 정리한 글입니다.
위 페이지에서는 프론트엔드 개발자가 알아야 할 전반적인 지식을 정리하였습니다. 프런트엔드 개발자를 희망하거나 프런트엔드 개발자로서 업무 중이지만 전반적인 지식들에 대한 복습을 하고 싶으신 분들에게 매우 유용한 콘텐츠입니다. 관심 있으신 분들은 한 번쯤 읽어보시면 좋을 것 같습니다.
Overview of Field of Work
프론트엔드 개발자는 어떠한 일을 하는가?
User Interface와 User Interact를 개발하는 업무를 한다.
여기서 User Interface는 사용자가 애플리케이션을 사용할 때 보이는 부분들이라고 생각하면 되고 User Interact는 사용자가 User Interface와 상호작용하는 것을 의미한다.
ex : 화면에 인풋박스와 버튼이 있다. (User Interface) 사용자가 인풋박스에 이메일을 입력하고 버튼을 클릭하면 입력된 이메일로 메일이 전송이 된다. (User Interact)
Web site vs Web Application
웹 사이트는 정적으로 정보 전달 기능을 하고 웹 어플리케이션은 동적으로 사용자와 상호작용을 하며 도움을 주는 툴이다.
여기서 '정적', '동적', '상호작용'이라는 말은 개발 관련 글을 읽다 보면 자주 등장하는 표현인데, 처음에는 이 표현들이 직관적으로 와닿지 않았다. 위 표현들은 다음과 같이 이해하면 도움이 될 것 같다.
'정적'이란 표현은 변경이 없이 (혹은 긴 텀을 가지고 변경이 되는) 처음 만들어진 상태로 전달이 되는 것. 예를 들어 나무위키에 작성된 글들은 글은 읽는 도중에 실시간으로 변경이 되거나 구성이 달라지지 않고 사용자는 작성된 그대로 접하게 된다.
반면 '동적'이라는 표현이 나는 처음에 컨텐츠가 애니메이션처럼 움직인다는 느낌으로 받아들였었는데 그렇다기보다는 사용자가 직접 관여하여 콘텐츠를 변화시킬 수 있는 느낌으로 이해하면 좋다. 예를 들어서 사용자가 블로그에 글을 작성하고 수정하면서 콘텐츠를 추가 및 수정하는 것이 동적인 콘텐츠의 한 예다.
마찬가지로 google docs같은 웹 애플리케이션은 사용자가 직접 콘텐츠를 생성, 수정, 삭제를 하면서 '동적'으로 변경시킨다.
따라서 웹 사이트는 사용자가 수동적으로 단순히 컨텐츠를 접하는 것이고 웹 애플리케이션은 사용자가 콘텐츠를 변경시키고 변경된 콘텐츠를 사용자가 다시 접하게 되는 즉 상호작용을 하여 사용자에게 도움이 되는 기능을 제공하는 것이다.
하지면 실제로는 두 용어의 경계가 모호하게 사용이 됨으로 그냥 이해만 하고 넘어가면 좋을것 같다.
Native Applications from Web Technologies
웹 기술을 사용하여 네이티브 어플리케이션을 구축할 수 있다. 대표적인 예시가 Discord이다.
Discord는 웹 상에서도 어플리케이션으로 동작하고 데스크톱 앱으로 다운로드하여 사용할 수 있는데 이 데스크톱 앱은 electron이라는 프레임워크로 개발이 되었다. 여기서 이 electron은 html, css, js로 데스크톱 앱을 개발할 수 있게 도와주는 프레임워크이다.
따라서 웹 기술을 이용하여 웹 어플리케이션만 만들 수 있는 것이 아니라 데스크톱 앱과 모바일 앱 즉, 네이티브 앱 또한 개발할 수 있다.
웹 기술은 다양한 분야로 뻗어나갈 수 있는 많은 가능성이 있는 기술임으로 많은 기회를 잡을 수 있는 좋은 기술이다.
Progressive Web App (PWA)
PWA는 쉽게 말해서 앱처럼 여러 디바이스에 ( 모바일, pc ) 설치하여 사용할 수 있는 웹 사이트라고 생각하면 된다.
백문이 불여일견. 다음 사이트를 보자.
Starbucks®
app.starbucks.com
우리가 아주 잘 아는 스타벅스의 pwa 웹 페이지이다. 상당히 멋있는 사이트이지만 브라우저에서만 볼 수 있다면 여타 다른 웹 사이트와 다를 바가 없다.
하지만 PWA는 디바이스에 설치가 가능하다는 것. Safari를 사용해서 데스크톱 앱처럼 설치를 해보자.
Safari에서 공유하기 버튼을 클릭하면 "Add to Dock"을 볼 수가 있다. 이를 클릭하면 Dock에 스타벅스 App이 추가가 된 것을 볼 수 있다.
그뿐만이 아니라 실제 앱처럼 등록이 된 것도 확인이 가능하다.
웹 사이트를 PWA로 만들 수 있다는 것은 웹 개발자에게 엄청난 장점이 될 수 있다. 웹 코드로 웹 페이지뿐 아니라 다양한 디바이스에서 네이티브 앱처럼 사용할 수 있는 어플을 개발할 수 있으니 말이다.
참고로 왜 이름이 "progressive" web Application인지는 다음과 같은 이유가 있다고 한다.
사용자의 환경에 맞추어 점진적으로 발전하며 최적의 경험을 제공할 수 있도록 하자는 웹 개발 철학에서 나온 개념인데, 예를 들자면 오래된 버전의 브라우저를 사용하면 그 브라우저가 지원하는 기능까지의 내용만 제공을 하고 최신 브라우저를 사용하면 더 많은 기능 (푸시 알림, 오프라인 모드 )등을 제공한다.
사실 PWA를 어플처럼 사용할 수 있는 웹 사이트라고 이해하고 있기 때문에 위 설명이 크게 와닿진 않는다.
PWA에 대한 조금 더 자세한 설명은 아래 영상을 참고하면 좋을 것 같다.
https://www.youtube.com/watch?v=FEBkne7Nyu4
Areas of Focus
웹 개발자로서 꼭 갖춰야 하는 능력들은 다음과 같다.
- Responsive한 web 구축 능력
- Interactive, dynamic user interface
- SEO
- performance 향상, 최적화 기법
- Cross-browser compatibility
- Web standards, Accessibility
- Communication 능력
- 디자인 툴에 익숙해지기
- 테스트, 디버그 툴 활용 능력
- 지속적으로 학습하고 습득하는 능력
Foundational Aspects
javascript 엔진이라는 것이 있다. 이는 여러 환경에서 ( 브라우저 ) javascript 코드를 실행시키는 엔진인데 파싱, 컴파일, 실행, 메모리 관리를 수행하면서 javascript를 실행시킨다.
대표적인 javascript엔진으론 v8엔진 (크롬) , spidermonkey (firefox), javascriptcore (safari), hermes ( react-native)가 있다.
Core Competencies
- HTML
markup 언어이고 웹페이지의 구조와 layout을 정의하는 언어이다. HTML사용 능력은 웹 개발을 할 때 가장 기초적이고 필수적인 능력이다.
모든 HTML 지식을 암기할 필요는 없지만 그래도 프론트엔드 업무를 하기 위해 어느정도는 자유자대로 사용할 수 있을 필요가 있다. - Document Object Model (DOM)
웹 문서를 프로그래밍적으로 다루는데 사용되는 기본적인 interface이다. ( fundamental programming interface for web documents )
DOM은 웹 페이지를 계층적인 노드의 트리로 개념화하여 동적인 상호작용과 변경,조작 할 수 있도록 한다.
아주 대략적으로 웹 페이지(HTML)를 같은 내용이지만 프로그래밍적으로 표현한 표현법이라고 이해하였다. - JavaScript Web APIs
웹 브라우저에 내재되어 있는 API의 집합. 이는 개발자가 브라우저, 기본적인 운영체제와 상호작용을 할 수 있도록 해준다.
ex : Fetch API, Geolocation API, LocalStorage API, Canvas API - JavaScript Object Notation (JSON)
번역하면 자바스크립트 객체 표기법. 즉 자바스크립트의 객체와 같이 생긴 표기법이고 이는 데이터 교환시 사용하는 데이터의 표현 형식 중 하나이다. 사람이 읽고 쓰기 쉽고, 기계(컴퓨터)가 parse하고 생성하기도 쉬워서 많이 사용 된다.
이름에 JavaScript가 사용 되지만 다른 언어에서도 사용 가능한 데이터 표현 방식이다.
아래 예시 참고
{
"name":"marunose",
"age" : 28,
"gender" : "male"
}
- ES Modules
모듈화된 JavaScript 코드를 위한 공식 표준
ES Modules은 재사용에 효과적이도록 JavaScript 코드의 구조 설계와 조직하는 방식을 제공한다.
또한 JavaScript 코드를 더 작은 modular 파일로 만들어서 더 정돈된 coding 구조를 작성하게끔 도와준다.
위 글을 읽고 다음과 같은 궁금증이 들었다.
"재사용에 효과적인 구조 설계와 조직하는 방식이 뭐지?"
"더 작은 modular 파일은 어떤 모습이지?"
"그게 어떻게 더 정돈된 coding 구조를 작성하게끔 도와주지?"
이는 ES Module을 사용하기 전(즉, 모듈 시스템이 JavaScript에 도입되기 전) 상황을 보면 와닿을 것이다.
모듈 시스템이 없던 시절에는 스크립트는 전부 전역 범위에서 실행이 되었다. 그렇게 스크립트는 각 변수들을 서로 공유하며 사용했었다. 그렇다보니 당연히 변수끼리 충돌이 나기도 하고 유지보수도 여려웠을 것이다. 이러한 문제를 해결하기 위해 JavaScript의 모듈 시스템이 탄생하였고 각 모듈은 각자의 범위를 가지며 다른 모듈에서 필요시 불러와서 사용할 수 있도록 해주었다.
CommonJs, AMD등 모듈 시스템이 있었지만 이는 비공식적이었고 이후 ECMA에서 공식적으로 ES Module을 지원해주었다.
좀 더 자세한 설명이 궁금하신 분을 위해 링크를 첨부한다.
모듈 시스템의 역사, 그리고 ESM
https://duck-blog.vercel.app/blog/js/javascript-module-history - Node.js
JavaScript를 웹 브라우저를 넘어 서버사이드에서 실행시킬 수 있도록 하는 오픈소스, cross-platform, JavaScript runtime 환경
아래는 Node.js의 특징들이다.
Event-driven : 이벤트가 발생할 때만 작업 실행,
Non-blocking I/O model : 파일 읽기, 네트워크 요청 등 I/O 작업을 비동기적으로 처리하여, 작업이 완료될 때 까지 기다리지 않고 다른 작업 처리 가능,
데이터 흐름이 많고 실시간 처리가 필요한 어플리케이션에 적합,
여러 디바이스간 작업을 효율적으로 분산 처리할 수 있는 구조 - understanding of various image file types
다양한 이미지 타입들의 종류와 용도를 파악하면 적재적소에 적절한 이미지 타입을 사용할 수 있다.
JEPG | PNG | GIF | |
특징 | - 손실압축(Lossy Compression)을 사용하여 파일 크기를 줄인다. - 고해상도 사진이나 복잡한 색감을 가진 이미지에 적합 - 파일 크기가 작아 웹에서 빠르게 로드 됨 |
- 무손실 압축(Lossless Compression)을 사용하여 품질을 유지. - 투명도를 지원 - 파일 크기가 JPEG보다 클 수 있음 |
- 256색 팔레트를 사용하는 제한적인 색상 지원 - 애니메이션을 지원하지만, 품질이 낮고 파일 크기가 클 수 있음 |
용도 | - 웹에서 사진,배너,썸네일 등 크기와 품질 간 균형이 중요한 경우 - 사진과 같이 색상이 풍부하고 세부 정보가 많은 이미지 |
- 로고, 아이콘, UI요소 등 투명도가 필요한 이미지. - 품질 손실 없이 선명한 그래픽이 필요한 경우 |
- 간단한 애니메이션 또는 저해상도의 그래픽 |
SVG | WebP | TIFF | |
특징 | - 벡터 기반 이미지 형식으로, 크기를 조정해도 품질 손실이 없음 - XML 기반으로 텍스트로 저장됨 |
- 구글이 개발한 포맷 - 손실 및 무손실 압축 모두 지원 - JPEG,PNG보다 작은 파일 크기로 비슷하거나 더 나은 품질 제공 - 투명도와 에니메이션 지원 |
- 고품질 무손실 이미지 형식 - 주로 사진 및 그래픽 디자인 작업에서 사용 - 파일 크기가 매우 큼 |
용도 | - 로고,아이콘, 일러스트레이션 등 크기 조정이 잦은 그래픽 - 웹에서 인터랙티브한 그래픽이나 애니메이션 |
- 웹에서 성능 최적화를 위해 사용 - JPEG,PNG,GIF로 대체로 점점 더 널리 사용됨. |
- 인쇄용 고해상도 이미지 - 사진 편집 및 보관 |
BMP | |||
특징 | - 무손실 이미지 형식 - 압축이 없으므로 파일 크기가 매우 큼 |
||
용도 | - 품질이 중요한 로컬 환경에서 사용되지만, 현대 웹에서는 거의 사용되지 않음. |
- A/B testing (split testing)
2가지 버전의 웹 페이지나 앱 기능 등을 서로 비교하면서 더 나은 것을 선택하는 테스트 방법 - Key concepts and features of asnychronous programming in JavaScript
JavaScript에서 비동기적인 기능에 대해서 이해를 해야한다. 다음은 개인적으로 정리하고 싶었던 기능들에 대한 간략한 설명이다.
promises : 언젠가 완수가 될 비동기적인 실행과 결과값을 나타내는 객체이다. 이 객체는 Promise가 생성될 때는 아직 알 수 없는 미래의 값에 대한 proxy(여기선 대리인으로 이해)이다.
promise는 다음과 같은 상태를 가진다. "pending", "fullfilled", "reject" 여기서 "fullfiled"와 "reject"는 settled 상태(promise의 결과가 나온 상태로 이해)라고 한다. 여기서 나를 헷갈리게 했던 상태가 하나 더 있는데 "resolve"이다. resolve상태는 주사위가 이미 던져진 상태로 받아드렸는데, settled와 차이는 settled는 fullfiled(성공)인지 reject(실패)인지 확정이 된 상태라고 한다면 resolve는 아직 결과는 모른채 promise 단계가 마무리가 되었음을 의미한다.
이 역시 글로만 보면 이해하기 어려울 수 있다. 아래 링크를 참조하여 다양한 예시를 보면서 받아들이면 좋을것 같다.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
Event Loop : 이벤트 루프는 브라우저에서 asynchronous , non-blocking I/O 작업을 수월하게 할 수 있도록 Call Stack, Callback Queue( macro task, micro task ), Web APIs를 모니터링 하며 각 작업의 흐름을 제어하는 관리자이다.
당연히 위 설명만 보면 이해가 되지 않는다. 보다 나은 이해를 위해 이벤트 루프에 대해 설명이 정말 잘 되어있는 링크를 같이 공유한다.
https://www.lydiahallie.com/blog/event-loop - Backend as a Service
애플리케이션 개발을 위한 백엔드 기능을 클라우드 기반으로 제공하는 서비스이다. 개발자가 백엔드 인프라를 직접 구축하거나 관리할 필요 없이 ,BaaS 플랫폼에서 제공하는 API와 SDK를 사용하여 애플리케이션의 백엔드 기능을 구현할 수 있다.
사용자 로그인/회원가입 기능은 Firebase Authentication을 사용하고, 데이터베이스 관리는 Firebase Realtime Database를 사용하고, 파일 저장 및 관리는 Firebase Storage를 사용하는 식으로 백엔드 인프라에서 처리할 것을 이를 제공하는 서비스를 사용하여 대체하는 방식이다. - CI & CD
CI(continuous integration)는 말 그대로 지속적인 통합이다. 개발자가 코드를 수정하면 자동으로 현재 코드베이스에 안좋은 영향을 미치는지 테스트하는 것이다.
CD(continuous deployment)는 지속적인 배포를 뜻한다. CI에서 확장된 개념인데, 코드베이스에 변화가 있으면 이를 staging 또는 production 환경에 자동으로 배포하는 것이다.
https://ccomccomhan.tistory.com/297 - Code Coverage
소프트웨어 테스트에서 사용되는 개념이다. 테스트 진행시에 얼마나 많은 소스코드가 실행 및 테스트가 되는지 측정한 수치라고 보면 된다.
Code Coverage가 100%라면 모든 소스코드에 대해서 테스트가 진행된 것이다. - Declarative Programming vs Imperative programming
이 두가지 용어가 항상 헷갈려서 이번 기회에 정리를 한다.
우선 용어의 뜻을 이해하자면 Declarative는 "선언적인" 이라는 의미이다. 선언적이라는건 뭘까? 자신의 생각이나 의지를 명확하고 단호하게 말로 드러낸다는 느낌이다. 반면에 Imperative 조금 이해하기 어렵다. 사전에서는 "매우 긴급한, 중요한"이라는 뜻인데 Imperative programming에서는 그런 의미보다는 작업의 절차를 하나하나 명령하는 의미를 가진다.
그래서 Imperative programming은 명령형(절차적) 프로그래밍이라고 불린다.
그럼 도대체 이게 무슨 의미인가.
Declarative는 간단하게 무엇을 하고 싶은지 "선언"을 하면 컴퓨터가 알아서 과정을 처리하여 무엇을 가져다 준다.
반면에 Imperative에서는 내가 하나하나 명령을 하면 컴퓨터가 그 명령을 순서대로 수행을 한 뿐이다. 여기서 컴퓨터는 무엇을 하는지 모르고 각 명령을 그대로 따른다.
예시를 보면 이해가 쉽다.
task : 유저 중에서 나이가 18살 이상인 유저의 이름을 전부 나열하라.
Declarative programming
SELECT name FROM users WHERE age > 18;
Imperative programming
users = [{'name': 'Alice', 'age': 20}, {'name': 'Bob', 'age': 17}]
adult_names = []
for user in users:
if user['age'] > 18:
adult_names.append(user['name'])
- functional programming
함수형 프로그래밍은 선언형 프로그래밍(Declarative Programming)의 하위 개념이다. 선언형 프로그래밍이지만 지켜야할 엄격한 규칙들이 있는 선언형 프로그래밍이라고 생각하면 된다. (순수함수, 불변성, 고차 함수, first-calss function)
명령형 프로그래밍과 함수형 프로그래밍의 차이를 비교하기 위해 아래 코드를 첨부했다.
// 객체의 배열에서 특정 조건을 만족하는 원소만 추출하는 코드
// 명령형 코드
const users = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 17 },
{ name: 'Charlie', age: 30 },
];
const adultNames = [];
for (let i = 0; i < users.length; i++) {
if (users[i].age >= 18) {
adultNames.push(users[i].name);
}
}
// 함수형 코드
const users = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 17 },
{ name: 'Charlie', age: 30 },
];
const adultNames = users
.filter(user => user.age >= 18)
.map(user => user.name);
조금 더 깊게 함수형 프로그래밍을 공부하고 싶으면 아래 링크를 참고하길 바란다.
https://github.com/MostlyAdequate/mostly-adequate-guide/blob/master/ch01.md
- E2E testing
End-to-End 테스트를 뜻한다. 어플리케이션을 실제 유저가 사용하는 방식대로 테스트를 하는 것이다.
유저의 실제 애플리케이션 사용시 워크플로우를 그대로 따라하면서 테스트를 진행한다.
Selenium,Cypress,Playwright 같은 도구를 이용하여 E2E 테스트를 자동화 할 수도 있다. - Functional Testing
번역하면 기능적 테스트이다. 소프트웨어의 기능이 명세서(요구사항)에 따라 올바르게 작동하는지 검증하는 테스트 방식이다.
애플리케이션의 기능적 요구사항을 중심으로 테스트를 하고 주로 입력값과 출력값을 확인하여, 기대한 결과가 나오는지 검증한다.
내부의 동작 방식보다는 해당 기능이 정상동작을 하는지에 초점을 맞춘다.
만약 로그인 기능을 테스트한다고 한다면 실제로 아이디와 비밀번호를 입력 후 로그인 버튼을 클릭하여 잘 동작하는지 테스트하는 방식이다.
여기서 Functional Testing과 E2E Testing의 차이가 무엇인지 궁금할 수 있다.
Functional Testing은 각 기능 또는 모듈별로 정상 작동을 하는지 테스트를 하는 것이고, E2E Testing은 전체 시스템을 검토하는 것에서 차이가 있다.
E2E Testing은 각각의 Functional Testing과 각 기능(또는 모듈) 간의 연결성, 사용자 경험까지 복합적으로 테스트하는 것이라고 이해하면 될 것 같다. - GraphQL
간단하게 설명하자면 클라이언트 사이드에서 사용하는 SQL이라고 할 수 있다. SQL은 서버사이드에서 DB에 있는 데이터를 불러올 때 사용하는 query language인데, GraphQL은 클라이언트 사이드에서 서버로 필요한 데이터를 요청할 때 사용하는 query language이다.
REST API와 비교를 하면 이해가 더 잘 된다.
// 사용자의 정보를 가저오는 요청
// REST API
GET /users1 HTTP/1.1
Host: example.com
// GrarphQL
{
"query": "{ user(id: 1) {id, name, email} } "
}
코드를 보면 대충 어떤 느낌인지 감이 올 것이다. 간단하게 REST API는 이미 구성 메뉴가 정해져 있는 코스요리이고, 손님이 먹고 싶은 메뉴가 있는 코스요리를 선택하는 느낌이라면 GraphQL은 먹고 싶은 메뉴들을 직접 종이에 적어서 전달하는 느낌이다.
좀 더 자세한 설명을 보고 싶으면 공식 문서를 확인하자
https://www.howtographql.com/basics/1-graphql-is-the-better-rest/
- Micro-Frontends
Micro-FrontEnds 는 Front-End의 Micro-Service 개념이다. 애플리케이션의 Front-End 파트를 작은 단위들로 나누어서 각각 독립적으로 개발, 테스트, 배포를 할 수 있도록 하는 방식이다. - polyfill
Polyfill은 특정 웹 브라우저가 지원하지 않는 최신 웹 기술을 구형 브라우저에서도 동작될 수 있도록 구현한 코드 또는 라이브러리를 뜻한다. 오래된 브라우저가 지원하지 않는 최신 기술을 흉내 내도록 도와주는 코드라고 이해하면 될 것 같다.
아래 예시를 보면 느낌이 올 것이다.
/* 구형 브라우저에서는 지원하지 않는 기능을 사용할 수 있도록 추가한 코드 */
// Promise
if (!window.Promise) {
window.Promise = function (executor) {
// Promise 구현 코드
};
}
// Fetch API
if (!window.fetch) {
// fetch Polyfill 추가
window.fetch = function (url, options) {
// XMLHttpRequest를 사용해 fetch 동작 구현
};
}
- Semantic Versioning
Semantic Versioning은 SemVer라고도 축약되어서 불리는데, 이는 배포시 변화된 것에 대한 의미를 전달하는 목적의 버전 시스템이다.
즉, 소프트웨어 버전을 체계적으로 관리하기 위한 버전 시스템 표준이고 버전 번호를 통하여 소프트웨어의 변경 사항과 호환성 여부를 명확히 전달한다.
형태는 다음과 같다. MAJOR.MINOR.PATCH (ex : 2.1.5)
MAJOR는 이전 버전과 호환되지 않는 변경 처럼 큰 변화가 있음을 뜻한다.
MINOR는 새로운 기능이지만 이전 버전과 호환이 되는 변경이 있음을 뜻한다. 기존 기능에는 영향을 주지 않는 변경이다.
PATCH는 버그 수정과 같이 이전 버전과 호환이 되는 비교적 사소한 변경이 있음을 뜻한다. - Web Workers
Web Worker는 script들을 웹 페이지의 메인 쓰레드와 떨어져 백그라운드에서 비동기적으로 실행될 수 있는 방법을 제공하는 브라우저 API이다.
이를 통하여 UI(메인 쓰레드에서 처리)가 멈추지 않고 부드럽게 동작하며 사용자 경험을 개선할 수 있다.
Web Worker는 비동기적으로 작업을 수행하며, 작업이 완료가 될 시 메인 스레드와 통신하여 결과를 전달한다. (postMessage, onMessage 이벤트를 통해)