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

국비 지원 개발자 과정_Day67

by 루팽 2023. 3. 6.

게시판 구현을 위한 MVC패턴 설계

1. 리액트를 고려한 설계인가?

2. JSON포맷을 지원하는 메소드가 추가되었는가?

3. 파일 업로드와 관련된 공통코드나 라이브러리 선택이 되었는가?

4. 유지보수에 편리하게 설계되었는가?

5. MyBatis와 같은 ORM 솔루션들이 바뀌더라도 전체 설계 틀이 변하지 않도록, 부분적 조립이 가능한 설계인가?

6. UI솔루션이 변경되더라도 클래스 설계에 영향이 가지 않도록 설계가 되었는가?

 

 

<pojo step3 - 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 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 step3 - ModelAndView.java>

package com.pojo.step3;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.Logger;

/*
 * spring4에서 제공되던 ModelAndView 흉내내기
 * ModelAndView
 * 1) scope가 request이다
 * 2) 화면 이름을 정해준다
 */
public class ModelAndView {
	Logger logger = Logger.getLogger(ModelAndView.class);
	HttpServletRequest req = null;
	// 캡슐화 코드는 반드시 getter/setter가 필요하다 -> 스프링부트는 Lombok대체
	private String viewName = null;
	List<Map<String, Object>> reqList = new ArrayList<>();
	
	public ModelAndView() {
	}

	public ModelAndView(HttpServletRequest req) {
		this.req = req;
	}

	public void addObject(String name, Object obj) {
		req.setAttribute(name, obj);
		Map<String, Object> pMap = new HashMap<>();
		pMap.put(name, obj);
		reqList.add(pMap);
	}
	
	public String getViewName() {
		return viewName;
	}

	public void setViewName(String viewName) {
		this.viewName = viewName;
	}
}

 

<pojo step3 - 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;
				}
			}
			
			// 글 수정 - 첨부파일 수정 유무 고려하기
			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 step3 - HashMapBinder.java>

package com.util;

import java.util.Enumeration;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;

// 스프링부트에선 Model, ModelMap
public class HashMapBinder {
	HttpServletRequest req = null;
	
	public HashMapBinder() {
	}
	
	public HashMapBinder(HttpServletRequest req) {
		this.req = req;
	}
	
	// 어떤 페이지 어떤 상황에서 공통코드를 재사용 가능하게 할 것인가?
	// 각 업무별 요청 클래스에서 주입받을 객체를 정해서 메소드의 파라미터로 전달받음
	// 전달받은 주소번지 원본에 값을 담아준다
	public void bind(Map<String, Object>pMap) {
		pMap.clear();
		Enumeration<String> en = req.getParameterNames();
		while(en.hasMoreElements()) {
			String key = en.nextElement();
			pMap.put(key, req.getParameter(key));
		}
	}
}

 

<pojo step3 - ActionSupport.java>

package com.pojo.step3;

import java.io.IOException;
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); // /board/boardList.st2가 찍힘

		String context = req.getContextPath();
		logger.info(context); // "/" -> server.xml에 들어있음

		String command = uri.substring(context.length() + 1);
		System.out.println(command); // board/boardList.st2

		int end = command.lastIndexOf(".");
		System.out.println(end); // 15(board의 경우)

		command = command.substring(0, end);
		System.out.println(command); // board/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인 경우
			if (obj instanceof String) {
				// obj에 :이 포함된 경우
				if (((String) obj).contains(":")) {
					logger.info(": 포함되어 있음");
					pageMove = obj.toString().split(":");
				}
				// obj에 :이 포함되지 않은 경우
				else {
					logger.info(": 포함되어 있지 않음");
					pageMove = obj.toString().split("/");
				}
				logger.info("페이지이동 => " + pageMove[0] + ", " + pageMove[1]);
			}
			// obj가 ModelAndView인 경우
			else if (obj instanceof ModelAndView) {
				mav = (ModelAndView) obj;
				pageMove = new String[2];
				pageMove[0] = "forward";
				pageMove[1] = mav.getViewName();
			}
			
			// pageMove가 null이 아닐 경우 각 방식으로 페이지 이동처리
			if (pageMove != null) {
				// pageMove[0] = redirect or forward
				// pageMove[1] = XXX.jsp
				String path = pageMove[1];
				// pageMove[0]이 redirect
				if ("redirect".equals(pageMove[0])) {
					res.sendRedirect(path);
				}
				// pageMove[0]이 forward
				else if ("forward".equals(pageMove[0])) {
					RequestDispatcher view = req.getRequestDispatcher("/" + path + ".jsp");
					view.forward(req, res);
				}
				// 그외 forward 처리
				else {
					path = pageMove[0] + "/" + pageMove[1];
					RequestDispatcher view = req.getRequestDispatcher("/WEB-INF/view" + path + ".jsp");
					view.forward(req, res);
				}
			}
		} // 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);
	}
}

 

