Jsp & Pojo : 2-1

지환·2023년 12월 13일
0

Jsp & Servlet

목록 보기
19/21
post-thumbnail

HashMapBinder

우리가 getAttribute & setAttribute를 해서 반복되는 코드를 HashMapBinder로 공통코드로 만들었음.

HashMapBinder로 Post방식으로 나머지 req로 값들을 받아오기 위해서 (사진) 추가했다.

[원래코드]

public class HashMapBinder {
	Logger logger = LoggerFactory.getLogger(HashMapBinder.class);
	HttpServletRequest req = null;
	
	HashMapBinder(HttpServletRequest req)
	{
		this.req = req;
	}
	
	public void bind(Map<String,Object> pMap)
	{
		pMap.clear(); // map을 비워주는 역할
		// <input type ="text" name = "n_title"/>
		//  <input type ="text" name = "n_content"/>
		//  <input type ="text" name = "n_writer"/>
		Enumeration<String> em = req.getParameterNames();

			String key = em.nextElement();
			pMap.put(key,req.getParameter(key));
			
			
		}
	}

}

이미지 처리 시 HashMapBinder로 Post방식으로 나머지 req로 값들을 받아오기 위해서 (사진) 추가했다.

[수정후]

public class HashMapBinder {
	Logger logger = Logger.getLogger(HashMapBinder.class);
	HttpServletRequest req = null;
	
	MultipartRequest multi  = null;
	String realFolder = "C:\\Program Files\\workspace_jsp\\nae2Gym\\src\\main\\webapp\\pds";
	String encType = "utf-8";
	int maxSize = 5*1024*1024;
	
	
	//우리가 getAttribute & setAttribute를 해서 반복되는 코드를 HashMapBinder로 공통코드로 만들었음.
	
	//왜 req인가? -> 생각해볼 필요가 있다.
	
	// public void multiBinder(Map<String,Object> pMap) 파라미터에있는 주소번지는 어디서 결정되나요?
	// boardController 에서 주입 받는다.(이 공통코드를 클래스를 주입받는다.
	public void multiBinder(Map<String,Object> pMap)
	{
		pMap.clear();//기존의 들어있는 정보는 비운다.
		try {
			multi = new MultipartRequest(req, realFolder, maxSize,  encType, new DefaultFileRenamePolicy());
		}
		catch(Exception e) {
			logger.info(e.toString());
		}
		
		
		//이미지 처리말고 Post에서 첨부파일에 있는 포스트 방식일 때 사용하는 메소드
		//첨부파일이 아닌 다른 정보에 대해서 받아준다.
		Enumeration<String> em = req.getParameterNames();
		while(em.hasMoreElements()) {
			//키값 꺼내기
			String key = em.nextElement();//n_title, n_content, n_writer
			pMap.put(key, req.getParameter(key));
		}////////////// end of while
		
		
		// 첨부파일에 대한 처리를 말함.
		Enumeration<String> files = multi.getFileNames();
		String fullPah = null;//파일 정보에 대한 전체경로
		String filename = null;//파일이름
		//첨부파일이 있다면?
		if(files !=null) {
			//파일 이름을 클래스로 정의하는 객체 - 파일객체가 생성되었다고 해서 그 안에 내용까지 포함하진 않음
			//파일 크기를 계산해주는 메소드 지원함
			File file = null;
			while(files.hasMoreElements()) {
				String fname = files.nextElement();
				filename = multi.getFilesystemName(fname);
				pMap.put("bs_file", filename);//avartar.png
				//File객체 생성하기
				file = new File(realFolder+"\\"+filename);
			}
			//첨부파일의 크기를 담기
			double size = 0;
			if(file !=null) {
				size = file.length();
				size = size/(1024);
				pMap.put("bs_size", size);
			}
		}
		
	}
	
	
	public HashMapBinder(HttpServletRequest req) {
		this.req = req;
	}

	public void bind(Map<String,Object> pMap) {
		pMap.clear();
		//<input type="text" name="n_title">
		//<input type="text" name="n_content">
		//<input type="text" name="n_writer">
		Enumeration<String> em = req.getParameterNames();
		
		
		while(em.hasMoreElements()) {
			//키값 꺼내기
			String key = em.nextElement();//n_title, n_content, n_writer
			pMap.put(key, req.getParameter(key));
		}////////////// end of while
		

}

컨트롤러 비교

noticeList

[원래코드]


