고래씌

[React] 6. 라우터 본문

React/node.js

[React] 6. 라우터

고래씌 2024. 1. 16. 14:50

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