본문 바로가기
🖥Web/🔥SpringBoot

[SpringBoot] HttpSession값 바로 가져오기, 어노테이션 기반 Session 으로 개선하기

by 후누스 토르발즈 2022. 6. 13.
반응형

기본으로 HttpSession 값 바로 가져오는 방식에서

노테이션 기반 Session 으로 개선하기

 

 

패키지 구조

 

 

 

build.gradle

buildscript {
    ext {
        springBootVersion = '2.1.9.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

plugins {
    id 'java'
    id 'eclipse'
    id 'org.springframework.boot' version '2.3.2.RELEASE'
    id 'io.spring.dependency-management' version '1.0.8.RELEASE'
}

group 'com.practice.springboot'
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    implementation('org.springframework.boot:spring-boot-starter-web')
    implementation('org.projectlombok:lombok')
}

test {
    useJUnitPlatform()
}

 

 

 

HttpSession 값 바로 가져오기

resources/application.yml

spring:
  mvc:
    view:
      suffix: .html

suffix 지정

 

 

 

resources/templates/index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>INDEX</title>
</head>
<body>
    <h1>INDEX</h1>
    <input type="text" id="name" value="김씨">
    <button type="button" id="btn-post">post</button>
    <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
    <script src="/js/app/index.js"></script>
</body>
</html>

 

 

 

resources/templates/home.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>HOME</title>
</head>
<body>
    <h1>HOME</h1>
</body>
</html>

 

 

 

resources/static/js/app/index.js

var main = {
    init: function() {
        var _this = this;
        $('#btn-post').on('click', function () {
            _this.post();
        });
    },
    post : function () {
        var data = {
            name: $('#name').val(),
        }
        $.ajax({
            type: 'POST',
            url: '/home',
            dataType: 'json',
            contentType: 'application/json; charset=utf-8',
            data: JSON.stringify(data),
        }).done(function() {
            window.location.href = '/home';
        }).fail(function (error) {
            alert(JSON.stringify(error));
        });
    },
};

main.init();

javascript

 

 

 

com/practice/springboot/Application.java

package com.practice.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

 

 

 

com/practice/springboot/config/WebConfig.java

package com.practice.springboot.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(final ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**")
                .addResourceLocations("classpath:/templates/", "classpath:/static/");
    }
}

WebMvcConfigurer

  • 자식이 되려는 클래스가 웹과 관련된 처리를 하게됩니다.
  • @EnableWebMvc 어노테이션을 사용하는것보다 코드량을 간단히 쓸 수 있습니다.
  • @EnableWebMvc 어노테이션이 자동적으로 세팅해주는 @Override를 할 수 있게 됩니다.

addResourceHandlers

  • 오버라이드 메서드로 경로 설정할 수 있습니다.
  • Cache-control 가능합니다.
  • 정적 캐시(/static/...)를 지우고 싶은 경우에 캐시컨트롤로 삭제가 안되는 경우에 mac인 경우 Shift + commend + R 로 강력 새로고침으로 지우는것을 시도해 볼 수 있습니다.

 

 

 

com/practice/springboot/web/dto/RequestUserDto.java

package com.practice.springboot.web.dto;

import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class RequestUserDto {
    private String name;

    @Builder
    public RequestUserDto(String name){
        this.name = name;
    }
}

페이로드로 넘어올 Dto, VO를 생성해서 두개로 사용하는게 좋지만 간단하게 DTO 하나로 테스트

 

 

 

com/practice/springboot/config/dto/SessionUser.java

package com.practice.springboot.config.dto;

import lombok.Builder;
import lombok.Getter;

import java.io.Serializable;

@Getter
public class SessionUser implements Serializable {
    private String name;

    @Builder
    public SessionUser(String name){
        this.name = name;
    }
}

인증쪽에서 관리할 SessionUser Dto입니다.

 

 

 

com/practice/springboot/web/HomeController.java

package com.practice.springboot.web;

import com.practice.springboot.web.dto.RequestUserDto;
import lombok.RequiredArgsConstructor;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpSession;

@RequiredArgsConstructor
@RestController
public class HomeController {

    private final HttpSession httpSession;

    @PostMapping("/home")
    public RequestUserDto home(Model model, @RequestBody RequestUserDto requestUserDto) {
        httpSession.setAttribute("user", requestUserDto);
        return requestUserDto;
    }
}

Controller에서 httpSession의 속성 값을 사용할 수 있도록 RestController에서 httpSession에 속성 값을 지정하기 위하여 생성

 

 

 

 

com/practice/springboot/web/IndexController.java

package com.practice.springboot.web;

import com.practice.springboot.web.dto.RequestUserDto;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

import javax.servlet.http.HttpSession;

@RequiredArgsConstructor
@Controller
public class IndexController {

    private final HttpSession httpSession;

    @GetMapping("/")
    public String index() {
        return "index";
    }

    @GetMapping("/home")
    public String home() {
        SessionUser user = (SessionUser) httpSession.getAttribute("user");
        System.out.println("name : " + user.getName());
        return "home";
    }
}

HomeController에서 지정한 세션값을 home() 에서 확인할 수 있습니다.

 

 

