고래씌
[React] 6. 라우터 본문
1. 라우터
▶ Router : url에 명시된 자원을 찾는 과정을 routing이라고 부른다.
https://...../main/main.kh => 메인페이지를 찾음
https://...../project/classList.kh => 프로젝트페이지를 찾음. 즉 url자원을 해석하여 알맞은 html페이지를 찾아서 클라이언트가 요청한 페이지를 찾아서 반환해주는 소프트웨어를 Router라고 부름.
▶ React Router : react에서 사용자가 요청한 url을 기반으로 알맞은 컴포넌트를 찾아서 화면을 랜더링해주는 외부 라이브러리로, react에서 가장 많이 사용된다.
2. 라우터 설치
▶ 새터미널 창을 열고, Router 설치
npm install --save react-router-dom |
▶ index.js 세팅
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
|
☞ 페이지 역할을 하는 것은 src 폴더아래에 pages 폴더에서 관리를한다!(관례임)
① src 폴더아래 pages 폴더 생성
② components 폴더에 있던 BoardDetail.js, BaordUpdate.js, BaordList.js, BoardInsert.js 파일들을 잘라내기한 후, 모두 pages 폴더에 붙여놓는다.

▶ App.js
import { Route, Routes } from 'react-router-dom';
|
- 우리가 지금까지 써왔던 레이아웃 == 0 ? ~ 방식은 지우고 모두 라우터 방식으로 변경
<Routes>
<Route path='/' element={<BoardList 게시글배열={게시글배열} 게시글배열변경함수={게시글배열변경함수}
상세보기변경={상세보기변경} 레이아웃변경={레이아웃변경} />}/>
<Route path='/list' element={<BoardList 게시글배열={게시글배열} 게시글배열변경함수={게시글배열변경함수}
상세보기변경={상세보기변경} 레이아웃변경={레이아웃변경} />} />
<Route path="/insert" element={<BoardInsert 게시글배열={게시글배열} 게시글배열변경함수={게시글배열변경함수}/>} />
<Route path="/detail" element={<BoardDetail 상세보기={상세보기} 레이아웃변경={레이아웃변경}/>} />
<Route paht="/update" element={<BoardUpdate 상세보기변경={상세보기변경} 게시글배열={게시글배열}
게시글배열변경함수={게시글배열변경함수} 상세보기={상세보기} 레이아웃변경={레이아웃변경} />} />
</Routes>
|
/* eslint-disable */
//콘솔창에 불필요한 에러를 지우는 코드
import './App.css';
import {useState} from 'react';
import 초기게시글 from './data'; //default옵션으로 export한 경우 변수명을 다르게 가져올 수 있음
import {a,b,c} from './data'; //a,b,c의 값을 가지고 옴
import BoardInsert from './pages/BoardInsert';
import BoardList from './pages/BoardList';
import BoardDetail from './pages/BoardDetail';
import BoardUpdate from './pages/BoardUpdate';
import { Route, Routes, Link } from 'react-router-dom';
function App() {
//let 제목 = "KH C CLASS";
// state 문법
let [제목2, 제목변경함수] = useState('KH E CLASS');
let [게시글배열, 게시글배열변경함수] = useState(초기게시글);
//2) 레이아웃상태를 state로 저장시키기
let [레이아웃, 레이아웃변경] = useState(0);
function 제목2변경 (){
// 제목2 = "KH C CLASS"; // 단순 대입연산자를 활용하는 경우 state값의 변경점을 reactDom이 알지못함
제목변경함수("KH C CLASS"); //useState의 두번째 매개변수로 전달받은 함수를 통해 변경 시 화면이 재랜더링됨
}
let [상세보기, 상세보기변경] = useState(null); // 각 게시글 정보 담아줄 예정
let 등록페이지url = "/insert";
let 모든데이터 = {
게시글배열, 게시글배열변경함수, 상세보기, 상세보기변경
}
return (
<div className="App">
<div className="header">
<h3 style={ {fontWeight : "bolder"} }> {제목2} </h3>
</div>
<div className='nav'>
{/* <button onClick={function(){ // ui상태관리 -3) state변경함수를 버튼등의 요소에 부여
레이아웃변경(0);
}}> 게시판 </button>
<button onClick={function(){
레이아웃변경(1);
}}> 등록 </button> */}
<Link to="/list">게시판</Link> {/* a태그로 하면 get방식,비동기. 데이터반영안됨*/}
<Link to={등록페이지url} >등록</Link>{/* 리액트전용 a태그 */}
</div>
<Routes>
<Route path='/' element={ <BoardList 모든데이터={모든데이터} /> } />
<Route path='/list' element={<BoardList 모든데이터={모든데이터} />}/>
<Route path='/insert' element={<BoardInsert 모든데이터={모든데이터} />}/>
<Route path='/detail' element={<BoardDetail 모든데이터={모든데이터} />}/>
<Route path='/update' element={<BoardUpdate 모든데이터={모든데이터} />}/>
{/* 사용자가 잘못된 url를 입력했을 때 에러페이지로 보내도록함. *: 모든경로 의미(항상 맨마지막에 작성) */}
<Route path='*' element={
<div>
<h1 style={ {color : "red" }} > 존재하지 않는 페이지입니다. </h1>
<Link to='/'> 사이트로 돌아가기 </Link>
</div>
}/>
{/* 위에꺼 제외한 모든 페이지, 맨 아래쪽에 위치해야함 */}
</Routes>
{/*
레이아웃 == 0?
<BoardList 게시글배열={게시글배열} 게시글배열변경함수={게시글배열변경함수} 상세보기변경={상세보기변경} 레이아웃변경={레이아웃변경} /> :
레이아웃 == 1 ?
<BoardInsert 게시글배열={게시글배열} 게시글배열변경함수={게시글배열변경함수}/> :
레이아웃 == 2 ?
<BoardDetail 상세보기={상세보기} 레이아웃변경={레이아웃변경}/> :
레이아웃 == 3 ?
<BoardUpdate 게시글배열={게시글배열} 게시글배열변경함수={게시글배열변경함수} 상세보기={상세보기} 상세보기변경={상세보기변경} 레이아웃변경={레이아웃변경} /> :
null
*/ }
</div>
);
}
export default App;
1) Link태그
- 게시판/등록 버튼도 Link로 모두 변경해준다.
=> Link는 리액트의 a 태그와 같다
import { Route, Routes, Link } from 'react-router-dom';
|
<Link to="/list"> 게시판 </Link>
<Link to="/insert">등록</Link>
|
=> 이방법도 가능하다.
let 등록페이지url = "/insert";
<Link to={등록페이지url}> 등록 </Link>
|

