프론트엔드 면접 스터디 3주차 - React

Hyeon·2024년 5월 4일
0

면접스터디

목록 보기
4/6

리액트는 라이브러리인가요 프레임워크 인가요?🔥

→ 라이브러리입니다.

라이브러리는 개발에 필요한 것을 미리 구현해놓은 도구입니다. 프로젝트에서 자유롭게 설치해서 사용할 수 있습니다. 예시로는 axios, react-hook-form, react query 등이 있습니다.

반면 프레임워크는, 라이브러리를 포함한 코드의 집합으로 웹 개발을 보다 쉽고 간편하게 하는 도구입니다. 프레임워크는 여러 기능이 구조화가 되어있어 시간 단축에 유용합니다. 예시로는 React를 기반으로 한 NextJs가 있습니다.

이 둘의 차이점을 보여주는 예시로는 라우팅이 있습니다. 리액트의 경우, react를 설치해서 import하고 react-router-dom을 따로 설치해서 라우팅을 해야하지만, NextJs는 폴더를 기반으로 라우팅을 따릅니다.

따라서 리액트는 라이브러리입니다.

프레임워크 틀, 도구 Next.Auth. 백엔드는 db 제공

순수 라이브러리… React는 넥스트와는 다르게…

넥스트는 라우팅 기능이 있는데, 리액트는 라우팅하려면 라이브러리를 설치해야함… create react app과 같은 편리한 도구도 있으니까.

리액트를 사용하는 이유🔥🔥

리액트를 사용하는 이유는 다음과 같습니다.

첫째, 컴포넌트를 사용하기 때문에 상태 관리에도 용이하고 재사용이 가능합니다.

둘째, 데이터의 흐름이 단방향이기 때문에 (단방향 바인딩) 상태가 변화했다면 그 상태 변화를 명시적으로 일으킨 함수만 찾으면 되기 때문에 상태 관리에 용이합니다. (= Angular는 양방향 바인딩)

셋째, HTML에 자바스크립트 문법을 더한 JSX라는 문법을 사용하기 때문에 어렵지 않게 JSX 코드를 익히고 구현 가능합니다.

넷째, 메타라는 강력한 커뮤니티가 있기 때문에 지속적으로 발전한다.

다섯째, 가상 DOM을 사용하기 때문에 실제 DOM에 바로 적용해서 렌더링 하는 것보다 DOM 조작이 줄고 비용도 줄어듭니다

💡 하지만 프로젝트가 크지 않다면 굳이 REACT를 쓸 필요는 없다 (Ex TODO)

리액트를 사용하는이유? 많은 사람들이 사용하다보니 러닝커브. 커뮤니티라던지, 알려져있는 지식들이 많음. v-dom을 사용하다보니 컴포넌트 기반으로 개발할 수 있다. react는 v-dom을 사용하는데요, dom이 구성될때 css, js가 컴파일 되고 렌더트리가 만들어지고 나서 그려지는데… 렌더트리에 바뀌는 부분에만 변경이 되는 특징이 있다. 변경된 부분만 바뀌니까 효율적인 도구.

virtual DOM에 대해서 아나요?🔥🔥

Virtual DOM(V-DOM)을 설명드리기 전에, DOM에 대해서 설명드리겠습니다.

DOM은 The Document Object Model, 문서 객체 모델이라는 뜻으로, 웹페이지의 콘텐츠와 구조를 어떻게 보여줄 지에 대한 정보를 담고 있습니다.

브라우저는 HTML을 파싱하여 DOM 트리를 만들고, CSS 파싱하여 CSSOM을 만들어 렌더트리를 만듭니다.

