s00jin 님의 블로그

4. [로그인] 세션 로그인 구현하기 - Spring/SpringBoot 본문

프로젝트/하고 싶은거 다해보는 내 사이트

4. [로그인] 세션 로그인 구현하기 - Spring/SpringBoot

s00jin 2025. 7. 7. 09:16

세션

로컬에 저장하던 쿠키와 다르게 서버에 저장한다

일정 시간동안 같은 사용자로부터 들어오는 일련의 요구를 하나의 상태로 보고 그 상태를 일정하게 유지시키는 기술
  • 쿠키 이름은 JSESSIONID (← 추정 불가능한 랜덤 값)

 

세션 생성 및 조회

  • HttpSession은 HttpServletRequest를 통해 가져올 수 있음
    • HttpServletRequest request (매개변수에)
    • HttpSession session = request.getSession();
  • request.getSession()
    • request.getSession(true) - default
      • 세션이 있으면 기존 세션 반환
      • 세션이 없으면 새로운 세션을 생성해서 반환
    • request.getSession(false)
      • 세션이 있으면 기존 세션 반환
      • 없으면 null 반환
  • session.getAttribute(”userId”)
    • session에서 userId를 키로 가진 값 가져오기

 

세션 파기

  • session.invalidate()
    • 세션 저장소에서 기존 세션 파기
  • request.getSession(false)
    • 로그아웃 할 때 세션이 없으면 null 리턴으로 메모리 낭비 방지

 

그 외

  • setMaxInactiveInterval(1800)
    • session 유효 시간 30분
    • 1800 - default 값
    • HTTP 비연결성 특징 때문에 서버는 사용자가 웹브라우저를 종료해도 해당 사항을 모름 → 서버 세션 데이터를 언제 삭제해야할 지 모름 → 쿠키 탈취 + 메모리 문제 등 발생 가능성 있음 ⇒ 세션 종료 지점 지정
  • setAttribute(”userId”, user.getId())
    • 세션 저장소에 저장
  • getID()
    • JSESSIONID 값
  • getLastAccessedTime()
    • 연결된 사용자가 최근에 서버에 접근한 시간
    • 반환 타입 Date

 

예시

// 이건 로그인에서 세션 사용

        // 로그인 성공 -> 세션 생성
        // 세션 생성 전 기존 세션 파기
        httpServletRequest.getSession().invalidate();
        // Session이 없으면 생성
        HttpSession session = httpServletRequest.getSession(true);
        // 세션에 userId를 넣어줌
        session.setAttribute("userId", user.getId());
        // session 유지 시간 30분
        session.setMaxInactiveInterval(1800);
    
 // 이건 로그아웃에서 세션 사용    
    public String logout(HttpServletRequest request, Model model){
				// 전체 코드는 아래 있음
				
        // false -> session이 없으면 null return
        HttpSession session = request.getSession(false);
        if(session != null) {
            session.invalidate();
        }

    }

 

url에 JSESSIONID 포함될 시

 

브라우저가 쿠키를 지원하지 않으면 url에 JSESSIONID를 포함시킨다. 이 전달 방식을 사용하지 않으려면 application.properties를 수정하면 된다.

 

server.servlet.session.tracking-modes=cookie

 

구현 코드

package org.mySite.user.controller;

@Controller
@RequiredArgsConstructor
@RequestMapping("/session-login")
public class SessionLoginController {

    private final UserService userService;

    // 로그인 홈 페이지
    @GetMapping(value = {"", "/"})
    public String home(Model model, @SessionAttribute(name = "userId", required = false) Long userId) {
        model.addAttribute("loginType", "session-login");
        model.addAttribute("pageName", "세션 로그인");

        if (userId != null){
            User loginUser = userService.getLoginUser(userId);
            model.addAttribute("nickname", loginUser.getNickname());
        }

        return "home";
    }

    // 회원가입 페이지
    @GetMapping("/join")
    public String joinPage(Model model){
        model.addAttribute("loginType", "session-login");
        model.addAttribute("pageName", "세션 로그인");

        // TODO JoinRequest 생성 이유 알아보기
        // JoinRequest를 작성하는 이유 알아보기
        model.addAttribute("joinRequest", new JoinRequest());
        return "join";
    }

