고래씌

[React] 7. REST 본문

React/node.js

[React] 7. REST

고래씌 2024. 1. 17. 10:21

1. REST

REST(Represntational State Transfer) : URL과 전송방법(Method)을 활용하여 작업내용을 url창에 표현하고, 필요한 상태를 전달하는 모든 행위를 의미한다.

      /board/detail → /board/detail/5(get)  board에서 5번게시물을 조회해와라.
      내가 하고자하는 것을 url에 모두 표시하는 것!



 

- BoardDetail.js, BoardUpdate.js, BoardInsert.js, BoardList.js 모두 공통적으로 들어가있는 태그이다. 이거는 따로 컴포넌트에서 만들어서 사용해보도록 하겠다!

 

 

=> 저 태그를 지우고 <></>로 묶어줘야 오류가 발생하지 않는다.

 

 

 

▶ components 폴더아래에 Outer.js 파일을 생성해주고 지웠던 태그들을 복붙하여 아래와 같이 기술

import {Outlet} from "react-router-dom"

export default function Outer(){
    return (
        <div classNmae="outer">
            <br/>
            <Outlet></Outlet>
        </div>
    )
}

 

 

 

▶ App.js에서 이런방법으로 (상대경로) 전송할 것이기 때문에 모두 경로 변경해준다.

 

=>  라우트 파라미터 문법
      /: 매개변수명
      중첩으로 여러개 작성도 가능

 

 

 

 App.js

 

=> 상세보기와 상세보기변경 지움

 

/* 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';
import Outer from './components/Outer';

function App() {

  // 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 모든데이터 = {
  //   게시글배열, 게시글배열변경함수, 상세보기, 상세보기변경
  // }

  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="/board/list">게시판</Link>  {/*  a태그로 하면 get방식&비동기. 데이터반영안됨 link로연결 */}
        <Link to={"/board"+등록페이지url} >등록</Link>{/* 리액트전용 a태그 */}
      </div>

      <Routes>
        <Route path='/' element={ <BoardList  모든데이터={모든데이터} /> } />
        <Route path="/board" element={<Outer/>}>
          {/* 하위요소는 /board/list 가 됨 */}
          <Route path='list' element={<BoardList 모든데이터={모든데이터} />}/>
          <Route path='insert' element={<BoardInsert 모든데이터={모든데이터} />}/>
          
          {/* 
            라우트 파라미터 문법
            /: 매개변수명
            중첩으로 여러개 작성도 가능
           */}
          <Route path="detail/:bno" element={<BoardDetail  모든데이터={모든데이터} />}/>
          <Route path='update/:bno' element={<BoardUpdate 모든데이터={모든데이터} />}/>
        </Route>

        {/* 위에꺼 제외한 모든 페이지, 맨 아래쪽에 위치해야함  */}
        <Route path='*' element={
          <div>
            <h1 style={ {color : "red" }} > 존재하지 않는 페이지입니다. </h1>
            <Link to='/'> 사이트로 돌아가기 </Link>
          </div>
        }/> 
      </Routes>
    </div>
  );
}

export default App;

 

 

▶ BoardDetail.js 경로 수정

=> url 창에 글번호 뽑아오는 법

=> 동일한 파라미터명으로 변수 호출하면됨

    const {bno} = useParams(); // /:파라미터명

 

=> 상세보기 불러오는 코드 변경
=> bno를 활용해서 게시글배열에서 내가 bno와 일치하는 게시글을 찾아서 넣어주기

 

=> 경로 변경

<Link to={'/board/update/' + 상세보기.글번호}>수정</Link>

 

import { Link, useParams } from 'react-router-dom';

export default function BoardDetail({모든데이터}){

    // url창에 글번호 뽑아오는 법
    const {bno} = useParams(); // /:파라미터명
    // 동일한 파라미터명으로 변수 호출하면됨
    console.log(bno);


    // 상세보기 불러오는 코드 변경
    // bno를 활용해서 게시글배열에서 내가 bno와 일치하는 게시글을 찾아서 넣어주기

    // let{상세보기} = 모든데이터;
    let 상세보기 = 모든데이터.게시글배열.find( function(게시글) {
        return 게시글.글번호 == bno;
    });
  
    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={'/board/update/' + 상세보기.글번호}>수정</Link>
          </div>
        </>
    )
  
  }

 

 

 

 BoardUpdate.js 경로 수정

 

=> 경로변경

        navigate('/board/detail/' + 입력값.글번호);  // 상대경로방식으로 이동

 

=> 상세보기변경 주석

 

import {useState} from 'react';
import { useNavigate, useParams } from 'react-router-dom';



