묻고 답해요
141만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결스프링 핵심 원리 - 기본편
MyLogger 를 Interceptor 로 구현 시 질문
=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오) 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) 예[질문 내용]안녕하세요? 강의를 듣다가 requesURL 같은 경우 Interceptor 에서 멤버 변수로 저장하는 것이 좋다고 해서 구현을 해보려고 했는데 문제가 생겨서 질문 드립니다.## 앞으로 강좌를 계속 듣긴 할 거라서 혹시 뒤의 강좌를 듣고 나면 해결될 수 있는 문제라면 어느 강좌인지 알려주셔도 될 것 같습니다. MyLogger.javapackage hello.core.common; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.util.UUID; @Component @Scope(value = "request") public class MyLogger { private String uuid; private String requestURL; public void setRequestURL(String requestURL) { this.requestURL = requestURL; } public void log(String message) { System.out.println("[" + uuid + "]" + "[" + requestURL + "] " + message); } @PostConstruct public void init() { uuid = UUID.randomUUID().toString(); System.out.println("[" + uuid + "] request scope bean create: " + this); } @PreDestroy public void close() { System.out.println("[" + uuid + "] request scope bean close: " + this); } } CoreInterceptor.javapackage hello.core.common; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.ObjectProvider; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Component @RequiredArgsConstructor public class CoreInterceptor implements HandlerInterceptor { private final ObjectProvider<MyLogger> myLoggerProvider; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("CoreInterceptor.preHandle"); String requestURL = request.getRequestURL().toString(); MyLogger myLogger = myLoggerProvider.getObject(); myLogger.setRequestURL(requestURL); return HandlerInterceptor.super.preHandle(request, response, handler); } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { HandlerInterceptor.super.postHandle(request, response, handler, modelAndView); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { HandlerInterceptor.super.afterCompletion(request, response, handler, ex); } } WebMvcConfig.javapackage hello.core.common; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.ObjectProvider; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration @RequiredArgsConstructor public class WebMvcConfig implements WebMvcConfigurer { private final ObjectProvider<MyLogger> myLoggerProvider; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new CoreInterceptor(myLoggerProvider)).excludePathPatterns("/css/**", "/images/**", "/js/**"); } } LogDemoController.javapackage hello.core.web; import hello.core.common.MyLogger; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.ObjectProvider; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest; @Controller @RequiredArgsConstructor public class LogDemoController { private final LogDemoService logDemoService; private final ObjectProvider<MyLogger> myLoggerProvider; @RequestMapping("log-demo") @ResponseBody public String logDemo(HttpServletRequest request) { String requestURL = request.getRequestURL().toString(); MyLogger myLogger = myLoggerProvider.getObject(); myLogger.log("controller test"); logDemoService.logic("testId"); return "OK"; } } LogDemoService.javapackage hello.core.web; import hello.core.common.MyLogger; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.ObjectProvider; import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor public class LogDemoService { private final ObjectProvider<MyLogger> myLoggerProvider; public void logic(String id) { MyLogger myLogger = myLoggerProvider.getObject(); myLogger.log("service id = " + id); } } 위와 같이 이렇게 구현을 해보니 문제가 생기는데...일단, 인터셉터를 스프링 빈으로 등록하면서 MyLogger 를 활용하려면 알려주신 ObjectProvider 를 사용해야 할 것 같은데... WebMvcConfig 에서 인터셉터를 등록하는 경우, 위의 경우처럼 처음 서버 구동 시에 처리되어야 하는데 이 때는 reqeust 를 사용할 수 없는 문제입니다. 앞서 이 문제를 ObjectProvider 로 해결했는데 Interceptor 의 경우, 나중에 등록이 안 될 것 같은데 이런 경우 어떻게 해결해야 될까요?저렇게 인터셉터를 null 로 호출하니 실행해 보면 WebMvcConfig.java -> registry.addInterceptor(new CoreInterceptor(null))...java.lang.NullPointerException: Cannot invoke "org.springframework.beans.factory.ObjectProvider.getObject()" because "this.myLoggerProvider" is null위와 같은 에러가 발생합니다.관련된 소스 첨부하였습니다.도움 부탁 드립니다. 위의 소스는 제대로 된 걸로 다시 첨부하였습니다.registry.addInterceptor(new CoreInterceptor(null))...->private final ObjectProvider<MyLogger> myLoggerProvider; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new CoreInterceptor(myLoggerProvider)).excludePathPatterns("/css/**", "/images/**", "/js/**"); } 위와 같이 수정하였습니다. 이런 저런 버그들과 강의에서 가르쳐 주신 ObjectProvider 를 사용해서 설정파일에서 request 가 들어오는 시점에 로그를 주입해서 처리되도록 수정하였습니다.Interceptor 에서는 왜 ObjectProvider 를 사용하지 못할 거라고 생각했는지 모르겠네요. 답변 글 보고 질문을 수정하다가 생각이 나서 소스를 수정해 보니 잘 되는 것 같네요.감사합니다.
-
해결됨예제로 배우는 스프링 입문 (개정판)
Interceptor 도 AOP라고 볼 수 있나요?
제목 그대로 입니다! interceptor도 관점지향적이잖아요..? controller 전이나 후 preHandler, postHandler를 활용하여 공통적으로 권한체크라던지 공통 로직을 처리할 수 있는데 그럼 interceptor도 AOP인지 궁금합니다!
-
미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
안녕하세요! 질문이 있습니다.
안녕하세요! 항상 좋은 강의 감사드립니다. @ControllerAdvice 공부하다가 궁금증이 생겨 질문 드립니다. 혹시, Controller에서 발생한 예외 말고, interceptor의 preHandle(), postHandle(), afterCompletion() 메서드 내에서 발생하는 예외도 @ControllerAdvice나 @ExceptionHandler로 잡을 수 있나요??
-
미해결Slack 클론 코딩[백엔드 with NestJS + TypeORM]
interceptor
제로초님 인터셉트 사용하기 강의를 듣던중 제로초님은 intercept부분이 자동완성이 되는데 저는 아무리 해도 안돼서 그냥 직접 입력했습니다. 제가 놓치고 있는 게 있을까요??그리고 궁금한건 제가 강의를 들으면서 내가 이걸 알고 있나?? 이런 의문이 드는데 한 번은 그냥 다 보고 다시 깊이 하는 게 맞을 까요 아님 한 편 한 편 충분한 이해를 하고 가야 할까요? 아직 완전한 백엔드 부분 강의를 안들어서 제가 이렇게 생각 할 수도 있습니다..
-
미해결Vue.js 끝장내기 - 실무에 필요한 모든 것
api instance 관련되서 질문드립니다.
안녕하세요. 강사님... 좋은 강의 항상 감사합니다. 다름이 아니라 강의때 배운거 실전에 써먹어보려고 합니다. 아래 코드 처럼 인터셉터에서 access토큰이 만료가 되었을때 refresh 토큰을 통해 새로은 access 토큰을 받아오거든요. 새로은 access 토큰을 세팅한 뒤에 이전에 요청했던 url을 받아서 사용자가 요청했던 화면 끊겨 보이지 않게 이동하도록 하려고 구글에서 검색해서 아래코드처럼 사용해봤습니다. 참고했던 자료에서 axios.create 구성이 비슷해서 토큰을 연장해서 다시 재요청까지는 성공합니다. response에서 데이터도 잘 받아어구요. 그런데 화면의 데이터가 변하지 않습니다. 강의의 메인화면을 페이징 처리해서 계속 불러오는건데 response에서 데이터는 받아왔지만 제대로 화면에 나타나지 않고 있는데요.. 어디부분을 더 수정을 해야할까요? 그리고 error.response.config을 axios의 newInstance를 매배변수로 받았을때 이전 url이 다시 요청되는게 왜 그러는걸까요? 원리가 궁금합니다. 답변 부탁드립니다. 감사합니다. import store from '@/store/index'; import router from '@/routes/index'; import axios from 'axios'; import { saveAuthToCookie, deleteCookie } from '@/utils/cookies'; import { instance as newInstance } from '@/api/index'; axios.defaults.withCredentials = true; export function setInterceptors(instance) { // Add a request interceptor instance.interceptors.request.use( function (config) { // Do something before request is sent // console.log(config); config.headers.Authorization = 'Bearer ' + store.state.token; return config; }, function (error) { // Do something with request error return Promise.reject(error); }, ); // Add a response interceptor instance.interceptors.response.use( function (response) { // Any status code that lie within the range of 2xx cause this function to trigger // Do something with response data return response; }, function (error) { // Any status codes that falls outside the range of 2xx cause this function to trigger // Do something with response error const originalConfig = error.response.config; console.log('originalConfig : ', originalConfig); const status = error.response.status; const message = error.response.data.message; //토큰 만료시 refreshtoken으로 토큰 연장 if (status == 401 && message == 'TokenExpiredError') { axios .post(`${process.env.VUE_APP_API_URL}auth/refreshToken`, {}) .then(function (response) { deleteCookie('til_auth'); saveAuthToCookie(response.data); store.commit('setToken', response.data); // originalConfig._retry = true; originalConfig.headers.Authorization = `Bearer ${response.data}`; return newInstance(originalConfig); }) .catch(function (error) { console.log(error.response); alert('인증이 완료되었습니다. 로그인 해주세요'); //state 삭제 // store.commit('clearUsername'); // store.commit('clearToken'); // //쿠키값 삭제 // deleteCookie('til_auth'); // deleteCookie('til_user'); //refreshtoken 만료 또는 없을때 로그인 화면으로 이동 if (error.response.status == 500) { router.push('/login'); } }); } else if (status == 403) { console.log('403 에러'); alert('권한이 없습니다.'); } return Promise.reject(error); }, ); return instance; }
-
해결됨스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
스프링 웹 프로젝트에서 서블릿 필터, 인터셉터, AOP가 선언된 경우 AOP가 동작하는 시점에 대한 질문입니다.
안녕하세요. 스프링 MVC Part.2 강좌에서 필터와 인터셉터를 배우고나서, 서블릿 필터, 스프링 인터셉터, AOP가 모두 선언이 되어 있는 경우 AOP가 동작하는 시점에 대해 질문을 드리고자 글을 남깁니다. [강의 자료에서 가져온 필터, 인터셉터의 동작 과정] HTTP 요청 -> WAS -> 필터 -> 서블릿(디스패처 서블릿) -> 스프링 인터셉터 -> 컨트롤러 동작 과정을 보다가 문득 든 생각입니다. '그럼 AOP는 어느 구간에서 요청을 캐치해서 동작하는거지?' 예를 들어, 공통 관심 사항(메소드 실행시간 체크)을 처리하는 AOP를 @Around(핵심 기능 실행 전/후 동작)로 선언했다면 AOP가 동작하는 과정은 아래 과정이 맞을까요? HTTP 요청 -> WAS -> 필터 -> 서블릿(디스패처 서블릿) -> 스프링 인터셉터 -> AOP -> 컨트롤러 무조건 위 과정이 맞는지 아니면 공통 관심 사항을 적용하는 방법(메소드 실행 전, 후, 전+후)에 따라 바뀌는지 궁금합니다.