# 2022.01
# 1/31 팩토리 함수
객체를 반환하는 함수를 팩토리 함수라고 부른다.
관용적으로 함수명 앞에 create
을 붙인다.
팩토리 함수는 새로운 객체를 리턴하지만 클래스나 생성자 함수는 아니다. JavaScript에서는 모든 함수가 객체를 리턴할 수 있는데, 이 때 new가 없으면 팩토리 함수라고 할 수 있다.
function createBird() {
return {
name: 'edie',
color: 'red'
};
}
여러 팩토리 함수를 조합해서 생성된 객체를 반환하는 더 큰 개념의 팩토리 함수를 만들 수도 있다.
function createAnimal() {
return {
type: 'animal',
zoo: [
createBird(),
createLion(),
crateHippo()
]
}
}
또는 new나 this 없이 임의로 객체를 생성하기 위해서도 팩토리 함수를 사용할 수 있다.
function Tiger() {
Animal.apply(this, arguments);
}
Tiger.prototype = Animal.prototype;
// or
class Tiger extends Animal {
constructor() {
super();
}
}
이런 팩토리 함수를 이용해서 아래와 같이 코드를 짤 수도 있다.
// rather than
createJelly().eat();
// write
eatJelly(createJelly());
ES6+와 팩토리 함수 (opens new window) 자바스크립트에서 팩토리 함수란 무엇인가? (opens new window)
# 1/30 왜 TCP 커넥션은 6개까지 허용될까?
이미지 최적화를 공부하던 중에 대부분의 최신 브라우저는 TCP 커넥션을 6개까지 맺을 수 있다는 사실을 알게 됐다..
Browser connection limitations (opens new window)
위의 글을 참고하면 IE11의 경우에 13까지 맺지만 그 외, 파이어폭스, 크롬, 사파리, 오페라, iOS, Android 모두 6개가 최대치다. 즉 동일한 도메인 이름의 HTTP 연결 수를 6개까지 제한하고 있다는 것이다. (브라우저 탭 기준이 아니라 브라우저 기준)
만약 다운로드 해야할 이미지의 갯수가 6개 이상이라면 6개까지는 로딩이 되는 게 유저에게 보이겠지만 그 이상의 이미지들은 큐에 쌓인다는 뜻.
# 왜 6개까지 허용하지 않는 것일까?
HTTP는 기본적으로 클라이언트가 여러 개의 커넥션을 맺게 해서 여러 개의 HTTP 트랜잭션을 병렬로 처리할 수 있게 한다. 직렬이 아닌 병렬로 처리함으로써 커넥션을 맺는 데 발생하는 지연을 줄일 수 있었고, 유저에게는 웹 페이지의 여러 이미지가 동시에 로드되므로 심리적인 지연 또한 감소시켰다. (실제로 사람들은 페이지의 총 다운로드 시간이 길지라도, 화면 전체에서 여러 작업이 일어나는 것을 눈으로 확인했을 때 더 빠르다고 여긴다.)
하지만 항상 병렬 커넥션이 빠르기만 한 것은 아니다. 여러 개의 커넥션을 생성하면서 생기는 부하 때문에 객체들을 순차적으로 내려받는 것 보다 더 오래 걸릴 수 있기 때문이다. 또한, 많은 커넥션으로 인해 메모리를 많이 소모하고 자체적인 성능 문제를 야기하며 서버의 성능을 떨어뜨릴 수 있다.
따라서 브라우저는 병렬 커넥션을 사용하긴 하지만 6개 정도의 병렬 커넥션만을 허용하는 것이다.
# 그렇다고 병렬 커넥션은 단점이 없느냐?
그건 아니다.
- 각 트랜잭션마다 새로운 커넥션을 맺고 끊기 때문에 시간과 대역폭이 소요된다.
- 각각의 새로운 커넥션은 TCP 느린 시작 때문에 성능이 떨어진다.
- 실제로 연결할 수 있는 병렬 커넥션의 수에는 제한이 있다.
따라서 최신 브라우저는 지속 커넥션
과 병렬 커넥션
을 함께 사용한다.
# 지속 커넥션이란?
HTTP1.1에서는 TCP 커넥션이 완료된 후에도 커넥션을 유지해서 앞으로 있을 HTTP 요청에 재사용하게 되는데, 이렇게 처리가 완료된 이후에도 계속 연결된 상태로 있는 TCP 커넥션을 지속 커넥션이라고 부른다.
지속 커넥션을 통해 커넥션을 맺기 위한 준비 작업에 소요되는 시간을 절약할 수 있다. 또한 이미 맺어진 커넥션은 TCP의 느린 시작으로 인한 지연을 피하고 더 빠르게 데이터를 전송할 수 있다.
# 1/28 리페인트 및 리플로우 최소화
아래와 같이 DOM api로 HTML 요소에 접근하여 CSS 요소를 변경하는 경우 3번의 리플로우를 발생시킨다.
const div = document.getElementById('target);
div.style.padding = '12px';
div.style.width = '200px';
div.style.height = '300px';
따라서 리페인트와 리플로우를 최소화하기 위해 아래와 같이 cssText
를 이용하면 DOM을 단 한 번만 수정하도록 리팩터링할 수 있다.
const div = document.getElementById('target);
div.style.cssText = 'padding: 12px; width: 200px; height: 300px;';
# 1/25 자바스크립트의 배열은 배열이 아니다
# 일반적인 배열
자료구조에서 말하는 배열은 동일한 크기의 메모리 공간이 빈틈없이 연속적으로 나열된 자료구조를 말한다. 즉, 배열의 요소는 하나의 데이터 타입으로 통일되어 있으며 서로 연속적으로 인접해 있다. 이러한 배열을 밀집 배열이라 한다.
따라서 일반적인 의미의 배열은 각 요소가 동일한 데이터 크기를 가지며, 빈틈없이 연속적으로 이어져 있기 때문에 인덱스를 통해 단 한번의 연산으로 임의의 요소에 접근할 수 있다.(시간복잡도 O(1))
하지만 정렬되지 않은 배열에서 특정한 요소를 검색하는 경우에 배열의 모든 요소를 처음부터 차례대로 검색해야 한다.(선형 검색, 시간복잡도 O(n))
또한 배열에 요소를 삽입하거나 삭제하는 경우 배열의 요소를 연속적으로 유지하기 위해 요소를 이동시켜야 하는 단점도 있다.
# 자바스크립트의 배열
따라서 자바스크립트의 배열은 일반적인 배열과 다르다. 즉, 배열의 요소를 위한 각각의 메모리 공간은 같은 크기를 가질 필요가 없으며, 연속적으로 이어져 있지 않을 수도 있다. 이렇게 배열의 요소가 연속적으로 이어져 있지 않은 배열을 희소 배열이라 한다. 따라서 자바스크립트의 배열은 일반적인 배열의 동작을 흉내 낸 특수한 객체다.
console.log(Object.getOwnPropertyDescriptors([1, 2, 3]))
{
'0': {
value: 1,
writable: true,
enumerable: true,
configurable: true
},
'1': {
value: 2,
writable: true,
enumerable: true,
configurable: true
},
'2': {
value: 3,
writable: true,
enumerable: true,
configurable: true
},
length: {
value: 3,
writable: true,
enumerable: false,
configurable: false
}
}
위에서 보았듯이 배열의 요소는 사실 프로퍼티 값이다. 자바스크립트의 배열은 인덱스를 나타내는 문자열을 프로퍼티 키로 가지며, length 프로퍼티를 갖는 특수한 객체다. 자바스크립트에서 사용할 수 있는 모든 값을 객체의 프로퍼티 값이 될 수 있으므로 어떤 타입의 값이라도 배열의 요소가 될 수 있다.
# 결론
따라서 자바스크립트 배열은 해시 테이블로 구현된 객체이므로, 인덱스로 요소에 접근하는 경우 일반적인 배열보다 성능적인 면에서 느릴수밖에 없는 구조적인 단점이 있다. 하지만 특정 요소를 검색하거나 요소를 삽입 또는 삭제하는 경우에는 일반적인 배열보다 빠른 성능을 기대할 수 있다.
(참고로 인덱스로 배열 요소에 접근할 때 일반적인 배열보다 느리다는 단점을 보완하기 위해 대부분의 모던 자바스크립트 엔진은 배열을 일반 객체와 구별해서 좀 더 배열처럼 동작하도록 최적화했다.)
# 1/24 Promise
미래에 하나의 값을 받을 수 있는 인터페이스..
# 1/23 useEffect !== lifecycle method
useEffect는 라이프 사이클 훅이 아니다. useEffect는 부수효과를 일으키는 훅이며, app의 state 값을 활용하여 동기적으로 부수효과를 만들 수 있는 매커니즘이다.
흔히 componentDidMount
를 흉내내기 위해서 useEffect(fn, [])
의 방식으로 사용한다.
하지만 useEffect는 비슷한 타이밍에 동작하지 않는다.
클래스 컴포넌트의 LifeCycle은 다음과 같이 이뤄진다.
- componentDidMount: 컴포넌트가 마운트된 직후, 즉 트리에 삽입된 직후 호출
- componentDidUpdate: 컴포넌트가 리렌더링(갱신)된 직후 호출
- componentWillUnmount: 컴포넌트가 마운트 해제되어 제거되기 직전 호출
리액트의 정확한 라이프사이클에 따르면, useLayoutEffect
가 componentDidMount
와 같은 타이밍에 실행되도록 설계되었다.
그래서 useLayoutEffect(fn, [])
를 사용하는 것이 useEffect(fn, [])
보다 더 옳다.
하지만 그렇다고 useLayoutEffect를 사용해야 할까? 대답은 아니오다. 리액트 공식문서에서도 useLayoutEffect를 사용하기 보다 useEffect를 먼저 사용하고, 정말 필요한 경우에만 useLayoutEffect를 사용하라고 권고한다. 상태를 동기적으로 설정해서 깜박임을 방지하려는 특이 케이스가 아닌 경우.. useEffect를 사용하면 된다.
# 1/22 매개변수 객체 만들기
// before
function foo (startDate, endDate) {...}
// after
function foo (dateRange) {...}
<클린 코드>에서 로버트 마틴이 함수에서 이상적인 인수 개수는 0개(무항)이며, 그 다음은 1개, 그 다음은 2개, 3개는 가능한 피하며 4개 이상은 특별한 이유가 있어도 사용하면 안 된다고 말했다.
더불어 <리팩터링>의 마틴 파울러도 함수에 인수로써 데이터 항목이 여러 개 몰려다닐 경우 데이터 무리를 데이터 구조 하나로 묶자고 제안한다. 데이터 구조를 묶게 되면 데이터 사이의 관계가 명확해지는 이점이 있기 때문이다. 새로 만든 데이터 구조가 문제 영역을 훨씬 간결하게 표현하며 함수의 구조적인 의미가 드러난다고 말한다.
# 1/20 PWA
기존 진행하고 있는 next-js 웹 어플리케이션에서 사용자를 위해 PWA로 제작해야 하는지를 따져보기 위해 조사해 보았다.
# 적용 방법
- next-pwa (opens new window) 라이브러리가 있다. 이걸로 서비스워커 설정은 필요 없고 meta 태그 설정, menifest.json이랑 오프라인 폴백만 준비하면 되는 듯
- web.dev - PWA (opens new window)에서 굉장히 자세하게 설명해 준다.
- pwa-asset-generator (opens new window) 같은 걸로 필요한 아이콘이나 splash 이미지를 만들 수 있는 듯..
# 단점
- 타깃 유저를 생각해보았을 때 과연 pwa를 화면에 추가하는 과정을 거칠까 싶다. 나 또한 '홈화면의 추가' 같은 건 1-2번 밖에 안 해 본 ios 유저라..
- ios 같은 경우 푸시 알림이 되지 않는다는 카더라(더 알아봐야 함..)
- 만약 도입한다면 alert이나 modal로 추가 방법을 알려야 할 것 같음
- 앱과 비슷하다는 것이지 앱처럼 네이티브한 기능들을 제공하는 것은 아님
# 장점
- 그럼에도 불구하고 해야한다면 오프라인에서도 어느 정도의 사용성을 줄 수 있다는 점? (PWA 환경에서 데이터 캐싱이 제대로 되는지 확인 필요)
- 앱스토어 등록한다는 전제 하에 꽤 괜찮은 시도
- 당장 APP 개발을 위한 비용이 부족할 때 PWA로 빠른 시간 내에 앱과 같은 UX 제공할 수 있다면 꽤나 큰 장점
만약 시도했을 때 개발 비용 대비 효용이 좋다면 앱 개발 전에 미리 시도해보는 게 최종 결론
# 1/19 Git Flow
- master: 기준이 되는 브랜치로 제품으로 배포하는 브랜치
- develop: 개발 브랜치로 개발자들은 이 브랜치를 기준으로 다음 출시 버전을 개발
- feature: 단위 기능을 개발하는 브랜치
- release: 배포를 위해 master로 보내기 전에 먼저 QA를 진행하는 브랜치
- hotfix: master 브랜치로 배포한 출시 버전에서 발생한 버그를 긴급 수정하는 브랜치
# 1/18 코드에서 나는 악취
코드를 명료하게 표현하는 데 가장 중요한 요소 하나는 바로 이름이다. 함수, 모듈, 변수, 클래스 등은 그 이름만 보고도 각각 무슨 일을 하고 어떻게 사용해야 하는지 명확히 알 수 있도록 신경써서 이름을 지어야 한다.
-> etc > 리팩터링 2.0에 보다 자세히 정리합니다.
# 1/17 새로운 지식 습득하기
기술을 습득하고 수련하는 것은 사람에게 천성은 아니며 그것 자체가 수련해야 할 또 하나의 기술이다. - 워드 커닝햄
새로운 지식을 습득하는 것이 힘들다면 계속 부딪혀서 내성이 생기도록 체득하면 된다.
# 1/16 함수 추출하기
코드 조각을 찾아 무슨 일을 하는지 파악한 다음, 독립된 함수로 추출하고 목적에 맞는 이름을 붙인다. 코드를 독립적인 함수로 묶을 때의 기준은 목적과 구현을 분리하는 방식으로 한다. 코드를 보고 어떤 기능을 하는지 파악하는 데 한참이 걸린다면 그 부분을 함수로 추출하고 '무슨 일'을 하는지 이름을 지어둔다. 이렇게 하면 나중에 코드를 읽을 때 목적이 눈에 들어오고 본문 코드(구체적으로 수행하는 일)에는 더 이상 신경 쓰지 않아도 된다.
# 절차
- 함수를 새로 만들고 목적을 잘 드러내는 이름을 붙인다. ('어떻게'가 아닌 '무엇을' 하는지가 드러나야 한다.)
- 추출할 코드를 원본 함수에서 복사하여 새 함수에 붙여넣는다.
- 추출한 코드 중 원본 함수의 지역 변수를 참조하거나 추출한 함수의 유효범위를 벗어나는 변수는 없는지 검사한다. 있다면 매개변수로 전달한다.
- 변수를 다 처리했다면 컴파일한다.
- 원본 함수에서 추출한 코드 부분을 새로 만든 함수를 호출하는 문장으로 바꾼다.(즉, 추출한 함수로 일을 위임한다)
- 테스트한다.
- 다른 코드에 방금 추출한 것과 똑같거나 비슷한 코드가 없는지 살핀다. 있다면 방금 추출한 새 함수를 호출하도록 바꿀지 검토한다.
# 1/14 useEffect와 useLayoutEffect
useEffect(() => {
// do side effects
return () => /* cleanup */
}, [dependency, array]);
useLayoutEffect(() => {
// do side effects
return () => /* cleanup */
}, [dependency, array]);
useEffect는 화면에 렌더링이 그려진 후에 비동기적으로 실행된다. 그래서 1) 렌더링 변경이 일어나면 (상태 변경, 부모의 리렌더링)
- 리액트는 컴포넌트를 렌더링하고
- 스크린은 시각적으로 업데이트 되는데
- 그럼 useEffect가 실행된다.
반면에 useLayoutEffect는 렌더링 후이지만 화면에 업데이트 되기 전에 동기적으로 실행된다.
- 렌더링 변경이 일어나면 (상태 변경, 부모의 리렌더링)
- 리액트는 컴포넌트를 렌더링하고
- useLayoutEffect가 실행되고서 리액트는 이것이 끝나길 기다린다.
- 스크린이 시각적으로 업데이트 된다.
useLayoutEffect는 리액트가 DOM을 업데이트하기 전에 약간의 추가 작업을 수행하는 방법. 그래서 만약 화면에 불필요한 깜빡거림(준비되지 않은 데이터로 한번 렌더링이 되고, 최종 데이터로 렌더링 되는 등..)이 발생할 경우 useLayoutEffect가 힌트가 될 수 있음.
공식문서에서 useLayoutEffect에 대해 설명하는 글을 추가한다.
클래스 컴포넌트에서 코드를 변환하는 경우에 useLayoutEffect는 componentDidMount나 componentDidUpdate와 동일한 단계를 실행하게 된다는 것에 주의하기 바랍니다. 그렇기는 하지만, 먼저 useEffect를 사용해 보고 문제가 있다면 그다음으로 useLayoutEffect를 사용해 보기를 권합니다.
서버 렌더링을 사용하는 경우라면 자바스크립트가 모두 다운로드될 때까지는 useLayoutEffect와 useEffect 어느 것도 실행되지 않는다는 것을 명심해야 합니다. 이것이 서버에서 렌더링 되는 컴포넌트에서 useLayoutEffect가 사용되는 경우에 대해 React가 경고하는 이유입니다. 이를 수정하기 위해서는 (최초 렌더링 시에 필요하지 않다면) 로직을 useEffect로 이동한다거나 (useLayoutEffect가 수행될 때까지 HTML이 깨져 보이는 경우는) 클라이언트 렌더링이 완료될 때까지 컴포넌트 노출을 지연하도록 하세요.
아래 코드 예제에서 렌더링을 비교해볼 수 있다. useLayoutEffect (opens new window)와 useEffect (opens new window)
리액트 공식 문서 - useLayoutEffect (opens new window) 참고 (opens new window)
# 1/13 attribute와 property 차이
attribute는 HTML 문서에서 사용되고, property는 DOM 객체에서 사용된다. HTML이 파싱되어서 DOM 객체를 생성하기 때문에 attribute는 property가 된다고 할 수 있다. 둘의 차이는 타입과 이름에 있다. property는 모든 타입이 될 수 있는 반면에 attribute는 문자열만 허용된다. 또 property의 이름은 대소문자를 구별하지만 attribute는 구분하지 않는다. HTML 문서는 대소문자를 구분하지 않고, DOM 객체는 자바스크립트 객체와 같은 성격을 갖기 때문이다.
# 1/12 Symbol & Enum
# Symbol
Symbol은 중복되지 않는 상수 값을 생성하고 것은 물론 기존에 작성된 코드에 영향을 주지 않고 새로운 프로퍼티를 추가하고 외부에 노출할 필요가 없는 프로퍼티를 은닉하기 위해, 즉 하위 호환성을 보장하기 위해 ES6부터 도입되었다.
심벌은 변경 불가능한 원시 타입의 값이다. 심벌은 다른값과 중복되지 않는 유일무이한 값이다. 따라서 주로 충돌 위험이 없는 유일한 프로퍼티 키를 만들기 위해 사용한다. (cf. 프로퍼티 키로 사용 가능한 값: 빈 문자열을 포함한 모든 문자열 또는 심벌 값)
// Symbol 함수를 호출해서 생성한다.
const newSymbol = Symbol();
console.log(typeof newSymbol); // 'symbol'
// 심벌 값은 외부로 노출되지 않는다.
console.log(newSymbol); // Symbol()
# Enum
enum은 명명된 숫자 상수의 집합으로 열거형이라고 부른다. 자바스크립트는 enum을 지원하지 않지만 C, java, python 등 여러 언어와 자바스크립트의 상위 확장인 타입스크립트에서 지원한다. 자바스크립트에서 enum을 흉내내어 사용하려면 객체의 변경을 방지하기 위해 Object.freeze 메서드와 심벌 값을 사용한다.
const Direction = Object.freeze({
UP: Symbol('up'),
DOWN: Symbol('down'),
LEFT: Symbol('left'),
RIGHT: Symbol('right'),
});
# 1/11 유사 배열 객체와 이터러블 객체
유사 배열 객체는 마치 배열처럼 인덱스로 프로퍼티 값에 접근할 수 있고 length 프로퍼티를 갖는 객체를 말함. 유사 배열 객체는 배열처럼 for 문으로 순회 가능. 이터러블 객체 iterable object는 Symbol.iterator 메서드를 구현하여 for...of 문으로 순회할 수 있으며, 스프레드 문법과 배열 디스트럭처링 할당의 대상으로 사용할 수 있는 객체를 말함.
ES6에서 제공하는 빌트인 이터러블은 Array, String, Map, Set, DOM 컬렉션(NodeList, HTMLCollection), arguments, TypedArray 등이 있다.
# 1/10 타입스크립트의 효용
자세한 타입스크립트의 디자인 목표는 여기 (opens new window)에 정리해놓았다. 본문은 여기 TypeScript Design Goals (opens new window)에서 확인 가능하다.
타입스크립트의 디자인 목표는 철옹성과 같은 안전한 타입 시스템을 도입해 자바스크립트에서 발생할 수 있는 모든 오류를 걷어내는 데 있는 게 아니라, 자바스크립트의 생산성을 보전하면서 오류가 될 수 있는 코드들을 걸러주는 거름망 같은 타입 시스템을 도입하는 데 있음. - 링크 (opens new window)
타입스크립트에서는 '어떤 변수는 어떤 타입만을 담을 수 있다'는 것이 타입스크립트가 가지고 있는 본질. 기존 자바스크립트가 타입 불안정성으로 인해 런타임 환경에서의 에러를 예측할 수 없어서 비효율적으로 작성해야만 했던 단점을 타입스크립트를 통해 런타임 이전에 컴파일 타임에서부터 에러를 미리 잡아낼 수 있다는 것. 컴파일 순간에 코드의 타입 안정성을 잡아주는 것이 타입스크립트의 가치이자 효용.
# 1/9 객체 깊은 복사를 위한 재귀함수
객체의 깊은 복사의 방법에는 JSON 객체 메소드를 이용하거나 (stringify()
, parse()
) lodash의 cloneDeep()
메서드를 이용하는 방법이 있음.
하지만 JSON 객체 메소드는 문자열로 변환한 뒤에 객체로 다시 변환시키는 방식이므로 참조를 끊어내는 대신에 함수와 같은 값은 undefiend로 처리된다는 한계가 있다는 점 잊지 말고..
커스텀으로 재귀 함수를 작성해보면 아래와 같다.
function deepCopy(obj) {
if (obj === null || typeof obj !== 'object') return obj;
const copy = {};
for (let key in obj) {
copy[key] = deepCopy(obj[key]);
}
return copy;
}
const obj = {
a: 1,
b: {
c: 2,
},
func: function() {
return this;
},
};
const newObj = deepCopy(obj);
newObj.b.c = 3;
console.log(obj); // { a: 1, b: { c: 2 }, func: ƒ func() }
console.log(newObj); // { a: 1, b: { c: 3 }, func: ƒ func() }
# 1/8 렉시컬 스코프
렉시컬 스코프 한 줄 요약.. 자바스크립트 엔진은 코드가 로드될 때 실행 컨텍스트를 생성하고 그 안에 선언된 변수, 함수를 실행 컨텍스트 최상단으로 호이스팅하는데 이러한 범주가 렉시컬 스코프..
# 1/4 Next.js Script 컴포넌트 업데이트
Next.js 버전 11(21.06) Script 컴포넌트 관련 정말 괜찮았던 업데이트..
beforeInteractive
: For critical scripts that need to be fetched and executed before the page is interactive, such as bot detection and consent management. These scripts are injected into the initial HTML from the server and run before self-bundled JavaScript is executed.afterInteractive (default)
: For scripts that can fetch and execute after the page is interactive, such as tag managers and analytics. These scripts are injected on the client-side and will run after hydration.lazyOnload
: For scripts that can wait to load during idle time, such as chat support and social media widgets.
기본적으로 페이지가 인터렉티브 가능한 상태일 때 script를 가져오는 게 디폴트라 TTI를 줄이는 방향으로 개선되었다. beforeInteractive
프로퍼티를 사용하면 인터렉티브 전에 미리 서버에서 자체 번들 JS가 실행되기 전에 초기 HTML과 같이 전달되는 프로퍼티가 있어서 사용자의 동의를 받고서 페이지를 띄워야하는 경우에 사용 가능하다.
아래처럼!
<Script src={url} // consent mangagement strategy="beforeInteractive" onLoad={()
=> { // If loaded successfully, then you can load other scripts in sequence //
요거 괜찮은데..? }} />
그리고 기본 script 태그의 프로퍼티인 async와 defer가 어떻게 적용되는지 궁금했는데, 넥제 블로그 글 보고 궁금증 해결..
We've also changed the default script loading experience in Next.js 11 from preloading and async to defer. Third-party scripts often compete with higher priority resources, such as CSS, fonts, and images. Maintaining the proper sequencing relative to these resources, as well as other scripts, is an undue burden on developers.
결국 기본 preloading하고 async하던 방식에서 defer로 변경되었으니 HTML 파싱도 막지 않을 거고 페이지 구성이 끝날 때까지 지연시키니까 FCP, LCP를 줄이는 데 도움이 될 것으로 보인다.
# 1/3 event.stopPropagation() 주의사항
# event.stopPropagation()
꼭 필요한 경우 아니면 쓰지 않기... 사람들이 페이지에서 어딜 클릭했는지와 같은 행동 패턴을 분석할 때 window 내에서 발생한 클릭 이벤트를 감지하게 되는데, 이런 분석 시스템의 코드는 클릭 이벤트를 캐치하기 위해 document.addEventListener('click', ...)을 사용함. 그런데 버블링을 막아놓으면 분석이 제대로 될 수 없겠지...? stopPropagation을 사용한 영역은 데드존이 되어버림. 따라서 커스텀 이벤트를 사용해서 문제 해결할 것
# 같은 코드일지라도 환경에 따라 퍼포먼스는 다룰 수 있다.
라이트하우스를 이용하면서도 느끼는 것이지만, 같은 코드일지라도 실행 환경(CPU, 브라우저 환경, OS 성능, browser extensions...)에 따라 성능이 천지차이인 경우를 보았다.
forloop이 forEach나 map과 같은 메서드보다 계산 시간이 빠르다는 결과가 Android나 다른 브라우저에서는 다르게 나온다는 포스팅을 보니, 어쩌면 코드 문법(계산 시간이 빠른 메서드를 사용하는 등)을 수정하는 단순한 방법만으로는 성능 최적화에서 사실상 무의미한 것이라는 생각이 든다. 구조적인 측면을 더 파고들거나, 비동기 코드를 개선해서 enormouse network payload를 줄이는 면이 더 효과적인 방법이 아닐까라는 생각.
# 1/2 parameter vs. argument
# parameter vs. argument
const a = 1;
const foo = function(b) {
// parameter, formal parameter, 매개변수, 형식 매개변수
};
foo(a); // arguments, actual parameter, 인자, 실인자