		if("noticeList".equals(upmu[1])) {//select
			logger.info("noticeList");
			List<Map<String ,Object>> nList = null;//nList.size()가 n개 
			// NoticeLogic의 메소드 호출 - 객체주입 - 내가(책임) 아님 스프링(제어역전)
			hmb.bind(pMap);  
			nList = nLogic.noticeList(pMap);
			//원본에다가 담아 두자
			req.setAttribute("nList", nList);
			path.append("noticeList.jsp");
			isRedirect = false;//false이면 forward처리됨
		}
        
        else if("noticeDetail".equals(upmu[1])) {//select
			logger.info("noticeDetail");
			List<Map<String ,Object>> nList = null;//nList.size()=1
			// NoticeLogic의 메소드 호출 - 객체주입 - 내가(책임) 아님 스프링(제어역전)
			//select * from notice where n_no=5;
			hmb.bind(pMap);
			nList = nLogic.noticeList(pMap);
			//원본에다가 담아 두자
			req.setAttribute("nList", nList);
			path.append("noticeDetail.jsp");
			isRedirect = false;//false이면 forward처리됨
		}

[수정코드]


if("boardList".equals(upmu[1])) {//select - 1-3버전에서는 이 장면을 메소드 단위로 변경하고 싶다(req, res)넘겨 받을 수 있어야 한다. -문제 해결못하니까
			logger.info("boardList");
			List<Map<String ,Object>> bList = null;//nList.size()가 n개 
			// NoticeLogic의 메소드 호출 - 객체주입 - 내가(책임) 아님 스프링(제어역전)
			hmb.bind(pMap);  
			bList = bLogic.boardList(pMap);      
			//원본에다가 담아 두자
			req.setAttribute("bList", bList);
			//pageMove[0]=forward, pageMove[1]=/board/boardList.jsp
			path = "forward:board/boardList"; 
		}//end of 목록조회
		//상세조회일때 - select - 1건 - Map or VO괜찮아 - read.jsp
		else if("boardDetail".equals(upmu[1])) {
			logger.info("boardDetail");
			List<Map<String ,Object>> bList = null;//nList.size()=1
			// NoticeLogic의 메소드 호출 - 객체주입 - 내가(책임) 아님 스프링(제어역전)
			//select * from notice where n_no=5;
			hmb.bind(pMap);
			bList = bLogic.boardList(pMap);
			//원본에다가 담아 두자
			req.setAttribute("bList", bList);
			path="forward:board/boardDetail";
		}
		
  • path.append("noticeDetail.jsp");
  • isRedirect = false; 이 부분들이 하나로 통일됐음

상세보기와 전체보기는 나누어야 하나? 아니면 하나의 메소드로 설계해야될까?

  1. if문 for문으로 경우의 수를 구분하는 건 직관적이지 않다.

  2. 응답 페이지가 다르잖아

  • n건일 떈[전체조회] : 커서가 움직인다. board의 4개가 입력되어 있었다면, 커서가 4번 찍은 것이다.
    • /board/boardList.jsp
  • 1건일 떈 :

    • /board/read.jsp

      • 하나의 메소드를 하고 if문으로 처리해도 되지만, 가독성이 떨어진다.
    • webapp{1건+컨트롤러분리했다.} /board/boardDetail.gd2 로 호출함.

    • Test Case : b_no로 호출한다. --> Pk이다 --> Get방식이다.

