안녕하세요 : )
SSAFYicial 9기 신산하입니다.
8월의 기획기사는 React 면접 질문 모음입니다!
공통 프로젝트 때 프론트엔드를 맡았던 여러분들,
기초 지식을 머리에 넣고, 후회없는 면접 봅시다!
LET's GO!
1. 함수형 컴포넌트와 Class형 컴포넌트 방식의 차이점에 대해서 답변해주세요.
함수형 컴포넌트
import React from 'react';
import './App.css'
function App(){
return(
<>
안녕
</>
)
}
클래스형 컴포넌트
import React, {Component} from "react";
export default class App extends Component{
render(){
return(
<div>
안녕
</div>
)
}
}
1) 선언 방식의 차이
클래스형 컴포넌트
- class 키워드가 반드시 필요하며, Component로 상속을 받아야 합니다.
- render() 메소드가 반드시 필요합니다.
함수형 컴포넌트
- 함수 자체가 렌더 함수이기 때문에 render() 메서드를 사용하지 않아도 됩니다.
- class, Component 키워드를 쓰지 않아도 되기 때문에 간결하게 코드 작성이 가능합니다.
2) 기능적 차이
클래스형 컴포넌트
- state, life Cycle 관련 기능 사용이 가능합니다.
- 임의 메서드를 정의할 수 있습니다.
plus) 임의 메서드란?
import React, {Component} from 'react';
class EventPractice extends Component {
state = {
message: ''
}
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleClick = this.handleClick.bind(this);
}
handleChange(e) {
this.setState({
message: e.target.value
})
}
handleClick(e) {
alert(this.state.message);
this.setState({
message: ''
})
}
render() {
return(
<div>
<h1>이벤트 연습</h1>
<input
type='text'
name='message'
placeholder='아무거나'
value={this.state.message}
onChange={this.handleChange}
/>
<button
onClick={this.handleClick}
>
확인
</button>
<h3>{this.state.message}</h3>
</div>
);
}
}
export default EventPractice;
onChange, onClick에 전달한 함수를 따로 빼내서 컴포넌트 임의 메서드로 생성하는 모습입니다.
이벤트를 처리할 때 렌더링을 하는 동시에 함수를 만들어서 전달해 주는 방법도 있지만, 이 대신 함수를 미리 준비하여 전달하는 방법도 있습니다. 성능상으로는 차이가 거의 없지만, 가독성을 훨씬 높여줍니다.
함수형 컴포넌트
- state, life Cycle 관련 기능 사용이 불가능합니다. (16.8 버전 이후 제공되는 Hook을 통해 해결 가능)
- 메모리 자원을 함수형 컴포넌트보다 덜 사용합니다.
- 빌드한 결과물의 크기 역시 클래스형 컴포넌트보다 작습니다.
plus) 라이프 사이클에 대하여
모든 리액트 컴포넌트에는 라이프사이클이 있습니다. 적절한 생명주기에 어떤 작업을 처리해야 하는지 지정해줘야 불필요한 업데이트를 방지할 수 있습니다.
컴포넌트는 생성(mount) > 업데이트(update)>제거(unmount)의 생명주기를 갖습니다.
함수형 컴포넌트에서는 Life Cycle API를 사용하며, 함수형 컴포넌트에서는 Hook을 사용합니다.
분류 | 클래스형 컴포넌트 | 함수형 컴포넌트 |
Mounting | constructor() | 함수형 컴포넌트 내부 |
Mounting | render() | return() |
Mounting | ComponenDidMount() | useEffect() |
Updating | componentDidUpdate() | useEffect() |
UnMounting | componentWillUnmount() | useEffect() |
3) state 사용 차이
클래스형 컴포넌트
- 객체 형식입니다.
- constructor 안에 this.state를 통해 초기값 설정이 가능합니다.
- constructor 없이도 초기 값 설정이 가능합니다.
- this.setState 함수로 state 값을 변경할 수 있습니다.
constructor(props){
super(props);
this.state = {
name: 'color',
items:[]
};
}
//without constructor
class App extends Component{
state = {
color:'red'
}
}
//state 값 변경
onClick = {
() => {
this.setState({
color:'blue'
})
}
}
함수형 컴포넌트
- useState로 state를 핸들링합니다.
- useState 함수를 호출하면 배열이 반환됩니다. 첫 번째 원소는 state이며, 두 번째 원소는 state를 변경해주는 함수입니다.
const App = () => {
const [color, setColor] = useState('red');
const ChangeColor = () => {
setColor("blue");
}
}
4) props 사용 차이
props는 컴포넌트의 속성을 설정할 때 사용하는 요소로, 읽기 전용이며 컴포넌트 자체의 props를 수정해서는 안됩니다.
모든 React 컴포넌트는 자신의 props를 다룰 때 순수 함수처럼 동작해야 하며, 수정되는 것은 state만 수정되어야 합니다.
클래스형 컴포넌트
- this.props로 불러올 수 있습니다.
class App extends Component{
render(){
const {color} = this.props;
return(
<div>
내가 좋아하는 색은 {color}야!
</div>
)
}
}
함수형 컴포넌트
- parameter로 props를 전달받아 사용합니다.
const App = ({color}) => {
return(
<div>
내가 좋아하는 색은 {color}야!
</div>
)
}
2. 동기식 방법과 비동기식 방법의 차이점에 대해서 답변해주세요.
1) 동기 VS 비동기
자바스크립트는 단일 스레드 프로그래밍 언어로 단일 호출 스택이 있어 한 번에 하나의 일을 처리할 수 있습니다. 그러므로 자바스크립트는 동기 방식으로 진행이 됩니다.
하나의 호출 스택만 있기 때문에 하나의 함수를 처리하는 데 매우 오랜 시간이 걸린다면 다음 실행해야할 함수에 지장을 줄 수 있다는 문제가 발생합니다. 이 부분이 바로 비동기의 필요성입니다.
동기
- 작업들이 순차적으로 이루어집니다.
- 다른 작업들을 blocking합니다.
비동기
- 작업들이 순차적으로 이루어지지 않습니다.
- 다른 작업들을 non-blocking합니다.
- HTTP 요청(get, post, put, delete), 이벤트 핸들러(click...), setTimeout, setInterval 등이 대표적인 예시입니다.
자바스크립트를 사용한 비동기 통신 방식을 Ajax(Asynchronous Javascript and XML)이라고 합니다.
2) 비동기 통신의 대표적인 방식
callback
- 인자로 들어오는 함수를 말합니다.
- 콜백 헬로 인한 에러처리 불가, 가독성이 떨어진다는 문제점이 있습니다.
promise
- ES6에서 나온 비동기 패턴입니다.
- 비동기 통신 결과와 상태를 저장하는 객체입니다.
- 후속처리 메서드로 then(성공), catch(에러), finally(무조건)이 있습니다.
async/awit
- Promise의 복잡성으로 인해 ES8에서 나온 비동기 패턴입니다.
- Promise를 기반으로 하며, 완전히 같지는 않으나 사용하기 편합니다.
3. CSR VS SSR
CSR (클라이언트 사이드 렌더딩)
Browser(Client)에서 js에 의해 View(HTML)을 동적으로 생성합니다.
때문에 페이지 전환이 SSR(서버 사이드 렌더링)보다 상대적으로 빠릅니다.
대신 최초 접속 시, 모든 js와 static 파일(HTML, image)를 가져와야해 최조 접속 시 로딩은 SSR에 비해 늦습니다.
SSR(서버 사이드 렌더링)
Web Server에서 View를 생성합니다.
페이지가 전환될 때마다, 클라이언트가 server에 View를 요청하고, server는 그것을 생성 후 client에게 보내줍니다.
때문에 View 전환 속도가 CSR에 비해 상대적으로 늦습니다.
그리고 페이지 요청이 빈번해질 수록 CSR에 비해 서버 부하가 더 커집니다.
plus) SPA
어떠한 웹 사이트의 전체 페이지를 하나의 페이지에 담아 동적으로 화면을 변경해 표시하는 기술입니다.
브라우저는 최초에 한 번 서버에 요청하여 페이지 전체를 로드하고, 이후부터는 특정 변경 사항이 일어나는 부분만 Ajax와 같은 기술을 통해 데이터를 바인딩하는 방식으로 동작합니다.
SPA도 문제점은 있습니다. 자바스크립트로 DOM 조작이 빈번하게 일어나 브라우저의 성능을 저해한다는 문제입니다.
그래서 Virtual DOM이라는 개념이 생겼습니다. React, Angular, Vue가 대표적인 기술들입니다.
가상의 DOM 트리로 HTML 정보를 저장하고 있다가, 이 트리에 변경이 발생하면 모든 변화를 모아 단 한 번 브라우저를 호출해 화면을 갱신하는 방법을 사용합니다.
Single Page Application(SPA) 기반 프레임워크는 CSR이고, php와 같은 Multiple Page Application(MPA) 기반 프레임워크는 SSR입니다.
4. 이벤트 동작 방식에 대해 말씀해주세요.
feat. 이벤트 버블링 / 이벤트 캡쳐
이벤트 버블링
하위 Element에서 발생한 클릭 이벤트가 최상단 Element까지 전달되는 현상입니다.
이벤트 캡쳐링
이벤트 버블링과 반대 방향으로 진행됩니다.
stopPropagation
내가 클릭한 요소에서만 이벤트를 발생시키고 싶을 땐 stopPropagation()을 이용하면 됩니다.
- stopPropagation() : 이벤트의 전파 방지, 방화벽처럼 역할 해 전파가 중단됩니다.
- preventDefault() : 이벤트 발생 시 브라우저의 기본 동작을 막습니다. submit할 때 새로고침을 막는 등...
plus) 이벤트 위임
React의 장점중 하나는 Virtual DOM을 이용하여 DOM에 직접 접근하지 않고, 둘의 차이를 비교하여 바뀐 부분의 DOM만 업데이트 하는 것입니다.
그래서 querySelector 등을 이용하여 DOM으로 직접 접근하여 addEventListener로 이벤트를 등록하는 Vanila JS와는 달리 React는 그냥 태그에 이벤트를 등록합니다.
그러면 React에서도 메모리 절약을 위해 ref로 직접 DOM에 접근하여 이벤트 위임을 구현하면 좋지 않을까요?
정답은 "아니다"입니다. React는 이미 자체적으로 DOM에서 react를 관리하는 root node에 이벤트 리스너를 등록하게 설계되어 있어서 일일이 ref로 DOM에 직접 접근하여 이벤트 위임을 해도 성능상 이점이 없습니다.
5. React의 Life Cycle을 아는대로 답변해주세요.
리액트 클래스형 컴포넌트는 컴포넌트 라이프 사이클 메서드를 사용하고, 함수형 컴포넌트는 Hook을 사용합니다.
클래스형 : 라이프 사이클 메서드
--- 마운트(mount) ---
1) constructor
컴포넌트 생성자 메서드, 컴포넌트가 생성되면 가장 먼저 실행되는 메서드입니다.
this.props, this.state에 접근이 가능하고 리액트 요소를 반환합니다.
2) getDerivedStateFromProps
props로부터 파생된 state를 가져옵니다. 즉 props로 받아온 것을 state에 넣어주고 싶을때 사용합니다.
3) render
컴포넌트를 렌더링하는 메서드입니다.
4) componentDidMount
컴포넌트가 마운트 됨, 즉 컴포넌트의 첫번째 렌더링이 마치면 호출되는 메서드 입니다.
이 메서드가 호출되는 시점에는 화면에 컴포넌트가 나타난 상태입니다.
여기서는 주로 DOM을 사용해야 하는 외부 라이브러리 연동, 해당 컴포넌트에서 필요로하는 데이터를 ajax로 요청, 등의 행위를 합니다.
--- 업데이트(update) ---
1) getDerivedStateFromProps
컴포넌트의 props나 state가 바뀌었을때도 이 메서드가 호출됩니다.
2) shouldComponentUpdate
컴포넌트가 리렌더링 할지 말지를 결정하는 메서드입니다.
- React.memo와 유사함, boolean 반환으로 결정
3) componentDidUpdate
컴포넌트가 업데이트 되고 난 후 발생합니다.
- 의존성 배열이 변할때만 useEffect가 실행하는 것과 같음
--- 언마운트(unmount) ---
1) componentWillUnmount
컴포넌트가 화면에서 사라지기 직전에 호출됩니다.
여기서 주로 DOM에 직접 등록했었던 이벤트를 제거하고, 만약에 setTimeout을 걸은 것이 있다면 clearTimeout을 통하여 제거를 합니다.
추가적으로, 외부 라이브러리를 사용한게 있고 해당 라이브러리에 dispose기능이 있다면 여기서 호출해주시면 됩니다.
함수형 : Hook
--- 사용 규칙 (rule) ---
최상위에서만 Hook을 호출해야 합니다.
- 반복문, 조건문, 중첩된 함수 내에서 Hook을 실행하면 안됩니다.
- 이 규칙을 따르면 컴포넌트가 렌더링될 때마다 항상 동일한 순서로 Hook이 호출되는 것이 보장됩니다.
- 리액트 함수 컴포넌트에서만 Hook을 호출해야 합니다.
- 일반 JS함수에서는 Hook을 호출해서는 안됩니다.
--- Hook 7가지 종류 ---
1) useState
상태를 관리합니다. [state이름, setter이름] 순으로 반환 받아서 사용합니다.
const [state, setState] = useState(initialState);
2) useEffect
화면에 렌더링이 완료된 후에 수행되며 componentDidMount와 componentDidUpdate, componentWillUnmount가 합쳐진 것입니다.
- 만약 화면을 다 그리기 이전에 동기화 되어야 하는 경우에는, useLayoutEffect를 활용하여 컴포넌트 렌더링 - useLayoutEffect 실행 - 화면 업데이트 순으로 effect를 실행시킬 수 있다.
useEffect안에서의 return은 정리 함수(clean-up)를 사용하기위해 쓰여집니다.
- 메모리 누수 방지를 위해 UI에서 컴포넌트를 제거하기 전에 수행
- 컴포넌트가 여러 번 렌더링 된다면 다음 effect가 수행되기 전에 이전 effect가 정리됩니다.
3) useMemo
- 메모이제이션된 값을 반환합니다.
- 이미 연산된 값을 리렌더링 시 다시 계산하지 않도록 합니다.
- 의존성이 변경되었을 때에만 메모이제이션된 값만 다시 계산 합니다.
- 의존성 배열이 없는 경우 매 렌더링 때마다 새 값을 계산합니다.
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
4) useCallback
메모이제이션 된 콜백을 반환합니다. useMemo와 유사하게 이용되며 '함수'에 적용해줍니다.
의존성이 변경되었을때만 변경됩니다. 때문에 특정 함수를 새로 만들지 않고 재사용가능하게 합니다.
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
5) useContext
기존에 컴포넌트 간에 데이터를 전달하려면 props를 이용해야 했습니다. props는 부모 자식 관계에서 데이터를 전달합니다. 따라서 A, B, C 컴포넌트가 각각 부모자식 관계일 때, A에서 C로 데이터를 내려보내주려면 중간 B 컴포넌트를 거쳐야 했죠. A, B, C, D, E 컴포넌트일 때 A에서 E로 데이터를 내려보내주려면 중간 B, C, D 컴포넌트를 거쳐야 합니다. 엄청난 비효율이죠.
따라서 사람들은 이 문제 해결을 위해 Redux를 쓰곤 했습니다. 그러다가 리액트 팀에서 죽어있던 context api를 새로 되살려냈습니다.
[조부모 함수]
import React, { createContext, useMemo, useState } from 'react';
import Parent from './Parent';
export const UserContext = createContext({
setLoggedIn: () => {},
setLoading: () => {},
});
const GrandParent = () => {
const [loggedIn, setLoggedIn] = useState(false);
const [loading, setLoading] = useState(false);
const value = useMemo(() => ({ setLoggedIn, setLoading }), [setLoggedIn, setLoading]);
return (
<UserContext.Provider value={value}>
<Parent />
<div>{loggedIn ? '로그인' : '로그인안해'}</div>
<div>{loading ? '로딩중' : '로딩안해'}</div>
</UserContext.Provider>
);
};
export default GrandParent;
createContext 내부에 공유하길 원하는 데이터의 초깃값을 넣어두고 value 변수로 묶어주면 됩니다. 이 때 value 객체는 객체이므로 리렌더링의 주범이 되므로 useMemo로 캐싱해두세요. 안 그러면 나중에 이 데이터를 쓰는 모든 컴포넌트가 매번 리렌더링됩니다.
[자식 함수]
함수 구조는 조부모 > 부모 > 자식 함수 순으로 구성되어 있습니다.
부모 함수는 아무 것도 하지 않지만, 조부모 함수에서 선언된 useContext가 자식 함수로 오는 모습을 볼 수 있습니다.
import React, { useContext } from 'react';
import { UserContext } from './GrandParent';
const Children = () => {
const { setLoading, setLoggedIn } = useContext(UserContext);
return (
<>
<button onClick={() => setLoading((prev) => !prev)}>로딩토글</button>
<button onClick={() => setLoggedIn((prev) => !prev)}>로딩토글</button>
</>
);
};
export default Children;
useContext를 쓸 때 주의할 사항은, Provider에 제공한 value가 달라지면 useContext를 쓰고 있는 모든 컴포넌트가 리렌더링 된다는 것입니다.
value 안에는 setLoading과 setLoggedIn이 들어있고 앞으로 개수가 더 늘어날 가능성이 높습니다. 그 중 하나라도 바뀌면 객체로 묶여있으므로 전체가 리렌더링되는 것입니다. 따라서 잘못 쓰면 엄청난 렉을 유발할 수 있습니다.
해결 방법은 자주 바뀌는 것들을 별도의 컨텍스트로 묶거나(컨텍스트는 여러 개 쓸 수 있습니다. Provider로만 잘 감싸주세요.), 자식 컴포넌트들을 적절히 분리해서 shouldComponentUpdate, PureComponent, React.memo 등으로 감싸주는 것이 있습니다.
6) useRef
특정 DOM 선택할때 주로 쓰이며 .current 프로퍼티로 전달된 인자로 초기화된 변경 가능한 ref 객체를 반환합니다. 반환된 객체는 컴포넌트의 전 생애주기를 통해 유지됩니다.
const refContainer = useRef(null);
7) useReducer
useState의 대체 함수로 컴포넌트 상태 업데이트 로직을 컴포넌트에서 분리시킬 수 있습니다.
컴포넌트 바깥에 로직을 작성할 수도 있고, 심지어 다른 파일에 작성한 후 불러와서 사용할 수도 있습니다.
reducer란 현재 상태와 액션 객체를 파라미터로 받아와서 새로운 상태를 반환해주는 함수 입니다.
const [state, dispatch] = useReducer(reducer, initialArg, init);
저의 주 종목이다 보니
하나의 질문에도 간단히 넘어가는 법이 없었네요 ㅎㅎ...
오늘 기사를 쓰면서 저도 몰랐던 점과 배운 점이 무척 많았습니다.
프론트엔드를 꿈꾸시는 분들께 오늘 기사가 도움이 되었으면 좋겠습니다!
참고 문서
1. 클래스형, 함수형 컴포넌트 차이
2. 임의 메서드
3. 동기vs비동기
4. 이벤트
5. 라이프 사이클
6. useContext
7. 참고 질문 리스트
'대외활동 > SSAFYicial' 카테고리의 다른 글
[CS 정리는 내가 할게, 면접은 누가볼래? - 알고리즘편] 자주쓰는 알고리즘 10가지 정리! (0) | 2023.09.26 |
---|---|
[TMI] 신한 해커톤 with SSAFY 현장 속으로! (0) | 2023.09.26 |
[회고] 내가 만약 공통프로젝트 1일차로 돌아간다면? 공통프로젝트를 마치며 깨달은 것들 (7) | 2023.08.20 |
[CS 정리는 내가 할게, 면접은 누가볼래? - 자바/Spring편] 자바/Spring 면접 질문 필수 암기 모음집 (0) | 2023.07.15 |
[잡페어/채용박람회] 면접부터 생생한 현장 후기까지! (0) | 2023.07.15 |