본문 바로가기
국비학원/수업기록

국비 지원 개발자 과정_Day73

by 루팽 2023. 3. 14.

<리액트, pojo 파일첨부 추가 - 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 BoardFileDetail from './BoardFileDetail'
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'}}>
          {/* 오라클 서버에서 읽어온 BM_CONTENT 정보를 Quill Editor에 출력하는 코드 */}
          <div dangerouslySetInnerHTML={{__html:board.BM_CONTENT}}></div>
        </section>
        <BoardFileDetail files={files} />
        <hr style={{height:"2px"}}/>
      </FormDiv>
    </ContainerDiv>
  )
}

export default BoardDetail

 

<리액트, pojo 파일첨부 추가 - BoardFileDetail.jsx>

import React from 'react';
import axios from 'axios';
import styled from 'styled-components';

const Dspan = styled.span`
  padding: 2px 5px 2px 5px;
  font-size: 14px;
  cursor: pointer;
  &:hover {
    border-bottom: 1px solid gray;
  }
`
const BoardFileDetail = ({files}) => {
  console.log(files[0]);
  const download = () => {
    axios({
      method: 'GET',
      url: process.env.REACT_APP_CHAT_BANANA_IP+`board3/imageDownload.st3?imageName=${files[0]}`,                 
      responseType: 'blob' 
    }).then(response =>{        
      const url = window.URL.createObjectURL(new Blob([response.data], 
        { type: response.headers['content-type'] }));
      const link = document.createElement('a');
      link.href = url;
      //link.setAttribute('download', 'img.jpg');
      link.setAttribute('download', `${files[0]}`);
      document.body.appendChild(link);
      link.click();
    })    
  }
  return (
    <div style={{display:'block', border:'1px solid lightGray', borderRadius:'10px', minHeight:'60px', padding:'5px'}}>
    <div style={{textAlign:"left", padding: "2px 5px 2px 5px"}}>첨부파일</div>
      {
          <div>
            <Dspan type='text' id='fileUpload'
              onClick={download}
            >
              {files[0]}
            </Dspan>
          </div>
      }
    </div>
  );
};

export default BoardFileDetail;

 

<리액트, pojo 파일첨부 추가 - 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;
				}
			}
			
			// 이미지 다운로드 - 리액트 quill editor 이미지 추가
			else if("imageDownload".equals(upmu[1])) {
				obj = controller.imageDownload(req, res);
				logger.info("imageDownload 호출 => " + 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;
	}
}

 

<리액트, pojo 파일첨부 추가 - 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 imageDownload(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;
}

 

