[35] 파일 업로드 처리 - 게시물의 조회와 첨부파일

게시물의 조회에서는 첨부파일을 다운로드하거나 원본 이미지의 파일을 볼 수 있는 기능을 사용하게 된다. 

 

게시물의 정보는 tbl_board 테이블에 기록되 어있고, 첨부파일의 정보는 tbl_attach에 기록되어 있기 때문에 화면에서 두 테이블에 있는 정보를 사용하기 위해서는 다음과 같은 방식을 고려할 수 있다. 

 

  • BoardVO 객체를 가져올 때 join을 처리해서 한꺼번에 게시물과 첨부파일의 정보를 같이 처리하는 방식이다. 데이터베이스를 한 번만 호출하게 되므로 효율적이지만 MyBatis 쪽에서 처리해아 하는 일이 많아진다
  • JSP 에서 첨부파일의 정보를 Ajax를 이용해서 처리하는 방식이다. 다시 쿼리를 처리해야 하는 불편함이 있지만 난이도가 낮고, 화면에서 처리는 JavaScript 처리가 복잡하다. 

위의 방식들 중에서 전통적인 방식은 쿼리를 이용해서 두 개의 테이블을 join 해서 처리하는 방식이다. 쿼리를 한 번만 실행하기 때문에 데이터베이스의 부하를 줄여주는 장점이 있다. 

 

예제는 Ajax를 이용하는 방식을 사용해서 구성한다. 가장 큰 이유는 기존에 개발해 둔 코드를 최소한으로 수정해서 사용하는 것이 가능하기 때문이기도 하고, 앞에서 작성한 예제 코드를 어느정도 사용할 수 있기 때문이다. 또한, 댓글의 처리 역시 Ajax를 이용했으므로 동작 방식에 일관성을 유지하는 의도도 있다. 

 

1. BoardService와 BoardController 수정

 

게시물을 조회할 때 첨부파일을 Ajax로 처리하기로 했다면 우선적으로 서버 측에서 JSON 데이터를 만들어서 화면에 올바르게 전송하는 작업을 먼저 처리해아한다. 

 

BoardAttachMapper에서는 이미 게시물의 번호를 이용해서 BoardAttachVO 타입으로 변환하는 메서드 findByBno() 메서드가 완성된 상태이므로, BoardService와 BoardServiceImpl 클래스를 수정한다. 

 

- BoardService 인터페이스

package org.zerock.service;

import java.util.List;

import org.zerock.domain.BoardAttachVO;
import org.zerock.domain.BoardVO;
import org.zerock.domain.Criteria;

public interface BoardService {
	
	public void register(BoardVO board);
	
	public BoardVO get(Long bno);
	
	public boolean modify(BoardVO board);
	
	public boolean remove(Long bno);
	
	//public List<BoardVO> getList();

	public List<BoardVO> getList(Criteria cri);
	
	public int getTotal(Criteria cri);
	
	public List<BoardAttachVO> getAttachList(Long bno);
}

 

마지막에 getAttachList()를 추가해서 게시물의 첨부파일들의 목록을 가져온다.

 

- BoardServiceImpl 클래스의 일부

	@Override
	public List<BoardAttachVO> getAttachList(Long bno){
		log.info("get Attach list by bno" + bno);
		return attachMapper.findByBno(bno);
	}

 

BoardServiceImpl은 이미 BoardAttachMapper를 주입하도록 설계한 상태이므로, BoardAttachMapper 인터페이스의 findByBno()를 호출하고 반환하도록 작성한다. 

 

2. BoardController의 변경과 화면 처리

 

BoardController 는 특정한 게시물 번호를 이용해서 첨부파일과 관련된 데이터를 JSON으로 반환하도록 처리한다.

 