export default function BoardUpdate({모든데이터}){

    const navigate = useNavigate();

    // 상세보기, 배열
    // let {게시글배열, 게시글배열변경함수, 상세보기, 상세보기변경} = 모든데이터;
    let {게시글배열, 게시글배열변경함수} = 모든데이터;

    let {bno} = useParams(); // {bno : 1}

    
    // let 상세보기 = 게시글배열.find(게시글 => {
    //   return 게시글.글번호 == bno
    // })

    let 상세보기 = 게시글배열.find(게시글 => 게시글.글번호 == bno);

    let [입력값, 입력값변경] = useState(상세보기); 
    // detail에서 가져왔던 상세보기 정보를 그대로 가져와줌


    console.log(입력값);
    
    function 게시글수정(){
        // 수정 완료 후 게시글 상세페이지로 이동
        let {글제목, 글내용, 작성자} = 입력값;
        if(!글제목 || !글내용 || !작성자){
            alert("뭐든 입력하세요");
            return;
        }

        const 변경된게시글배열 = 게시글배열.map(function(게시글){
            if(게시글.글번호 == 입력값.글번호) return 입력값;  // 일치한다면 입력값 반환
            return 게시글;  // 일치하지 않는다면 게시글 반환
        })
        
        게시글배열변경함수([...변경된게시글배열]); // [...변경된게시글배열] 2차원이 배열이 될수 있어서 ()를 붙여야 배열이된다.
        // 레이아웃변경(2);
        // 상세보기변경(입력값);
        navigate('/board/detail/' + 입력값.글번호);  // 상대경로방식으로 이동
    }

    // 입력했던 값들이 알맞게 추가될 것
    function onIputHandler(e){
        let {name , value} = e.target;
        입력값변경({...입력값, [name] : value});
    }   

    return (
      <>
        <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>
      </>
    )
}

 

 

 

 

 BoardInsert.js 경로 수정

=> 경로변경

navigate('/board/list'); // 상대경로방식으로 이동

 

import { useState } from "react";
import { useNavigate } from "react-router-dom";

export default function BoardInsert({모든데이터}){
    let {게시글배열 , 게시글배열변경함수} = 모든데이터;

    const navigate = useNavigate();

  let [입력값, 입력값변경] =useState({글제목 : "", 
                                    글내용 : "",
                                    작성자 : ""});
  // 입력값.글제목
  function 게시글등록 () {

    let {글내용 , 글제목, 작성자} = 입력값;

    if( !글내용 || !글제목  || !작성자 ){
      alert("뭐든 입력하십쇼");
      return;
    }

    //2) 가져온 데이터를 바탕으로 게시글 객체 생성하기
    let 게시글 = {
        //글번호는 게시글배열에서 고유해야함. 게시글배열에서 가장큰 글번호값을 찾은후 +1해서 반환해줄 예정.
        글번호 : Math.max( ...게시글배열.map( function(게시글, 인덱스) {return 게시글.글번호})) +1,
        ...입력값, // 한줄로 키밸류값으로 가져옴
        작성일 : new Date().toLocaleDateString() // 2024. 01. 15
    }
    //3) 생성한 게시글객체를 게시글배열에 추가한 후 , 게시글배열변경함수 호출하기(랜더링)   
    게시글배열변경함수([...게시글배열 , 게시글]);

    //입력값 초기화(state초기화)
    입력값변경({
      글제목 : "",
      글내용 : "",
      작성자 : ""
    })

    navigate('/board/list'); // 상대경로방식으로 이동
  }
  function onIputHandler(e){
    let {name , value} = e.target;
    입력값변경({...입력값, [name] : value});
  }   
    return (
        <>
          <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>
        </>

    )
}

//export default BoardInsert;

 

 

 

 BoardList.js 경로 수정

=> 상세보기변경 주석처리

 

=> 경로수정

navigate('/board/detail/' + 조회할글번호);

 

 

import { useNavigate } from "react-router-dom";

export default function BoardList({모든데이터}){
    // console.log(props);

    // let {게시글배열, 게시글배열변경함수, 상세보기변경} = 모든데이터;
    let {게시글배열, 게시글배열변경함수} = 모든데이터;

    const navigate = useNavigate();

    function 게시글삭제(삭제할글번호) {

        //2) 게시글배열에서 글번호와 일치하지 않는 게시글만 필터링하기. (filter함수 이용)
        let 필터링배열 = 게시글배열.filter( function(게시글) { 
          return 게시글.글번호 !== 삭제할글번호; 
        }); 
        게시글배열변경함수(필터링배열); 

    }

    
    function 게시글상세조회(조회할글번호) {
        let 상세게시글 = 게시글배열.find( function(게시글) { 
          
          return 게시글.글번호 == 조회할글번호;
        });
        
        // 레이아웃변경(2);
        // navigate로 이동하고자 하는 페이지 적어주면됨
        navigate('/board/detail/' + 조회할글번호);
        // 상세보기변경(상세게시글);
    }

    return (
      <>
        <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={ () =>  게시글상세조회(게시글.글번호)}>
                {/* <tr key={인덱스} onClick={ () =>   */}
                 {/* {navigate('/board/detail' + 게시글.글번호); */}
                  {/* 상세보기변경(게시글); */}
                   {/* }}> */}
                  <td>{게시글.글번호}</td>
                  <td>{게시글.글제목}</td>
                  <td>{게시글.작성자}</td>
                  <td>{게시글.작성일}</td>
                  <td><button onClick={(e) => {
                    e.stopPropagation(); // 이벤트 전파 방지
                    게시글삭제(게시글.글번호)}}
                    >삭제</button>
                  </td>
                </tr>
                )
             })
            }
          </tbody>
        </table>
      </>
    )
}

 

 

 

-  해당목록에서 첫번째 게시글을 클릭했을때 다음과 같이 url이 변경됐음을 확인할 수 있다.

 

 

▶ 결과

'React > node.js' 카테고리의 다른 글

[React] 9. axios, ContextApi  (0) 2024.01.17
[React] 8. useEffect  (0) 2024.01.17
[React] 6. 라우터  (0) 2024.01.16
[React] 5-2. 게시글 수정  (0) 2024.01.16
[React] 5-1. 게시판 상세페이지 조회  (0) 2024.01.16