=> 하위로 들어가게되는거라서 /board/~ 이런식으로 다 들어간다!
=> 일단 이방법말고 위에 있는 방식으로 진행
2) 모든데이터 보내기
코드를 줄이기 위해 모든데이터 변수를 만들어준다!
=> 딱 필요한 데이터만 전달해야하는데 지금은 모든데이터를 긁어와서 보내줌.
=> 프로젝트할 때는 딱 필요한 데이터만 전달해야한다!
▶ App.js
let 모든데이터 = {
게시글배열, 게시글배열변경함수, 상세보기, 상세보기변경
}
|
- Routes에 있는 데이터들도 모두 "모든데이터={모든데이터} />" 로 변경
<Routes>
<Route path='/' element={ <BoardList 모든데이터={모든데이터} /> } />
<Route path='/list' element={<BoardList 모든데이터={모든데이터} />}/>
<Route path='/insert' element={<BoardInsert 모든데이터={모든데이터} />}/>
<Route path='/detail' element={<BoardDetail 모든데이터={모든데이터} />}/>
<Route path='/update' element={<BoardUpdate 모든데이터={모든데이터} />}/>
{/* 사용자가 잘못된 url를 입력했을 때 에러페이지로 보내도록함. *: 모든경로 의미(항상 맨마지막에 작성) */}
<Route path='*' element={
<div>
<h1 style={ {color : "red" }} > 존재하지 않는 페이지입니다. </h1>
<Link to='/'> 사이트로 돌아가기 </Link>
</div>
}/>
{/* 위에꺼 제외한 모든 페이지, 맨 아래쪽에 위치해야함 */}
|
- 이제 레이아웃변경을 없앴으니 BoardDetail.js, BoardUpdate.js, BaordInsert.js, BoardList.js 도 작업해주어야한다.
=> props 를 모든데이터로 변경
=> 레이아웃변경에서 navigate 사용
▶ BoardDetail.js