- BoardController 클래스의 일부

	@GetMapping(value = "/getAttachList",
			produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
	@ResponseBody
	public ResponseEntity<List<BoardAttachVO>>getAttachList(Long bno){
		log.info("getAttachList" + bno);
		return new ResponseEntity<>(service.getAttachList(bno), HttpStatus.OK);
	}

 

BoardController는 @RestController로 작성되지 않았기 때문에 직접 @RestponseBody를 적용해서 JSON 데이터를 반환하도록 처리한다.

 

2-1. 게시물 조회 화면의 처리

 

게시물을 조회하는 화면은 views 내에 /board/get.jsp에서 이루어진다. 가장 먼저 해당 게시물의 댓글을 가져오는 부분이 이 자동으로 동작하게 처리하는 작업부터 시작한다.

 

<script type="text/javascript">
$(document).ready(function() {
	
	(function(){
		var bno = '<c:out value = "${board.bno}"/>';
		$.getJSON("/board/getAttachList", {bno:bno}, function(arr){
			console.log(arr);
		});
	})();
</script>

 

get.jsp 내에 <script> 태그를 추가하고, $(document).ready()를 이용해서 첨부파일의 데이터를 가져오는 부분을 즉시 실행 함수를 이용해서 처리한다. 브라우저에서 첨부파일이 추가된 게시물을 선택하면 아래 그림과 같이 콘솔창에 해당 게시물의 첨부파일의 목록을 볼 수 있다. 

 

 

첨부파일 데이터를 가져왔다면 파일들을 보여줄 수 있도록 <div>를 생성한다. 기존의 게시물이 보여지는 <div class='row'>의 아래쪽에 별도의 <div class='row'>를 생성해서 처리한다. 

 

- get.jsp 일부 ( 댓글 패널 위쪽에 코드를 추가한다!)

<div class = 'bigPictureWrapper'>
	<div class = 'bigPicture'>
	</div>
</div>
<style>
.uploadResult {
	width : 100%;
	background-color : gray;
}

.uploadResult ul{
	display : flex;
	flex-flow : row;
	justify-content : center;
	align-items : center;
}

.uploadResult ul li{
	list-style : none;
	padding : 10px;
	align-content : center;
	text-align : center;
}

.uploadResult ul li img {
	width : 100px;
}

.uploadResult ul li span {
	color : white;
}

.bigPictureWrapper{
	position : absolute;
	display : none;
	justify-content : center;
	align-items : center;
	top : 0%;
	width : 100%;
	height : 100%;
	background-color : gray;
	z-index : 100;
	background : rgba(255, 255, 255, 0.5);
}

.bigPicture {
	position : relative;
	display : flex;
	justify-content : center;
	align-items : center;
}

.bigPicture img {
	width : 600px;
}
</style>
<div class = "row">
	<div class = "col-lg-12">
		<div class = "panel-heading">Files</div>
		<!-- /.panel-heading -->
		<div class = "panel-body">
			<div class = 'uploadResult'>
				<ul>
				</ul>
			</div>
		</div>
		<!-- end panel body -->
	</div>
	<!-- end panel -->
</div>
<!-- /.row -->

 

get.jsp 내에 추가되는 <div>는 크게 첨부파일의 목록을 보여주는 <div class = 'uploadResult'>와 원본 이미지를 보여주는 <div class = 'bigPicture'> 부분이다. 

 

2-2. 첨부파일 보여주기 

 

JSON으로 가져온 첨부파일 데이터는 작성된 <div> 안에서 보이도록 처리해주어야 한다. 전달된 JSON 데이터는 BoardAttachVO 객체다

 

JavaScript에서는 다음과 같은 처리를 통해서 화면에 보여주는 부분을 작성한다.

 

 - get.jsp 의 일부 (function() 함수안에 집어넣도록 한다)

	$.getJSON("/board/getAttachList", {bno : bno}, function(arr){
		console.log(arr);
		var str = " ";
		$(arr).each(function(i, attach){
			
			//img type
			if(attach.fileType){
				var fileCallPath = encodeURIComponent(attach.uploadPath + "/s_" + attach.uuid + "_" + attach.fileName);
				
				str += "<li data-path = '" + attach.uploadPath + "' data-uuid = '" + attach.uuid + "'data-filename = '"
				+ attach.fileName + "' data-type = '" + attach.fileType+"' ><div>";
				str += "<img src='/display?fileName=" + fileCallPath + "'>";
				str += "</div>";
				str + "</li>";
			}else {
				str += "<li data-path = '" + attach.uploadPath + "' data-uuid = '" + attach.uuid + "' data-filename = '"
				+ attach.fileName + "'data-type'" + attach.fileType + "'><div>";
				str += "<span>" + attach.fileName + "</span><br/>";
				str += "<img src = '/resources/img/attach.png'>";
				str += "</div>";
				str + "</li>;
			}
		});
		
		$(".uploadResult ul").html(str);
	});

 

 

게시물의 등록과 달리 첨부파일의 삭제 표시 등의 필요 없는 부분은 조금 정리해서 출력 하도록 한다!

 

 

2-3. 첨부파일 클릭 시 이벤트 처리

 

첨부파일의 목록이 보인다면 이미지 파일의 경우 화면에서 원본 이미지, 일반 파일의 경우에는 다운로드 처리가 필요하다.

 

- get.jsp 일부

	 $(".uploadResult").on("click","li", function(e){
	      
		    console.log("view image");
		    
		    var liObj = $(this);
		    
		    var path = encodeURIComponent(liObj.data("path")+"/" + liObj.data("uuid")+"_" + liObj.data("filename"));
		    
		    if(liObj.data("type")){
		      showImage(path.replace(new RegExp(/\\/g),"/"));
		    }else {
		      //download 
		      self.location ="/download?fileName="+path
		    }
		    
		    
		  });
		  
		  function showImage(fileCallPath){
			    
		    alert(fileCallPath);
		    
		    $(".bigPictureWrapper").css("display","flex").show();
		    
		    $(".bigPicture")
		    .html("<img src='/display?fileName="+fileCallPath+"' >")
		    .animate({width:'100%', height: '100%'}, 1000);
		    
		  }

		  $(".bigPictureWrapper").on("click", function(e){
		    $(".bigPicture").animate({width:'0%', height: '0%'}, 1000);
		    setTimeout(function(){
		      $('.bigPictureWrapper').hide();
		    }, 1000);
		  });

 

첨부파일과 관련된 정보는 모두 <li> 태그 내에 존재하므로 이를 이용해서 이미지 파일과 일반 파일을 구분해서 처리한다. 파일 경로의 경우 함수로 전달될 때 문제가 생기므로 replace()를 이용해서 변환한 뒤에 전달한다.

 

showImpage()는 해당 경로의 이미지를 보여주는 역할을 한다. 위의 코드가 추가되면 화면에서 원본 이미지를 클릭했을 때 크게 보이게 된다.