안녕하세요! 오늘은 React와 Redux에 관한 학습 내용을 정리해 보려고 합니다. 컴포넌트 스타일링부터 상태 관리, 그리고 최적화까지 중요한 개념들을 다뤄볼게요.
1. CSS in JS: 스타일드 컴포넌트
React에서 스타일을 적용하는 방법은 여러 가지가 있지만, 요즘 가장 인기 있는 방식 중 하나는 CSS-in-JS입니다. 그 중에서도 styled-components는 npm 트렌드에서 Tailwind 다음으로 많이 사용되는 라이브러리입니다.
기본 사용법
yarn add styled-components
VSCode를 사용한다면 vscode-styled-components 확장 프로그램을 설치하면 편리합니다.
const StBox = styled.div`
/* 여기에 스타일 작성 */
width: 100px;
height: 100px;
background-color: #f0f0f0;
`;
Props를 활용한 동적 스타일링
const StBox = styled.div`
border: 1px solid ${(props) => props.borderColor};
`;
// 컴포넌트 사용 시
<StBox borderColor="red">
<div>스타일드 박스 내용</div>
</StBox>
2. Reset CSS
브라우저마다 기본 스타일이 다르기 때문에, 모든 브라우저에서 일관된 스타일을 보장하기 위해 Reset CSS를 사용합니다. 이는 기본 스타일을 초기화하여 스타일링을 처음부터 시작할 수 있게 해줍니다.
3. React Hooks 심화
useState
함수형 컴포넌트에서 상태를 관리하는 가장 기본적인 Hook입니다.
const [state, setState] = useState(initialState);
// 상태 업데이트
setState(newState);
// 이전 상태를 기반으로 업데이트 (함수형 업데이트)
setState(prevState => prevState + 1);
배치 업데이트 vs 함수형 업데이트
- 배치 업데이트: React는 성능 최적화를 위해 여러 setState 호출을 하나로 묶어 처리합니다.
- 함수형 업데이트: 이전 상태를 매개변수로 받아 새 상태를 계산하는 방식으로, 상태 업데이트 간의 의존성을 해결합니다.
useEffect
컴포넌트의 생명주기와 관련된 부수 효과를 처리하는 Hook입니다.
// 컴포넌트 마운트 시 한 번만 실행
useEffect(() => {
console.log("컴포넌트가 마운트되었습니다");
}, []);
// 특정 값이 변경될 때마다 실행
useEffect(() => {
console.log(`count가 ${count}로 변경되었습니다`);
}, [count]);
// 클린업 함수 (언마운트 시 실행)
useEffect(() => {
const timer = setTimeout(() => {
console.log("타이머 실행");
}, 1000);
return () => {
clearTimeout(timer); // 컴포넌트 언마운트 시 타이머 정리
};
}, []);
useRef
렌더링과 무관하게 값을 저장할 수 있는 Hook입니다. DOM 요소에 직접 접근할 때도 사용됩니다.
// 값 저장 용도
const countRef = useRef(0);
countRef.current++; // 렌더링을 발생시키지 않음
// DOM 요소 접근 용도
const inputRef = useRef(null);
useEffect(() => {
// 컴포넌트 마운트 시 input에 자동 포커스
inputRef.current.focus();
}, []);
return <input type="text" ref={inputRef} />;
useContext
컴포넌트 트리 깊숙이 props를 전달하지 않고도 상태를 공유할 수 있는 방법입니다.
// Context 생성
const ThemeContext = createContext();
// Provider 컴포넌트 내부에서 상태 제공
function App() {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<MainComponent />
</ThemeContext.Provider>
);
}
// 자식 컴포넌트에서 Context 사용
function Button() {
const { theme } = useContext(ThemeContext);
return <button className={theme}>테마 적용 버튼</button>;
}
주의점
- Provider에서 제공하는 값이 변경되면 해당 Context를 구독하는 모든 컴포넌트가 리렌더링됩니다.
4. 리렌더링과 최적화
리렌더링 발생 조건
- state가 변경되었을 때
- props가 변경되었을 때
- 부모 컴포넌트가 리렌더링될 때 (자식 컴포넌트도 함께 리렌더링)
최적화 방법
React.memo
컴포넌트를 메모이제이션하여, props가 변경되지 않으면 불필요한 리렌더링을 방지합니다.
function MyComponent(props) {
// 컴포넌트 로직
}
export default React.memo(MyComponent);
useCallback
함수를 메모이제이션하여, 의존성이 변경되지 않는 한 동일한 함수 참조를 유지합니다.
const handleClick = useCallback(() => {
console.log('버튼 클릭됨');
}, []); // 빈 의존성 배열: 함수가 재생성되지 않음
// count가 변경될 때만 함수 재생성
const resetCount = useCallback(() => {
setCount(0);
}, [count]);
useMemo
계산 비용이 큰 값을 메모이제이션하여 불필요한 재계산을 방지합니다.
const expensiveValue = useMemo(() => {
return computeExpensiveValue(a, b);
}, [a, b]); // a나 b가 변경될 때만 재계산
5. 커스텀 훅
로직을 재사용하기 위해 자신만의 Hook을 만들 수 있습니다. 커스텀 Hook은 이름이 use로 시작해야 합니다.
function useWindowSize() {
const [size, setSize] = useState({ width: 0, height: 0 });
useEffect(() => {
const handleResize = () => {
setSize({
width: window.innerWidth,
height: window.innerHeight
});
};
window.addEventListener('resize', handleResize);
handleResize(); // 초기값 설정
return () => window.removeEventListener('resize', handleResize);
}, []);
return size;
}
// 사용 예시
function MyComponent() {
const { width, height } = useWindowSize();
return <div>화면 크기: {width} x {height}</div>;
}
6. Redux: 전역 상태 관리
Redux는 애플리케이션의 상태를 중앙에서 관리하기 위한 라이브러리입니다. 특히 컴포넌트 간의 상태 공유가 복잡해질 때 유용합니다.
Redux를 사용하는 이유
- 성능 최적화: 필요한 컴포넌트만 리렌더링
- 상태 로직의 중앙화와 일관성: 예측 가능한 상태 관리
- 강력한 미들웨어와 개발 도구: 디버깅 및 확장성
기본 설치
yarn add redux react-redux
기본 구성 요소
- Action: 상태 변경을 설명하는 객체
- Reducer: Action을 처리하고 새 상태를 반환하는 함수
- Store: 애플리케이션의 전체 상태를 보관
- Dispatch: Action을 Store로 보내는 함수
예시: 카운터 모듈
// src/redux/modules/counter.js
const initialState = {
number: 0,
};
// 액션 타입 정의
const PLUS_ONE = "counter/PLUS_ONE";
const ADD_NUMBER = "counter/ADD_NUMBER";
// 액션 생성자
export const plusOne = () => {
return {
type: PLUS_ONE,
};
};
export const addNumber = (payload) => {
return {
type: ADD_NUMBER,
payload,
};
};
// 리듀서
const counter = (state = initialState, action) => {
switch (action.type) {
case PLUS_ONE:
return {
number: state.number + 1
};
case ADD_NUMBER:
return {
number: state.number + action.payload
};
default:
return state;
}
};
export default counter;
컴포넌트에서 사용하기
import { useDispatch, useSelector } from 'react-redux';
import { plusOne, addNumber } from '../redux/modules/counter';
function Counter() {
const dispatch = useDispatch();
const number = useSelector(state => state.counter.number);
return (
<div>
<h1>현재 카운트: {number}</h1>
<button onClick={() => dispatch(plusOne())}>+1</button>
<button onClick={() => dispatch(addNumber(5))}>+5</button>
</div>
);
}
7. Redux Toolkit (RTK)
Redux Toolkit은 Redux의 보일러플레이트 코드를 줄이고 더 효율적으로 사용할 수 있게 해주는 공식 도구입니다.
설치
yarn add @reduxjs/toolkit
createSlice API
// src/redux/slices/counterSlice.js
import { createSlice } from '@reduxjs/toolkit';
const initialState = {
number: 0,
};
const counterSlice = createSlice({
name: "counter",
initialState,
reducers: {
addNumber: (state, action) => {
state.number = state.number + action.payload;
},
minusNumber: (state, action) => {
state.number = state.number - action.payload;
},
},
});
// 액션 생성자 내보내기
export const { addNumber, minusNumber } = counterSlice.actions;
// 리듀서 내보내기
export default counterSlice.reducer;
configureStore
// src/redux/config/configStore.js
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "../slices/counterSlice";
import todosReducer from "../slices/todosSlice";
const store = configureStore({
reducer: {
counter: counterReducer,
todos: todosReducer
},
});
export default store;
8. React Router DOM
리액트에서 페이지 이동을 구현하기 위한 라이브러리입니다.
설치
yarn add react-router-dom
기본 라우터 설정
// src/shared/Router.jsx
import React from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Home from "../pages/Home";
import About from "../pages/About";
import Contact from "../pages/Contact";
const Router = () => {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
</Routes>
</BrowserRouter>
);
};
export default Router;
useNavigate: 프로그래밍 방식 페이지 이동
import { useNavigate } from 'react-router-dom';
function NavigationButtons() {
const navigate = useNavigate();
return (
<div>
<button onClick={() => navigate('/about')}>소개 페이지로</button>
<button onClick={() => navigate('/contact')}>연락처 페이지로</button>
<button onClick={() => navigate(-1)}>뒤로 가기</button>
</div>
);
}
useLocation: 현재 URL 정보 가져오기
import { useLocation } from 'react-router-dom';
function CurrentPath() {
const location = useLocation();
return (
<div>
<p>현재 경로: {location.pathname}</p>
</div>
);
}
Link 컴포넌트: 페이지 이동 링크
일반 <a> 태그는 페이지를 새로고침하므로, SPA에서는 Link 컴포넌트를 사용합니다.
import { Link } from 'react-router-dom';
function Navigation() {
return (
<nav>
<ul>
<li><Link to="/">홈</Link></li>
<li><Link to="/about">소개</Link></li>
<li><Link to="/contact">연락처</Link></li>
</ul>
</nav>
);
}
마치며
이상으로 React와 Redux에 관한 주요 개념과 기술을 정리해보았습니다. 이 내용들을 잘 숙지하고 실제 프로젝트에 적용한다면, 더 효율적이고 유지보수하기 좋은 React 애플리케이션을 만들 수 있을 것입니다.
React 학습은 끝이 없는 여정이지만, 꾸준히 공부하고 새로운 기술을 시도해보면서 계속 성장해 나가면 좋겠습니다!
'프론트 JS 뿌시기' 카테고리의 다른 글
| Tailwind CSS 테일윈드 V4 업데이트 (1) | 2025.05.27 |
|---|---|
| 프론트 js 처음부터 뿌시기 5월 - 1 <React 기초 정리> (2) | 2025.05.11 |
| 프론트 js 처음부터 뿌시기 4월 - 2 <JavaScript 기본 문법과 함수 정리> (0) | 2025.05.11 |
| 프론트 js 처음부터 뿌시기 4월 - 1 (1) | 2025.04.26 |