개발 일기

2024-12-17 / Controller와 RestController 차이, Cookie,Session 메소드 재정리

어떻게말이름이히힝 2024. 12. 17. 20:13

@Controller : 주로 view(html)를 반환하기 위해 사용, 메소드가 반환하는 값은 보통 뷰 이름.

@RestController : 클라이언트가 JSON 또는 XML 형식의 데이터만 필요로하는 경우 사용

 

ServletRequest : 모든 종류의 프로토콜(HTTP, FTP 등)을 처리할 수 있도록 설계된 인터페이스

- 요청 파라미터와 속성에 접근 : getParameter, getAttribute 등

- HTTP에 특화된 기능은 제공하지 않음

 

HttpServletRequest : ServletRequest를 확장한 서브인터페이스로 HTTP 프로토콜에 특화된 기능을 제공

- HTTP 헤더 정보 접근 : getHeader, getCookies 등

- HTTP 메소드(GET, POST 등)확인 : getMethod

- 요청 URI 및 URL 정보 접근 : getRequestURI, getRequestURL

- 세션 관리 : getSession

 

 

쿠키로 구현한 필터 부분

Filter 인터페이스를 받아서 doFilter 메소드를 구현해준다.

HttpServletRequest 형식으로 servletRequest를 바꿔준다.

- getCookies() : 클라이언트가 요청(Request)과 함께 보낸 모든 쿠키를 배열 형태로 반환, 요청에 쿠키가 없다면 null을 반환함. 쿠키배열을 순환하면서 getName, getValue로 쿠키의 이름과 값을 확인하고 가져올 수 있음.

 

public class LoginFilter implements Filter{

    private static final String[] WHITE_LIST = {"/","/users","/login","/logout"};

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
        String requestURI = httpRequest.getRequestURI();

        HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
                    //쿠키로 구현한 부분
        if(!isWhiteList(requestURI)) {            
            Cookie[] cookies = httpRequest.getCookies();
            if(cookies==null){
                httpResponse.sendError(HttpStatus.UNAUTHORIZED.value(),"로그인 필요");
                return;
            }
            boolean used = false;
            for(Cookie cookie : cookies) {
                if(cookie.getName().equals("userId")){
                    used = true;
                    String cookieValue = cookie.getValue();
                }
            }
            if(!used) {
                return;
            }//필드이름이 userId인 쿠키가 없으면 리턴함
        }
         filterChain.doFilter(servletRequest,servletResponse);

    }

    private boolean isWhiteList(String requestURI) {
        return PatternMatchUtils.simpleMatch(WHITE_LIST,requestURI);
    }
}

 

여기는 LoginController로

로그인하려는 요청의 값이 데이터베이스의 조회한 값이 맞으면 쿠키를 발급해준다.

로그아웃시 userId값이 null이고 소멸시간이 0인 쿠키를 클라이언트에게 반환해준다.

    @PostMapping("/login")
    public String loginByCookie(@Valid @RequestBody LoginRequestDto requestDto) {
        UserFindResponseDto loginUser = loginService.login(requestDto.getEmail(),requestDto.getPassword());
        if(loginUser==null){
            return "로그인 실패";
        }

        Cookie idCookie = new Cookie("userId",String.valueOf(loginUser.getUserId()));
        response.addCookie(idCookie);
        return "로그인 성공";

    }
    @PostMapping("/logout")
    public String logoutByCookie(HttpServletResponse response) {
        Cookie cookie = new Cookie("userId",null);
        cookie.setMaxAge(0);
        response.addCookie(cookie);
        return "로그아웃 성공";
    }

 

 

문제점 1 : Filter에서 만들어놓았던 사용자정의 Exception이 동작하지않음(GlobalExceptionHandler)

답 : GlobalExceptionHandler 는 컨트롤러 계층에서 발생하는 예외를 처리함.

 

@ExceptionHandler와 @ControllerAdvice는 기본적으로 컨트롤러 계층에서 발생한 예외만 처리할 수 있음

Filter와 같은 Servlet 레벨의 컴포넌트에서 발생한 예외는 별도의 처리가 필요함

 

나는 여기서 http 응답 객체에 에러를 넣어주는 부분을 추가했다.

다른 방법은 직접 시도해보진 않앗지만 이런게 있다 정도는 알고 있으면 좋을듯

 

1. Filter 체인 상단에 별도의 Exception Handling Filter를 추가

2. Spring Security와 연동

3. "/error" 엔드포인트로 리다이렉트


 

세션으로 구현

 

HttpSession : 이 객체를 통해 서버가 클라이언트와의 세션을 관리하고 사용자별 데이터를 저장함.

세션 ID는 쿠키(JSESSIONID) 또는 URL 재작성 방식으로 클라이언트와 교환

이후 요청에서 세션 ID를 통해 사용자를 식별하고 세션 데이터를 유지 

세션의 기본 설정 수명은 30분

- getSession() : 기존 세션이 없으면 새로운 세션을 생성함

- getSession(false) : 기존 세션이 없으면 새로운 세션을 생성하지 않고 null을 반환함

- getId() : 현재 세션의 고유 ID 반환

- getCreationTime() : 세션이 생성된 시간 반환(밀리초 단위 , Long)

- getLastAccesseTime() : 마지막으로 이 세션에 접근한 시간

- setAttribute(String name, Object value) : 세션에 데이터를 저장

- getAttribute(String name) : 지정된 이름의 데이터를 가져옴

- invalidate() : 현재 세션을 무효화하여 삭제함

- setMaxInactiveInterval(int interval) : 세션의 유효시간을 초단위로 설정

 

 

필터

public class LoginFilter implements Filter{

    private static final String[] WHITE_LIST = {"/","/users","/login","/logout"};

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
        String requestURI = httpRequest.getRequestURI();

        HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;

        if(!isWhiteList(requestURI)) {

            HttpSession session = httpRequest.getSession(false);

            if(session==null) {
                httpResponse.sendError(HttpStatus.UNAUTHORIZED.value(),"로그인 필요");
                return;
            }
       }

        filterChain.doFilter(servletRequest,servletResponse);

    }

    private boolean isWhiteList(String requestURI) {
        return PatternMatchUtils.simpleMatch(WHITE_LIST,requestURI);
    }
}

 

 

LoginController 부분

 @PostMapping("/login")
    public String loginBySession(@Valid @RequestBody LoginRequestDto requestDto, HttpServletRequest request) {
        UserFindResponseDto responseDto = loginService.login(requestDto.getEmail(),requestDto.getPassword());
        Long userId = responseDto.getUserId();

        if(userId==null){
            throw new RuntimeException("유저 아이디가 없습니다.");
        }

        HttpSession session = request.getSession();

        session.setAttribute(Const.LOGIN_USER,responseDto);

        return "로그인 성공";
    }

    @PostMapping("/logout")
    public String logoutBySession(HttpServletRequest request) {

        HttpSession session = request.getSession(false);

        if(session != null) {
            session.invalidate();
        }

        return "로그아웃 성공";
    }