목차
1. 모의 면접 준비
1. React
리액트 등장배경과 철학 (virtualDOM
)
리액트는 UI구축에 중점이 있는 자바스크립트 라이브러리 입니다.
- 등장배경
- SPA의 발전, 커지는 서비스규모, 늘어난 사용자인터랙션
- 빠르고 효율적인 UI 렌더링 처리방식에 대한 고민
- 철학
- 컴포넌트 not Template
- MVC 패턴에서 FLUX 패턴
- 기존 Model의 변화에 따라 View의 변화된 곳을 찾아 변경한 것에서 변경 시 View를 통째로 날리고 전부 새로 업데이트 한다.
- 모든 데이터가 최신임을 보장
- 선언형 데이터 작성
- virtualDOM
- 실제 DOM 구조와 비슷한 React객체의 트리
- Virtual DOM은
실제 DOM 변화횟수
를 최소화 시켜주는 역할 - virtualDOM을 이용하는 이유는
효율성
때문입니다. 실제 DOM을 직접 바꾸는 것보다시간 복잡도가 현저히 낮아짐
- Virtual DOM은 뷰에 변화가 있다면, 그 변화가 실제 DOM에 적용되기 전에 Virtual DOM에 적용시키고 최종 결과만 실제 DOM에 전달합니다. 따라서 20개의 변화가 있다면 Virtual DOM은 변화된 부분만 가려내어 실제 DOM에 전달하고 실제 DOM은 그 변화를 1회로 인식하여 단 한번의 렌더링 과정만 거치게 됩니다.
리액트의 virtual DOM
React Element와 Component의 차이
- ReactElement는 화면에 나타내고 싶은 정보를 담은 가상돔에서의 하나의 객체(Object)이다.
{ type: 'div', props: { children: 'Login', id: 'login-btn' } }
- 이 객체를 ReactDOM.render()를 통해 DOM에 렌더링한다.
<div id='login-btn'>Login</div>
반면
- 컴포넌트는 렌더링 로직과 UI 로직이 결합 된 하나의 단위
- 컴포넌트는 props와 state를 가지고, 생명주기를 가진다는 큰 차이
리액트의 특징과 장단점 (JSX
)
- 특징
- jsx는 javascript를 확장한 문법으로 HTML 형식을 가짐 (optional)
- 디자이너와 소통장점
- React Element를 생성
- Babel을 통해 React.createElemnt()로 호출 됨
- 렌더링로직(JSX)과 UI 로직(이벤트,State, 로딩)은 강하게 연관되어있으므로, 하나의 파일에서 느슨하게 연결
[ JSX ]
- 장단점
- virtualDOM을 통해 직접 돔조작을 하지 않는 편의성과 빠른 렌더링 속도로 성능적으로 우수
- 컴포넌트를 통해 재사용성, 높은 가독성과 쉬운 유지보수 가능
- 선언형 프로그래밍
- JSX를 통해 직접적으로 DOM을 컨트롤 하지 않아도 되는 장점
- 넓은 생태계와 커뮤니티
- 라이브러리이기 때문에, IDE지원부터, Typescript, babel, 스토리북, Next 등의 생태계들의 빠른 지원
- 빠르게 시장을 확보하여, 강력한 커뮤니티 및 시장을 형성한 것
- 자유도가 높아, 사용자의 코드퀄리티가 중요함 (
라이브러리
) - Vue의 경우 패턴이 정해져있어서, 어느정도 패턴화 됨 (
프레임워크
)
• 자바스크립트나 제이쿼리 개발자에게 React로 개발하라고 설득하고 싶다면, 어떤 이유를 들어서 설득시키겠는가?
[ 장점 ]
To사용자:
동적으로 변하는 웹서비스에서 UX측면
에서 훨씬 뛰어난 서비스를 제공가능 (빠른 렌더링 속도
)
To개발자:
선언형, 컴포넌트 기반으로 가독성
과 재사용성
, 유지보수
의 편리함, 넓은생태계
[ 단점 ]
(React) LifeCycle (1회독)
- LifeCycle
- 모든 컴포넌트는 생성, 업데이트 제거의 단계를 가지게 됨 (lifeCycle)
- React에서 컴포넌트가 생성, 업데이트, 제거 될 때 특정 메소드를 호출할 수 있는 것 (lifecycleMethod)
- 렌더이전에는 props를 가져와 state와 동기화하고, 상태변화를 감지하는 동작 수행
- 첫 렌더링 후,
componenentDidMount
실행 - 업데이트(new props, setState(), forceUpdate())시 렌더링 이후
componentDidUpdate
- 제거 될 때
componentWillUnmount
실행
- Hooks에서의 lifeCycle
- useEffect로 초기렌더링, 업데이트, 제거될 때 생명주기메소드 역할 모두 가능
- 의존성배열 없다면 컴포넌트렌더링 마다 Effect 실행
- 의존성배열이 [] (빈배열)이면 초기 렌더링에서 딱 1번만 실행
- 의존성 배열의 값이 있다면, 해당 값의 변화할 때만 Effect 실행
- useEffect 내에서 return 문 사용시
componentWillUnmount
역할
리액트 동작원리 (재조정-reconciliation
diffing알고리즘
, setState
)

