Q1.
Q2.
Q3.
Q4.
Q5.
Q6.
Q7. styled-components를 사용해 props를 기반으로 스타일을 조건부로 설정하는 예시 코드를 작성해보세요.
import styled from 'styled-components';
// 조건부 스타일링이 적용된 Button 컴포넌트
const Button = styled.button`
background-color: ${({ primary }) => (primary ? 'blue' : 'gray')};
color: white;
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
&:hover {
background-color: ${({ primary }) => (primary ? 'darkblue' : 'darkgray')};
}
`;
// 컴포넌트를 사용할 때 primary prop을 전달해 스타일을 조정
function App() {
return (
<div>
<Button primary>Primary Button</Button>
<Button>Default Button</Button>
</div>
);
}
export default App;
- primary prop이 true일 때 버튼의 배경색이 blue, false일 때 gray가 됩니다.
- primary prop 값에 따라 호버 효과의 색상도 조건부로 설정됩니다.
Q8. 아래 예시에서 조건부 스타일을 적용할 때 사용하는 문법인 ${ } 의 역할에 대해 작성해 보세요. ex) background-color: ${({ primary }) => (primary ? '#3498db' : '#e74c3c')};
A : ${ }는 템플릿 리터럴 (template literal) 구문을 활용해 스타일 컴포넌트 내부에서 JavaScript 코드를 실행할 수 있게 해주는 문법입니다. styled-components에서 props나 변수 등을 활용해 조건부로 스타일을 설정할 때 유용합니다.
예제 코드에서 사용된 ${({ primary }) => (primary ? '#3498db' : '#e74c3c')}; 부분을 해석해보면 다음과 같습니다:
- ${ } 구문: CSS 속성 값 내부에서 JavaScript 표현식을 삽입할 때 사용됩니다. 템플릿 리터럴 방식으로, 변수나 함수를 스타일에 직접 넣을 수 있습니다.
- ({ primary }) => (primary ? '#3498db' : '#e74c3c') 함수: props로 전달된 값을 통해 조건부 스타일을 결정하는 함수입니다.
- 여기서 primary라는 props를 구조 분해 할당(destructuring) 방식으로 가져옵니다.
- primary가 true이면 #3498db가 반환되고, 그렇지 않으면 #e74c3c가 반환됩니다.
따라서 최종적으로 primary prop 값에 따라 background-color가 #3498db 혹은 #e74c3c로 설정됩니다.
Q9.
A : 이 코드에서 배열 상태를 관리하고 불변성을 유지하며 업데이트할 수 있게 해주는 Hook은 **useState**입니다.
useState는 React에서 상태를 관리하기 위한 기본적인 Hook으로, 상태를 업데이트할 때 새로운 상태를 설정함으로써 불변성을 유지합니다. 이 예제에서는 todos와 input이라는 두 개의 상태 변수를 useState를 사용하여 정의하였으며, setTodos와 setInput을 통해 각각의 상태를 업데이트하고 있습니다.
따라서, 이 코드에서 배열 상태(todos)를 관리하고 불변성을 유지하며 업데이트할 수 있게 만들어 주는 Hook은 **useState**입니다.
Q10. 배열 내의 객체 상태를 업데이트하는 패턴(map과 불변성 유지를 통한 방법)을 사용한 예시 코드를 하나 작성해보세요.
A : 배열 내의 객체 상태를 업데이트할 때, map을 사용하여 배열을 순회하면서 특정 조건에 맞는 객체를 변경하고, 나머지 객체는 그대로 유지하는 패턴을 사용할 수 있습니다. 이 패턴은 불변성을 유지하며 상태를 업데이트하는 데 유용합니다.
다음은 사용자 목록이 있는 배열에서 특정 사용자의 active 상태를 토글하는 예제 코드입니다.
import React, { useState } from 'react';
function UserList() {
const [users, setUsers] = useState([
{ id: 1, name: 'Alice', active: false },
{ id: 2, name: 'Bob', active: true },
{ id: 3, name: 'Charlie', active: false },
]);
const toggleActive = (id) => {
setUsers(users.map(user =>
user.id === id ? { ...user, active: !user.active } : user
));
};
return (
<div>
<h3>User List</h3>
<ul>
{users.map(user => (
<li key={user.id}>
<span style={{ color: user.active ? 'green' : 'gray' }}>
{user.name} {user.active ? '(Active)' : '(Inactive)'}
</span>
<button onClick={() => toggleActive(user.id)}>
Toggle Active
</button>
</li>
))}
</ul>
</div>
);
}
export default UserList;
- users 상태: useState를 사용해 초기 사용자 목록 배열을 설정합니다. 각 사용자 객체에는 id, name, active 속성이 있습니다.
- toggleActive 함수: 사용자 id를 인자로 받아, map을 통해 배열을 순회하면서 해당 id를 가진 사용자를 찾고, 해당 객체의 active 속성을 토글합니다.
- 조건에 맞는 객체를 업데이트할 때는 { ...user, active: !user.active }를 사용하여 기존 객체를 복사한 후, active 속성만 변경합니다. 이렇게 하면 불변성이 유지됩니다.
- 조건에 맞지 않는 객체는 그대로 반환하여 배열 내 다른 객체들이 변경되지 않도록 합니다.
- 렌더링: 각 사용자 정보와 active 상태를 화면에 표시하고, 버튼을 클릭하면 toggleActive 함수가 호출되어 해당 사용자의 active 상태가 토글됩니다.
이 패턴을 통해 배열 내 객체의 상태를 불변성을 유지하면서 안전하게 업데이트할 수 있습니다.
Q11. useEffect 는 어떤 상황에서 사용하면 좋은지 예시 코드를 떠올려 보고, 의존성 배열을 사용하는 이유와 함께 작성해주세요.
A : `useEffect`는 React에서 컴포넌트의 사이드 이펙트를 처리하기 위한 Hook으로, 컴포넌트가 렌더링된 후 특정 작업을 수행할 수 있게 해줍니다. 주로 다음과 같은 상황에서 사용됩니다:
1. **데이터 페칭**: 컴포넌트가 마운트될 때 API 호출 등을 통해 데이터를 불러올 때.
2. **구독 및 정리 작업**: WebSocket 연결이나 이벤트 리스너 등록 및 해제와 같은 작업에서.
3. **DOM 업데이트**: 컴포넌트가 렌더링된 후 DOM을 직접 조작하거나 애니메이션을 적용할 때.
### 예제 코드: 컴포넌트가 마운트될 때 데이터 페칭하기
아래 예제에서는 `useEffect`를 사용해 컴포넌트가 처음 렌더링될 때 API에서 데이터를 불러오는 작업을 수행합니다.
```javascript
import React, { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [userData, setUserData] = useState(null);
useEffect(() => {
// 데이터 페칭 함수
const fetchUserData = async () => {
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
const data = await response.json();
setUserData(data);
} catch (error) {
console.error("Error fetching user data:", error);
}
};
fetchUserData();
}, [userId]); // 의존성 배열에 userId 추가
if (!userData) return <div>Loading...</div>;
return (
<div>
<h1>{userData.name}</h1>
<p>{userData.email}</p>
</div>
);
}
export default UserProfile;
```
### 설명
1. **`useEffect` 내부의 함수**: `fetchUserData`라는 비동기 함수를 정의하고, `userId`가 변경될 때마다 이 함수를 호출하여 사용자 데이터를 불러옵니다.
2. **의존성 배열**: `[userId]`
- `useEffect`의 두 번째 매개변수인 의존성 배열에는 `userId`가 포함되어 있습니다.
- `userId`가 변경될 때마다 `useEffect`가 다시 실행되어 새로운 사용자 데이터를 가져옵니다.
- 의존성 배열이 비어 있으면(`[]`), `useEffect`는 컴포넌트가 처음 마운트될 때 한 번만 실행됩니다.
### 의존성 배열을 사용하는 이유
- **불필요한 재실행 방지**: 의존성 배열을 통해 특정 값이 변경될 때만 `useEffect`가 재실행되도록 제어할 수 있습니다. 예를 들어, `userId`가 바뀌지 않으면 데이터를 다시 가져올 필요가 없으므로, `userId`가 변경될 때만 `useEffect`가 실행되도록 합니다.
- **성능 최적화**: 필요할 때만 효과를 실행하여 불필요한 리소스 사용을 줄입니다.
- **효과적인 의존성 관리**: 의존성 배열을 통해 특정 상태나 prop에 의존성을 명시적으로 선언함으로써 React가 최적의 타이밍에 효과를 적용할 수 있도록 도와줍니다.
Q12. React 에서 비동기 함수에서 데이터를 가져오고 상태를 업데이트하려면 어떻게 하는지 간단히 작성해 보세요. (코드가 아니어도 괜찮습니다)
A : React에서 비동기 함수로 데이터를 가져와 상태를 업데이트하려면 다음 단계를 따릅니다.
1. **상태 초기화**: `useState`를 사용해 데이터를 저장할 상태를 초기화합니다. 예를 들어, `const [data, setData] = useState(null);`과 같이 설정합니다.
2. **비동기 함수 생성**: 데이터를 가져오는 비동기 함수를 `useEffect` 내부에 정의합니다. 이 함수는 `fetch`나 `axios` 등을 사용해 데이터를 요청하고, 응답을 받으면 `setData`를 호출해 상태를 업데이트합니다.
3. **`useEffect`에서 비동기 함수 호출**: `useEffect`를 사용해 컴포넌트가 마운트되거나 의존성 배열에 지정된 값이 변경될 때 비동기 함수를 호출합니다.
4. **에러 처리**: 데이터 가져오기가 실패할 경우를 대비해 `try-catch` 구문을 사용해 에러를 처리하고, 에러 상태를 업데이트하거나 콘솔에 오류 메시지를 출력하도록 합니다.
5. **로딩 상태 추가 (선택 사항)**: `useState`로 로딩 상태를 관리하여 데이터가 로드될 때는 로딩 중임을 사용자에게 표시하고, 데이터가 준비되면 이를 숨기는 방식으로 UX를 개선할 수 있습니다.
Q13. [useRef] 를 활용해 상태를 리렌더링 없이 유지하는 이유와 필요성에 대해 작성해보세요.
A : `useRef`는 React에서 특정 값이나 DOM 요소를 직접 참조할 수 있게 해주는 Hook으로, **상태를 리렌더링 없이 유지**할 때 유용하게 사용됩니다. `useRef`를 활용해 상태를 리렌더링 없이 유지하는 이유와 필요성에 대해 설명해 보겠습니다.
### 이유와 필요성
1. **리렌더링 방지**:
- `useState`를 사용해 상태를 업데이트하면 컴포넌트가 리렌더링됩니다. 그러나, 어떤 값은 단순히 컴포넌트 안에서 계속 유지될 필요만 있을 뿐, 리렌더링이 필요하지 않은 경우가 있습니다.
- 예를 들어, 타이머 ID, 이전 값 저장, 외부 API 호출의 횟수 등을 추적하는 값은 화면에 표시되지 않으므로, 리렌더링 없이 값만 유지하고 싶을 때 `useRef`를 사용하면 효율적입니다.
2. **값의 지속성 유지**:
- `useRef`로 관리하는 값은 컴포넌트가 리렌더링되어도 초기화되지 않고 계속 유지됩니다. 반면, 일반 변수는 리렌더링 시 초기화되므로 값이 사라지게 됩니다.
- 예를 들어, 폼의 특정 입력에 대한 이전 값을 비교하거나, 컴포넌트가 이전에 렌더링된 횟수를 추적하려면 리렌더링해도 값이 사라지지 않는 `useRef`가 필요합니다.
3. **DOM 요소 접근**:
- `useRef`는 DOM 요소에 직접 접근할 수 있는 방법을 제공합니다. 이를 통해 특정 DOM 요소에 포커스를 주거나, 스크롤 위치를 조정하는 등의 작업을 할 수 있습니다.
- 이러한 경우에는 상태를 업데이트하지 않고 DOM에 접근하는 것이 더 효율적이며, 불필요한 리렌더링을 방지할 수 있습니다.
### 예시 상황
예를 들어, 컴포넌트 내에서 사용자 입력을 추적하는 시간이 걸리는 비동기 작업을 수행하고 싶다고 가정해 봅시다. 이때 타이머 ID를 `useRef`로 관리하면 상태가 변경될 때마다 리렌더링을 하지 않고, 기존 타이머 ID를 추적할 수 있습니다. 이를 통해 타이머가 중복 실행되지 않게 하면서도 불필요한 리렌더링을 방지할 수 있습니다.
### 요약
- `useRef`는 리렌더링 없이 값의 지속성을 유지하고, DOM 요소에 접근하는 기능을 제공합니다.
- `useState`와 다르게 상태 변경이 컴포넌트를 리렌더링하지 않기 때문에, 필요 없는 리렌더링을 줄이고 성능을 최적화할 수 있습니다.
Q14. 전역 상태를 통해 상태를 관리하는 이유에 대해 작성해보세요.
A : React 애플리케이션에서 **전역 상태 관리**를 사용하는 이유는 여러 컴포넌트에서 공통으로 사용하는 상태를 효율적으로 관리하기 위함입니다. 전역 상태는 특정 컴포넌트에 국한되지 않고 애플리케이션 전반에서 접근 및 수정할 수 있는 상태를 의미하며, 주로 Redux, Context API, Recoil과 같은 라이브러리를 통해 구현합니다. 전역 상태 관리의 필요성과 이유는 다음과 같습니다.
### 1. **컴포넌트 간 상태 공유의 복잡성 감소**
- React는 기본적으로 컴포넌트 간의 상태 공유를 부모-자식 간의 props를 통해 전달합니다. 하지만 여러 컴포넌트에서 동일한 상태를 필요로 할 경우, props로 상태를 계속 전달하는 것은 비효율적이며 복잡해집니다.
- 전역 상태 관리를 사용하면 이러한 상태를 부모 컴포넌트 없이도 여러 컴포넌트에서 바로 접근하고 업데이트할 수 있어 코드가 훨씬 간결해지고 유지보수가 쉬워집니다.
### 2. **애플리케이션의 일관성 유지**
- 여러 컴포넌트가 동일한 상태를 사용하는 경우, 전역 상태 관리를 통해 모든 컴포넌트가 동일한 상태 값을 참조할 수 있게 됩니다. 이를 통해 애플리케이션의 일관성을 유지할 수 있으며, 어디서든 상태를 변경하면 모든 관련 컴포넌트가 최신 상태를 반영하게 됩니다.
- 예를 들어, 로그인 상태, 사용자 프로필 정보, 장바구니 정보 등의 상태는 애플리케이션 전역에서 일관된 값을 유지해야 합니다.
### 3. **코드의 가독성 및 유지보수성 향상**
- 전역 상태 관리를 통해 상태와 로직을 중앙에서 관리하면, 컴포넌트별 상태 관리 로직을 줄일 수 있어 코드가 간결해지고 가독성이 높아집니다. 또한, 상태가 필요한 컴포넌트에서 전역 상태를 불러오기만 하면 되므로 코드 재사용성이 높아지고 유지보수가 쉬워집니다.
- 상태를 한 곳에서 관리하면 버그 추적이 용이해지고, 새로운 기능 추가 시에도 수정해야 할 부분을 쉽게 찾을 수 있습니다.
### 4. **전역 상태와 비동기 작업 관리의 용이성**
- 전역 상태 관리를 통해 데이터 페칭과 같은 비동기 작업의 상태도 관리할 수 있습니다. 예를 들어, 데이터 로딩 상태나 API 응답 값을 전역 상태로 관리하면, 데이터를 사용하는 컴포넌트들이 일관된 로딩 상태와 데이터 값을 공유할 수 있습니다.
- Redux 같은 전역 상태 관리 라이브러리는 미들웨어(Redux-Saga, Redux-Thunk 등)를 통해 비동기 로직을 쉽게 관리할 수 있는 기능도 제공합니다.
### 5. **성능 최적화**
- props를 깊이 전달하는 방식으로 상태를 공유할 경우, 불필요한 리렌더링이 발생할 수 있습니다. 반면 전역 상태 관리를 사용하면 필요한 컴포넌트만 상태 변화를 감지하도록 최적화할 수 있습니다.
- React Context API는 기본적으로 모든 하위 컴포넌트를 리렌더링할 수 있지만, 일부 라이브러리는 이러한 성능 문제를 개선하기 위한 방법을 제공합니다. 이를 통해 불필요한 리렌더링을 줄이고 성능을 최적화할 수 있습니다.
### 요약
전역 상태 관리를 통해 컴포넌트 간의 상태 공유를 간단하고 일관되게 유지할 수 있으며, 코드의 유지보수성과 가독성을 높이고 성능을 최적화할 수 있습니다. 이러한 이유로 전역 상태 관리는 애플리케이션의 크기와 복잡도가 커질수록 매우 유용하게 활용됩니다.
Q15. React.memo() 와 같은 Hook을 사용하면 자식 컴포넌트의 리렌더링을 방지할 수 있는데, 그 동작 원리에 대해서 작성해보세요.
A : `React.memo()`는 React에서 컴포넌트의 불필요한 리렌더링을 방지하기 위해 사용하는 고차 컴포넌트(Higher Order Component)입니다. 주로 부모 컴포넌트가 리렌더링되더라도, **props가 변경되지 않았다면 자식 컴포넌트는 리렌더링하지 않도록** 해줍니다. 이는 성능 최적화에 유용합니다.
### `React.memo()`의 동작 원리
1. **기본 개념**:
- `React.memo()`는 컴포넌트를 메모이제이션(Memoization)하여, 동일한 props로 컴포넌트를 호출할 경우 React가 컴포넌트를 재실행하지 않고 이전에 계산된 결과를 재사용하게 합니다.
- 이를 통해 부모 컴포넌트가 리렌더링되어도 자식 컴포넌트에 전달되는 `props`가 변경되지 않았다면, 해당 자식 컴포넌트는 리렌더링하지 않습니다.
2. **props 비교**:
- `React.memo()`는 기본적으로 얕은 비교(Shallow Comparison)를 사용하여 `props`가 이전 렌더링과 동일한지 확인합니다.
- props가 이전과 같다면 메모이제이션된 컴포넌트를 반환하고, 그렇지 않으면 새로운 렌더링을 수행합니다.
- 즉, `props` 객체 내의 각 속성을 단순히 비교하여 변경 사항이 없는지 확인하는 방식으로 동작합니다.
3. **사용 예시**:
```javascript
import React from 'react';
const ChildComponent = React.memo(({ value }) => {
console.log("ChildComponent rendered");
return <div>{value}</div>;
});
export default ChildComponent;
```
이 코드에서 `ChildComponent`는 `React.memo()`로 래핑되어 있습니다. 부모 컴포넌트가 리렌더링되어도 `value` prop이 변경되지 않으면 `ChildComponent`는 리렌더링되지 않습니다.
4. **커스텀 비교 함수** (선택 사항):
- `React.memo()`는 기본적으로 얕은 비교를 수행하지만, 필요에 따라 커스텀 비교 함수를 두 번째 인자로 전달할 수 있습니다.
- 커스텀 비교 함수를 통해 복잡한 비교 로직을 추가하여 props 변경 여부를 더 세밀하게 제어할 수 있습니다.
```javascript
const ChildComponent = React.memo(
({ value }) => {
console.log("ChildComponent rendered");
return <div>{value}</div>;
},
(prevProps, nextProps) => {
// true를 반환하면 리렌더링 방지, false를 반환하면 리렌더링
return prevProps.value === nextProps.value;
}
);
```
5. **성능 최적화**:
- `React.memo()`는 주로 **정적인 컴포넌트**나, **props가 자주 변경되지 않는 컴포넌트**에 사용합니다.
- 하지만 모든 컴포넌트에 `React.memo()`를 적용하는 것이 항상 최적화로 이어지지는 않습니다. `React.memo()`는 props 변경 여부를 비교하는 오버헤드가 있기 때문에, 메모이제이션으로 얻는 이득이 비교 오버헤드를 초과할 때만 유용합니다.
### 요약
`React.memo()`는 부모 컴포넌트가 리렌더링될 때 자식 컴포넌트의 props를 얕은 비교하여 변경이 없으면 자식 컴포넌트의 리렌더링을 방지합니다. 이를 통해 불필요한 렌더링을 줄여 성능을 최적화할 수 있으며, 필요에 따라 커스텀 비교 함수를 추가하여 props의 변경 여부를 세밀하게 제어할 수 있습니다.
Q16. useCallback() 은 어떤 상황에서 사용하는지 작성해보세요.
A : `useCallback()`은 React에서 **함수의 재생성을 방지**하여 성능을 최적화하기 위해 사용하는 Hook입니다. 특히, 자식 컴포넌트에 함수를 props로 전달할 때 유용합니다. 이 Hook은 **컴포넌트가 리렌더링될 때마다 불필요하게 함수가 새로 생성되는 것을 막기 위해** 사용됩니다.
### `useCallback()`을 사용하는 상황
1. **자식 컴포넌트에 함수를 props로 전달할 때**:
- 부모 컴포넌트가 리렌더링될 때마다 함수가 새로 생성되면, 자식 컴포넌트에 전달되는 함수가 매번 새 함수로 인식됩니다.
- React는 기본적으로 props가 변경되면 자식 컴포넌트를 리렌더링하므로, 함수가 새로 생성될 때마다 자식 컴포넌트도 불필요하게 리렌더링됩니다.
- `useCallback`을 사용하면 함수의 메모이제이션을 통해 함수 참조를 유지하여, props가 변경되지 않은 경우 자식 컴포넌트의 리렌더링을 방지할 수 있습니다.
2. **함수 재생성으로 인한 성능 저하를 방지하고자 할 때**:
- 컴포넌트가 리렌더링될 때마다 함수가 다시 생성되면 성능에 영향을 미칠 수 있습니다. 특히, 복잡한 연산을 수행하는 함수나 이벤트 핸들러가 자주 생성되면 불필요한 메모리와 CPU 리소스를 소비하게 됩니다.
- `useCallback`을 사용하여 함수 재생성을 방지함으로써, 성능 최적화에 도움이 됩니다.
3. **의존성 배열을 기반으로 특정 값이 변경될 때만 함수가 재생성되도록 할 때**:
- `useCallback`을 사용하면 의존성 배열을 통해 함수가 필요할 때만 재생성되도록 제어할 수 있습니다.
- 예를 들어, 함수 내부에서 사용하는 값이 특정 상태나 prop에 의존하는 경우, 의존성 배열에 해당 상태나 prop을 포함하여 값이 변경될 때만 함수가 재생성되도록 할 수 있습니다.
### 예제 코드
```javascript
import React, { useState, useCallback } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const increment = useCallback(() => {
setCount((prevCount) => prevCount + 1);
}, []); // 의존성 배열이 비어있어 컴포넌트가 처음 렌더링될 때만 함수가 생성됩니다.
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}
```
### 요약
- `useCallback`은 **함수의 재생성을 방지**하여 불필요한 연산을 줄이고 성능을 최적화하는 데 사용됩니다.
- 주로 **자식 컴포넌트에 함수가 props로 전달될 때**, 그리고 **함수 재생성으로 인한 성능 저하를 방지할 때** 사용됩니다.
- **의존성 배열**을 통해 필요한 값이 변경될 때만 함수가 재생성되도록 제어할 수 있습니다.
'DIARY' 카테고리의 다른 글
자습...? (5) | 2024.12.02 |
---|---|
24.11.14 (1) | 2024.11.14 |
2024.11.07 (0) | 2024.11.08 |
2024.11.04 (2) | 2024.11.04 |
2024.10.24 (1) | 2024.10.24 |