




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;
