React-front에 추가설치
yarn add react-quill
yarn add quill-image-resize-module-react
<리액트와 pojo3 연결 - index.js>
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import { BrowserRouter } from "react-router-dom";
import AuthLogic from "./service/authLogic";
import firebaseApp from "./service/firebase";
import "react-quill/dist/quill.snow.css";
import "@fortawesome/fontawesome-free/js/all.js";
// import SampleApp from "./SampleApp";
// 공통코드: service -> authLogic.js -> import 외부 js 재사용 가능 -> export default 클래스명 -> module
// 브라우저에서 import하려면 반드시 babel이 필요함
const authLogic = new AuthLogic(firebaseApp); // 인스턴스화(파라미터가 올 수 있다)
// 왜 파라미터가 필요한가? - firebaseApp -> import firebaseApp from "./service/firebase"; -> export default firebaseApp
// authLogic.파이어베이스에서 제공하는 함수를 호출하겠다
// document.getElementById("root") -> index.html문서에서 DOM정보를 수집하는 것임
// const root = ReactDOM.createRoot(document.getElementById("root"));
const root = ReactDOM.createRoot(document.querySelector("#root"));
root.render(
<>
<BrowserRouter>
{/* App컴포넌트를 렌더링할 때 속성자리에 주소번지를 넘길 수 있다 - props */}
{/* 태그와 JS 섞어쓰기 가능 - 중괄호{} */}
<App authLogic={authLogic} />
{/* <SampleApp /> */}
</BrowserRouter>
</>
);
<리액트와 pojo3 연결 - App.jsx>
import { Route, Routes } from "react-router-dom";
import "./App.css";
import "bootstrap/dist/css/bootstrap.min.css";
//import DeptPage from './components/page/DeptPage';
import EmpPage from "./components/page/EmpPage";
import HomePage from "./components/page/HomePage";
import FireDeptPage from "./components/page/FireDeptPage";
import LoginPage from "./components/login/LoginPage";
import BoardPage from "./components/page/BoardPage";
import WorkoutPage from "./components/page/WorkoutPage";
import HackerNewsPage from "./components/page/HackerNewsPage";
import YoutubePage from "./components/page/YoutubePage";
import { useEffect, useState } from "react";
import BoardDetail from "./components/board/BoardDetail";
import BoardWriteFrom from "./components/board/BoardWriteFrom";
// index.js에서 브라우저 라우터로 감싸진 App태그 속성값으로 넘어온 주소번지를 받는다
const App = ({ authLogic }) => {
console.log("App 호출");
// 상수값 -> 나중에 axios(NodeJS, React-모듈화), fetch(바닐라-브라우저 지원)로 처리(둘 다 비동기 처리방식)
const [items, setItems] = useState([
{ id: 1, name: "벤치프레스", count: 0 },
{ id: 2, name: "랫풀다운", count: 0 },
{ id: 3, name: "스쿼트", count: 0 },
]);
/*
첫번째 파라미터는 콜백함수 - 객체
두번째 파라미터는 의존성 배열 - Dependency Array
의존성 배열이 비어있으면 최초 App 컴포넌트가 렌더링될 때 딱 한번만 실행됨
리렌더링이 되는 대상은 return안에 있는 코드들이다
만약 items가 변경되면 그때는 리렌더링이 일어남
*/
useEffect(() => {
console.log("effect 호출");
}, []);
const handleIncrement = (item) => {
const index = items.indexOf(item);
items[index].count += 1;
setItems([...items]);
};
const handleDecrement = (item) => {
const index = items.indexOf(item);
const count = items[index].count - 1;
items[index].count = count < 0 ? 0 : count;
setItems([...items]);
};
const handleDelete = (item) => {
console.log(`handleDelete => ${item.name}`);
const workouts = items.filter((workout) => workout.id != item.id);
setItems([...workouts]);
};
const handleAdd = (name) => {
console.log(`handleAdd => ${name}`);
// AddForm화면에서 사용자가 입력한 운동이름을 받아온다
// 세번째 파라미터는 0으로 초기화
// 스프레드 연산자를 활용하여 기존 배열에 한 개의 객체를 추가하는 코드
const workouts = [...items, { id: Date.now(), name, count: 0 }];
// 상태 훅에 반영 - spread 연산자를 활용하여 새로운 주소번지가 채번되도록 처리해야함
// 상태값이 변경되었다는 사실을 리액트에서 인지할 수 있다
setItems([...workouts]);
};
// 사용자 정의 컴포넌트에서 return 다음에 오는 코드가 element의 집합(컴포넌트)
// Router를 이용하면 SPA(Single Page Application)를 누릴 수 있다
return (
<>
<Routes>
{/* 아래같이 계속 authLogic 넘기는게 불편 -> 리덕스 배우면 상태관리가능! */}
{/* exact쓰면 주소가 100% 일치할때만 이동되니 주의(*못씀!) */}
<Route path="/" exact={true} element={<LoginPage authLogic={authLogic} />} />
<Route path="/home/:userId" xact={true} element={<HomePage authLogic={authLogic} />} />
<Route path="/board" exact={true} element={<BoardPage authLogic={authLogic} />} />
<Route path="/board/write/*" element={<BoardWriteFrom />} />
<Route path="/boarddetail/:bm_no" exact={true} element={<BoardDetail />} />
<Route path="/workout" exact={true} element={ <WorkoutPage authLogic={authLogic}
workouts={items}
onIncrement={handleIncrement}
onDecrement={handleDecrement}
onDelete={handleDelete}
onAdd={handleAdd} />} />
<Route path="/hackernews" exact={true} element={<HackerNewsPage authLogic={authLogic} />} />
<Route path="/youtube" exact={true} element={<YoutubePage authLogic={authLogic} />}/>
<Route path="/dept/:id" exact={true} element={<FireDeptPage authLogic={authLogic} />} />
{/* <Route path="/dept/:id" exact={true} element={<DeptPage authLogic={authLogic} />} /> */}
<Route path="/emp" exact={true} element={<EmpPage authLogic={authLogic} />} />
</Routes>
</>
);
};
export default App;
<리액트와 pojo3 연결 - BoardRow.jsx>
import React from "react";
import { Link } from "react-router-dom";
const BoardRow = ({board}) => {
return (
<>
<tr>
<td>{board.BM_NO}</td>
<td><Link to={"/boarddetail/"+board.BM_NO} className="btn btn-primary">{board.BM_TITLE}</Link></td>
<td>{board.BM_WRITER}</td>
</tr>
</>
);
};
export default BoardRow;
<리액트와 pojo3 연결 - BoardPage.jsx>
import React, { useEffect, useState } from "react";
import { Button, Table } from "react-bootstrap";
import { useNavigate } from "react-router-dom";
import Bottom from "../include/Bottom";
import Header from "../include/Header";
import '../css/board.css'
import BoardRow from "../board/BoardRow";
import { boardListDB } from "../../service/dbLogic";
const BoardPage = ({ authLogic }) => {
console.log('BoardPage App')
const [boards, setBoards] = useState([]);
// Single Page Application 컨벤션을 위한 훅
const navigate = useNavigate();
const onLogout = () => {
console.log("BoardPage onLogout 호출");
authLogic.logout();
};
// pojo3과 통신하는 useEffect -> CORS on으로 바꾸고 테스트
useEffect (()=>{
console.log('POJO3 서버와 통신 effect')
// async를 써야 await 쓸 수 있음
const boardList = async() => {
const gubun = document.querySelector('#gubun').value
const keyword = document.querySelector('#keyword').value
const board = {
gubun: gubun,
keyword: keyword,
}
const res = await boardListDB(board)
console.log(res.data)
const list = []
res.data.forEach((item) => {
const obj = {
BM_NO: item.BM_NO,
BM_TITLE : item.BM_TITLE,
BM_WRITER: item.BM_WRITER,
}
list.push(obj)
})
setBoards(list)
}
boardList()
}, [setBoards]) // 빈배열이면 최초 한번만 실행, 여기선 조회 결과가 달라질 때마다 호출됨
useEffect(() => {
authLogic.onAuthChange((user) => {
if (!user) {
navigate("/");
}
});
});
// 조건 검색 구현
const reactSearch = () => {
}
// 전체 조회 구현
const boardList = () => {
}
return (
<>
<Header onLogout={onLogout} />
<div className="container">
<div className="page-header">
<h2>공지관리<small>글목록</small></h2>
<hr />
</div>
<div className="row">
<div className="col-3">
<select id="gubun" className="form-select" aria-label="분류선택">
<option defaultValue>분류선택</option>
<option value="bm_title">제목</option>
<option value="bm_writer">작성자</option>
<option value="bm_content">내용</option>
</select>
</div>
<div className="col-6">
<input type="text" id="keyword" className="form-control" placeholder="검색어를 입력하세요"
aria-label="검색어를 입력하세요" aria-describedby="btn_search"/>
</div>
<div className="col-3">
<Button variant="danger" id="btn_search" onClick={reactSearch}>검색</Button>
</div>
</div>
<div className="board-list">
<Table striped bordered hover>
<thead>
<tr>
<th>#</th>
<th>제목</th>
<th>작성자</th>
</tr>
</thead>
<tbody>
{boards && boards.map((board, index) => (
<BoardRow key={index} board={board} />
))}
</tbody>
</Table>
<hr />
<div className="boardlist-footer">
<Button variant="warning" onClick={boardList}>
전체조회
</Button>
<Button variant="success" onClick={() => {navigate(`/board/write`);}}>
글쓰기
</Button>
</div>
</div>
</div>
<Bottom />
</>
);
};
export default BoardPage;
<리액트와 pojo3 연결 - dbLogic.js>
import axios from "axios";
// pojo3과 통신해서 DB값 가져오는 boardListDB
export const boardListDB = (board) => {
return new Promise((resolve, reject) => {
try {
const response = axios({
method: "get",
url: process.env.REACT_APP_CHAT_BANANA_IP + "board3/jsonBoardList.st3",
params: board,
});
resolve(response);
} catch (error) {
reject(error);
}
});
};
export const uploadImageDB = (file) => {
console.log(file);
return new Promise((resolve, reject) => {
try {
const response = axios({
method: "post",
url: process.env.REACT_APP_CHAT_BANANA_IP + "board3/imageUpload.st3",
headers: {
"Content-Type": "multipart/form-data",
},
processData: false,
contentType: false,
data: file,
});
resolve(response);
} catch (error) {
reject(error);
}
});
};
export const boardInsertDB = (board) => {
return new Promise((resolve, reject) => {
try {
const response = axios({
method: "post",
url: process.env.REACT_APP_CHAT_BANANA_IP + "board3/boardInsert.st3",
headers: {
"Content-Type": "multipart/form-data",
},
data: board,
});
resolve(response);
} catch (error) {
reject(error);
}
});
};
/* rafce로 자동완성 단축키 - arrow function export default */
<리액트와 pojo3 연결 - BoardWriteForn.jsx>
import React, { useCallback, useRef, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { boardInsertDB, uploadImageDB } from '../../service/dbLogic'
import { BButton, ContainerDiv, FormDiv, HeaderDiv } from '../styles/FormStyle'
import QuillEditor from './QuillEditor'
const BoardWriteFrom = () => {
const navigate = useNavigate()
const[title, setTitle] = useState("")
const[writer, setWriter] = useState("")
const[pw, setPw] = useState("")
const[content, setcontent] = useState("")
const[bsfile, setBsFile] = useState("")
const[bssize, setBsSize] = useState("")
const[fileName, setFileName] = useState("")
const[files, setFiles] = useState([])
const quillRef = useRef()
const fileRef = useRef()
// 사용자가 입력한 값을 useState에 초기화하기
const handleTitle = useCallback((e) => {
setTitle(e)
}, [])
const handleWriter = useCallback((e) => {
setWriter(e)
}, [])
const handlePW = useCallback((e) => {
setPw(e)
}, [])
const handleContent = useCallback((e) => {
setcontent(e)
}, [])
const boardInsert = async() => {
const board = {
bm_title: title,
bm_content: content,
bm_writer: writer,
bm_pw: pw,
bs_file: bsfile,
bs_size: bssize,
}
const res = await boardInsertDB(board)
console.log(res)
// window.location.replace("/board")
navigate("/board")
}
const handleClick = (event) => {
event.preventDefault()
document.querySelector("#file-input").click((event)=>{
console.log(event.currentTarget.value);
})
}
const handleChange = async (event) => {
console.log('첨부파일 선택'+event.target.value);
//console.log(fileRef.current.value);
//fileRef에서 가져온 값중 파일명만 담기
const str = fileRef.current.value.split('/').pop().split('\\').pop()
setFileName(str)
console.log(str);
//선택한 파일을 url로 바꾸기 위해 서버로 전달할 폼데이터 만들기
const formData = new FormData()
const file = document.querySelector("#file-input").files[0]
formData.append("application", file)
const res = await uploadImageDB(formData)
console.log(res.data)
const fileinfo = res.data.split(',')
console.log(fileinfo)
setBsFile(fileinfo[0])
setBsSize(fileinfo[1])
}
const handleFiles = () => {
}
return (
<>
<ContainerDiv>
<HeaderDiv>
<h3 style={{marginLeft:"10px"}}>공지사항 글작성</h3>
</HeaderDiv>
<FormDiv>
<div style={{width:"100%", maxWidth:"2000px"}}>
<div style={{display: 'flex', justifyContent: 'space-between', marginBottom:'5px'}}>
<h3>제목</h3>
<BButton onClick={()=>{boardInsert()}}>글쓰기</BButton>
</div>
<input id="dataset-title" type="text" maxLength="50" placeholder="제목을 입력하세요."
style={{width:"100%",height:'40px' , border:'1px solid lightGray', marginBottom:'5px'}} onChange={(e)=>{handleTitle(e.target.value)}}/>
<div style={{display: 'flex', justifyContent: 'space-between', marginBottom:'5px'}}>
<h3>작성자</h3>
</div>
<input id="dataset-writer" type="text" maxLength="50" placeholder="작성자를 입력하세요."
style={{width:"200px",height:'40px' , border:'1px solid lightGray', marginBottom:'5px'}} onChange={(e)=>{handleWriter(e.target.value)}}/>
<div style={{display: 'flex', justifyContent: 'space-between', marginBottom:'5px'}}>
<h3>비밀번호</h3>
</div>
<input id="dataset-pw" type="text" maxLength="50" placeholder="비밀번호를 입력하세요."
style={{width:"200px",height:'40px' , border:'1px solid lightGray', marginBottom:'5px'}} onChange={(e)=>{handlePW(e.target.value)}}/>
<div style={{display: 'flex', justifyContent: 'space-between', marginBottom:'5px'}}>
<h3>첨부파일</h3>
</div>
<input id="file-input" ref={fileRef} type="file" maxLength="50" className="visuallyhidden" onChange={handleChange}/>
<button style={{height:'40px'}} onClick={handleClick}>파일선택</button>
<input id="bs_file" name='bs_file' type="text" maxLength="50" value={fileName}
style={{width:"600px",height:'40px' , border:'1px solid lightGray', marginBottom:'5px'}} />
<h3>상세내용</h3>
<hr style={{margin:'10px 0px 10px 0px'}}/>
<QuillEditor value={content} handleContent={handleContent} quillRef={quillRef} files={files} handleFiles={handleFiles}/>
{/* <BoardFileInsert files={files}/> */}
</div>
</FormDiv>
</ContainerDiv>
</>
)
}
export default BoardWriteFrom
<리액트와 pojo3 연결 - QuillEditor.jsx>
import { useCallback, useEffect, useMemo } from 'react';
import ReactQuill, { Quill } from 'react-quill';
import ImageResize from 'quill-image-resize-module-react';
import { uploadImageDB } from '../../service/dbLogic';
//import 'react-quill/dist/quill.snow.css';
Quill.register('modules/ImageResize', ImageResize);
const QuillEditor = ({ value, handleContent, quillRef, files, handleFiles}) => {
console.log(files);
//const dispatch = useDispatch();
const imageHandler = useCallback(() => {
console.log(files);
if(files.length > 2){
return "이미지는 3장까지 업로드 가능합니다.";
}
const formData = new FormData(); // 이미지를 url로 바꾸기위해 서버로 전달할 폼데이터 만들기
const input = document.createElement("input"); // input 태그를 동적으로 생성하기
input.setAttribute("type", "file");
input.setAttribute("accept", "image/*"); // 이미지 파일만 선택가능하도록 제한
input.setAttribute("name", "image");
input.click();
// 파일 선택창에서 이미지를 선택하면 실행될 콜백 함수 등록
input.onchange = async () => {
const file = input.files[0];
const fileType = file.name.split('.');
console.log(fileType);
if(!fileType[fileType.length-1].toUpperCase().match('JPG')&&
!fileType[fileType.length-1].toUpperCase().match('PNG')&&
!fileType[fileType.length-1].toUpperCase().match('JPEG')) {
console.log("jpg png jpeg형식만 지원합니다.");
}
formData.append("image", file); // 위에서 만든 폼데이터에 이미지 추가
for (let pair of formData.entries()) {
console.log(pair[0], pair[1]);
}
// 폼데이터를 서버에 넘겨 multer로 이미지 URL 받아오기
const res = await uploadImageDB(formData);
console.log(res.data); // 리턴받는 파일명
const files = res.data.split(',')
if (!res.data) {
console.log("이미지 업로드에 실패하였습니다.");
}
const url = process.env.REACT_APP_CHAT_BANANA_IP+`board3/imageGet.st3?imageName=${files[0]}`;
const quill = quillRef.current.getEditor();
/* ReactQuill 노드에 대한 Ref가 있어야 메서드들을 호출할 수 있으므로
useRef()로 ReactQuill에 ref를 걸어주자.
getEditor() : 편집기를 지원하는 Quill 인스턴스를 반환함
여기서 만든 인스턴스로 getText()와 같은 메서드를 사용할 수 있다.*/
const range = quill.getSelection().index;
//getSelection()은 현재 선택된 범위를 리턴한다. 에디터가 포커싱되지 않았다면 null을 반환한다.
if (typeof (range) !== "number") return;
/*range는 0이 될 수도 있으므로 null만 생각하고 !range로 체크하면 잘못 작동할 수 있다.
따라서 타입이 숫자이지 않을 경우를 체크해 리턴해주었다.*/
quill.setSelection(range, 1);
/* 사용자 선택을 지정된 범위로 설정하여 에디터에 포커싱할 수 있다.
위치 인덱스와 길이를 넣어주면 된다.*/
quill.clipboard.dangerouslyPasteHTML(
range,
`<img src=${url} style="width: 100%; height: auto;" alt="image" />`);
//handleFiles(res.data, `${Math.floor(file.size/(1024*1024)*10)/10}MB`);
} //주어진 인덱스에 HTML로 작성된 내용물을 에디터에 삽입한다.
}, [quillRef, files]);
const modules = useMemo(
() => ({
toolbar: { // 툴바에 넣을 기능들을 순서대로 나열하면 된다.
container: [
[{ 'header': [1, 2, 3, 4, 5, 6, false] }, { color: [] }, { 'align': [] }, { 'background': [] }],
["bold", "italic", "underline", "strike", "blockquote"],
[
{ list: "ordered" },
{ list: "bullet" },
{ indent: "-1" },
{ indent: "+1" },
],
['clean'],
['link', "image"],
],
handlers: { // 위에서 만든 이미지 핸들러 사용하도록 설정
image: imageHandler,
},
},
ImageResize: {
modules: ['Resize']
}
}), [imageHandler])
useEffect(()=>{
console.log('QuillEditor useEffect');
},[])
const formats = [
//'font',
'header',
'bold', 'italic', 'underline', 'strike', 'blockquote',
'list', 'bullet', 'indent',
'link', 'image',
'align', 'color', 'background',
]
return (
<div style={{height: "550px", display: "flex", justifyContent: "center", padding:"0px"}}>
<ReactQuill
ref={quillRef}
style={{height: "470px", width: "100%"}}
theme="snow"
placeholder= "본문 입력"
modules={modules}
formats={formats}
value={value}
onChange={(content, delta, source, editor) => {handleContent(editor.getHTML());}} />
</div>
)
}
export default QuillEditor
<리액트와 pojo3 연결 - BoardDetail.jsx>
import React, { useEffect, useState } from 'react'
import { json, useParams } from 'react-router-dom'
import { boardListDB } from '../../service/dbLogic'
import { ContainerDiv, FormDiv, HeaderDiv } from '../styles/FormStyle'
import BoardHeader from './BoardHeader'
const BoardDetail = () => {
const {bm_no} = useParams()
console.log(bm_no)
const[pboard, setPBoard] = useState({
bm_no: bm_no,
})
// 하나: 파일명(bs_file), 둘: 파일크기(bs_size)
const[files, setFiles] = useState({
})
const[board, setBoard] = useState({
BM_NO: 0,
BM_TITLE: "",
BM_WRITER: "",
BM_CONTENT: "",
BM_PW: "",
BM_REG: "",
BM_GROUP: 0,
BM_POS: 0,
BM_STEP: 0,
BM_HIT: 0,
BS_FILE: "",
BS_SIZE: "",
})
useEffect (() => {
// 동기-기다림, 비동기- 기다리는동안 다른일 처리
const boardDetail = async() => {
const res = await boardListDB(pboard)
console.log(res)
const result = JSON.stringify(res.data)
const jsonDoc = JSON.parse(result)
setBoard({BM_NO: jsonDoc[0].BM_NO, BM_TITLE: jsonDoc[0].BM_TITLE, BM_WRITER: jsonDoc[0].BM_WRITER,
BM_CONTENT: jsonDoc[0].BM_CONTENT, BM_PW: jsonDoc[0].BM_PW, BM_REG: jsonDoc[0].BM_REG,
BM_GROUP: jsonDoc[0].BM_GROUP, BM_POS: jsonDoc[0].BM_POS, BM_STEP: jsonDoc[0].BM_STEP,
BM_HIT: jsonDoc[0].BM_HIT
})
// 첨부파일, quill editor 이미지 파일
setFiles([jsonDoc[0].BS_FILE, jsonDoc[0].BS_SIZE])
}
boardDetail()
}, [pboard]) // 의존성 배열에는 제목에 해당되는 bm_no가 변경될 때마다 새로 실행됨
// 빈 배열이면 최초 App이 렌더링될 때(리턴에있는 컴포넌트가 렌더링될때) 딱 한 번만 실행됨
// 의존성 배열을 적지 않으면 변경될때마다 새로 읽는다 - 초기화된다(유지x)
// 주의사항: boards와 같은 n개 로우를 갖는 변수명을 사용X! - 무한루프(리렌더링) -> 대신 setBoards를 쓸것
return (
<ContainerDiv>
<HeaderDiv>
<h3 style={{marginLeft:"10px"}}>계층형 게시판</h3>
</HeaderDiv>
<FormDiv>
<BoardHeader board={board} bm_no={bm_no}/>
<section style={{minHeight: '400px'}}>
<div dangerouslySetInnerHTML={{__html:board.BM_CONTENT}}></div>
</section>
<hr style={{height:"2px"}}/>
</FormDiv>
</ContainerDiv>
)
}
export default BoardDetail
<리액트와 pojo3 연결 - BoardHeader.jsx>
import React from 'react'
import { useNavigate } from 'react-router-dom'
import { BButton } from '../styles/FormStyle'
const BoardHeader = ({board}) => {
const navigate = useNavigate()
const boardDelete = () => {
}
return (
<div>
<div style={{display: 'flex', flexDirection: 'column', width: '100%'}}>
<div style={{display: 'flex', justifyContent:"space-between"}}>
<div style={{overflow: "auto"}}>
<span style={{marginBottom:'15px', fontSize: "30px", display:"block"}}>
{board.BM_TITLE}
</span>
</div>
{
<div style={{display: 'flex', justifyContent: 'flex-end'}}>
<BButton style={{margin:'0px 10px 0px 10px'}} onClick={()=>{navigate(`/board/update?bm_no=${board.BM_NO}`)}}>
수정
</BButton>
<BButton onClick={()=>{boardDelete()}}>
삭제
</BButton>
</div>
}
</div>
<div style={{display: 'flex', justifyContent: 'space-between', fontSize: '14px'}}>
<div style={{display: 'flex', flexDirection: 'column'}}>
<span>작성자 : {board.BM_WRITER}</span>
<span>작성일 : {board.BM_REG}</span>
</div>
<div style={{display: 'flex', flexDirection: 'column', marginRight:'10px'}}>
<div style={{display: 'flex'}}>
<span style={{marginRight:'5px'}}>조회수 :</span>
<div style={{display: 'flex', justifyContent: 'flex-end', width:'30px'}}>{board.BM_HIT}</div>
</div>
</div>
</div>
</div>
<hr style={{height: '2px'}}/>
</div>
)
}
export default BoardHeader
<리액트와 pojo3 연결 - ActionSupport.java>
package com.pojo.step3;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import com.pojo.step2.Board2Controller;
public class ActionSupport extends HttpServlet {
Logger logger = Logger.getLogger(ActionSupport.class);
protected void doService(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
logger.info("doService 호출");
String uri = req.getRequestURI();
logger.info(uri); // /board3/boardList.st3가 찍힘
String context = req.getContextPath();
logger.info(context); // "/" -> server.xml에 들어있음
String command = uri.substring(context.length() + 1);
System.out.println(command); // board3/boardList.st3
int end = command.lastIndexOf(".");
System.out.println(end); // 16(board3의 경우)
command = command.substring(0, end);
System.out.println(command); // board3/boardList
String upmu[] = null; // upmu[0]=업무명(폴더명), upmu[1]=요청기능이름(메소드명)
upmu = command.split("/"); // /기준으로 문자열 잘라서 upmu 배열에 담기
logger.info(upmu[0] + ", " + upmu[1]);
// upmu req에 담기
req.setAttribute("upmu", upmu);
Object obj = ""; // null이 맞지만 String이 들어온다는 전제로 ""
try {
obj = HandlerMapping.getController(upmu, req, res);
} catch (Exception e) {
logger.info("Exception: " + e.toString());
}
// 페이지 이동처리 공통코드
// obj 형식 예시 -> redirect:XXX.jsp or forward:XXX.jsp
if (obj != null) {
String pageMove[] = null;
ModelAndView mav = null;
// obj가 String인 경우 -> webapp
if (obj instanceof String) {
// obj에 :이 포함된 경우
if (((String) obj).contains(":")) {
logger.info(": 포함되어 있음");
pageMove = obj.toString().split(":");
}
// objdp /가 포함된 경우
else if (((String) obj).contains("/")) {
logger.info("/ 포함되어 있음");
pageMove = obj.toString().split("/");
}
// obj에 :과 /이 포함되지 않은 경우
else {
// spring boot -> @RestController 사용
// spring4 -> ResponseBody 사용(@RestController 미지원)
logger.info(":과 / 포함되어 있지 않음"); // 마임타입 text/plain
pageMove = new String[1];
pageMove[0] = obj.toString();
logger.info(obj.toString());
}
}
// obj가 ModelAndView인 경우 -> WEB-INF
else if (obj instanceof ModelAndView) {
mav = (ModelAndView) obj;
pageMove = new String[2];
pageMove[0] = ""; // forward가 들어있으면 안됨 -> 있으면 webapp로 향함
pageMove[1] = mav.getViewName();
}
logger.info("Object가 String, ModelAndView일 경우가 끝난 지점");
// pageMove가 null이 아닐 경우 각 방식으로 페이지 이동처리
if (pageMove != null && pageMove.length == 2) {
// pageMove[0] = redirect or forward or ""
// pageMove[1] = XXX
new ViewResolver(req, res, pageMove);
}
else if (pageMove != null && pageMove.length == 1) {
res.setContentType("text/plainl;charset=UTF-8");
PrintWriter out = res.getWriter();
out.print(pageMove[0]);
}
} // end of 페이지 이동처리 공통코드
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
logger.info("doGet호출");
doService(req, res);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
logger.info("doPost호출");
doService(req, res);
}
}
<리액트와 pojo3 연결 - HandlerMapping.java>
package com.pojo.step3;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
public class HandlerMapping {
static Logger logger = Logger.getLogger(HandlerMapping.class);
/***************************************
* @param upmu[](upmu[0]=업무명,폴더명 | upmu[1]=메소드명, 기능명, 페이지이름)
* @param request -> 1-1, 1-2와는 다르게 인터페이스를 implements하지 않는다
* @param response -> HttpServlet
* Q. 어디서 받아오는가?
* @return Object
* 테스트 -> http://localhost:9000/board3/boardList.st3
***************************************/
public static Object getController(String[] upmu, HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
logger.info(upmu[0]+", "+upmu[1]);
Controller3 controller = null;
String path = null;
Object obj = null;
ModelAndView mav = null;
// 게시판 구현
if("board3".equals(upmu[0])) {
controller = new Board3Controller();
// 게시글 전체 목록 -> html 화면 출력(text/html)
if("boardList".equals(upmu[1])) {
obj = controller.boardList(req, res);
// 리턴타입이 ModelAndView인 경우
if(obj instanceof ModelAndView) {
return (ModelAndView)obj;
}
// 리턴타입이 String인 경우
else if(obj instanceof String) {
return (String)obj;
}
}
// json 게시글 전체 목록 -> json 화면 출력(application/json)
else if("jsonBoardList".equals(upmu[1])) {
obj = controller.jsonBoardList(req, res);
// 리턴타입이 ModelAndView인 경우
if(obj instanceof ModelAndView) {
return (ModelAndView)obj;
}
// 리턴타입이 String인 경우
else if(obj instanceof String) {
return (String)obj;
}
}
// 글 입력 - 새글쓰기와 댓글쓰기
else if("boardInsert".equals(upmu[1])) {
obj = controller.boardInsert(req, res);
// 리턴타입이 String인 경우
if(obj instanceof String) {
return (String)obj;
}
}
// 이미지 업로드 - 리액트 quill editor 이미지 추가
else if("imageUpload".equals(upmu[1])) {
obj = controller.imageUpload(req, res);
logger.info("imageUpload 호출 => " + obj instanceof String);
// 리턴타입이 String인 경우
if(obj instanceof String) {
return (String)obj;
}
}
// 리액트 -
else if("imageGet".equals(upmu[1])) {
obj = controller.imageGet(req, res);
logger.info("imageGet 호출 => " + obj instanceof String);
// 리턴타입이 String인 경우
if(obj instanceof String) {
return (String)obj;
}
}
// 글 수정 - 첨부파일 수정 유무 고려하기
else if("boardUpdate".equals(upmu[1])) {
obj = controller.boardUpdate(req, res);
// 리턴타입이 String인 경우
if(obj instanceof String) {
return (String)obj;
}
}
// 글 삭제 - 첨부파일 삭제 유무 고려하기
else if("boardDelete".equals(upmu[1])) {
obj = controller.boardDelete(req, res);
// 리턴타입이 String인 경우
if(obj instanceof String) {
return (String)obj;
}
}
// 글 상세보기
else if("boardDetail".equals(upmu[1])) {
obj = controller.boardDetail(req, res);
// 리턴타입이 ModelAndView인 경우
if(obj instanceof ModelAndView) {
return (ModelAndView)obj;
}
// 리턴타입이 String인 경우
else if(obj instanceof String) {
return (String)obj;
}
}
} // end of 게시판 구현
// 인증 관리 구현
else if("auth".equals(upmu[0])) {
}
// 회원 관리 구현
else if("member".equals(upmu[0])) {
}
// 주문 관리 구현
else if("order".equals(upmu[0])) {
}
return obj;
}
}
<리액트와 pojo3 연결 - Controller3.java>
package com.pojo.step3;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
// javascript기반의 UI API
// 리액트는 바닐라스크립트 + ES6(주요이슈-spread, module, arrow),ES7 + html 섞어쓰기
public interface Controller3 {
public Object jsonBoardList(HttpServletRequest req, HttpServletResponse res);
public Object boardList(HttpServletRequest req, HttpServletResponse res);
public Object boardDetail(HttpServletRequest req, HttpServletResponse res);
public Object imageUpload(HttpServletRequest req, HttpServletResponse res);
public Object imageGet(HttpServletRequest req, HttpServletResponse res);
public Object boardInsert(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException;
public Object boardUpdate(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException;
public Object boardDelete(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException;
}
<리액트와 pojo3 연결 - Board3Controller.java>
package com.pojo.step3;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import com.oreilly.servlet.MultipartRequest;
import com.oreilly.servlet.multipart.DefaultFileRenamePolicy;
import com.util.HashMapBinder;
public class Board3Controller implements Controller3 {
Logger logger = Logger.getLogger(Board3Controller.class);
Board3Logic boardLogic = new Board3Logic();
// 메소드
@Override
public ModelAndView boardList(HttpServletRequest req, HttpServletResponse res) {
logger.info("boardList 호출");
List<Map<String, Object>> bList = null;
// 사용자가 조건 검색을 원하는 경우 - 조건 값을 전달할 객체 생성함
// MyBatis에서는 동적쿼리를 지원하므로 하나로 2가지 경우 사용 가능함
Map<String, Object> pMap = new HashMap<>();
HashMapBinder hmb = new HashMapBinder(req);
hmb.bind(pMap);
bList = boardLogic.boardList(pMap);
ModelAndView mav = new ModelAndView(req);
mav.setViewName("board3/boardList");
mav.addObject("bList", bList);
return mav; // 리턴이 mav면 webapp/WEB-INF/views/board3 아래의 jsp
}
@Override
public Object jsonBoardList(HttpServletRequest req, HttpServletResponse res) {
logger.info("jsonBoardList호출");
List<Map<String, Object>> bList = null;
Map<String, Object> pMap = new HashMap<>();
HashMapBinder hmb = new HashMapBinder(req);
hmb.bind(pMap);
bList = boardLogic.boardList(pMap);
// 오라클 연동 후에 조회 결과가 bList에 담겨있음
// forward할 때 그 주소번지를 저장해둠 - 화면(jsonBoardList.jsp)에서 접근함 - 키값이 중요함
req.setAttribute("bList", bList);
return "forward:board3/jsonBoardList"; // 리턴이 String이면 webapp/board3 아래의 jsp
}
@Override
public Object boardDetail(HttpServletRequest req, HttpServletResponse res) {
logger.info("boardDetail호출");
List<Map<String, Object>> bList = null;
// 전체 조회에 대한 sql문 재사용 가능함 - 1건 조회 경우
// 하지만 재사용성(상세보기 조회수 업데이트)를 위해 Logic의 메소드 나눔
Map<String, Object> pMap = new HashMap<>();
HashMapBinder hmb = new HashMapBinder(req);
hmb.bind(pMap);
bList = boardLogic.boardDetailList(pMap);
logger.info(bList);
req.setAttribute("bList", bList);
return "forward:board3/boardDetail";
}
/*
* INSERT INTO board_master_t(bm_no, bm_title, bm_writer, bm_content, bm_reg,
* bm_hit) VALUES(seq_board_no.nextval, #{bm_title}, #{bm_writer},
* to_char(sysdate, 'YYYY-MM-DD') , 0)
*
* 화면에서 받아올 값 - bm_title, bm_writer, bm_content 그렇지 않은 경우 - bm_reg
*/
@Override
public Object boardInsert(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
logger.info("boardInsert호출");
int result = 0;
// 폼태그 안에 사용자가 입력한 정보(bm_writer, bm_title, bm_content...)를 받아온다
// req.getParameter("bm_writer")
// req.getParameter("bm_title")
// req.getParameter("bm_content")
// -> 계속 반복해야하기에 HashMapBinder클래스 공통코드 생성
Map<String, Object> pMap = new HashMap<>();
logger.info("before ==> " + pMap);
HashMapBinder hmb = new HashMapBinder(req);
hmb.multiBind(pMap);
logger.info("after ==> " + pMap);
result = boardLogic.boardInsert(pMap);
String path = "";
if (result == 1) {
path = "redirect:/board3/boardList.st3";
} else {
path = "redirect:/board3/boardInsertFail";
res.sendRedirect(path);
}
return path;
}
@Override
public Object boardUpdate(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
logger.info("boardUpdate호출");
int result = 0;
Map<String, Object> pMap = new HashMap<>();
HashMapBinder hmb = new HashMapBinder(req);
hmb.bind(pMap);
logger.info(pMap);
// result값의 변화를 주는 코드(0->1)
result = boardLogic.boardUpdate(pMap);
String path = "";
if (result == 1) {
path = "redirect:/board3/boardList.st3";
} else {
path = "redirect:/board3/boardUpdateFail";
res.sendRedirect(path);
}
return path;
}
@Override
public Object boardDelete(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
logger.info("boardDelete호출");
int result = 0;
Map<String, Object> pMap = new HashMap<>();
HashMapBinder hmb = new HashMapBinder(req);
hmb.bind(pMap);
result = boardLogic.boardDelete(pMap);
String path = "";
if (result == 1) {
path = "redirect:/board3/boardList.st3";
} else {
path = "redirect:/board3/boardUpdateFail";
res.sendRedirect(path);
}
return path;
}
// quill editor에서 이미지 선택하면 업로드처리 - 물리적인 위치 - 톰캣서버 - chat_banana - webapp - pds
// 첨부파일 업로드 API는 cos.jar 사용 - maven repo
@Override
public Object imageUpload(HttpServletRequest req, HttpServletResponse res) {
logger.info("imageUpload 호출 성공");
// 첨부파일 처리에 필요한 변수 선언
// get방식 - header에 담김 - query string
// post방식 - encType속성 - request.getParameter("") 사용자가 입력한 값을 읽을 수 없음
MultipartRequest multi = null; // post이면서 첨부파일이 있는 형태인 경우 이 클래스가 반드시 필요함
String realFolder = "D:\\workspace_java\\chat_banana\\src\\main\\webapp\\pds";
// 첨부파일의 한글처리
String encType = "utf-8";
// 첨부파일의 크기
int maxSize = 50 * 1024 * 1024; // 5MB
try {
// 인스턴스화하기 - 인스턴스화가 성공하자마자 pds폴더에 추가됨
// @param1 - req 요청 - body에 담김(post방식) - 단위테스트x
// @param2 - 실제 파일이 있는 물리적인 위치
// @param3 - 첨부파일의 최대 크기값
// @param4 - 한글 인코딩 설정값
// @param5 - 같은 이름이 있을 경우 관찰하고 그에대한 대응값 반환
multi = new MultipartRequest(req, realFolder, maxSize, encType, new DefaultFileRenamePolicy());
// 거의 즉시 업로드됨 - 파일 크기가 크면 지연상태에 빠짐 - dead lock로 상태 이어지지 않도록 조심!
} catch (Exception e) {
logger.info("Exception : " + e.toString());
}
// String filename = boardLogic.imageUpload(multi, realFolder);
Map<String, Object> rMap = boardLogic.imageUpload(multi, realFolder);
logger.info(rMap);
// Gson g = new Gson();
// String temp = g.toJson(rMap);
// logger.info(temp);
// logger.info(g);
String temp = "";
temp = rMap.get("bs_file").toString() + "," + rMap.get("bs_size").toString();
logger.info(temp);
return temp;
}
// process.env.REACT_APP_CHAT_BANANA_IP+`board3/imageGet.st3?imageName=${res.data}`
@Override
public Object imageGet(HttpServletRequest req, HttpServletResponse res) {
String b_file = req.getParameter("imageName");
logger.info("imageGet 호출 성공===>" + b_file);
String filePath = "D:\\workspace_java\\chat_banana\\src\\main\\webapp\\pds"; // 절대경로.
String fname = b_file;
logger.info("b_file: 8->euc" + b_file);
// File은 내용까지 복제되는 것은 아니고 파일명만 객체화해주는 클래스이다
File file = new File(filePath, b_file.trim());
String mimeType = req.getServletContext().getMimeType(file.toString());
// 마임타입이 널이면 아래의 속성값으로 마임타입을 설정
// -> 브라우저는 해석이 가능한 마임타입은 페이지 로딩 처리,
// 해석이 불가능한 마임타입은 다운로드함
// 강제로 다운로드 처리를 위한 마임타입 변경
if (mimeType == null) {
res.setContentType("application/octet-stream");
}
String downName = null;
FileInputStream fis = null;
ServletOutputStream sos = null;
try {
if (req.getHeader("user-agent").indexOf("MSIE") == -1) {
downName = new String(b_file.getBytes("UTF-8"), "8859_1");
} else {
downName = new String(b_file.getBytes("EUC-KR"), "8859_1");
}
res.setHeader("Content-Disposition", "attachment;filename=" + downName);
// 위에서 생성된 파일 문자열 객체를 가지고 파일생성에 필요한 객체의 파라미터 넘김
fis = new FileInputStream(file);
sos = res.getOutputStream();
// 파일 내용을 담을 byte배열을 생성
byte b[] = new byte[1024 * 10];
int data = 0;
while ((data = (fis.read(b, 0, b.length))) != -1) {
// 파일에서 읽은 내용을 가지고 실제 파일에 쓰기 처리함
sos.write(b, 0, data);
}
// 처리한 내용이 버퍼에 있는데 이것을 모두 처리요청하기
// 내보내고 버퍼를 비운다 - 버퍼는 크기가 작음(휘발성)
sos.flush();
} catch (Exception e) {
logger.info(e.toString());
} finally {
try {
if (sos != null)
sos.close();
if (fis != null)
fis.close();
} catch (Exception e2) {
// TODO: handle exception
}
}
// byte[] fileArray = boardLogic.imageDownload(imageName);
// logger.info(fileArray.length);
return null;
}// end of imageGet
/**
* 이미지 다운로드
*
* @param req
* @param res
*/
public void imageDownload(HttpServletRequest req, HttpServletResponse res) {
logger.info("imageDownload 호출 성공");
String b_file = req.getParameter("imageName");
String filePath = "D:\\workspace_java\\chat_banana\\src\\main\\webapp\\pds"; // 절대경로.
String fname = b_file;
logger.info("b_file: 8->euc" + b_file);
File file = new File(filePath, b_file.trim());
String mimeType = req.getServletContext().getMimeType(file.toString());
if (mimeType == null) {
res.setContentType("application/octet-stream");
}
String downName = null;
FileInputStream fis = null;
ServletOutputStream sos = null;
try {
if (req.getHeader("user-agent").indexOf("MSIE") == -1) {
downName = new String(b_file.getBytes("UTF-8"), "8859_1");
} else {
downName = new String(b_file.getBytes("EUC-KR"), "8859_1");
}
res.setHeader("Content-Disposition", "attachment;filename=" + downName);
fis = new FileInputStream(file);
sos = res.getOutputStream();
byte b[] = new byte[1024 * 10];
int data = 0;
while ((data = (fis.read(b, 0, b.length))) != -1) {
sos.write(b, 0, data);
}
sos.flush();
} catch (Exception e) {
logger.info(e.toString());
} finally {
try {
if (sos != null)
sos.close();
if (fis != null)
fis.close();
} catch (Exception e2) {
// TODO: handle exception
}
}
}// end of imageDownload
}
<리액트와 pojo3 연결 - HashMapBinder.java>
package com.util;
import java.io.File;
import java.util.Enumeration;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.Logger;
import com.oreilly.servlet.MultipartRequest;
import com.oreilly.servlet.multipart.DefaultFileRenamePolicy;
// 스프링부트에선 RequestParam이 대신해줌, Model, ModelMap
// 사용자가 입력한 값을 Map에 담아준다
// 담을 Map은 컨트롤계층에서 bind메소드호출시 파라미터를 이용해서 원본 주소번지를 받아온다
// 그리고 그 안에 담는다
public class HashMapBinder {
Logger logger = Logger.getLogger(HashMapBinder.class);
// 표준 요청 객체
HttpServletRequest req = null; // 전역변수
// cos.jar에서 제공하는 요청객체임 - 첨부파일로 post이면서 encType 속성이 적용된 경우 사용할것
MultipartRequest multi = null;
// 첨부파일의 업로드 물리적인 경로
String realFolder = null;
// 첨부파일 한글처리
String encType = "UTF-8";
// 첨부파일 최대크기
int maxSize = 5 * 1024 * 1024;
public HashMapBinder() {
}
// 생성자 파라미터(지역변수)에 요청객체가 필요한 이유는?
// -> 생성자의 1역할: 전역변수의 초기화
public HashMapBinder(HttpServletRequest req) {
this.req = req;
realFolder = "D:\\workspace_java\\chat_banana\\src\\main\\webapp\\pds";
}
public void multiBind(Map<String, Object> pMap) {
logger.info("multiBind 호출");
// 컨트롤 계층에서 생성한 맵 객체 비우기
pMap.clear();
try {
multi = new MultipartRequest(req, realFolder, maxSize, encType, new DefaultFileRenamePolicy());
} catch (Exception e) {
logger.info(e.toString());//발생한 예외 이름 출력하기
}
Enumeration<String> en = multi.getParameterNames();
System.out.println("multiBind en => " + en);
while (en.hasMoreElements()) {
String key = en.nextElement();
pMap.put(key, multi.getParameter(key));
System.out.println("multiBind key => " + key + " | multi.getParameter(key) => " + multi.getParameter(key));
}
// 첨부파일에 대한 정보 받아오기
Enumeration<String> files = multi.getFileNames(); // n개만큼 처리가능
if(files != null) {
//업로드 대상 파일을 객체로 만듦
File file = null; // 내용까지 복제되는건 아니다 - 파일명에 대해서만 객체화
while(files.hasMoreElements()) {
String fName = files.nextElement();
String fileName = multi.getFilesystemName(fName);
pMap.put("bs_file", fileName);
if(fileName != null && fileName.length() > 1) {
file = new File(realFolder + "\\" + fileName);
}
logger.info(file);
} // end of while
// 첨부파일 크기를 담을 수 있는 변수 선언
double size = 0;
if(file != null) {
size = file.length(); // 파일크기를 byte단위로 담아줌
size = size/1024.0; // byte -> kbyte단위로 바꾸기
pMap.put("bs_size", size);
}
} // end of if
}
// 어떤 페이지 어떤 상황에서 공통코드를 재사용 가능하게 할 것인가?
// 각 업무별 요청 클래스에서 주입받을 객체를 정해서 메소드의 파라미터로 전달받음
// 전달받은 주소번지 원본에 값을 담아준다
public void bind(Map<String, Object> pMap) {
logger.info("bind 호출");
pMap.clear();
Enumeration<String> en = req.getParameterNames();
System.out.println("bind en => " + en);
while (en.hasMoreElements()) {
String key = en.nextElement();
pMap.put(key, req.getParameter(key));
System.out.println("bind key => " + key + " | bind req.getParameter(key) => " + req.getParameter(key));
}
}
}
.
<리액트와 pojo3 연결 - Board3Logic.java>
package com.pojo.step3;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import com.oreilly.servlet.MultipartRequest;
public class Board3Logic {
Logger logger = Logger.getLogger(Board3Logic.class);
Board3Dao boardDao = new Board3Dao();
/**
* 글 목록 출력
*
* @param pMap
* @return
*/
public List<Map<String, Object>> boardList(Map<String, Object> pMap) {
logger.info("boardList 호출" + pMap);
List<Map<String, Object>> bList = null;
bList = boardDao.boardList(pMap);
return bList;
}
/**
* 글 상세보기
*
* @param pMap
* @return
*/
public List<Map<String, Object>> boardDetailList(Map<String, Object> pMap) {
logger.info("boardDetailList 호출" + pMap);
List<Map<String, Object>> bList = null;
bList = boardDao.boardList(pMap);
int bm_no = Integer.parseInt(pMap.get("bm_no").toString());
boardDao.hitCount(bm_no);
return bList;
}
/**
* 새 글쓰기
*
* @param pMap
* @return
*/
public int boardInsert(Map<String, Object> pMap) {
logger.info("boardInsert 호출" + pMap);
int result = 0;
int bm_no = 0;
int bm_group = 0;
bm_no = boardDao.getBNo();
pMap.put("bm_no", bm_no);
// Map안에서 꺼낸다는 건 화면에서 넘어온 값이라는 뜻
if (pMap.get("bm_group") != null) {
bm_group = Integer.parseInt(pMap.get("bm_group").toString());
}
// 댓글쓰기인 경우
if (bm_group > 0) {
logger.info("댓글쓰기 로직 호출");
boardDao.bStepUpdate(pMap);
pMap.put("bm_pos", Integer.parseInt(pMap.get("bm_pos").toString()) + 1);
pMap.put("bm_step", Integer.parseInt(pMap.get("bm_step").toString()) + 1);
}
// 새글쓰기인 경우 -> 그룹번호 채번 포함
else {
logger.info("새글쓰기 로직 호출 => " + bm_group);
bm_group = boardDao.getBGroup();
pMap.put("bm_group", bm_group);
pMap.put("bm_pos", 0);
pMap.put("bm_step", 0);
}
result = boardDao.boardInsert(pMap);
// 첨부파일이 존재할 경우
if (pMap.get("bs_file") != null && pMap.get("bs_file").toString().length() > 1) {
pMap.put("bm_no", bm_no);
// 현재 첨부파일은 하나만 담는 것으로 가정하고 처리함
pMap.put("bs_seq", 1);
int result2 = 0;
result2 = boardDao.boardSInsert(pMap);
logger.info(result2); // 1이면 등록 성공(첨부파일 추가 성공)
}
return result;
}
/**
* 글 수정
*
* @param pMap
* @return
*/
public int boardUpdate(Map<String, Object> pMap) {
logger.info("boardUpdate 호출" + pMap);
int result = 0;
result = boardDao.boardMUpdate(pMap);
return result;
}
/**
* 글 삭제
*
* @param pMap
* @return
*/
public int boardDelete(Map<String, Object> pMap) {
logger.info("boardDelete 호출" + pMap);
int result = 0;
result = boardDao.boardMDelete(pMap);
return result;
}
/**
* 이미지 업로드
*
* @param multi
* @param realFolder
* @return
*/
// 컨트롤 계층에서 multi 주소번치를 넘겨준 이유는
// 실제 파일명과 파일 크기를 Map에 담기 위해서임
// 반환값으로는 파일명, 파일크기
// pageMove[0]에만 값이 들어가고 [1]은 null인 상태 - ArrayIndexOutofBoundException
public Map<String, Object> imageUpload(MultipartRequest multi, String realFolder) {
Map<String, Object> pMap = new HashMap<>();
logger.info("image:" + multi);
String filename = null;
String fullPath = null;
// 첨부파일에 대한 정보를 받아오는 코드 추가
Enumeration<String> files = multi.getFileNames();
logger.info("files : " + files);
// 첨부파일이 존재하나?
if (files != null) {
File file = null;
while (files.hasMoreElements()) {
String fname = files.nextElement();
logger.info("fname:" + fname);// bs_file
filename = multi.getFilesystemName(fname);
logger.info("filename:" + filename);// 첨부파일이름
pMap.put("bs_file", filename);
// file객체 생성하기 -> 왜냐하면 첨부파일의 크기를 알고 싶어요....
// file = multi.getFile(filename);
if (filename != null && filename.length() > 1) {
file = new File(realFolder + "\\" + filename);
}
logger.info("file:" + file);
} // end of while
// 첨부파일의 크기를 담을 수 있는 변수
double size = 0;
if (file != null) {
size = file.length();// 파일 크기가 저장 5.2MB
logger.info("size:" + size);
size = size / (1024);
logger.info("size/1024:" + size);
pMap.put("bs_size", size);
}
// int result = boardSDao.boardSInsert(pMap);
} ////// end of if
// return filename;
return pMap;
}
/**
* 이미지 다운로드
*
* @param imageName
* @return
*/
public byte[] imageDownload(String imageName) {
String fname = null;
try {
fname = URLDecoder.decode(imageName, "UTF-8");
logger.info(fname);
} catch (UnsupportedEncodingException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
// out.print("b_file: 8->euc"+b_file);
// out.print("<br>");
String filePath = "D:\\workspace_java\\chat_banana\\src\\main\\webapp\\pds"; // 절대경로.
// 가져온 파일이름을 객체화 시켜줌. - 파일이 있는 물리적인 경로가 필요함.
File file = new File(filePath, fname.trim());
// 해당 파일을 읽어오는 객체 생성해 줌. - 이 때 파일명을 객체화 한 주소번지가 필요함.
FileInputStream fis = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
fis = new FileInputStream(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
int readCount = 0;
byte[] buffer = new byte[1024];
byte[] fileArray = null;
try {
while ((readCount = fis.read(buffer)) != -1) {
baos.write(buffer, 0, readCount);
}
fileArray = baos.toByteArray();
fis.close();
baos.close();
} catch (IOException e) {
throw new RuntimeException("File Error");
}
return fileArray;
}
}
'국비학원 > 수업기록' 카테고리의 다른 글
국비 지원 개발자 과정_Day74 (1) | 2023.03.15 |
---|---|
국비 지원 개발자 과정_Day73 (1) | 2023.03.14 |
국비 지원 개발자 과정_Day71 (1) | 2023.03.10 |
국비 지원 개발자 과정_Day70 (1) | 2023.03.09 |
국비 지원 개발자 과정_Day69 (1) | 2023.03.08 |
댓글