20211217 게시물 수정 & 첨부파일만 삭제

DUUUPPAAN·2021년 12월 19일
0

Spring_Framework

목록 보기
10/19

·게시물 수정

-게시물 수정을 진행하다보니, 왜 먼저 게시물 삭제부터 했는지 알 것 같았다. 개인적으로 뭔가 게시물 생성->수정->삭제 이런 식으로 진행하는게 더 순서에 맞다는 생각을 했었는데, 수정 자체에도 삭제의 과정이 들어가서 삭제 다음에 수정을 진행하는 것이 훨씬 합리적이라는 생각이 들었다.

·수정 버튼

-우선 view.jsp에서 수정버튼에 대한 jQuery를 작성해준다. 수정버튼은 기본적으로 내용을 수정할 수 있는 페이지가 보여져야 하기 때문에, 컨트롤러에서 String type으로 리턴을 받아 ViewResolver가 해당하는 포맷으로 경로를 응답할 수 있게 해야 한다.

			//수정 삭제 버튼은 boardMe가 Y일 때만 나타나야 함.
			<c:if test="${boardMe eq 'Y'}">
				$("#btnUpdate").on("click", function(){
					document.bbsForm.action = "/board/updateForm";
					document.bbsForm.submit();
				});
            ...
            </c:if>

-버튼은 당연히 모델맵으로 받은 값인 boardMe가 Y인 상태여야 한다. BoardMe가 N인 상태에서는 해당 게시물의 주인이 아닌 경우이기 때문에 수정창으로 넘어갈 수도 없어야 하기 때문이다. 이제 컨트롤러에서 "/board/updateForm" 이 부분에 대한 RequestMapping 어노테이션으로 해당 .jsp를 리턴할 수 있도록 정의해준다.

	//게시물 수정 폼
	@RequestMapping("/board/updateForm")
	public String updateForm(ModelMap model, HttpServletRequest request, HttpServletResponse response) 
	{
		String cookieUserId = CookieUtil.getHexValue(request, AUTH_COOKIE_NAME);
		
		long hiBbsSeq = HttpUtil.get(request, "hiBbsSeq", (long)0);
		String searchType = HttpUtil.get(request, "searchType", "");
		String searchValue = HttpUtil.get(request, "searchValue", "");
		long curPage = HttpUtil.get(request, "curPage", (long)1);
		
		HiBoard hiBoard = null;
		
		if(hiBbsSeq > 0) 
		{
			hiBoard = hiBoardService.boardViewList(hiBbsSeq);			

			if(hiBoard != null) 
			{
				if(!StringUtil.equals(cookieUserId, hiBoard.getUserId())) 
				{
					//쿠키 아이디와 게시물의 유저 아이디가 같지 않음
					hiBoard = null;
					//게시물 작성자가 현재 접속한 사람과 다르면 수정페이지를 보여주지 않을 것! 날림
				}
			}
		}
		
		model.addAttribute("searchType", searchType);
		model.addAttribute("searchvalue", searchValue);
		model.addAttribute("curPage", curPage);
		
		model.addAttribute("hiBoard", hiBoard);
		
		return "/board/updateForm";
	}

-수정 페이지를 들어갈 때 url을 통해 바로 들어가는 경우도 있을 수 있으니 다시 한번 쿠키아이디와 게시물의 작성자가 같은지 확인하고, 값들이 정확하게 넘어왔는지를 확인한다. 또한, 게시물 수정에 들어가면 기존의 게시물에 대한 정보를 가져와야 하기 때문에 hiBoard객체를 모델맵에 담아서 넘겨야 한다.
원래 처음엔 교수님이 hiBoard객체에 hiBoardService.boardView(hiBbsSeq)를 담으려고 하셨는데, 그렇게 하면 수정 페이지에 들어갈 때마다 게시물의 조회수가 늘어나는 것 같아서 교수님께 질문을 드렸는데, 이미 xml과 dao에서 정의해준 줄 착각하셨던 것 같다. 안그래도 복잡한 과정인데, 교수님이 원하는 스케줄대로 수업을 진행하지 못하기 때문에(코로나, 우리들의 이해도 등등 때문에) 교수님도 굉장히 헷갈리셨던 것 같다. 우리도 충분히 할 수 있는 실수이기 때문에 항상 불러오는 서비스와 메소드가 어떤 것인지 확실하게 확인하는 습관이 필요할 것 같다.

