App4(){} : 함수형 component
{ useEffect, useState } : 함수형 component에서 동작하는 Hook
useState(초기값)
[상태값, 상태 변경 함수] 형태로 반환배열에 저장된값을 구조 분해 할당해서 상수에 대입
const [state, setState] = useState(초기값)
예시
const arr = [10, "kim"];
const a = arr[0];
const b = arr[1];
⭣
const [a,b] = arr;
function AComponent(){
return <>
ui 구성
</>
}
함수 안에서 변수를 선언할 일이 별로 없다
재호출시 다시 변수가 초기화되기 떄문이다
⚠️ 함수 내 변수 선언 주의
useState or useRef 사용[ ] 넣으면 최초 한 번만 실행useEffect(()=>{}, [])
const obj = {num:1, name:"kim"}
const num = obj.num;
const name = obj.name;
⭣
const {num, name} = obj;
인자로 객체를 전달
arg 는 object type
객체를 인자로 넘길 수 있다
function test(arg){
}
test({num:1, name:"kim"});
구조 분해 할당으로 받기
함수 인자에서 바로 구조 분해 가능
num 은 number type , name 은 string typefunction test2({num, name}){
}
test2({num:2, name:"park"});
배열이나 객체를 펼쳐서 새로운 배열/객체 만들 때 사용
기존 데이터 복사 + 추가 or 변경 가능
const nums = [10,20,30];
const nums2 = [...nums, 40];
const obj = {num:1, name:"kim"};
const obj2 = {...obj, addr:"seoul"};
object 의 key 값이 변수 안에 들어 있는 경우가 존재
const mem = {num:1, name:"kim"};
키가 고정된 경우 주로 사용
const result = mem.num;
const result2 = mem["num"];
const a = "num";
const result3 = mem[a];
변수로 객체 키 만들기
[a]: 1 : 변수 a에 들어있는 값이 키가 된다
{a: 1} : 키가 문자 a , 다름에 주의
let a = "num"
let b = "name"
let result = {[a]:1, [b]:"kim"};
// result는 { num:1, name:"kim" }
function App5() {
return (
<div className='container'>
<h1>ReactComponent 사용하기</h1>
</div>
);
}
export default App5;
Fortune 추가function Fortune(){
const style = {
color:"red",
backgroundColor:"#00ff00"
}
return<>
<h2>오늘의 운세</h2>
<p style={style}>동쪽으로 가면 귀인을 만나요</p>
</>
}
Fortune 컴포넌트 호출function App5() {
return (
<div className='container'>
<h1>ReactComponent 사용하기</h1>
<Fortune/>
</div>
);
}
➜ 같은 컴포넌트를 여러 번 사용 가능

App 컴포넌트 : 최상위 (부모)
자식 컴포넌트에 데이터를 전달할 수 있다
Friends 컴포넌트 생성
function Friends(){
return <>
<h2>친구 목록</h2>
<ul>
</ul>
</>
}
function App5() {
// 친구 목록
const names = ["유재석", "박명수", "정준하"];
return (
<div className='container'>
<h1>ReactComponent 사용하기</h1>
<Fortune/>
<Fortune/>
<Friends names={names}/>
</div>
);
}
args : 부모 component 가 전달한 속성이 objectfunction Friends(args){
// args.names 는 출력할 이름이 들어 있는 배열
return <>
<h2>친구 목록</h2>
<ul>
{args.names.map(item=><li>{item}</li>)}
</ul>
</>
}

Person 컴포넌트 생성function Person(){
return<>
</>
}
function App5() {
// 친구 목록
const names = ["유재석", "박명수", "정준하"];
return (
<div className='container'>
<h1>ReactComponent 사용하기</h1>
<Fortune/>
<Fortune/>
<Friends names={names}/>
<Person person={"노홍철"}/>
</div>
);
}
function Person({person}){
return<>
<p>오늘의 인물 : <strong>{person}</strong></p>
</>
}
<Person person={"노홍철"} color={"blue"} />
function Person({person, color}){
return<>
<p>오늘의 인물 : <strong style={{backgroundColor:color}}>{person}</strong></p>
</>
}
import React from 'react';
// props 에 names 라는 키값으로 배열이 전달되어야 정상 동작하는 component
function FriendsComponent(props) {
return (
<>
<h2>친구 목록</h2>
{props.names.map(item=><li>{item}</li>)}
</>
);
}
export default FriendsComponent;
import FriendsComponent from './components/FriendsComponent';
function App6() {
const names = ["유재석", "박명수", "정준하"];
return (
<div className="container">
<FriendsComponent names={names}/>
</div>
);
}
VSCODE 에 React Bootstrap 설치
react bootstrap click

