본문 바로가기

개념정리/기술면접대비

[기술면접 대비] 1. 웹/브라우저

1. 브라우저가 렌더링 되는 과정

사용자 인터페이스, 그 중에서도 주소창에 사용자가 특정 URL를 입력한다. DNS 서비스를 통해 주소가 IP로 바뀌고, 해당 서버의 자원을 요청한다. 서버에서 제공하는 여러 자원 중 html 및 css문서는 브라우저의 렌더링 엔진을 통해 렌더링이 된다. html 파서와 css 파서가 각각 DOM트리와 SSOM트리를 만들고, 화면에 보여지는 요소들만으로 렌더 트리구축하고 배치한 뒤 그리기 시작한다. 크롬(28버전 이전)과 사파리는 웹킷 엔진을 사용하고 파이어폭스는 게코 엔진을 사용한다. 현재 크롬은 웹킷을 기반으로 구글에서 자체적으로 개발한 블링크 엔진을 사용한다.

이러한 과정은 점진적으로 진행된다. 모든 문서의 파싱이 다 끝나기를 기다리지 않고 먼저 파싱이 된 것들에 한해 배치와 그리기를 시작한다.

이 때 파서는 스크립트 태그를 만나면 스크립트가 실행되는 동안 문서의 파싱을 중단하고 렌더링 제어 권한을 자바스크립트 엔진에 넘겨준다. dom을 조작하는 코드가 포함된 스크립트가 dom 트리에 영향을 주기 때문이다.

이를 방지하기 위해서는 스크립트를 지연으로 표기하여 비동기로 처리하는 속성을 추가할 수 있다. 

팁) 렌더링을 빠르게 완료하기 위해서는  css 파일은 html의 상단에, js파일은 하단에 위치하는게 좋다. 이미지 파일의 경우도 브라우저가 렌더링 시 한번에 다운로드 할 수 있는 이미지의 수가 제한적이므로 하나의 파일로 다운받고 추후에 쪼개서 사용하는 방식을 사용할 수 있다.

 

브라우저 렌더링 과정 도식화

출처 : https://onlydev.tistory.com/9

화면 다시 그리기 (JS나 CSS의 조작) 주의점

composition만 발생(translate 속성 사용) - 베스트

paint 재발생 - 경우엔 나쁘지 않음

layout이 재발생 - 최악의 성능

 

워커

렌더링 프로세스에서 파싱을 담당하는 메인 스레드를 방해하지 않고 독립적인 일을 수행하는 스레드

웹 워커

스크립트 연산을 웹 어플리케이션의 주 스레드와 분리된 별도의 백그라운드 스레드에서 실행하는 기술.

UI 블락을 피하기 위해 이미지 인코딩, AI, game 등의 무거운 연산이 소요되는 작업을 담당함. 

서비스 워커

웹, 브라우저, 네트워크 사이에서 프록시 서버 역할을 담당함.

서비스 워커는 활성화 탭과 독립적인 수명을 가지고 백그라운드에서 계속 살아있을 수 있음.

네트워크 요청을 가로채 수정 할 수 있으며(HTTPS에서만 사용 가능) 캐시, 네트워크 요청 등의 처리를 결정함.

1) 본래 네트워크 요청은 브라우저 프로세스의 네트워크 스레드가 담당하는데

2) 네트워크 스레드는 우선 서비스 워커가 등록된 스코프 레퍼런스를 참고해

3) 해당 사이트가 서비스 워커 스코프 내에 있다면 서비스 워커에 권한을 넘긴다.

4) 서비스 워커가 캐싱된 자원을 로드할지, 네트워크 스레드에 네트워크 요청을 보낼지 결정한다.

 

 

렌더링 파이프라인

1) HTML 문서, CSS 문서 파싱 DOM & SSOM트리 생성

* 토큰화된 html 태그 중 하위 리소스 요청 관련은 모두 프리로드 스캐너가 브라우저 프로세스의 네트워크 스레드에 요청함

2) DOM 노드에 계산된 스타일 적용해 렌더 트리 생성 (attachment)

3) 화면에 보이는 요소만 가지고 레이아웃 배치, 크기 위치 등 결정 (reflow)

 - 윈도우 리사이징, 노트 추가 또는 제거, 요소 위치 크기 변경, 폰트, 이미지 크기 변경

4) 레이아웃 트리를 순회하며 페인트 순서(기록) 생성 (repaint)

- 색상 변경, 그림자 설정 등

5) 메인 스레드에서 컴포지터 스레드로 데이터 커밋. (re-composite)