-그래서 이제 hiBoard에 조회수를 증가시키지 않아도 해당 게시물의 값을 가져올 수 있는 서비스부터 구현해봤다.

   //게시품 폼 수정 시, 게시물 및 첨부파일 전부 가져오기
   public HiBoard boardViewList(long hiBbsSeq) 
   {
	   HiBoard hiBoard = null;
	   
	   try 
	   {
		   hiBoard = hiBoardDao.boardSelect(hiBbsSeq);
		   if(hiBoard != null) 
		   {
			   //값이 있음
			   HiBoardFile hiBoardFile = hiBoardDao.boardFileSelect(hiBbsSeq);
			   
			   if(hiBoardFile != null) 
			   {
				   //첨부파일 존재. 하이보드 객체에 하이보트파일을 넣어줌.
				   hiBoard.setHiBoardFile(hiBoardFile);
			   }
		   }
	   }
	   catch(Exception e) 
	   {
		   logger.error("[HiBoardService] boardViewList Exception", e);
	   }
	   
	   return hiBoard;
   }

-전에 해봤던 부분에서 조회수 증가만 없는 것이라 따로 크게 언급할 부분이 없다. 그냥 예외처리에 대한 것들을 잘 해주면 된다.

·updateForm.jsp

-게시물 수정에 대한 정보를 실질적으로 입력하는 jsp를 작성한다. 단, 여기서 값들을 넣어줘야 하기 때문에 모델맵으로 받은 hiBoard객체를 사용해서 값들을 세팅해준다. 단, 무엇인가 오류로 인해서, 혹은 페이지를 직접 치고 들어오는 경우를 방지하기 위해서 hiBoard객체가 null인 경우 body태그 부분을 아예 보여주지 않도록 조건을 준다.

<c:if test="${!empty hiBoard}">

<body>

...(생략)

</c:if>

<form name="bbsForm" id="bbsForm" method="post">
   <input type="hidden" name="hiBbsSeq" value="${hiBoard.hiBbsSeq}" />
   <input type="hidden" name="searchType" value="${searchType}" />
   <input type="hidden" name="searchValue" value="${searchValue}" />
   <input type="hidden" name="curPage" value="${curPage}" />
</form>
</body>

-여기서 조건에 hidden타입의 폼 태그를 포함시키지 않은 것은 히든 타입의 폼 태그는 리스트로 다시 보내거나 할 경우에 사용할 수 있기 때문에 항상 보이지 않더라도 조건에 걸리지 않게 하려는 의도다.

-이제 제목이나 이름에 부합하는 영역에 value="${hiBoard.userId}"와 같은 방식으로 입력해준다. 너무 코드가 길어지는 부분이라 따로 올리지 않겠다.

-이제 게시물을 수정해야하니 수정에 대한 쿼리문을 작성한다.

--업데이트
UPDATE TBL_HIBOARD
SET
    HIBBS_TITLE = 'tttt',
    HIBBS_CONTENT = 'tttt'
WHERE
        HIBBS_SEQ = 6
;

당연히 .xml에 해당 업데이트에 대한 부분을 정의해준다.

<!-- 게시물 수정 시작 -->
<update id="boardUpdate" parameterType="com.icia.web.model.HiBoard">
UPDATE TBL_HIBOARD
SET
    HIBBS_TITLE = #{hiBbsTitle},
    HIBBS_CONTENT = #{hiBbsContent}
WHERE
        HIBBS_SEQ = #{hiBbsSeq}
</update>
<!-- 게시물 수정 종료 -->

이제 해당 id와 동일한 추상메소드를 정의해준다.

	//게시물 수정
	public int boardUpdate(HiBoard hiBoard);