npm i react-bootstrap
function App7() {
return (
<div className="container">
<h1>react bootstrap 컴포넌트</h1>
</div>
);
}
BootStrap snippet 등록


복사해서 vs code 설정 json 문서에 작성하기

복사한 내용을 javascriptreact.json 파일의 { } 안에 넣기
"boostrap 로딩": {
"prefix": "bootstrap",
"body": [
"import 'bootstrap/dist/css/bootstrap.css'"
],
"description": "boostrap 로딩"
}
// react bootstrap 을 사용하기 위해서는 css 를 먼저 import 하고
import 'bootstrap/dist/css/bootstrap.css'
// 원하는 component 를 react-bootstrap 으로부터 import 해서 사용
import { Button } from 'react-bootstrap';
function App7() {
const btnColor = "warning";
return (
<div className="container">
<h1>react bootstrap 컴포넌트</h1>
<Button variant='primary'>버튼 1</Button>
<Button variant='success'>버튼2</Button>
<Button variant={"info"}>버튼3</Button>
<Button variant={btnColor}>버튼4</Button>
</div>
);
}
<Alert variant="danger" dismissible>
<Alert.Heading>Oh snap! You got an error!</Alert.Heading>
<p>
Change this and that and try again. Duis mollis, est non commodo
luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit.
Cras mattis consectetur purus sit amet fermentum.
</p>
</Alert>
<Button variant='danger'>알림 띄우기</Button>
const [show, setShow] = useState(false);
<Button variant='danger' onClick={()=>setShow(true)}>알림 띄우기</Button>
{ show &&
<Alert variant="danger" dismissible>
<Alert.Heading>알림</Alert.Heading>
<p>삭제 실패</p>
</Alert>
}
Modal.Dialog : 모달의 기본 구조Modal.Header : 제목 및 닫기 버튼Modal.Body : 모달 내용Modal.Footer : 하단 버튼 영역display : block 을 줘서 모달을 항상 띄우는 형태import React from 'react';
import { Button, Modal } from 'react-bootstrap';
function MyModal(props) {
return (
<div
className="modal show"
style={{ display: 'block', position: 'initial' }}
>
<Modal.Dialog>
<Modal.Header closeButton>
<Modal.Title>Modal title</Modal.Title>
</Modal.Header>
<Modal.Body>
<p>Modal body text goes here.</p>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary">Close</Button>
<Button variant="primary">Save changes</Button>
</Modal.Footer>
</Modal.Dialog>
</div>
);
}
MyModal 호출import MyModal from './components/MyModal';
function App7() {
return (
<div className="container">
<h1>React Bootstrap 컴포넌트</h1>
<MyModal />
</div>
);
}
props 를 전달받아서 동작하는 react bootstrap 의 Modalshow : boolean type 모달을 띄울지 여부message : Modal 에 띄울 메세지onYes : function type 확인 버튼을 눌렀을때 실행할 함수 import { Button, Modal } from "react-bootstrap";
function AlertModal({show, message, onYes}) {
return (
<Modal show={show}>
<Modal.Header>알림</Modal.Header>
<Modal.Body>{message}</Modal.Body>
<Modal.Footer>
<Button variant="success" onClick={onYes}>확인</Button>
</Modal.Footer>
</Modal>
);
}
export default AlertModal;
const [show, setShow] = useState(true);
<AlertModal show={show} message={"안녕하세요"} onYes={()=>{setShow(false)}}/>

