reactboard
Boardsql.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="data.mapper.BoardMapper">
<select id="getTotalCount" resultType="int">
select count(*) from reactboard
</select>
<insert id="insertBoard" parameterType="BoardDto">
insert into reactboard values (null,#{myid},#{myname},#{photo},#{subject},
#{content},0,now())
</insert>
<select id="getPagingList" parameterType="Map" resultType="BoardDto">
select * from reactboard order by num desc limit #{start},#{perpage}
</select>
<update id="updateReadcount" parameterType="int">
update reactboard set readcount=readcount+1 where num=#{num}
</update>
<select id="detailPage" parameterType="int" resultType="BoardDto">
select * from reactboard where num=#{num}
</select>
<delete id="deleteBoard" parameterType="int">
delete from reactboard where num=#{num}
</delete>
</mapper>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="data.mapper.BoardMapper">
<select id="getTotalCount" resultType="int">
select count(*) from reactboard
</select>
<insert id="insertBoard" parameterType="BoardDto">
insert into reactboard values (null,#{myid},#{myname},#{photo},#{subject},
#{content},0,now())
</insert>
<select id="getPagingList" parameterType="Map" resultType="BoardDto">
select * from reactboard order by num desc limit #{start},#{perpage}
</select>
<update id="updateReadcount" parameterType="int">
update reactboard set readcount=readcount+1 where num=#{num}
</update>
<select id="detailPage" parameterType="int" resultType="BoardDto">
select * from reactboard where num=#{num}
</select>
<delete id="deleteBoard" parameterType="int">
delete from reactboard where num=#{num}
</delete>
</mapper>
BoardMapper
package data.mapper;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Mapper;
import data.dto.BoardDto;
@Mapper
public interface BoardMapper {
public void insertBoard(BoardDto dto);
public List<BoardDto> getPagingList(Map<String, Integer> map);
public void updateReadcount(int num);
public BoardDto detailPage(int num);
public void deleteBoard(int num);
public int getTotalCount();
}
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Mapper;
import data.dto.BoardDto;
@Mapper
public interface BoardMapper {
public void insertBoard(BoardDto dto);
public List<BoardDto> getPagingList(Map<String, Integer> map);
public void updateReadcount(int num);
public BoardDto detailPage(int num);
public void deleteBoard(int num);
public int getTotalCount();
}
BoardServiceInter
package data.service;
import java.util.List;
import data.dto.BoardDto;
public interface BoardServiceInter {public int getTotalCount();
public void insertBoard(BoardDto dto);
public List<BoardDto> getPagingList(int start,int perpage);
public void updateReadcount(int num);
public BoardDto detailPage(int num);
public void deleteBoard(int num);
}
import java.util.List;
import data.dto.BoardDto;
public interface BoardServiceInter {public int getTotalCount();
public void insertBoard(BoardDto dto);
public List<BoardDto> getPagingList(int start,int perpage);
public void updateReadcount(int num);
public BoardDto detailPage(int num);
public void deleteBoard(int num);
}
BoardService
package data.service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.stereotype.Service;
import data.dto.BoardDto;
import data.mapper.BoardMapper;
import lombok.AllArgsConstructor;
@Service
@AllArgsConstructor
public class BoardService implements BoardServiceInter {
private BoardMapper boardMapper;
@Override
public int getTotalCount() {
// TODO Auto-generated method stub
return boardMapper.getTotalCount();
}
@Override
public void insertBoard(BoardDto dto) {
// TODO Auto-generated method stub
boardMapper.insertBoard(dto);
}
@Override
public List<BoardDto> getPagingList(int start, int perpage) {
// TODO Auto-generated method stub
Map<String, Integer> map=new HashMap<>();
map.put("start", start);
map.put("perpage", perpage);
return boardMapper.getPagingList(map);
}
@Override
public void updateReadcount(int num) {
// TODO Auto-generated method stub
boardMapper.updateReadcount(num);
}
@Override
public BoardDto detailPage(int num) {
// TODO Auto-generated method stub
return boardMapper.detailPage(num);
}
@Override
public void deleteBoard(int num) {
// TODO Auto-generated method stub
boardMapper.deleteBoard(num);
}
}
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.stereotype.Service;
import data.dto.BoardDto;
import data.mapper.BoardMapper;
import lombok.AllArgsConstructor;
@Service
@AllArgsConstructor
public class BoardService implements BoardServiceInter {
private BoardMapper boardMapper;
@Override
public int getTotalCount() {
// TODO Auto-generated method stub
return boardMapper.getTotalCount();
}
@Override
public void insertBoard(BoardDto dto) {
// TODO Auto-generated method stub
boardMapper.insertBoard(dto);
}
@Override
public List<BoardDto> getPagingList(int start, int perpage) {
// TODO Auto-generated method stub
Map<String, Integer> map=new HashMap<>();
map.put("start", start);
map.put("perpage", perpage);
return boardMapper.getPagingList(map);
}
@Override
public void updateReadcount(int num) {
// TODO Auto-generated method stub
boardMapper.updateReadcount(num);
}
@Override
public BoardDto detailPage(int num) {
// TODO Auto-generated method stub
return boardMapper.detailPage(num);
}
@Override
public void deleteBoard(int num) {
// TODO Auto-generated method stub
boardMapper.deleteBoard(num);
}
}
BoardController.java
package data.controller;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import data.dto.BoardDto;
import data.service.BoardService;
import naver.cloud.NcpObjectStorageService;
@RestController
@CrossOrigin
@RequestMapping("/board")
public class BoardController {
@Autowired
private NcpObjectStorageService storageService;
//버켓이름지정
private String bucketName="bit701-bucket-94";//각자 자기 버켓이름
//사진 업로드할 변수
String photo;
@Autowired
private BoardService boardService;
@PostMapping("/upload")
public String photoUpload(MultipartFile upload)
{
if(photo!=null) {
//이전 사진 삭제
storageService.deleteFile(bucketName, "board", photo);
}
photo=storageService.uploadFile(bucketName, "board", upload);
return photo;
}
@PostMapping("/insert")
public void insert(@RequestBody BoardDto dto)
{
System.out.println("dto>>"+dto);
dto.setPhoto(photo);
boardService.insertBoard(dto);
photo=null;
}
@GetMapping("/detail")
public BoardDto detailPage(int num)
{
System.out.println("detail>>"+num);
//조회수 먼저 증가
boardService.updateReadcount(num);
return boardService.detailPage(num);
}
@GetMapping("/list")
public Map<String, Object> list(@RequestParam(defaultValue = "1") int currentPage)
{
System.out.println("list>>"+currentPage);
//페이징처리
int totalCount;//총갯수
int perPage=3;//한페이지당 출력할 글갯수
int perBlock=3;//출력할 페이지갯수
int startNum;//db에서 가져올 시작번호
int startPage;//출력할 시작페이지
int endPage;//출력할 끝페이지
int totalPage;//총 페이지수
int no;//출력할 시작번호
//총갯수
totalCount=boardService.getTotalCount();
//총 페이지수
totalPage=totalCount/perPage+(totalCount%perPage==0?0:1);
//시작페이지
startPage=(currentPage-1)/perBlock*perBlock+1;
//끝페이지
endPage=startPage+perBlock-1;
if(endPage>totalPage)
endPage=totalPage;
//시작번호
startNum=(currentPage-1)*perPage;
//각페이지당 출력할 번호
no=totalCount-(currentPage-1)*perPage;
List<BoardDto> list=boardService.getPagingList(startNum, perPage);
//출력할 페이지번호들을 Vector에 담아서 보내기
Vector<Integer> parr=new Vector<>();
for(int i=startPage;i<=endPage;i++){
parr.add(i);
}
//리액트로 필요한 변수들을 Map 에 담아서 보낸다
Map<String,Object> smap=new HashMap<>();
smap.put("totalCount",totalCount);
smap.put("list",list);
smap.put("parr",parr);
smap.put("startPage",startPage);
smap.put("endPage",endPage);
smap.put("no",no);
smap.put("totalPage",totalPage);
return smap;
}
@DeleteMapping("/delete")
public void delete(int num)
{
//num 에 해당하는 사진 스토리지에서 지우기
String prePhoto=boardService.detailPage(num).getPhoto();
storageService.deleteFile(bucketName, "board", prePhoto);
//db 에서 데이타 삭제
boardService.deleteBoard(num);
}
}
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import data.dto.BoardDto;
import data.service.BoardService;
import naver.cloud.NcpObjectStorageService;
@RestController
@CrossOrigin
@RequestMapping("/board")
public class BoardController {
@Autowired
private NcpObjectStorageService storageService;
//버켓이름지정
private String bucketName="bit701-bucket-94";//각자 자기 버켓이름
//사진 업로드할 변수
String photo;
@Autowired
private BoardService boardService;
@PostMapping("/upload")
public String photoUpload(MultipartFile upload)
{
if(photo!=null) {
//이전 사진 삭제
storageService.deleteFile(bucketName, "board", photo);
}
photo=storageService.uploadFile(bucketName, "board", upload);
return photo;
}
@PostMapping("/insert")
public void insert(@RequestBody BoardDto dto)
{
System.out.println("dto>>"+dto);
dto.setPhoto(photo);
boardService.insertBoard(dto);
photo=null;
}
@GetMapping("/detail")
public BoardDto detailPage(int num)
{
System.out.println("detail>>"+num);
//조회수 먼저 증가
boardService.updateReadcount(num);
return boardService.detailPage(num);
}
@GetMapping("/list")
public Map<String, Object> list(@RequestParam(defaultValue = "1") int currentPage)
{
System.out.println("list>>"+currentPage);
//페이징처리
int totalCount;//총갯수
int perPage=3;//한페이지당 출력할 글갯수
int perBlock=3;//출력할 페이지갯수
int startNum;//db에서 가져올 시작번호
int startPage;//출력할 시작페이지
int endPage;//출력할 끝페이지
int totalPage;//총 페이지수
int no;//출력할 시작번호
//총갯수
totalCount=boardService.getTotalCount();
//총 페이지수
totalPage=totalCount/perPage+(totalCount%perPage==0?0:1);
//시작페이지
startPage=(currentPage-1)/perBlock*perBlock+1;
//끝페이지
endPage=startPage+perBlock-1;
if(endPage>totalPage)
endPage=totalPage;
//시작번호
startNum=(currentPage-1)*perPage;
//각페이지당 출력할 번호
no=totalCount-(currentPage-1)*perPage;
List<BoardDto> list=boardService.getPagingList(startNum, perPage);
//출력할 페이지번호들을 Vector에 담아서 보내기
Vector<Integer> parr=new Vector<>();
for(int i=startPage;i<=endPage;i++){
parr.add(i);
}
//리액트로 필요한 변수들을 Map 에 담아서 보낸다
Map<String,Object> smap=new HashMap<>();
smap.put("totalCount",totalCount);
smap.put("list",list);
smap.put("parr",parr);
smap.put("startPage",startPage);
smap.put("endPage",endPage);
smap.put("no",no);
smap.put("totalPage",totalPage);
return smap;
}
@DeleteMapping("/delete")
public void delete(int num)
{
//num 에 해당하는 사진 스토리지에서 지우기
String prePhoto=boardService.detailPage(num).getPhoto();
storageService.deleteFile(bucketName, "board", prePhoto);
//db 에서 데이타 삭제
boardService.deleteBoard(num);
}
}
BoardForm.js
import React, { useState } from 'react';
import '../App.css';
import { useNavigate } from 'react-router-dom';
import Axios from 'axios';
function BoardForm(props) {
const [subject,setSubject]=useState('');
const [photo,setPhoto]=useState('');
const [content,setContent]=useState('');
const navi=useNavigate();
//이미지 경로
const photoUrl=process.env.REACT_APP_BOARDURL;
//세션스토리지에 저장된 아이디와 이름 가져오기
const myid=sessionStorage.myid;
const myname=sessionStorage.myname;
const onSubmitEvent=(e)=>{
e.preventDefault();
Axios.post("/board/insert",{myid,myname,subject,content})
.then(res=>{
//목록으로 이동
navi("/board/list")
})
}
//파일 업로드
const onUploadEvent=(e)=>{
const uploadFile=new FormData();
uploadFile.append("upload",e.target.files[0]);
Axios({
method:'post',
url:'/board/upload',
data:uploadFile,
headers:{'Content-Type':'multipart/form-data'}
}).then(res=>{
setPhoto(res.data);
});
}
return (
<div style={{marginLeft:'100px',width:'400px'}}>
<form onSubmit={onSubmitEvent}>
<table className='table'>
<caption align="top"><b>게시판글쓰기</b></caption>
<tbody>
<tr>
<th style={{backgroundColor:'#ddd',width:'100px'}}>제목</th>
<td>
<input type='text' className='form-control' required
onChange={(e)=>setSubject(e.target.value)} value={subject}/>
</td>
</tr>
<tr>
<th style={{backgroundColor:'#ddd',width:'100px'}}>사진</th>
<td>
<input type='file' className='form-control' onChange={onUploadEvent}/>
</td>
</tr>
<tr>
<th style={{backgroundColor:'#ddd',width:'100px'}}>내용</th>
<td>
<textarea style={{width:'100%',height:'100px'}}
required value={content} onChange={(e)=>setContent(e.target.value)}></textarea>
</td>
</tr>
<tr>
<td colSpan={2} align='center'>
<button type='submit' className='btn btn-outline-info'
style={{width:'100px'}}>글저장</button>
</td>
</tr>
</tbody>
</table>
</form>
<img alt='' src={`${photoUrl}${photo}`}
style={{width:'200px',position:'absolute',left:'600px',top:'160px'}}/>
</div>
);
}
export default BoardForm;
import '../App.css';
import { useNavigate } from 'react-router-dom';
import Axios from 'axios';
function BoardForm(props) {
const [subject,setSubject]=useState('');
const [photo,setPhoto]=useState('');
const [content,setContent]=useState('');
const navi=useNavigate();
//이미지 경로
const photoUrl=process.env.REACT_APP_BOARDURL;
//세션스토리지에 저장된 아이디와 이름 가져오기
const myid=sessionStorage.myid;
const myname=sessionStorage.myname;
const onSubmitEvent=(e)=>{
e.preventDefault();
Axios.post("/board/insert",{myid,myname,subject,content})
.then(res=>{
//목록으로 이동
navi("/board/list")
})
}
//파일 업로드
const onUploadEvent=(e)=>{
const uploadFile=new FormData();
uploadFile.append("upload",e.target.files[0]);
Axios({
method:'post',
url:'/board/upload',
data:uploadFile,
headers:{'Content-Type':'multipart/form-data'}
}).then(res=>{
setPhoto(res.data);
});
}
return (
<div style={{marginLeft:'100px',width:'400px'}}>
<form onSubmit={onSubmitEvent}>
<table className='table'>
<caption align="top"><b>게시판글쓰기</b></caption>
<tbody>
<tr>
<th style={{backgroundColor:'#ddd',width:'100px'}}>제목</th>
<td>
<input type='text' className='form-control' required
onChange={(e)=>setSubject(e.target.value)} value={subject}/>
</td>
</tr>
<tr>
<th style={{backgroundColor:'#ddd',width:'100px'}}>사진</th>
<td>
<input type='file' className='form-control' onChange={onUploadEvent}/>
</td>
</tr>
<tr>
<th style={{backgroundColor:'#ddd',width:'100px'}}>내용</th>
<td>
<textarea style={{width:'100%',height:'100px'}}
required value={content} onChange={(e)=>setContent(e.target.value)}></textarea>
</td>
</tr>
<tr>
<td colSpan={2} align='center'>
<button type='submit' className='btn btn-outline-info'
style={{width:'100px'}}>글저장</button>
</td>
</tr>
</tbody>
</table>
</form>
<img alt='' src={`${photoUrl}${photo}`}
style={{width:'200px',position:'absolute',left:'600px',top:'160px'}}/>
</div>
);
}
export default BoardForm;
BoardList.js
import React, {useEffect, useState } from 'react';
import '../App.css';
import { Link, NavLink, useNavigate, useParams } from 'react-router-dom';
import Axios from 'axios';
import BoardRowList from './BoardRowList';
function BoardList(props) {
const [data,setData]=useState('');
const {currentPage}=useParams();
console.log({currentPage});
const navi=useNavigate();
//페이징처리에 필요한 데이타 가져오기
const list=()=>{
const url="/board/list?currentPage="+(currentPage==null?1:currentPage);
Axios.get(url)
.then(res=>{
setData(res.data);
})
}
useEffect(()=>{
list();
},[currentPage]);
const onWriteButtonEvent=()=>{
if(sessionStorage.loginok==null){
alert("먼저 로그인을 해주세요");
navi("/login");
}else{
navi("/board/form")
}
}
return (
<div style={{marginLeft:'30px'}}>
<button type='button' className='btn btn-outline-success'
style={{width:'100px',marginLeft:'100px'}} onClick={onWriteButtonEvent}>글쓰기</button>
<br/><br/>
<h5><b>총 {data.totalCount}개의 글이 있습니다</b></h5>
<table className='table table-bordered' style={{width:'700px'}}>
<thead>
<tr style={{backgroundColor:'#ddd'}}>
<th style={{width:'40px'}}>번호</th>
<th style={{width:'200px'}}>제목</th>
<th style={{width:'70px'}}>작성자</th>
<th style={{width:'100px'}}>작성일</th>
<th style={{width:'50px'}}>조회</th>
</tr>
</thead>
<tbody>
{
data.list &&
data.list.map((row,idx)=>
<BoardRowList key={idx} row={row} no={data.no} idx={idx}
currentPage={currentPage}/>)
}
</tbody>
</table>
<div style={{width:'800px',textAlign:'center'}}>
{/* 페이징처리 */}
{
//이전
data.startPage>1?
<Link to={`/board/list/${data.startPage-1}`}
style={{textDecoration:'none',cursor:'pointer',marginRight:'10px'}}>
이전</Link>:''
}
{
data.parr &&
data.parr.map((pno,i)=>
<NavLink to={`/board/list/${pno}`} style={{textDecoration:'none'}}>
<b style={{marginRight:'10px',
color:pno===Number(currentPage)?'red':'black'}}>{pno}</b>
</NavLink>)
}
{
// 다음
data.endPage<data.totalPage?
<Link to={`/board/list/${data.endPage+1}`}
style={{textDecoration:'none',cursor:'pointer'}}>
다음</Link>:''
}
</div>
</div>
);
}
export default BoardList;
import '../App.css';
import { Link, NavLink, useNavigate, useParams } from 'react-router-dom';
import Axios from 'axios';
import BoardRowList from './BoardRowList';
function BoardList(props) {
const [data,setData]=useState('');
const {currentPage}=useParams();
console.log({currentPage});
const navi=useNavigate();
//페이징처리에 필요한 데이타 가져오기
const list=()=>{
const url="/board/list?currentPage="+(currentPage==null?1:currentPage);
Axios.get(url)
.then(res=>{
setData(res.data);
})
}
useEffect(()=>{
list();
},[currentPage]);
const onWriteButtonEvent=()=>{
if(sessionStorage.loginok==null){
alert("먼저 로그인을 해주세요");
navi("/login");
}else{
navi("/board/form")
}
}
return (
<div style={{marginLeft:'30px'}}>
<button type='button' className='btn btn-outline-success'
style={{width:'100px',marginLeft:'100px'}} onClick={onWriteButtonEvent}>글쓰기</button>
<br/><br/>
<h5><b>총 {data.totalCount}개의 글이 있습니다</b></h5>
<table className='table table-bordered' style={{width:'700px'}}>
<thead>
<tr style={{backgroundColor:'#ddd'}}>
<th style={{width:'40px'}}>번호</th>
<th style={{width:'200px'}}>제목</th>
<th style={{width:'70px'}}>작성자</th>
<th style={{width:'100px'}}>작성일</th>
<th style={{width:'50px'}}>조회</th>
</tr>
</thead>
<tbody>
{
data.list &&
data.list.map((row,idx)=>
<BoardRowList key={idx} row={row} no={data.no} idx={idx}
currentPage={currentPage}/>)
}
</tbody>
</table>
<div style={{width:'800px',textAlign:'center'}}>
{/* 페이징처리 */}
{
//이전
data.startPage>1?
<Link to={`/board/list/${data.startPage-1}`}
style={{textDecoration:'none',cursor:'pointer',marginRight:'10px'}}>
이전</Link>:''
}
{
data.parr &&
data.parr.map((pno,i)=>
<NavLink to={`/board/list/${pno}`} style={{textDecoration:'none'}}>
<b style={{marginRight:'10px',
color:pno===Number(currentPage)?'red':'black'}}>{pno}</b>
</NavLink>)
}
{
// 다음
data.endPage<data.totalPage?
<Link to={`/board/list/${data.endPage+1}`}
style={{textDecoration:'none',cursor:'pointer'}}>
다음</Link>:''
}
</div>
</div>
);
}
export default BoardList;
BoardRowList.js
import React from 'react';
import { NavLink } from 'react-router-dom';
import noimage from '../image/error404.png';
function BoardRowList(props) {
const {idx,no,row,currentPage}=props;
const smallurl1=process.env.REACT_APP_SMALL_BOARDURL1;
const smallurl2=process.env.REACT_APP_SMALL_BOARDURL2;
return (
<tr>
<td>{no-idx}</td>
<td>
<NavLink to={`/board/detail/${row.num}/${currentPage}`}
style={{textDecoration:'none',color:'black',cursor:'pointer'}}>
{/* 40x40 썸네일 이미지 나오게 하기(사진이 없을경우 noimage 로 대치) */}
{
row.photo==null?
<img alt='' src={noimage}
style={{width:'40px',height:'40px',border:'1px solid black',
marginRight:'10px'}}/>:
<img alt='' src={`${smallurl1}${row.photo}${smallurl2}`}
style={{border:'1px solid black',
marginRight:'10px'}}/>
}
<b>{row.subject}</b>
</NavLink>
</td>
<td>{row.myname}</td>
<td align='center'>{row.writeday.substring(0,10)}</td>
<td align='center'>{row.readcount}</td>
</tr>
);
}
export default BoardRowList;
import { NavLink } from 'react-router-dom';
import noimage from '../image/error404.png';
function BoardRowList(props) {
const {idx,no,row,currentPage}=props;
const smallurl1=process.env.REACT_APP_SMALL_BOARDURL1;
const smallurl2=process.env.REACT_APP_SMALL_BOARDURL2;
return (
<tr>
<td>{no-idx}</td>
<td>
<NavLink to={`/board/detail/${row.num}/${currentPage}`}
style={{textDecoration:'none',color:'black',cursor:'pointer'}}>
{/* 40x40 썸네일 이미지 나오게 하기(사진이 없을경우 noimage 로 대치) */}
{
row.photo==null?
<img alt='' src={noimage}
style={{width:'40px',height:'40px',border:'1px solid black',
marginRight:'10px'}}/>:
<img alt='' src={`${smallurl1}${row.photo}${smallurl2}`}
style={{border:'1px solid black',
marginRight:'10px'}}/>
}
<b>{row.subject}</b>
</NavLink>
</td>
<td>{row.myname}</td>
<td align='center'>{row.writeday.substring(0,10)}</td>
<td align='center'>{row.readcount}</td>
</tr>
);
}
export default BoardRowList;
BoardDetailPage.js
import Axios from 'axios';
import React, { useEffect, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
function BoardDetailPage(props) {
const [dto,setDto]=useState({});
const {num,currentPage}=useParams();
const navi=useNavigate();
const photourl=process.env.REACT_APP_BOARDURL;
const myid=sessionStorage.myid;
const loginok=sessionStorage.loginok;
const selectData=()=>{
const url=`/board/detail?num=${num}`;
Axios.get(url)
.then(res=>{
setDto(res.data);
})
}
useEffect(()=>{
selectData();
},[]);
return (
<div style={{marginLeft:'30px',width:'600px'}}>
<h5><b>{dto.subject}</b></h5>
<h6>
<span>작성자:{dto.myname}({dto.myid})</span>
<span style={{float:'right',color:'gray'}}>
조회 {dto.readcount}
{dto.writeday}
</span>
</h6>
{
dto.photo==null?'':
<img alt='' src={`${photourl}${dto.photo}`}
style={{border:'1px solid gray',maxWidth:'500px'}}/>
}
<br/><br/>
<pre>{dto.content}</pre>
<br/>
<div>
<button type='button' className='btn btn-outline-danger'
style={{width:'80px',marginRight:'5px'}}
onClick={()=>navi("/board/form")}>글쓰기</button>
<button type='button' className='btn btn-outline-danger'
style={{width:'80px',marginRight:'5px'}}
onClick={()=>navi("/board/list/1")}>목록</button>
{
loginok!=null && myid===dto.myid?
<button type='button' className='btn btn-outline-danger'
style={{width:'80px',marginRight:'5px'}}
onClick={()=>{
const url=`/board/delete?num=${dto.num}`;
Axios.delete(url)
.then(res=>{
//목록으로 이동
navi(`/board/list/${currentPage}`)
})
}}>삭제</button>:''
}
{
loginok!=null && myid===dto.myid?
<button type='button' className='btn btn-outline-danger'
style={{width:'80px',marginRight:'5px'}}
onClick={()=>{
}}>수정</button>:''
}
</div>
</div>
);
}
export default BoardDetailPage;
import React, { useEffect, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
function BoardDetailPage(props) {
const [dto,setDto]=useState({});
const {num,currentPage}=useParams();
const navi=useNavigate();
const photourl=process.env.REACT_APP_BOARDURL;
const myid=sessionStorage.myid;
const loginok=sessionStorage.loginok;
const selectData=()=>{
const url=`/board/detail?num=${num}`;
Axios.get(url)
.then(res=>{
setDto(res.data);
})
}
useEffect(()=>{
selectData();
},[]);
return (
<div style={{marginLeft:'30px',width:'600px'}}>
<h5><b>{dto.subject}</b></h5>
<h6>
<span>작성자:{dto.myname}({dto.myid})</span>
<span style={{float:'right',color:'gray'}}>
조회 {dto.readcount}
{dto.writeday}
</span>
</h6>
{
dto.photo==null?'':
<img alt='' src={`${photourl}${dto.photo}`}
style={{border:'1px solid gray',maxWidth:'500px'}}/>
}
<br/><br/>
<pre>{dto.content}</pre>
<br/>
<div>
<button type='button' className='btn btn-outline-danger'
style={{width:'80px',marginRight:'5px'}}
onClick={()=>navi("/board/form")}>글쓰기</button>
<button type='button' className='btn btn-outline-danger'
style={{width:'80px',marginRight:'5px'}}
onClick={()=>navi("/board/list/1")}>목록</button>
{
loginok!=null && myid===dto.myid?
<button type='button' className='btn btn-outline-danger'
style={{width:'80px',marginRight:'5px'}}
onClick={()=>{
const url=`/board/delete?num=${dto.num}`;
Axios.delete(url)
.then(res=>{
//목록으로 이동
navi(`/board/list/${currentPage}`)
})
}}>삭제</button>:''
}
{
loginok!=null && myid===dto.myid?
<button type='button' className='btn btn-outline-danger'
style={{width:'80px',marginRight:'5px'}}
onClick={()=>{
}}>수정</button>:''
}
</div>
</div>
);
}
export default BoardDetailPage;
src>board>index.js
export {default as BoardList} from './BoardList';
export {default as BoardForm} from './BoardForm';
export {default as BoardDetailPage} from './BoardDetailPage';
export {default as BoardForm} from './BoardForm';
export {default as BoardDetailPage} from './BoardDetailPage';
src>RouteMain.js
import React from 'react';
import { Home, Menu } from './components';
import { Route, Routes } from 'react-router-dom';
import { LoginForm, MemberForm, MemberList } from './member';
import { BoardForm, BoardList } from './board';
// import errorimg from './image/error404.jpg';
import BoardDetailPage from './board/BoardDetailPage';
function RouteMain(props) {
return (
<div>
<Menu/>
<br style={{clear:'both'}}/><br/>
<Routes>
<Route path='/' element={<Home/>}/>
<Route path='/login' element={<LoginForm/>}/>
<Route path='/member'>
<Route path='form' element={<MemberForm/>}/>
<Route path='list' element={<MemberList/>}/>
</Route>
<Route path='/board'>
<Route path='form' element={<BoardForm/>}/>
<Route path='list' element={<BoardList/>}/>
<Route path='list/:currentPage' element={<BoardList/>}/>
<Route path='detail/:num/:currentPage' element={<BoardDetailPage/>}/>
</Route>
<Route path='*' element={
<div>
<h1>잘못된 URL 주소입니다</h1>
<br/><br/>
{/*<img alt='' src={errorimg}/>*/}
</div>
}/>
</Routes>
</div>
);
}
export default RouteMain;
import { Home, Menu } from './components';
import { Route, Routes } from 'react-router-dom';
import { LoginForm, MemberForm, MemberList } from './member';
import { BoardForm, BoardList } from './board';
// import errorimg from './image/error404.jpg';
import BoardDetailPage from './board/BoardDetailPage';
function RouteMain(props) {
return (
<div>
<Menu/>
<br style={{clear:'both'}}/><br/>
<Routes>
<Route path='/' element={<Home/>}/>
<Route path='/login' element={<LoginForm/>}/>
<Route path='/member'>
<Route path='form' element={<MemberForm/>}/>
<Route path='list' element={<MemberList/>}/>
</Route>
<Route path='/board'>
<Route path='form' element={<BoardForm/>}/>
<Route path='list' element={<BoardList/>}/>
<Route path='list/:currentPage' element={<BoardList/>}/>
<Route path='detail/:num/:currentPage' element={<BoardDetailPage/>}/>
</Route>
<Route path='*' element={
<div>
<h1>잘못된 URL 주소입니다</h1>
<br/><br/>
{/*<img alt='' src={errorimg}/>*/}
</div>
}/>
</Routes>
</div>
);
}
export default RouteMain;
.env
REACT_APP_BOARDURL="https://kr.object.ncloudstorage.com/xxxxx/"
REACT_APP_SMALL_BOARDURL1="http://ggsmoyxsdxjl1xxxxxx.cdn.ntruss.com/board/"
REACT_APP_SMALL_BOARDURL2="?type=f&w=40&h=40"
REACT_APP_SMALL_BOARDURL1="http://ggsmoyxsdxjl1xxxxxx.cdn.ntruss.com/board/"
REACT_APP_SMALL_BOARDURL2="?type=f&w=40&h=40"
(화면 이미지)
'학교 & 학원 이론 수업 > 네이버 클라우드 AIaaS 개발자 양성 과정' 카테고리의 다른 글
react(11) - useContext , JWT Token (0) | 2023.06.28 |
---|---|
react (10) useReducer , useCallback (0) | 2023.06.28 |
react(8) - 로그인 구현 (0) | 2023.06.23 |
react(6),(7) - docker, Jenkins, docker hub (0) | 2023.06.22 |
react (5) - router (0) | 2023.06.21 |