document.createElement(..

위와 같은 기능이 DOM에 접근하는 기능이라고 볼 수 있습니다.

V-DOM은 React에서 사용하는 개념으로, 실제 브라우저 DOM이 아닌, 리액트가 관리하는 가상의 DOM을 의미합니다. V-DOM은 웹 페이지가 표시해야 할 DOM을 메모리에 저장하고, 리액트가 실제 변경에 대한 준비가 완료되었을 때 실제 브라우저의 돔에 반영합니다.

이를 통해 렌더링 과정을 최소화할 수 있고, 브라우저와 개발자의 부담을 덜 수 있다는 장점이 있습니다.

💡 하지만 REACT이 V-DOM을 사용하는 방식이, 무조건 빠르다는 건 아니다. 웬만한 어플리케이션을 만들 수 있을 정도로 충분히 빠르다는 것.

v-dom이란? 브라우저가 어떻게 화면을 보여주냐. 페이지는 html, css, js로 이루어짐. 렌더 트리에 변경 사항이 생기면 리플로우→ 리페인트가 발생함. 리액트랑 관련되어서 설명함. v-dom을 만들어서 실제돔과 바뀐 사항들을 v-dom과 비교하여 ..

current라는 파이버트리, workinprocess 변경된 파이버트리.

바뀐 상황이 있다면 루트 노드의 값을 바꿔끼우는 방식임.

[실제 dom에는 언제 바뀔까?]

current ← workinprocess (바뀐 사항 수정. 비교할 때 실제 dom을 비교)

+) DOM과 브라우저 렌더링 과정

  • 브라우저가 웹사이트 접근 요청을 받고 화면을 그리는 과정에서 일어나는 일
    • HTML 파싱 → 스타일 계산 → 레이아웃 → 페인팅
    1. 브라우저가 사용자가 요청한 주소를 방문해 HTML 파일 다운

    2. 브라우저의 렌더링 엔진은 HTML을 파싱, DOM 노드로 구성된 트리(DOM)를 만든다.

    3. 2번 과정에서 CSS 파일을 만나면 해당 CSS 파일도 다운로드 한다.

    4. 브라우저의 렌더링 엔진은 이 CSS도 파싱, CSS 노드로 구성된 트리(CSSOM)을 만든다.

    5. 브라우저는 2번에서 만든 DOM 노드를 순회한다. 여기서 모든 노드를 방문한 게 아니고, 사용자 눈에 보이는 노드만 방문한다 (ex display: none)은 방문하지 않는다.

    6. 눈에 보이는 노드를 대상으로 해당 노드에 대한 CSSOM 정보를 찾고, 여기서 발견한 CSS 스타일 정보를 이 노드에 적용한다. 이 DOM 노드에 CSS를 적용하는 과정 = 레이아웃, 페인팅
      1. 레이아웃: 각 노드가 브라우저 화면의 어느 좌표에 정확히 나타나야 하는지 계싼하는 과정, 레이아웃 과정을 거치면 반드시 페인팅 과정도 거친다.
      2. 페인팅: 레이아웃 단계를 거친 노드에 색과 같은 실제 유효한 모습을 그리는 과정

      따라서… 레이아웃 변경이 일어나면 반드시 리페인팅이 발생함

    • SPA의 경우, 하나의 HTML으로 여러 페이지를 그려야 하기 때문에, 계산 과정이 더 많아짐. DOM을 관리하는 과정에서 부담해야 할 비용이 더 커진다.

    • 사용자의 인터랙션에 따라 DOM의 모든 변경 사항을 추적하는 것은 수고스럽다. 어차피 결과적으로 나오는 DOM 결과물 하나만 궁금한 것임!

      → 그래서 가상 DOM이 탄생!

      → 가상 DOM은 실제 브라우저 DOM이 아닌 리액트가 관리하는 가상의 DOM을 의미한다.

      → 가상 DOM은 웹페이지가 표시해야 할 DOM을 메모리에 저장하고 리액트(react-dom)가 실제 변경에 대한 준비가 완료되었을때, 실제 브라우저의 돔에 반영한다. ⇒ 렌더링 과정 최소화, 브라우저와 개발자의 부담을 덜 수 있음.

+) 가상 DOM을 위한 아키텍처, 리액트 파이버

어떻게 가상 DOM을 만들까? 바로바로 리액트 파이버

  • 리액트 파이버란?
    • 리액트에서 관리하는 평범한 자바스크립트 객체다.
    • 파이버 재조정자(fiber reconciler)가 가상 DOM과 실제 DOM을 비교해 변경 사항을 수집하며 둘의 차이가 발생하면, 변경에 관련된 정보를 가지고 있는 파이버를 기준으로 화면에 렌더링을 요청한다.
    • 리액트 파이버가 하는 일
      • 작업을 작은 단위로 분할하고 쪼갠 다음, 우선순위를 매긴다.

      • 이러한 작업을 일시중지하고 나중에 다시 시작 가능

      • 이전에 했던 작업을 다시 재사용하거나 필요하지 않은 경우에는 폐기할 수 있다.

        ⇒ 이 과정이 모두 비동기로 일어남!

        예전에는 조정 알고리즘이 스택 알고리즘이어서 동기적으로 일어났고, 비효율적이었음.

        그래서 파이버라는 개념이 탄생한 것

React에서 함수 컴포넌트와 클래스 컴포넌트의 차이 🔥

원래 함수 컴포넌트는 잘 안쓰이다가 16버전으로 바뀌면서 클래스 컴포넌트보다 메모리 감소.

함수 컴포넌트와 클래스 컴포넌트의 차이는 생명 주기 메서드의 유무와 예측 불가능함을 들 수 있습니다.

첫째, 생명 주기 메서드(componentDidMount, componentDidUpdate, componentWillunmount 등) 의 부재입니다.

클래스 컴포넌트에는 생명 주기 메서드가 있지만, 함수 컴포넌트에는 존재하지 않습니다. 함수 컴포넌트는 단순히 props를 받아 리액트 요소를 반환하지만, 클래스 컴포넌트는 React.Component를 상속받아 구현합니다. 생명 주기 메서드는 React.Component에만 존재하기 때문에 함수 컴포넌트에서는 사용할 수 없습니다. 대신 함수 컴포넌트에서는 useEffect 훅을 사용하여 비슷하게 구현할 수 있습니다.

