React의 핵심 개념
React를 시작하는 개발자들을 위한 핵심 개념들을 정리해보았습니다. react의 핵심 개념을 이해하고, 실제 프로젝트에 적용해보면서 경험을 쌓는 것이 중요합니다.
1. React란?
React는 페이스북에서 개발한 UI 라이브러리로, 사용자 인터페이스를 만들기 위한 JavaScript 라이브러리입니다.
리액트는 하나의 html 페이지만 존재하는 웹사이트(웹애플리케이션)으로 다른 컨텐츠 페이지를 불러들어오는 구조입니다.
이미지출처: 소플의 리액트
- 설치
node.js 설치 후, 터미널에서 아래 명령어로 설치합니다.
npx create-react-app@latest ./
- 실행
npm start
1. 컴포넌트 (Components)
React의 가장 기본이 되는 개념으로, UI를 구성하는 블록입니다.
재사용 가능하며, 독립적으로 동작합니다.
- 함수형 컴포넌트 예시
- 함수형 컴포넌트는 순수 함수로, 입력값(props)에 따라 UI를 반환합니다.
- 함수형 컴포넌트는 간단하고 가독성이 좋으며, React Hooks를 사용할 수 있습니다.
- 클래스 컴포넌트보다 성능이 좋기 때문에 최근에는 함수형 컴포넌트를 권장합니다.
function SignupForm(props) {
return <h1>Hello, {props.name}</h1>;
}
function App() {
return <SignupForm name="odada" />;
}
export default App;
- 클래스형 컴포넌트 예시
- 클래스형 컴포넌트는
React.Component
클래스를 상속받아 구현합니다. render
메서드를 구현하여 UI를 반환합니다.this.props
로 부모 컴포넌트에서 전달받은 데이터를 사용할 신₩ 있습니다.
class SignupForm extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
class App extends React.Component {
render() {
return <SignupForm name="odada" />;
}
}
export default App;
2. JSX
JavaScript XML의 약자로, React에서 UI를 표현할 때 사용하는 문법입니다.
- JSX 규칙
- 반드시 하나의 부모 요소로 감싸져야 함
- 모든 태그는 닫혀있어야 함
- JavaScript 표현식은 중괄호
{}
안에 작성
const element = ({ name }) => {
return (
<>
<h1>Hello! {name}</h1>
<p>Good to see you here.</p>
</>
);
};
- JSX 사용 예시
function App() {
return (
<ul>
<NewsItem />
<NewsItem />
<NewsItem />
</ul>
);
}
function NewsItem() {
return (
<li>
<strong>제목 1</strong>
<p>내용 1</p>
<LikeButton />
</li>
);
}
function LikeButton() {
return <button>좋아요</button>;
}
export default App;
3. Props
컴포넌트 간에 데이터를 전달하는 방법입니다.
- Props의 특징
- 읽기 전용 (불변성)
- 부모에서 자식으로만 전달 가능
- 객체, 함수 등 모든 JavaScript 값을 전달할 수 있음
// 부모 컴포넌트
function ParentComponent() {
return <ChildComponent message="Hello from parent" />;
}
// 자식 컴포넌트
function ChildComponent(props) {
return <div>{props.message}</div>;
}
- Props 사용 예시
function App() {
return (
<ul>
<NewsItem
title="제목1"
content="내용1"
/>
<NewsItem
title="제목2"
content="내용2"
/>
<NewsItem
title="제목3"
content="내용3"
/>
</ul>
);
}
function NewsItem({ title, content }) {
return (
<li>
<strong>{title}</strong>
<p>{content}</p>
<LikeButton />
</li>
);
}
function LikeButton() {
return <button>좋아요</button>;
}
export default App;
4. 이벤트 처리
React의 이벤트는 카멜케이스를 사용하며, JSX에 함수로 전달합니다.
function handleClick(e) {
e.preventDefault();
console.log('버튼이 클릭되었습니다.');
}
return <button onClick={handleClick}>Click me</button>;
5. 조건부 렌더링
조건에 따라 다른 내용을 렌더링하는 방법입니다.
function Greeting({ isLogin }) {
return isLogin ? <p>로그인되었습니다.</p> : <p>로그인이 필요합니다.</p>;
}
6. Hooks
함수형 컴포넌트에서 상태 관리와 생명주기 기능을 사용할 수 있게 해주는 기능입니다.
- 주요 Hook들
useState
- 상태를 추가하고, 상태를 변경할 수 있게 해줍니다.
const [state, setState] = useState(initialState);
useEffect
- 컴포넌트가 렌더링될 때, 업데이트될 때, 언마운트될 때 등 특정 시점에 작업을 수행할 수 있게 해줍니다.
useEffect(() => {
// 컴포넌트가 마운트될 때 실행
return () => {
// 클린업 함수
};
}, [dependencies]);
useContext
- 컴포넌트 트리 전체에서 전역적으로 사용할 수 있는 값을 공유할 수 있게 해줍니다.
const value = useContext(MyContext);
useRef
- DOM 요소에 직접 접근하거나, 컴포넌트의 인스턴스 변수를 사용할 수 있게 해줍니다.
const inputRef = useRef();
useMemo
- 연산량이 많은 함수의 반환값을 캐싱하여 성능을 최적화할 수 있게 해줍니다.
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
7. State
컴포넌트 내부에서 관리되는 데이터입니다.
- State 사용 규칙
- setState로만 값을 변경해야 함
- 비동기적으로 처리됨
- 이전 상태를 기반으로 업데이트할 때는 함수형 업데이트 사용
function Counter() {
const [count, setCount] = useState(0);
// const result = useState(0);
// const count = result[0];
// const setCount = result[1];
return (
<div>
<p>현재 카운트: {count}</p>
<button onClick={() => setCount(count + 1)}>증가</button>
</div>
);
}
- props 사용 예시
function App() {
return (
<ul>
<NewsItem
title="제목1"
content="내용1"
/>
<NewsItem
title="제목2"
content="내용2"
/>
<NewsItem
title="제목3"
content="내용3"
/>
</ul>
);
}
function NewsItem({ title, content }) {
return (
<li>
<strong>{title}</strong>
<p>{content}</p>
<LikeButton />
</li>
);
}
function LikeButton() {
const [like, setLike] = useState(false);
return <button onClick={() => setLike(prevLike => !prevLike)}>{like ? '좋아요 취소' : '좋아요'}</button>;
}
export default App;
8. 생명주기 (Lifecycle)
컴포넌트의 생성부터 소멸까지의 과정을 말합니다.
- 주요 생명주기
- 마운트 (Mount) : 컴포넌트가 생성되어 DOM에 추가될 때
- 업데이트 (Update) : 컴포넌트의 상태나 props가 변경될 때
- 언마운트 (Unmount) : 컴포넌트가 DOM에서 제거될 때
function LifecycleComponent() {
useEffect(() => {
console.log('컴포넌트 마운트');
return () => {
console.log('컴포넌트 언마운트');
};
}, []);
useEffect(() => {
console.log('컴포넌트 업데이트');
});
return <div>컴포넌트 생명주기</div>;
}
export default LifecycleComponent;
- 배경색 변경 예시
import React, { useState, useEffect } from 'react';
function BackgroundColor({ color }) {
useEffect(() => {
document.body.style.backgroundColor = color; // 마운트 시 배경색 변경
return () => {
document.body.style.backgroundColor = ''; // 언마운트 시 배경색 초기화
};
}, [color]); // color 값이 변경될 때만 실행
return null;
}
function App() {
const [color, setColor] = useState('yellow');
return (
<div>
<BackgroundColor color={color} />
<button onClick={() => setColor('red')}>빨간색</button>
<button onClick={() => setColor('blue')}>파란색</button>
<button onClick={() => setColor('green')}>초록색</button>
</div>
);
}
export default App;
9. 리스트와 키
배열 데이터를 렌더링할 때는 각 항목을 구분할 수 있는 key가 필요합니다.
const mockupNews = [
{ id: 1, title: '제목1', content: '내용1' },
{ id: 2, title: '제목2', content: '내용2' },
{ id: 3, title: '제목3', content: '내용3' },
];
function NewsList() {
return (
<ul>
{mockupNews.map(news => (
<li key={news.id}>
<strong>{news.title}</strong>
<p>{news.content}</p>
</li>
))}
</ul>
);
}
10. useContext
컴포넌트 트리 전체에서 전역적으로 사용할 수 있는 값을 공유할 수 있게 해줍니다.
const MyContext = React.createContext(defaultValue);
function App() {
return (
<MyContext.Provider value={/* 어떤 값 */}>
<ChildComponent />
</MyContext.Provider>
);
}
function ChildComponent() {
const value = useContext(MyContext);
return <div>{value}</div>;
}
- 뉴스 useContext 사용 예시
// context/NewsContext.js
import React from 'react';
const NewsContext = React.createContext();
export default NewsContext;
// data/mockupNews.js
const mockupNews = [
{ id: 1, title: '제목1', content: '내용1' },
{ id: 2, title: '제목2', content: '내용2' },
{ id: 3, title: '제목3', content: '내용3' },
];
export default mockupNews;
// components/NewsItem.js
import React, { useContext } from 'react';
import NewsContext from '../context/NewsContext';
function NewsItem() {
const news = useContext(NewsContext);
return (
<li>
<strong>{news.title}</strong>
<p>{news.content}</p>
</li>
);
}
export default NewsItem;
// App.js
import React from 'react';
import NewsContext from './context/NewsContext';
import mockupNews from './data/mockupNews';
import NewsItem from './components/NewsItem';
function App() {
return (
<NewsContext.Provider value={mockupNews}>
<ul>
<NewsItem />
<NewsItem />
<NewsItem />
</ul>
</NewsContext.Provider>
);
}
export default App;
- Recoil 사용 예시
// atoms/newsAtom.js
import { atom } from 'recoil';
export const newsState = atom({
key: 'newsState',
default: [
{ id: 1, title: '제목1', content: '내용1' },
{ id: 2, title: '제목2', content: '내용2' },
{ id: 3, title: '제목3', content: '내용3' },
],
});
// components/NewsItem.js
import React from 'react';
import { useRecoilValue } from 'recoil';
import { newsState } from '../atoms/newsAtom';
function NewsItem({ id }) {
const news = useRecoilValue(newsState).find(item => item.id === id);
return (
<li>
<strong>{news.title}</strong>
<p>{news.content}</p>
</li>
);
}
export default NewsItem;
// App.js
import React from 'react';
import { RecoilRoot } from 'recoil';
import NewsItem from './components/NewsItem';
function App() {
return (
<RecoilRoot>
<ul>
<NewsItem id={1} />
<NewsItem id={2} />
<NewsItem id={3} />
</ul>
</RecoilRoot>
);
}
export default App;
11. API 통신
실무에서는 API 통신을 위한 코드를 별도의 파일이나 폴더로 분리하여 관리하는 것이 일반적입니다. 이렇게 하면 코드의 재사용성과 유지보수성이 높아집니다. 예를 들어, data
폴더를 만들어 API 통신 관련 코드를 작성할 수 있습니다.
https://newsapi.org/ 에서 API 키를 발급받아 사용할 수 있습니다.
- API 통신 예시
data
폴더를 만들고,api.js
파일을 생성합니다.
// data/api.js
import axios from 'axios';
export const fetchNews = async () => {
try {
const response = await axios.get('https://jsonplaceholder.typicode.com/posts');
return response.data;
} catch (error) {
console.error('Error fetching news:', error);
throw error;
}
};
- 뉴스 컴포넌트에서 API 호출
// .env
NEWS_API_KEY = YOUR_API_KEY;
// data/api.js
import axios from 'axios';
const API_URL = `https://newsapi.org/v2/top-headlines?country=kr&apiKey=${process.env.NEWS_API_KEY}`;
export const fetchNews = async () => {
try {
const response = await axios.get(API_URL);
return response.data.articles;
} catch (error) {
console.error('Error fetching news:', error);
throw error;
}
};
// components/NewsList.js
import React, { useEffect } from 'react';
import { useRecoilState } from 'recoil';
import { newsState } from '../atoms/newsAtom';
import { fetchNews } from '../data/api';
function NewsList() {
const [news, setNews] = useRecoilState(newsState);
useEffect(() => {
const getNews = async () => {
try {
const articles = await fetchNews();
setNews(articles);
} catch (error) {
console.error('Error fetching news:', error);
}
};
getNews();
}, [setNews]);
return (
<ul>
{news.map(item => (
<li key={item.id}>
<strong>{item.title}</strong>
<p>{item.description}</p>
</li>
))}
</ul>
);
}
export default NewsList;
// App.js
import React from 'react';
import { RecoilRoot } from 'recoil';
import NewsList from './components/NewsList';
function App() {
return (
<RecoilRoot>
<NewsList />
</RecoilRoot>
);
}
export default App;
12. Virtual DOM
React가 실제 DOM 업데이트를 효율적으로 처리하기 위해 사용하는 개념입니다.
- 실제 DOM과 가상의 DOM을 비교
- 변경된 부분만 실제 DOM에 적용
- 성능 최적화에 도움
실무에서 알면 좋은 추가 개념들
1. 상태 관리 라이브러리
- Redux : 가장 많이 사용되는 상태 관리 라이브러리
- Recoil : Facebook에서 만든 상태 관리 라이브러리
- MobX : 상태 관리 라이브러리
- Zustand : 간단하고 빠른 상태 관리 라이브러리
2. 라우팅
- React Router : React에서 라우팅을 구현할 수 있는 라이브러리
- Next.js : React 프레임워크로, SSR(Server Side Rendering)을 지원
import { BrowserRouter, Route, Switch } from 'react-router-dom';
3. 스타일링
- styled-components :
- Emotion
- Tailwind CSS
- CSS Modules
4. 성능 최적화
const MemoizedComponent = React.memo(function MyComponent(props) {
// ... 컴포넌트 로직
});
5. Custom Hooks 작성
- 커스텀 로직을 재사용할 수 있게 해주는 기능입니다.
function useCustomHook() {
const [value, setValue] = useState(null);
// ... 커스텀 로직
return value;
}
6. 폼 핸들링
- React Hook Form
- Formik
- Yup (유효성 검사)
7. API 통신
- axios
- React Query (TanStack Query)
- SWR
import { useState, useEffect } from 'react';
import axios from 'axios';
function PostList() {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(false);
// 게시글 목록 조회
useEffect(() => {
const fetchPosts = async () => {
try {
setLoading(true);
const response = await axios.get('https://jsonplaceholder.typicode.com/posts');
setPosts(response.data.slice(0, 5)); // 5개만 가져오기
} catch (error) {
console.error('Error fetching posts:', error);
} finally {
setLoading(false);
}
};
fetchPosts();
}, []);
if (loading) return <div>로딩중...</div>;
return (
<div>
<h1>게시글 목록</h1>
{posts.map(post => (
<div
key={post.id}
style={{ margin: '20px 0', padding: '10px', border: '1px solid #ddd' }}
>
<h2>{post.title}</h2>
<p>{post.body}</p>
</div>
))}
</div>
);
}
export default PostList;
마치며
위의 개념들을 하나씩 학습하고 실제 프로젝트에 적용해보면서 경험을 쌓는 것이 중요합니다. 특히 초기에는 기본적인 개념들(컴포넌트, props, state)을 확실히 이해하는 것에 집중하시기 바랍니다.
관련 문서나 자세한 내용은 React 공식 문서를 참고하시면 도움이 됩니다.
'Front > React' 카테고리의 다른 글
React Recoil 로 todolist 제작하기 (1) | 2024.11.02 |
---|---|
조건부 렌더링 (Conditional Rendering) - React 배우기 (0) | 2024.11.02 |
Rudux로 전역 상태 관리하기 (0) | 2024.05.04 |
[React style] chakra-ui theme 설정 (0) | 2024.04.13 |
React로 웹사이트 만들기 (0) | 2024.04.05 |