최근 면접 준비를 하면서 공부한 React 중요 개념에 대해 정리해봤습니다.
프론트엔드 면접 질문 목록을 찾아보면 대부분 중요한 질문들이 비슷한데 해당 질문 목록과 실제 면접 경험 + 본인의 프로젝트 경험을 넣어 내용 정리했습니다.
참고로 실제 면접에서 받은 질문들 앞에는 ⭐️ 별을 붙였습니다.
React는 사용자 인터페이스를 구축하기 위한 자바스크립트 라이브러리로 Facebook에서 개발하고 관리합니다. 빠르고 효율적인 렌더링을 통해 동적인 웹 애플리케이션을 쉽게 개발할 수 있도록 도와줍니다.
React는 가상 돔을 활용하여 효율적인 UI 업데이트를 가능케 하며, 컴포넌트 기반 구조로 독립적이고 재사용 가능한 컴포넌트로 분리하여 개발해 각 상태관리와 로직을 관리할 수 있습니다. 또 큰 커뮤니티와 생태계를 갖추고 있어 다양한 라이브러리와 도구를 활용하여 빠르게 애플리케이션을 개발할 수 있습니다.
DOM(Document Object Model)은 웹 페이지의 구조를 프로그래밍적으로 접근하고 조작할 수 있는 방법을 제공하는 표준 인터페이스입니다. HTML, XML 문서의 계층적 구조를 트리 형태로 표현한 것으로, 웹 브라우저가 웹 페이지를 렌더링할 때 이 구조를 사용합니다.
최상위 노드는 문서 전체를 나타내는 document 객체이고, <div>, <p> 등 웹 페이지의 각 요소는 DOM 트리의 하나의 노드로 표현됩니다. JavaScript로 DOM을 직접 조작 가능합니다. 또한 키보드 입력, 마우스 이동 등의 사용자 인터랙션에 대한 이벤트를 처리하는 메커니즘을 제공합니다.
Virtual DOM은 실제 DOM의 복사본으로, 웹 성능을 최적화하기 위해 사용되는 DOM 관리 방법입니다. 애플리케이션 상태 변경시 가상 DOM 객체를 통해 변경된 부분만 찾아내어 이를 실제 DOM에 적용하는 기능을 합니다.
Virtual DOM의 동작 순서는 크게 Diffing, Reconciliation 두 가지로 구분할 수 있습니다. Diffing이란 가상돔에서 변경점을 찾아내는 과정을 의미하며, Reconciliation은 찾아낸 변경점을 실제 DOM에 적용하는 과정을 의미합니다.
애플리케이션이 처음 실행될 때 애플리케이션의 초기 상태를 담은 Virtual DOM을 메모리 상에 하나 생성합니다. 이후 애플리케이션이 실행되면서 상태가 변경된 부분이 있는 경우, 새로운 버전의 Virtual DOM을 메모리상에 하나 더 생성합니다. 새로운 버전의 Virtual DOM이 생성된 후, 이전 버전의 Virtual DOM과 비교하는 과정이 Diffing에 돌입하고, 변경점을 찾아냅니다. 이 과정에서 두 Virtual DOM 트리의 각 노드를 비교하여 어떤 부분이 변경되었는지 확인합니다. 변경점을 찾아낸 이후에는, 실제 DOM에 적용하는 과정인 Reconciliation에 돌입합니다. 이 과정에서 변경된 부분만 실제 DOM에 업데이트하기 때문에, 브라우저 성능이 향상될 수 있는 것입니다. Reconciliation이 완료된 이후, 또 다른 변경점이 생기면 구 버전의 Virtual DOM이 폐기되고, 새로운 변경 사항을 반영한 최신 버전의 Virtual DOM이 다시 생성됩니다.
짧은 시간안에 여러 개의 state, props가 동시에 변경되면 이를 각각 처리하는 것이 아니라 한꺼번에 모아서 처리하는 Batch Update가 이루어집니다.
항상 그런 것은 아닙니다. 간단한 애플리케이션 경우에는 가상돔을 사용하는 것이 오히려 오버헤드를 초래할 수 있습니다. 가상돔 자체도 메모리 공간을 차지하고 Diffing하는 과정 역시 CPU를 활용하기 때문입니다. 다만, DOM 트리가 복잡하고, 상태 변경도 빈번하게 일어나는 대규모 애플리케이션의 경우 사람의 인지 능력으로는 정확히 어떤 DOM을 업데이트해야 하는지 식별하기 어렵기 때문에 가상돔을 사용하는 것입니다. 따라서 애플리케이션의 복잡도와 요구 사항에 맞게 가상돔 적용 여부를 정하는 것이 좋습니다.
React 18에서는 동시성 모드(Concurrent Mode)가 도입되어 긴 렌더링 작업을 중단하고 사용자 입력과 같은 더 중요한 작업을 먼저 처리할 수 있습니다. 자동 일괄 처리 기능(Automatic Batching)이 향상되어 이벤트 핸들러뿐만 아니라 비동기 코드 내에서도 일괄 처리가 가능해졌습니다. 또한 useTransition 훅을 사용하여 상태 업데이트의 우선순위를 구분할 수 있으며, useDeferredValue 훅을 사용해 긴급하지 않은 상태 업데이트에서 이전 값을 사용할 수 있습니다. Suspense 기능도 개선되어 데이터를 가져오는 동안 컴포넌트의 로딩 상태를 더 쉽게 관리할 수 있습니다.
Batch Update(일괄 업데이트)는 여러 상태 업데이트를 하나의 단위로 묶어 한 번에 처리하는 방식입니다. React에서는 이러한 배치 업데이트를 통해 성능을 최적화할 수 있습니다. 상태 업데이트가 일어날 때마다 리렌더링이 발생하면 성능에 큰 영향을 미칠 수 있기 때문에, React는 여러 업데이트를 모아서 한 번에 처리함으로써 리렌더링 횟수를 줄입니다.
React는 이벤트 핸들러 내에서 발생하는 여러 상태 업데이트를 자동으로 일괄 처리합니다. 예를 들어, 버튼 클릭 시 여러 상태를 변경해야 하는 경우, React는 각 상태 업데이트마다 리렌더링하지 않고, 모든 상태 업데이트가 완료된 후에 한 번만 리렌더링합니다.
React 18에서는 비동기 코드에서도 일괄 처리가 가능하도록 기능이 확장되었습니다. 기존에는 이벤트 핸들러 내에서만 일괄 처리가 가능했지만, 이제는 Promise나 setTimeout과 같은 비동기 작업에서도 일괄 처리가 지원됩니다.
JSX는 JavaScript XML로 자바스크립트 코드 안에 HTML과 같은 구문을 사용할 수 있게 해줘 이를 통해 UI 구조를 직관적으로 작성할 수 있습니다.
TSX란 TypeScript XML로 JSX와 유사하지만 타입스크립트의 타입 시스템을 활용할 수 있습니다. 이를 활용하면 타입 체크를 통해 더 안전한 코드를 작성할 수 있습니다.
SPA는 단일 페이지 애플리케이션으로 초기에 필요한 코드를 모두 로드한 후, 페이지를 다시 로드하지 않고 동적으로 콘텐츠를 업데이트하는 방식입니다.
CSR는 클라이언트 측에서 페이지를 동적으로 렌더링하는 방식입니다. 초기 페이지 로드 후, 클라이언트가 JS를 사용하여 데이터를 가져와 렌더링합니다.
SSR은 서버 측에서 초기 페이지 렌더링을 수행하고, 완성된 HTML을 클라이언트에게 전달하는 방식입니다. 페이지가 서버에서 렌더링되기 때문에 초기 로딩 시간이 단축되고 SEO에 유리합니다.
SSG(Static Site Generation)는 빌드 타임에 모든 페이지를 미리 렌더링하여 정적 파일(HTML, CSS, JS)로 생성합니다. 생성된 정적 파일들은 서버나 CDN을 통해 제공됩니다.
특성 | SSR | SSG |
---|---|---|
렌더링 시점 | 요청 시 서버에서 렌더링 | 빌드 시 미리 렌더링 |
초기 로드 속도 | 빠름 | 매우 빠름 |
데이터 최신성 | 최신 데이터 제공 | 빌드 시점 이후 데이터 반영 어려움 |
서버 부하 | 요청마다 서버 부하 있음 | 거의 없음 |
SEO | 매우 좋음 | 매우 좋음 |
사용 사례 | 동적 컨텐츠, 사용자별 데이터가 많은 페이지 | 정적 컨텐츠, 자주 변경되지 않는 페이지 |
SSR은 요청시마다 서버에서 HTML을 렌더링하므로 동적 컨텐츠에 적합합니다. 예를 들어, 사용자별로 다른 데이터를 보여줘야 하는 경우 대시보드나 사용자 프로필 페이지에 적합합니다. 또 SEO가 중요한 페이지 블로그나 뉴스 사이트 등에 적합합니다.
SSG는 빌드 타임에 모든 데이터를 미리 렌더링하여 정적 파일로 생성하므로 정적 컨텐츠가 많은 페이지 혹은 빠른 초기 로드가 중요한 페이지에 적합합니다.
Next.js는 React를 기반으로 한 프레임워크로, 서버 사이드 렌더링(SSR)과 정적 사이트 생성(SSG)을 포함한 다양한 기능을 제공합니다.
파일/폴더 기반 라우팅, 이미지 최적화, 자동 코드 분할 등의 기능을 제공합니다.
(Next는 SEO, 페이징이 중요할 때 주로 사용하고, React는 사용자 인터랙션이 중요할 때 주로 사용합니다.)
코드 분할(Code Splitting)은 애플리케이션의 코드베이스를 여러 개의 작은 번들로 나누는 기법을 말합니다. 이 기법을 통해 초기 로드 시간을 줄이고, 사용자가 필요로 하는 코드만을 로드함으로써 네트워크 트래픽을 줄일 수 있습니다. 또한 코드를 더 작은 모듈로 나누어 유지보수가 쉽고 특정 기능이나 페이지의 변경이 전체 애플리케이션의 미치는 영향을 줄입니다. 또한 여러 개의 작은 파일을 병렬로 로드할 수 있어 더 빠르게 애플리케이션을 사용할 수 있습니다. 이러한 성능 이점으로 코드 분할은 특히 대규모 애플리케이션에서 중요한 성능 최적화 기술 중 하나입니다.
Next.js의 Image 컴포넌트를 사용하면 이미지 최적화를 자동으로 처리해 페이지 성능을 향상시킵니다. 또한 이미지 크기를 미리 알고 있기 때문에 레이아웃 시프트를 방지할 수 있고 이미지 로딩 전 홀더 이미지 설정으로 사용자 경험을 향상시킬 수 있습니다.
이미지가 로드되기 전과 후에 페이지의 레이아웃이 변경되는 현상을 의미합니다. 이는 사용자 경험에 악영향을 미칠 수 있으며, 특히 이미지가 많은 페이지에서 문제가 됩니다.
Next.js에서 서버사이드 렌더링을 하기 전 초기 상태를 적용하는 과정을 말합니다. 서버에서 렌더링된 HTML을 클라이언트로 전달하고, 클라이언트에서 JavaScript가 실행되면서 초기 상태를 클라이언트와 동기화하는 과정을 말합니다.
Flux 패턴은 Facebook에서 만든 아키텍처 패턴으로, React에서 데이터의 흐름을 관리하는 데 사용됩니다. Flux는 단방향 데이터 흐름을 특징으로 하며, Action -> Dispatcher -> Store -> View의 흐름을 따릅니다. View에서 사용자가 상호작용하면 Action이 Dispatcher를 통해 전파되고, Store는 Action을 처리하여 View를 갱신합니다. 이 패턴은 React의 선언형 프로그래밍 스타일과 잘 맞아 데이터 변경을 명확하게 처리할 수 있습니다.
단방향 데이터 흐름 패턴의 장점은 데이터가 항상 한 방향으로 흐르기 때문에 상태 변화의 흐름을 예측 가능하고 쉽게 추적할 수 있어 디버깅과 문제 해결에 용이합니다. 또 상태 관리가 명확하게 이루어져 코드의 가독성과 유지보수성이 향상됩니다. 각 컴포넌트는 자신의 상태와 props만 신경쓰면 됩니다. 또한 상태가 중앙에서 관리되므로 상태 변경이 일관되게 관리되어 데이터의 일관성이 유지될 수 있습니다. 추가로 상태와 뷰가 명확하게 분리되어 있어 개별 컴포넌트의 테스트가 쉬워집니다.
단점은 설정과 보일러플레이트 코드가 많을 수 있으며, 초기 학습 곡선이 존재합니다.
MVC (Model-View-Controller)
MVC 패턴은 애플리케이션을 모델, 뷰, 컨트롤러의 세 가지 주요 컴포넌트로 분리합니다. 모델은 데이터와 비즈니스 로직을 관리하고, 뷰는 사용자 인터페이스를 관리하며, 컨트롤러는 모델과 뷰 사이의 상호작용을 관리합니다.
MVVM (Model-View-ViewModel)
MVVM 패턴은 모델, 뷰, 뷰모델로 구성됩니다. 뷰모델은 뷰와 모델 사이의 데이터 바인딩을 관리하여 뷰가 모델의 상태 변화를 쉽게 반영할 수 있게 합니다.
Redux
Redux는 Flux 패턴에서 발전한 상태 관리 라이브러리로, 단일 스토어와 순수 함수인 리듀서(Reducer)를 사용합니다.
단방향 바인딩은 데이터가 한 방향으로 흐르고, 양방향 바인딩은 데이터가 양방향으로 흐르는 차이가 있습니다. 단방향 바인딩은 주로 모델에서 뷰로 데이터가 전달되고 뷰의 변경은 모델의 영향을 주지 않지만 양방향 바인딩은 모델, 뷰 모두 서로가 영향을 줍니다. 따라서 단방향 바인딩이 데이터 흐름이 단방향이기때문에 디버깅이 용이하고 코드의 복잡성이 낮습니다. 양방향 바인딩은 이러한 것이 상대적으로 어렵지만, 사용자 인터페이스 반응성이 더 높습니다. 양방향 데이터 바인딩을 제공하는 것은 Angular, Vue.js가 있습니다.
선언적 프로그래밍 (Declarative Programming)은 “무엇을” 수행할 것인지를 기술하는 프로그래밍 패러다임입니다. 즉, 원하는 결과가 무엇인지를 설명하고, 그것을 어떻게 수행할지는 명시하지 않습니다. 예를 들어 함수형 프로그래밍, SQL, 리액트 등이 있습니다.
명령형 프로그래밍(Imperative Programming)은 “어떻게” 수행할 것인지를 기술하는 프로그래밍 패러다임입니다. 즉, 프로그램이 수행할 일련의 절차나 단계를 명령어로 명확하게 기술합니다.
함수형 프로그래밍은 함수를 일급 시민으로 취급하고, 부작용을 피하며, 순수 함수를 선호하는 선언적 프로그래밍 방식입니다.
리액트의 생명주기는 마운트(mount), 업데이트(updated), 언마운트(unmount) 크게 세단계로 분류됩니다.
마운트는 컴포넌트가 처음 DOM에 삽입되는 단계를 말하고, 업데이트는 컴포넌트가 다시 렌더링될 때 발생하는 단계입니다. 주로 props나 상태 변화에 의해 트리거됩니다. 마지막으로 컴포넌트가 DOM에서 제거되는 단계를 언마운트라고 합니다.
리액트에서 생명주기 메서드를 사용하는 이유는 컴포넌트의 생성, 업데이트, 제거 시점을 효과적으로 관리하기 위해서입니다.
업데이트를 통해 UI를 최신 상태로 유지하고 컴포넌트가 제거될 때 타이머나 구독을 정리하여 메모리 누수를 방지합니다. 또한 불필요한 리렌더링을 방지하여 성능을 개선합니다. 즉, 생명주기 메서드는 컴포넌트의 상태를 관리하고, 성능을 최적화하며, 자원을 적절히 정리하는 데 도움을 줍니다.
생명주기는 소프트웨어가 시작되고, 성장하며, 유지되고, 결국에는 종료되는 과정을 말합니다. 즉 소프트웨어가 처음 개발되기부터, 배포, 유지보수까지의 전체 과정을 말합니다.
주로 '요구사항 분석 -> 설계 -> 개발 -> 테스트 -> 배포 -> 유지보수' 과정을 따릅니다.
이러한 생명주기 단계는 소프트웨어가 안정적이고 효과적으로 운영될 수 있도록 보장해줍니다. 계속해서 개선하고 문제를 해결하면서 소프트웨어의 품질과 사용자 만족도를 유지하는 데 필수적입니다.
리액트 훅이란 함수형 컴포넌트에서 상태와 생명주기 메서드를 사용할 수 있도록 해주는 기능입니다. React 16.8에서 도입된 훅은 클래스형 컴포넌트의 복잡한 구조를 피하고, 함수형 컴포넌트를 이용해 더 간결하고 직관적인 코드를 작성할 수 있게 합니다.
useState:
const [count, setCount] = useState(0);
count
는 상태 변수, setCount
는 상태를 갱신하는 함수입니다.useEffect:
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]);
useContext:
const value = useContext(MyContext);
useReducer:
useState
의 대안으로, 복잡한 상태 로직을 다룰 때 유용합니다.const [state, dispatch] = useReducer(reducer, initialState);
useCallback:
const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b]);
useMemo:
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
useRef:
프로젝트시 사용자 로그인 정보 등 전역 상태 관리가 필요한 데이터가 생겨 다양한 전역 상태 관리 라이브러리를 고민했습니다. 그 중 팀원간 협의를 거쳐 Zustand를 사용했습니다. 그 이유는 팀원간 공통적으로 사용할 수 있는 상태 관리 라이브러리가 없어 새로운 라이브러리를 선택해야 했고 ContextAPI, RTX 등 다양한 라이브러리를 고민하다 짧은 프로젝트 기간 내 가장 간단하고 직관적인 문법으로 쉽게 사용할 수 있고, 보일러플레이트 코드도 없어 간편한 Zustand를 사용했습니다.
Props는 부모 컴포넌트에서 자식 컴포넌트로 전달되는 데이터입니다. 이는 읽기 전용입니다.
State는 컴포넌트 내부에서 관리되는 동적인 데이터를 의미합니다. 컴포넌트 내부에서 변경 가능합니다.
Props Drilling은 React 애플리케이션에서 부모 컴포넌트로부터 자식 컴포넌트로 데이터나 함수를 전달할 때 발생하는 문제를 의미합니다. 이는 데이터나 콜백 함수가 여러 계층의 컴포넌트를 통해 깊게 전달될 때 발생합니다.
Props Drilling은 컴포넌트 간의 의존성을 증가시키고 재사용성을 감소시킵니다. 또한 구조가 복잡해져 유지보수가 어려워질 수 있습니다.
컴포넌트 구조를 재설계해 props drilling을 줄이거나 Context API, Redux 등의 상태 관리 라이브러리를 사용해 해결할 수 있습니다.
리액트 확장 프로그램(React Dev Tools)을 이용하여 색상으로 렌더링 빈도를 확인하고 불필요한 리렌더링이 발생하지 않도록 최적화할 수 있습니다. 또한 Lighthouse를 통해 성능, 웹 접근성, SEO, 초기 렌더링 시간, 권장 사항 등을 확인하고 이를 최적화할 수 있습니다.
💬 실제 면접 경험을 공유하자면 암기식 기술 질문을 계속 하는 곳도 있고 그런 질문 하나 없이 프로젝트 기반 질문만 하는 곳도 있었습니다.
제가 느끼기에는 자신이 한 프로젝트, 사용한 기술 기반으로 꼬리 질문을 준비하는 것이 제일 중요하고 나머지는 회사마다 차이가 크다고 느꼈습니다.
실제로 '리액트가 뭔가요?' 같은 질문은 받은 적이 없고 동작 원리를 이해하는 부분은 중요하다고 생각합니다. 기본 질문 외에 답변도 프로젝트 경험에 따라 달라질 수 있으니 참고해주세요.
추가하면 좋은 부분이나 잘못된 점이 있다면 댓글 남겨주세요. 감사합니다 :)
bluevulpe블루불페 - 프론트엔드 주니어 기술 면접 답변 회고(feat.2023상반기이직준비)