❓ 근데 왜 없앤걸까

둘째, 예측 불가능한 값이 있습니다.

함수 컴포넌트는 props의 값을 읽기 전용으로 가져오기 때문에 값이 변경될 일이 없습니다. 하지만 클래스 컴포넌트의 경우, this.props로 값을 가져오기 때문에 this의 값이 변경되면 변경된 값이 들어옵니다. 그렇기 때문에 최신 React 버전에서는 함수 컴포넌트를 권장합니다.

예를 들어

import React, { Component } from 'react';

class MyComponent extends Component {
  render() {
    return (
      <div>
        <p>Props value: {this.props.value}</p>
      </div>
    );
  }
}

export default MyComponent;

클래스 컴포넌트는 이렇게 작성하고

import React from 'react';

function MyFunctionalComponent(props) {
  return (
    <div>
      <p>Props value: {props.value}</p>
    </div>
  );
}

export default MyFunctionalComponent;

함수 컴포넌트는 이렇게 작성할 수 있는데,

클래스 컴포넌트의 this.props.value의 값이 외부에서 바뀌면 그 값이 반영되지만, 함수 컴포넌트의 props.value는 외부에서 바뀌어도 그 값이 반영되지 않습니다. (만약 바뀌었다면 state로 관리된 경우일..걸요?)

리액트에서 함수형 컴포넌트라고 부르지 않고 함수 컴포넌트라고 부르는 이유가 무엇인가요 🔥

원래 리액트 측에서는 함수형 컴포넌트라는 단어를 사용했는데, 이러한 네이밍이 함수형 프로그래밍과 비슷했기 때문에, 마치 함수형 컴포넌트를 사용하면 함수형 프로그래밍이 가능해지는 것처럼 들릴 수도 있다 생각하여 함수 컴포넌트로 네이밍을 바꿨습니다.

함수형 프로그래밍은 순수함수를 지향하는데, 리액트의 함수 컴포넌트는 훅을 사용하고 이 훅이 사이드 이펙트를 일으키기 때문에 이러한 점이 함수형 프로그래밍이라고 보기 어려운 점입니다.

❓ 함수형 프로그래밍 ? 함수형 컴포넌트 ? 함수 컴포넌트?

props와 state의 차이🔥

props와 state 둘 다 렌더링 결과물에 영향을 주는 정조입니다.

props는 컴포넌트의 속성을 설정할 때 사용하는 요소입니다. props는 해당 컴포넌트를 사용하는 부모 컴포넌트에서 설정해줄 수 있으며, 읽기 전용입니다.

state는 컴포넌트 안에서 관리되는 값입니다.

state는 내부에서 바뀔 수 있지만, props는 읽기 전용이므로 내부에서 바뀔 수 없고 부모에서 변경해야 합니다.

❓ props는 복사되는가?

💡 렌더링이 트리거되는 순간은 리액트 공식 문서에서 설명한 대로 다음과 같습니다:

  1. 컴포넌트의 초기 렌더링
  2. 컴포넌트(또는 상위 컴포넌트 중 하나)의 상태가 변경되었을 때

Props가 컴포넌트간에 전달받는 것이라고 했는데 자식에서 부모로도 전달할 수 있는가 🔥

Lifting-state up(끌어올리기)로 가능하다.

props는 부모 컴포넌트에서 자식 컴포넌트에게 설정하는 값으로, 보통 부모에서 자식으로 데이터를 준다. 그러므로 자식이 부모에게 직접적으로 props를 전달할 수는 없다. 해당 값을 변경하는 함수를 부모에서 자식에게 넘겨주는 방식으로는 가능하다. ex) setState 함수를 넘겨주기

FLUX에 대해서 아나요? 🔥🔥

Flux는 페이스북에서 개발한 아키텍쳐로, 주로 웹 애플리케이션에서 상태 관리와 데이터 흐름을 관리하기 위해서 사용됩니다. Flux 패턴을 사용하는 라이브러리의 예시로, redux와 zustand 등이 있습니다.

Flux는 데이터의 단방향 흐름을 강조하며 상태 변경은 항상 예측 가능하도록 만들어집니다. 이를 통해 애플리케이션의 상태 관리를 단순화할 수 있습니다.

Flux의 구성요소로는 Action, dispatcher, store, view가 있다.

action에는 어떤 데이터로 어떤 작업을 할지 정의한다. 그리고 이를 dispatch로 보낸다.

dispatcher는 action을 스토어에 보낸다.

store: 실제 상태와 상태를 변경할 수 있는 메서드를 가진다.

view: store에서 만들어진 데이터를 가져와 화면에서 렌더링한다. 그리고 뷰에서 액션을 호출하여 상태를 업데이트 한다.

→ 설계 패턴. MVC = 컨트롤러, 모델, 뷰. 모델을 설명할때 컨트롤을 통해서. 뷰↔모델. 모델이랑 뷰가 엄청 많아지면 다대다… 하나의 뷰가 있다면 다른 모델이 바뀌고… 연쇄적으로 바뀐다. 추적하기가 어렵다!

