오늘은 앞의 이미지 업로드 실습에 이어 글 수정 시 파일을 추가하지 않았을 경우 파일이 디폴트 이미지로 들어가지는 경우에 대해서 포스팅 하려고 한다. 이미지 업로드 실습은 아래의 링크로 이동하면 된다.
https://junwons.tistory.com/124
[Spring] MultipartFile을 활용한 파일 업로드
오늘은 Spring에서 제공하는 방법을 통해 파일 업로드에 대해 알아보려고 한다. 먼저 파일 업로드를 하기 위해서는 [.jar] 파일이 필요하다. 따라서 pom.xml 파일에 아래의 코드를 추가한다. pom.xml comm
junwons.tistory.com
NULL 업데이트 이슈란?
NULL 데이터가 업데이트에 들어갔다라는 뜻
글 수정 시에 별도의 이미지를 추가하지 않으면, 해당 name 파라미터에 NULL이 들어간다. 그래서 NULL 값이 그대로 Controller --> Service --> DAO --> DB 까지 전달되게 되어, 해당 글을 다시 보려고 하면, 내가 업로드한 파일이 아닌 디폴트 파일로 설정되어 'NULL 업데이트 이슈'가 발생한다. 따라서 NULL 업데이트가 되지 않도록 @(어노테이션) 설정을 해야한다.
이 때의 로직은 이러하다.
혹시 NULL 이니?
그럼 이전에 불렀던 내용으로 세팅해줄게
↓ ↓ ↓
라고 설정해주는 @(어노테이션)이 @SessionAttributes 이다.
@SessionAttributes
별도로 설정하지 않아도 브라우저가 종료되기 전까지 모든 데이터를 기억할 수 있는 힘이 있는 어노테이션이다.
이 어노테이션은 Controller에서 설정하는 것 !
따라서, View에서 이전에 불렀던 내용을 알려줘야하고, 그러기 위해서는 같이 설정해야하는 @(어노테이션)이 존재한다.
그 @(어노테이션)은 @ModelAttribute이다.
- @SessionAttributes : View에서 넘어온 데이터들의 이름을 불러올 수 있는 어노테이션
- Controller 상단에 위치
- @ModelAttribute : View에서 넘어온 데이터들의 이름이 '설정한 이름 / 예시: data' 라고 알려주는 어노테이션
- Command 객체와 같이 위치
현재는 글 수정을 기준으로 설명할 예정이며, 코드는 아래와 같다.
먼저, View에서 기존의 데이터가 넘어오는지를 확인한다.
board.jsp
<c:if test="${empty data}">
<h1>해당 게시글은 없거나 이용이 불가능한 게시글입니다!</h1>
</c:if>
<c:if test="${not empty data}">
<form action="updateBoard.do" method="post">
<input type="hidden" name="bid" value="${data.bid}">
<input type="text" name="writer" value="${data.writer}" disabled> <br>
<input type="text" name="title" required value="${data.title}"> <br>
<input type="text" name="content" required value="${data.content}"> <br>
<c:if test="${ data.fileName eq null }">
<img alt="" src="./images/paper.png">
</c:if>
<c:if test="${ data.fileName ne null }">
<img alt="" src="./images/${ data.fileName }"> <br>
</c:if>
<c:if test="${data.writer eq member}">
<input type="submit" value="글 변경"> <input type="button" onclick="del(${data.bid})" value="글 삭제">
</c:if>
</form>
<form> 태그를 이용해 값을 넘겨주기 때문에 문제가 없다. 따라서 Controller에서 설정만 해주면 끝이 난다.
BoardController.java
@Controller
@SessionAttributes("data")
public class BoardController {
...
@RequestMapping(value="/insertBoard.do", method=RequestMethod.POST)
public String insertBoard(@ModelAttribute("data")BoardVO bVO) throws IllegalStateException, IOException {
MultipartFile fileUpload = bVO.getFileUpload();
if(!fileUpload.isEmpty()) { // 파일 업로드 했니 ?
String fileName = fileUpload.getOriginalFilename();
System.out.println("파일명: " + fileName);
// 복사본을 생성해서 전용 폴더에 넣어줘
fileUpload.transferTo(new File("/Users/lyujun-won/Desktop/Ryu/SpringWorkspace/day69/src/main/webapp/images/" + fileName));// 그럼 복사본 생성해주세요
bVO.setFileName(fileName);
}
if(boardService.insert(bVO)){
return "redirect:main.do";
}
else{
return "redirect:insertBoard.jsp";
}
}
}
현재 필요한 데이터는 BoardVO에 담겨있기 때문에 Command 객체인 BoardVO 앞에 '@ModelAttribute("data")' 를 선언하고 data라는 이름으로 선언하여 클래스 최상단의 '@SessionAttributes' 를 선언할 때 똑같이 ("data") 를 넣어 같은 값임을 알려준다. 이렇게 작성하게 되면 끝이 난다 !
그러면 이렇게 생각할 수도 있다. '그럼 막 갖다 붙혀도 되는 거 아닌가?' 라고 할 수도 있는데 정답은 '그럴 수 없다' 이다.
그 이유는
- 등록할 데이터가 많아질수록 요청이 무거워진다.
--> 따라서, 하나의 클래스에 Select와 Update만 남겨두고 다른 클래스로 옮기는 것이 좋다. (현재의 경우)
--> 현재의 코드로는 검색어 + main.do / @SessionAttributes + selectOne + update 와 같이 묶는 것이 좋다.
BoardController.java
@Controller
@SessionAttributes("data")
public class BoardController {
@Autowired
private BoardService boardService;
@ModelAttribute("searchMap")
public Map<String,String> searchMap(){
Map<String,String> map=new HashMap<String,String>();
map.put("제목", "TITLE");
map.put("작성자", "WRITER");
return map;
}
@RequestMapping(value="/main.do")
public String main(@ModelAttribute("mem")MemberVO mVO, BoardVO bVO, Model model) {
System.out.println("searchCondition: "+bVO.getSearchCondition());
System.out.println("searchContent: "+bVO.getSearchContent());
if(bVO.getSearchCondition() == null) {
bVO.setSearchCondition("TITLE");
bVO.setSearchContent("");
} // 검색 로직에서 종종 활용되는 기법
mVO.setMid("test");
mVO.setMpw("1234");
// model.addAttribute("mem", mVO);
model.addAttribute("datas", boardService.selectAll(bVO));
return "main.jsp";
}
@RequestMapping(value="/board.do")
public String selectBoard(BoardVO bVO, Model model) {
model.addAttribute("data", boardService.selectOne(bVO));
boardService.update(bVO);
return "board.jsp";
}
@RequestMapping(value="/updateBoard.do")
public String updateBoard(BoardVO bVO) {
boardService.update(bVO);
return "board.do";
}
@RequestMapping(value="/deleteBoard.do")
public String deleteBoard(BoardVO bVO) {
if(boardService.delete(bVO)){
return "redirect:main.do";
}
else{
return "board.do";
}
}
@RequestMapping(value="/insertBoard.do", method=RequestMethod.GET)
public String insertBoardPage(BoardVO bVO) {
return "redirect:insertBoard.jsp";
}
@RequestMapping(value="/insertBoard.do", method=RequestMethod.POST)
public String insertBoard(@ModelAttribute("data")BoardVO bVO) throws IllegalStateException, IOException {
MultipartFile fileUpload = bVO.getFileUpload();
if(!fileUpload.isEmpty()) { // 파일 업로드 했니 ?
String fileName = fileUpload.getOriginalFilename();
System.out.println("파일명: " + fileName);
// 복사본을 생성해서 전용 폴더에 넣어줘
fileUpload.transferTo(new File("/Users/lyujun-won/Desktop/Ryu/SpringWorkspace/day69/src/main/webapp/images/" + fileName));// 그럼 복사본 생성해주세요
bVO.setFileName(fileName);
}
if(boardService.insert(bVO)){
return "redirect:main.do";
}
else{
return "redirect:insertBoard.jsp";
}
}
}
- 두번째로는 검색 데이터 저장 문제가 발생한다.
--> 예를 들어 설명하면 사용자가 검색에 'apple'이라고 검색을 한번 한 후에, 그 다음 번에 검색을 하지 않고 전체 데이터를 보려고 할 때 @SessionAttribute 입장에서는 ' 어? 검색어가 없네? apple로 검색해줄게! ' 라고 인식하기 때문에 NULL을 만들 수가 없다는 단점이 있다.
'Spring' 카테고리의 다른 글
[Spring] 예외 처리 페이지 (0) | 2023.08.18 |
---|---|
[Spring] MultipartFile을 활용한 파일 업로드 (0) | 2023.08.17 |
[Spring] Spring JDBC (JDBCTemplate) (1) | 2023.08.16 |
[Spring] AOP (AfterReturning & AfterThrowing) (0) | 2023.08.14 |
[Spring] AOP (Aspect Oriented Programming) (0) | 2023.08.11 |