<pojo step3 - Board3Controller.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 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<>();
        bList =boardLogic.boardList(pMap);
        req.setAttribute("bList", bList);
        return "forward:jsonBoardList.jsp"; // 리턴이 String이면 webapp/board3 아래의 jsp
    }

    @Override
    public Object boardDetail(HttpServletRequest req, HttpServletResponse res) {
        logger.info("boardDetail호출");
        List<Map<String, Object>> bList = null;
        //전체 조회에 대한 sql문 재사용 가능함 - 1건 조회 경우
        Map<String, Object> pMap = new HashMap<>();
        HashMapBinder hmb = new HashMapBinder(req);
        hmb.bind(pMap);
        bList =boardLogic.boardList(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;
        Map<String, Object> pMap = new HashMap<>();
        HashMapBinder hmb = new HashMapBinder(req);
        hmb.bind(pMap);
        result = boardLogic.boardInsert(pMap);
        String path = "";
         if(result==1) {
             path = "redirect:/board3/boardList.st3";
        } else {
            path = "boardInsertFail.jsp";
            res.sendRedirect(path);
        }
        return path;
    }

    @Override
    public Object boardUpdate(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        logger.info("boardUpdate호출");
        int result = 0;

        if(result==1) {
            res.sendRedirect("boardInsertSuccess.jsp");
            return null;
        }
        return "redirect:/board3/boardList.st3";
    }

	@Override
	public Object boardDelete(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
		logger.info("boardDelete호출");
		int result = 0;		
		
		if(result==1) {
			res.sendRedirect("boardInsertSuccess.jsp");
			return null;
		}
		return "redirect:/board3/boardList.st3";
	}
}

 

<pojo step3 - Board3Logic.java>

package com.pojo.step3;

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

import org.apache.log4j.Logger;

public class Board3Logic {
	Logger logger = Logger.getLogger(Board3Logic.class);
	Board3Dao boardDao = new Board3Dao();
	
	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;
	}

	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);
		return result;
	}
}

 

<pojo step3 - Board3Dao.java>

package com.pojo.step3;

import java.util.HashMap;
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;

public class Board3Dao {
	Logger logger = Logger.getLogger(Board3Dao.class);
	MyBatisCommonFactory mcf = new MyBatisCommonFactory();

