[ Spring MVC 작업시 발생하는 null 업데이트 이슈 해결 하기 ]
>> @SessionAttributes 어노테이션
1.
위의 이미지는 작성된 게시글을 수정하는 기능 페이지인, detailBoard.jsp 이다.
본인은 18번째 라인에 작성한것처럼, 만약 이미지 파일을 새롭게 올리지 않을 경우,
처음 게시글을 올릴 때 업로드 했었던 이미지 파일을 다시 hidden 으로 해서,
Controller 에게 전달 하는 코드 이다.
이렇게 하지 않을 경우, 이미지 파일을 새롭게 올리지 않으면,
fileUpload 라는 이름으로 Controller 에게 전송 되는 파일은 null 이 된다.
null 데이터가 Controller 에게 전달되면,
bVO.getFileUpload().isEmpty() 는 업로드된 파일이 비어있니 ? 이고,
! 가 붙어 있기 때문에,
업로드된 파일이 비어있지 않니 ?
가 되는데,
비어있지 않다 == 있다
의 경우에는,
파일의 이름을 String fileName 변수에 저장해서,
filename 이라는 프로퍼티에 값을 set 하고,
update() 를 해서,
DB에 저장을 하는 흐름인데,
null 데이터가 온다면, if문이 참이 아니기 때문에,
if문을 통과해서,
filename 이라는 프로퍼티의 데이터가 null 이 된다.
그렇게 update() 를 하고,
DB에 null이 저장 된다.
이때, 다시 detailBoard.jsp 를 확인해보자.
게시글 객체 == bdata 의 filename 이라는 프로퍼티가 비어있다면 ~
이미지를 default.jpg 이미지로 보여주게 되는 코드가 작성 되어 있다.
즉, 사용자가 게시글 수정을 할 때,
이미지를 새로 수정하지 않았으면,
기존의 이미지는 유지 되어야 하는데,
위와 같이,
이미지가 엉뚱하게 변경 되어 버리는 문제점이 발생 하게 된다.
그렇다고 사용자에게 사용 방법을 알려줄것인가 ?
>>
우리 웹 페이지 게시글 수정 기능은, 기존에 업로드한 파일을 올리지 않으면,
디폴트 이미지로 변경 되거든 ?
그러니까 기존에 업로드 했었던 파일을 무조건 올리세요 ~
라고,,
이런 기능을 가진 게시판 이면, 어떤 클라이언트가 해당 웹 페이지를 이용하겠는가..
위와 같은 상황을 보고, "null 업데이트 이슈" 라고 말할 수 있겠다.
그래서, 이러한 이슈가 발생 되지 않도록,
<input type = "hidden" name = "filename" value = "${bdata.filename}">
위와 같이 로직을 작성해서, 이슈를 해결하는게 일반적인 방법이다.
하지만, @SessionAttributes 라는 어노테이션을 사용해서 해결 하는 방법을 알아보자.
2.
View 에서 Controller 으로 전달된 특정한 자료형의 객체를
XxxController.java 내부의 메서드 인자로 넣어서,
Command 객체 개념을 사용 할 때,
해당 자료형의 객체가 가지고 있는 프로퍼티명과,
View 으로부터 온 데이터의 파라미터명이,
동일 하다면,
Command 객체가 자동으로 setter을 호출해서, 해당하는 값을 모두 set 하게 된다.
이때, 사용자가 파일을 업로드 하지 않은 경우,
!(bVO.getFileUpload().isEmpty()) 가 거짓이 되기 때문에,
if 문을 통과 하고,
결국, filename 이라는 동일한 파라미터명을 가진 데이터가 없기 때문에,
setter을 자동 호출 했지만, 데이터가 set 되지 못하고,
filename 프로퍼티의 값은 null 이 되게 된다.
이렇게 Command 객체가 특정한 프로퍼티를 set 하지 못했을 경우에,
인지해서 !
set 하지 못한 그 프로퍼티의 값을 !
바로 직전에 세션 (스코프) 으로 저장했던 객체가 (== 게시글) 들고 있었던 !
해당하는 프로퍼티의 값으로 set 해준다 !
누가 ?
@SessionAttributes 어노테이션이 해준다 !
근데 @SessionAttributes 어노테이션이 해주려면,
command 객체로 사용되는 특정 자료형의 객체를 알아야 한다.
그래서, @ModelAttribute 어노테이션과 반드시 함께 사용 되어야 한다.
@SessionAttributes 어노테이션은 다음과 같다.
1)
Spring MVC 에서 세션에 데이터를 유지 하기 위해 사용 된다.
2)
@SessionAttributes 어노테이션 을 사용하면,
해당 컨트롤러의 @ModelAttribute 에 지정된,
이름을 가진 데이터들을 세션에 저장하고 관리 한다.
3)
즉,
컨트롤러 (내부의 메서드) 가 호출 될 때,
해당 이름으로 된 @ModelAttribute 에 지정된,
이름을 가진 데이터들이,
@SessionAttributes 어노테이션에 의해, 세션에 생성되고 초기화 된다.
@ModelAttribute 어노테이션은 다음과 같다.
참고 링크 :
https://hwanii96.tistory.com/326
@ModelAttribute("이름") 어노테이션
그외의 어노테이션 정리 참고 : https://hwanii96.tistory.com/309 스프링 컨테이너 어노테이션 메모1. com.spring.biz 하위 폴더에, 모든 자료형을 (클래스 파일) 대상으로 객체화를 해주게 하는 코드 이다. 위
hwanii96.tistory.com
Controller 에서 @ModelAttribute("파라미터명") 어노테이션을 붙혀서,
붙어진,
객체 / 메서드 / 변수
를,
View 에서 지정한 파라미터명으로 해당하는 데이터를 사용 할 수 있게 된다.
(근데, 변수를 사용하려고, 굳이 어노테이션을 사용하진 않음..)
여기서 짚고 넘어가야 하는 것은,
아래의 Map<String,String> searchMap() 메서드 위에 달린,
@ModelAttribute("searchMap") 은,
메서드 자체를 읽는게 아니라, 메서드가 반환 하는 map 이라는 데이터를 저장 하는 개념 이다.
예시 1 )
위의 이미지를 보면, searchMap 이라는 파라미터명으로 설정을 했고,
아래 searchMap() 이라는 메서드가 있기 때문에,
해당 메서드를 View 에서 searchMap 이라는 이름으로 사용 할 수 있게 된다.
예시 2 )
위의 이미지와 같이,
MemberVO mVO 객체를 mem 이라는 파라미터명으로 설정을 했기 때문에,
View 에서는 mVO 객체를 mem 이라는 이름으로 사용 할 수 있게 된다.
이때, 생각해야할점은,
mVO 객체는 아무런 데이터도 세팅 되어 있지 않은 객체 이다.
그래서 View에서 해당 객체의 데이터를 사용 하기 위해서,
아래와 같이 프로퍼티 값을 set 해줘야 한다.
그러면, View에서 해당 데이터를 사용 할 수 있게 된다.
이런식으로 ~
3.
예시로 확인 하기.
BoardVO bVO에 @ModelAttribute 어노테이션을 달고,
소괄호 내부에 @SessionAttributes 어노테이션이 인지 할 수 있도록,
매개변수명을 작성해준다.
그리고,
@SessionAttributes 어노테이션을 사용해서,
소괄호 내부에,
해당 매개변수명을 작성 하면 된다.
위와 같이 사용함으로써,
혹시나 특정 프로퍼티의 값이 null 인 경우에,
null 업데이트 이슈를 처리 할 수 있게 된다.
4.
@SessionAttributes 어노테이션은,
@Component 어노테이션을 상속 받은,
@Controller 어노테이션 하단에 작성 하여 사용 하면 된다.
왜냐하면, XxxController.java 내부에서 사용하는 거니까 ~
5.
[ 흐름 정리 ]
View 으로부터, 특정한 .do 요청이 들어오면,
XxxController.java 클래스 내부의 특정 메서드 위에 달린,
RequestMapping 어노테이션이 요청을 매핑 한다.
이때,
메서드 내부의 인자에 작성 되어 있는, ModelAttribute 어노테이션을 읽고,
해당 XxxController.java 클래스 내부에서 유효한,
바로 직전의 bdata 라는 데이터를 읽고,
이 bdata 데이터를 SessionAttributes 어노테이션이 읽어서,
세션 스코프에 bdata를 저장 한다.
그리고,
메서드 내부의 커맨드 객체인 BoardVO bVO가, setter 호출을 하게 되고,
View 에서 보내준 동일한 파라미터명을 가진 데이터를 set 하게 되는데,
이때,
View 에서 null 값으로 일부러 보내준 데이터는 그냥 null로 set을 하게 되고,
View 에서 아예 보내주는 데이터가 아닌 경우에,
(값이 빈게 아니라 아예 안보내주는)
그 값이 null 이면,
세션에 저장 해놓았던 bdata 데이터의, 해당하는 값을 set 하게 된다.
이런 과정과 흐름으로 null 업데이트 이슈를 방지 하게 된다.
이것을 사용 하는 이유는,
위와 같이, 이미 게시글에 업로드한 이미지를 변경 하고 싶지 않을 때,
fileUpload 라는 데이터를 null 값으로 보내게 될텐데,
fileUpload 가 있어야지 filename 의 값을 구할 수 있어서,
filename 역시 null 이 되고,
게시글에 업로드한 이미지가 원치 않는 이미지로 변경되는 이슈가 생기는 상황일 때 사용 한다.
6.
[ 주의 사항 1 ]
어? 그러면 이렇게 null 업데이트 이슈를 방지 할 수 있는 기능을 가진 어노테이션 이니까 ~
이 SessionAttributes 어노테이션을 여러개 등록 하면 좋겠네 ~
는 아니다.
너무 많은 등록을 하게 되면,
즉,
등록한 데이터가 많아지면 많아질수록,
View 에서 요청했을 때, 요청 자체가 무거워 지게 된다.
(세션 스코프에 데이터를 저장 하니까)
그래서,
위의 SessionAttributes 어노테이션 으로 이슈를 해결 해야 한다 싶으면,
기능별로 클래스를 나눠서 설계 하게 되면,
요청 자체가 한 클래스 내부로 다 들어오는게 아니라,
분산 되기 때문에, 무거워지는 이슈를 줄일 수 있게 된다.
[ 주의 사항 2 ]
필터 검색 기능이 있다고 가정 해보자.
검색 데이터 자체도 저장이 가능한데,
만약에 검색을 hello 라고 했고,
한번 더 필터 검색을 이용 하려고 할 때,
DAO 내부의 쿼리문이 LIKE 구문으로 되어 있어서,
전체 검색을 원해서 아무런 값도 입력 하지 않은 경우,
원래라면, 모든 데이터가 검색 되어야 하는데,
SessionAttributes 어노테이션 때문에,
검색 데이터를 의미하는 프로퍼티가 null 이네 ?
하고, 직전의 검색어 이였던 hello로 set을 해줘서,
원치 않는 엉뚱한 결과가 나올 수 있는 단점이 있다.
즉, 특정 프로퍼티의 값을 일부러 null 으로 주고 싶은데도,
null 으로 주지 못하는 이슈가 발생 할 수 있다.
( 위의 경우는 실제로 코드를 작성해서 해봐야 정확히 알듯 .. )
++ 23.08.21 추가
사용자가 View 에서 보내는 데이터는 정해져 있다.
<form> 태그 내부에 있는,
<input> 태그에 있는 name 속성 값에 작성 되어 있는,
프로퍼티 들만 보내는 데이터가 된다.
이때, 만약 사용자가 일부러 게시글 내용을 작성 하지 않고, submit 버튼을 눌렀다고 해보자.
그러면, 게시글 내용에 해당하는 프로퍼티는 content 이고,
content를 사용자가 일부러 입력 하지 않았기 때문에,
빈값.
즉, null 값이 가게 된다.
그렇다는것은, null값이 갔기 때문에, @SessionAttributes("bdata") 가
BoardVO bVO가 setter을 호출 해서 set 할 때,
아예 없는 데이터가 아니라, null 값이 있기 때문에, 자동으로 set 해주지 않는다.
생각해보자..
사용자가 일부러 데이터를 빈값으로 보냈는데, 그것을 예전 게시글 객체에 있는 값으로
자동으로 set 해서 다시 보여주면 이상 하지 않겠는가 ..
정리하자면,
이 null 업데이트 이슈는 어느 경우에 사용 하냐면,
사용자가 이미 업로드된 이미지를 변경 하고 싶지 않아서,
파일을 업로드 하지 않았을 때,
fileUpload 는 null 값이 가게 될것 이다.
이때, filename 은 fileUpload가 있어야 구할 수 있는 값인데,
fileUpload가 null 이니까, 당연히 filename 을 구할 수 없게 된다.
그러면 filename이 null이 되어버려서, 원치 않는 이미지를 보게 되버린다.
그래서, 위와 같은 경우는,
애초에 View 에서 filename 이라는 데이터 자체를 보내주지 않는 상황이고,
fileUpload가 null 이면,
filename 자체가 null이 되어버리니,
(사용자가 filename 이라는 값을 안보내준 상황이 아니다)
이런 경우에, @SessionAttributes("bdata")가 set을 해줘서,
파일을 따로 업로드 하지 않았지만,
이미 게시글에 올려두었던 파일을 보여 줄 수 있도록 한다.
선택된 파일이 없을 때,
fileUpload 가 null 이라서, filename 도 null 이 되는데,
@SessionAttributes() 어노테이션 덕분에 filename이 set 되어 버린다.
정리 끝..
틀린 내용 있으면 댓글 달아주세요.
'Spring 프레임워크 > 이론' 카테고리의 다른 글
@RequestMapping / @GetMapping / @PostMapping (0) | 2023.09.20 |
---|---|
Spring 프레임워크 - 다국어 처리 기능 (3) | 2023.08.24 |
Spring ExceptionResolver 을 사용한 예외 처리 (0) | 2023.08.18 |
Spring 프레임워크 MultipartFile 인터페이스 (0) | 2023.08.17 |
Spring JDBC (1) | 2023.08.16 |