반응형

서블릿은 다음 2가지 방식으로 예외 처리를 지원한다.
1. Exception(예외) - 500처리 
2. response.sendError(HTTP 상태코드, 오류 메시지)
->직접 오류메시지 담아서 처리

1.Exception
먼저 스프링부트가 제공하는 기본 예외페이지를 꺼둔다.
- application.properties

server.error.whitelabel.enabled=false
package hello.exception.servlet;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Slf4j
@Controller
public class ServletExController {

    @GetMapping("/error-ex")
    public void errorEx(){
        throw new RuntimeException("예외발생!");
    }
}

실행 결과:

HTTP Status 500 – Internal Server Error
:서버 내부에서 처리할 수 없는 오류 

exception이 WAS까지 전달되면 서버에서 처리할 수 없는 예외로 간주하고 500으로 상태코드를 만들어서 반환해주고 페이지를 뿌려준다. 

exception터진건 무조건 500오류로 나간다. 


response.sendError(HTTP 상태코드, 오류메시지)
: 오류가 발생할때 HttpServletResponse가 제공하는 sendError라는 메서드 사용할 수 있다. 

@GetMapping("/error-404")
public void error404(HttpServletResponse response) throws IOException {
response.sendError(404, "404 오류!");
}
@GetMapping("/error-500")
public void error500(HttpServletResponse response) throws IOException {
response.sendError(500);
}

sendError흐름

WAS(sendError 호출 기록 확인) <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러

response.sendEroor()호출하면 response내부에는 오류가 발생했다는 상태를 저장해둔다.
그리고 서블릿 컨테이너는 고객에게 응답전에 response에 sendError()가 호출되었는지 확인한다.
만약 호출되었다면 설정한 오류고드에 맞추어 기본 오류페이지를 보여준다.

 

*본 게시물은 인프런 스프링MVC2편 - 백엔트 웹 개발 활용 기술 강의를 토대로 작성했습니다.

반응형

서블릿 필터와 마찬가지로 웹과 관련된 공통 관심사항을 해결하는 기술이다.
필터는 서블릿이 제공하는 기술인 반면, 인터셉터는 스프링 MVC가 제공하는 기술.
둘다 웹관련 공통 관심사항을 처리하지만, 적용되는 순서와 범위, 사용방법이 다르다.

*스프링 인터셉터 흐름
HTTP요청 -> WAS -> 필터 -> (디스페처)서블릿 -> 스프링 인터셉터 -> 컨트롤러

*스프링 인터셉터 제한
HTTP요청 -> WAS -> 필터 -> (디스페처)서블릿 -> 스프링 인터셉터 -> 컨트롤러 // 로그인 사용자
HTTP요청 -> WAS -> 필터 -> (디스페처)서블릿 -> 스프링 인터셉터(적절치 않은 요청이라 판단, 컨트롤러 호출X) // 비 로그인 사용자

*스프링 인터셉터 체인
HTTP요청 -> WAS -> 필터 -> (디스페처)서블릿 -> 인터셉터1 -> 인터셉터2 -> 컨트롤러 
: 필터와 마찬가지로 중간에 인터셉터 자유롭게 추가할 수 있다. 

public interface HandlerInterceptor {
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {}
    
    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {}
   
    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {}
}

preHandle: 컨트롤러 호출 전, 호출된다. 응답값이 true면 다음으로 진행하고, false면 진행하지 않는다.
postHandle: 컨트롤러 호출 후
afterCompletion: http 요청이 완전히 끝날때, 뷰가 랜더링 된 이후에 호출된다. / 예외가 발생해도 호출된다.

예외가 발생하면 postHandle은 호출되지 않으므로 예외와 무관하게 공통처리를 하려면 afterCompletion()을 사용해야한다. 

 

 

 

 

 

반응형

필터: 서블릿이 지원하는 '수문장'이다.

필터흐름
HTTP요청 -> WAS(서버) -> 필터 -> 서블릿 ->컨트롤러 

