이전글
https://greed-yb.tistory.com/322
boardRead.tsx
import React, {useEffect, useState} from "react";
import axios from "axios";
import {Link, useNavigate, useParams} from "react-router-dom";
import Cookies from 'js-cookie';
import { useUserContext } from '../utils/UserInfo';
// 게시판 변수 타입 설정
interface BoardRead {
title: string;
contents: string;
writer: string;
}
function BoardRead() {
// navigate 훅 사용
const navigate = useNavigate();
// 전역변수로 저장한 login 한 사용자 username
const { username } = useUserContext();
// Route path 로 받은 no 값을 받아온다
const { no } = useParams();
const [board, setBoarRead] = useState<BoardRead>({
title: "",
contents: "",
writer: ""
});
// readOnly 제어하기
const [ readOnly, setReadOnly ] = useState({
title: true,
writer: true,
contents: true
})
// button display 제어하기
const [ displayButton , setDisplayButton ] = useState(false);
useEffect(() => {
axios.get("/api/boardRead" , {
params : {
no
}
}).then((response => {
setBoarRead(response.data);
// 로그인한 사용자와 글 작성자가 같을때
if(response.data.writer == username){
setReadOnly((prevState) => ({
...prevState, // 이전 데이터를 유지
title: false, // 글쓴이가 일치한다면 해제
writer: true, // 글쓴이는 계속 true
contents: false, // 글쓴이가 일치한다면 해제
}));
// 저장 버튼 display
setDisplayButton(true);
}
})).catch((error) => {
console.log(error)
})
}, [no , username]) // 글번호와 로그인한 유저정보가 변경될 경우
// 태그의 name 을 인식해서 값을 가져온다
// HTMLInputElement 와 HTMLTextAreaElement 으로 type 을 지정한다
const dataCheck = ({ target: { value, name} }: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement> ) => {
// readOnly 가 해제 되었을때 setBoarRead 로 값을 동기화 해야 텍스트를 입력할 수 있다
setBoarRead((prevBoard) => ({
...prevBoard, // 이전 상태의 데이터를 유지
[name]: value, // 일치하는 데이터만 변경
}));
}
const axiosSave : () => Promise<void> = async () => {
const token = document.cookie;
if (!token){
// token 이 없다면 로그인 화면으로
navigate('/login');
}else{
// npm install js-cookie
// npm install @types/js-cookie
// Cookies 에서 바로 token 을 가져올 수 있게 js-cookie 를 설치해서 사용하였다
let checkToken : string | undefined = "";
if(!Cookies.get('refreshToken')){
// refreshToken 이 없다면 로그인 화면으로
navigate('/login');
}else{
checkToken = Cookies.get('refreshToken')
}
await axios.post("http://localhost:7172/api/boardSave" , board ,{
params: {checkToken}
}
).then((response) => {
if(response.data == "success"){
alert("저장하였습니다.");
navigate('/board');
}
}).catch((error) => {
alert("저장 실패하였습니다.");
})
}
}
return (
<main id="main" className="main">
<div className="pagetitle">
<h1>게시판</h1>
<nav>
<ol className="breadcrumb">
<li className="breadcrumb-item"><Link to="/">Home</Link></li>
<li className="breadcrumb-item">Forms</li>
<li className="breadcrumb-item active">Layouts</li>
</ol>
</nav>
</div>
<section className="section">
<div className="row">
<div className="col-lg-6">
<div className="card">
<div className="card-body">
<h5 className="card-title">상세보기</h5>
<form className="row g-3">
<div className="col-6">
<label htmlFor="title" className="form-label">제목</label>
<input type="text" className="form-control" id="title" name="title" value={board.title} onChange={dataCheck} readOnly={readOnly.title} />
</div>
<div className="col-6">
<label htmlFor="writer" className="form-label">글쓴이</label>
<input type="text" className="form-control" id="writer" name="writer" value={board.writer} onChange={dataCheck} readOnly={readOnly.writer} />
</div>
<div className="col-12">
<label htmlFor="contents" className="form-label">내용</label>
<textarea className="form-control" id="contents" name="contents" style={{height: "200px"}} value={board.contents} onChange={dataCheck} readOnly={readOnly.contents}></textarea>
</div>
<div className="text-center">
<button type="button" className="btn btn-primary" onClick={axiosSave} style={{display: displayButton ? `inline-block` : `none`}} >저장</button>
<Link to="/board" className="btn btn-secondary">뒤로</Link>
</div>
</form>
</div>
</div>
</div>
</div>
</section>
</main>
);
}
export default BoardRead;
로그인 또는 Jwt Token 을 이용하여 전역변수에 저장한 유저정보를 이용한다
전역변수에 따라 readOnly 을 설정하기 위한 변수 선언
전역변수에 따라 수정버튼의 display 를 설정하기 위한 변수 선언
하나에서 readOnly 와 display 를 설정해도 되는데
기능이 다르다보니 나눠놓았다
조회한 데이터의 글쓴이와 현재 로그인한 사용자가 같을 경우
preState 는 예전 데이터를 유지하기 위해 사용한다
변수명은 사용자가 원하는 대로 하면 된다
아래 title, writer, contents 를 설정함으로써
이전 데이터는 유지하면서
title, writer, contents 의 값을 변경하겠다 라는 의미인데
readOnly 에 변수를 3개만 생성하였으니 사실상 전체를 변경하는 거다
기존 데이터는 유지하면서 설정한 데이터의 값만 변경한다 라고 기억하자
title 과 contents 수정 시
setBoardRead 에 현재 작성한 내용을 변경해 준다
글을 저장할 때 데이터를 담은 변수명이 변경되었으므로 변수명만 변경해 준다
readOnly 속성에 readOnly 변수를 넣어 제어를 하고 있고
style 속성은 displayButton 변수를 삼항연산자를 사용하여 제어하고 있다
Controller
@PostMapping("/boardSave")
public String boardSave(@RequestBody Board vo, @RequestParam String checkToken) throws Exception{
try {
// refreshToken 에서 사용자의 username 을 가져온다
String username = jwtTokenUtil.getUsernameFromRefreshToken(checkToken);
// 수정이 아닌 글 작성시 글쓴이 데이터가 없기 때문에
if(vo.getWriter() == null){
vo.setWriter(username);
}
boardRepository.save(vo);
return "success";
}catch (Exception e){
e.printStackTrace();
return "유효하지 않은 사용자";
}
}
TEST
다른 부분은 수정이 되지만 글쓴이는 readOnly 가 적용되고 있다
수정되었다
다른 사용자 계정으로 접속해 본다
저장 버튼이 안 보이고 있다
작성자가 페이지에 들어오면 바로 수정이 되는 게 아니라
수정 버튼을 클릭하면 수정 페이지로 변경되거나
readOnly 설정이 해제가 되는 식으로 되는 게 보통이지만
지금 구현된 코드로 다 변경이 가능한 기능이므로 생략하였다
'개발 > Spring' 카테고리의 다른 글
[SpringBoot] React + TypeScript + JPA 프로젝트 (12) - 로그아웃 하기 (0) | 2024.11.23 |
---|---|
[SpringBoot] React + TypeScript + JPA 프로젝트 (11) - 게시판 만들기(삭제하기) (0) | 2024.11.22 |
[SpringBoot] React + TypeScript + JPA 프로젝트 (9) - 게시판 만들기(상세보기) (0) | 2024.11.20 |
[SpringBoot] React + TypeScript + JPA 프로젝트 (8) - useContext 전역변수 다루기 (0) | 2024.11.19 |
[SpringBoot] React + TypeScript + JPA 프로젝트 (7) - 게시판 만들기(글 작성) (1) | 2024.11.08 |
댓글