고래씌

[Spring] 8-2. 사진게시판(사진 여러개일 때(다중 insert문)) 본문

Server/Spring

[Spring] 8-2. 사진게시판(사진 여러개일 때(다중 insert문))

고래씌 2024. 1. 24. 15:54

1. 사진 여러개를 업로드 할 때

 

▶ boardEnrollForm.jsp

=> 사진게시판일 때는 여러개의 사진을 업로드 할 수 있도록 하는 form이 보이도록 하고 아닐 때는 첨부파일이 하나만 첨부할 수 있도록 보이도록 설정

 

                    <c:if test="${boardCode ne 'T'}">   <!-- 사진게시판이 아닐때 -->
                        <tr>
                            <th>첨부파일</th>
                            <td><input type="file" id="upfile" class="form-control" name="upfile"></td>
                        </tr>
                    </c:if>
                    <c:if test="${boardCode eq 'T' }">  <!-- 사진게시판 이면 -->
                    	<tr>
	                        <th><label  for="image">업로드 이미지1</label></th>
	                        <td>
	                        <img class="preview" >
	                        <input type="file" name="upfiles" class="form-control inputImage" accept="images/*" id="img1">
	                        <span class="delete-image">&times;</span>
                        </td>
	                    </tr>
	                    <tr>
	                        <th><label  for="image">업로드 이미지2</label></th>
	                        <td>
	                        <img class="preview" >
	                        <input type="file" name="upfiles" class="form-control inputImage" accept="images/*" id="img2">
	                        <span class="delete-image">&times;</span>
	                    </tr>
	                    <tr>
	                        <th><label  for="image">업로드 이미지3</label></th>
	                        <td>
	                        <img class="preview">
	                        <input type="file" name="upfiles" class="form-control inputImage" accept="images/*" id="img3">
	                        <span class="delete-image">&times;</span>
	                        </td>
	                    </tr>
	                    <tr>
	                        <th><label  for="image">업로드 이미지4</label></th>
	                        <td>
	                        <img class="preview" >
	                        <input type="file" name="upfiles" class="form-control inputImage" accept="images/*" id="img4">
	                        <span class="delete-image">&times;</span>
	                        </td>
                    </tr>
                    </c:if>

 

 

=> 사용자가 등록한 이미지를 미리 볼 수 있도록 설정

=> X 버튼을 클릭하면 사진 미리보기를 삭제하도록 설정

 

	<!-- 사진 미리보기 -->
	<c:if test="${boardCode eq 'T'}"> <!-- 사진게시판이라면 -->
		<script>
			const inputImage = document.querySelectorAll('.inputImage'); // input type = file
			const preview = document.querySelectorAll('.preview'); // img
			const deleteImage = document.querySelectorAll('.delete-image'); // 삭제버튼들
			
			inputImage.forEach( function ( value, index  ){
				//현재 반복중인 file태그
				value.addEventListener('change', function(){
					if(this.files[0] != undefined){// 선택한 파일이 있는경우
						const reader = new FileReader(); // 선택된 파일을 읽을 객체 생성
						reader.readAsDataURL(this.files[0]);
						
						// reader가 파일을 다 읽어온 경우
						reader.onload = function(e) {
							preview[index].setAttribute("src", e.target.result);
						}
					}else{ // 파일이 선택되지 않았을때 (취소)
						preview[index].removeAttribute("src");
					}
					
				})
				
				// X 버튼을 클릭하였을때 사진 삭제하도록 설정
				deleteImage[index].addEventListener('click', function(){
					
					// 현재 미리보기가 존재하는 경우에 삭제처리되도록 수정
					if(preview[index].getAttribute("src") != "") {
						
						// 미리보기 삭제 + input태그 비워주기
						preview[index].removeAttribute("src");
						inputImage[index].value = "";
					}
				})
			})
		</script>
	</c:if>

 

 

 

 

☞ 사진 미리 볼수 있는 것을 확인

X 클릭시 사진 미리보기가 삭제되는 것을 확인

 

 

1) 게시판 이미지를 저장

▶ BoardImg.java

=> 게시판 이미지를 저장할 vo를 만들어줌(BoardImg.java)