pagination 클래스를 사용해 기본 틀 생성function App8() {
return (
<div className="container">
<h1>페이징 처리 ui</h1>
<ul className="pagination">
<li className="page-item disabled">
<a className="page-link" href="#">Prev</a>
</li>
<li className="page-item">
<a className="page-link" href="#">1</a>
</li>
<li className="page-item">
<a className="page-link active" href="#">2</a>
</li>
<li className="page-item">
<a className="page-link" href="#">3</a>
</li>
<li className="page-item">
<a className="page-link" href="#">Next</a>
</li>
</ul>
</div>
);
}
const pageInfo = {
startPageNum: 1, // 시작 페이지 번호
endPageNum: 10, // 끝 페이지 번호
totalPageCount: 13, // 전체 페이지 수
pageNum: 3, // 현재 페이지
list: [] // 글 목록 (여기서는 사용 X)
};
range() 함수를 만들어 사용// 페이징 UI 를 만들때 사용할 배열을 리턴해주는 함수를 만들어 두고 활용하자
function range(start, end) {
const result = [];
for (let i = start; i <= end; i++) {
result.push(i);
}
return result;
}
// 페이지를 출력할 배열을 미리 준비한다
const pageArray = range(pageInfo.startPageNum, pageInfo.endPageNum);

map( ) 으로 반복 출력<ul className="pagination">
{/* Prev 버튼 */}
<li className={`page-item ${pageInfo.startPageNum === 1 ? 'disabled' : ''}`}>
<a className="page-link" href="#">Prev</a>
</li>
{/* 페이지 번호 버튼 */}
{pageArray.map(num => (
<li key={num} className={`page-item ${pageInfo.pageNum === num ? 'active' : ''}`}>
<a className="page-link" href="#">{num}</a>
</li>
))}
{/* Next 버튼 */}
<li className={`page-item ${pageInfo.totalPageCount > pageInfo.endPageNum ? '' : 'disabled'}`}>
<a className="page-link" href="#">Next</a>
</li>
</ul>
npm install react-router-dom
기존 src 폴더를 Step01Basic 으로 폴더명 변경
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.jsx'
createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
</StrictMode>,
)
index.css 생성
src 폴더 안에 router, pages 폴더 생성 후 router 폴더 안에 index.jsx 생성
index.js or index.jsx 로 작성하면 외부에서index 는 약속된 파일명import { createHashRouter } from "react-router-dom";
import App from "../App";
// 페이지에 routing 정보를 배열에 미리 저장해 둔다
const routes = [];
// export 해줄 router 객체를 만든다
const router = createHashRouter([{
path:"/",
element:<App/>,
children: routes.map((route)=>{
return {
index: route.path === "/", //자식의 path 가 "/" 면 index 페이지 역활을 하게 하기
path: route.path === "/" ? undefined : route.path, // path 에 "/" 두개가 표시되지 않게
element: route.element //어떤 컴포넌트를 활성화 할것인지
}
})
}]);
RouterProvider 추가import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
// SPA 를 구현하기 위한 RouterProvider 를 import
import { RouterProvider } from 'react-router-dom'
// routing 정보를 담고 있는 router import
import router from './router'
createRoot(document.getElementById('root')).render(
<StrictMode>
<RouterProvider router={router}/>
</StrictMode>,
)
function home() {
return (
<div>
<h1>인덱스 페이지</h1>
</div>
);
}
useOutlet() 사용import 'bootstrap/dist/css/bootstrap.css'
import { useOutlet } from 'react-router-dom';
function App() {
// React Router v6 에서 제공하는 hook
// 현재 경로에 맞는 자식 route component 를 반환한다
const currentOutlet = useOutlet();
return (
<div className="container">
{currentOutlet}
</div>
);
}
import Home from "../pages/home";
const routes = [
// spring boot 서버에 넣어서 실행하면 최초 로딩될때 /index.html 경로로 로딩된다
// 그럴때도 Home 컴포넌트가 활성화 될수 있도록 라우트 정보를 추가한다
{path:"/index.html", element: <Home/>},
{path:"/", element:<Home />},
];
import { NavLink } from 'react-router-dom';
function Home() {
return (
<div>
<h1>인덱스 페이지</h1>
<ul>
<li>
<NavLink to="/study">공부하기</NavLink>
</li>
<li>
<NavLink to="/play">놀러가기</NavLink>
</li>
</ul>
</div>
);
}
function Study() {
return (
<>
<h1>Study 페이지</h1>
<p>열심히 공부합시다</p>
</>
);
}
function Play() {
return (
<>
<h1>Play 페이지</h1>
<p>즐겁게 놉시다</p>
</>
);
}
import Home from "../pages/Home";
import Study from "../pages/Study";
import Play from "../pages/Play";
const routes = [
{ path:"/index.html", element: <Home/> },
{ path:"/", element: <Home /> },
{ path:"/study", element: <Study /> },
{ path:"/play", element: <Play /> },
];
export default routes;
Nav 요소 추가react bootstrap 의 Nav.Link 이지만as={NavLink} 설정으로 동작은 react router 의 NavLink 의 동작을 한다to="경로" 에 작성하면 된다<Nav variant='pills'>
<Nav.Item>
<Nav.Link as={NavLink} to="/">Home</Nav.Link>
</Nav.Item>
<Nav.Item>
<Nav.Link as={NavLink} to="/study">Study</Nav.Link>
</Nav.Item>
<Nav.Item>
<Nav.Link as={NavLink} to="/play">Play</Nav.Link>
</Nav.Item>
</Nav>

