본문 바로가기

Study

[모던 리액트 Deep Dive 스터디] 리액트 개발을 위해 꼭 알아야 할 자바스크립트

1장. 리액트 개발을 위해 꼭 알아야 할 자바스크립트
- 자바스크립트의 동등비교
- 함수
- 클래스
- 클로저
- 이벤트 루프와 비동기 통신의 이해
- 리액트에서 자주 사용하는 자바스크립트 문법
- 선택이 아닌 필수, 타입스크립트

자바스크립트의 동등비교

리액트 컴포넌트 렌더링이 일어나는 이유props의 동등비교에 따른 결과 props의 동등 비교는 얕은 비교 기반

자바스크립트 동등비교 종류

  • Equal Operator ==
  • Strict Equal Operator ===
  • Object.is (리액트에서 사용)
// Object.is는 참조가 다른 객체에 대한 비교 불가능
Object.is({hello: 'world'}, {hello: 'world'}) // false

// 리액트의 shallowEqual은 객체의 1depth까지는 비교 가능
shallowEqual({hello: 'world'}, {hello: 'world'}) // true

// 그러나 2depth는 비교할 방법이 없어 false 반환
shallowEqual({hello: {hi: 'world'}, {hello: {hi: 'world'}} // false

 

리액트는 Object.is를 기반으로 한 shallowEqual 함수를 통해 동등비교를 한다.

Object.is는 참조가 다른 객체에 대해 비교가 불가능하지만,
shallowEqual 함수에서는 이부분이 보완되어 1depth 까지는 비교가 가능하다.

불완전한 객체 비교의 특징을 잘 알고 있어야 리액트에서 의존성 배열의 비교, 렌더링 최적화를 위한 useMemo, useCallback을 올바르게 작동시킬 수 있다.

함수

함수는 작업을 수행하거나 값을 계산하는 등의 과정을 하나의 블록으로 감싼 실행 단위이다.

리액트에서 컴포넌트를 생성하는 2가지 방식 중 하나이다.

함수를 정의하는 4가지 방법

  • 함수 선언문
    • 가장 일반적으로 사용하는 방식
  • 함수 표현식
    • 이름이 없는 익명 함수로 작성하는 것이 일반적
  • Function 생성자
    • 가독성X, 클로저X ⇒ 거의 사용되지 않음
  • 화살표 함수
    • 최근 각광받는 방식, 함수 내부에서 this 참조 시 상위 스코프의 this를 그대로 따름

함수 만들 때 주의사항

  • 함수의 부수효과 최대한 억제하기
    • 리액트 관점에서는 부수효과를 처리하는 훅인 useEffect의 작동을 최소화 하기
  • 가능한 함수를 작게 만들기
  • 누구나 이해 할 수 있는 함수명 짓기
    • 리액트 관점에서는 useEffect나 useCallback 함수에 네이밍을 붙여주는것도 좋은 방법

클래스

리액트에서 컴포넌트 생성하는 2가지 방식 중 나머지 하나이다.

특정한 형태의 객체를 반복적으로 만들기 위해서 사용한다.

클래스 구성 요소

  • constructor
    • 클래스의 생성자로 객체를 생성하는 데 사용하는 특수 메서드
  • property
    • 인스턴스 생성 시 내부에 정의 할 수 있는 속성 값
  • getter/setter
    • 값을 가져오거나 값을 할당할 때 사용
  • 인스턴스 메서드
    • 클래스 내부에 선언한 메서드로 prototype에 선언됨
  • 정적 메서드
    • 클래스의 인스턴스가 아닌 클래스의 이름으로 호출 할 수 있는 메서드

클래스 특징

  • 상속
    • 기존 클래스를 상속받아 자식 클래스에서 상속 받은 클래스를 기반으로 확장
  • 프로토타입 체이닝
    • 직접 객체에서 선언하지 않았음에도 프로토타입에 있는 메서드를 찾아 실행

클래스가 작동하는 방식은 자바스크립트의 프로토타입을 활용하는 것이다.

클래스는 객체지향 언어를 사용하던 프로그래머가 좀 더 자바스크립트에 접근하기 쉽게 만들어 주는 일종의 syntactic sugar 이다.

16버전 이전의 리액트에서는 클래스 컴포넌트로 생성되어 있으므로 클래스를 이해하고 있는 것이 좋다.

클로저

함수와 함수가 “선언된 어휘적 환경”의 조합

선언된 어휘적 환경이라는 것은 변수가 코드 내부 어디서 선언되었는지를 의미한다.

호출되는 방식에 따라 동적으로 결정되는 this와는 다르게 코드가 작성된 순간에 정적으로 결정된다.

변수의 유효 범위, 스코프의 종류

  • 전역 스코프
    • 전역 레벨에 변수 선언, 어디서나 호출 가능
  • 함수 스코프
    • 함수 레벨 범위, 함수내에서 선언한 변수는 함수 내에서만 호출 가능
  • 블록 스코프
    • {} 블록 레벨 범위

클로저의 활용

function outerFunction() {
	var x = 'hello'
	function innerFunction() {
		console.log(x)
	}

	return innerFunction
}

const innerFunction = outerFunction()
innerFunction() // 'hello'

 

클로저가 활용된 예시 코드이다.

outerFunction에는 x라는 변수가 존재하기 때문에 같은 환경에서 선언되고 반환된 innerFunction에서 접근 할 수 있는 것이다.

⇒ 선언된 어휘적 환경을 기억하는 것

활용 예제

  1. 클로저 활용 X
var counter = 0
function handleClick() {
	counter++
}

counter 변수인 전역 변수는 어디서든 원하는 값을 꺼내올 수 있다는 장점이 있다. 그러나, 누구나 수정이 가능하고 window.counter를 활용해 접근도 가능하다.

  1. 클로저 활용 O
function Counter() {
	var counter = 0
	
	return {
		increase: function() {
			return ++counter
		},
		decrease: function() {
			return --counter
		},
		counter: function() {
			console.log('counter에 접근!')
			return counter
		}
	}
}

var c = Counter()

console.log(c.increase()) // 1
console.log(c.decrease()) // 0
console.log(c.counter()) // 0

 

클로저를 활용한 코드로 변경했을 때는 다음과 같은 이점을 얻을 수 있다.

  • counter 변수를 직접적으로 노출하지 않아 사용자가 직접 수정하는 것을 방지
  • counter 변수의 업데이트를 increase 와 decrease로 제한해 무분별한 변경 방지
  • 개발자가 원하는 정보만 개발자가 원하는 방향으로 노출 가능

리액트에서의 클로저의 대표 활용 예제는 useState 이다.

function Component() {
	const [state, setState] = useState()
	
	function handleClick() {
		setState((prev) => prev + 1)
	}
}

 

useState 호출은 끝났지만 setState에서 계속 내부의 최신값인 prev를 알고 있는건 클로저를 활용했기 때문이다.

외부 함수인 useState가 반환한 내부 함수 setState 에서 자신이 선언된 외부 함수가 선언된 환경 어딘가에

state (prev)가 저장되어 있기 때문에 이 값을 사용할 수 있는 것이다.

클로저 주의 할 점

생성될 때마다 그 선언적 환경을 기억해야 하므로 추가 비용이 발생하므로 사용에 주의를 기울여야 한다.

이벤트 루프와 비동기 통신의 이해

자바스크립트싱글 스레드 언어이기 때문에 하나의 작업만 동기적으로 처리 할 수 있다.

그러나 자바스크립트로 개발된 웹페이지에서도 수많은 비동기 작업이 존재하는데

이 작업이 어떻게 이뤄지는지 알기 위해서는 이벤트 루프, 호출스택, 태스크 큐에 대해 이해해야 한다.

호출 스택과 이벤트 루프

  • 호출 스택
    • 자바스크립트에서 수행해야 할 코드나 함수를 순차적으로 담아두는 스택
  • 이벤트 루프
    • 호출 스택 내부에 수행해야 할 작업이 있는지 확인하는 역할
  • 태스크 큐
    • 비동기적으로 실행해야 할 태스크의 집합

비동기 통신 과정을 요약하자면 다음과 같다.

  1. 모든 작업은 호출 스택에 들어간다.
  2. 비동기 작업은 호출 스택에서 제거 되면 태스크 큐에 들어가 대기한다.
  3. 이벤트 루프는 호출 스택이 비어 있는지 지속적으로 확인한다.
  4. 호출 스택이 비어 있는 경우에 태스크 큐에서 비동기 작업을 가져와 호출 스택에 넣어 실행한다.

비동기 작업은 메인 스레드가 아닌 태스크 큐가 할당되는 별도의 스레드에서 수행된다.

이건 브라우저나 node.js의 역할이다.

마이크로 태스크 큐

  • 마이크로 태스크 큐
    • 태스크 큐보다 우선권을 갖는 태스크 집합으로 태스크 큐보다 먼저 실행된다.

마이크로 태스크 큐, 태스크 큐 종류

  • 태스크 큐 : setTimeout, setInterval, setImmediate
  • 마이크로 태스크 큐 : process.nextTick, Promises, queueMicroTask, MutationObserver

브라우저 렌더링 과정은 마이크로 테스크 큐와 테스크 큐 사이에 일어난다.

마이크로 태스크 큐 → 렌더링 → 태스크 큐 마이크로 태스크 큐 작업이 끝날 때마다 한번씩 렌더링 할 기회를 얻는다.

리액트에서 자주 사용하는 자바스크립트 문법

  • 구조 분해 할당
    • 배열 구조 분해 할당
    • 객체 구조 분해 할당
  • 전개 구문
    • 배열의 전개 구문
    • 객체의 전개 구문
  • 객체 초기자
  • Array 프로토타입 메서드 (map, filter, reduce, forEach)
  • 삼항 조건 연산자

선택이 아닌 필수, 타입 스크립트

타입스크립트는 기존 자바스크립트 문법에 타입을 가미한 것이다.

자바스크립트의 한계를 벗어나 타입 체크를 런타임이 아닌 빌드 타임에 정적으로 수행한다.

리액트 코드를 효과적으로 작성하기 위한 타입 스크립트 활용법

  • any 대신 unknown을 사용하기
  • 타입 가드 적극 활용하기
  • 다양한 타입에 대응 가능한 제네릭 사용하기
  • key에 원하는 타입을 부여 할 수 있는 인덱스 시그니처 사용하기

타입스크립트 전환 가이드

  • tsconfig.json 먼저 작성하기
  • JSDoc과 @ts-check를 활용해 점진적으로 전환하기
  • 타입 기반 라이브러리 사용을 위해 @types 모듈 설치하기
  • 파일 단위로 조금씩 전환하기
    • 가장 먼저 전환해볼만한 파일 : 상수나 유틸 같이 별도의 의존성이 없는 파일