[정의]
- 컴포넌트의 state나 props가 변경되면 React는 새로 반환된 컴포넌트를 이전에 렌더링된 컴포넌트와 비교하여 실제 DOM을 업데이트 해야하는지 결정합니다. 두 컴포넌트가 동일하지 않다면, React는 DOM을 업데이트 합니다. 이 과정을 재조정(Reconciliation)이라고 합니다. (공홈)
- 변화가 발생했을 때 VirtualDOM을 효율적으로 갱신하는 과정을 Reconciliation이라고 함
[특징]
- setState()를 통해 비동기적으로 배치 처리하여 한 번에 업데이트를 적용한다.
- render()함수 호출 시 virtualDOM의 최상위부터 변경된 부분을 확인한다. [Diffing알고리즘]
- 성능적 개선을 위해 참조비교만 진행 (불변성 필요), 키 기반으로 빠르게 컴포넌트 비교
- 잦은 변경이 있는 컴포넌트는 깊이가 얕도록 설계하기
[원리 - Diffing알고리즘]
- 일반적인 트리를 갱신하는 것의 시간복잡도(
O(N^3)
) →O(N)
으로 줄이는 리액트의 휴리스틱 알고리즘 - 서로 다른 타입의 엘리먼트는 서로 다른 트리를 가진다
- key prop을 통해, 변경되지 않을 자식 엘리먼트를 표시
- 루트엘리먼트부터 level by level 비교
- 타입이 다르면 버리고, 새로 구축
- 타입이 같다면 속성을 확인하여, 변경된 속성만 갱신 (style도 해당)
주의
state는 그대로 유지되지만, props는 갱신 (함수 재정의)- 자식노드들을 재귀적으로 처리
- key를 통해 변경된 부분만을 찾아 렌더링
[ 2가지원칙 ]
[ 과정 ]
(React) Hooks (1회독)
[개념]
Hooks는 클래스 기반 컴포넌트의 장점(예를 들면 내부 state와 생명주기 메소드)을 함수형 컴포넌트로 가져오려는 리액트의 시도입니다.
[장점]
1. 공통 기능을
커스텀 hook
로 만들어서 로직을 재사용
하기 쉬워집니다.
2. lifecycle method를 관련있는 코드를 모아 사용가능하다. (가독성, 관심사분리가능)
3. 클래스 기반 컴포넌트
, lifecycle method
, this
의 필요성이 사라집니다.
: 컴포넌트 자체에서 로직을 분리할 수 있어서 읽기 쉬운를 작성할 수 있습니다.- useState
- this.state는 항상 객체인 반면 useState는 다양한 타입 가능
- useEffect
- effect는 DOM을 조작하거나, 데이터를 가져오는 등의 렌더링과정에서 할 수 없는 작업들
- useMemo
- useCallback
- Hooks 장점
- 로직의 재사용 가능, 관리가 쉽다
Hook
은 함수형 컴포넌트 이므로 함수 안에서 다른 함수를 호출하는 것으로 새로운Hook
을 만들어 볼 수 있기 때문이다따라서 리액트의 내장되어있는Hook
과 다른 사람들이 만든 여러custom Hook
을 레고처럼 조립해서 쉽게custom Hook
을 만들수 있다- 로직을 한 곳으로 모을수 있어서 가독성이 좋다. (관심사 분리)
- 클래스형 컴포넌트의 라이프사이클 API는 서로 다른 로직이 하나의 메서드에 섞여 있어서 가독성이 좋지 않다.
Hook
은 같은 로직을 한 곳으로 모을 수 있다 - 구독 설정 및 데이터를 불러오는 것과 같은 로직의 분리
- 정적 타입언어로 타입을 정의하기 쉽다
- 상태 관리 방법을 더 쉽게 설명할 수 있다
- 클래스형의 경우 바벨 번들링 시 코드가 엄청 길어진다. 성능문제 야기
useMemo, React.memo, useCallback를 통한 최적화
2. useMemo + React.memo- 7m + 3m
값비싼 계산의 함수의 경우는 useMemo를 통해 memoization 하여, 관련된 상태의 변경 시에만 새로 계산하여, 컴포넌트를 리렌더링하도록하고,
부모컴포넌트의 변경과 관련 없는 자식 컴포넌트의 리렌더링 방지를 위해 React.memo를 사용할 수 있다.
함수 컴포넌트의 리렌더링 상황
- 자신의 상태(state)가 변경될 때
- 부모 컴포넌트로 부터 받는 props가 변경될 때
- 부모 컴포넌트가 리렌더링될 때 ⇒
React.memo
2-1 useMemo
useMemo는 memoization을 통해 특정 함수의 렌더링 최적화를 위해 사용
첫번째 인자로 값비싼 비용의 함수를 콜백형태로 받고, 두번째 인자로 의존성 배열을 넣어 사용useMemo( () ⇒ func , [] )
!! 연산속도가 느린 컴포넌트가 있다면, 리렌더링 상황에 취약하다.
- 값 비싼 연산을 가진 컴포넌트 예제
코드
xport default function UseMemoCompoent({ label, n }) { const expensiveComputeSum = (n) => { let result = 0; console.log("게산 시작"); for (let i = 0; i < n; i++) { result = result + n; } console.log("게산 끝"); return result; }; const result = useMemo(() => expensiveComputeSum(n), [n]);
주의
useMemo로 전달된 함수는 렌더링 중에 실행된다. 사이드 이펙트의 경우 useEffect에서 처리할 것!주의
useMemo는 필수가 아니라 값비싼 계산과 같은 필요한 부분에 적용하는 것 (메모리 비용들기 때문에)2-2. React.memo(공식문서 link)
React.memo는 고차컴포넌트(HOC)로, 동일한 props를 통해 같은 결과를 렌더링한다면, 이전에 Memoization 해두었던 컴포넌트를 그대로 재사용한다.
부모 컴포넌트 렌더링되면 무조건 자식컴포넌트가 렌더링되는 상황의 최적화시 사용
주의
React.memo로 감싸더라도, 내부 컴포넌트에서 useState, useReducer, useContext등을 통해 state를 변경한다면 리렌더링 된다.
주의
부모로 부터 받는 props가 변할 경우에 당연히 리렌더링 한다.
심화
props는 기본적으로 얕은 비교를 수행한다.- 깊은 비교로 리렌더링 여부를 결정하기 위해서는 두번째 인자로 custom비교함수를 넣어주어 사용할 수 있다.
React.memo(ChildComponent, areDeepEquaul)
3. useCallback- (공식문서) 6m
컴포넌트에서 props을 통해 함수를 받게 될 때, 해당 함수는 항상 재정의되기 때문에 React.memo를 적용하여도 리렌더링 된다.
이를 방지하기 위해, 함수 자체를 memoization하여, props으로 넘겨주어도 재정의되어 리렌더링 되는 것을 막을 수 있다.
사용상황
- 하나의 부모요소의 여러개의 자식요소가 있을 때, 특정 자식요소의 변경이 모든 자식요소의 리렌더링 되는 것을 방지할 때 사용
- 즉 여러 자식요소 중 변경이 있는 자식요소만을 리렌더링하고 싶을 때에는
- React.memo를 통해 자식 컴포넌트를 memoization하고,
- props로 넘어오는 함수들은 부모컴포넌트의 변화로 리렌더링시 재정의되지 않도록 useCallback을 통해 memoization 해주어야 한다.
(React) 불변성을 지켜야 하는 이유 (1회독)
React에서 컴포넌트 렌더링 시 state, props의 얕은 비교를 진행 하기 때문 (계산효율성위해)
state를 setState가 아닌 직접 값을 변경할 경우 이를 감지하지 못하여 리렌더링 되지 않음
state를 직접 변경하지 않고, setState를 사용하는 이유
- state는 불변성(새로운 값을 만들어 사용하는 것)을 유지해야하기 때문입니다.
- React의 재조정과정에서 성능을 위해 참조비교만을 진행한다.
- 따라서 참조가 같으면(state.count = 3) 상태변화를 인지할 수 없다.
(
render함수 미호출
)
- 불변성 (immutability)이란
- 어떤 값을 직접 변경하는 것이 아니라 새로운 값을 만들어 내는 것 (사본을 만들고 변경시킴)
- 같은 참조(reference)를 가지지 않도록 설정
- React에서는 얕은 복사를 통해 state,props를 비교하기 때문에 참조타입 자료형의 경우 deep copy를 통해 내부의 값까지 똑같이 복사해주어야 한다.
- call by value (깊은 비교) , call by reference(얕은 비교- 동일참조인지만 확인)
- 불변성 지키는 방법
- spreadOperator(
...
)을 통한 복사, Object.assign({}, ) (얕은복사) - concat(), map(), filter(), slice() 등 새로운 객체를 반환하는 메소드 사용
(
원본데이터 변경x
) - 깊은 복사의 경우 lodash의 deepclone, immer 라이브러리 사용
2. 번들링
webpack
ECMAScript2015 이전에는 모듈을 만들기 위해 즉시실행함수와 네임스페이스 패턴을 사용했다. 이후 각 커뮤니티에서 모듈 시스템 스펙이 나왔고 웹팩은 ECMAScript2015 모듈시스템을 쉽게 사용하도록 돕는 역할을 한다.
엔트리포인트를 시작으로 연결되어 었는 모든 모듈을 하나로 합쳐서 결과물을 만드는 것이 웹팩의 역할이다. 자바스크립트 모듈 뿐만 아니라 스타일시트, 이미지 파일까지도 모듈로 제공해 주기 때문에 일관적으로 개발할 수 있다.
웹팩의 로더와 플러그인의 원리에 대해 살펴보았고 자주 사용하는 것들의 기본적인 사용법에 대해 익혔다.
babel
바벨
React에서는 ES5/ES6 + JSX를 사용하게 된다. 따라서 이를 지원하지 않는 브라우저를 위해서는 반드시 `ES6 -> ES6`, `JSX -> JS` 로 변환해주어 **브라우저 호환성**을 높일 수 있어야한다.
바벨이란
바벨은 컴파일러의 종류 중 한 가지로, 컴파일러는 코드가 실행되기 이전 코드를 읽어, 다른 코드로 변환하는 작업을 의미한다.<br>
- 컴파일은 일반적으로 소스코드를 바이트코드로 변환하는 작업을 의미하므로, 바벨은 컴파일보다는 트랜스파일링(TransPiling)이 더 적합하다.
바벨은 `크로스 브라우징(Cross Browsing)`을 해결하기 위해 생겨났는데, 최신 브라우저에서만 동작하는 기능을 그렇지 않은 브라우저에서 구현해야하는 경우, 최신버전의 언어로 작성된 파일등이 정상적으로 동작할 수 있도록 한다.
바벨은 일관적인 방식으로 코딩하면서, 다양한 브라우져에서 돌아가는 어플리케이션을 만들기 위한 도구다.
바벨의 코어는 파싱과 출력만 담당하고 변환 작업은 플러그인이 처리한다.
여러 개의 플러그인들을 모아놓은 세트를 프리셋이라고 하는데 ECMAScript+ 환경은 env 프리셋을 사용한다.
바벨이 변환하지 못하는 코드는 폴리필이라 부르는 코드조각을 불러와 결과물에 로딩해서 해결한다.
babel-loader로 웹팩과 함께 사용하면 훨씬 단순하고 자동화된 프론트엔드 개발환경을 갖출 수 있다.