Spring Web Project 구조
src/main/java/
com/
spring/
web/
controller/
users/
boards/
service/...
dao/...
vo/...
이러한 구조의 경우, 도메인이 서로 섞여있게 된다.
장점은 구조가 보기 쉽고 만들기도 쉽지만
단점은 관리가 어렵다.
도메인형 구조
src/main/java/
com/
spring/
web/
users/
controller/...
service/...
dao/...
vo/...
boards/
controller/...
service/...
dao/...
vo/...
이러한 구조의 경우, 장점으로는 분업이 굉장히 용이하고, git 으로 형상관리를 하기도 용이하다.
단점으로는 구조를 생성하고, 파악하기가 복잡할 수 있다.
@Controller
public class Controller {
@RequestMappring(value="", method=RequestMehtod.Get)
public String main(){
return "index";
}
}
@Controller
어노테이션은 @Component
어노테이션을 상속받아 구현한 어노테이션이다. 따라서 @Controller
어노테이션이 붙은 클래스 역시 Bean
객체로 등록하게 된다.
물론 @Service 어노테이션을 붙였다고 해서 무조건 ServiceModel 역할을 해야 한다는 것은 아니지만, 모든 Bean 으로 등록할 객체를 Component 어노테이션을 붙이는 것이 아니라 역할에 따라 나누어 어노테이션을 붙임으로써 명시적으로 클래스의 역할을 알려줄 수 있도록 한다.
@RequestMapping
어노테이션은 속성값 value="url 주소"
로 입력한 값(서블릿주소) 에 따라서 요청이 들어왔을 때, 해당 메소드를 실행시키도록 연결짓는다.
설정파일
applicationContext
Ben Factory
Ioc Controller
Srping DI Controller
Servlet Web ApplicationContext
모두 동일한 표현이다.
servlet-conext.xml 파일은 스프링 내에서 2개의 context 파일로 나뉘어져 있다.
sevlet web application : controller(selvet) 부분을 관리(설정))하는 applicationContext.
root web appliction : web과 상관없는, applicationContext 를 담당하는 application Context
<context:component-scan base-package="com.spring.web" />
에노테이션 설정이 가능하도록 해주는 설정으로, base-package="package" 에 입력한 패키지부터 시작해 모든 하위 패키지에서 모든 어노테이션을 정상적으로 실행할 수 있도록 한다.
sts 에서 기본적으로 생성되는 base-pakage 의 경우, controller 뿐만 아니라 모든 패키지를 다 찾게 된다. 이는 이원화된 applicationContext 의 역할을 생각할 때 좋지 않은 방법이다.
servlet-context.xml에서는 contoller 어노테이션만을 스캔하겠다는 설정
root-context.xml에서는 contoller 어노테이션을 제외한 모든 어노테이션을 스캔하겠다는 설정
ViewResolver 설정
handler(클라이언트의 요청을 처리하는.. class 혹은 method) 로부터 만약 String 이 리턴되면
String의 앞부분에 prefix 를 붙이고, 뒷부분에 suffix 를 붙여서 하나의 jsp 파일 경로를 반환한다.
만약 url 처리를 담당하는 handler 가 없다면, default-sertvlet 한테 넘어가고, 처리가 가능하다면(resources 같은 정적 reosurce에 대한 처리가 가능하다면) 처리를 진행한다.
Model
객체를 주입할 수 있다. 이 주입은 servlet-comtext.xml 설정이 객체를 주입시킨다.
Model 객체 : request 객체랑 비슷한 역할을 한다. method 내부에서 처리한 데이터를 최종적으로 응답에 전달할 때, Model 객체에 담아서 전달한다.
즉, jsp 에서 인식하게 되는 일종의 저장소 역할을 하게 된다.
DispatcherServlet 으로부터 전달된 reqeust 를 처리하는 controller(handler)
handler 는 클래스 자체를 지칭하기도 하고, 혹은 클래스 내부의 method 를 가르키기도 한다.
webapp : web home directory
resources : 정적 resource(html, css, js, image...)
servlet-context .xml : (controller 부분의) applicationContext 설정정보 파일
root-context.xml : (controller 를 제외한) apllicationContext 설정정보 파일
web.xml : dispatcherServlet 설정, filter 설정
이 모든 servlet 이 동일한 서블릿을 거친 뒤, 개별 로직을 처리하는 서블릿으로 분배시키는 구조로 만들어보자.
spring 에서는 이러한 front controller pattern 역할을 dispatcher servlet이 공통적인 부분들을 처리하게 된다.
이러한 구조의 장점은, 클라이언트의 각 요청마다 서블릿을 굳이 만들지 않아도 된다. 모든 요청은 dispatcher servlet 을 통해 공통적인 처리를 하고 난 뒤 로직 처리를 하게 되는 메소드가 실행되게 된다.
dispatcher servlert 은 handler mapping 한테 적절한 매핑을 물어본다.
handler mapping 은 mapping 된 url 주소를 가지고 메소드를 dispatcher servlet 에게 알려준다.
어떤 메소드인지 알게된 dispatcher servlet 은 다시 handler adapter 에게 해당 컨트롤러를 요청할 수 있도록 위임함
handler adapter 는 dispatcher servlet 이 위임한 대상(@controller 가 붙은..) 요청 처리를 시작한다.
주의해야 할 점은 DTO, VO 는 stateful 이기 때문에 bean 으로 관리하지 않는다.
handler mapping은 handlerMaiing 이라는 interface 이다.
그래서 스프링에서는 handler mapping 이라는 인터페이스를 구현한 여러 개의 클래스를 제공하고 있다.
그 중에서도 requestMappingHandlerMapping
이라는 구현체 클래스를 기본적으로 사용한다.
왜 기본적으로 사용하는가 하면, 이 구현체를 사용하게 되면 @RequestMappting
이라는 어노테이션을 사용할 수 잇기 때문이다.
@RequestMapping
어노테이션을 사용하게 되면, class 가 아닌 method 를 handler 로 지정할 수 있다.
handler Adapter
역시 HandlerAdaper 라는 interface 이다.
RequestMappingHandlerAdapter
라는 구현체 클래스를 사용한다.
View interface 를 구현한 객체를 지칭한다.
Model 이 가진 정보를 어떻게 표현해야 하는지에 대한 로직을 가지고 있는 component
이 View interface 를 구현한 구현체 클래스가 여러 개 존재한다.
InternalResourceView
: jsp 를 이용할 때 사용하게 되는 view 클래스로, 최종 결과 객체를 만들 수 있게 하는,
즉 dispatcher servlet 입장에서는 view rosolver 를 굳이 통하지 않고도, view 객체를 전달받을 수 있다면 문제가 없다.
즉 @controller 에서 바로 InternalResourcesView 객체를 생성해서 반환하더라도 문제가 없다.
response.sendRedirect()
를 이용해 servlet 에 재접속을 했다면, spring에서는 ModelAndView 객체를 만들어 return 한다.MashallingView : XML 형태의 view 객체를 만들어 응답한다.
MappingJackonJsonView : Json 형태의 view 객체를 만들어 응답한다.
log4j
@Controller 어노테이션
@RequestMapping 어노테이션
InternalResourceView class
Model 객체
console 에 System.out.println() 을 통해 확인하는 것은 여러 측면에서 시스템적 비효율을 불러온다.
성능적인 측면에서, 관리적인 측면에서 좋지 않고, 시스템을 운영할 때에는 반드시 로그를 생성해야 한다. 그래야만 시스템의 문제가 어디인지, 클라이언트의 요청에 어느 시점에 잘못되었는지 알 수 있기 때문이다.
그런데 보안성에 대한 문제가 대두되면서, 확장 버전으로 2가지가 등장했다.
첫번째가 log4j2
(버전 2) 이고, 두번째가 logback
(spring boot 에서 사용) 이다.
log4j 의 설정
로그의 출력 위치를 정해주는 태그.
로그를 console에 출력할 것인지, 파일에 출력할 것인지 등을 지정할 수 있는 xml 태그이다.
appender 의 속성에 들어가는 class 타입에는 여러 가지가 존재한다.(console, file ... 등등)
CnsoleAppender : 콘솔에 로그를 찍는다. org.apache.log4j.ConsoleAppender
FileAppender : 특정 파일 하나에 로그가 다 찍히게 된다.
RollingFileAppender : 다만 파일 하나에만 로그를 찍게되면 파일의 크긱가 너무나 커지게 된다. 그래서 일정 사이즈를 지정한 뒤, 그걸 넘어가면 파일을 새롭게 만드는 방식이다. 즉 파일의 용량을 제한하는 방식이다.
DailyRollingAppender : 일자 별로 log 파일을 따로따로 만들어서 저장하는 방식이다.
<appender name="appender 고유 식별자" class="target에 따라 이미 지정되어있는 클래스 타입을 명시">
<param name="Target" value="System.out" />
-> param 태그의 value 를 통해 어떻게 로그를 출력할 것인지를 지정하게 된다.
<layout class="지정하려는 출력 패턴에 따라 클래스 타입이 정해져 있다.">
-> 로그의 출력 형태를 지정해줄 수 있다.
<param name="클래스 타입에 따라 입력해야 할 name 이 정해 져있다." value="출력 형태를 실제로 지정하는 부분">
</layout>
</appender>
layout 에 사용하게 되는 약자들
%p : 로그 레벨을 같이 출력하라는 약자
%m : 로그 메세지 출력
%d : 로그 발생 시간을 출력. 형식은 yyyy- MM-dd HH:mm:ss
%c : 어떤 파일/어떤 클래스에서 로그가 발생했는지는 출력
<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 값을 입력해준다.