컴포지터 스레드는 각 레이어를 레스터화(데이터의 픽셀화)해 합성 프레임 생성

- transform, opacity 등

* 옛날에는 뷰포트를 기준으로 화면을 그리고 스크롤시 새로운 영역에 대한 추가 레스터화가 필요했다면 최근에는 화면을 여러 레이어로 나눠 각각 레스터화해서 가지고 있다가 스크롤 등이 일어나면 프레임만 재합성함

 

프론트 성능개선

1) 스크립트 태그의 defer, async 속성으로 백그라운드 다운 허용

2) 이벤트 리스너 등록시 세번째 파라미터에 {passive:true} 속성을 주어 메인스레드에서 이벤트를 처리하는 동안에도 컴포저터 스레드가 메인 스레드의 처리를 기다리지 않고 새로운 프레임을 생성하도록 허용함.

3) layout보단 paint, paint보단 composite 변경

 

2. 브라우저란

클라이언트/서버 모델로 클라이언트는 웹서버에 정보를 요청하고, 서버는 해당 정보를 통신 규격에 맞게 브라우저에 전송한다. http, https통신으로 웹서버에서 문서를 가져와 렌더링 엔진을 통해 이를 화면에 출력하는 그랙픽 사용자 인터페이스 기반의 응용 소프트웨어.

 

3. DOM이란

Document Object Model, 즉 html, xml 문서를 객체화한 모델이다. html의 태그들을 브라우저가 인식할 수 있고, js가 활용할 수 있는 객체로 만든다. 

 

4. Window객체와 Document

Winodw란 브라우저의 모든 요소와 js의 모든 객체, 전역변수를 담고있는 브라우저 내장 객체. 탭이 존재하는 브라우저는 각각의 탭을 나타낸다. window창을 직접 조작할 수 있는 메서드를 포함한다. 대표적으로 alert(), setTimeOut(), open(), close() 등이 있다.

document는 window 객체의 중요한 프로퍼티 중 하나로 브라우저에 표시되는 문서 정보를 포함하는 객체.

 

13. AJAX

Asynchronous Javascript and XML. 비동기 웹 어플리케이션 제작을 위한 JS라이브러리

기존의 웹은 브라우저에서 서버로 요청을 보내면 서버는 새로운 데이터를 가지고 웹 페이지를 처음부터 새로 그려 응답을 보냈다. 반면 Ajax는 필요한 데이터만을 요청해 받은 뒤 클라이언트 단에서 데이터에 대한 처리를 할 수 있게 한다. 웹서버에서 전적으로 처리하던 데이터를 클라이언트 재량으로 컨트롤 할 수 있게되면서 전체 페이지를 새로고침 하지 않고 일부 데이터만을 빠르게 주고받을 수 있어 트래픽의 낭비를 줄이고 응답성을 향상시켰다. 또한 통신 중 서버의 응답을 기다리지 않고 다른 처리를 할 수 있는 비동기 요청이 가능하다. 

하지만 대표적인 단점으로 동일-출처 정책으로 인해 다른 도메인과는 통신이 불가능한 Cross-Domain 문제가 있다.

진행과정은 다음과 같다.

우선 XMLHttpRequest Object를 만든다. 서버에서 response가 왔을 때 실행시킬 callback 함수를 만든다. request를 보내고 응답을 받으면 클라이언트의 콜백함수가 동작한다.

 

 

17. json, xml

데이터를 저장하고 전달하기 위해 고안된 계층적인 데이터 구조

xml은 웹초기부터 사용된 방식으로 헤더와 태그 등의 요소가 결합됨. 가독성이 떨어지고 메타 데이터의 양이 많다는 단점이 있는 대신 데이터 형식의 무결성을 보장한다.

JSONJS객체 문법을 따르는 문자 기반 데이터 포맷이다. 태그를 사용하지 않아 불필요한 데이터의 양이 적으며, 객체와 다중 배열 등 js 표준으로 파싱이 용이하다. 

XMLHttpRequest 로 가져와서 사용한 과정?

 

21. local storage와 session storage

브라우저 내에 데이터를 저장하는 웹 스토리지 객체. 웹스토리지는 쿠키와 달리 서버에 전달하지 않으며 서버에서 이를 조작 할 수 없다. 웹 스토리지는 도메인, 프로토콜, 포트로 정의되는 origin에 종속된다

local storage는 origin이 같은 모든 탭과 창에서 공유되며 브라우저나 OS가 재시작되어도 데이터가 파기되지 않음.

