console 에 System.out.println() 을 통해 확인하는 것은 여러 측면에서 시스템적 비효율을 불러온다.
성능적인 측면에서, 관리적인 측면에서 좋지 않고, 시스템을 운영할 때에는 반드시 로그를 생성해야 한다. 그래야만 시스템의 문제가 어디인지, 클라이언트의 요청에 어느 시점에 잘못되었는지 알 수 있기 때문이다.
그런데 보안성에 대한 문제가 대두되면서, 확장 버전으로 2가지가 등장했다.
첫번째가 log4j2
(버전 2) 이고, 두번째가 logback
(spring boot 에서 사용) 이다.
log4j 의 설정
로그의 출력 위치를 정해주는 태그.
로그를 console에 출력할 것인지, 파일에 출력할 것인지 등을 지정할 수 있는 xml 태그이다.
appender 의 속성에 들어가는 class 타입에는 여러 가지가 존재한다.(console, file ... 등등)
org.apache.log4j.ConsoleAppender
<appender name="appender 고유 식별자" class="target에 따라 이미 지정되어있는 클래스 타입을 명시">
<param name="Target" value="System.out" />
-> param 태그의 value 를 통해 어떻게 로그를 출력할 것인지를 지정하게 된다.
<layout class="지정하려는 출력 패턴에 따라 클래스 타입이 정해져 있다.">
-> 로그의 출력 형태를 지정해줄 수 있다.
<param name="클래스 타입에 따라 입력해야 할 name 이 정해 져있다." value="출력 형태를 실제로 지정하는 부분">
</layout>
</appender>
layout 에 사용하게 되는 약자들
<logger name="log를 출력할 패키지 명시">
-> 메세지를 appender 에 전달하는 역할을 수행한다.
무조건적으로 모든 로그를 전달하는 것이 아니라, 로그의 출력 레벨에 따라서
출력 여부를 결정하게 된다.
출력 레벨은 총 6단계가 존재한다. 그 중에서 내가 설정한 레벨에 따라서 로그가 찍히게 된다.
<level value="출력하고자 하는 로그 레벨 명시">
</logger>
6단계 - FATAL : 가장 높은 level 로, 시스템에 심각한 문제가 있다는 의미이다.
5단계 - ERROR : 실행 중 error 가 발생했음을 알려주는 의미이다.
4단계 - WARN : 오류의 원인이 될 수 있는 내용을 출력하는 것
3단계 - INFO : 어플리케이션 운영 단계
에서 주로 사용하게 되는 로그
2단계 - DEBUG : 어플리케이션 개발 단계
에서 사용하게 되는 로그로 System.out 대신 사용하는 것을 권장한다.
1단계 : TRACE : DEBUG 의 상세한 내용까지 출력하는 로그
로그 레벨을 지정할 경우, 지정한 로그 이상의 로그들을 모두 출력하게 된다.
root logger 가 작동되는 형식을 정하는 태그이다. 이 중 appender-ref 태그의 속성 값으로 위에서 설정했던 appender 의 name 값을 입력해준다.
해당 어노테이션이 붙은 클래스가 Controller 역할을 하는 Bean
객체임을 나타내주는 어노테이션으로, @Component
어노테이션을 상속한 어노테이션이다.
WEB-INF
폴더는 사용자에 의해 직접적으로 접근할 수 없는 폴더이다.
jsp 를 설정 잡을 때, WEB-INF
폴더 안에 jsp 파일이 존재하도록 설정을 잡는다. 왜냐하면 외부에서 직접적으로 jsp 파일에 접근하는 것을 막기 위해서이다.
default 값으로 value 가 지정되어 있다.
@RequestMapping("url path")
// == @RequestMappring(value="url Path") 와 같은 형태이다.
하지만 value
handler 에서 return 이 안이루어지면, reqeust URL 를 이용해서 결과 View Object 를 생성하려고 한다.
즉 아무런 return 이 안이루어지면, url path 를 가지고 jsp 를 찾게 된다.
url path 만을 가지고 dispatcher servlet 에게 전달이 되고,
dispatcher servlet 은 view resolver 에 설정한 값에 따라서
prefix 와 suffix 를 url path 에 추가한 뒤에 해당 view 폴더에 jsp 파일이 있는지 확인한다.
만약 없다면 error 를 내뱉게 된다.
Mappring url 이 동일하더라도, 메소드에 따라 handler 호출을 구분할 수 있다.
일반적으로 @RequestMappring
이라는 어노테이션은 클래스 단위로 사용한다. 클래스 내부의 각 메소드에서는 ET, POST, PUT, DELETE
의 각 방식에 따른 Mapping
어노테이션을 사용할 수 있다.
또한 각각의 어노테이션을 사용할 때 역시 value="url path" 를 통해 추가적인 url path 경로를 설정해줄 수 있다.
params 로 myName 이라는 key 값을 반드시 받겠다는 형태
myName=홍길동
으로 요청한 결과, 정상적으로 호출이 이루어지는 모습.
그러나, 현 Controller 에서는 오직 params=myName 을 받는 handler 딱 하나만 존재하기 때문에, 1) myName 을 받지 않거나, 2) 오타가 났을 경우 잘못된 요청이라는 페이지가 나오게 된다.
만약 동일한 RequestMapping 에 대해, params 설정이 다른 두 메소드가 있다면 어떻게 처리될까
원래라면, myName=신사임당
이라는 vey-value 가 없다면 오류가 나는 것이 당연하겠지만, 위에 있는 메소드에 의해 실행이 이루어졌다. 실제 로그로 확인해보면 myName 로그가 나오는 것을 확인할 수 있다.
반면 myName=신사임당 을 모두 입력한 경우, 앞선 순위의 메소드로 가거나 두 개의 메소드가 실행되는 것이 아니라 하단의 메소드만 실행되는 것을 로그를 통해 확인해볼 수 있다.
다만 이러한 케이스는 절대 권장되는 케이스는 아니며, 가능한한 쓰지 말아야 할 형태이다.
기존은, 문자열을 return 하거나, 아예 return 을 안하고 dispatcher servlet 이 url url path 를 참고한 후 view resolver 를 통해 view 객체를 생성받든가의 두 가지 방식이었다.
기존의 jsp 파일에서 사용하던
jsp expreestion
<%= %>,
jsp scripter
<% %>
들은 유지, 보수가 너무 어렵다.
그 점을 개선한 방법이 아래의 형태이다.
`EL` ${변수명(key 값)} <- jsp expreestion` <%= %>,
`JSTL` ${} <- jsp scripter` <% %>
이 때, 변수명을 찾는 순서로 jsp의 4단계 scope
를 순차적으로 돌면서 변수명을 찾게 된다.
각각의 scope 들은 독립적으로 존재하는 공간이기 때문에 각 scope 들은 동일한 key 값을 가지는 케이스가 있을 수도 있다.
pageContext
객체를 가르킨다.httpReqeust
객체는 사실 requestScope를 대표하던 객체였다.servletContext
객체가 대표적인 객체이다.spring을 사용할 때 역시 일반적이 servlet 통신 시와 사용하는 객체는 거의 동일하지만, requestScope 단계에서 request 객체 뿐만 아니라 Model
객체를 하나 더 사용하게 된다.
<h1>${myName}</h1>
EL
표기법은 자체적으로 Scope 를 참조하여 변수값을 찾는다. 따라서 jsp expression 에서의 <% request.get~ %> 과 같이 scope(request 객체)를 명시하지 않아도 사용이 가능하다.
물론, EL
표기법은 page->application 순으로 순차적으로 scope 를 탐색하기 때문에 pageScope 와 requestScope 에 동일한 key 가 있다면 pageScope 의 key 가 출력된다.
이 때는 사용하고자 하는 scope 를 직접적으로 명시함으로써 원하는 scope 의 key 를 찾아갈 수 있다.
<h1>${sessionScope.myName}</h1>
EL의 내장 객체
param
: client request parameter의 이름과 값을 가지고 있는 내장 객체header
: client reqeust 의 header 정보해당 page 에서만 유효한 scope 를 가지는 객체. 해당 객체에 저장된 데이터는 해당 jsp 파일 내에서만 유효한 데이터로, 다른 페이지에서는 사용이 불가능하다.
일반적인 servlet 방식 : request.getParameter("key")
spring에서는 controller annotaion 을 사용했을 뿐, 실제 사용하는 것은 POJO, 즉 순수 자바 파일 이기 때문에 굳이 servlet 방식을 따라서 사용하지는 않는다.
@PostMapping(value="calc.do")
public ModelAndView process(@RequestBady int secondNum){
System.out.println(firstNum + secondNum);
return null;
}
@PostMapping(value="calc.do")
public ModelAndView process(
@RequestParam("firstNum") int num1
, @RequestParam("secondNum") int num2)
, @RequestParam("operator") Stirng operator) {
...
}
@PostMapping(value="calc.do")
public ModelAndView process(
@RequestParam int firstNum
, @RequestParam int secondNum
, @RequestParam Stirng operator){
...
}
@PostMapping(value="calc.do")
public ModelAndView process(int firstNum int secondNum, String operator){
...
}
@PostMapping(value="calc.do")
public ModelAndView process(String operator, User user){
...
넘어오는 parameter 가 VO 인 case 가 있을 수 있다.
이 경우, 앞부분에 생략된 부분은 ModelAttribute 라는 어노테이션이 생략되어 있다.
즉 parameter 의 데이터 타입에 따라 생략되는 어노테이션의 종류가 다르기 때문에 이 점을 알고, 가능하면 어노테이션을 사용하는 것을 권장한다.
}