git config --global user.name
git config --global user.
git init
git status
git add .
git status
git commit -m ""
git log
git remote add origin https://github.com/leesfact/react.git
git push -u origin main
git clone -b main https://github.com/leesfact/react.git
SPA (Single Page Application)
에서 사용되는 라우팅 라이브러리컴포넌트
를 렌더링할 수 있도록 지원npm add react-router-dom
Auto Import
Reactjs code snippets
ESLint
Prettier - Code formatter
vscode-styled-components
자동완성
, 코드 스니펫 등의 편의 기능을 제공GitLens
Material Icon Theme
App.js
import { Route, Routes } from "react-router-dom";
import InputSample from "./pages/InputSample/InputSample";
function Test1() {
return (<h1>Test1</h1>)
}
function Test2() {
return (<h1>Test2</h1>)
}
function App() {
return (
<>
<Routes>
<Route path="/t1" Component={Test1} />
<Route path="/t2" Component={Test2} />
<Route path="/sample/input/1" Component={InputSample} />
</Routes>
</>
);
}
export default App;
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { BrowserRouter } from 'react-router-dom';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
pages > InputSample > inputSample.js
import React, { useRef, useState } from 'react';
const InputSample = () => {
const userInfo = {
username: '',
password: '',
}
const [userInput, setUserInput] = useState(userInfo);
//비구조 할당
const {username, password} = userInput;
//Ref
const passwordRef = useRef();
const handlerChange = (e) => {
const { name, value } = e.target;
setUserInput({...userInput, [name]: value});
// [] <---key
}
const nextFocus = (e) => {
if(e.keyCode === 13){
passwordRef.current.focus();
}
}
return (
<div>
<input
type="text"
onChange={handlerChange}
onKeyUp={nextFocus}
name="username"
/>
<input
type="text"
onChange={handlerChange}
onKeyUp={null}
name="password"
ref={passwordRef}
/>
<div>username: {username}</div>
<div>password: {password}</div>
</div>
);
};
export default InputSample;
useState
훅 사용, 객체 형태로 초기 상태 값을 설정, setUserInput을 통해 상태값 업데이트
비구조 할당을 사용, username & password 값을 선언, setUserInput으로 상태값을 업데이트할 때, spread operator를 사용, 객체를 복제한 뒤 해당 name
속성 값을 업데이트
Ref
을 사용하여 password input 요소에 대한 속성을 생성, 키보드 이벤트가 일어날 경우
ref.current.focus()
를 호출하여 focus 설정
username input 요소에 onKeyUp 이벤트 핸들러를 등록하여 엔터키를 누를 경우 password input 요소로 포커스가 이동하도록 구현
pages > InputSample > inputSample.js (수정)
import React, { useRef, useState } from 'react';
const InputSample = () => {
const userInfo = {
username: '',
password: '',
}
const [userInput, setUserInput] = useState(userInfo);
const [userInfoText, setUserInfoText] = useState(userInfo);
//비구조 할당
const {username, password} = userInfoText;
//Ref
const passwordRef = useRef();
const handlerChange = (e) => {
const { name, value } = e.target;
setUserInput({...userInput, [name]: value});
/**
{
username:'',
password:'',
username: value,
password: value
}
*/
// [] <---key
}
const nextFocus = (e) => {
if(e.keyCode === 13){
passwordRef.current.focus();
}
}
const submitHandler = (e) => {
if(e.keyCode === 13){ // 자료형, 값 모두가 일치 하는 경우 '==='
setUserInfoText({...userInput});
}
}
return (
<div>
<input
type="text"
onChange={handlerChange}
onKeyUp={nextFocus}
name="username"
/>
<input
type="text"
onChange={handlerChange}
onKeyUp={submitHandler}
name="password"
ref={passwordRef}
/>
<div>username: {username}</div>
<div>password: {password}</div>
</div>
);
};
export default InputSample;
결과
SEO
에 유리하고, 자바스크립트를 비활성화한 사용자도 웹 페이지를 볼 수 있음SEO
에도 불리함npm install react-minimal-side-navigation
npm install awesome-react-icons
import { Global } from '@emotion/react';
import 'react-minimal-side-navigation/lib/ReactMinimalSideNavigation.css';
import { Route, Routes } from 'react-router-dom';
import MainAside from './components/Aside/MainAside/MainAside';
import InputSample from './pages/InputSample/InputSample';
import UserList from './pages/UserList/UserList';
import { reset } from './styles/Global/reset';
function Test1() {
return (<h1>Test1</h1>);
}
function Test2() {
return (<h1>Test2</h1>);
}
function App() {
return (
<>
<Global styles={reset}/>
<MainAside />
<Routes>
<Route path="/t1" Component={Test1}/>
<Route path="/t2" Component={Test2}/>
<Route path="/sample/input/1" Component={InputSample}/>
<Route path="/users" Component={UserList}/>
</Routes>
</>
);
}
export default App;
react-router-dom
라이브러리를 사용하여 라우팅을 구현할 수 있음.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from "react-router-dom"
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
BrowserRouter
컴포넌트를 React.StrictMode
컴포넌트 내부에서 App
컴포넌트를 렌더링BrowserRouter
는 컴포넌트는 react-router-dom
라이브러리에서 제공하는 브라우저 라우팅을 위한 컴포넌트이며, App
컴포넌트를 감싸면서 해당 앱의 라우팅을 구현React.StrictMode
컴포넌트는 React의 Strict 모드를 활성화함, 이 모드에서는 개발 모드에서만 작동하며, 앱에서 잠재적인 문제를 감지하고 경고를 표시하게 됨ReactDOM.createRoot
함수를 사용하여 root
DOM 노드에 App
컴포넌트를 렌더링함, 이 함수는 Concurrent Mode에서 사용되며, Concurrent Mode는 리액트 애플리케이션을 더 빠르게 렌더링하기 위한 방식으로, createRoot
함수를 사용하면 해당 DOM 노드에서 렌더링을 시작할 수 있음Mode | 설명 |
---|---|
StrictMode | 컴포넌트 안의 문제를 조기에 감지하여 개발자가 버그를 수정하기 쉽게 만듦 |
ConcurrentMode | 리액트가 DOM 변경 작업을 보다 효율적으로 처리할 수 있도록 하는 모드 |
SuspenseMode | 코드 스플리팅과 같이 지연되는 작업에 대한 로딩 상태를 처리할 수 있는 모드 |
/** @jsxImportSource @emotion/react */
import React from 'react';
import { Navigation } from 'react-minimal-side-navigation/lib';
import 'react-minimal-side-navigation/lib/ReactMinimalSideNavigation.css';
import { HiHome } from 'react-icons/hi';
import { GrTest } from 'react-icons/gr';
import { BsCardChecklist } from 'react-icons/bs';
import { BiListCheck } from 'react-icons/bi';
import { FaUsers } from 'react-icons/fa';
import * as S from './style';
import { useNavigate } from 'react-router-dom';
const MainAside = () => {
const navigate = useNavigate();
return (
<aside css={S.style}>
<Navigation
activeItemId="/"
onSelect={({ itemId }) => {
navigate(itemId);
}}
items={[
{
title: 'Home',
itemId: '/',
elemBefore: () => <HiHome />,
},
{
title: 'T1',
itemId: '/t1',
elemBefore: () => <GrTest />,
},
{
title: 'T2',
itemId: '/t2',
elemBefore: () => <GrTest />,
},
{
title: 'Sample',
itemId: '/sample/input/1',
elemBefore: () => <BsCardChecklist />,
subNav: [
{
title: 'input1',
itemId: '/sample/input/1',
elemBefore: () => <BiListCheck />
}
]
},
{
title: 'List',
itemId: '/users',
elemBefore: () => <BsCardChecklist />,
subNav: [
{
title: '사용자 전체 조회',
itemId: '/users',
elemBefore: () => <FaUsers />
}
]
},
]}
/>
</aside>
);
};
export default MainAside;
react-minimal-side-navigation
패키지를 사용하여 만들어진, MainAside
컴포넌트Navigation
컴포넌트를 사용하여 구성됨Navigation
컴포넌트의 items
속성을 통해 각각의 사이드바 메뉴를 구성하고, onSelect
속성을 통해 메뉴를 선택할 때의 동작을 정의useNavigate
훅을 사용하여 메뉴를 클릭했을 때 해당 경로로 이동하도록 구현훅 함수 메서드 | 설명 |
---|---|
useState | - 상태를 관리하기 위한 훅함수 - 상태를 변경하고자 할 때, set 함수를 사용하여 변경가능 - 예: const [count, setCount] = useState(0) |
useEffect | - 컴포넌트 렌더링 이후에 실행되는 훅함수 - 컴포넌트가 업데이트될 때마다 실행되는데, 조건을 걸어서 실행되도록 할 수도 있음 - 예: userEffect(() => { console.log("Mounted!");,[]}; |
useContext | - React 컨텍스트를 사용하여 전역 상태를 관리하기 위한 훅 함수 - createContext를 사용하여 컨텍스트를 생성하고, Provider를 사용하여 전달할 값을 지정 - 예: const MyContext = createContext(defaultValue); |
useRef | - DOM 요소나 다른 값을 유지하고 싶을 때 사용하는 훅함수 - 일반 변수와는 달리 값이 변경되어도 컴포넌트가 다시 렌더링되지 않음 - 예: const inputRef = userRef(null); |
useCallback | - 함수를 메모이제이션하여 성능 향상을 위해 사용하는 훅 함수 - 컴포넌트가 렌더링될 때마다 함수가 재선언되는 것을 방지하여, 함수를 재사용할 수 있도록 함 - 예: const onClick = useCallback(() => { console.log("Clicked!"); },[]); |
useMemo | - 계산된 값을 캐싱하여 성능 향상을 위해 사용하는 훅 함수 - 계산 결과가 동일하다면 이전 결과를 사용하고, 다르면 다시 계산 - 예: const result = useMemo(() => expensiveCalculation(a,b) ,[a,b]); |
useReducer | - 상태를 관리하기 위한 훅 함수 중 하나로, Redux와 유사한 기능을 제공 - state와 dispatch 함수를 반환하여, state를 변경하기 위한 action을 dispatch 함수로 전달 가능 - 예: const [state, dispatch] - useReducer(reducer, initialState); |
useNavigate | - 현재 URL을 바꾸기 위한 훅 함수 - 반환된 함수는 클릭 이벤트 또는 다른 이벤트 핸들러에서 호출될 수 있음 - 예: const navigate = useNavigate(); navigate('/home') |
import { css } from "@emotion/react";
export const style = css`
position: fixed;
top: 0;
left: 0;
z-index: 99;
width: 300px;
height: 100%;
`;
import React, { useRef, useState } from 'react';
const InputSample = () => {
const userInfo = {
username: '',
password: ''
}
const [userInput, setUserInput] = useState(userInfo);
const [userInfoText, setUserInfoText] = useState(userInfo);
const { username, password } = userInfoText;
const passwordRef = useRef();
const handlerChange = (e) => {
const { name, value } = e.target;
setUserInput({...userInput, [name]: value});
}
const nextFocus = (e) => {
if(e.keyCode === 13) {
passwordRef.current.focus();
}
}
const submitHandler = (e) => {
if(e.keyCode === 13) {
setUserInfoText({...userInput});
}
}
return (
<div>
<input
className='test'
type="text"
onChange={handlerChange}
onKeyUp={nextFocus}
name="username"
/>
<input
type="text"
onChange={handlerChange}
onKeyUp={submitHandler}
name="password"
ref={passwordRef}
/>
<div>username: {username}</div>
<div>password: {password}</div>
</div>
);
};
export default InputSample;
import { css } from "@emotion/react";
export const Container = css`
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin: 50px auto;
width: 900px;
`;
export const Table = css`
border: 1px solid #dbdbdb;
width: 600px;
`;
export const ThAndTd = css`
border: 1px solid #dbdbdb;
width: 100px;
height: 40px;
text-align: center;
`;
/** @jsxImportSource @emotion/react */
import React, { useEffect, useRef, useState } from 'react';
import * as S from './style';
const UserList = () => {
useEffect(() => {
console.log("컴포넌트 렌더링");
}, []);
const user = {
id: 0,
username: '',
password: '',
name: '',
email: ''
}
const [inputs, setInputs] = useState(user);
const inputRefs = [useRef(), useRef(), useRef(), useRef()];
const addButtonRef = useRef();
const inputHandler = (e) => {
const { name, value } = e.target;
setInputs({...inputs, [name]: value});
}
const keyupHandler = (e) => {
if(e.keyCode === 13) {
let index = 0;
switch(e.target.name) {
case 'username': index = 1; break;
case 'password': index = 2; break;
case 'name': index = 3; break;
default: addButtonRef.current.click();
}
if(index !== 0){
inputRefs[index].current.focus();
}
}
}
const addHandler = () => {
}
const users = [
{
id: 1,
username: 'aaa',
password: '1234',
name: 'AAA',
email: 'aaa@gmail.com'
},
{
id: 2,
username: 'bbb',
password: '1234',
name: 'BBB',
email: 'bbb@gmail.com'
},
{
id: 3,
username: 'ccc',
password: '1234',
name: 'CCC',
email: 'ccc@gmail.com'
},
]
const userIndex = useRef(4);
return (
<div css={S.Container}>
<div>
<input type="text" onKeyUp={keyupHandler} onChange={inputHandler} placeholder='username' name='username' value={0} ref={inputRefs[0]}/>
<input type="text" onKeyUp={keyupHandler} onChange={inputHandler} placeholder='password' name='password' value={0} ref={inputRefs[1]}/>
<input type="text" onKeyUp={keyupHandler} onChange={inputHandler} placeholder='name' name='name' value={0} ref={inputRefs[2]}/>
<input type="text" onKeyUp={keyupHandler} onChange={inputHandler} placeholder='email' name='email' value={0} ref={inputRefs[3]}/>
<button type='button' onClick={addHandler} ref={addButtonRef}>추가</button>
</div>
<table css={S.Table}>
<thead>
<tr>
<th css={S.ThAndTd}>index</th>
<th css={S.ThAndTd}>username</th>
<th css={S.ThAndTd}>password</th>
<th css={S.ThAndTd}>name</th>
<th css={S.ThAndTd}>email</th>
</tr>
</thead>
<tbody>
{users.map(user => {
return (
<tr>
<td css={S.ThAndTd}>{user.id}</td>
<td css={S.ThAndTd}>{user.username}</td>
<td css={S.ThAndTd}>{user.password}</td>
<td css={S.ThAndTd}>{user.name}</td>
<td css={S.ThAndTd}>{user.email}</td>
</tr>
);
})}
</tbody>
</table>
</div>
);
};
export default UserList;