리액트 (Routing)
라우팅 개발 환경 세팅
외부 라이브러리 설치 (프로젝트별로 설치해야 함)
npm install react-router-dom
1. index.js에서 <App />를 <BrowserRouter> 태그로 감싸주기
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();
2. App.js에서 루트 엘리먼트로 <BrowserRouter>를 사용하기
import { BrowserRouter } from 'react-router-dom';
import MyTest from './mydir/Test';
import About from "./mydir/About"
function App() {
return (
<BrowserRouter>
<div className='App'>
<h2>라우팅 연습</h2>
<MyTest />
</div>
</BrowserRouter>
);
}
export default App;
라우팅 실습 1 (워밍업)
App.js
import { BrowserRouter, Link, Routes, Route } from 'react-router-dom';
import MyTest from './mydir/Test';
import About from "./mydir/About"
function App() {
return (
<BrowserRouter>
<div className='App'>
<h2>라우팅 연습</h2>
<MyTest />
<hr/>
<nav>
<Link to="/">TEST 화면</Link> |
<Link to="about">ABOUT 화면</Link>
<Routes>
<Route path="/" element={<MyTest/>}></Route>
<Route path="about" element={<About/>}></Route>
</Routes>
</nav>
</div>
</BrowserRouter>
);
}
export default App;
Test.js
import React from "react";
const Test = () => {
return(
<h2>Test 문서</h2>
);
}
export default Test;
About.js
import React from "react";
const HelloAbout = () => {
return <div>라우터에 대한 소개!</div>
}
export default HelloAbout;
결과
하나의 페이지 내에서 링크 클릭 시 해당 컴포넌트가 보여주는 UI가 보여진다. 실제로는 다른 페이지로 이동하는 것이 아니기에 이 라우터를 이용하면 SPA(Single Page Application)을 구현할 수 있다.
라우팅 실습 2 (워밍업)
App.js
import { BrowserRouter, Link, Routes, Route } from 'react-router-dom';
import MyTest from './mydir/Test';
import About from "./mydir/About"
import Counter from './mydir/Counter';
import Input1 from './mydir/Input1';
import Input2 from './mydir/Input2';
import MultiData from './mydir/MultiData';
function App() {
return (
<BrowserRouter>
<div className='App'>
<h2>라우팅 연습</h2>
<MyTest />
<hr/>
<nav>
{/* Link는 a 태그와 같다고 생각해도 된다. 화면 상에 a태그를 나타내는 역할.
실제로 라우팅 기능은 아래에 적을 <Route>가 실질적인 라우팅 기능을 수행한다. */}
<Link to="/">TEST</Link> |
<Link to="about">ABOUT</Link> |
<Link to="count">친구 추가</Link> |
<Link to="input1">입력 1</Link> |
<Link to="input2">입력 2</Link> |
<Link to="multi">멀티 데이터</Link> |
{/* element 속성에는 컴포넌트명을 명시. 해당 element의 페이지로 이동된다.
하나의 싱글 페이지에서 해당 컴포넌트로 이동하기에 SPA 원칙을 유지할 수 있다. */}
<Routes>
<Route path="/" element={<MyTest/>}></Route>
<Route path="about" element={<About/>}></Route>
<Route path="count" element={<Counter/>}></Route>
<Route path="input1" element={<Input1/>}></Route>
<Route path="input2" element={<Input2/>} />
<Route path="multi" element={<MultiData/>} />
</Routes>
</nav>
</div>
</BrowserRouter>
);
}
export default App;
input1.js
import { useState } from "react"
const Input1 = () => {
const [txtValue, setTxtValue] = useState("");
const changFunc = (e) => {
setTxtValue(e.target.value);
}
return(
<div>
<input type="text" value={txtValue} onChange={changFunc} />
<br/>
{txtValue}
</div>
);
}
export default Input1;
Input2.js
import { useState } from "react"
const Input2 = () => {
const [params, setParams] = useState({
name:'',
age:'',
addr:''
});
// 구조 분해 할당 이용.
const {name, age, addr} = params;
const changeFunc = event => {
const id = event.target.id;
const value = event.target.value;
setParams({
...params, // 깊은 복사(구조 분해 할당, destructuring assignment)
[id]:value // 계산된 프로퍼티. id내의 값이 실질적 프로퍼티가 된다.
// 예를 들어, id='name'일 경우 => [id]: value => name:'염정섭'
// 덮어쓰기
});
}
return (
<div>
<br/>
<div>
이름 : <input type="text" value={name} id="name" onChange={changeFunc} />
</div>
<div>
나이 : <input type="text" value={age} id="age" onChange={changeFunc} />
</div>
<div>
주소 : <input type="text" value={addr} id="addr" onChange={changeFunc} />
</div>
<br/>
<table>
<tr>
<td>이름 : {name}</td>
<td>나이 : {age}</td>
<td>주소 : {addr}</td>
</tr>
</table>
</div>
);
}
export default Input2;
MultiData.js
// 멤버 배열 자료 처리용
const MemberComp = ({memeberData}) => {
return (
<tr>
<td>{memeberData.name}</td>
<td>{memeberData.tel}</td>
</tr>
);
};
const MultiData = () => {
const members = [
{name:"가가가", tel:"010-1111-2222"},
{name:"나나나", tel:"010-3333-4444"},
{name:"다다다", tel:"010-5555-6666"},
];
return (
<table>
<thead>
<tr>
<th>이름</th>
<th>번호</th>
</tr>
</thead>
<tbody>
{members.map((mem, idx) =>
(<MemberComp key={idx} memeberData={mem} />)
)}
</tbody>
</table>
)
}
export default MultiData;
결과
입력 1
입력 2
여러 데이터
라우팅 실습 3 (AJAX)
실습을 위해 이클립스를 킨다. JSP로 작성!
“Dynamic Web Project”로 새 프로젝트 폴더 생성
practice.jsp
<%@ page language="java" contentType="text/plain; charset=UTF-8"
pageEncoding="UTF-8"%>
{
"items":
[
{"id":1, "name":"바닐라라떼", "price":"4500"},
{"id":2, "name":"아메리카노", "price":"1500"},
{"id":3, "name":"아인슈페너", "price":"5500"}
]
}
plain으로 변경하는 이유! JSON 객체로 넘기기 위함, json으로 바꾸어주어도 괜찮다.
서버 실행 후 JSON 형식으로 만들어진 것을 확인!
리액트로 AJAX 요청해보자!
package.json
CROS 문제를 해결하기 위해 package.json에 서버 주소를 입력!
"proxy": "http://localhost:8080"
{
"name": "my-app13route",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.7.7",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.28.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
... 생략
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"proxy": "http://localhost:8080"
}
MyProduct.js
import React, { useEffect, useState } from "react";
const MyProduct = () => {
const [error, setError] = useState(null);
const [isLoaded, setIsLoaded] = useState(false);
const [items, setItems] = useState([]);
// 컴포넌트가 mount된 후 AJAX 요청을 수행
useEffect(() => {
// 같은 도메인에 대해서는 도메인 생략이 가능하다.
fetch(`/abcReact/practice.jsp`, {method:'GET'})
.then(response => {
if(!response.ok) {
throw new Error("HTTP Responsw Status Not OK");
}
return response.json();
})
.then(result => {
setIsLoaded(true); // 로드 성공
setItems(result.items); // AJAX로 가져온 데이터를 state에 저장
},
error => {
setIsLoaded(true);
setError(error);
console.log(error);
});
},[]); // 매 렌더링 후에만 1회 실행
if (error) {
return <div>Error : {error.message}</div>;
}
if (!isLoaded) {
return <div>Loading</div>
}
return (
<ul>
{items.map(item => (
<li key={item.id}>
<p>상품명 : {item.name}</p>
<p>가격 : {item.price}원</p>
</li>
))}
</ul>
);
}
export default MyProduct;
AJAX 요청 결과 확인
리액트에서는 fetch보다 axios 사용!
먼저 axios 의존성 추가 : npm install axios
MyProduct2.js
import axios from "axios";
import { useState, useEffect } from "react";
const MyProduct2 = () => {
const [error, setError] = useState(null);
const [isLoaded, setIsLoaded] = useState(false);
const [items, setItems] = useState([]);
// 컴포넌트가 mount된 후 AJAX 요청을 수행
useEffect(() => {
// 같은 도메인에 대해서는 도메인 생략이 가능하다.
axios(`/abcReact/practice.jsp`, {method:'GET'})
.then(response => {
setIsLoaded(true) // 로드 성공
setItems(response.data.items) // 서버에서 받아온 자료로 상태(state) 갱신
})
.catch(error => {
setIsLoaded(true);
setError(error);
console.log(error);
})
},[]); // 매 렌더링 후에만 1회 실행
if (!isLoaded) {
return <div>Loading</div>
}
if (error) {
return <div>Error : {error.message}</div>;
}
return (
<ul>
{items.map(item => (
<li key={item.id}>
<p>상품명 : {item.name}</p>
<p>가격 : {item.price}원</p>
</li>
))}
</ul>
);
};
export default MyProduct2;
App.js
import { BrowserRouter, Link, Routes, Route } from 'react-router-dom';
import MyTest from './mydir/Test';
import About from "./mydir/About"
import Counter from './mydir/Counter';
import Input1 from './mydir/Input1';
import Input2 from './mydir/Input2';
import MultiData from './mydir/MultiData';
import MyProduct from './mydir/MyProduct';
import MyProduct2 from './mydir/MyProduct2';
function App() {
return (
<BrowserRouter>
<div className='App'>
<h2>라우팅 연습</h2>
<MyTest />
<hr/>
<nav>
{/* Link는 a 태그와 같다고 생각해도 된다. 화면 상에 a태그를 나타내는 역할.
실제로 라우팅 기능은 아래에 적을 <Route>가 실질적인 라우팅 기능을 수행한다. */}
<Link to="/">TEST</Link> |
<Link to="about">ABOUT</Link> |
<Link to="count">친구 추가</Link> |
<Link to="input1">입력 1</Link> |
<Link to="input2">입력 2</Link> |
<Link to="/kbs/product">상품 정보 (AJAX)</Link> |
<Link to="/kbs/product2">상품 정보 (axios)</Link> |
{/* element 속성에는 컴포넌트명을 명시. 해당 element의 페이지로 이동된다.
하나의 싱글 페이지에서 해당 컴포넌트로 이동하기에 SPA 원칙을 유지할 수 있다. */}
<Routes>
<Route path="/" element={<MyTest/>}></Route>
<Route path="about" element={<About/>}></Route>
<Route path="count" element={<Counter/>}></Route>
<Route path="input1" element={<Input1/>}></Route>
<Route path="input2" element={<Input2/>} />
<Route path="multi" element={<MultiData/>} />
<Route path="/kbs/product" element={<MyProduct/>} />
<Route path="/kbs/product2" element={<MyProduct2/>} />
</Routes>
</nav>
</div>
</BrowserRouter>
);
}
export default App;
axios 확인!
라우팅 실습 4 (AJAX -axios / DB)
DB 데이터를 가져와 라우팅 실습을 해보자!
JSP를 사용할 예정이어서, 이클립스 내에 maridb driver를 넣어주자
practice.jsp
<%@page import="java.sql.DriverManager"%>
<%@page import="java.sql.ResultSet"%>
<%@page import="java.sql.PreparedStatement"%>
<%@page import="java.sql.Connection"%>
<%@ page language="java" contentType="text/plain; charset=UTF-8"
pageEncoding="UTF-8"%>
<%--
{
"items":
[
{"id": 1, "name": "바닐라라떼", "price": "4500"},
{"id": 2, "name": "아메리카노", "price": "1500"},
{"id": 3, "name": "아인슈페너", "price": "5500"}
]
}
--%>
{
"items": [
<%
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
String result = "";
try {
Class.forName("org.mariadb.jdbc.Driver");
String url = "jdbc:mariadb://localhost:3306/test";
conn = DriverManager.getConnection(url, "root", "1111");
} catch(Exception e) {
System.out.println("=== DB 연동 오류 ===");
e.printStackTrace();
return;
}
try {
pstmt = conn.prepareStatement("SELECT * FROM sangdata");
rs = pstmt.executeQuery();
while(rs.next()) {
// {"id": 1, "name": "바닐라라떼", "price": "3000"},
/*
String strFormat = """
{\"id\": %s, \"name\": %s, \"price\": %d},
""".trim();
result += String.format(strFormat,
rs.getString("code"),
rs.getString("sang"),
rs.getInt("su") * rs.getInt("dan")
);*/
result += "{";
result += "\"id\":" + "\"" + rs.getString("code") + "\",";
result += "\"name\":" + "\"" + rs.getString("sang") + "\",";
result += "\"price\":" + "\"" + (rs.getInt("su") * rs.getInt("dan")) + "\"";
result += "},";
}
if (result.length() > 0) {
result = result.substring(0, result.length() - 1);
}
out.print(result);
} catch(Exception e) {
System.out.println("=== DB 연동 오류 ===");
e.printStackTrace();
return;
} finally {
if (rs != null) rs.close();
if (pstmt != null) pstmt.close();
if (conn != null) conn.close();
}
%>
]
}
서버에서 DB 데이터를 가져온 것을 확인
리액트에서도 확인!
참고! 로딩중 메세지 띄워보기!
practice.jsp
<%@page import="java.sql.DriverManager"%>
<%@page import="java.sql.ResultSet"%>
<%@page import="java.sql.PreparedStatement"%>
<%@page import="java.sql.Connection"%>
<%@ page language="java" contentType="text/plain; charset=UTF-8"
pageEncoding="UTF-8"%>
<%--
{
"items":
[
{"id": 1, "name": "바닐라라떼", "price": "4500"},
{"id": 2, "name": "아메리카노", "price": "1500"},
{"id": 3, "name": "아인슈페너", "price": "5500"}
]
}
--%>
{
"items": [
<%
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
String result = "";
try {
Class.forName("org.mariadb.jdbc.Driver");
String url = "jdbc:mariadb://localhost:3306/test";
conn = DriverManager.getConnection(url, "root", "1111");
} catch(Exception e) {
System.out.println("=== DB 연동 오류 ===");
e.printStackTrace();
return;
}
try {
Thread.sleep(3000); // 지연을 재현하기 위한 코드 추가
pstmt = conn.prepareStatement("SELECT * FROM sangdata");
rs = pstmt.executeQuery();
while(rs.next()) {
// {"id": 1, "name": "바닐라라떼", "price": "3000"},
/*
String strFormat = """
{\"id\": %s, \"name\": %s, \"price\": %d},
""".trim();
result += String.format(strFormat,
rs.getString("code"),
rs.getString("sang"),
rs.getInt("su") * rs.getInt("dan")
);*/
result += "{";
result += "\"id\":" + "\"" + rs.getString("code") + "\",";
result += "\"name\":" + "\"" + rs.getString("sang") + "\",";
result += "\"price\":" + "\"" + (rs.getInt("su") * rs.getInt("dan")) + "\"";
result += "},";
}
if (result.length() > 0) {
result = result.substring(0, result.length() - 1);
}
out.print(result);
} catch(Exception e) {
System.out.println("=== DB 연동 오류 ===");
e.printStackTrace();
return;
} finally {
if (rs != null) rs.close();
if (pstmt != null) pstmt.close();
if (conn != null) conn.close();
}
%>
]
}
문제!
1. 라우트 사용하기
2. 백엔드는 Spring Boot와 Spring data JPA 사용
'Study > Acorn' 카테고리의 다른 글
241121 리액트 (리덕스 DB연동, RESTful) (0) | 2024.11.21 |
---|---|
241120 리액트 (Redux) (1) | 2024.11.20 |
241118 리액트 (memo / life cycle / 컴포넌트 설계 / 문제) (1) | 2024.11.18 |
241115 리액트 (useState / 문제) (0) | 2024.11.15 |
241114 리액트 (개요 / 컴포넌트 생성 / 배포 / 데이터 전달 (Props and State) / CSS 및 이미지 적용) (14) | 2024.11.14 |