* 필터는 특정 URL피턴에 적용할 수 있다. /* 라고 하면 모든 요청에 필터가 적용된다.
* 스프링을 사용하는 경우의 서블릿은 스프링의 디스패쳐 서블릿이다. 

필터 제한
HTTP요청 -> WAS(서버) -> 필터 -> 서블릿 ->컨트롤러 //로그인 사용자
HTTP요청 -> WAS(서버) -> 필터(적절치 않은 요청이라 판단, 서블릿 호출X) //비 로그인 사용자

필터 체인
HTTP요청 -> WAS(서버) -> 필터1-> 필터2-> 필터3-> 서블릿 ->컨트롤러
: 중간에 필터를 자유롭게 추가할 수 있다. 
 EX) 로그를 남기는 필터를 먼저 적용하고 로그인 여부를 체크하는 필터 만들 수 있다. 

 

 

반응형

쿠키값은 임의로 변경될 수 있고
쿠키에 보관된 정보는 해킹될 수 있기에 

서버에 중요한 정보를 보관하고 연결을 유지하는 방법, 즉 세션으로 로그인처리해야 한다. 

동작방식:
1. 사용자가 로그인, 비밀번호 정보를 저장하면 서버에서 해당 사용자가 맞는지 확인한다.
2. 그럼 서버에서 추정불가능한 랜덤값, 즉 'UUID'를 만든다.
3. 서버는 세션 저장소를 만들고 그 값(UUID)을 토큰 키(sessionId)로 사용한다. 
4. 값(value)은 회원객체로 매핑한다.
5. 그렇게 서버에 sessionId와 value가 저장된다. 세션아이디를 알면 회원 객체를 꺼낼 수 있다.  

6. 서버는 클라이언트에 'sessionId만 쿠키에 담아서 전달한다.  
7. 클라이언트는 쿠키저장소에 sessionId 정보가 담긴 쿠키를 보관한다.  

*회원과 관련된 정보는 전혛 클라이언트에 전달되지 않는다.
*추정 불가능한 세션id만 쿠미를 통해 클라이언트에 전달한다.

8. 웹브라우저가 특정페이지 들어가면 쿠키를 전달하는데, sessionId 정보를 서버로 전달한다.
9. 서버에서는 세션저장소를 뒤져서 키(sessionId)에 맞는 value(회원객체)가 있는지 확인한다.

반응형
public: 아무런 제약 없이 모든 접근이 허용된다.
protected: 같은 패키지의 객체와 상속 관계에 있는 객체까지만 접근이 허용된다.
default:  해당 패키지 내에서만 접근이 허용된다.
private: 해당 class 내에서만 접근이 허용된다. 

 

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
32
33
 
public class AccessModifier {
    
    public static void main(String[] args) {
        
        Wallet myWallet = new Wallet(10000);
        
        myWallet.myMoney -= 2200;
        
        myWallet.checkMyMoney();
    }
}    
    
    class Wallet {
        int myMoney;
        
        public Wallet(int myMoney) {
            this.myMoney = myMoney;                    
        }
        
        public void pay(int payMoney) {
            this.myMoney -= (int)(payMoney *1.1);
        }
        
        public void  checkMyMoney() {
            System.out.println("남은 돈: " + this.myMoney);
        }
        
    }
 
 
 
 
cs

8라인을보면 pay메소드를 활용하지 않고 직접 myMoney에 접근해서 시스템에 의한 지불이 아닌 임의의 지불을 처리했다.
처리되는건 맞지만 시스템에 의한 지불처리가 아니기 때문에 myMoney의 정보를 신뢰할 수 없다. 

그렇기 때문에 pay메소드를 통해 처리되게 해야한다. 
Wallet의 멤버 변수 myMoney는 클래스 내부에서만 접근할 수 있어야한다. 그래서 접근 제어자로 'private'을 사용하여
해당 클래스 내에서만 접근할 수 있게 한다. 
생성자나 pay메소드, checkMyMoney메소드는 외부에서 접근할 수 있도록 public 으로 한다. 

 

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
 
public class AccessModifier {
    
    public static void main(String[] args) {
        
        Wallet myWallet = new Wallet(10000);
        
        //myWallet.myMoney -= 2200;
        myWallet.pay(2200);
        
        myWallet.checkMyMoney();
    }
}    
    
    class Wallet {
        private int myMoney;
        
        public Wallet(int myMoney) {
            this.myMoney = myMoney;                    
        }
        
        public void pay(int payMoney) {
            this.myMoney -= (int)(payMoney *1.1);
        }
        
        public void  checkMyMoney() {
            System.out.println("남은 돈: " + this.myMoney);
        }        
    }
 
 
cs

 

남은 돈: 7580

                                                                          

+ Recent posts