일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- Operating System
- OS
- 네트워크
- 해킹
- Pwnable.kr
- Lord of BOF
- webhacking.kr
- Buffer Overflow
- stack overflow
- 정보보안기사
- 웹해킹
- webhacking
- system hacking
- LOB
- wargame
- SQL
- System
- 정보처리기사 실기
- BOF
- Spring MVC
- Payload
- PWN
- pwnable
- Spring
- Spring Framework
- 정보보안기사 실기
- Shell code
- hacking
- 워게임
- 운영체제
- Today
- Total
DongDD's IT
[Spring] Spring MVC - Session, Async, 공통 처리, Locale 본문
[Spring] Spring MVC - Session, Async, 공통 처리, Locale
HTTP Session
- 여러 요청이 같은 데이터를 공유할 수 있어야함 → HTTP session을 이용해 해결할 수 있음
- @SessionAttributes, Session Scope, HttpSession API 3가지 방법이 있음
SessionAttributes
- @SessionAttributes 사용
- HttpSession API를 직접 사용하지 않고 HTTP session에서 관리
- 하나의 컨트롤러에서 여러 요청 간 데이터 공유에 효과적
1. Session에서 관리할 대상 객체 지정
1 2 3 4 5 6 | @Controller @RequestMapping("/test1") @SessionAttributes(types = TestModel.class, names = "id") public class TestController1 { // } | cs |
- types : 세션에 저장할 객체 지정
- names : 세션에 저장할 객체의 변수 지정
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | @Controller @RequestMapping("/test1") @SessionAttributes(types = TestModel.class) public class TestController1 { @ModelAttribute("testModel") public TestModel createSession() { return new TestModel(); } @RequestMapping(value = "/", method = RequestMethod.POST) public String create(@ModelAttribute("testModel") TestModel testModel) { System.out.println(testModel.getId()); System.out.println(testModel.getName()); return "aa"; } @RequestMapping(value = "/", method = RequestMethod.GET) @ResponseBody public String getId(@ModelAttribute("testModel") TestModel testModel) { System.out.println(testModel.getId()); System.out.println(testModel.getName()); return testModel.getId().toString(); } } | cs |
- @SessionAttributes를 이용해 Testmodel.class를 세션에 저장
- @ModelAttribute를 이용해 "testModel"이라는 모델에 TestModel 객체 저장
- POST 메소드와 form 형식으로 testModel을 전송하면 "testModel" 모델에 해당 값들이 저장
- 반환 값으로 "aa" 페이지와 Cookie로 sessionId 반환(JSESSIONID)
- GET 방식으로 요청 시, 세션과 모델에 저장된 "testModel"을 불러와 id를 전송
- POST 결과
- GET 결과
3. Session 삭제
1 2 3 4 5 6 | @RequestMapping(value = "/", method = RequestMethod.DELETE) @ResponseBody public String deleteSession(SessionStatus sessionStatus) { sessionStatus.setComplete(); return "complete"; } | cs |
- handler인자로 SessionStatus를 받음
- setComplete() method로 session을 delete
- 결과
Session Scope Bean
- 컨트롤러 간 데이터를 공유하는 매개체 역할
- 세션 스코프 빈 객체의 라이프 사이클은 HTTP Session 라이프 사이클과 같음
1. 세션 스코프 Model 생성
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | @Component @Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS) public class SessionModel { private Long id; private String name; public SessionModel() { } public SessionModel(Long id, String name) { this.id = id; this.name = name; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } | cs |
- @Component를 사용하여 빈 등록
- @Scope를 이용하여 Session Scope로 설정
2. Controller에서 사용
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | @Controller @RequestMapping(value = "/session") public class SessionController { @Autowired private SessionModel sessionModel; @RequestMapping(value = "/", method = RequestMethod.POST) @ResponseBody public String create(@Validated @RequestBody SessionModel model) { sessionModel = model; System.out.println(sessionModel.getId()); System.out.println(sessionModel.getName()); return "complete"; } @RequestMapping(value = "/", method = RequestMethod.GET) @ResponseBody public String getId() { return sessionModel.getId().toString(); } } | cs |
- session scope로 지정하고 bean으로 등록한 SessionModel을 @Autowired로 주입
- POST 메소드를 통해 받은 데이터를 저장
- Response에 Cookie값 전송(JSESSIONID)
- GET 메소드를 통해 저장했던 데이터를 가져옴
- 결과
Async(비동기 처리)
동작 방식
1. 비동기 실행 종료 후 응답
- Resource가 많이 필요한 작업의 경우, 분리된 스레드에서 해당 작업을 수행하게 함
- 비동기 처리 후, 응답이 가기때문에 Client에는 동기와 비슷하게 느껴짐
1) MVC의 스레드에서 처리
- Callable, WebAsyncTask 타입 반환
2) MVC외의 스레드에서 처리
- DeferredResult, ListenableFuture, CompletableFuture 타입 반환
2. 비동기 실행 중 응답
- 임의의 타이밍에 데이터를 전송하기 싶은 경우에 사용
- 비동기 처리 시점에 응답을 한번한 후에 처리 중 응답 데이터를 전송
- Client가 'Transfer-Encoding: chunked'를 지원해야함
1) Long Polling 비동기 처리
- ResponseBodyEmitter 타입 반환
2) SSE 비동기 처리
- SseEmitter 타입 반환
- Client가 SSE를 지원해야함
- SSE(Server Sent Event)(12,b) : Client가 요청없이 서버로부터 자동으로 Data를 받을 수 있음
Async
- method에 @Async를 사용하여 비동기 처리를 하도록 만듬
- 같은 클래스에 있는 method에 @Async 사용 시, 동작하지 않음
1. Bean 정의
- @Async 사용을 위한 Bean 정의
1 2 3 4 5 6 7 8 9 10 11 12 | @Configuration @EnableAsync public class AsyncConfig { @Bean public TaskExecutor taskExecutor() { ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor(); threadPoolTaskExecutor.setCorePoolSize(3); threadPoolTaskExecutor.setMaxPoolSize(5); threadPoolTaskExecutor.setQueueCapacity(10); return threadPoolTaskExecutor; } } | cs |
- @Configuration을 설정하고 @EnableAsync로 @Async 활성화
- TaskExecutor를 Bean으로 지정해 요청마다 thread pool을 생성
- setCorePoolSize : 기본 thread pool size
- setMaxPoolSize : 최대 thread pool size
- setQueueCapacity : MaxPoolSize로 동작하는 경우, 대기하는 queue size
2. Async 처리 후 응답
1) Controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | @RestController @RequestMapping(value = "/async") public class AsyncController { @Autowired private AsyncService asyncService; @RequestMapping(value = "/", method = RequestMethod.GET) public CompletableFuture<String> get() { System.out.println(Thread.currentThread().getName()); try { return asyncService.sleep(); } catch (InterruptedException e) { e.printStackTrace(); } return null; } } | cs |
- AsyncService를 Autowired로 주입받고 해당 service에 있는 sleep method 실행
- 반환 값으로 CompletableFuture를 사용하고 최종적으로 보내질 응답 값이 String이기 때문에 Completable<String> 반환
- return asyncService.sleep()으로 service 수행
2) Service
1 2 3 4 5 6 7 8 9 | @Service public class AsyncService { @Async public CompletableFuture<String> sleep() throws InterruptedException { Thread.sleep(3000); System.out.println(Thread.currentThread().getName()); return CompletableFuture.completedFuture("complete"); } } | cs |
- Thread.sleep(3000)으로 3초를 기다린 후, CompletableFuture<String> 반환
- 다른 thread에서 수행되지만, 해당 thread의 return 값을 기다린 후, 응답 값을 보냄
3. SseEmitter
- SseEmitter에 send를 통해 client의 요청없이 server에서 지속적으로 data를 내려줌
- AsyncController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | @RestController @RequestMapping(value = "/async") public class AsyncController { @Autowired private AsyncService asyncService; @RequestMapping(value = "/", method = RequestMethod.GET, produces= MediaType.TEXT_EVENT_STREAM_VALUE) public SseEmitter sseEmitter() { SseEmitter sseEmitter = new SseEmitter(); asyncService.emit(sseEmitter); return sseEmitter; } } | cs |
- SseEmitter를 반환 값으로 함
- SseEmitter를 생성하고 AsyncService의 emit method 실행
- AsyncService - emit
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | @Service public class AsyncService { @Async public void emit(ResponseBodyEmitter emitter) { try { emitter.send("aaa"); Thread.sleep(2000); emitter.send("bbb"); Thread.sleep(2000); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } emitter.complete(); } } | cs |
- "aaa"를 보내고 2초간 sleep
- "bbb"를 보내고 2초간 sleep 후 complete를 이용해 emitter 종료
- 결과
- spring mvc에서 SseEmitter 사용 시, String 변환에 문제가 있었음
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | @Configuration @EnableWebMvc @ComponentScan("com") public class WebMvcConfig implements WebMvcConfigurer { @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { converters.add(jackson2HttpMessageConverter()); converters.add(stringHttpMessageConverter()); } @Bean public StringHttpMessageConverter stringHttpMessageConverter() { return new StringHttpMessageConverter(); } } | cs |
- StringHttpMessageConverter Bean을 생성하고
- converter에 추가하여 해결
Async 공통 처리
- CallableProcessingInterceptor, DeferredResultProcessingInterceptor 구현체를 이용
1. method
- beforeConcurrentHandling : Async 실행 전 호출
- preProcess : Async 실행 후 호출
- postProcess : Async 처리 결과/예외 후 호출
- handleTimeout : Async가 Timeout되면 호출
- afterCompletion : Async 종료 시 호출
- handleError : Async Error 발생 시 호출
2. Interceptor 작성
1) CallableProcessingInterceptor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | public class AsyncCallable implements CallableProcessingInterceptor { @Override public <T> void beforeConcurrentHandling(NativeWebRequest request, Callable<T> task) throws Exception { } @Override public <T> void preProcess(NativeWebRequest request, Callable<T> task) throws Exception { } @Override public <T> void postProcess(NativeWebRequest request, Callable<T> task, Object concurrentResult) throws Exception { } @Override public <T> Object handleTimeout(NativeWebRequest request, Callable<T> task) throws Exception { return null; } @Override public <T> Object handleError(NativeWebRequest request, Callable<T> task, Throwable t) throws Exception { return null; } @Override public <T> void afterCompletion(NativeWebRequest request, Callable<T> task) throws Exception { } } | cs |
2) DeferredResultProcessingInterceptor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | public class AsyncDeferred implements DeferredResultProcessingInterceptor { @Override public <T> void beforeConcurrentHandling(NativeWebRequest request, DeferredResult<T> deferredResult) throws Exception { } @Override public <T> void preProcess(NativeWebRequest request, DeferredResult<T> deferredResult) throws Exception { } @Override public <T> void postProcess(NativeWebRequest request, DeferredResult<T> deferredResult, Object concurrentResult) throws Exception { } @Override public <T> boolean handleTimeout(NativeWebRequest request, DeferredResult<T> deferredResult) throws Exception { return false; } @Override public <T> boolean handleError(NativeWebRequest request, DeferredResult<T> deferredResult, Throwable t) throws Exception { return false; } @Override public <T> void afterCompletion(NativeWebRequest request, DeferredResult<T> deferredResult) throws Exception { } } | cs |
3. Config 추가
1 2 3 4 5 6 7 8 9 10 11 | @Configuration @EnableWebMvc @ComponentScan("com") public class WebMvcConfig implements WebMvcConfigurer { @Override public void configureAsyncSupport(AsyncSupportConfigurer configurer) { configurer.registerCallableInterceptors(new AsyncCallable()); configurer.registerDeferredResultInterceptors(new AsyncDeferred()); } } | cs |
- 작성한 interceptor들을 등록
공통 처리
- Controller의 handler 호출 전후의 공통 처리
Servlet Filter
- DispathcerServlet 호출 전후의 공통 처리
- Spring에서 GenericFilterBean, OncePerRequestFilter 지원
1 2 3 4 5 6 7 8 9 | public class CustomFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // implement } } | cs |
- OncePerRequestFilter를 상속받아 구현
HandlerInterceptor
- Controller에서 처리되는 내용들 중 공통 처리가 필요할 때 사용
- 요청 경로에 대한 handler method 결정된 후 호출
1. method
- preHandle : handler method 실행 전 호출
- postHandle : handler method 정상 종료 후 호출
- afterCompletion : handler method 종료 후 호출
2. interceptor 생성
1 2 3 4 5 6 7 8 9 10 11 | public class CommonInterceptor extends HandlerInterceptorAdapter { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HandlerMethod handlerMethod = (HandlerMethod) handler; Method method = handlerMethod.getMethod(); System.out.println(method.getName()); return super.preHandle(request, response, handler); } } | cs |
- HandlerInterceptorAdapter를 상속받아 구현
- preHandle을 이용하여 handler 실행 전에 실행
3. Interceptor 등록
1 2 3 4 5 6 7 8 9 | @Configuration @EnableWebMvc @ComponentScan("com") public class WebMvcConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new CommonInterceptor()); } } | cs |
- webmvc config 파일에 interceptor 추가
- 결과
ControllerAdvice
- Controller에 대한 특수한 method를 정의할 수 있음
- 일반적으로 ExceptionHandler 용도로 사용
1. RestControllerAdvice
1 2 3 4 5 6 7 8 | @RestControllerAdvice public class TestExceptionHandler { @ExceptionHandler(value = TestException.class) public ResponseEntity handleException(TestException e) { return ResponseEntity .status(e.getHttpStatus()) .contentType(MediaType.APPLICATION_JSON_UTF8) .body(e.getMsg()); } } | cs |
- 지난 번에 사용했던 exception, exception handler 사용
2. Option
- annotations : 해당 annoation이 붙은 controller에 대한 처리
- assignableTypes : 지정한 클래스나 인터페이스로 할당 가능한 controller에 대한 처리
- value : basePackages와 같음
- basePackages : package 경로 내에 있는 controller에 대한 처리
- basePackageClasses : 지정한 클래스나 인터페이스가 저장된 package 경로 내에 있는 controller에 대한 처리
Locale
- 웹 서비스에서 다국어를 지원하고 싶을 때 사용
LocaleResolver
- LocaleResolver의 구현 클래스를 이용해 locale 사용
1. 구현 클래스
- AcceptHeaderLocaleResolver : HTTP request의 Accept-Language 헤더 정보 이용
- SessionLocaleResolver : HTTP session의 locale 정보 이용
- CookieLocaleResolver : Cookie의 locale 정보 이용
- FixedLocaleResolver : Server의 Locale 설정을 따름(LocaleResolver default -> JVM -> OS)
2. Locale 사용
1 2 3 4 5 6 7 8 | @RestController @RequestMapping(value = "/locale") public class AsyncController { @RequestMapping(value = "/", method = RequestMethod.GET) public String locale(Locale locale) { return locale.getLanguage(); } } | cs |
- handler의 인자로 Locale을 받아서 사용
- 결과
'프로그래밍 > Spring' 카테고리의 다른 글
[Spring] Spring Security - 인증, 인가, CSRF, Test (0) | 2019.04.20 |
---|---|
[Spring] Spring Test (0) | 2019.04.20 |
[Spring] Spring REST (0) | 2019.04.14 |
[Spring] Spring Web Application, Controller, Exception Handler (0) | 2019.04.02 |
[Spring] Spring MVC - MVC, IntelliJ 설정, 프로젝트 생성 (0) | 2019.03.28 |