고래씌
[Spring] 8-2. 사진게시판(사진 여러개일 때(다중 insert문)) 본문
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">×</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">×</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">×</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">×</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 모두 수정
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;
}
하지만, 위 코드는 효율이 안좋다!!!! 정확성도 안좋다.
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;
}
'Server > Spring' 카테고리의 다른 글
[Spring] 8-4. 첨부파일 사진 다운로드 (0) | 2024.01.25 |
---|---|
[Spring] 8-3. 일반 게시판 상세보기, 조회수 증가 (0) | 2024.01.25 |
[Spring] 8-1. 일반 게시글 등록(크로스사이트스크립트 공격 방지) (0) | 2024.01.24 |
[Spring] 7. Application 전역에 있는 데이터를 저장(Intercepter) (0) | 2024.01.24 |
[Spring] 6-2. 게시판 검색기능 (0) | 2024.01.24 |