단방향인 flux 패턴이 등장. FLUX = dispatch, store, action, view

action→dispatch→store

불변성을 유지한 채로 변경이 되어야 한다.

객체나 배열을 두었을때… 배열 자체를 직접 바꾸면 변경을 추적할 수 없음. setState 같은걸로 추적할 수 있다.

❓ Context Api

+) MVC

Flux 전에 있던 디자인 패턴.

Model, View, Controller로 구성된다.

사용자의 어떤 액션이 있을때, 이를 바탕으로 설계된 컨트롤러를 통해 모델을 변화하고 이를 뷰에 반영한다. → 모델과 뷰가 복잡하게 얽혀있다면 어떤 모델이 변경되어 뷰가 변경되었는지 확인하기 어려운 경우가 생겼음.

즉, 양방향 데이터 흐름이 상태를 따라가기 힘들게 만든다고 하여, Flux라는 단방향 데이터 흐름을 제시한 것.

리덕스에 대해서 아나요? 🔥🔥

리덕스란 Flux 패턴을 바탕으로 나온 전역 상태 기반 라이브러리입니다.

리덕스는 store에 읽기 전용으로 값을 저장한다. 이 상태를 변경하려면 action을 dispatch해야 한다. action은 애플리케이션에서 발생하는 어떤 이벤트를 나타낸다. reducer는 액션을 통해 상태를 어떻게 변경할지를 정의하는 함수다. reducer가 이전 상태와 액션을 받아서 새로운 상태를 반환한다. dispatch는 액션을 스토어에 전달하는 메소드다. 액션을 디스패치하면 스토어는 해당 액션을 리듀서에 전달하여 상태를 전달한다.

Untitled

Untitled

  • 예시 코드 Actions: actions.js 파일에 액션을 정의합니다.
    javascriptCopy code
    // actions.js
    export const increment = () => ({
      type: 'INCREMENT'
    });
    
    export const decrement = () => ({
      type: 'DECREMENT'
    });
    
    Reducers: reducers.js 파일에 리듀서를 작성합니다.
    javascriptCopy code
    // reducers.js
    const initialState = {
      count: 0
    };
    
    const counterReducer = (state = initialState, action) => {
      switch(action.type) {
        case 'INCREMENT':
          return { count: state.count + 1 };
        case 'DECREMENT':
          return { count: state.count - 1 };
        default:
          return state;
      }
    };
    
    export default counterReducer;
    
    Store: store.js 파일에 스토어를 생성합니다.
    javascriptCopy code
    // store.js
    import { createStore } from 'redux';
    import counterReducer from './reducers';
    
    const store = createStore(counterReducer);
    
    export default store;
    
    컴포넌트: 이제 React 컴포넌트를 작성합니다. App.js 파일에 작성합니다.
    javascriptCopy code
    // App.js
    import React from 'react';
    import { connect } from 'react-redux';
    import { increment, decrement } from './actions';
    
    function App(props) {
      return (
        <div>
          <h1>Counter: {props.count}</h1>
          <button onClick={props.increment}>Increment</button>
          <button onClick={props.decrement}>Decrement</button>
        </div>
      );
    }
    
    const mapStateToProps = (state) => ({
      count: state.count
    });
    
    const mapDispatchToProps = {
      increment,
      decrement
    };
    
    export default connect(mapStateToProps, mapDispatchToProps)(App);
    
    엔트리 포인트: 마지막으로 애플리케이션의 엔트리 포인트인 index.js 파일을 작성합니다.
    javascriptCopy code
    // index.js
    import React from 'react';
    import ReactDOM from 'react-dom';
    import { Provider } from 'react-redux';
    import store from './store';
    import App from './App';
    
    ReactDOM.render(
      <Provider store={store}>
        <App />
      </Provider>,
      document.getElementById('root')
    );
    

리덕스의 장점으로는 전역으로 상태를 관리할 수 있기 때문에, props drilling 문제를 해결 가능하다. 하지만 단점으로는 단순하게 하나의 상태를 바꾸고 싶어도 해야할 일이 너무 많다는 점이다. 액션에 대한 타입 선언, 액션을 수행할 함수, dispatcher 등… 하고자 하는 일에 비해 해야할 일이 너무 많다.

리덕스의 기본 원칙은? 🔥🔥

3가지 원칙 | Redux

  1. 진실은 하나의 근원으로부터

애플리케이션의 모든 상태는 하나의 저장소 안에 하나의 객체 트리 구조로 저장됩니다

  1. 상태는 읽기 전용이다

상태를 변화시키는 유일한 방법은 무슨 일이 벌어지는 지를 묘사하는 액션 객체를 전달하는 방법뿐입니다.

  1. 변화는 순수 함수로 작성되어야한다

액션에 의해 상태 트리가 어떻게 변화하는 지를 지정하기 위해 프로그래머는 순수 리듀서를 작성해야합니다.