// import { useNavigate } from "react-router-dom";
import { Link } from 'react-router-dom';
export default function BoardDetail({모든데이터}){
// const navigate = useNavigate();
let{상세보기} = 모든데이터;
return(
<>
<table className='detail-table'>
<tr>
<th colSpan={4}>{상세보기.글제목}</th>
</tr>
<tr>
<th>작성자</th>
<td>{상세보기.작성자}</td>
<th>작성일</th>
<td>{상세보기.작성일}</td>
</tr>
<tr>
<th>글내용</th>
<td colSpan={3} style={{ height: "200px" }}>{상세보기.글내용}</td>
</tr>
</table>
<div className='btn-area'>
<Link to={'/update'}>수정</Link>
</div>
</>
)
}
▶ BoardUpdate.js

import {useState} from 'react';
import { useNavigate } from 'react-router-dom';
export default function BoardUpdate({모든데이터}){
const navigate = useNavigate();
// 상세보기, 배열
let {게시글배열, 게시글배열변경함수, 상세보기, 상세보기변경} = 모든데이터;
let [입력값, 입력값변경] = useState(상세보기);
// detail에서 가져왔던 상세보기 정보를 그대로 가져와줌
console.log(입력값);
function 게시글수정(){
// 수정 완료 후 게시글 상세페이지로 이동
let {글제목, 글내용, 작성자} = 입력값;
if(!글제목 || !글내용 || !작성자){
alert("뭐든 입력하세요");
return;
}
const 변경된게시글배열 = 게시글배열.map(function(게시글){
if(게시글.글번호 == 입력값.글번호) return 입력값; // 일치한다면 입력값 반환
return 게시글; // 일치하지 않는다면 게시글 반환
})
게시글배열변경함수([...변경된게시글배열]); // [...변경된게시글배열] 2차원이 배열이 될수 있어서 ()를 붙여야 배열이된다.
// 레이아웃변경(2);
상세보기변경(입력값);
navigate('/detail');
}
// 입력했던 값들이 알맞게 추가될 것
function onIputHandler(e){
let {name , value} = e.target;
입력값변경({...입력값, [name] : value});
}
return (
<div className='outer'>
<br />
<h2>수정</h2>
<table className='enroll-table'>
<tr>
<th>제목</th>
<td colSpan={3}>
<input type='text' name='글제목'
onChange={onIputHandler}
value={입력값.글제목}
/>
</td>
</tr>
<tr>
<th>작성자</th>
<td colSpan={3}>
<input type='text' name='작성자'
onChange={onIputHandler}
value={입력값.작성자}
/>
</td>
</tr>
<tr>
<th>글내용</th>
<td colSpan={3} style={ { height: "200px" } }>
<textarea name='글내용'
onChange={onIputHandler}
value={입력값.글내용}
></textarea>
</td>
</tr>
<tr>
<th colSpan={4}><button onClick={ 게시글수정}>수정</button></th>
</tr>
</table>
</div>
)
}
▶ BoardInsert.js