-서비스쪽에서 해당 메소드를 불러와서 처리하는 부분을 정의해준다.

   //게시물 수정
   @Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)
   public int boardUpdate(HiBoard hiBoard) throws Exception
   {
	   int count = 0;
	   
	   count = hiBoardDao.boardUpdate(hiBoard);
	   if(count > 0 && hiBoard.getHiBoardFile() != null) 
	   {
		   //파일이 존재해, 게시물 업데이트도 성공했어.
		   //기존 파일이 있다면, 삭제하고 다시 인서트임
		   //삭제할 하이보드 파일의 정보를 가져옴
		   HiBoardFile delHiBoardFile = hiBoardDao.boardFileSelect(hiBoard.getHiBbsSeq());
		   
		   if(delHiBoardFile != null) 
		   {
			   //테이블 날리기
			   hiBoardDao.boardFileDelete(hiBoard.getHiBbsSeq());

			   //삭제할 애가 존재함, 파일 삭제
			   FileUtil.deleteFile(UPLOAD_SAVE_DIR + FileUtil.getFileSeparator()+delHiBoardFile.getFileName());
			   
		   }
		   
		   //다시 인서트했음 
		   HiBoardFile hiBoardFile = hiBoard.getHiBoardFile();
		   hiBoardFile.setHiBbsSeq(hiBoard.getHiBbsSeq());		   
		   hiBoardFile.setFileSeq((short)1);
		   hiBoardDao.boardFileInsert(hiBoardFile);
		   
	   }
	   
	   return count;
   }

-게시물 수정이지만 조금 주의해야할 부분이 있다. 우선, 게시물 수정이지만, 등록과 같이 게시물과 게시물의 첨부파일 두개를 전부 고려해야 한다. 그래서 만약 과정중에 게시물은 올라가고 첨부파일은 수정이 되지 않았다면, 해당 과정을 전부 롤백시켜야 한다. 즉, 하나의 트랜잭션으로 묶어야 한다. 그래서 따로 해당 부분에 try catch를 걸어주지 않고, 해당 서비스를 호출한 곳으로 예외처리를 넘긴다.
-또한, 게시물 첨부파일의 경우 실제로는, 게시글과 다르게, 첨부파일 자체를 수정하는 것이 아니라 올렸던 첨부파일을 지우고, 새로운 첨부파일을 올리는 과정이다. 그래서 사실상 update가 아니라 delete 후 insert의 과정이라고 보는 것이 맞다. 단, 인서트의 경우, 애초에 ajax통신으로 값이 넘어온 상태여야 파일에 대한 insert가 가능하기 때문에, 실제파일에 대한 처리는 컨트롤러에서 해준다. 물론, 매개변수로 받은 hiBoard에 hiBoardFile이 있으니 해당하는 값들을 db에 넣어주는 것은 서비스에서 해줘야 한다.