React에서 state의 불변성을 유지하라는 말이 있는데 이에 대해 설명해달라 🔥

useState의 state는 setState로 state 값을 할당한다. 왜 setState로만 값을 할당할 수 있을까? 그 이유에 불변성을 유지해야 하는 이유가 나온다.

불변성(Immutability)은 프로그래밍에서 데이터를 변경할 때 해당 데이터를 직접 수정하는 것이 아니라 새로운 데이터를 생성하여 원래 데이터를 변경하지 않는 것을 의미한다.

JavaScript는 얕은 비교를 한다. 얕은 비교란, 객체나 배열 같은 참조 타입의 데이터는 참조 값만 비교하는 것이다. 반면 원시 자료형은 값을 비교한다.

+) 구체적으로 설명하자면, JavaScript는 state의 변화감지를 call stack의 주소값으로 한다. 원시타입의 경우 값이 변경되면 아예 새로운 주소에 값을 할당하기 때문에 주소값이 바뀌어서 값이 변경되는 걸 감지할 수 있지만, 참조 타입은 주소값 안의 내용을 바꾸기 때문에 값의 변경을 감지할 수 없다.

따라서 참조 타입의 변경된 값을 React가 감지할 수 있도록 불변성을 유지해야 한다. 불변성을 지킴으로서 사이드 이펙트와 복잡한 코드를 방지할 수 있다.

리듀서 내부에서 불변성을 지키는 이유는? 전개 연산자의 단점을 해결할 수 있는 방법은 무엇인가 🔥

Redux와 React-Redux는 모두 얕은 검사를 하기 때문이다.

redux는 store에 있는 값을 action을 통해서 수정하도록 해놨는데, 그 이유가 불변성을 지키기 위해서다. state를 직접 건들면 어디서 state를 변경했는지 감지하기 어렵기 때문에 action을 통해서 변경해야 한다.

전개 연산자의 단점을 해결할 수 있는 방법은 무엇인가 🔥

단점으로는 spread 연산자의 경우 객체의 깊이가 깊어질수록 로직 구성이 어려워진다는 점이 있다. immer 라이브러리를 이용하면 스프레드 연산자 없이 불변성을 유지해주고 사이드 이펙트를 막아준다.

리액트 사용시에 부수효과로 인해 생기는 문제점이 있다면 🔥🔥

부수 효과란 함수가 만들어진 목적과는 다른 효과가 일어나는 것을 말한다.

  • 부수 효과를 일으키는 함수 (불순 함수)
  • 부수 효과를 일으키지 않는 함수 (순수 함수)
  • 요약

직접 조작을 피하는 방식으로 부수 효과를 방지한다. (ex 배열을 직접 조작하지 않는다!)

왜 부수효과를 피해야 하나면, state, props가 변경될 때 리렌더링이 되기 때문이다. 의도치 않게 부수효과를 가진 함수들로 인해 불필요한 리렌더링이 잦아질 수 있다.

