1 : 자바스크립트 문법
* 리액트 컴포넌트의 렌더링이 일어나는 이유 : props의 동등 비교에 따른 결과
props의 동등비교는 객체의 얕은 비교를 기반으로 이뤄짐.
또한, 가상DOM과 실제 DOM의 비교, 렌더링 판단 방법, 메모이제이션등은 모두 '동등비교' 를 기반으로 함.
1. 데이터 타입 : 값을 저장하는 방식의 차이
1) 원시 타입 (primitive) : 불변 형태의 값으로 저장, 변수 할당 시점에 메모리 영역을 차지
-> boolean, null, string, number, undefined, symbol, bigint 7개
객체가 아닌 모든 타입은 원시타입.
** undefined, null의 차이
-> undefined : 선언 후 값을 할당하지 않은 변수 / 값이 주어지지 않은 인수에 자동으로 할당되는 값
-> null : 아직 값이 없거나, 비어있는 값, 명시적으로 비어있음을 나타냄 / typeof로 나타냈을때 object가 반환됨.
** falsy값 : false, 0, -0, 0n, 0x0n, NaN, "", ' ', ` `, null, undefined
** number, bigint의 차이
- number : -(2⁵³ -1) ~ 2⁵³ -1
- bigint : ES2020에서 등장, number의 한계 극복
** ` `(템플릿리터럴) : 줄바꿈이 가능하고, 문자열 내부에 표현식을 쓸 수 있다는 차이가 있음.
** Symbol : ES6(ES2015)에서 등장, 중복되지 않는 어떠한 고유한 값을 나타내기 위해 만들어짐.
2) 객체 타입 (object) : 변경 가능한 형태, 값이 아닌 참조 전달
-> object (함수, 배열, 정규식, 클래스 등이 포함), 참조를 전달하므로 '참조타입'이라고 불림.
2. 자바스크립트의 비교 방법 (==, ===, Object.is)
1) == : 같음을 비교하기 전에 강제 형 변환을 함, 타입이 다르면 자동으로 타입 변환
2) === : 값과 타입을 모두 비교, 타입이 다르면 무조건 false
3) Object.is : 엄격 동등 연산자(===)와 유사하지만, 몇 가지 예외 케이스 존재
** Object.is 와 ===의 예외 케이스
1) NaN
NaN === NaN // false, NaN은 어떤 값과도 같지 않다고 판단
Object.is(NaN, NaN) // true
2) -0, +0
-0 === +0 //true
Object.is(-0, +0) // false
3) 0 / 0, (= NaN임)
0 / 0 === NaN // false, NaN은 어떤 값과도 같지 않다고 판단
Object.is(0 / 0, NaN) // true
+ 리액트에서는 기본적으로 얕은 비교를 사용하여 성능을 최적화하지만,
props가 깊어지는 경우에는 커스텀 비교 함수를 사용하거나 불변성을 유지하는 패턴을 활용해야 함.
3. 함수
작업을 수행하거나, 값을 계산하는 과정을 표현하고 이를 블록으로 감싸 실행 단위로 만들어 놓은 것
자바스크립트에서는 Component(props) 형태로 호출하지만, 리액트에서는 JSX문법으로 단일 props별로 받거나, 전개 연산자로 모든 props를 받는 차이
함수는 일급객체임.
** 함수 정의 하는 방법
1) 함수 선언문 : 표현식이 아닌 문
function add(a,b) = ...
2) 함수 표현식 : 변수에 함수를 할당
const add= function(a,b) ...
-> 둘의 차이는 호이스팅 여부.
( * 호이스팅: 함수에 대한 선언을 실행 전에 미리 메모리에 등록하는 작업)
함수 표현식은 변수에 함수를 할당한거고, 변수에는 호이스팅이 일어나므로 런타임 이전에는 undefined로 초기화 됨.
3) Function 생성자 : 모두 문자열로 작성해야해서 비추천, 또한 클로저도 생성 안됨.
4) 화살표 함수 : ES6 도입, 하지만 몇가지 차이 있음
-> constructor 사용 불가, this 바인딩
( * this : 자신이 속한 객체나, 생성할 인스턴스 가리킴. 화살표 함수 등장 전까지는 동적으로 결정 / 화살표 함수는 바인딩을 갖지 않고, 상위 스코프 this 그대로 따름)
** 함수 종류
- 즉시 실행 함수 : 함수 정의하고 즉시 실행되는 함수, 한 번만 호출되고 다시는 호출 할 수 없음.
글로벌 스코프를 오염시키지 않는 장점
- 고차 함수 : 함수를 인수로 받거나, 결과로 새로운 함수 반환 가능
** 함수 주의 사항
- 작게 만들기 -> 재사용성 높아짐
- 부수효과 줄이기 -> 안정성 높이기
- 이름 잘 붙이기 -> 이해하기 쉽게
4. 클래스
특정한 형태의 객체를 반복적으로 만들기 위해 사용
객체를 만드는데 필요한 데이터나 코드를 추상화해 객체 생성을 편리하게 할 수 있음
- constructor : 객체 생성하는 메서드, 하나만 존재 하고 생략 가능
- property : 내부 정의 속성
- getter/setter
- instance method : 클래스 내부에서 선언한 메서드
- static method : 클래스의 인스턴스가 아닌 이름으로 호출 가능한 메서드, 정적 메서드 내부 this는 클래스 자신을 가리킴.
인스턴스 생성하지 않아도 사용/접근 가능 -> 여러 곳에서 재사용 가능
- extends : 상속
+ 프로토타입 체이닝 : 직접 객체에 선언하지 않아도, 프로토타입에 있는 메서드를 찾아 실행을 도와주는 것
5. 클로저
- 전역 스코프
- 함수 스코프 : js는 기본적으로 함수 스코프
클로저를 활용하면, 전역 스코프 사용 막고, 개발자가 원하는 방향으로만 노출 가능
ex) 리액트에서 클로저의 활용 : useState
클로저 사용 비용을 고려하기!
클로저가 선언된 순간, 내부 함수는 외부 함수의 선언적 환경을 기억하고 있어야 하므로, 사용 여부와 관계없이 저장함.
-> 성능에 영향 ㅇ
6. 이벤트 루프와 비동기 통신의 이해
자바스크립트는 싱글스레드에서 동작, 한 번에 하나의 작업만 동기 방식으로 처리
-> 자바스크립트 코드의 실행이 하나의 스레드에서 순차적으로 이뤄짐.
(이유 : 멀티스레딩은 메모리 공유로 인해서 동시에 같은 자원에 접근이 가능
-> 타이밍 이슈로 인한 브라우저 DOM 표시 문제 발생 가능)
* 호출 스택 : 자바스크립트에서 수행해야 할 코드나 함수를 순차적으로 담아두는 스택
* 이벤트 루프 : 자바스크립트 런타임 외부에서 비동기 실행을 돕기 위한 장치,
호출스택이 비어있는지 확인하고, 수행해야 할 코드가 있으명 엔진을 이용해서 실행.
코드를 실행하는 것과 호출 스택이 비었는지 확인 하는 것은 단일스레드에서 이뤄짐
-> 동시에 일어날 수 없고, 순차적으로 이뤄짐.
* 태스크 큐 : 실행해야 할 태스크의 집합
실행가능한 가장 오래된 태스크를 가져와야 하므로 큐 형태가 아닌 set형태임. (큐 형태는 FIFO니까)
(여기서 실행해야 할 태스크는 비동기 함수의 콜백 함수나, 이벤트 핸들러 등을 의미 -> setInterval, setTimeout, setImmediate)
비동기 함수는 메인 스레드가 아닌 태스크 큐가 할당되는 별도의 스레드에서 수행됨.(작업 할당은 브라우저/Nodejs의 역할)
메인 스레드에서는 자바스크립트가 동기적으로 실행됨.
만약 비동기 함수들의 작업이 메인스레드에서 이뤄진다면 비동기 작업 수행 불가!
* 마이크로 태스크 큐 : 기존의 태스크 큐와 다른 태스크를 처리함.
태스크 큐보다 우선순위가 높음.
Promise, process.nextTick, process.queueMicroTask, process.MutationObserver
* 순서 : 마이크로 태스크 큐 -> 렌더링 -> 태스크 큐
7. 리액트에서 자주 사용하는 자바스크립트 문법
+ 바벨 : 사용자의 다양한 브라우저 환경과 최신문법 요구 해결, 자바스크립트의 최신 문법을 다양한 브라우저에서도 일관적으로 지원하도록 코드를 트랜스파일 해줌, ES5 기준
1) 구조 분해 할당
: 배열 / 객체 값을 개별 변수에 즉시 할당하는 것, 선언문 없이 즉시 분해해서 변수를 선언하고 할당하고 싶을 때 사용
- 배열 구조 분해 할당 : ES6
- 객체 구조 분해 할당 : ECMA2018
+ useState가 객체가 아닌 배열을 반환하는 이유
: 배열 구조 분해 할당은 자유롭게 이름을 선언할 수 있기 때문.
배열 구조 분해 할당은 , 의 위치에 따라 값이 결정됨
객체 구조 분해 할당은 배열 구조 분해 할당과 달리, 객체는 객체 내부 이름으로 꺼내옴.
또, 트랜스파일을 거치면 번들링 크기가 상대적으로 크다는 특징도 있음.
2) 전개 구문
: 배열, 객체, 문자열과 같이 순회할 수 있는 값에 대해 전개해 간결하게 사용하는 구문
- 배열 전개 구문 : ES6, 내부 배열에서 활용 가능, 기존 배열에 영향 주지 않거 복사도 가능
- 객체 전개 구문 : ECMA2018, 순서가 중요, 트랜스파일을 거치면 번들링 크기가 상대적으로 크다는 특징
3) 객체 초기자
: 객체 선언시, 객체에 넣고자 하는 키와 값을 가지고 있는 변수가 이미 존재한다면, 해당 값을 간결하게 넣어주는 방식
4) Array 프로토타입 메서드
: 기존 배열의 값을 건드리지 않고 새로운 값을 만들어 냄 -> 기존 값이 변경되지 않아 안전함.
- map : 인수로 전달받은 배열과 똑같은 길이의 새로운 배열 반환
- filter : 콜백을 인수로 받아 truthy조건 만족하는 원소만 반환
- reduce : 콜백함수와 초깃값을 인수로 받음, 원하는 하나의 객체로 변환하는 등의 예시도 가능
- forEach : 콜백함수받아서 배열을 순회하며, 단순히 그 콜백함수 실행, 반환값이 없고 에러/종료가 아니면 중간에 멈출 수 없단게 특징.
5) 삼항 조건 연산자 : 조건문?참값:거짓값
8. 타입스크립트
자바스크립트는 동적 타입의 언어이므로, 실행해야 에러 확인이 가능하다는 단점.
타입스크립트는 타입 체크를 정적으로 빌드시에 수행함
타입스크립트로 작성된 파일은 자바스크립트로 변환되어, 자바스크립트 런타임 환경에서 실행되는 것이 최종 목표
* 타입스크립트 팁
1) any 대신 unknown 쓰기 : 불가피하게 타입 단정 불가한 경우에는 unknown쓰기
unknown은 any와 다르게 바로 사용 불가능하고, type narrowing 해야 사용 가능
( * type narrowing : 타입을 의도대로 좁히기)
2) 타입가드 활용
타입가드: 타입을 좁히는 데 도움을 줌.
- instanceof, typeof : 인스턴스가 특정 클래스의 인스턴스인지 확인 가능
- in : 어떤 객체에 키가 존재하는지 확인 가능
- 제네릭 : 단일 타입이 아닌 다양한 타입에 대응할 수 있게 도와줌,
타입만 다른 비슷한 작업 컴포넌트를 단일 제네릭 컴포넌트로 선언해서 간결하게 작성 가능
useState에 활용 가능 -> 기본값 넘기지 않으면 udefined되는 문제 방지
- 인덱스 시그니처 : 객체의 키를 정의하는 방식, 동적인 객체 정의할 때 유용