function Member() {
return (
<>
<h1>회원 목록</h1>
<table className="table table-striped">>
<thead>
<tr>
<th>번호</th>
<th>이름</th>
<th>주소</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</>
);
}
import Member from "../pages/Member";
{path:"/members", element:<Member/>}
<Nav.Item>
<Nav.Link as={NavLink} to="/members">회원 목록</Nav.Link>
</Nav.Item>
// 회원 목록을 상태값으로 관리
const [members, setMembers] = useState([]);
// 컴포넌트가 활성화 될 때 회원 목록 받아오기
useEffect(()=>{
fetch("/api/v1/members")
.then(res=>res.json())
.then(data=>setMembers(data));
}, []);
<tbody>
{members.map(item=>
<tr>
<td>{item.num}</td>
<td>{item.name}</td>
<td>{item.addr}</td>
</tr>
)}
</tbody>

<h1>회원 목록</h1>
<table className="table table-striped">
<thead>
<tr>
<th>번호</th>
<th>이름</th>
<th>자세히</th>
</tr>
</thead>
<tbody>
{members.map(item => (
<tr key={item.num}>
<td>{item.num}</td>
<td>{item.name}</td>
<td>
<NavLink to={`/members/${item.num}`}>보기</NavLink>
</td>
</tr>
))}
</tbody>
</table>
➜ 라우터에서 path variable(경로 변수) 을 사용할 수 있다
http://localhost:5173/#/members/1
http://localhost:5173/#/members/2
http://localhost:5173/#/members/3
function MemberDetail() {
// 자세히 보여줄 회원의 번호를 읽어와서
// API 서버에 해당 회원 한 명의 정보를 받아와서 ui 에 출력한다
return (
<>
</>
);
}
MemberDetail 라우트 정보 추가{path:"/members/:num", element:<MemberDetail/>}
useParams( ) 를 이용한 Path Variable 사용function MemberDetail() {
// 자세히 보여줄 회원의 번호를 읽어와서
// react router 에서 제공해주는 hook 을 이용해서
// "/members/:num" 에서 num 에 해당하는 자세히 보여줄 회원의 번호를 얻어낸다
const {num} = useParams();
// API 서버에 해당 회원 한 명의 정보를 받아와서 ui 에 출력한다
const [dto, setDto] = useState({});
useEffect(()=>{
fetch(`/api/v1/members/${num}`)
.then(res=>res.json())
.then(data=>setDto(data));
}, []);
return (
<>
<h1>회원 자세히 보기</h1>
<p>번호 : <strong>{dto.num}</strong></p>
<p>이름 : <strong>{dto.name}</strong></p>
<p>주소 : <strong>{dto.addr}</strong></p>
</>
);
}

<form> 태그를 이용한 기본 전송 방식은Content-Type = application/x-www-form-urlencoded<input> 요소에 입력한 내용이 query 문자열 로 변환되어 요청이 body 에 들어간다name = kimgura&addr=seoul<form>
<input type="text" name="name">
<input type="text" name="addr">
</form>
React에서는 form 자체를 전송하지 않고, fetch() 또는 axios 같은 JavaScript 로 페이지 전환없이 전송할 내용을 json 문자열로 만들어서 전송
이때 전송 방식은 Content-Type = application/json이며, 요청 body에 JSON 문자열을 포함
{
"name": "kimgura",
"addr": "seoul"
}
request.getParameter() 로 추출이 되지 않는다@RequestBody 어노테이션을 이용하면 쉽게 추출할 수 있다@PostMapping("/api/members")
public String insert(@RequestBody MemberDto dto) {
// dto.name, dto.addr 로 접근 가능
return "ok";
}