여기까지 작성하였을때 session 값을 사용하는것에 문제는 없습니다.

 

하지만 2개 이상의 엔드포인트에서 httpSession.getAttribute(?) 가 반복적으로 작성될 경우에 코드가 굉장히 많아지기 때문에

어노테이션 기반으로 세션값을 가져오도록 설정하도록 합니다.

 

 

post 버튼 클릭시

 

 

 

name 출력 후 이동

 

 

 

 

 

 

 

 

 

 

 

어노테이션 기반 Session 으로 개선하기

패키지, 클래스 추가

 

 

 

 

com/practice/springboot/config/auth/LoginUser.java

package com.practice.springboot.config.auth;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginUser {

}

어노테이션 기반으로 Session 값을 받도록 어노테이션 클래스 생성

 

@Target(ElementType.PARAMETER)

  • 이 어노테이션이 생성될 수 있는 위치를 지정합니다.
  • PARAMETER로 지정했으니 메소드의 파라미터로 선언된 객체에서만 사용할 수 있습니다.
  • 이 외에도 클래스 선언문에 쓸 수 있는 TYPE 등이 있습니다.

@interface

  • 이 파일을 어노테이션 클래스로 지정합니다.
  • LoginUser라는 이름을 가진 어노테이션이 생성되었다고 보면 됩니다.

@Retention(RetentionPolicy.RUNTIME)

  • 어노테이션의 라이프 사이클 설정
    • RetentionPolicy.SOURCE
      • 컴파일러에 의해 삭제됩니다.
    • RetentionPolicy.CLASS
      • 컴파일러에 의해 클래스 파일에 기록되지만 런타임에 VM에 의해 유지될 필요는 없습니다.
    • RetentionPolicy.RUNTIME
      • 컴파일러에 의해 클래스 파일에 기록되고 런타임에 VM에 의해 유지되므로 반사적으로 읽을 수 있습니다.

 

com/practice/springboot/config/auth/LoginUserArgumentResolver.java

package com.practice.springboot.config.auth;

import com.practice.springboot.config.dto.SessionUser;
import lombok.RequiredArgsConstructor;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpSession;

@RequiredArgsConstructor
@Component
public class LoginUserArgumentResolver implements HandlerMethodArgumentResolver {

    private final HttpSession httpSession;

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        boolean isLoginUserAnnotation = parameter.getParameterAnnotation(LoginUser.class) != null;
        boolean isUserClass = SessionUser.class.equals(parameter.getParameterType());
        return isLoginUserAnnotation && isUserClass;
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        return httpSession.getAttribute("user");
    }
}

HandlerMethodArgumentResolver

  • 조건에 맞는 경우 메소드가 있다면 HandlerMethodArgumentResolver의 구현체가 지정한 값으로 해당 메소드의 파라미터로 넘길 수 있습니다.

supportsParameter()

  • 컨트롤러 메서드의 특정 파라미터를 지원하는지 판단합니다.
  • 여기서는 파라미터에 @LoginUser 어노테이션이 붙어 있고, 파라미터 클래스 타입이 SessionUser.class인 경우 true를 반환합니다.

resolveArgument()

  • 파라미터에 전달할 객체를 생성합니다.
  • 여기서는 세션에서 객체를 가져옵니다.

 

 

 

com/practice/springboot/config/WebConfig.java

package com.practice.springboot.config;

import com.practice.springboot.config.auth.LoginUserArgumentResolver;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

@RequiredArgsConstructor
@Configuration
public class WebConfig implements WebMvcConfigurer {

    private final LoginUserArgumentResolver loginUserArgumentResolver;

    @Override
    public void addResourceHandlers(final ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**")
                .addResourceLocations("classpath:/templates/", "classpath:/static/");
    }

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(loginUserArgumentResolver);
    }
}

addArgumentResolvers()

  • 사용자 지정 컨트롤러 메서드 인수 유형을 지원하는 확인자를 추가합니다.

 

 

 

/com/practice/springboot/web/IndexController.java

package com.practice.springboot.web;

import com.practice.springboot.config.auth.LoginUser;
import com.practice.springboot.config.dto.SessionUser;
import com.practice.springboot.web.dto.RequestUserDto;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

import javax.servlet.http.HttpSession;

@RequiredArgsConstructor
@Controller
public class IndexController {

    private final HttpSession httpSession;

    @GetMapping("/")
    public String index() {
        return "index";
    }
    
    @GetMapping("/home")
    public String home(@LoginUser SessionUser user) {
        System.out.println("name : " + user.getName());
        return "home";
    }
}

@LoginUser SessionUser user

  • 위의 작업으로 어노테이션 클래스가 생성되었습니다.
  • httpSession.getAttribute("user")를 반복적으로 적을 필요가 없어졌습니다.
  • 이제는 어느 컨트롤러이든지 @LoginUser를 사용하여 세션 정보를 가져올 수 있습니다.

 

 

 

post 버튼 클릭시 동일하게

 

 

 

name 출력 후 이동

 

 

 

 

 

 

 

 

 

 

 

 

 

반응형