컴포넌트의 라이프 사이클 메서드 🔥🔥

  • 이해
    • 클래스 컴포넌트의 라이프 사이클 메서드는 대표적으로 마운팅, 업데이트, 언마운팅, (에러 처리)로 구분된다.
    • 마운팅은 컴포넌트의 인스턴스가 생성되어 DOM에 삽입되는 것
    • 업데이트 컴포넌트의 props나 state가 변경되어 컴포넌트가 리렌더링 되는 것
    • 언마운팅은 컴포넌트가 DOM에서 제거 되는 것
  • 메서드 종류 🔥
    • render
      • 리액트 클래스 컴포넌트의 유일한 필수 값으로 항상 쓰인다.
      • 이 함수는 컴포넌트가 UI를 렌더링하기 위해서 쓰인다.
      • 렌더링은 앞서 언급한 시점 중 두 가지인 마운트와 업데이트 과정에서 일어난다.`
    • componentDidMount
      • 클래스 컴포넌트가 마운트되고 준비가 됐다면 호출되는 생명 주기 메서드
      • API 호출 후 업데이트, DOM에 의존적인 작업 (이벤트 리스너 추가 등) 할 때 사용
    • componentDidUpdate
      • 컴포넌트 업데이트가 일어난 이후 바로 실행
      • state나 props의 변화에 따라 DOM을 업데이트하는 등에 쓰인다.
      • 조건문 써주기
    • componentWillUnmount
      • 컴포넌트가 언마운트되거나 더이상 사용하지 않을 때 호출
      • 메모리 누수나 불필요한 작동을 위한 클린업 함수 호출
    • shouldComponentUpdate
      • state나 props의 변경으로 리액트 컴포넌트가 다시 리렌더링을 하는 걸 막고 싶다면 사용…
    • static getDerivedStateFromprops
    • getSnapShotBeforeUpdate
    • getDerivedStateFromError
    • componentDidCatch

Untitled

❓ Render, Pre-commit, commit 단계란?

Hooks의 종류 🔥🔥

아 너무 많다

useState

  • 함수 컴포넌트 내부에서 상태를 정의하고, 이 상태를 관리할 수 있게 해주는 훅
  • 클로저 사용하여 useState와 관련된 정보를 저장해놓았다가 필요할 때마다 꺼내놓는다. 이를 통해 외부에 해당 값을 노출시키지 않고, 컴포넌트가 매번 실행되더라도 이전 값을 꺼내서 쓸 수 있다.
  • 게으른 초기화
    • useState의 기본값에 변수 대신 함수를 넘기는 것
    • 초기값이 복잡하거나 무거운 연산을 포함할 때 사용. state가 처음 만들어질때만 사용된다.

💡 리액트에서는 렌더링이 실행될 때마다 함수 컴포넌트의 함수가 다시 실행된다. 물론 useState의 값도 재실행된다. 내부에 클로저를 통해 값을 가져오기 때문에 값이 유지될 수 있는 것

useEffect

  • 애플리케이션의 컴포넌트의 여러 값을 활용해 동기적으로 부수 효과를 만드는 매커니즘
  • 첫번째 인수: 실행할 부수효과가 포함된 함수, 두번째 인수: 의존성 배열
  • 렌더링할 때마다 의존성에 있는 값을 보면서 이 의존성의 값이 이전과 다른 게 하나라도 있으면 부수 효과를 실행하는 평범한 함수
  • 클린업 함수: 새로운 값을 기반으로 렌더링 뒤에 실행되지만 이 변경된 값을 읽는 것이 아니라 함수가 정의됐을 당시에 선언했던 이전 값을 보고 실행된다는 것이다. (리렌더링 됐을때 의존성 변화가 있으면? 이전 상태를 청소해준다) → useEffect는 그 콜백이 실행될 때마다 이전의 클린업 함수가 존재한다면 그 클린업 함수를 실행한 뒤에 콜백을 실행한다.
  • 주의할 점
    • useEffect의 콜백 함수내에 없는 변수를 의존성 배열에 넣지 말기
    • 빈 배열은 자제하기
      • 차라리 부모 컴포넌트에서 실행해
    • useEffect의 첫번째 인수에 함수명을 부여하라 (익명함수x)
      • 목적 파악하기 쉬워짐
    • 거대한 useEffect 만들지 말기
      • 적은 의존성 배열을 사용하는 여러개의 useEffect로 분리하는 게 좋다?
      • 너무 거대하면 뭐때문에 발생하는지 찾아가기 어려워짐
      • 불가피하게 의존성 배열에 여러 변수 들어가야한다면? useCallback, useMemo등으로 정제한 내용만 넣기
    • 불필요한 외부 함수를 만들지 않기

useReducer

  • useState의 심화버전
  • 좀 더 복잡한 상태값을 미리 정의해놓은 시나리오에 따라 관리할 수 있다.
  • const [state, dispatcher] = useReducer(reducer, initialState, init)
    • state: 현재 useReducer가 가지고 있는 값을 의미.
    • dispatcher: state를 업데이트 하는 함수. action을 넘겨준다.
    • reducer: useReducer의 기본 action을 정의하는 함수
    • initialState: 초깃값
    • init?: useState의 인수로 함수를 넘겨줄때처럼 초기값을 지연해서 생성하고 싶을때(게으른 초기화) 사용. initialState를 인수로 init 함수가 실행된다.

→ state 의 변경을 dispatcher로만 제한하기

useMemo (값)

  • 비용이 큰 연산에 대한 결과를 메모이제이션 해두고, 이 저장된 값을 반환하는 훅
  • 렌더링 발생 시 의존성 배열의 값이 변경되지 않았다면? 함수를 재실행하고, 아니면 그냥 이전에 기억해 둔 값을 반환한다.
  • 메모이제이션은 값뿐만 아니라 컴포넌트도 가능(React.memo를 쓰는게 낫긴함)
  • 어떤 값을 계산할 때 해당 값을 연산하는데 비용이 많이 든다면 고려

useCallback (함수)

  • useCallback은 인수로 넘겨받은 콜백 자체를 기억한다.
  • 특정 함수를 새로 만들지 않고 다시 재사용한다는 의미 → state 값이 바뀌면 컴포넌트가 리렌더링되는데, 그러면 onChange로 넘기는 함수가 재생성됨.
  • 의존성 배열이 변경되지 않는 한 함수를 재생성하지 않음.

useRef

  • 컴포넌트 내부에서 렌더링이 일어나도 변경 가능한 상태값을 저장한다.
  • vs useState
    • 반환 값인 객체 내부에 있는 current 로 값에 접근 or 변경할 수 있다.
    • useRef는 그 값이 변하더라도 렌더링을 발생시키지 않는다.
    • 컴포넌트가 렌더링 될때마단 생성되고, 컴포넌트 인스턴스가 여러개라도 별개의 값을 바라본다. ← ?? 무슨말??

사용자 정의 훅

  • use로 시작한다는 규칙
  • ex) fetch를 수행하는 useFetch
  • 훅은 함수 컴포넌트 내부 또는 사용자 정의 훅 내부에서만 사용 가능
  • 사용자 정의 훅 참고할 만한 것: usehooks, react-use, ahooks

useMemo와 useCallback의 차이를 아나요 🔥🔥

useMemo는 계산한 값을 메모이제이션하고 useCallback은 함수를 메모이제이션한다.

왜 사용해야 하나요? 컴포넌트는 state나 props의 변경 등 다양한 조건에 의해 쉽게 리렌더링되는데, 굳이 다시 계산하지 않아도 되는 내용들이 리렌더링되면서 다시 계산되는 경우가 있다. 이럴 때 쓰는 게 메모이제이션이다. 의존성 배열에 담긴 변수가 변경될 때만 리렌더링 되고, 그 외의 경우에는 값이 변하지 않았다고 생각하여 메모이제이션 했던 이전에 계산한 값을 다시 사용한다.

💡 메모이제이션에도 비용이 든다. 값을 비교하고 렌더링 또는 재계산이 필요한지 확인하는 작업, 그리고 이전에 결과물을 저장해두었다가 다시 꺼내와야 한다는 비용.

리액트에서 setState는 비동기 동작인가요 동기 동작인가요? 🔥

호출은 비동기로 하지만 돌아가는 건 동기 동작입니다.

react는 state의 변경에 따라 리렌더링이 일어나는데, 한 컴포넌트안에서 state를 여러번 바꿔주는 동작이 생기면 여러번 비교하고 다시 그리게 될 것입니다.

그러므로 같은 state를 업데이트하는 setState가 여러번 호출될 때, 리액트는 setState를 콜백 큐에 넣고 추후에 한꺼번에 동기적으로 setState를 처리하도록 했습니다.

+) 그렇지만 setState 자체는 비동기 함수는 아닙니다. setState의 state값이 반영이 안된 이유는 렌더링 함수가 호출되었을 당시의 closure의 값을 가지고 setState를 실행하기 때문에 state값이 바로 업데이트가 안되는 것처럼 보이는 것입니다.

콘솔로그가 이상한건 setState가 비동기 함수여서가 아닙니다. (feat: fiber architecture)

setState가 비동기 동작을 취했을 때 얻을 수 있는 이점은 무엇인가요? 🔥

prepare_frontend_interview/react.md at main · junh0328/prepare_frontend_interview

setState가 동기적으로 호출되고 실행된다면, 리액트의 컴포넌트가 연속적으로 리렌더링 할 것이기 때문에 비효율적이다 ❓

useLayoutEffect는 무엇인가요? 🔥

useLayoutEffect란, 모든 DOM 변경 후에 동기적으로 실행되는 훅입니다.

즉, 리액트가 DOM을 계산하고 브라우저에 변경사항(페인팅)을 반영한 뒤 useEffect가 실행되는데, useLayoutEffect는 리액트가 DOM을 렌더링한 직후에 실행됩니다. 따라서, 리액트는 useLayoutEffect의 실행이 종료될 때까지 기다린 다음에 화면을 그리기 때문에 웹 애플리케이션 성능에 문제가 발생할 수 있습니다.

그렇기 때문에 스크롤 위치를 제어하는 것처럼, DOM은 계산되었으나 이것이 화면에 반영되기 전에 하고 싶은 작업 정도에 사용할 수 있습니다.

useLayoutEffect – React

리액트의 성능개선 방법에 대해서 설명해주세요

  1. useCallback, useMemo, React.memo 같은 메모이제이션을 사용한다.
  2. 코드 스플리팅을 통해 초기 로딩 시간을 줄이고 필요한 코드만 로드하여 성능을 향상 시킨다. (lazy 사용)
  3. 동적 임포트를 통해 필요할 때만 임포트 하여 로딩 시간을 줄인다.
  4. 크기 최적화된 이미지를 사용하여 이미지 로딩 시간을 줄인다.
  5. 리스트 있을 때 키 값을 넣어서…

컴포넌트에서 이벤트를 실행시키기 위해서는 어떻게 핸들링해야 하나요?🔥

이벤트로 설정할 함수를 호출하는 것이 아닌 직접 넣어 줄 때는 화살표 함수 문법을 사용하여 넣어야 한다.

이벤트로 설정할 함수를 호출하는 것이 아닌 직접 넣어 줄 때는 화살표 함수 문법을 사용하여 넣어 주어야 합니다

case 1: 작동하지 않는 문법
<button onClick={this.setState({ number: number + 1})}>  (x)

case 2: 작동하는 문법
<button onClick={()=> this.setState({ number: number + 1}; )}>

❓ 이유가 뭘까요

SPA가 뭔가요?🔥

SPA란

Single Page Application로, 하나의 페이지에서 여러 페이지가 동작하는 것처럼 보이는 애플리케이션입니다. 렌더링을 부분적으로 바꾸는 방식입니다.

SPA의 장점으로는 화면 전환이 자연스럽다는 점이 있습니다. 기존의 MPA의 경우 페이지를 변경할 때마다 새롭게 페이지를 불러오기 때문에 화면이 깜빡 거리는 문제점이 있었는데, SPA의 경우 새로운 페이지에 필요한 부분만 렌더링하기 때문에 화면 전환이 자연스럽게 된다는 점이 있습니다.

또한 서버에서는 페이지에 필요한 HTML, CSS, JS를 보내주고 렌더링은 브라우저에서 진행하기 때문에 서버의 부담은 덜하다는 장점이 있습니다.

SPA의 단점

SPA는 대부분의 화면이 Client Side로 그려지기 때문에 초기에 불러오는 HTML에 루트 div만 있습니다. 검색 엔진은 Js를 실행하기 때문에 빈 HTML을 기준으로 사이트의 내용을 분석하기 때문에 SEO에 불리하다는 단점이 있습니다.

또한 client에서 화면을 렌더링하기 때문에 렌더링 속도가 느립니다. 특히 실시간으로 그려지기 때문에 없었던 화면이 튀어나와 레이아웃이 갑자기 변하는 레이아웃 쉬프트 같은 현상이 일어나기도 합니다.

+) 전통적인 페이지는 서버에서 페이지 던져줬지만, SPA는 서버에서 HTML, CSS, JS 던져주고 클라이언트에서 렌더링한다.

SSR이 뭔가요?🔥

Sever side Rendering으로, 서버에서 HTML을 그려준다.

❓️ 그럼 서버에서 HTML의 어디까지 그려주는걸까? 스타일까지 입혀주는 걸까…?

SEO가 뭔가요?🔥

  • TTV란 Time to View로 사용자가 화면을 보는 시점을 뜻한다.
  • TTI란 Time to Interact로 사용자가 웹에서 특정 동작을 수행할 수 있는 시점을 뜻한다.
  • CSR의 경우 TTV와 TTI 사이의 간극이 없다. 뷰와 기능이 동시에 생성되기 때문에
  • SSR의 경우 TTV와 TTI 사이의 간극이 있다. 뷰가 먼저 로드되고 그 이후에 JS가 로드되어 기능이 붙여지기 때문이다. 사용자가 화면을 보고 동작을 기대했으나 동작하지 않을수도 있다.

하이드레이션에 대해 알고 있나요 🔥

  • 하이드레이션이란, 수분공급이라는 의미다.
  • 리액트에서 하이드레이션이란, 리액트가 서버 환경에서 미리 렌더링한 HTML에 연결하는 방식이다.
  • 다시 말해 동적인 이벤트가 아직 연결되지 않은 DOM가 그려진 HTML에 JS코드를 서로 일치시켜 동적인 브라우저를 렌더링하는 방식
  • 오류가 나는 이유? 서버가 만들어진 HTML이랑 실제로 JS가 붙여준 HTML이 다르면 하이드레이션 오류가 남!

https://ko.react.dev/reference/react-dom/hydrate

Next의 렌더링 수행 방식

  1. 웹 사이트를 방문하면
  2. 브라우저가 서버에 콘텐츠 요청한다
  3. 서버가 렌더링 준비를 마친 html, js code 를 브라우저에 전달
  4. 브라우저에서 HTML 렌더와 JS 로직 연

Next를 쓴 이유가 있나요 ? 🔥

  • 라우팅이 편해서
  • SEO를 위해서
  • NEXTJS는 자동으로 코드를 분리하기 때문에 (코드 스플릿트) 어느정도 알아서 성능 최적화가 된다

Next를 구성하는 기본 설정 파일에 대해서 알고 있나요? 🔥

사전 렌더링을 위해 사용해 본 함수가 있나요 🔥

  • 12버전에서는 getServerSideProps, getStaticProps…
  • 13버전에는 fetch에 옵션을 넣어서 호출

Suspense 🔥

  • suspense가 뭔가요?
    • Suspense란,
    • 데이터가 아직 준비되지 않았음을 알려주는 매커니즘
  • suspense로 가능한 것은 어떤 것들이 있나요?

웹 성능 최적화

  • 이미지 크기를 줄이기
  • JS 번들 사이즈 줄이기

LCP가 뭔가요?

  • 최대 콘텐츠 풀 페인트
  • 뷰포트 내부에서 가장 큰 요소가 얼마나 로딩됐는지를 체크
  • 큰 이미지와 텍스트
  • lazy loading / img / 이미지 무손실 압축

FCP가 뭔가요?

  • 최초 콘텐츠 풀 페인트
  • first content = 뭐라도 뜨면
  • 렌더링을 가로 막는 리소스 최소화
  • 스크롤을 하지 않아도 보이는 영역을 게으른 로딩 하지 않기
  • dom 크기 최소화

❓ 리액트의 V-DOM이 두 개인데 둘은 무슨 용도지?

❓ setState의 비동기 동작…

profile
어 왜 되지? 에 대한 고찰

0개의 댓글