2024.06.15 - [Programming Language/React] - [React] React 개발을 하며 지키면 좋을 것들 (3)
2024.06.13 - [Programming Language/React] - [React] React 개발을 하며 지키면 좋을 것들 (1)
2024.06.09 - [Programming Language/Javascript] - [JS] 효율적이고 효과적인 자바스크립트 개발을 위한 45가지 팁(2)
위 글은 50 React.js Super Hacks Every Developer Should Know를 번역 겸 약간의 각색을 곁들인 글로,
팁이 50가지나 되는 만큼 한 글에 담기보단 10개씩 5번으로 나눠서 포스팅할 예정이다.
이 글은 5번째 중 1번째 글이다.
1. Component Reusability (컴포넌트 재사용성)
문제: 여러 개의 버튼을 만들어야 하는데, 같은 버튼 코드를 반복 작성하면 버그가 발생할 가능성이 높아지고 코드 유지보수가 어려워집니다.
해결책: 컴포넌트 재사용성을 높이면 일관성을 유지하고 유지보수를 쉽게 할 수 있습니다.
// PROBLEM: 반복되는 버튼 코드
const SaveButton = () => <button onClick={handleSave}>Save</button>;
const CancelButton = () => <button onClick={handleCancel}>Cancel</button>;
// SOLUTION: 재사용 가능한 버튼 컴포넌트
const Button = ({ label, onClick }) => (
<button onClick={onClick}>{label}</button>
);
// 사용 예시
<Button label="Save" onClick={handleSave} />
<Button label="Cancel" onClick={handleCancel} />
2. Default Props (기본 Props 설정)
문제: 컴포넌트가 모든 필요한 props를 받지 못했을 때, 예상치 못한 동작이나 오류가 발생할 수 있습니다.
해결책: 기본 props를 설정하면 props가 누락되었을 때도 컴포넌트가 올바르게 동작할 수 있습니다.
// PROBLEM: props가 없을 때 컴포넌트가 올바르게 동작하지 않음
const Button = ({ label, onClick }) => (
<button onClick={onClick}>{label}</button>
);
// 사용 예시
<Button onClick={handleClick} /> // label이 undefined로 표시됨
// SOLUTION: 기본 props 설정
const Button = ({ label, onClick }) => (
<button onClick={onClick}>{label}</button>
);
Button.defaultProps = {
label: 'Click me',
};
// 사용 예시
<Button onClick={handleClick} /> // "Click me" 라벨이 있는 버튼이 렌더링됨
3. Prop Types (프롭 타입 검사)
문제: 컴포넌트에 잘못된 타입의 props가 전달되면 예기치 않은 동작과 버그가 발생할 수 있습니다.
해결책: 프롭 타입 검사는 잘못된 타입의 props로 인한 버그를 조기에 발견할 수 있게 도와줍니다.
// PROBLEM: 잘못된 타입의 props가 전달될 때 발생하는 문제
const Button = ({ label, onClick }) => (
<button onClick={onClick}>{label}</button>
);
// 사용 예시
<Button label={42} onClick={handleSave} /> // 숫자 타입의 label로 인해 오류 발생 가능
// SOLUTION: PropTypes를 사용한 타입 검사
import PropTypes from 'prop-types';
const Button = ({ label, onClick }) => (
<button onClick={onClick}>{label}</button>
);
Button.propTypes = {
label: PropTypes.string,
onClick: PropTypes.func.isRequired,
};
// 사용 예시
<Button label="Save" onClick={handleSave} /> // 경고 없음
<Button label={42} onClick={handleSave} /> // 콘솔에 경고: Invalid prop `label` of type `number` supplied to `Button`, expected `string`.
4. Functional Components with Hooks (훅을 사용한 함수형 컴포넌트)
문제: 클래스 컴포넌트는 간단한 컴포넌트에도 불필요하게 장황할 수 있습니다.
해결책: 프롭 타입 검사는 잘못된 타입의 props로 인한 버그를 조기에 발견할 수 있게 도와줍니다.
// PROBLEM: 간단한 상태 관리를 위해 클래스 컴포넌트 사용
class Counter extends React.Component {
state = { count: 0 };
handleClick = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={this.handleClick}>Click me</button>
</div>
);
}
}
// SOLUTION: 훅을 사용한 함수형 컴포넌트
// 함수형 컴포넌트와 훅을 사용하면 코드가 간결하고 가독성이 높아집니다.
import React, { useState } from 'react';
const Counter = () => {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
};
// 사용 예시
<Counter />
5. Custom Hooks (커스텀 훅)
문제: 여러 컴포넌트에서 동일한 상태 관리 로직을 복사하여 사용하면 코드 중복과 유지보수 문제가 발생합니다.
해결책: 커스텀 훅을 사용하면 상태 관리 로직을 쉽게 재사용할 수 있습니다.
// PROBLEM: 여러 컴포넌트에서 동일한 로직을 중복해서 사용
const ComponentA = () => {
const [data, setData] = useState(null);
useEffect(() => {
fetch('https://api.example.com/data')
.then((response) => response.json())
.then((data) => setData(data));
}, []);
return <div>{data ? data.title : 'Loading…'}</div>;
};
const ComponentB = () => {
const [data, setData] = useState(null);
useEffect(() => {
fetch('https://api.example.com/data')
.then((response) => response.json())
.then((data) => setData(data));
}, []);
return <div>{data ? data.title : 'Loading…'}</div>;
};
// SOLUTION: 커스텀 훅을 사용하여 로직 재사용
import { useState, useEffect } from 'react';
const useFetch = (url) => {
const [data, setData] = useState(null);
useEffect(() => {
fetch(url)
.then((response) => response.json())
.then((data) => setData(data));
}, [url]);
return data;
};
// 사용 예시
const ComponentA = () => {
const data = useFetch('https://api.example.com/data');
return <div>{data ? data.title : 'Loading…'}</div>;
};
const ComponentB = () => {
const data = useFetch('https://api.example.com/data');
return <div>{data ? data.title : 'Loading…'}</div>;
};
6. Context API (컨텍스트 API)
문제: 깊게 중첩된 컴포넌트 트리에서 데이터를 전달할 때, 모든 중간 컴포넌트에 props를 넘겨주는 것은 번거롭고 유지보수에 어려움을 줍니다.
해결책: Context API를 사용하면 중첩된 컴포넌트 트리에서 props를 전달할 필요 없이 데이터를 공유할 수 있습니다.
// PROBLEM: 중첩된 컴포넌트에서 props를 계속 전달해야 함
const Grandchild = ({ theme }) => <div className={`theme-${theme}`}>Grandchild</div>;
const Child = ({ theme }) => <Grandchild theme={theme} />;
const Parent = ({ theme }) => <Child theme={theme} />;
const App = () => <Parent theme="dark" />;
// SOLUTION: Context API를 사용하여 전역 상태 관리
import React, { createContext, useContext } from 'react';
const ThemeContext = createContext('light');
const ThemeProvider = ({ children }) => {
return (
<ThemeContext.Provider value="dark">{children}</ThemeContext.Provider>
);
};
const ThemedComponent = () => {
const theme = useContext(ThemeContext);
return <div className={`theme-${theme}`}>Themed Component</div>;
};
// 사용 예시
<ThemeProvider>
<ThemedComponent />
</ThemeProvider>
7. Error Boundaries (에러 경계)
문제: 앱의 일부에서 오류가 발생하면 전체 애플리케이션이 크래시되어 사용자가 앱의 다른 부분을 사용할 수 없게 됩니다.
해결책: 에러 경계를 사용하면 특정 컴포넌트 트리에서 발생하는 오류를 포착하고, 전체 애플리케이션이 크래시되는 것을 방지할 수 있습니다.
// PROBLEM: 앱의 오류로 인해 전체 애플리케이션이 크래시됨
//// 클래스형
class MyComponent extends React.Component {
render() {
if (this.props.hasError) {
throw new Error('An error occurred!');
}
return <div>My Component</div>;
}
}
//// 함수형
const MyComponent = ({ hasError }) => {
if (hasError) {
throw new Error('An error occurred!');
}
return <div>My Component</div>;
}
const App = () => <MyComponent hasError={true} />; // 오류 발생으로 전체 애플리케이션 크래시
// SOLUTION: 에러 경계를 사용하여 오류를 포착하고 대체 UI를 표시
//// 클래스형
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// 오류 로깅 서비스에 로그
console.log(error, errorInfo);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
//// 함수형
const ErrorBoundary = ({ children }) => {
const [hasError, setHasError] = useState(false);
const handleError = (error, errorInfo) => {
console.log(error, errorInfo);
setHasError(true);
};
useEffect(() => {
const handleErrorEvent = (error) => {
handleError(error.error);
};
window.addEventListener('error', handleErrorEvent);
return () => {
window.removeEventListener('error', handleErrorEvent);
};
}, []);
if (hasError) {
return <h1>Something went wrong.</h1>;
}
return children;
};
const MyComponent = ({ hasError }) => {
if (hasError) {
throw new Error('An error occurred!');
}
return <div>My Component</div>;
};
// 사용 예시
<ErrorBoundary>
<MyComponent hasError={true} />
</ErrorBoundary>
8. Memoization (메모이제이션)
문제: 컴포넌트가 props나 상태가 변경되지 않았음에도 불필요하게 다시 렌더링됩니다.
해결책:React.memo를 사용하여 불필요한 리렌더링을 방지하고 성능을 최적화할 수 있습니다.
// PROBLEM: 불필요한 리렌더링 발생
const MyComponent = ({ value }) => {
console.log('Rendering MyComponent');
return <div>{value}</div>;
};
// 사용 예시
<MyComponent value="Hello" /> // 상태나 props가 변경되지 않아도 리렌더링
// SOLUTION: React.memo를 사용하여 불필요한 리렌더링 방지
const MyComponent = React.memo(({ value }) => {
console.log('Rendering MyComponent');
return <div>{value}</div>;
});
// 사용 예시
<MyComponent value="Hello" /> // `value`가 변경될 때만 리렌더링
9. useCallback and useMemo (useCallback과 useMemo 사용)
문제: 렌더링 중에 생성된 인라인 함수나 복잡한 계산이 불필요하게 다시 생성되거나 수행되어 성능 저하를 일으킵니다.
해결책: useCallback과 useMemo를 사용하면 인라인 함수와 계산을 메모이제이션하여 성능을 최적화할 수 있습니다.
// PROBLEM: 인라인 함수와 복잡한 계산이 매번 다시 생성됨
const MyComponent = ({ a, b }) => {
const handleClick = () => {
console.log('Clicked!');
};
const expensiveValue = a + b; // 예시: 간단한 계산
return (
<div>
<button onClick={handleClick}>Click me</button>
<div>{expensiveValue}</div>
</div>
);
};
// 사용 예시
<MyComponent a={1} b={2} />
// SOLUTION: useCallback과 useMemo를 사용하여 함수와 값을 메모이제이션
import React, { useCallback, useMemo } from 'react';
const computeExpensiveValue = (a, b) => {
// 복잡한 계산
return a + b;
};
const MyComponent = ({ a, b }) => {
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
const memoizedCallback = useCallback(() => {
console.log('Clicked!');
}, []);
return (
<div>
<button onClick={memoizedCallback}>Click me</button>
<div>{memoizedValue}</div>
</div>
);
};
// 사용 예시
<MyComponent a={1} b={2} />
10. Lazy Loading (지연 로딩)
문제: 큰 컴포넌트를 한 번에 모두 로딩하면 초기 렌더링 속도가 느려집니다.
해결책: 지연 로딩을 사용하면 컴포넌트를 필요한 시점에 로딩하여 초기 렌더링 속도를 개선할 수 있습니다.
// PROBLEM: 모든 컴포넌트를 한 번에 로딩하여 초기 렌더링 속도 저하
import MyComponent from './MyComponent';
const App = () => (
<div>
<MyComponent />
</div>
);
// 사용 예시
<App />
// SOLUTION: React의 lazy와 Suspense를 사용하여 컴포넌트를 지연 로딩
import React, { lazy, Suspense } from 'react';
const LazyComponent = lazy(() => import('./LazyComponent'));
const App = () => (
<Suspense fallback={<div>Loading…</div>}>
<LazyComponent />
</Suspense>
);
// 사용 예시
<App />
관련 글
2024.06.14 - [Programming Language/React] - [React] React 개발을 하며 지키면 좋을 것들 (2)
[React] React 개발을 하며 지키면 좋을 것들 (2)
위 글은 50 React.js Super Hacks Every Developer Should Know를 번역 겸 약간의 각색을 곁들인 글로,팁이 50가지나 되는 만큼 한 글에 담기보단 10개씩 5번으로 나눠서 포스팅할 예정이다.이 글은 5번째
juniortunar.tistory.com
2024.06.15 - [Programming Language/React] - [React] React 개발을 하며 지키면 좋을 것들 (3)
[React] React 개발을 하며 지키면 좋을 것들 (3)
위 글은 50 React.js Super Hacks Every Developer Should Know를 번역 겸 약간의 각색을 곁들인 글로,이 글은 5번째 중 3번째 글이다.21. Component Composition (컴포넌트 구성)문제: 복잡한 컴포넌트를 단순한 컴포넌
juniortunar.tistory.com
2024.06.16 - [Programming Language/React] - [React] React 개발을 하며 지키면 좋을 것들 (4)
[React] React 개발을 하며 지키면 좋을 것들 (4)
위 글은 50 React.js Super Hacks Every Developer Should Know를 번역 겸 약간의 각색을 곁들인 글로,이 글은 5번째 중 4번째 글이다.31. Custom Hooks for Fetching Data (데이터 가져오기 위한 커스텀 훅)문제: 여러 컴포
juniortunar.tistory.com
2024.06.17 - [Programming Language/React] - [React] React 개발을 하며 지키면 좋을 것들 (5)
[React] React 개발을 하며 지키면 좋을 것들 (5)
위 글은 50 React.js Super Hacks Every Developer Should Know를 번역 겸 약간의 각색을 곁들인 글로,이 글은 5번째 중 5번째(마지막) 글이다.41. Static Site Generation (SSG) with Next.js (Next.js를 사용한 정적 사이트 생
juniortunar.tistory.com