- @SessionAttribute 사용 권장 안함
- @SessionAttribute는 주로 세션에 저장된 모델 속성을 뷰에서 다시 사용할 때 활용됩니다.
- 인증 정보를 관리하는 용도로는 적합하지 않으며, 세션에 해당 속성이 없을 경우 예외가 발생할 수 있습니다.
- HttpSession을 멤버 필드로 주입받는 것의 문제점이 발생 될 수 있음
- Spring MVC 컨트롤러는 기본적으로 싱글톤(하나의 인스턴스)으로 관리됩니다.
- HttpSession을 멤버 필드로 주입받으면 여러 요청 사이에 공유될 수 있어 쓰레드 안전성 문제가 발생할 수 있습니다.
- 메서드 파라미터로 HttpSession을 받아 사용하는 것의 장점
- HttpSession을 메서드 파라미터로 받아 사용하면, 각 요청마다 개별적인 세션 객체에 접근할 수 있어 쓰레드 안전성이 확보됩니다.
package com.tenco.blog_v2.user;
import com.tenco.blog_v2.common.errors.Exception401;
import com.tenco.blog_v2.common.errors.Exception500;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
@RequiredArgsConstructor
@Slf4j
@Controller
public class UserController {
// DI 처리
private final UserService userService;
private final HttpSession session;
/**
* 회원 정보 수정 페이지 요청
* 주소설계 : http://localhost:8080/user/update-form
*
*
* @return 문자열
* 반환되는 문자열을 뷰 리졸버가 처리하며
* 머스태치 템플릿 엔진을 통해서 뷰 파일을 렌더링 합니다.
*/
@GetMapping("/user/update-form")
public String updateForm(HttpServletRequest request, HttpSession session) {
//public String updateForm(HttpServletRequest request, @SessionAttribute(name = "sessionUser") User sessionUser) {
// @SessionAttribute(name = "sessionUser") User sessionUser
// 이어노테이션은 모델에 저장되어 있는 세션 값을 바로 가지고 오는 어노테이션이다.
// 단. 이녀석 뷰, 템플릿 엔진에서 접근하도록 설계 되어 있다. 권장 x
User sessionUser = (User) session.getAttribute("sessionUser");
User user = userService.readUser(sessionUser.getId());
request.setAttribute("user", user);
return "user/update-form"; // 템플릿 경로 : user/join-form.mustache
}
/**
* 사용자 정보 수정
* @param reqDTO
* @return 메인 페이지
*/
@PostMapping("/user/update")
public String update(@ModelAttribute(name = "updateDTO") UserDTO.UpdateDTO reqDTO) {
User sessionUser = (User) session.getAttribute("sessionUser");
if (sessionUser == null) {
return "redirect:/login-form";
}
User updatedUser = userService.updateUser(sessionUser.getId(), reqDTO);
// 세션 정보 동기화 처리
session.setAttribute("sessionUser", updatedUser);
return "redirect:/";
}
/**
* 회원가입 페이지 요청
* 주소설계 : http://localhost:8080/join-form
*
* @param model
* @return 문자열
* 반환되는 문자열을 뷰 리졸버가 처리하며
* 머스태치 템플릿 엔진을 통해서 뷰 파일을 렌더링 합니다.
*/
@GetMapping("/join-form")
public String joinForm(Model model) {
log.info("회원가입 페이지");
model.addAttribute("name", "회원가입 페이지");
return "user/join-form"; // 템플릿 경로 : user/join-form.mustache
}
/**
* 회원 가입 기능 요청
* @param reqDto
* @return
*/
@PostMapping("/join")
public String join(@ModelAttribute(name = "joinDTO") UserDTO.JoinDTO reqDto) {
// 유효성 검사 생략 ...
try {
userService.signUp(reqDto);
} catch (DataIntegrityViolationException e) {
throw new Exception500("동일한 유저네임이 존재 합니다.");
}
return "redirect:/login-form";
}
/**
* 로그인 페이지 요청
* 주소설계 : http://localhost:8080/login-form
*
* @param model
* @return 문자열
* 반환되는 문자열을 뷰 리졸버가 처리하며
* 머스태치 템플릿 엔진을 통해서 뷰 파일을 렌더링 합니다.
*/
@GetMapping("/login-form")
public String loginForm(Model model) {
log.info("로그인 페이지");
model.addAttribute("name", "로그인 페이지");
return "user/login-form"; // 템플릿 경로 : user/join-form.mustache
}
/**
* 자원에 요청은 GET 방식이지만 보안에 이유로 예외 !
* 로그인 처리 메서드
* 요청 주소 POST : http://localhost:8080/login
* @param reqDto
* @return
*/
@PostMapping("/login")
public String login(UserDTO.LoginDTO reqDto) {
try {
User sessionUser = userService.signIn(reqDto);
session.setAttribute("sessionUser", sessionUser);
return "redirect:/";
} catch (Exception e) {
throw new Exception401("유저이름 또는 비밀번호가 틀렸습니다.");
}
}
@GetMapping("/logout")
public String logout() {
session.invalidate(); // 세션을 무효화 (로그아웃)
return "redirect:/";
}
}
결론
로그인 처리 후 사용자 정보를 세션 메모리에 저장하는 것 자체는 문제없습니다. 이때 세션 객체는 JSESSIONID를 통해 사용자별로 관리되기 때문에 안전합니다. 하지만 세션 정보를 컨트롤러의 멤버 필드로 저장하게 되면, 컨트롤러의 싱글톤 특성 때문에 해당 필드가 여러 사용자 요청 간에 공유되면서 쓰레드 안전성 문제가 발생할 수 있습니다.따라서, 세션 정보를 관리할 때는 멤버 필드로 사용하지 말고, 메서드 파라미터로 세션 객체에 접근하여 개별적으로 사용하는 것이 더 안전하고 권장되는 방법입니다.
'Spring boot > 개념 공부' 카테고리의 다른 글
RestAPI 주소 설계 규칙 (0) | 2024.11.08 |
---|---|
CORS(Cross-Origin Resource Sharing)이란 뭘까? (1) | 2024.11.08 |
댓글 쓰기 및 삭제 및 인터셉터 적용 (0) | 2024.11.08 |
게시글 삭제 오류 해결 (0) | 2024.11.08 |
댓글 목록 보기 (0) | 2024.11.08 |