package com.kh.spring.board.model.vo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@NoArgsConstructor
@AllArgsConstructor
@Data
public class BoardImg {
	private int boardImgNo;
	private String originName;
	private String changeName;
	private int refBno;
	private int imgLevel;
}

 

 

 

▶ BoardController.java

 

=> List<MultipartFile> upfiles 추가

 

 

 

=> 첨부파일목록(upfiles)같은 경우 선택하고 안하고 상관없이 객체는 생성이 된다. 단, 길이가 0일수가 있음
=> 전달된 파일이 있을경우에만 해당파일을 웹서버에 저장하고, BOARD_IMG 테이블에 해당정보를 등록할 예정

 

 

 

=> 전에 작성하였던 insertBoard(b, imgList); 추가

 

 

 

 

▶ BoardSerive, BoardSeriveImpl, BoardDao, BoardDaoImpl 모두 수정

BoardService.java

 

 

 

BoardServiceImpl.java

//	게시판 등록
	@Override
	public int insertBoard(Board b, List<BoardImg> imgList) {
		/*
		 * 게시글 삽입
		 * 
		 * 게시글 삽입시 게시글의 제목, 내용에 들어가는 문자열에 크로스사이트스크립트 공격을 방지하기 위한 메소드 추가
		 * textarea, 제목에 들어가는 엔터 및 스페이스바를 개행문자로 변환처리
		 */
		
		b.setBoardContent(Utils.XSSHandling(b.getBoardContent()));
		b.setBoardContent(Utils.newLineHandling(b.getBoardContent()));
		b.setBoardTitle(Utils.XSSHandling(b.getBoardTitle()));
		
		int result = boardDao.insertBoard(b);
		
		// 2_ 첨부파일 이미지리스트 등록(BOARD_IMG)
		int boardNo = b.getBoardNo();
		
		// imgList가 비어있을 수가 있으니까
		if(!imgList.isEmpty()) {
			for(BoardImg bi : imgList) {
				bi.setRefBno(boardNo);
				result *= boardDao.insertBoardImg(bi);
			}
		}
		
		return result;
	}

 

 

BoardDao.java

 

BoardDaoImpl.java

 

 


하지만, 위 코드는 효율이 안좋다!!!! 정확성도 안좋다.

new version으로 만들어보겠다!

 

 

2. 사진게시판 등록 - 버전 2 (다중 insert문)

▶ BoardController.java

 

=> 예외 처리를 해줌

 

 

 

=> HttpSession session을 없애고 RedirectAttributes ra 로 변경하고, 아래에 코드에 다음과 같이 추가

 

 

 

 

▶ BoardService.java (게시판 등록 수정)

//	게시판 등록
	int insertBoard(Board b, List<BoardImg> imgList) throws Exception;

 

=> throws Exveption 추가

 

 

 

▶ BoardServiceImpl.java

 

 

=> 게시판 등록 메소드 맨위에 @Transactional 추가 ☞ 기본 자동 커밋인데 언제는 rollback을 시킬 수 있다! 예외가 발생하면 rollback 처리해주는 속성

 

 

☞ 다중 insert문

 

 

 

 

 

▶ BoardDao.java

//	사진게시글 버전2
	int insertBoardImgList(List<BoardImg> imgList);

 

 

▶ BoardDaoImpl.java

//	사진게시글 등록 버전2
	@Override
	public int insertBoardImgList(List<BoardImg> list) {
		return sqlSession.insert("boardMapper.insertBoardImgList", list);
	}

 

 

 

▶ board-mapper.xml

 

동적 sql문중 foreach
- 특정 SQL구문을 반복할 때 사용함
- 반복되는 쿼리문 사이에 구문자를 추가할 수 있음

collection : 반복할 객체의 타입(자료형)
item : collection에서 순차적으로 꺼낸 값을 저장하는 변수
separator : 반복 사이의 구분자

index : 현재 반복접근 중인 인덱스(0,1,2,3,4)
open : 반복 "전에" 출력할 sql
close : 반복 "후에" 출력할 sql

 

	<!-- 게시판 등록 버전 2 -->
	<insert id="insertBoardImgList" parameterType="list">
		INSERT INTO BOARD_IMG 
		(BOARD_IMG_NO,ORIGIN_NAME,CHANGE_NAME,REF_BNO,IMG_LEVEL)
		SELECT SEQ_INO.NEXTVAL,C.* 
		FROM (
			<foreach collection="list" item="boardImg" separator="UNION ALL">
				SELECT
				#{boardImg.originName},
				#{boardImg.changeName},
				#{boardImg.refBno},
				#{boardImg.imgLevel}
				FROM DUAL
			</foreach>
		) C
	</insert>

 

 

 

 