import { useState } from "react";
import { useNavigate } from "react-router-dom";
export default function BoardInsert({모든데이터}){
let {게시글배열 , 게시글배열변경함수} = 모든데이터;
const navigate = useNavigate();
let [입력값, 입력값변경] =useState({글제목 : "",
글내용 : "",
작성자 : ""});
// 입력값.글제목
function 게시글등록 () {
// "" => false , "dasdsa" => true
let {글내용 , 글제목, 작성자} = 입력값;
if( !글내용 || !글제목 || !작성자 ){
alert("뭐든 입력하십쇼");
return;
}
//2) 가져온 데이터를 바탕으로 게시글 객체 생성하기
let 게시글 = {
//글번호는 게시글배열에서 고유해야함. 게시글배열에서 가장큰 글번호값을 찾은후 +1해서 반환해줄 예정.
글번호 : Math.max( ...게시글배열.map( function(게시글, 인덱스) {return 게시글.글번호})) +1,
...입력값, // 한줄로 키밸류값으로 가져옴
작성일 : new Date().toLocaleDateString() // 2024. 01. 15
}
//3) 생성한 게시글객체를 게시글배열에 추가한 후 , 게시글배열변경함수 호출하기(랜더링)
게시글배열변경함수([...게시글배열 , 게시글]);
//입력값 초기화(state초기화)
입력값변경({
글제목 : "",
글내용 : "",
작성자 : ""
})
navigate('/list');
}
function onIputHandler(e){
let {name , value} = e.target;
입력값변경({...입력값, [name] : value});
}
return (
<div className='outer'>
<br />
<h2>등록</h2>
<table className='enroll-table'>
<tr>
<th>제목</th>
<td colSpan={3}>
<input type='text' name='글제목'
onChange={function(e){
입력값변경({...입력값, 글제목 : e.target.value});
}}
value={입력값.글제목} // 입력값 내부에 있는 글제목값을 가져와라
/>
</td>
</tr>
<tr>
<th>작성자</th>
<td colSpan={3}>
<input type='text' name='작성자'
onChange={function(e){
console.dir(e.target)
입력값변경({
...입력값 ,
[e.target.name] : e.target.value // 입력하고자하는 값은 뒤에 놔야함
// [] 표기법을 통해 속성명을 지정해줘야함. [e.target.name]에는 작성자가 들어감
})
}}
value={입력값.작성자} // 입력값 내부에 있는 작성자값을 가져와라
/>
</td>
</tr>
<tr>
<th>글내용</th>
<td colSpan={3} style={ { height: "200px" } }>
<textarea name='글내용'
onChange={onIputHandler}
value={입력값.글내용}
></textarea>
</td>
</tr>
<tr>
<th colSpan={4}><button onClick={게시글등록}>등록</button></th>
</tr>
</table>
</div>
)
}
//export default BoardInsert;
▶BoardList.js


=> 레이아웃변경대신 navigate 이용
import { useNavigate } from "react-router-dom";
export default function BoardList({모든데이터}){
// console.log(props);
let {게시글배열, 게시글배열변경함수, 상세보기변경} = 모든데이터;
const navigate = useNavigate();
function 게시글삭제(삭제할글번호) {
//2) 게시글배열에서 글번호와 일치하지 않는 게시글만 필터링하기. (filter함수 이용)
let 필터링배열 = 게시글배열.filter( function(게시글) {
return 게시글.글번호 !== 삭제할글번호;
});
게시글배열변경함수(필터링배열);
}
function 게시글상세조회(조회할글번호) {
let 상세게시글 = 게시글배열.find( function(게시글) {
return 게시글.글번호 == 조회할글번호;
});
// 레이아웃변경(2);
// navigate로 이동하고자 하는 페이지 적어주면됨
navigate('/detail');
상세보기변경(상세게시글);
}
return (
<div className='outer'>
<br />
<h2>일반게시판</h2>
<table className='list-table'>
<thead>
<tr>
<th style={ {width : "10%" } }>번호</th>
<th style={ {width : "40%" } }>제목</th>
<th style={ {width : "20%" } }>작성자</th>
<th style={ {width : "20%" } }>작성일</th>
<th style={ {width : "10%" } }>삭제</th>
</tr>
</thead>
<tbody>
{/*
Array내부의 map함수 사용 예정
[1,2,3].map(function () { return 1}) ==> [1,1,1]
*/
게시글배열.map(function( 게시글, 인덱스) {
return (
<tr key={인덱스} onClick={ () => 게시글상세조회(게시글.글번호)}>
<td>{게시글.글번호}</td>
<td>{게시글.글제목}</td>
<td>{게시글.작성자}</td>
<td>{게시글.작성일}</td>
<td><button onClick={(e) => {
e.stopPropagation(); // 이벤트 전파 방지
게시글삭제(게시글.글번호)}}
>삭제</button>
</td>
</tr>
)
})
}
</tbody>
</table>
</div>
)
}
'React > node.js' 카테고리의 다른 글
[React] 8. useEffect (0) | 2024.01.17 |
---|---|
[React] 7. REST (0) | 2024.01.17 |
[React] 5-2. 게시글 수정 (0) | 2024.01.16 |
[React] 5-1. 게시판 상세페이지 조회 (0) | 2024.01.16 |
[React] 4. 동적으로 ui상태 관리하기(레이아웃) (0) | 2024.01.16 |