-수정에 대한 처리 부분, 즉 버튼을 눌렀을 때 수정이 되었나 안되었나는 당연히 ajax 통신으로, 비동기 통신을 통해서 이뤄진다. 그래서 컨트롤러에 @RequestBody어노테이션을 추가해준다.

	@RequestMapping(value="/board/updateProc", method=RequestMethod.POST)
	@ResponseBody
	public Response<Object> updateProc(MultipartHttpServletRequest request, HttpServletResponse response)
	{
		Response<Object> ajaxResponse = new Response<Object>();
		
		String cookieUserId = CookieUtil.getHexValue(request, AUTH_COOKIE_NAME);
		
		long hiBbsSeq = HttpUtil.get(request, "hiBbsSeq", (long)0);
		String hiBbsTitle = HttpUtil.get(request, "hiBbsTitle", "");
		String hiBbsContent = HttpUtil.get(request, "hiBbsContent", "");
		
		FileData fileData = HttpUtil.getFile(request, "hiBbsFile", UPLOAD_SAVE_DIR);
		HiBoard hiBoard = null;
		
		if(hiBbsSeq >0 && !StringUtil.isEmpty(hiBbsTitle) && !StringUtil.isEmpty(hiBbsContent)) 
		{
			//게시글 존재, 제목, 내용도 넘어옴.
			hiBoard = hiBoardService.boardSelect(hiBbsSeq);
			if(hiBoard != null) 
			{
				if(StringUtil.equals(cookieUserId, hiBoard.getUserId())) 
				{
					//게시물 작성자 아이디와 쿠키 아이디가 같음
					//얘는 변경이 없지만 해두자
					hiBoard.setHiBbsSeq(hiBbsSeq);
					hiBoard.setHiBbsTitle(hiBbsTitle);
					hiBoard.setHiBbsContent(hiBbsContent);
					if(fileData != null && fileData.getFileSize()>0) 
					{
						//파일 존재
						HiBoardFile hiBoardFile = new HiBoardFile();

						//새로운 값들을 넣어줌
						hiBoardFile.setFileName(fileData.getFileName());
						hiBoardFile.setFileOrgName(fileData.getFileOrgName());
						hiBoardFile.setFileExt(fileData.getFileExt());
						hiBoardFile.setFileSize(fileData.getFileSize());
						
						hiBoard.setHiBoardFile(hiBoardFile);
					}
					
					//업데이트 할 것인데, 파일첨부까지 생각해야 함. 그래서 트랜잭션을 걸어야 하기 때문에 try catch
					try 
					{
						if(hiBoardService.boardUpdate(hiBoard) > 0) 
						{
							ajaxResponse.setResponse(0, "Success");
						}
						else 
						{
							ajaxResponse.setResponse(500, "Internal Server Error");
						}
					}
					catch(Exception e) 
					{
						logger.error("[HiBoardController] updateProc Exception", e);
						ajaxResponse.setResponse(500, "Internal Server Error");
					}
					
				}
				else 
				{
					//본인 게시물이 아님
					ajaxResponse.setResponse(404, "Not The Owner");
				}
			}
			else 
			{
				ajaxResponse.setResponse(401, "Not Found");
			}
		}
		else 
		{
			ajaxResponse.setResponse(400, "Bad Request");
		}
		
		
		return ajaxResponse;
	}

-서비스에서 예외처리를 넘겼기 때문에, 컨트롤러에서 try catch문을 반드시 사용해줘야 한다. 그 외에 예외처리와 해당 결과에 대한 코드는 알아서 설정하는 것이라서 적당한 예외처리에 해당 번호로 넘겨주면 된다. 사실 이 부분은 등록 삭제에 이미 한 부분이라서 크게 다를 것이 없다.

·한 학생의 질문

-수업 도중 한 학생이 질문을 했다.

 "현재 상태에서는 첨부파일에 대한 교체만 가능한데, 혹시 첨부파일 자체를 삭제하는 방법은 없는 것인가요?"

-그래서 갑자기 댓글에 대한 부분을 수업하려다가 삭제 버튼을 알아서 만들어보게 되었다. 결론적으로 삭제버튼을 만드는 것을 성공한 사람은 반에 두 명 정도인(사실 몇 명 더 있을 수도 있는데 대답이 없어서 내가 아는 사람은 두 명인 것일 수도 있다.) 것 같았는데, 교수님은 그 상황에서 더 진도를 나갈 수 없다고 판단하셔서 해당 부분에 대한 수업을 진행하셨다.

-우선 교수님의 방법은 다음과 같다.

	//수정 페이지 삭제 버튼
	@RequestMapping(value="/board/deleteFileProc", method=RequestMethod.POST)
	@ResponseBody
	public Response<Object> deleteFileProc(HttpServletRequest request, HttpServletResponse response)
	{
		Response<Object> ajaxResponse = new Response<Object>();
		String cookieUserId = CookieUtil.getHexValue(request, AUTH_COOKIE_NAME);
		long hiBbsSeq = HttpUtil.get(request, "hiBbsSeq", (long)0);
		
		if(hiBbsSeq>0) 
		{
			HiBoard hiBoard = hiBoardService.boardSelect(hiBbsSeq);
			
			if(hiBoard != null) 
			{
				if(StringUtil.equals(cookieUserId, hiBoard.getUserId())) 
				{
					HiBoardFile hiBoardFile = hiBoardService.boardFileSelect(hiBbsSeq);
					
					hiBoard.setHiBoardFile(hiBoardFile);
					
					if(hiBoardFile != null) 
					{
						//파일 삭제
						try 
						{
							if(hiBoardService.boardFileDelete(hiBoard) > 0) 
							{
								ajaxResponse.setResponse(0, "Success");
							}							
						}
						catch(Exception e) 
						{
							logger.debug("[HiBoardController] deleteFileProc Exception", e);
							ajaxResponse.setResponse(500, "Bad Request");
						}
					}
					else 
					{
						ajaxResponse.setResponse(404, "Bad Request");
					}
				}
				else 
				{
					ajaxResponse.setResponse(401, "Bad Request");
				}
			}
		}
		else 
		{
			ajaxResponse.setResponse(404, "Bad Request");

		}
		

		return ajaxResponse;
	}