=> DB에 정상적으로 값이 추가되는 것 확인

 

 

 

▶ BoardController.java

	// url로 전달받은 동적 파라미터 => @PathVariable("boardCode") String boardCode
	// @ModelAttribute("loginUser") Member loginUser => loginUser와 일치하는 것을 가져올 수 있음 
	// @ModelAttribute("loginUser") Member loginUser => sessionAttributes에 의해 session으로 이관된 데이터를 매개변수에서 얻어올 때 사용하는 방법
	@PostMapping("/insert/{boardCode}")  
	public String insertBoard(
			Board b, 
			@PathVariable("boardCode") String boardCode,
			@ModelAttribute("loginUser") Member loginUser, 
			Model model,
			RedirectAttributes ra, 
			@RequestParam(value="upfile", required=false) MultipartFile upfile, // 첨부파일
			@RequestParam(value="upfiles", required=false) List<MultipartFile> upfiles
			) {
		
		// 이미지, 파일을 저장할 저장경로 얻어오기
		String webPath = "/resources/images/board/" + boardCode + "/";  // boardCode가 T라면 사진게시판, C라면 일반게시판..
		String serverFolderPath = application.getRealPath(webPath);
		
		// resources 폴더안에는 아무것도 없기 때문에 디렉토리가 존재하지 않는다면 생성해주는 코드 추가
		File dir = new File(serverFolderPath);
		if(!dir.exists()) {   // 존재하지 않는다면 디렉토리 생성
			dir.mkdirs();  // 디렉토리를 여러개 추가해야하니까 mkdirs로 추가
		}
		
		// 사용자가 첨부파일을 등록한 경우
		// upfile은 첨부파일이 있든, 없든 무조건 객체는 생성됨.
		// 단, 첨부파일 등록을 하지 않은경우 내부에 데이터가 비어있다. ("")
		// 사용자가 전달한 파일이 있는지 없는지는 filename이 존재하는지로 확인하면 된다.
		if(upfile != null && !upfile.getOriginalFilename().equals("")) { // 원본파일이름이 비어있지않다면
			String changeName = Utils.saveFile(upfile, serverFolderPath);  // 업로드 파일, 저장되는 경로
			b.setOriginName(upfile.getOriginalFilename());
			b.setChangeName(changeName);
		}

		
		// Board 객체에 데이터 추가
		b.setBoardWriter(loginUser.getUserNo()+"");
		b.setBoardCd(boardCode);
		
		log.info("board {}", b);
		
		
		
		// 첨부파일목록(upfiles)같은 경우 선택하고 안하고 상관없이 객체는 생성이 된다. 단, 길이가 0일수가 있음
		// 전달된 파일이 있을경우에만 해당파일을 웹서버에 저장하고, BOARD_IMG 테이블에 해당정보를 등록할 예정
		List<BoardImg> imgList = new ArrayList();
		int level = 0;
		
		log.info("imgList ?? {}", upfiles);   // 길이 4
		if(upfiles != null) {  
			for(int i=0; i<upfiles.size(); i++) {
				
				if(upfiles.get(i).getOriginalFilename().equals("")) {
					continue; // 반복 다시 돌림
				}
				
				String changeName = Utils.saveFile(upfiles.get(i), serverFolderPath);
				BoardImg bi = new BoardImg();
				bi.setChangeName(changeName);
				bi.setOriginName(upfiles.get(i).getOriginalFilename());
				bi.setImgLevel(i);
				// pk, refBno
				imgList.add(bi);
			}
		}
		
		
		int result = 0;
		try {
			result = boardService.insertBoard(b, imgList);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		String url = "";
		if(result > 0) {
			ra.addFlashAttribute("alertMsg", "글작성 성공함");
			// session.setAttribute("alertMsg", "글작성 성공함");
			url = "redirect:/board/list/" + boardCode;
		}else {
			model.addAttribute("errorMsg", "게시글 작성 실패함");
			url = "common/errorPage";
		}
		
		return url;
	}