session storage는 동일한 origin을 가지는 현재 탭 내에서만 유지됨. 페이지 새로고침시에는 유지되지만 브라우저나 탭 종료시 사라짐

+내가 사용한 경험?

 

22. 브라우저 캐싱

웹페이지 성능을 최적화하기 위함. 특정 사이트를 반복접속 하는 경우 용량이 큰 이미지 등을 사용자의 디스크에 저장하고 캐시로 사용함. 캐시를 최신버전으로 유지하기 위해 캐시 사용 전에 서버에 head 요청을 보내 last-modified date를 비교한다. 캐싱 파일 크기가 작은 경우 매번 head 요청을 만들어 전송하는 것과 캐싱을 하는 것 중 효율성 판단이 필요하다.

 

23. assets의 역할

웹 캐싱을 방지하는 폴더. 파일 이름 뒤에 난수를 붙여 파일이 수정되면 이를 변경해 새로운 파일을 불러오게 한다.

 

 

25. SEO

검색 엔진 최적화

 

26. 프론트쪽에서 일어날 수 있는 보안관련 이슈

 

script 태그

- src를 외부 사이트의 자원에서 요청할 때는 CORS에러가 발생한다.

 

script 의 defer와 async

기본적으로 html은 script 태그를 만나면 파싱을 멈추고 js 엔진에 권한을 넘김.

하지만 script 태그에 defer 또는 async 속성을 명시할 경우 다음과 같은 차이가 있다.

1) defer

- html 파싱을 멈추지 않고 백그라운드에서 스크립트 파일을 다운로드한다. 

- 스크립트 실행은 html 파싱이 다 완료되어 페이지 구성이 끝날 때까지 지연된다.

- 구체적인 실행 타이밍은 모든 DOM이 준비된 이후, DOMContentLoaded 이벤트 발생 이전.

- defer 속성이 표기된 스크립트 끼리는 다운로드는 개별적으로 완료되어도 실행은 순차적으로 실행된다. 

- src로 외부 스크립트를 가져오는 스크립트 태그에서만 사용한다.

- 주로 DOM 전체가 필요하거나 실행 순서가 중요한 경우에 적용한다.

2) async

- html 파싱을 멈추지 않고 백그라운드에서 스크립트 파일을 다운로드한다.

- 스크립트 실행은 파일이 다운로드 되는 즉시 실행된다. async 스크릡트의 실행 중에는 html 파싱이 중단된다.

- async 속성이 표기된 스크립트 끼리도 순서에 구애받지 않고 다운로드가 먼저 완료되는 스크립트가 먼저 실행된다.

- 주로 방문자 수 카운트 등 해당 페이지와 독립적인 기능을 구현한 스크립트에 적용한다.

 

모듈 스크립트

- script의 type 속성에 "module"을 명시해 사용

- 파일 스코프를 지원한다.

- 항상 엄격모드(use strict)로 실행됨

- 모듈 속성 스크립트 파일에서 바인딩 되지 않은 this의 기본값은 전역객체가 아닌 undefined (엄격모드)

- 항상 defer 속성을 가진다(html 구문 분석 중에도 백그라운드 다운로드)

- 모듈 스크립트에선 외부 스크립트가 아닌 인라인 스크립트(script태그 내에 js 코드 작성)에도 async를 사용할 수 있다.

- 동일한 src를 가지는 모듈 스크립트 태그를 중복해서 작성해도 한 번만 평가된다.

 

동적으로 모듈 가져오기

- import( ... ) 표현식으로 모듈을 가져오면 해당 모듈이 export한 것들을 모두 포함하는 객체를 담은 Promise 객체 반환

1) Promise/then 사용

// 📁 module.js
export function hi() {
  console.log(`안녕하세요.`);
}

export function bye() {
  console.log(`안녕히 가세요.`);
}

export default function() {
  console.log("export default한 모듈을 불러왔습니다!");
}


// 📁 main.js
import("./module.js")
  .then(obj => <모듈 객체>) // {hi:function(){...}, bye:...}
  .catch(err => <로딩 에러, e.g. 해당하는 모듈이 없는 경우>)

 

2) async/await 사용

// 📁 module.js
export function hi() {
  console.log(`안녕하세요.`);
}

export function bye() {
  console.log(`안녕히 가세요.`);
}

export default function() {
  console.log("export default한 모듈을 불러왔습니다!");
}


// 📁 main.js
async function load() {
    let moduleObj = await import('./module.js');
    moduleObj.hi(); // 안녕하세요.
    moduleObj.bye(); // 안녕히 가세요.
    moduleObj.default(); // export default한 모듈을 불러왔습니다!
}