    // 회원가입
    @PostMapping("/join")
    public String join(@Valid @ModelAttribute JoinRequest joinRequest, BindingResult bindingResult, Model model) {
        model.addAttribute("loginType", "session-login");
        model.addAttribute("pageName", "세션 로그인");

        // id 중복 체크
        if(userService.checkLoginId(joinRequest.getLoginId())){
            bindingResult.addError(new FieldError("joinRequest", "loginId", "로그인 아이디가 중복됩니다."));
        }

        // nickname 중복 체크
        if(userService.checkNickname(joinRequest.getNickname())){
            bindingResult.addError(new FieldError("joinRequest", "nickname", "닉네임이 중복됩니다"));
        }

        // 비밀번호 + 비밀번호 체크 동일 확인
        if(!joinRequest.getPassword().equals(joinRequest.getPasswordCheck())) {
            bindingResult.addError(new FieldError("joinRequest", "passwordCheck", "비밀번호가 일치하지 않습니다"));
        }

        // 최종 확인
        if(bindingResult.hasErrors()){
            return "join";
        }

        userService.join(joinRequest);
        return "redirect:/session-login";
    }

    // 로그인 페이지
    @GetMapping("/login")
    public String loginPage(Model model){
        model.addAttribute("loginType", "session-login");
        model.addAttribute("pageName", "세션 로그인");

        model.addAttribute("loginRequest", new LoginRequest());
        return "login";
    }

    // 로그인
    @PostMapping("/login")
    public String login(@ModelAttribute LoginRequest loginRequest, BindingResult bindingResult, HttpServletRequest httpServletRequest, Model model) {
        model.addAttribute("loginType", "session-login");
        model.addAttribute("pageName", "세션 로그인");

        User user = userService.login(loginRequest);

        if(user == null) {
            bindingResult.reject("loginFail", "로그인 아이디 또는 비밀번호가 틀렸습니다.");
        }

        // 로그인 실패 시 로그인 페이지로 반환
        if(bindingResult.hasErrors()){
            return "login";
        }

        // 로그인 성공 -> 세션 생성
        // 세션 생성 전 기존 세션 파기
        httpServletRequest.getSession().invalidate();
        // Session이 없으면 생성
        HttpSession session = httpServletRequest.getSession(true);
        // 세션에 userId를 넣어줌
        session.setAttribute("userId", user.getId());
        // session 유지 시간 30분
        session.setMaxInactiveInterval(1800);

        return "redirect:/session-login";
    }

    // logout
    @GetMapping("/logout")
    public String logout(HttpServletRequest request, Model model){
        model.addAttribute("loginType", "session-login");
        model.addAttribute("pageName", "세션 로그인");

        // false -> session이 없으면 null return
        HttpSession session = request.getSession(false);
        if(session != null) {
            session.invalidate();
        }
        return "redirect:/session-login";
    }

    // info
    @GetMapping("/info")
    public String info(@SessionAttribute(name = "userId", required = false) Long userId, Model model){
        model.addAttribute("loginType", "session-login");
        model.addAttribute("pageName", "세션 로그인");

        User loginUser = userService.getLoginUser(userId);

        if(loginUser == null){
            return "redirect:/session-login/login";
        }

        model.addAttribute("user", loginUser);
        return "info";
    }

    // admin
    @GetMapping("/admin")
    public String admin(@SessionAttribute(name = "userId", required = false) Long userId, Model model){
        model.addAttribute("loginType", "session-login");
        model.addAttribute("pageName", "세션 로그인");

        User loginUser = userService.getLoginUser(userId);

        if(loginUser == null){
            return "redirect:/session-login/login";
        }

        if(!loginUser.getRole().equals(UserRole.ADMIN)) {
            return "redirect:/session-login";
        }

        return "admin";
    }
}


참고

https://jddng.tistory.com/268

https://innovation123.tistory.com/57