<리액트, pojo 파일첨부 추가 - 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') , 
	 * )
	 * 
	 * 화면에서 받아올 값 - 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
	// 하나. 바이너리 타입 코드 첨부할 때
	// 둘. 이미지 파일 첨부할 때
	// 첨부파일 처리(application[main타입]/*[sub타입-img.png, img.gif]
	// 파일크기 제한 5MB - 유효성 체크 필요(UI에서 체크해서 5MB 초과시 에러화면 처리)
	@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}`
	// Quill Editor에서 필요 - 위지웤기능 첨가 <p></p><img... /> PNG, JPG, JPEG
	// 일단 이미지를 선택하면 pds에 먼저 업로드되고, 그 이미지 경로를 참조해서 editor에 출력해줌
	// 리턴타입이 널인 이유는 이미지 정보를 얻어오는 것이기에 -> 화면적으로 처리할 부분이 없다
	// 에디터에서 이미지를 선택하면 bm_content 컬럼에 img태그와 함께 이미지 정보에대한 소스가
	// 텍스트 형태로 저장됨
	// 오라클 서버에 저장된 bm_content내용을 읽어서 브라우저에 출력해줌
	@Override
	public Object imageGet(HttpServletRequest req, HttpServletResponse res) {
		// imageName 정보는 공통코드로 제공된 QuillEditor.jsx에서 파라미터로 넘어오는 값임
		// imageUpload 메소드에서는 업로드된 파일 정보(파일명, 파일크기)가 리턴됨
		String b_file = req.getParameter("imageName"); // get방식으로 넘어옴
		logger.info("imageGet 호출 성공===>" + b_file); // XXX.png
		// 톰캣 서버의 물리적인 위지 정보
		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());
		logger.info(mimeType); // image, video, text
		if (mimeType == null) { // 마임타입이 널이면 아래의 속성값으로 마임타입을 설정
			// -> 브라우저는 해석이 가능한 마임타입은 페이지 로딩 처리,
			// 		해석이 불가능한 마임타입은 다운로드함
			// 강제로 다운로드 처리를 위한 마임타입 변경
			// -> 브라우저에서 해석가능한 마임타입의 경우 화면에 그대로 출력되니까 그걸 방지하기위해
			res.setContentType("application/octet-stream");
		}
		// 다운로드되는 파일 이름 담기
		String downName = null;
		// 위 File 객체에서 생성된 객체에 내용을 읽기위한 클래스 선언
		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
	 */
	// download.jsp 페이지의 내용과 같다
	@Override
	public Object 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());
	 	logger.info("mimeType : "+mimeType);
		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
			}
		}
		return null;
	}// end of imageDownload
}

 

<리액트, pojo 파일첨부 추가 - PageBar.java>

package com.util;

import org.apache.log4j.Logger;

public class PageBar {
	Logger logger = Logger.getLogger(PageBar.class);
	//전체레코드 갯수
	private int totalRecord;//list.size():47row
	//페이지당 레코드 수
	private int numPerPage;// 10개씩이다
	//블럭당 디폴트 페이지 수 - 여기서는 일단 3개로 정함.
	private int pagePerBlock=3;
	//총페이지 수
	private int totalPage;
	//총블럭 수
	private int totalBlock;
	//현재 내가 바라보는 페이지 수
	private int nowPage;
	//현재 내가 바라보는 블럭 수
	private int nowBlock;
	//적용할 페이지 이름
	private String pagePath;
	
	private String pagination;
	//페이지 네비게이션 초기화
	/*
	 * 화면에서 받아와야 하는 정보에는 어떤 것들이 있을까?
	 * 페이지에 뿌려질 로우의 수 numberPerPage
	 * 전체 레코드 수 totalRecord
	 * 현재 내가 바라보는 페이지 번호 nowPage
	 * 내가 처리해야할 페이지 이름 pagePath
	 * 
	 * 공식을 세우는데 필요한 인자는 누구?
	 * 
	 * 세워진 공식들은 어디에서 적용하면 되는 거지?
	 * 
	 * 화면에 내보내 져야 하는 언어는 html 아님 자바 중에서 ?????
	 * html
	 * 내보내지는 정보는 어디에 담으면 될까?
	 * 
	 */
	public PageBar(int numPerPage, int totalRecord, int nowPage, String pagePath) {
		this.numPerPage = numPerPage;
		this.totalRecord = totalRecord;
		this.nowPage = nowPage;
		this.pagePath = pagePath;
		
		this.totalPage = 
				(int)Math.ceil((double)this.totalRecord/this.numPerPage);// 47.0/10=> 4.7 4.1->5page 4.2->5page
		this.totalBlock= 
				(int)Math.ceil((double)this.totalPage/this.pagePerBlock);//5.0/2=> 2.5-> 3장
		//현재 내가바라보는 페이지 : (int)((double)4-1/2)
		this.nowBlock = (int)((double)this.nowPage/this.pagePerBlock);
	}
	
	//setter메소드 선언
	public void setPageBar() {
		StringBuilder pageLink = new StringBuilder();
		//전체 레코드 수가 0보다 클때 처리하기
		if(totalRecord>0) {
			//nowBlock이 0보다 클때 처리
			//이전 페이지로 이동 해야 하므로 페이지 번호에 a태그를 붙여야 하고
			//pagePath뒤에 이동할 페이지 번호를 붙여서 호출 해야함.
			if(nowBlock > 0 ) {                                    //(1-1)*2+(2-1)=1
				pageLink.append("<a href='"+pagePath+"?nowPage="+((nowBlock-1)*pagePerBlock+(pagePerBlock-1))+"'>");
				pageLink.append("<img border=0 src='/images/bu_a.gif'>");
				pageLink.append("</a>&nbsp;&nbsp;");
			}
			for(int i=0;i<pagePerBlock;i++) {
				//현재 내가 보고 있는 페이지 블록 일때와
				if(nowBlock*pagePerBlock+i==nowPage) {
					pageLink.append("<b>"+(nowBlock*pagePerBlock+i+1)+"</b>&nbsp;");
				}
				//그렇지 않을 때를 나누어 처리해야 함.
				else {
					pageLink.append("<a href='"+pagePath+"?nowPage="+((nowBlock*pagePerBlock)+i)+"'>"+((nowBlock*pagePerBlock)+i+1)+"</a>&nbsp;");
					
				}
				//모든 경우에 pagePerBlock만큼 반복되지 않으므로 break처리해야 함.
				//주의할 것.
				if((nowBlock*pagePerBlock)+i+1==totalPage) break;
			}
			//현재 블록에서 다음 블록이 존재할 경우 이미지 추가하고 페이지 이동할 수 있도록
			//a태그 활용하여 링크 처리하기
			if(totalBlock > nowBlock+1) {
				pageLink.append("&nbsp;&nbsp;<a href='"+pagePath+"?nowPage="+((nowBlock+1)*pagePerBlock)+"'>");
				pageLink.append("<img border=0 src='/images/bu_b.gif'>");
				pageLink.append("</a>");	
			}
		}
		logger.info("pageLink.toString():"+pageLink.toString());
		pagination = pageLink.toString();
	}
	
	//getter메소드 선언
	public String getPageBar() {
		this.setPageBar();
		return pagination;
	}
}

 

<리액트, pojo 파일첨부 추가 - boardList.jsp>

<%@page import="com.util.PageBar"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="java.util.*, com.util.PageBar" %>    
<%
// jsp에서 자바코드(스크립틀릿)와 html코드의 작성 위치는 문제가 되지 않는다.
// 왜냐하면 어차피 jsp는 서버에서 실행되고 그 결과가 text로 출력되는 것이므로 
// html과 처리 시점이 완전 다르니까...
	List<Map<String,Object>> boardList = 
			(List<Map<String,Object>>)request.getAttribute("bList");
	int size = 0;
	if(boardList!=null){
		size = boardList.size();
	}		
	// 한 페이지에 출력될 로우의 수
	int numPerPage = 3;
	// 현재 내가 바라보는 페이지
	int nowPage = 0;
	if(request.getParameter("nowPage") != null){
		nowPage = Integer.parseInt(request.getParameter("nowPage"));
	}
%>    
<!DOCTYPE html>
<html>
<head>
<!-- <meta charset="UTF-8"> 이것때문에 한글깨짐.-->
<title>MVC기반의 계층형 게시판 구현하기[WEB-INF]</title>
<%@ include file="../common/easyUI_common.jsp" %>
<script type="text/javascript">
	let g_no=0;//그리드에서 선택이 바뀔때 마다 변경된 값이 저장됨.
	let tb_value;//사용자가 입력한 문자열 담기
	let isOk = false;
	function dlgIns_save(){
		//폼 전송 처리함.
		$("#f_boardIns").submit();
	}
	function dlgIns_close(){
		$("#dlg_boardIns").dialog('close');
	}
	function getBoardList(){
		//alert("getBoardList호출");
		//사용자가 선택한 콤보박스에 value가 담김 - b_title, or b_content or b_writer
		cb_value = user_combo;
		tb_value = $("#tb_search").val();//사용자가 입력한 조건 검색 문자열
		console.log("콤보박스 값: "+ cb_value+", 사용자가 입력한 키워드: "+tb_value);
		location.href = "./boardList.st3?cb_search="+cb_value+"&tb_search="+tb_value+"&bm_reg="+v_date;
	}	
	function boardDetail(bm_no){
		console.log(bm_no)
		location.href = "./boardDetail.st3?bm_no="+bm_no;
	}
    function fileDown(fname){
		location.href="downLoad.jsp?bs_file="+fname;
    }	
</script>
</head>
<body>
<script type="text/javascript">
	let user_combo="bm_title";//제목|내용|작성자
	//전변 - javascript에서는 선언만 하고 선택을 하지 않았거나 값이 할당되지 않으면 
	//그냥 null비교만 해서는 안된다.
	let v_date;//사용자가 선택한 날짜 정보 담기
//기본 날짜포맷을 재정의
	$.fn.datebox.defaults.formatter = function(date){
		var y = date.getFullYear();
		var m = date.getMonth()+1;
		var d = date.getDate();
		return y+'-'+(m<10? "0"+m:m)+'-'+(d<10? "0"+d:d);
	}
//날짜 포맷을 적용	
	$.fn.datebox.defaults.parser = function(s){
		var t = Date.parse(s);
		if (!isNaN(t)){
			return new Date(t);
		} else {
			return new Date();
		}
	}	
	$(document).ready(function(){//DOM구성이 완료된 시점-자바스크립트로 태그접근,설정변경,이미지
		$("#dg_board").datagrid({
			onSelect:function(index,row){
				g_no = row.B_NO;
				console.log("g_no:"+g_no);
			},
			onDblClickCell: function(index, field, value){
				if("B_TITLE" == field){
					location.href="./boardDetail.pj?b_no="+g_no;
					g_no = 0;
					$("#dg_board").datagrid('clearSelections')
				}
			}
		});
	
		//등록 날짜 정보를 선택했을 때
		$('#db_date').datebox({
			//왜? undefinded이었나?
			onSelect: function(date){
				//alert(date.getFullYear()+":"+(date.getMonth()+1)+":"+date.getDate());
				const y = date.getFullYear();
				const m = date.getMonth()+1;
				const d = date.getDate();
				v_date = y+"-"+(m<10? "0"+m:m)+"-"+(d<10? "0"+d:d);
				//console.log("사용자가 선택한 날짜 ==> "+v_date);
			}
		});
		
		//검색 조건 콤보에 변경이 일어났을 때
		$('#cb_search').combobox({
			onChange:function(){
				user_combo = $("#cb_search").combobox('getValue');//b_title or b_content or b_writer
				console.log(user_combo)
			}
		});

		$('#tb_search').textbox({
			icons: [{
				iconCls:'icon-search',
				handler: function(e){
					console.log("검색");
					//$(e.data.target).textbox('setValue', 'Something added!');
					$("#dg_board").datagrid({

					});
				}
			}]
		});

	    /*===================== CRUD버튼 시작 ====================*/	    
		//조회버튼 클릭했을 때
	    $('#crudBtnSel').bind('click', function(){
	    	getBoardList();
	    });
		$('#crudBtnIns').bind('click', function(){
	        //alert('입력 버튼');
	        $("#dlg_boardIns").dialog('open');
	    });	
		$('#crudBtnUpd').bind('click', function(){
	        alert('수정 버튼');
	    });	
		$('#crudBtnDel').bind('click', function(){
	        alert('삭제 버튼');
	    });			
	    /*===================== CRUD버튼 끝 ====================*/	    

	});///////////////// end of ready
</script>
<center>
    <table id="dg_board" class="easyui-datagrid" title="계층형 게시판 목록" style="width:800px;height:550px"
            data-options="rownumbers:true,singleSelect:true,toolbar:'#tb',footer:'#pn_board'">
        <thead>
            <tr>
                <th data-options="field:'BM_NO',width:60, align:'center', hidden:'true'">글번호</th>
                <th data-options="field:'BM_TITLE',width:350">제목</th>
                <th data-options="field:'BM_WRITER',width:80,align:'center'">작성자</th>
                <th data-options="field:'BM_REG',width:100,align:'center'">작성일</th>
                <th data-options="field:'BS_FILE',width:170">첨부파일</th>
                <th data-options="field:'BM_HIT',width:60,align:'center'">조회수</th>
            </tr>
        </thead>
        <tbody>
<%
	if(size==0){
%> 	
<script>
	$.messager.alert('Info','조회결과가 없습니다.');
</script>
<%
	}
	else if(size>0){
		for(int i = nowPage*numPerPage; i < (nowPage*numPerPage)+numPerPage; i++){
			if(size == i) break;
			Map<String,Object> rMap = boardList.get(i);
%>	      
        	<tr>
        		<td><%=1%></td>
        		<td>
<!-- 너 댓글이니? -->
<%
	String imgPath = path + "..\\images\\";
	if(Integer.parseInt(rMap.get("BM_POS").toString()) > 0){
		for(int j=0; j<Integer.parseInt(rMap.get("BM_POS").toString()); j++){
			out.print("&nbsp;&nbsp;&nbsp;");
		}
%>
	<img src="<%=imgPath%>reply.gif" />
<%
	}
%>	
<a href="javascript:boardDetail('<%=rMap.get("BM_NO") %>')" style="text-decoration:none;color:#000000">        		
        		<%=rMap.get("BM_TITLE")%>
</a>        		
        		</td>
        		<td><%=rMap.get("BM_WRITER")%></td>
        		<td><%=rMap.get("BM_REG")%></td>
        		<td>
        		<a href="javascript:fileDown('<%=rMap.get("BS_FILE")%>')" style="text-decoration: none; color:#000000">
        		<%=rMap.get("BS_FILE")%>	
        		</a>
        		</td>
        		<td><%=rMap.get("BM_HIT")%></td>
        	</tr>
<%
		}// end of for
	}// end of else if
%>        	
        </tbody>
    </table>
<!-- 툴바 추가 중 조건검색 화면 시작 -->    
    <div id="tb" style="padding:2px 5px;">
                                    <!-- 
                                    req.getParameter("cb_search"):String
                                    SELECT * FROM board_master_t
                                    WHERE ?(컬럼) LIKE %||?||%
                                     -->
        <select class="easyui-combobox" id="cb_search" name="cb_search" panelHeight="auto" style="width:100px">
            <option selected>선택</option>
            <option value="bm_title">제목</option>
            <option value="bm_content">내용</option>
            <option value="bm_writer">작성자</option>
        </select>
        <input id="tb_search" name="tb_search" class="easyui-textbox" style="width:320px">
        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                작성일: <input id="db_date" class="easyui-datebox" name="bm_date" style="width:110px">
	<!-- 버튼 추가 화면 시작 --> 
	    <div id="ft" style="padding:2px 5px;">
	        <a id="crudBtnSel" href="#" class="easyui-linkbutton" iconCls="icon-search" plain="true">조회</a>
	        <a id="crudBtnIns" href="#" class="easyui-linkbutton" iconCls="icon-edit" plain="true">입력</a>
	        <a id="crudBtnUpd" href="#" class="easyui-linkbutton" iconCls="icon-reload" plain="true">수정</a>
	        <a id="crudBtnDel" href="#" class="easyui-linkbutton" iconCls="icon-cut" plain="true">삭제</a>
	    </div>
	<!-- 버튼 추가 화면 끝 -->     
    </div>
<!-- 툴바 추가 중 조건검색 화면 끝 -->
   
<!-- 페이지 네이션 추가 시작 -->
	<div style="display:table-cell;vertical-align:middle; width:800px; background:#efefef; height:30; border:1px solid #ccc;">
	<%
		// 페이지 네비게이션이 필요한 페이지가 다를 것이다
		String pagePath = "boardList.st3";
		// 인스턴스화 할 때 생성자 파라미터로 페이징 처리에 필요한 변수 초기화
		// 페이징 처리에 필요한 a태그들에 대한 문자열을 만들어서 getPageBar() 메소드를 호출하면
		// 페이징 처리에 필요한 문자열을 stringBuilder에 처리하였고
		// 그 문자열을 toString()으로 읽어와서 출력해줌
		PageBar pb = new PageBar(numPerPage, size, nowPage, pagePath);
		out.print(pb.getPageBar());
	%>
	</div>
<!-- 페이지 네이션 추가   끝  -->
<%
	String gubun = request.getParameter("gubun");
	if("list".equals(gubun)){
%>	
<script type="text/javascript">
		getBoardList();
</script>	
<%		
	}
%>
<!-- 글입력 화면 추가 시작 -->
    <div id="dlg_boardIns" footer="#tb_boardIns" class="easyui-dialog" title="글쓰기" data-options="modal:true,closed:true" style="width:600px;height:400px;padding:10px">
        <form id="f_boardIns" method="post" enctype="multipart/form-data" action="./boardInsert.st3">
        <!-- <form id="f_boardIns" method="get" action="./boardInsert.st3"> -->
        <!-- 
        	hidden속성은 화면에 보이지 않음 - 개발자가 필요로하는 값
        	등록 부분과 수정 부분이 동시에 발생할 수도 있다 - 트랜젝션 처리가 필요함
        	트랜잭션 처리가 필요한 경우의 메소드 설계
         -->
	    <input type="hidden" id="bm_no" name="bm_no" value="0">
	    <input type="hidden" id="bm_group" name="bm_group" value="0">
	    <input type="hidden" id="bm_pos" name="bm_pos" value="0">
	    <input type="hidden" id="bm_step" name="bm_step" value="0">
        	<table>
        		<tr>
        			<td width="100px">제&nbsp;&nbsp;&nbsp;목</td>
        			<td width="500px"><input id="bm_title" name="bm_title" class="easyui-textbox" data-options="width:'250px'" required></td>
        		</tr>
        		<tr>
        			<td width="100px">작&nbsp;성&nbsp;자</td>
        			<td width="500px"><input id="bm_writer" name="bm_writer" class="easyui-textbox" data-options="width:'150px'" required></td>
        		</tr>
        		<tr>
        			<td width="100px">내&nbsp;&nbsp;&nbsp;용</td>
        			<td width="500px"><input id="bm_content" name="bm_content" class="easyui-textbox" data-options="multiline:'true',width:'350px', height:'90px'" required></td>
        		</tr>
        		<tr>
        			<td width="100px">비&nbsp;&nbsp;&nbsp;번</td>
        			<td width="500px"><input id="bm_pw" name="bm_pw" class="easyui-textbox" data-options="width:'100px'" required></td>
        		</tr>
        		<tr>
        			<td width="100px">첨부파일</td>
        			<td width="500px"><input id="bs_file" name="bs_file" class="easyui-filebox" data-options="width:'350px'"></td>
        		</tr>
        	</table>
        </form>
    </div>
    <!-- 다이얼로그 화면 버튼 추가 시작 -->
	<div id="tb_boardIns">
	<a href="javascript:dlgIns_save()" class="easyui-linkbutton">저장</a>
	<a href="javascript:dlgIns_close()" class="easyui-linkbutton">닫기</a>
	</div>    
    <!-- 다이얼로그 화면 버튼 추가  끝   -->
<!-- 글입력 화면 추가  끝   -->
</center>
</body>
</html>

 

<스프링 맵 - InsaMap.java>

package com.example.demo.di;

import java.util.HashMap;

public class InsaMap {
	HashMap<String, String> insaMap = null;

	// 생성자 객체주입법 코드와 setter 객체주입법 코드가 있다
	// spring boot에서는 @Autowired 어노테이션으로 필요가 없어짐
	// insaMap = new HashMap<>();를 대신 해줌
	// setter 객체 주입법
	public void setInsaMap(HashMap<String, String> insaMap) {
		this.insaMap = insaMap;
	}
}

 

<스프링 맵 - InsaMap.xml>

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="insaMap" class="com.example.demo.di.InsaMap">
	<property name="insaMap">
		<map>
			<entry>
				<key><value>one</value></key>
				<value>Hi!</value>
			</entry>
			<entry>
				<key><value>two</value></key>
				<value>Have a good time</value>
			</entry>
			<entry>
				<key><value>three</value></key>
				<value>Have a nice day</value>
			</entry>
		</map>
	</property>
</bean>
</beans>

 

<스프링 맵 - InsaMain.java>

package com.example.demo.di;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class InsaMain {

	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext("com\\example\\demo\\di\\insaMap.xml");
		InsaMap insaMap = (InsaMap)context.getBean("insaMap");
		
		System.out.println(insaMap.insaMap);
		// {one=Hi!, two=Have a good time, three=Have a nice day}
	}
}

 

STS 실행 시 톰캣 못 찾아서 jsp 다운로드될 경우 아래코드 추가하고 spring-boot-starter-tomcat 삭제할 것!

<!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-jasper -->
<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-jasper</artifactId>
</dependency>

 

pojo와 스프링비교

서블릿 등록하기

  web.xml - 배치서술자(deployment descriptor) → pojo

  @WebServlet(”XXX.do”) → 스프링

 

웹서비스-http, request, response → 서블릿 제공

  request.getParameter(”mem_id”); → 반복코드(파라미터만 다름, <input>에 사용자가 입력한 값)

  request.getParameter(”mem_pw”);

  request.getParameter(”mem_name”);

pojo3부터 HashMapBinder.java 사용 → 반복되는 코드를 줄임

스프링→@RequestParam Map(타입)

 

서블릿

  FrontMVC1 → ActionServlet → ActionSupport(pojo1~3)

    서블릿에 의존적인 프레임워크(req, res 없이는 페이지이동 x)

    요청객체와 응답객체가 있어야 듣기와 말하기가 가능함

    req.getParameter() → Servlet필요(web.xml에 등록)

    → 요청객체와 응답객체가 없어도 웹서비스 가능했으면..

 

  DispatcherServlet

    spring에서 제공하는 서블릿 → 3.0까지

    web.xml에 등록

    4.0부터는 완전한 독립이 가능(서블릿으로부터의 완전한 독립)

 

  @Controller → 화면 출력용

  @RestController → 데이터셋 출력용(리턴타입 String, 마임타입 text/plain)

    더 이상 web.xml 필요하지 않음

    완전 자동화

 

컨트롤계층-연결만 담당, 응답페이지 결정

  forward:board/boardList → application.properties에 prefix, suffix 설정해 뒀기에 뒤에 .jsp 붙이지 않음, scope는 request

  redirect:board/boardList → scope는 page

  둘 다 없는 것 → ModelAndView(이제 안 씀) → Model, ModelMap(어노테이션 아님, 파라미터로 객체 주입받음, UI패키지 제공, 화면처리용)

 

void doGet() or doPost()

→ void doService(): 오버라이딩 아님

→ ActionForward execute(req, res): 오버라이딩

→ String execute(req, res): 오버라이딩

→ String 메소드이름변함(req, res): 일반 메소드도 req, res 가지게 됨, XXXController가 더 이상 서블릿이 아니다

 

<테스트 시나리오>

1. board/boardList.jsp

  위와 같이 테스트하는 것은 컨트롤 클래스를 경유하지 않음

  webapp폴더 아래 board 아래에 있는 boardList.jsp 페이지를 요청하는 것임

 

2. board/boardList

  컨트롤 클래스를 경유함

  응답 페이지를 webapp에서 찾을지 혹은 WEB-INF/views/ 아래에서 찾을지 아직은 결정할 수 없는 상태임

  return “forward:board/boardList”

  return “redirect:board/boardList”

  return “board/boardList”

 

Spring Boot 어노테이션

  자바에서 Annotation(@)은 코드 사이에 주석처럼 쓰이며 특별한 의미, 기능을 수행하도록 하는 기술

  프로그램에 추가적인 정보를 제공해 주는 메타데이터

 

@Controller

  Spring에게 해당 Class가 Controller의 역할을 한다고 명시하기 위해 사용

  view(화면) 리턴이 주목적

 

@RestController

  Controller 중 View로 응답하지 않는 Controller를 의미

  method의 반환 결과를 JSON 형태로 반환함

  view가 필요 없는 API만 지원하는 서비스에서 사용

  data(json, xml 등) 리턴이 주목적

 

@Service

  Service Class에서 쓰임

  비즈니스 로직을 수행하는 클래스라는 것을 나타내는 용도

 

@Repository

  Dao Class에서 쓰임  DB에 접근하는 메소드를 가지고 있는 클래스에서 쓰임

 

@RequestMapping

  @RequestMapping(value=”“)와 같은 형태로 작성

  요청 들어온 URI의 요청과 Annotation value 값이 일치하면 해당 클래스나 메소드가 실행

  요청받는 형식을 정의하지 않으면 GET으로 자동 설정됨

 

@GetMapping

  RequestMapping(Method=RequestMethod.GET)과 똑같은 역할

 

@Autowired

  Bean을 주입받기 위하여 사용

  스프링이 자동적으로 값을 할당해 줌

  → Bean을 주입받는 방식: @Autowired, setter, 생성자

 

<스프링부트 - application.properties>

server.port=8000

spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp

 

<스프링부트 - BoardController.java>

package com.example.demo.controller;

import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import com.example.demo.logic.BoardLogic;

@Controller
@RequestMapping("/board/*")
public class BoardController {
	Logger logger = LoggerFactory.getLogger(BoardController.class);
	
	@Autowired
	private BoardLogic boardLogic = null;
	// setter없이 @Autowired만으로 의존성 주입 가능
	
	@GetMapping("boardList")
	public String boardList(Model model, @RequestParam Map<String, Object> pMap) {
		logger.info("boardList 호출");
		logger.info(pMap.toString());
		List<Map<String, Object>> bList = null;
		bList = boardLogic.boardList(pMap);
		model.addAttribute("bList", bList);
//		return "forward:boardList.jsp"; -> webapp 연결
		return "board/boardList"; // WEB-INF 연결(application.properties에 설정해둠)
	}
}

 

<스프링부트 - RestBoardController.java>

package com.example.demo.controller;

import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.example.demo.logic.BoardLogic;
import com.google.gson.Gson;

@RestController
@RequestMapping("/board/*")
public class RestBoardController {
	Logger logger = LoggerFactory.getLogger(RestBoardController.class);
	
	@Autowired
	private BoardLogic boardLogic = null;
	// setter없이 @Autowired만으로 의존성 주입 가능
	
	@GetMapping("jsonBoardList")
	public String boardList(Model model, @RequestParam Map<String, Object> pMap) {
		logger.info("jsonBoardList 호출");
		logger.info(pMap.toString());
		List<Map<String, Object>> bList = null;
		bList = boardLogic.boardList(pMap);
		model.addAttribute("bList", bList);
		Gson g = new Gson();
		String temp = g.toJson(bList);
		return temp;
	}
	
	@GetMapping("getTest")
	public String getTest () {
		logger.info("getTest 호출");
		return "테스트";
	}
}

 

<스프링부트 -BoardLogic.java>

package com.example.demo.logic;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.example.demo.dao.BoardDao;

@Service
public class BoardLogic {
	Logger logger = LoggerFactory.getLogger(BoardLogic.class);

	@Autowired
	private BoardDao boardDao = null;
	
	public List<Map<String, Object>> boardList(Map<String, Object> pMap) {
		logger.info("boardList 호출");
		List<Map<String, Object>> bList = new ArrayList<>(); // NullPointerException 회피
		bList = boardDao.boardList(pMap);
		return bList;
	}
}

 

<스프링부트 -BoardDao.java>

package com.example.demo.dao;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;

// @Repository -> MVC패턴 학습위해 Service로 처리
@Service
public class BoardDao {
	Logger logger = LoggerFactory.getLogger(BoardDao.class);

	public List<Map<String, Object>> boardList(Map<String, Object> pMap) {
		logger.info("boardList 호출");
		List<Map<String, Object>> bList = null;
		if(bList == null) { // NullPointerException 방어코드
			bList = new ArrayList<>();
			Map<String, Object> rMap = new HashMap<>();
			rMap.put("BM_TITLE", "공지사항1");
			rMap.put("BM_WRITER", "김춘추");
			bList.add(rMap);
			rMap = new HashMap<>();
			rMap.put("BM_TITLE", "공지사항2");
			rMap.put("BM_WRITER", "이성계");
			bList.add(rMap);
			rMap = new HashMap<>();
			rMap.put("BM_TITLE", "공지사항3");
			rMap.put("BM_WRITER", "강감찬");
			bList.add(rMap);
		}
		return bList;
	}
}

 

<스프링부트 -boardList.jsp>

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="java.util.List, java.util.Map" %>
<%
	List<Map<String, Object>> bList = null;
	bList = (List<Map<String, Object>>)request.getAttribute("bList");
	
	//if(bList != null){
		for(int i=0; i<bList.size(); i++) {
			Map<String, Object> rMap = bList.get(i);
			out.print("제목:" + rMap.get("BM_TITLE") + "<br />");
			out.print("작성자:" + rMap.get("BM_WRITER") + "<br />" + "<hr />");
		}		
	//}
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>계층형 게시판(WEB-INF)</title>
</head>
<body>
<h3>계층형 게시판</h3>
</body>
</html>

 

<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;
		
		// common
		if("common".equals(upmu[0])) {
			controller = new CommonController();
			// 우편번호 조회
			if("zipcodeList".equals(upmu[1])) {
				obj = controller.zipcodeList(req, res);
				// 리턴타입이 ModelAndView인 경우
				if(obj instanceof ModelAndView) {
					return (ModelAndView)obj;
				}
				// 리턴타입이 String인 경우
				else if(obj instanceof String) {
					return (String)obj;
				}
			} // end if zipcodeList if문
		}
		
		// 게시판 구현
		else 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;
				}
			}
			
			// 이미지 다운로드 - 리액트 quill editor 이미지 추가
			else if("imageDownload".equals(upmu[1])) {
				obj = controller.imageDownload(req, res);
				logger.info("imageDownload 호출 => " + 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 zipcodeList(HttpServletRequest req, HttpServletResponse res);
	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 imageDownload(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 우편번호 - CommonController.java>

package com.pojo.step3;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;

import com.util.HashMapBinder;

public class CommonController implements Controller3 {
	Logger logger = Logger.getLogger(CommonController.class);
	private CommonLogic commonLogic = new CommonLogic();
	
	@Override
	public ModelAndView zipcodeList(HttpServletRequest req, HttpServletResponse res) {
		logger.info("zipcodeList 호출");
		List<Map<String, Object>> zList = null;
		// 사용자가 조건 검색을 원하는 경우 - 조건 값을 전달할 객체 생성함
		// MyBatis에서는 동적쿼리를 지원하므로 하나로 2가지 경우 사용 가능함
		Map<String, Object> pMap = new HashMap<>();
		HashMapBinder hmb = new HashMapBinder(req);
		hmb.bind(pMap);
		zList = commonLogic.zipcodeList(pMap);
		ModelAndView mav = new ModelAndView(req);
		mav.setViewName("common/zipcodeList");
		mav.addObject("zList", zList);
		return mav; // 리턴이 mav면 webapp/WEB-INF/views/common 아래의 jsp
	}
	
	@Override
	public Object jsonBoardList(HttpServletRequest req, HttpServletResponse res) {
		return null;
	}

	@Override
	public Object boardList(HttpServletRequest req, HttpServletResponse res) {
		return null;
	}

	@Override
	public Object boardDetail(HttpServletRequest req, HttpServletResponse res) {
		return null;
	}

	@Override
	public Object imageUpload(HttpServletRequest req, HttpServletResponse res) {
		return null;
	}

	@Override
	public Object imageDownload(HttpServletRequest req, HttpServletResponse res) {
		return null;
	}

	@Override
	public Object imageGet(HttpServletRequest req, HttpServletResponse res) {
		return null;
	}

	@Override
	public Object boardInsert(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
		return null;
	}

	@Override
	public Object boardUpdate(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
		return null;
	}
    
	@Override
	public Object boardDelete(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
		return null;
	}
}

 

<pojo3 우편번호 - CommonLogic.java>

package com.pojo.step3;

import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

// @Service -> 스프링일경우
public class CommonLogic {
	Logger logger = Logger.getLogger(CommonLogic.class);
	private CommonDao commonDao = new CommonDao();

	// @Autowired -> 스프링일경우
	public List<Map<String, Object>> zipcodeList(Map<String, Object> pMap) {
		logger.info("zipcodeList 호출" + pMap);
		List<Map<String, Object>> zList = null;
		zList = commonDao.zipcodeList(pMap);
		return zList;
	}
}

 

<pojo3 우편번호 - CommonDao.java>

package com.pojo.step3;

import java.util.List;
import java.util.Map;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.log4j.Logger;

import com.util.MyBatisCommonFactory;

/*
 * 스프링일경우
 * @Service
 * @Repository
 */
public class CommonDao {
	Logger logger = Logger.getLogger(CommonDao.class);
	MyBatisCommonFactory mcf = new MyBatisCommonFactory();
	
	public List<Map<String, Object>> zipcodeList(Map<String, Object> pMap) {
		logger.info("zipcodeList 호출");
		List<Map<String, Object>> zList = null;
		SqlSessionFactory sqlSessionFactory = null;
		SqlSession sqlSession = null;
		try {
			sqlSessionFactory = mcf.getSqlSessionFactory();
			sqlSession = sqlSessionFactory.openSession();
			zList = sqlSession.selectList("zipcodeList", pMap);
			logger.info(zList);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return zList;
	}
}

 

<pojo3 우편번호 - common.xml>

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mybatis.mapper.CommonMapper">
	<!-- 우편번호 검색 -->
	<select id="zipcodeList" parameterType="map" resultType="map">
		SELECT zipcode, address FROM zipcode_t
		WHERE dong LIKE '%'||#{dong}||'%'
	</select>
</mapper>

 

<pojo3 우편번호 - MyBatisConfig.xml>

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="oracle.jdbc.OracleDriver" />
				<property name="url"
					value="jdbc:oracle:thin:@localhost:1521:orcl11" />
				<property name="username" value="scott" />
				<property name="password" value="tiger" />
			</dataSource>
		</environment>
	</environments>
	<mappers>
		<!-- 업무에따라 관리할 쿼리문을 담을 xml문서의 물리적인 위치와 파일명을 등록할것 -->
		<mapper resource="com/mybatis/mapper/test.xml" />
		<mapper resource="com/mybatis/mapper/member.xml" />
		<mapper resource="com/mybatis/mapper/board.xml" />
		<mapper resource="com/mybatis/mapper/common.xml" />
		<mapper resource="com/util/book.xml" />
		<mapper resource="com/util/member2.xml" />
	</mappers>
</configuration>

 

<pojo3 우편번호 - zipcodeList.jsp>

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@page import="java.util.*"%>
<%
	List<Map<String,Object>> zList = 
	(List<Map<String,Object>>)request.getAttribute("zList");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>우편번호 검색기</title>
</head>
<body>
<h3>우편번호 검색기</h3>
<%
if(zList != null) {
	for(int i=0; i<zList.size(); i++){
		out.print(zList.get(i) + "<br />");
	}	
}
%>
</body>
</html>

 

 

<pojo3 우편번호 - memberMgr.jsp>

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원관리 시스템</title>
<%@ include file="../common/easyUI_common.jsp" %>
</head>
<body>
<script>
	$(document).ready(function(){
		$("#btn_search").linkbutton({
			onClick:function(){
				const u_dong = $("#_easyui_textbox_input1").val();
				if(u_dong == null || u_dong.length < 1) {
					alert("검색할 동을 입력하세요");
					// 동 정보가 없으면 처음부터 새로 시작해야하니 return함 
					return;
				} else {
					console.log('사용자가 입력한 동이름 => ' + u_dong);
					// 오라클 서버를 경유해서 조회된 결과를 datagrid에 출력해보기
				}
				location.href = "./zipcodeList.st3?dong="+u_dong;
			}
		}) // end of 찾기버튼
		$('#dong').textbox('textbox').bind('keydown', function(e){
			const u_dong = $("#_easyui_textbox_input1").val();
			if(e.keyCode == 13){
				alert('사용자가 입력한 동이름은 ' + u_dong);
			}
		})
	})
</script>
<!--======================= 우편번호 검색기 =======================-->
<div id="dlg_zipcode" style="width:100%;max-width:600px;padding:30px 30px;">
	<input class="easyui-textbox" id="dong" name="dong" labelPosition="top" data-options="prompt:'동이름 이나 주소정보 입력...'" style="width:210px;">
	<a id="btn_search" href="#" class="easyui-linkbutton" data-options="iconCls:'icon-search'">찾기</a>
	<div  style="margin-bottom:10px;"></div>
	<table id="dg_zipcode">
	</table>
</div>
<!--======================= 우편번호 검색기 =======================-->
</body>
</html>

댓글