React
- 유저 인터페이스(UI)를 관리하는 Javascript 라이브러리
NPM(node package manager)
- Javascript 라이브러리들을 설치하고 버전을 관리하는 도구
- node_modules 폴더에 있는 모든 라이브러리들을 주고받기에는 용량이 크기에, package.json만 주고받으며 npm을 통해 라이브러리들을 설치한다
Yarn
- NPM의 역할을 하는 동시에 NPM의 단점을 보완한 라이브러리 관리 도구 (feat. Meta)
# 빈 폴더를 Visual Studio Code에서 오픈
# 터미널에 입력하여 yarn 설치
npm -install -g yarn
# 어플리케이션 설치
yarn create react-app "앱 이름"
# 설치된 앱으로 이동
cd "앱 이름"
# 앱 실행
yarn start
JSX
- JS + XML
- Babel: 선언형(결과물을 선언) 문법인 JSX를 명령형(어떻게 만들지 명령) 문법인 JS로 변환해주는 도구
// React.Fragment의 축약형 <> </>: 불필요한 div를 사용하지 않고 여러 요소를 렌더링할 수 있게 해준다.
// 자바스크립트 표현식 사용 가능 ex. {name}
// 삼향연산자 사용 가능
function Hello() {
const name = 'World';
const num = 1;
return (
<>
<div>Hello {name}!</div>
{num === 1 ? <div>1입니다.</div> : <div>1이 아닙니다.</div>}
</>
);
};
Component
1. 클래스형 컴포넌트
- state 기능, 라이프 사이클 기능 제공
- 임의 메소드 정의 가능
- render 함수 필수: render 함수 내부에서 jsx 반환
- import를 통해 컴포넌트를 불러오고, export를 통해 컴포넌트를 내보냄
import React from 'react';
import { Component } from 'react';
class Hello extends Component {
render() {
return (<h1>Hello World!</h1>);
}
};
export default Hello;
2. 함수형 컴포넌트: 권장
- state, 라이프 사이클 api 사용 불가능: 16.8 업데이트 이후 Hooks 도입으로 해결
- 배포 단계에서도 비교적 작은 파일 크기
- 편한 선언, 비교적 적은 메모리 자원 사용
- import를 통해 컴포넌트를 불러오고, export를 통해 컴포넌트를 내보냄
import React from 'react';
function Hello() {
return (<h1>Hello World!</h1>);
};
export default Hello;
props
- 컴포넌트 속성 설정 시에 사용하는 요소
- props의 값은 부모 컴포넌트에서 설정
- 자식 컴포넌트를 수정하여 렌더링
// 부모 컴포넌트
import React from 'react';
import Child from './child';
const parent = () => {
// 별도의 props 값이 없는 경우 보여주는 기본 값
Child.defaultProps = {
name: 'World'
};
return (
<div>
<Child name="React"/>
</div>
);
};
export default parent;
// 자식 컴포넌트
import React from 'react';
const child = (props) => {
return (
<div>
Hello {props.name}!
</div>
);
};
export default child;
state
- 현재 상태에 따라 바뀌는 값
- useState를 활용하여 구현
// 배열의 첫 번째 원소에는 현재 상태 저장
// 배열의 두 번째 원소에는 상태를 바꿔주는 setter 함수
// 함수 인자에는 상태의 초기값
// name은 state, setName은 state를 변경하는 함수, "Tom"은 state의 초기값
const [name, setName] = useState("Tom")
props | state |
부모 컴포넌트로부터 자녀 컴포넌트에 데이터 등을 전달 |
해당 컴포넌트 내부에서 데이터 전달 |
읽기 전용으로 자녀 컴포넌트 입장에서 변동 없음 |
변경 가능함(변경시 re-render) |
둘 다 컴포넌트에서 사용하거나 렌더링 할 데이터를 담고 있음 |
map
- 반복되는 형태의 코드(컴포넌트 반복)를 효율적으로 관리
- 태그로 감싸준 코드를 map 할 때에는 key 값 설정 권장
const numbers = [1,2,3,4,5];
const result = numbers.map(num => num * num);
console.log(result); //[1,4,9,16,25]
MPA / SPA
1. MPA
- 사용자가 새로운 페이지를 요청할 때마다 서버에서 미리 준비한 화면을 보내줌
- 페이지를 이동하거나 새로고침할 때마다 전체 페이지를 렌더링하기 때문에 상태 유지가 어렵고, 불필요한 로딩이 생김
2. SPA: React
- 웹 에플리케이션에 필요한 모든 정적 리소스를 최초 접근 시 단 한번만 다운로드
- 라우팅(다른 주소에 다른 화면을 보여주는 기술)을 통해 페이지 전환
Router
# Router 설치
npm install react-router-dom@6
// index.js 코드를 아래와 같이 수정
import { BrowserRouter } from 'react-router-dom';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
// App.js에서 라우팅할 페이지들을 불러오기
import React from 'react';
import { Route, Routes } from 'react-router-dom';
import Home form "./pages/Home";
import Login form "./pages/Login";
function App() {
return (
<Routes>
<Route path='/' element={<Home />} />
<Route path='/login' element={<Login />} />
</Routes>
);
}
export default App;
// Home.js에서 Link를 통해 Loing.js로 이동할 수 있도록 구현
import React from 'react';
import { Link } from 'react-router-dom';
const Home = () => {
return (
<div>
<Link to='/login'>로그인</Link>
</div>
);
}
export default Home;
Router 중첩
// 상위 Route 안에 하위 Route들을 구현할 수 있음
import React from 'react';
import { Route, Routes } from 'react-router-dom';
import Home form "./pages/Home";
import Login form "./pages/Login";
import Register form "./pages/Register";
function App() {
return (
<Routes>
<Route path='/' element={<Home />}>
<Route path='/login' element={<Login />} />
<Route path='/register' element={<Register />} />
</Route>
</Routes>
);
}
export default App;
// Outlet을 통해 login과 register 컴포넌트도 함께 렌더링할 수 있음
import React from 'react';
import { Link, Outlet } from 'react-router-dom';
const Home = () => {
return (
<div>
<Link to='/login'>로그인</Link>
<Link to='/register'>회원가입</Link>
<Outlet />
</div>
);
}
export default Home;
페이지 주소 정의
- URL 파라미터: 특정 아이디, 이름을 사용하여 조회
- 쿼리스트링: 키워드 검색, 페이지네이션, 옵션 전달
- NotFound 페이지: path에 *를 설정하여 구현
// :movieId는 URL 파라미터로 사용할 것임을 의미
// 존재하지 않는 URL을 요청했을 때 NotFound 컴포넌트를 렌더링
function App() {
return (
<Routes>
<Route path='/' element={<Home />} />
<Route path='/movies' element={<Movies />}>
<Route path=':movieId' element={<Movie />} />
</Route>
<Route path='*' element={<NotFound />} />
</Routes>
);
}
// Movies.js에서 map을 통해 movie_data의 각 요소를 렌더링
const Movies = () => {
const movies = movie_data;
return (
<div>
{movies.map(movie => () => (
<Link to={`/movies/${movie.id}`}>
{movie.title}
</Link>
))}
</div>
);
}
// Movie.js에서 특정 정보를 렌더링
const Movie = () => {
// URL 파라미터 사용하기
const params = useParams();
// movie_data에서 id가 일치하는 영화를 찾아서 movie에 저장
const movie = movie_data.find(movie => movie.id === parseInt(params.movieId));
// 쿼리스트링 사용 ex. /movies/1?detail=true
const [searchParams, setSearchParams] = useSearchParams();
const detail = searchParams.get('detail');
const handleClick = () => {
setSearchParams({ detail: detail === 'true' ? false : true });
};
return (
<div>
<div>{movie.title}</div>
<div>감독: {movie.director}</div>
<button onClick={handleClick}>상세보기</button>
{detail && <div>상세정보: {movie.detail}</div>}
</div>
);
}
useNavigate
- Link처럼 페이지 전환 기능을 가지고 있는 Hook
// useNavigate를 사용하여 홈으로 이동
const Login = () => {
const navigate = useNavigate();
const goHome = () => {
navigate('/');
};
return (
<div>
<div>Login</div>
<button onClick={goHome}>홈으로</button>
</div>
);
}
NavLink
- Link의 특수한 버전으로 링크가 active되어 있을 때 특정 style 적용
// NavLink를 통해 클릭 시 style 적용
import React from 'react';
import { NavLink } from 'react-router-dom';
const Movies = () => {
const movies = movie_data;
const style = {
fontWeight:'900',
color:'red',
}
return (
<div>
{movies.map(movie => () => (
<NavLink to={`/movies/${movie.id}`} activeStyle={style}>
{movie.title}
</NavLink>
))}
</div>
);
}
export default Movies;
useEffect
- 리액트 컴포넌트가 렌더링 될때마다 특정 작업을 실행할 수 있도록 하는 Hook
- function / deps: 수행하고자 하는 작업 / 검사하고자 하는 값 또는 배열, 배열형태
// 가장 처음 렌더링 될 때 한 번만 실행: 빈 배열 넣기
useEffect(function, [])
// 특정 props나 state가 바뀔 때 실행: 특정 값 넣기
useEffect(function, [deps])
제어 컴포넌트
// 제어 컴포넌트를 통해 input 상태 관리
const WritePage = () => {
// useState 만들어주기
const [inputs, setInputs] = useState({
title: '',
content: '',
});
// 비구조화 할당을 통해 값 추출
const { title, content } = inputs;
// onChange 함수: input 상태 변동
const onChange = (e) => {
const { value, name } = e.target; // 우선 e.target 에서 name 과 value 를 추출
setInputs({
...inputs, // 기존의 input 객체를 복사한 뒤
[name]: value, // name 키를 가진 값을 value 로 설정
});
}
return (
<div>
<input
name="title"
onChange={onChange}
value={title}
/>
<textarea
name="content"
onChange={onChange}
value={content}
/>
</div>
);
}
React Virtual Dom
- 원래 브라우저는 DOM tree(HTML을 파싱하여 DOM 노드로 이뤄진 트리) + CSSOM(CSS을 파싱하여 생성) = Render tree를 생성하는 방식으로 작동
- 문제: 변화가 생길 때마다 tree를 전부 다시 생성해야하는 비효율성
- Virtual Dom: State Change > Compute Diff > Re-render 순으로 변화된 부분만을 재생성해 효율성 제고
useRef
- Virtual Dom을 효율적으로 관리할 수 있는 Hook
- 특정 컴포넌트에 focus를 줄 수 있도록 작동
function InputPost() {
// useRef 설정
const titleInput = useRef();
const contentInput = useRef();
// 첫 렌더링 후 title에 포커스
useEffect(() => {
titleInput.current.focus();
}, []);
// 엔터키 입력 시 content에 포커스
const onKeyUp = (e) => {
if (e.key === 'Enter') {
contentInput.current.focus();
}
};
return (
<>
<input
name="title"
ref={titleInput}
onKeyUp={onKeyUp}
/>
<textarea
name="content"
ref={contentInput}
/>
</>
);
}
useCallback
- 성능 최적화를 위해 함수를 memorize 해주는 Hook
- 컴포넌트가 re-render 될 때마다 컴포넌트의 함수가 재생성되는 문제 방지
- 연산이 오래 걸리고 복잡한 함수일수록 useCallback이 더욱 효율적
- function / deps: 수행하고자 하는 작업 / 검사하고자 하는 값 또는 배열, 배열형태
// deps가 바뀌면 함수를 재생성하고, deps가 바뀌지 않으면 변동 없음
useCallback(function, [deps])
useMemo
- 성능 최적화를 위해 연산된 값을 재사용하게 해주는 Hook
- function / deps: 수행하고자 하는 작업 / 검사하고자 하는 값 또는 배열, 배열형태
// deps가 바뀌면 함수를 호출하여 연산하고, deps가 바뀌지 않으면 변동 없음
useMemo(function, [deps])
React.memo
- 성능 최적화를 위해 컴포넌트를 memorize해놨다가 필요한 상황에만 re-render 해주는 Hook
- 상위 컴포넌트가 re-render되면 하위 컴포넌트도 무조건으로 re-render되는 문제 방지
- function / deps: 수행하고자 하는 작업 / 검사하고자 하는 값 또는 배열, 배열형태
// deps가 바뀌면 컴포넌트를 재생성하고, deps가 바뀌지 않으면 변동 없음
React.memo(function, [deps])
axios
- 외부와 데이터를 주고받을 때 대표적으로 사용하는 라이브러리
// 최초 렌더링 시에만 작동
useEffect(() => {
// get 방식으로 데이터 조회
axios.get(`${hostURL}/api/list`)
.then((response) => {
setItemList(response.data); // 받아온 데이터를 변수에 저장
setLoading(false); // 로딩 종료
window.location.reload(); // React는 SPA이므로 내부적으로 새로고침 기능이 없음
})
}, []);
useQuery
- 데이터 조회 로직 단순화
- 에러처리, 캐싱 등을 간편하게 구현
function Todos() {
// 간편하게 데이터 조회
const { status, data, error } = useQuery("todos", fetchTodoList);
// 로딩 중일 때
if (status === "loading") {
return <span>Loading...</span>;
}
// 에러가 발생했을 때
if (status === "error") {
return <span>Error: {error.message}</span>;
}
return (
<ul>
{data.map(todo => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
);
}
'React' 카테고리의 다른 글
React로 커머스 사이트 만들기 (5): 주문인증/주문조회 페이지 (0) | 2023.09.24 |
---|---|
React로 커머스 사이트 만들기 (4): 구매 Modal & 주문완료 페이지 (0) | 2023.09.24 |
React로 커머스 사이트 만들기 (3): 홈페이지 & 상품페이지 (2) | 2023.09.23 |
React로 커머스 사이트 만들기 (2): REST API & json-server (0) | 2023.09.23 |
React로 커머스 사이트 만들기 (1): 세팅 (0) | 2023.09.23 |