1장 리액트 개발을 위해 꼭 알아야 할 자바스크립트
자바스크립트의 데이터 타입
자바스크립트의 모든 값은 데이터 타입을 갖고있고, 이 데이터 타입은 크게 원시 타입과 객체 타입으로 나뉩니다.
원시 타입
- boolean : 참(true)과 거짓(false)만을 가질 수 있는 데이터 타입. 주로 조건문에서 많이 쓰인다.
- null : 아직 값이 없거나 비어 있는 값을 표현할 때 사용한다.
- undefined : 선언한 후 값을 할당하지 않은 변수 또는 값이 주어지지 않은 인수에 자동으로 할당되는 값.
- number : (2^53-1) 과 2^53-1 사이의 값을 저장할 수 있다.
- string : 텍스트 타입의 데이터를 저장하기 위해 사용된다. ( '' , "" , `` ) 으로 표현할 수 있다.
- symbol : ES6에서 새롭게 추가된 타입, 중복되지 않는 어떠한 고유한 값을 나타내기 위해 만들어졌다. (심벌을 생성하려면 반드시 Symbol() 을 사용해야만 한다.)
- bigint : number가 다룰 수 있는 숫자 크기의 제한을 극복하기 위해 ES2020에서 새롭게 나온 것.
객체 타입
- object
원시타입은 객체가 아닌 다른 모든 타입을 의미합니다.
객체가 아니므로 이러한 타입들은 메서드를 갖지 않습니다.
undefined 는 '선언됐지만 할당되지 않은 값' 이고, null은 '명시적으로 비어 있음을 나타내는 값' 입니다.
객체타입은 원시 타입 이외의 모든 것. 즉 자바스크립트를 이루고 있는 대부분의 타입입니다.
원시 타입과 객체 타입의 가장 큰 차이점은 바로 값을 저장하는 방식의 차이 입니다.
원시 타입은 불변 형태의 값으로 저장되고, 이 값은 변수 할당 시점에 메모리 영역을 차지하고 저장됩니다.
반면, 객체는 프로퍼티를 삭제, 추가, 수정할 수 있어 변경 가능한 형태로 저장되고,
값을 복사할 때도 값이 아닌 참조를 전달합니다.
자바스크립트의 또 다른 비교 공식, Object.is
Object.is는 두 개의 인수를 받아 이 인수 두 개가 동일한지 확인하고 반환하는 메서드입니다.
Object.is 가 == 나 === 와 다른 점
- ==비교는 같음을 비교하기 전에 양쪽이 같은 타입이 아니라면 비교할 수 있도록 강제로 형변환을 한 후 비교.
- === 과 Object.is 둘 다 타입이 다르면 false를 반환 하지만 Object.is가 좀 더 개발자가 기대하는 방식으로 정확히 비교.
-0 === +0 //true
Object.is(-0, +0) //false
Number.NaN === NaN //false
Object.is(Number.NaN, NaN) //true
NaN === 0 / 0 //false
Object.is(NaN, 0 / 0) //true
이러한 몇 가지 특별한 사항에서 동등 비교 ===가 가지는 한계를 극복하기 위해 Object.is가 만들어졌지만 ,
여전히 객체 간 비교에 있어서는 자바스크립트의 특징으로 인해 ===와 동일하게 동작합니다.
리액트에서의 동등 비교
리액트에서 사용하는 동등 비교는 Object.is입니다.
이 objecIs를 기반으로 동등 비교를 하는 shallowEqual 이라는 함수를 만들어 사용한다.
리액트에서의 비교를 요약하자면 Object.is로 먼저 비교를 수행한 다음에 Object.is에서 수행하지 못하는 비교,
즉 객체 간 얕은 비교를 한 번 더 수행합니다.
ㄴ 객체의 첫 번째 깊이에 존재하는 값만 비교하는 것을 의미
이렇게 객체의 얕은 비교까지만 구현한 이유는?
먼저 리액트에서 사용하는 JSX props는 객체이고, 여기에 있는 props만 일차적으로 비교하면 되기 때문입니다.
리액트는 props에서 꺼내온 값을 기준으로 렌더링을 수행하기 때문에 얕은 비교로 충분합니다.
함수란 무엇인가?
자바스크립트에서 함수란
'작업을 수행하거나 값을 계산하는 등의 과정을 표현하고, 이를 하나의 블록으로 감사 실행 단위로 만들어 놓은 것 '
// 함수의 기본적인 형태
function sum(a,b) {
return a + b
}
sum(10,24) //34
function 뒤에 오는 것이 함수명
함수수의 입력값으로 받는 a,b를 각각 매개변수라 하고, return으로 작성된 것이 반환값입니다.
리액트에서 컴포넌트를 만드는 함수도 이러한 기초적인 형태를 따릅니다.
function Component(props) {
return <div>{props.hello}</div>
}
Component라고 하는 함수를 선언하고 매개변수로는 일반적으로 props라고 부르는 단일 객체를 받으며
return 문으로 JSX를 반환합니다.
함수를 정의하는 4가지 방법
함수 선언문
- 자바스크립트에서 함수를 선언할 때 가장 일반적으로 사용하는 방식
function add(a, b) {
return a + b
}
함수 선언문은 표현식이 아닌 일반 문으로 분류됩니다.
표현식이란 무언가 값을 산출하는 구문을 의미하는데 어떠한 값이 표현되지 않았으므로 표현식이 아닌 문으로 분류됩니다 .
함수 표현식
- 자바스크립트에서 함수는 일급 객체.
- 함수는 다른 함수의 매개변수가 될 수도 있고, 반환값이 될 수도 있으며, 할당도 가능하므로 일급 객체가 될 수 있다.
일급 객체 : 다른 객체들에 일반적으로 적용 가능한 연산을 모두 지원하는 객체
함수 표현식과 선언식의 차이
함수 표현식과 함수 선언 식의 가장 큰 차이는 호이스팅(hoisting) 여부입니다.
호이스팅(Hoisting)은 자바스크립트에서 변수와 함수 선언이 코드의 최상단으로 끌어올려지는 현상을 말합니다. 이는 코드가 실행되기 전에 자바스크립트 엔진이 변수와 함수 선언을 미리 처리하기 때문에 발생하는데, 변수와 함수 선언이 마치 코드의 맨 위에 위치한 것처럼 작동하게 됩니다.
hello() //hello
function hello() {
console.log('hello')
}
hello() //hello
함수의 호이스팅이라는 특징 덕분에 함수 선언문이 미리 메모리에 등록되어
코드의 순서에 상관없이 정상적으로 함수를 호출할 수 있게 되었습니다.
함수의 호이스팅 : 함수에 대한 선언을 실행 전에 미리 메모리에 등록하는 작업
반면 함수 표현식은 함수를 변수에 할당.
변수도 호이스팅이 발생하지만 함수의 호이스팅과는 다르게, 호이스팅 되는 시점에서 var의 경우에는 undefined로 초기화.
함수와 다르게 변수는, 런타임 이전에 undefined로 초기화되고,
할당문이 실행되는 시점, 즉 런타임 시점에 함수가 할당되어 작동합니다.
Fiction 생성자
const add = new Function('a','b', 'return a + b')
add(10, 24) //34
화살표 함수
- ES6에서 새롭게 추가된 함수 생성 방식
- function 이라는 키워드 대신 => 라는 화살표를 활용해서 함수를 만든다.
const add = (a, b) => {
return a + b
}
const add = (a, b) => a + b
화살표 함수는 앞서 언급한 함수 생성 방식과 몇 가지 큰 차이점이 있습니다.
- 화살표 함수에서는 constructor을 사용할 수 없다. 즉, 생성자 함수로 화살표 함수를 사용하는 것은 불가능.
- 화살표 함수에서는 arguments가 존재하지 않는다.
- 가장 큰 차이점은 바로 this 바인딩.
this : 자신이 속한 객체나 자신이 생성할 인스턴스를 가리키는 값
this는 화살표 함수 이전까지 함수를 정의 할 때 결정되는 것이 아니라, 함수가 어떻게 호출되느냐에 따라 동적으로 결정됩니다.
함수가 일반 함수로서 호출된다면 , 그 내부의 this는 전역 객체를 가리킵니다.
이와 달리 화살표 함수는 함수 자체의 바인딩을 갖지 않습니다.
화살표 함수 내부에서 this를 참조하면 상위 스코프의 this를 그대로 따르게 됩니다.
즉, 화살표 함수는 별도의 작업을 추가로 하지 않고 this에 접근할 수 있습니다.
다양한 함수 살펴보기
즉시 실행 함수
- 함수를 정의하고 그 순간 즉시 실행되는 함수.
- 단 한 번만 호출되고, 다시 호출할 수 없는 함수.
즉시 실행 함수는 한 번 선언하고 호출된 이후에 재호출이 불가능하기 때문에 이름을 붙이지 않습니다.
고차 함수
- 자바스크립트의 함수가 함수를 인수로 받거나 결과를 새로운 함수로 반환시키는 역할을 하는 함수.
함수 컴포넌트를 인수로 받아 새로운 함수 컴포넌트를 반환하는 고차 함수를 만들 수도 있습니다.
고차 함수 컴포넌트를 만들면 컴포넌트 내부에서 공통으로 관리되는 로직을 분리해 관리할 수 있어 효율적인 리팩터링이 가능합니다.
함수를 만들 때 주의해야 할 사항
- 자바스크립트 함수에서는 가능한 한 부수효과를 최소화하고,
함수의 실행과 결과를 최대한 예측 가능하도록 설계해야 한다. - 가능한 함수를 작게 만들어라
- 누구나 이해할 수 있는 이름을 붙여라.
클래스란 무엇인가?
자바스크립트의 클래스란 특정한 객체를 만들기 위한 일종의 템플릿과 같은 개념입니다.
즉, 특정한 형태의 객체를 반복적으로 만들기 위해 사용되는 것입니다.
constructor
- 생성자. 객체를 생성하는 데 사용하는 특수한 메서드.
- 단 하나만 존재할 수 있고, 여러 개를 사용한다면 에러가 발생.
프로퍼티
- 클래스로 인스턴스를 생성할 때 내부에 정의할 수 있는 속성값을 의미.
- 인스턴스 생성 시 constructor 내부에 빈 객체에 프로퍼티의 키와 값을 넣어 활용 가능.
- #을 넣어 private를 선언 가능.
인스턴스 메서드
- 클래스 내부에서 선언한 메서드.
- 자바스크립트의 prototype에 선언되기 때문에 프로토타입 메서드로 불리기도 한다.
정적 메서드
- 클래스의 인스턴스가 아닌 이름으로 호출할 수 있는 메서드.
상속
- extends는 기존 클래스를 상속받아 자식 클래스에서 이 상속받은 클래스를 기반으로 확장하는 개념.
클로저의 정의
MDN 의 클로저 정의 : 함수와 함수가 선언된 어휘적 환경의 조합
ㄴ 변수가 코드 내부에서 어디에 선언됐는지
클로저는 자바스크립트에서 함수와 그 함수가 선언된 환경(scope)를 기억하는 기능입니다.
쉽게 말해 함수가 선언될 때의 외부 변수에 접근할 수 있는 함수를 의미합니다.
리액트에서의 클로저
리액트 함수 컴포넌트 훅에서 클로저는 어떻게 사용될까?
대표적인 예로 useState를 들어봅시다.
function Component() {
const [state, setState] = useState()
function handleClick() {
//useState호출은 위에서 끝났지만
//setState는 계속 내부의 최신값(prev)을 알고 있다.
//이는 클로저를 활용했기 때문에 가능하다.
setState((prev) => prev + 1)
}
}
외부 함수(useState)가 반환한 내부 함수(setState)는 외부함수(useState)의 호출이 끝났음에도 자신이 선언된 외부 함수가 선언된 환경(state가 저장돼 있는 어딘가)을 기억하기 때문에 계속해서 state 값을 사용할 수 있습니다.
클로저의 기본 개념인 '함수와 함수가 선언된 어휘적 환경의 조합'을 주의 깊게 살펴봐야 클로저를 제대로 활용할 수 있습니다.
주의할 점
- 클로저를 사용하는 데는 비용이 든다.
-생성될 때 마다 선언적 환경을 기억해야 하므로 추가로 비용이 발생합니다.
싱글 스레드 자바스크립트
자바스크립트는 싱글 스레드 언어입니다.
하나의 프로그램에는 하나의 프로세스만 할당되기 때문에 하나의 프로그램에서 동시에 여러 개의 복잡한 작업을 수행하기 위해
더 작은 실행 단위인 스레드(thread)가 탄생했습니다.
하나의 프로세스에서는 여러 개의 스레드를 만들 수 있습니다.
스레드끼리는 메모리를 공유할 수 있어 여러 가지 작업을 동시에 수행할 수 있습니다.
자바스크립트가 싱글 스레드 라는 것은 ?
자바스크립트 코드의 실행이 하나의 스레드에서 순차적으로 이루어진다는 것
Run-to-completion
ㄴ 하나의 코드가 실행하는 데 오래 걸리면 뒤이은 코드가 실행되지 않는 자바스크립트의 특징.
하나의 작업이 끝나기 전까지는 다른 작업이 실행되지 않아 어떠한 작업이 오래 걸린다면 사용자에게 마치 웹페이지가 멈춘듯한 느낌을 줄 수 있습니다.
즉, 자바스크립트의 모든 코드는 '동기식'으로 한 번에 하나씩 순차적으로 처리됩니다.
이벤트 루프란?
이벤트 루프란
자바스크립트 런타임 외부에서 자바스크립트의 비동기 실행을 돕기 위해 만들어진 장치.
'비동기' 란? async(asynchronous) 여러 작업을 동시에 시작할 수 있는 기능입니다.
호출 스택과 이벤트 루프
호출 스택(call stack)
- 자바스크립트에서 수행해야 할 코드나 함수를 순차적으로 담아두는 스택.
이 호출 스택이 비어 있는지 여부를 확인하는 것이 바로 이벤트 루프입니다.
이벤트 루프는 단일 스레드 내부에서 이 호출 스택 내부에 수행해야 할 작업이 있는지 확인하고,
수행해야 할 코드가 있으면 자바스크립트 엔진을 이용해 실행합니다.
'코드를 실행하는 것'과 '호출 스택이 비어있는지 확인하는 것' 모두 단일 스레드에서 일어납니다.
두 작업은 동시에 일어날 수 없고, 한 스레드에서 순차적으로 일어납니다.
비동기 작업은 어떻게 실행될까??
이벤트 루프는 태스크 큐를 한 개 이상 가지고 있습니다.
태스크큐는 자료구조의 (queue)가 아닌 set의 형태를 띄고있습니다.
FIFO(First In First Out) 형식이 아닌 선택된 큐 중에서 실행 가능한 가장 오래된 태스크를 가져오는 방식입니다.
태스크 큐 : 실행해야 할 태스크의 집합 (비동기 함수의 콜백 함수나 이벤트 핸들러 등)
여기서 이벤트 루프의 역할은
- 호출 스택에 실행중인 코드가 있는지 확인
- 태스크 큐에 대기 중인 함수가 있는지 반복해서 확인
- 호출 스택이 비었다면 태스크 큐에 대기중인 작업이 있는지 확인
- 실행 가능한 오래된 것부터 순차적으로 꺼내와 실행 (태스크 큐가 빌 때까지)
비동기 함수는 태스크 큐가 할당되는 별도의 스레드에서 수행됩니다.
자바스크립트 코드 실행은 싱글 스레드에서 이루어지지만
외부 Web API등은 모두 자바스크립트 코드 외부에서 실행되고 콜백이 태스크 큐로 들어갑니다.
이벤트 루프는 호출 스택이 비고, 콜백이 실행 가능하면 이것을 꺼내 수행하는 역할을 합니다.
태스크 큐와 마이크로 태스크 큐
이벤트 루프는 하나의 마이크로 태스크 큐를 가집니다.
마이크로 태스크에는 대표적으로 Promise 가 있는데 기존 태스크 큐보다 우선권을 가지기 때문에
setTimeout 과 setInterval은 Promise 보다 늦게 실행됩니다.
>> 마이크로 태스크 큐가 빌 때까지 기존 태스크 큐의 실행은 뒤로 미뤄집니다.
렌더링은 마이크로 태스크 큐를 실행하고, 그 이후에 일어납니다.
각 마이크로 태스크 큐 작업이 끝날 때마다 한 번씩 렌더링 할 기회를 얻습니다.
브라우저에 렌더링하는 작업은 마이크로 태스크 큐와 태스크 큐 사이에서 일어납니다.
리액트에서 자주 사용하는 자바스크립트 문법
구조 분해 할당
- 배열 또는 객체의 값을 말 그대로 분해해 개별 변수에 즉시 할당하는 것.
- 어떠한 객체나 배열에서 선언문 없이 즉시 분해해 변수를 선언하고 할당하고 싶을 때 사용.
배열 구조 분해 할당
- 배열의 값을 쉽게 변수에 할당
- 배열의 각 요소를 순서대로 변수에 한 번에 할당할 수 있다.
const array = [1, 2, 3, 4, 5]
const [first,second, third ...arrayRest] = array
//first 1
//second 2
//third 3
//arrayRest[4,5]
배열 분해 할당에는 기본값을 선언할 수도 있습니다.
주의할 점은 반드시 undefined일 때만 기본값을 사용합니다.
뒤에 ...을 사용하면 나머지 모든 값을 해당 변수에 배열로 넣게 됩니다.
객체 구조 분해 할당
말 그대로 객체에서 값을 꺼내온 뒤 할당하는 것을 의미합니다.
배열 구조 분해 할당과는 달리, 객체는 객체 내부 이름으로 꺼내온다는 차이가 있습니다.
const object = {
a: 1,
b: 2,
c: 3,
d: 4,
e: 5,
}
const { a, b, c, ...objectRest } = object
//a 1
//b 2
//c 3
//objectRest = {d:4, e:5}
Array 프로토타입의 메서드 :map, filter, reduce, forEach
Array.prototype.map
- 인수로 전달받은 배열과 똑같은 길이의 새로운 배열을 반환하는 메서드.
- 배열의 각 아이템을 순회하면서 각 아이템을 콜백으로 연산한 결과로 구성된 새로운 배열을 만들 수 있다.
const arr = [1, 2, 3, 4, 5]
const doubledArr = arr.map((item) => item * 2)
// [2,4,6,8,10]
Array.prototype.filter
- 콜백 함수를 인수로 받는다.
- filter의 결과에 따라 원본 배열의 길이 이하의 새로운 배열이 반환된다.
- 기존 배열에 대해 어떠한 조건을 만족하는 새로운 배열을 반환할 때 쓰인다.
const arr = [1, 2, 3, 4, 5]
const evenArr = arr.filter((item) => item % 2 === 0)
// [2,4]
Array.prototype.reduce
- 콜백 함수와 함께 초깃값을 추가로 인수를 받는다.
- 초깃값에 따라 배열이나 객체, 또는 그 이외의 다른 무언가를 반환할 수 있다.
- reducer 콜백 함수를 실행하고, 이를 초깃값에 누적해 결과를 반환한다.
const arr = [1, 2, 3, 4, 5]
const sum = arr.reduce((result, item) => {
return result + item
}, 0)
// 15
Array.prototype.forEach
- 콜백 함수를 받아 배열을 순회하면서 단순히 그 콜백 함수를 실행하기만 한다.
const arr = [1, 2, 3]
arr.forEach((item) => console.log(item))
// 1, 2, 3
forEach는 아무런 반환값이 없습니다.
실행되는 순간 에러를 던지거나 프로세스를 종료하지 않는 이상 이를 멈출 수 없습니다.
>> return 함수의 return이 아닌 콜백 함수의 return으로 간주되기 때문입니다.
forEach 내부의 콜백 함수는 무조건0(n)만큼 실행됩니다.
삼항 조건 연산자
- 자바스크립트에서 유일하게 3개의 피연산자를 취할 수 있는 문법이다.
const value = 10
const result = value % 2 === 0 ? '짝수' : '홀수'
//짝수
맨 앞에 true/false를 판별할 수 있는 조건문이 들어가고 ? 뒤에는 참일 경우 반환할 값 : 뒤에는 거짓일 때 반환할 값을 지정합니다.
조건문 ? 참일 때의 값 : 거짓일 때의 값
타입스크립트란?
타입스크립트
ㄴ기존 자바스크립트 문법에 타입을 가미한 것.
자바스크립트는 기본적으로 동적 타입의 언어이기때문에 대부분의 에러를 코드를 실행했을 때만 확인할 수 있다는 문제점이 있습니다.
typeof를 적용하여 문제점을 방지할 수 있지만 너무 번거롭고 코드의 크기를 과도하게 키우게 됩니다.
타입스크립트는 이러한 자바스크립트의 한계를 벗어나 타입 체크를 정적으로 런타임이 아닌 빌드 타임에 수행할 수 있게 해줍니다.
리액트 코드를 효과적으로 작성하기 위한 타입스크립트 활용법
- any 대신 unknown을 사용하자
- 타입 가드를 적극 활용하자
- 제네릭
- 함수나 클래스 내부에서 단일 타입이 아닌 다양한 타입에 대응할 수 있도록 도와주는 도구.
- 타입만 다른 비슷한 작업을 하는 컴포넌트를 단일 제네릭 컴포넌트로 선언해 간결하게 작성할 수 있다.
- > 사용할 때 필요한 타입을 지정할 수 있다.
- useState에 제네릭으로 타입을 선언하면 state 사용과 기본값 선언을 좀 더 명확하게 할 수 있다.
function Component() {
//state: string
const [state, setState] = useState<string>('')
//...
}
- 인덱스 시그니처
- 객체의 키를 정의하는 방식.
- Record<key, Value>를 사용하면 객체의 타입에 각각 원하는 키와 값을 넣을 수 있다.
- 객체를 원하는 형태로 최대한 좁힐 수 있다
'모던 리액트 Deep Dive 스터디' 카테고리의 다른 글
리액트 훅 깊게 살펴보기(useReducer, useRef) (7) | 2024.11.15 |
---|---|
리액트 훅 깊게 살펴보기(useState, useEffect, useMemo, useCallback) (0) | 2024.11.12 |
선택이 아닌 필수, 타입스크립트 (0) | 2024.11.07 |
리액트에서 자주 사용하는 자바스크립트 문법 (0) | 2024.11.07 |
이벤트 루프와 비동기 통신의 이해 (0) | 2024.11.06 |