-컨트롤러

   //게시물 첨부파일만 삭제
   @Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)
   public int boardFileDelete(HiBoard hiBoard) throws Exception
   {
	   int count = 0;
	   

	   if(hiBoard.getHiBoardFile() != null) 
	   {
		   //파일이 존재해, 게시물 업데이트도 성공했어.
		   //기존 파일이 있다면, 삭제하고 다시 인서트임
		   //삭제할 하이보드 파일의 정보를 가져옴
		   HiBoardFile delHiBoardFile = hiBoardDao.boardFileSelect(hiBoard.getHiBbsSeq());
		   
		   if(delHiBoardFile != null) 
		   {
			   //테이블 날리기
			   count = hiBoardDao.boardFileDelete(hiBoard.getHiBbsSeq());
			   
			   //삭제할 애가 존재함, 파일 삭제
			   FileUtil.deleteFile(UPLOAD_SAVE_DIR + FileUtil.getFileSeparator()+delHiBoardFile.getFileName());
			   
		   }
	   }
	   return count;
   }

-서비스

-사실 나도 첨부파일 삭제 버튼을 만드는 것에 성공했는데, 교수님과는 방법이 달랐다. 애초에 게시물 수정, 등록, 삭제와는 다르게 첨부파일에 대한 삭제는 그냥 hiBoardFile에 대한 처리만 해주면 되는 것이라고 생각했다. 그래서 굳이 따로 hiBoard객체를 넘기지 않고, hiBbsSeq(게시물 등록번호)만 가지고도 충분히 첨부파일 삭제가 가능하다고 생각했다. 물론 그렇게 될 경우 쿠키아이디와 유저아이디의 일치를 확인하는 것이 어려워질 수 있지만, 어차피 화면이 뜬 상태이기 때문에 그럴 필요가 없다고 판단했다. 그래서 hiBbsSeq만 넘겨서 값이 있는지 없는지 등에 대한 처리만 했다. 물론 첨부파일도 제대로 삭제되게 작성했고 처리도 되었지만, 그래도 교수님의 방법과 같지 않아서 지웠다. 그런데 교수님께서는 굳이 본인처럼 쿠키 아이디를 확인하지 않아도 된다는 점에서 동의하셔서, 결국 괜히 지웠다는 생각마저 들게 되었다. 두 방법 전부 공부할 수 있었을텐데, 물론 주말에 해당 코드를 다시 작성할 수 있지만, 다음주까지 프로젝트에 대한 테이블을 정해야하기 때문에 테이블 컬럼에 온 신경을 쏟아부어야 한다. 그래서 따로 만들 시간은 없을 것 같아서 해당 코드를 정말 열심히 공부했다.

·테이블 걸럼

-테이블 컬럼 작성, 생각보다 프로젝트에 필요한 테이블이 엄청나게 많은 것 같다 또한, 컬럼도 굉장히 다양한 것 같다. 그래서 주말 내내 생각을 많이 했고 작성도 많이했다. 하지만 뭔가 다른 테이블을 생각하다 보면 필요한 컬럼이 또 추가되고 추가되는 것 같아서 정말 컬럼 정하는 것은 힘든 것 같다. 그래도 열심히 생각해서 내일 꼭 원만한 결과를 얻고 테이블을 작성해야겠다.

profile
비전공자란 이름으로 새로운 길을 가려 하는 신입

0개의 댓글