		else if("boardDetail".equals(upmu[1]))
		{
				logger.info("BoardController클래스 안 : if ==>boardDetail");
				List<Map<String ,Object>> bList = null;//nList.size()=1
				// NoticeLogic의 메소드 호출 - 객체주입 - 내가(책임) 아님 스프링(제어역전)
				//select * from notice where n_no=5;
				hmb.bind(pMap);
				bList = bLogic.boardList(pMap);
				//원본에다가 담아 두자
				req.setAttribute("bList", bList);
				path = "forward:/board/boardDetail.jsp";
				
			}

한건인데 왜 List에 담았음? --> jsp에 출력하기 위해서

boardInsert

[원래코드]

else if("noticeInsert".equals(upmu[1])) {//insert
			logger.info("noticeInsert");
			int result = 0;
			hmb.bind(pMap);
			result = nLogic.noticeInsert(pMap);
			if(result == 1) {
				path.append("noticeList.gd");
				isRedirect = true;//sendRedirect
			}else {
				path.append("noticeError.jsp");
				isRedirect = true;
			}
		}else if("noticeUpdate".equals(upmu[1])) {//update
			logger.info("noticeUpdate");
			int result = 0;
			hmb.bind(pMap);
			result = nLogic.noticeUpdate(pMap);
			if(result == 1) {
				path.append("noticeList.gd");
				isRedirect = true;
			}else {
				path.append("noticeError.jsp");
				isRedirect = true;
			}

[수정코드]

else if("boardInsert".equals(upmu[1])) {//insert
			logger.info("boardInsert");
			int result = 0;
			hmb.bind(pMap);
			result = bLogic.boardInsert(pMap);
			if(result == 1) {//글등록 성공했을때
				path="redirect:board/boardList";//jsp --(redirect)---->boardInsert.gd2 -----(redirect)------> boardList.gd2 --(forward)---> jsp
			}else {
				path="redirect:/board/boardError.jsp";
			}
		}////////////end of boardInsert
		
		//수정일때 - get,put방식 - 큰의미 없다 - Restful 상징성을 표현함 - update:1(수정성공) or 0(수정안됨)

		else if("boardUpdate".equals(upmu[1])) {//update
			logger.info("boardUpdate");
			int result = 0;
			hmb.bind(pMap);
			result = bLogic.boardUpdate(pMap);
			if(result == 1) {//글등록 성공했을때
				path="redirect:board/boardList";
			}else {
				path="redirect:/board/boardError.jsp";
			}
		}///////////end of boardUpdate		
  • jsp ----(redirect)------> boardInsert.gd2 ----(redirect)----> boardList.gd2 --(forward)--> jsp 이런 순서로 진행함.

insert문

BoardController


		if("boardList".equals(upmu[1])) {//select - 1-3버전에서는 이 장면을 메소드 단위로 변경하고 싶다(req, res)넘겨 받을 수 있어야 한다. -문제 해결못하니까
			logger.info("boardList");
			List<Map<String ,Object>> bList = null;//nList.size()가 n개 
			// NoticeLogic의 메소드 호출 - 객체주입 - 내가(책임) 아님 스프링(제어역전)
			hmb.bind(pMap);  
			bList = bLogic.boardList(pMap);      
			//원본에다가 담아 두자
			req.setAttribute("bList", bList);
			//pageMove[0]=forward, pageMove[1]=/board/boardList.jsp
			path = "forward:board/boardList"; 
		}
	   if("redirect".equals(pageMove[0]))
	     -> res.sendRedirect("/"+path+".jsp);
 	     == res.sendRedirect(/board/boardList.gd.jsp)


  1. 글 쓰기 버튼을 누른 순간

  2. ActionServlet ->

  3. BoardController(boardInsert) ->

  4. HashMapBinder ->

  5. bLogic.boardInsert(pMap) ->

  6. insert부분 처리완료

  7. 다시 bLogic.boardInsert result == 1이기 때문에,

  8. path="redirect:/board/boardList.gd2";에 넣는다. 최종 리턴 path를 하고

  9. 다시 ActionServlet으로 돌아와서 result에 controller를 담는다.

    • result = "redirect:board/boardList" 를 담는다.

      • if(null이 아닌경우를 타고)
      • pageMove라는 배열을 선언해서 경우를 나눈다.
      1. result가 (:)를 가지고 있을 떄, (sendRedirect와, forward가 이에 해당함)

      2. else(그 밖의 경우->(/)로 쪼개야한다.)

        • WEB-INF/jsp/~~{변수}
      3. 그러면 WEB-INF일때만, (/)으로 분기한다.

      • 콜론을 가지고 split를 진행하게 되면
        - 로직에서 WEB-INF 처리 시 경로를 /board/boardList로 잡기 떄문에
        - [board]0번방 , [boardList]1번방만 존재한다.
      1. pageMove = result.split("/");
        • 이렇게 나눠주면, 총 2개의 방이 생긴다.
        • pageMove[0] : redirect
        • pageMove[1] : board/boardList

      4-1. 그것을 가지고 조건을 한 번 더 분기한다.

      • String path = pageMove[1];
      • path = board/boardList 들어간다.
      • 0번방에 "redirect" 일 경우,
        • res.sendRedirect(/board/boardList.jsp)가 들어간다.
      • 0번방에 forward일 경우
        • req.getRequestDispatcher(/board/boardList.jsp) 들어간다.

둘 다 아닌 경우에 들어가기 이전,

WEB-INF 파일을 BoardController에서 path 경로를 잡아줄 때, 이것은 String으로 ActionServlet에 리턴한다.

[WEB-INF]

  • path = board/boardList로 잡아준다.

[WebApp]

  • path = "redirect:board/boardList";
    이렇게 경로를 설정한다.

이렇게 리턴받는게 다르기 때문에 먼저 숙지하고 처리해야된다.

둘 다 아닌 경우(else)

  • path = pageMove[0]+"/"+pageMove[1]; 를 하게 되는데,

    • pageMove[0] : board
    • pageMove[1] : boardList
  • 저것을 알맞게 처리하면,

    • page = board/boardList가 대입된다.
    • req.getRequestDispatcher("/WEB-INF/jsp/"+path+".jsp");에 들어가게된다.
      • /WEB-INF/jsp/board/boardList.jsp

처음에 경우를 나눈 이유가 뭘까?

  • 처음에 else{} 에 걸리고 pageMove = result.split("/");를 진행한다.

  • 이 때, pageMove에 [0] = board + [1] = boardList 가 분기되어 들어간다. (처음에는 ("/")로 슬라이싱) 하는 if)

  • 두 번째 조건분기 if(pageMove != null) 이 때, path의 경로를 설정하고 forward하기 위한 if문이다.


delete

[원래코드]

else if("noticeDelete".equals(upmu[1])) {//delete
			logger.info("noticeDelete");
			int result = 0;
			hmb.bind(pMap);
			result = nLogic.noticeDelete(pMap);
			if(result == 1) {
				path.append("noticeList.gd");
				isRedirect = true;
			}else {
				path.append("noticeError.jsp");
				isRedirect = true;
			}	
		}

[수정코드]

else if("noticeDelete".equals(upmu[1])) {//delete
			logger.info("noticeDelete");
			int result = 0;
			hmb.bind(pMap);
			result = bLogic.boardDelete(pMap);
			if(result == 1) {//글등록 성공했을때
				path="redirect:board/boardList";
			}else {
				path="redirect:/board/boardError.jsp";
			}
		}/////// end of boardDelete -  조건문 블록 하나하나가 메소드로 분할될것.		
		
  • 이미지 업로드 부분은 공통코드로 뻈음

Dao

  • Dao는 생성자를 통해서 값을 주입받는 뺴곤 크게 달라진 곳은 없다.
public class BoardDao {
	Logger logger = Logger.getLogger(BoardDao.class);
	SqlSessionFactory sqlSessionFactory = null;
	
	public BoardDao() {
		sqlSessionFactory = MyBatisCommonFactory.getSqlSessionFactory();
	}


Logic

public class BoardLogic {
	Logger logger = Logger.getLogger(BoardLogic.class);
	BoardDao bDao = new BoardDao();
	public List<Map<String, Object>> boardList(Map<String, Object> pMap) {
		logger.info("boardList");
		//웹개발에서는 NullPointerException이 발동하면 화면자체가 안열림 - 막막함
		//어떤 힌트를 보고 문제를 예측해서(추측) 하나씩 가능성을 제거해 나가는 과정을 통해서 트러블슈팅을 완성함
		//화면은 출력이 된다
		List<Map<String, Object>> bList = new ArrayList<>();//NullPointerException방어코드로
		bList = bDao.boardList(pMap);
		return bList;//화면과 로직을 분리하자 - > POJO 설계해 본다
	}
	//아래 메소드는 트랜잭션처리 대상이다
	//업무적인 depth가 깊을 수록 이런상황이 발생된다
	//AOP{수평적인 관점 관계설정, 개입, 처리가능하게 지원}(<->  OOP-상하관계)는 프레임워크의 한 종류로 공통된 관심사에 대해서 클래스의 어떤 지점을 
	//접근하고 추가코드 없이 자동으로 어떤 일 처리를 가능하게 해주는 프로그래밍 기법이다
	public int boardInsert(Map<String, Object> pMap) {
		logger.info("boardInsert");
		int result = 0;
		result = bDao.boardInsert(pMap);
		//result2 = mDao.memberUpdate(pMap);
		return result;
	}
	public int boardUpdate(Map<String, Object> pMap) {
		logger.info("boardUpdate");
		int result = 0;
		result = bDao.boardUpdate(pMap);
		return result;
	}
	public int boardDelete(Map<String, Object> pMap) {
		logger.info("boardDelete");
		int result = 0;
		result = bDao.boardDelete(pMap);
		return result;
	}
}

  • 전체적으로 코드가 간결해짐

트랜잭션처리

	public int boardInsert(Map<String, Object> pMap) {
		logger.info("BoardLogic : boardInsert 메소드 안에 진입했습니다.");
		int result = 0;
		result = bDao.boardInsert(pMap);
//		result2 = mDao.memberUpdate(pMap) 
		return result;
	}
  • result2 = mDao.memberUpdate(pMap) -- 이 부분에서 다른 Dao로 접근한다.트랜잭션 처리 대상이다.

  • 서로 다른 2가지에서 한 쪽에서 insert, delete, 이런식으로 2가지 Dao로 접근한다면 트랜잭션 처리 대상이다. + 즉, 업무적인 depth가 깊을 수록 이런 상황이 발생된다.

  • AOP(수평적인 관계) <-> OOP(상하관계) : 프레임워크의 한 종류로 공통된 관심사(트랜잭션 처리)에 대해서 클래스의 어떤 지점을 접근하고 추가코드없이 자동으로 어떤 일 처리를 가능하게 해주는 프로그래밍 기법이다.

ActionServlet

[원래코드]

		ActionForward af = null;
		NoticeController nc = new NoticeController();//결합도가 강하다-별로다-제어역전아니다 - 스프링관계된  포인트
		//MemberController mc = new MemberController();
		//LectureController lc = new LectureController();
		////////////////////////[[  어떤 컨트롤러를 태울것인가? ]]/////////////////////////
		//이 지점은 내려가는 방향이다
		if("notice".equals(upmu[0])) {
			req.setAttribute("upmu",upmu);
			af = nc.execute(req, res);//NoticeController클래스로 건너감 - upmu[1]-메소드이름이니까...
		}

		if(af !=null) {
			if(af.isRedirect()) {//true라는 건 sendRedirect인 경우임
				if(af.getPath() == null) {
					return;//해당메소드 탈출
				}else {
					res.sendRedirect(af.getPath());// -> notice/noticeList.jsp					
				}
			}
			else{//forward인 경우임 - url안바뀜, 화면은 바뀜, 유지됨. a페이지에서 쥐고 있는 정보를 b페이지에서도 사용가능함
				if(af.getPath().contains("/")) {
					RequestDispatcher view = req.getRequestDispatcher(af.getPath());
					view.forward(req, res);					
				}
				else if(af.getPath() == null) {//파일 업로드 처리시 ActionForward를 통해서 값을 리턴 받을때 문제가 발생됨. 이부분에 대한 해결  프로세스 추가하였다.
					logger.info("path가 null일때");
				}
				else {
					logger.info("슬래쉬가 미포함인 경우 ===> " + af.getPath());
					res.setCharacterEncoding("utf-8");
					res.setContentType("text/plain;utf-8");
					PrintWriter out = res.getWriter();
					out.print(af.getPath());
					logger.info(af.getPath());
					return;					
				}
			}
		}/////////// end of if - 응답페이지 호출하기 - 포워드

[수정코드]

Controller controller = new BoardController();
		if("board".equals(upmu[0]))
		{
			logger.info("ActionServlet WorkName ==> execute 호출");
			req.setAttribute("upmu",upmu);
			result = controller.execute(req, res);
		}
		// board 컨트롤러를 경유한 다음에 리턴값으로 문자열을 받았다.
		// 콜론으로 자르고(1) + 슬래시로 자른다(2)
		
		
		
		// 1. 널체크하기
		// 2. 문자열 배열을 선언할 것,
		// 3. 콜론이 포함되어 있니?
		// 4. 콜론이 포함되어 있지 않은 경우
		if(result != null)
		{
			logger.info("result 타니?");
			String [] pageMove = null;
			if(result.contains(":"))
			{
				pageMove = result.split(":"); // :를 기준으로 나눔
				// [0] = redirect [1] = /board/boardList.jsp
				// 이 경우에는 슬래시로 한 번더 짤라야된다.
				
			}
			else //WEB-INF 경로를 가진 경우 /board/boardList.jsp
			{
				// 슬래시 기준으로 나눈다.
				pageMove = result.split("/");
				//[0] = board [1] = boardList.jsp
				logger.info(pageMove);
			}
			logger.info(pageMove[0] + ", " + pageMove[1]);
			
			if(pageMove != null)
			{
				String path = pageMove[1];
				logger.info("pageMove타니?");
				
				if("redirect".equals(pageMove[0]))
				{
					logger.info("redirect\".equals(pageMove[0] 구역입니다.");
					res.sendRedirect("/"+path+".jsp");
				}
				else if("forward".equals(pageMove[0]))
				{
					logger.info("forward\"..equals(pageMove[0])");
					RequestDispatcher view = req.getRequestDispatcher("/"+path+".jsp");
					view.forward(req, res);
				}
				else {
					logger.info("WEB-INF타니?");
					path = pageMove[0]+"/"+pageMove[1];
					RequestDispatcher view = req.getRequestDispatcher("/WEB-INF/jsp/"+path+".jsp");
					view.forward(req, res);
				}///// end of 배포위치가 WEB-INF인 경우
			}

		}
	}//end of Service
  • 인터페이스로 -> 인스턴스화

  • result 리턴 타입이 String

경로 체크

[원래코드]

if(af !=null) {
			if(af.isRedirect()) {//true라는 건 sendRedirect인 경우임
				if(af.getPath() == null) {
					return;//해당메소드 탈출
				}else {
					res.sendRedirect(af.getPath());// -> notice/noticeList.jsp					
				}
			}
			else{//forward인 경우임 - url안바뀜, 화면은 바뀜, 유지됨. a페이지에서 쥐고 있는 정보를 b페이지에서도 사용가능함
				if(af.getPath().contains("/")) {
					RequestDispatcher view = req.getRequestDispatcher(af.getPath());
					view.forward(req, res);					
				}
				else if(af.getPath() == null) {//파일 업로드 처리시 ActionForward를 통해서 값을 리턴 받을때 문제가 발생됨. 이부분에 대한 해결  프로세스 추가하였다.
					logger.info("path가 null일때");
				}
				else {
					logger.info("슬래쉬가 미포함인 경우 ===> " + af.getPath());
					res.setCharacterEncoding("utf-8");
					res.setContentType("text/plain;utf-8");
					PrintWriter out = res.getWriter();
					out.print(af.getPath());
					logger.info(af.getPath());
					return;					
				}
			}
		}/////////// end of if - 응답페이지 호출하기 - 포워드

[수정코드]

		if(result != null)
		{
			logger.info("result 타니?");
			String [] pageMove = null;
			if(result.contains(":"))
			{
				pageMove = result.split(":"); // :를 기준으로 나눔
				// [0] = redirect [1] = /board/boardList.jsp
				// 이 경우에는 슬래시로 한 번더 짤라야된다.
				
			}
			else //WEB-INF 경로를 가진 경우 /board/boardList.jsp
			{
				// 슬래시 기준으로 나눈다.
				pageMove = result.split("/");
				//[0] = board [1] = boardList.jsp
				logger.info(pageMove);
			}
			logger.info(pageMove[0] + ", " + pageMove[1]);
			
			if(pageMove != null)
			{
				String path = pageMove[1];
				logger.info("pageMove타니?");
				
				if("redirect".equals(pageMove[0]))
				{
					logger.info("redirect\".equals(pageMove[0] 구역입니다.");
					res.sendRedirect("/"+path+".jsp");
				}
				else if("forward".equals(pageMove[0]))
				{
					logger.info("forward\"..equals(pageMove[0])");
					RequestDispatcher view = req.getRequestDispatcher("/"+path+".jsp");
					view.forward(req, res);
				}
				else {
					logger.info("WEB-INF타니?");
					path = pageMove[0]+"/"+pageMove[1];
					RequestDispatcher view = req.getRequestDispatcher("/WEB-INF/jsp/"+path+".jsp");
					view.forward(req, res);
				}///// end of 배포위치가 WEB-INF인 경우
			}

		}

testCase 1) {webapp- path = "forward:board/boardDetail";}

testcase 2) WEB-INF이 보고싶으면 BoardListController부분
board/boardDetail 수정하면 된다.

profile
아는만큼보인다.

0개의 댓글