테오의 프론트엔드 방 안에서 어떤분이 React에 관한 면접 질문을 한다면 어떤 질문을 하고 싶으신가요..? 라고 물어봤더니 친절하게 답해주셨다..
그 답들에 대해서 어떤 것은 생각나고 어떤 것은 생각이 나지 않으니
근본적인 부분이 좀 더 부족하고, 공부가 많이 필요하다고 느꼈다.
아무래도 아직 이직 준비를 하고 있으니 좀 더 공부해야겠다!
Automatic Batching
배칭이란? 배칭은 우리말로 "일괄 처리"정도로 해석할 수 있을 것 같습니다. 개별적으로 어떤 요청이 있을 때마다 실시간으로 통신하는 것이 아닌 한꺼번에 일괄적으로 처리하는 것입니다. React적으로 의미를 좁히면, 여러 개의 state 업데이트를 하나의 리렌더링으로 묶는 것을 의미합니다.
자동 배칭 기능을 사용하기 위해 무언가를 할 필요가 없습니다. React가 알아서 하기 때문입니다. React 18의 createRoot를 통해, 모든 업데이트들은 어디서 왔는가와 무관하게 자동으로 배칭됩니다. ( React 18 이전에는 React 이벤트 핸들러 내부에서 발생하는 업데이트만 배칭을 했습니다.)
transitions
성공한 경우에만 집중할 수 있는
로딩 상태와 에러 상태가 분리된
동기처럼 사용할 수 있는
컴포넌트를 '쓰는 쪽'에서 로딩 처리와 에러 처리를 합니다. 컴포넌트를 사용할 때 그 컴포넌트를 위 코드처럼 Suspense로 감싸주면, 컴포넌트의 렌더링을 특정 작업 이후로 미루고, 그 작업이 끝날 때까지는 fallback 속성으로 넘긴 컴포넌트를 대신 보여줄 수 있습니다.
Virtual DOM?
Virtual DOM이란 말 그대로 가상의 DOM 이란 뜻이고, DOM을 추상화한javascript 객체입니다.
사용하는 이유?
동적인 웹 애플리케이션을 만들기 위해서
-> 기존의 정적인 DOM 으로 만들게 되면 브라우저가 많은 연산을 하게되고 전체적인 프로세스가 떨어진다.
어떤 역할?
React의 state, props 등이 변경될 때 마다 이전 Virtual DOM과 새로운 Virtual DOM을 비교합니다. 변경이 발생할 때 전체 페이지를 렌더링 하는 것이 아니라, 변경한 부분만 렌더링하는 역할을 수행합니다.
import React, { useState } from "react";
function App() {
const [state, setState] = useState(1);
const onClick = () => {
setState(state + 1);
console.log(state);
};
return (
<div className="App">
<button onClick={onClick}>+1</button>
<p>현재 값 {state}</p>
</div>
);
}
export default App;
현재 setState는 이벤트 핸들러 내에서 비동기적입니다. state값은 반영이 되지만, console.log()에 찍힌 값은 이전 상태 값을 출력합니다. React가 브라우저 이벤트가 끝날 시점에 state를 일괄적으로 업데이트 하기 때문에
import React, { useState } from "react";
function App() {
const [state, setState] = useState(1);
const onClick = () => {
setState(state + 1);
setState(state + 1);
setState(state + 1);
};
return (
<div className="App">
<button onClick={onClick}>+1</button>
<p>현재 값 {state}</p>
</div>
);
}
export default App;
setState를 3 번 호출해서 매번 + 3 되길 원하지만 오직 + 1만 증가하는 것을 볼 수 있습니다. 그 이유는 React가 batch update를 하기 때문입니다.
setState 함수가 호출되면 리액트는 전달받은 state를 바로 바꾸는 것이 아니라 이전의 리액트 엘리먼트 트리와 전달받은 state 엘리먼트 트리를 비교하는 작업을 거치고, 최종적으로 변경된 부분만 DOM에 적용합니다. batch update는 16ms 단위로 진행하며 그 동안 변경된 상태 값들을 모아 단 한 번 리렌더링을 진행합니다.
setState() 함수는 인자로 새로운 state 객체를 인자로 받을 수 있지만, 이전 state 기준으로 값을 계산해야 한다면 updater 함수를 전달하면 됩니다! setState 호출은 일괄적으로 처리되기 때문에 여러 업데이트 사항이 충돌 없이 차례대로 반영되도록 합니다.
import React, { useState } from "react";
function App() {
const [state, setState] = useState(1);
const onClick = () => {
setState((prevState) => prevState + 1);
setState((prevState) => prevState + 1);
setState((prevState) => prevState + 1);
};
return (
<div className="App">
<button onClick={onClick}>+1</button>
<p>현재 값 {state}</p>
</div>
);
}
export default App;
이것처럼요 !
결론적으로는 State가 비동기적으로 처리되게 만든 이유는
결론적으로는 동일한 입력이 있거나 복잡한 계산이 반복될 때 사용되며 무작정 최적화를 위한다며 사용하면 메모리 낭비와 복잡한 코드 때문에 오히려 손해를 볼 수 있을 것이다!
next.js 13버전에서 부터는 app 라우팅으로 세팅되어 있고, app 라우팅에서는 기본적으로 모든 컴포넌트가 서버 컴포넌트입니다. 그렇다면 서버컴포넌트란 무엇이냐? 그 말대로 서버에서 렌더되고 요청되는 컴포넌트입니다.
데이터베이스나 , 파일 시스템에 기반한 작업들을 위해서 만들어 졌습니다.
서버 컴포넌트들은 클라이언트 사이드 번들을 포함하지 않기 때문에 적은양의 자바스크립트 코드를 다운로드 하도록해서 빠른 렌더링을 꾀할 수 있습니다. "use client"를 사용한 컴포넌트만 웹팩 번들에 포함됩니다.
저의 경우는 useInput 이라는 훅을 만들어 보았습니다.
왜 만들었냐 ? -> 컴포넌트간에 input들이 사용되는 경우가 많고, 같은 함수들이 중복되기 때문에
import { useState } from "react";
import { supabase } from "../lib/supabase";
import { ManagerProps, CategoryProps } from "../types/InputValueProps";
import { redirect } from "next/navigation";
import { useRouter } from "next/router";
import { type } from "os";
export async function signInWithEmail(managerIntfo: ManagerProps) {
try {
const { data, error } = await supabase.auth.signInWithPassword({
email: managerIntfo.email,
password: managerIntfo.password,
});
console.log("redirect 해볼게 얍");
} catch (err) {
alert(err);
}
}
export const useInput = (state: ManagerProps | CategoryProps) => {
const [values, setValues] = useState(state);
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setValues({ ...values, [name]: value });
// 조건부를 넣어볼 것.
// if(typeof state === "string"){
// setValues(e.target.value);
// }else if("email" in state && "password" in state){
// setValues((prevValues) => ({
// ...prevValues,
// [name]: value,
// }));
// }
console.log(values);
};
const onSubmit = (
e: React.FormEvent<HTMLFormElement>,
url: string,
state: ManagerProps
) => {
e.preventDefault();
if (url.includes("login")) {
signInWithEmail(state);
}
};
return { values, onChange, onSubmit };
};
로그인할 때와 에디터에 있는 내용을 등록할 때를 분기처리 하기 위하여 고민중이고, state와 onChange , onSubmit을 export해주어 사용할 수 있게 하였습니다.
초기 렌더링: 컴포넌트가 처음으로 마운트될 때 초기 렌더링이 발생합니다. 이때 컴포넌트의 render() 메서드가 호출되어 가상 DOM을 생성하고, 이를 실제 DOM에 반영합니다.
상태(State) 변경: 컴포넌트의 상태가 setState() 메서드를 통해 업데이트되면, React는 새로운 상태로 다시 렌더링합니다. 이때 render() 메서드가 다시 호출되며, 변경된 부분만 가상 DOM에 반영합니다.
속성(Props) 변경: 컴포넌트의 속성이 부모 컴포넌트로부터 변경되었을 때도 렌더링이 발생합니다. 이때 render() 메서드가 호출되어 새로운 속성값으로 가상 DOM을 업데이트하고, 변경된 부분만 실제 DOM에 반영합니다.
강제 렌더링: forceUpdate() 메서드를 호출하여 컴포넌트를 강제로 렌더링할 수 있습니다. 일반적으로 상태나 속성 변경에 의해 자동으로 렌더링이 처리되지만, 특별한 경우에 강제로 렌더링해야 할 때 사용됩니다.
라우팅 변화: React Router 또는 다른 라우팅 라이브러리를 사용할 때, 라우팅 경로가 변경되면 해당하는 컴포넌트가 다시 렌더링됩니다.
부모 컴포넌트의 렌더링: 부모 컴포넌트가 렌더링되면 자식 컴포넌트도 함께 렌더링됩니다. 이때 자식 컴포넌트의 render() 메서드가 호출되어 새로운 가상 DOM을 생성하고, 변경된 부분만 실제 DOM에 반영합니다.
[출처]
리액트 공식문서 한글번역
https://react-ko.dev/
React 18버전 내용
https://velog.io/@perfumellim/쉽게-떠먹여주는-React-18-업데이트
state 동기, 비동기
https://taenami.tistory.com/49
props와 state의 차이점
https://ljh86029926.gitbook.io/coding-apple-react/1/props-and-state
useMemo, useCallback
https://hayeondev.gatsbyjs.io/230202-usememo-and-usecallback/
서버컴포넌트
https://velog.io/@brgndy/React-Server-vs-Client-Component-in-Next.js-13-%ED%95%B4%EC%84%9D