	public List<Map<String, Object>> boardList(Map<String, Object> pMap) {
		logger.info("boardList 호출");
		List<Map<String, Object>> bList = null;
		SqlSessionFactory sqlSessionFactory = null;
		SqlSession sqlSession = null;
		try {
			sqlSessionFactory = mcf.getSqlSessionFactory();
			sqlSession = sqlSessionFactory.openSession();
			bList = sqlSession.selectList("boardList", pMap);
			logger.info(bList);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return bList;
	}

	public int boardInsert(Map<String, Object> pMap) {
		logger.info("boardInsert 호출");
		int result = 0;
		SqlSessionFactory sqlSessionFactory = null;
		SqlSession sqlSession = null;
		try {
			sqlSessionFactory = mcf.getSqlSessionFactory();
			sqlSession = sqlSessionFactory.openSession();
			// insert이지만 update로 하는 이유는 리턴타입이 Object이기 때문이다
			// 메소드 이름은 상관없이 해당 쿼리문을 id로 찾기 때문이다
			result = sqlSession.update("boardMInsert", pMap);
			if(result == 1) {
				sqlSession.commit();
			}
			logger.info(result);
		} catch (Exception e) {
			e.printStackTrace();
		}		
		return result;
	}
	
	public int getBGroup() {
		int result = 0;
		logger.info("getBGroup 호출");
		SqlSessionFactory sqlSessionFactory = null;
		SqlSession sqlSession = null;
		try {
			sqlSessionFactory = mcf.getSqlSessionFactory();
			sqlSession = sqlSessionFactory.openSession();
			result = sqlSession.selectOne("getBGroup", "");
			logger.info(result); // 채번한 글그룹번호
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}
	
	public int getBNo() {
		int result = 0;
		logger.info("getBNo 호출");
		SqlSessionFactory sqlSessionFactory = null;
		SqlSession sqlSession = null;
		try {
			sqlSessionFactory = mcf.getSqlSessionFactory();
			sqlSession = sqlSessionFactory.openSession();
			result = sqlSession.selectOne("getBNo", "");
			logger.info(result); // 채번한 글번호
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

	public void bStepUpdate(Map<String, Object> pMap) {
		logger.info("bStepUpdate 호출");
		int result = 0;
		SqlSessionFactory sqlSessionFactory = null;
		SqlSession sqlSession = null;
		try {
			sqlSessionFactory = mcf.getSqlSessionFactory();
			sqlSession = sqlSessionFactory.openSession();
			result = sqlSession.update("bStepUpdate", pMap);
			if(result == 1) {
				sqlSession.commit();
			}
			logger.info(result);
		} catch (Exception e) {
			e.printStackTrace();
		}
	} // end of bStepUpdate
}

 

<pojo step3 - board.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.BoardMapper">
	<!-- 게시글목록 가져오는 쿼리문 -->
	<select id="boardList" parameterType="java.util.HashMap"
		resultType="map">
		SELECT bm.bm_no,
		       bm.bm_title,
		       bm.bm_writer,
		       bs.bs_file,
		       bm.bm_hit,
		       bm.bm_group,		       
		       bm.bm_pos,
		       bm.bm_step,
		       bm.bm_reg
		       FROM board_master_t bm, board_sub_t bs
		       WHERE bm.bm_no =
		       bs.bm_no(+)
		<!-- 글번호로 검색 -->
		<if test= 'bm_no  > 0' >
		 		AND bm.bm_no = #{bm_no}
		</if>
		<!-- 글날짜로 검색 -->
		<if test= 'bm_reg!=null and bm_reg.length()!=0 and bm_reg!="undefined"' >
		 		AND bm_reg LIKE  '%'||#{bm_reg}||'%'
		 </if>
	  <!-- 글제목으로 검색 -->
		<if test= 'cb_search!=null and cb_search.length()>0 and cb_search.equals("bm_title")' >
		 		AND bm_title LIKE '%'||#{tb_search}||'%'
		</if>
	  <!-- 글내용으로 검색 -->
		<if test= 'cb_search!=null and cb_search.equals("bm_content")' >
		 		AND bm_content LIKE '%'||#{tb_search}||'%'
		</if>
	  <!-- 작성자로 검색 -->
		<if test= 'cb_search!=null and cb_search.equals("bm_writer")' >
		 		AND bm_writer LIKE '%'||#{tb_search}||'%'
		</if>
		ORDER BY bm.bm_group DESC, bm.bm_step ASC
	</select>
  
	<!-- board Master에 인서트 -->
	<!-- pos와 step은 원글이면 0, 댓글이면 read.jsp에서 가진 값에 1을 더한 값으로 결정됨
			 조회수는 0(새글이니까), 날짜는 시스템 날짜정보로 등록 -->
	<insert id="boardMInsert" parameterType="map">
		INSERT INTO board_master_t (bm_no,
                            bm_title,
                            bm_writer,
                            bm_content,
                            bm_pw,
                            bm_reg,
                            bm_hit,
                            bm_group,
                            bm_pos,
                            bm_step)
     VALUES (#{bm_no}, #{bm_title}, #{bm_writer},
     #{bm_content}, #{bm_pw}, to_char(sysdate, 'YYYY-MM-DD'), 0,
     #{bm_group}, #{bm_pos}, #{bm_step})
	</insert>
	
		<!-- board Sub에 인서트 -->
		<!-- 첨부파일을 추가하는 경우에만 실행됨, 없으면 해당없음
				 글번호는 새글쓰기에서 결정된 값이 대입되어야함, 새로채번하면 안됨! -->
	<insert id="boardSInsert" parameterType="map">
		INSERT INTO
		board_sub_t(bm_no, bs_seq, bs_file, bs_size)
		VALUES(#{bm_no}, 1, #{bs_file}, #{bs_size})
	</insert>
		
		<!-- board Master에 Step 업데이트 -->
		<!-- 내가 쓰는 댓글 뒤에 댓글이 존재하는 경우만 실행됨
				 조건절에 들어오는 그룹번호와 step은 상세보기 화면에서 가져온 값이 대입됨 -->
	<update id="bStepUpdate" parameterType="map">
		UPDATE board_master_t
		SET bm_step = bm_step + 1
		WHERE bm_group = #{bm_group}
		AND bm_step > #{bm_step}
	</update>
	
	<!-- bm_no 채번 -->
	<select id="getBNo" parameterType="string" resultType="int">
	SELECT
    NVL ( (SELECT /*+index_desc(board_master_t BOARD_BM_NO_PK) */ bm_no
        FROM board_master_t
        WHERE ROWNUM = 1), 0)+1 bm_no
  FROM DUAL
	</select>
	
	<!-- bm_group 채번 -->
	<select id="getBGroup" parameterType="string" resultType="int">
	SELECT
	    NVL ( (SELECT /*+index_desc(board_master_t i_board_group) */ bm_group
	        FROM board_master_t
	        WHERE ROWNUM = 1
	        AND bm_group > 0), 0)+1 bm_group
	  FROM DUAL
	</select>
</mapper>

 

<pojo step3 - boardList.jsp - webapp에 위치>

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="java.util.*" %>    
<%
// 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();
	}		
%>    
<!DOCTYPE html>
<html>
<head>
<!-- <meta charset="UTF-8"> 이것때문에 한글깨짐.-->
<title>MVC기반의 계층형 게시판 구현하기[webapp]</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){
		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=0;i<size;i++){
			if(size == i) break;
			Map<String,Object> rMap = boardList.get(i);
%>	      
        	<tr>
        		<td><%=1%></td>
        		<td>
<!-- 너 댓글이니? -->     	
<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>
        		<%="첨부파일 없음"%>	
        		</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;">
		1 2 3 4 5 6 7 8 9 10
	</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.pj"> -->
        <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>

 

<pojo step3 - boardInsertSuccess.jsp>

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<script>
	alert("입력|수정|삭제 성공하였습니다.");
	// response.sendRedirect("./boardList.st3");
</script>

 

<pojo step3 - boardDetail.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>> getBoardList = 
		(List<Map<String,Object>>)request.getAttribute("bList");
	String bm_title = null;
	String bm_writer = null;
	String bm_content = null;
	String bm_pw = null;
	String bm_no = null;
	String bm_group = null;
	String bm_pos = null;
	String bm_step = null;
	if(getBoardList!=null && getBoardList.size()>0){
		bm_title = getBoardList.get(0).get("BM_TITLE").toString();
		bm_writer = getBoardList.get(0).get("BM_WRITER").toString();
		//bm_content = getBoardList.get(0).get("BM_CONTENT").toString();
		//bm_pw = getBoardList.get(0).get("BM_PW").toString();
		bm_no = getBoardList.get(0).get("BM_NO").toString();
		bm_group = getBoardList.get(0).get("BM_GROUP").toString();
		bm_pos = getBoardList.get(0).get("BM_POS").toString();
		bm_step = getBoardList.get(0).get("BM_STEP").toString();
	}
	
%>    
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>글상세보기</title>
<!-- 공통 코드 include처리 -->
<%@ include file="../common/easyUI_common.jsp" %>
<script type="text/javascript">
	function addAction(){
		$("#f_boardAdd").attr("method","post");
		$("#f_boardAdd").attr("action","/board/boardList.mvc?crud=ins");
		$("#f_boardAdd").submit();
		//부모창에 함수를 호출할때 opener.함수명();
		//opener.boardList();
		//self.close();
	}
	function updateForm(){
		//alert("updateForm 호출 성공");
		$('#d_boardUpd').dialog({
			title:'글수정'
		  ,width:720
		  ,height:450
		  ,closed:false
		  ,cache:false
		 ,modal:true	  
		});
		//$('#d_boardUpd').dialog('open');	
		//$('#d_boardUpd').dialog('refresh', '');
	}
	//댓글쓰기
	function repleForm(){
		$("#dlg_boardAdd").dialog('open');
	}
	//글삭제하기 이벤트 처리
	function boardDelView(){
		alert("boardDelView호출 성공");
		  $('#d_boardDel').dialog({
			    title: '글삭제',
			    buttons: btn_boardDel,
			    width: 420,
			    height: 250,
			    closed: true,
			    cache: false,
			    //href: 'boardDelForm.jsp?bm_no=<%=bm_no%>&bm_pw=<%=bm_pw%>',
			    modal: true
	   }); 
	   $('#d_boardDel').dialog('open');		
	}
	//글삭제 화면에서 확인 버튼을 클릭했을 때
	function boardDel(){
		var db_pw = <%=bm_pw%>
		var u_pw = $("#u_pw").textbox('getValue');
		//alert("db_pw:"+db_pw+", u_pw:"+u_pw);
		//alert("사용자가 입력한 비번:"+$("#db_pw").textbox('getValue'));
		//사용자가 입력한 비번과 DB에서 읽어온 비번을 비교하여
		//일치하면 삭제 처리 진행하고
		//불일치하면 비번을 다시 입력받도록 해주세요.
		if(u_pw==db_pw){
			//alert("같다");
			$.messager.confirm('Confirm','정말 삭제하시겠습니까?',function(r){
			 //r:true-ok, false-cancel
				if (r){//자바스크립트는 0이면 false 나머지 true
			    	location.href="./boardList.mvc?crud=del&bm_no=<%=bm_no%>&bs_file=파일명";    
			    }
			});
		}else{
			$("#db_pw").textbox('setValue','');
		}
	}
	function boardDelClose(){
		 $('#d_boardDel').dialog('close');
	}
	function boardList(){
		location.href="/board/boardList.jsp";
	}
</script>
</head>
<body>
<script type="text/javascript">
	$(document).ready(function(){
		
	});
</script>
    <table align="center" id="p" class="easyui-panel" title="글상세보기" data-options="footer:'#tb_read'"
        style="width:670px;height:380px;padding:10px;background:#fafafa;">
	    	<tr>
	    	<td>제목</td>
	    	<td><input id="bm_title" value="<%=bm_title %>" name="bm_title" data-options="width:'450px'" class="easyui-textbox"></td>
	    	</tr>
	    	<tr>
	    	<td>작성자</td>
	    	<td><input id="bm_writer" value="<%=bm_writer %>" name="bm_writer" class="easyui-textbox"></td>
	    	</tr>
	    	<tr>
	    	<td>내용</td>
	    	<td><input id="bm_content" value="<%="내용" %>" name="bm_content" data-options="multiline:'true', width:'570px', height:'90px'" class="easyui-textbox"></td>
	    	</tr>
	    	<tr>
	    	<td>비밀번호</td>
	    	<td><input id="bm_pw" value="<%=bm_pw %>" name="bm_pw" class="easyui-passwordbox"></td>
	    	</tr>	    	
	   </table>
	 <div id="tb_read" style="padding:2px 5px;" align="center">
	    <a href="javascript:repleForm()" class="easyui-linkbutton" iconCls="icon-edit" plain="true">댓글쓰기</a>
	    <a href="javascript:updateForm()" class="easyui-linkbutton" iconCls="icon-add" plain="true">수정</a>
	    <a href="javascript:boardDelView()" class="easyui-linkbutton" iconCls="icon-remove" plain="true">삭제</a>
	    <a href="javascript:boardList()" class="easyui-linkbutton" iconCls="icon-search" plain="true">목록</a>
	</div>
		<!-- 글삭제 시작 -->
		<div id="d_boardDel" closed="true" class="easyui-dialog" style="padding:20px 50px">
			<div id="btn_boardDel" align="right">
			<a href="javascript:boardDel()" class="easyui-linkbutton" iconCls="icon-ok" style="width:90px">확인</a>
			<a href="javascript:boardDelClose()" class="easyui-linkbutton" iconCls="icon-cancel" style="width:90px">닫기</a>
			</div>
		</div>
		<!-- 글삭제  끝   -->
		<!-- 글수정 시작 -->
		<div id="d_boardUpd" closed="true" class="easyui-dialog" style="padding:20px 50px">
<form id="uf_board" method="post" enctype="multipart/form-data">
<input type="hidden" id="bm_no" name="bm_no" value="<%=0 %>">
<input type="hidden" id="bm_seq" name="bm_seq" value="<%=1 %>">
<input type="hidden" id="old_file" name="old_file" value="이전파일명">
<table align="center" width="650px" height="280px">
	<tr>
		<td width="120px">글제목</td>
		<td width="580px">
			<input id="b_title" value="글제목" name="b_title" class="easyui-textbox">
		</td>
	</tr>
	<tr>
		<td width="120px">작성자</td>
		<td width="580px">
			<input id="b_writer" value="작성자" name="b_writer" class="easyui-textbox">
		</td>
	</tr>	
	<tr>
		<td width="120px">내용</td>
		<td width="580px">
			<input id="b_content" multiline="true" value="내용" name="b_content" class="easyui-textbox" style="width:100%;height:100px">
		</td>
	</tr>	
	<tr>
		<td width="120px">첨부파일</td>
		<td width="580px">
			<input id="b_file" name="b_file" class="easyui-filebox" style="width:100%">
		</td>
	</tr>	
	<tr>
		<td width="120px">비번</td>
		<td width="580px">
			<input id="b_pw" name="b_pw" class="easyui-textbox" style="width:100px">
		</td>
	</tr>	
</table>
</form>		
			<div id="btn_boardUpd" align="right">
			<a href="javascript:boardUpd()" class="easyui-linkbutton" iconCls="icon-ok" style="width:90px">등록</a>
			<a href="javascript:boardUpdClose()" class="easyui-linkbutton" iconCls="icon-cancel" style="width:90px">닫기</a>
			</div>
		</div>
		<!-- 글수정  끝  -->
		<!-- 댓글쓰기 시작 -->
<!--================== [[댓글쓰기 화면]] ==================-->
<div id="dlg_boardAdd" title="댓글쓰기" class="easyui-dialog" style="width:600px;height:400px;padding:10px" data-options="closed:'true',modal:'true',footer:'#tbar_boardAdd'">	
<!-- 
form전송시 encType옵션이 추가되면 request객체로 사용자가 입력한 값을 꺼낼 수 없다.
MultipartRequest  => cos.jar
 -->	
	<form id="f_boardAdd" method="post" enctype="multipart/form-data">
	<input type="hidden" name="bm_no" value="0">
	<input type="hidden" name="bm_group" value="0">
	<input type="hidden" name="bm_pos" value="0">
	<input type="hidden" name="bm_step" value="0">
	<!-- <form id="f_boardAdd"> -->
	<table>
		<tr>
			<td width="100px">제목</td>
			<td width="500px">
				<input class="easyui-textbox" data-options="width:'350px'" id="bm_title" name="bm_title" required>
			</td>
		</tr>
		<tr>	
			<td width="100px">작성자</td>
			<td width="500px">
				<input class="easyui-textbox" data-options="width:'150px'" id="bm_writer" name="bm_writer" required>
			</td>
		</tr>
		<tr>
			<td width="100px">이메일</td>
			<td width="500px">
				<input class="easyui-textbox" data-options="width:'250px'" id="bm_email" name="bm_email">
			</td>
		</tr>
		<tr>			
			<td width="100px">내용</td>
			<td width="500px">
				<input class="easyui-textbox" id="bm_content" name="bm_content" data-options="multiline:'true',width:'400px',height:'90px'" required>
			</td>
		</tr>
		<tr>			
			<td width="100px">비번</td>
			<td width="500px">
				<input class="easyui-textbox" data-options="width:'100px'" id="bm_pw" name="bm_pw" required>
			</td>
		</tr>
	</table>
	</form>
</div>
<!-- 입력 화면 버튼 추가 -->
<div id="tbar_boardAdd" align="right">
	<a href="javascript:addAction()" class="easyui-linkbutton" iconCls="icon-save">저장</a>
	<a href="javascript:$('#dlg_boardAdd').dialog('close')" 
	   class="easyui-linkbutton" iconCls="icon-cancel">닫기</a>
</div>
		<!-- 댓글쓰기  끝  -->	    
</body>
</html>

 

boardList.jsp → 새글쓰기 → 글번호 채번, 그룹번호 채번

boardDetail.jsp → 댓글쓰기(1건 조회된 상태) → 글번호 채번, 그룹번호 그대로

글번호 → 채번

글제목

글내용

작성자

비번

첨부파일 → 배제

boardInsert(req, res): String → jsp - action - acion - jsp

boardInsert(Map):int

 

클라이언트 →전송→ 서버 방법

1. form 전송 → Restful API

2. ajax → XXX.st3?bm_no=5&bm_title=제목

3. location.href=”XXX.jsp?bm_no=5”

4. 리액트 fetch - 비동기처리

5. 리액트 axios - 비동기처리 - 모듈화 - NodeJS기반 - import(브라우저/require)

 

<form method=”get|post” action=”XXX.st3”>

pojo1 → 서블릿 FrontMVC1  /인터페이스 Action - 리턴타입 ActionForward

pojo2서블릿 ActionServlet / 인터페이스 Controller- 리턴타입 String

pojo3서블릿 ActionSupport / 인터페이스 Controller3 - 리턴타입 Object

 

첨부파일은 post로 해야함

저장버튼 누르면 제목, 작성자, 내용, 비번 전달

새글쓰기 / 댓글쓰기 구분 → 새글은 목록에, 댓글은 상세보기에 존재(상세보기엔 글번호가 있으니 해당 글의 댓글은 그 번호 반영함)

댓글