OOP 5대 설계원칙 - SOLID
SRP 단일책임 원칙 : 하나의 메서드는 하나의 책임만 지녀야 한다.
OCP 개방폐쇄 원칙 : 확장에는 열려 있어야 하며 수정 변경에는 닫혀 있어야 한다.
LSP 리스코프 치환 원칙 : 서브타입은 항상 기반타입으로 대체 될수 있어야 한다(바꿔도 이상없이 작동해야 한다)
ISP 인터페이스 분리 원칙 : 필요한 인터페이스만 선언되어야 한다(불필요한 인터페이스 선언으로 의존성을 높이면 안된다)
DIP 의존역전 원칙 : 상위 모듈은 하위 모듈에 직접적으로 의존해서는 안된다 (둘다 추상화에 의존해야 한다)
의미 자체는 대략적으로 알겠는데 아직 직접 겪어보지 못해서 그런가 확 와닿지는 않는다..
결국에는 최대한 결합도를 낮춰서 서로 간에 의존성이 강해지면 안된다 이런 얘기 같은데
관심사 - Concern
관심사는 AOP에서도 나오지만 결국엔 시스템내에서의 여러 기능을 독립적으로 관리하기 쉽고 확장성/유지보수성을 높이기 위해 분리하는것이라고 생각하는게 편할것 같다. 여기서 분리하고자 하는 영역들을 생각해 보자면
- 관심사(특정기능)
- 변하는것, (자주)변하지 않는것 -> common/uncommon code
- 공통코드 → 같은작업을 여러번(중복)
2. 공통 코드 분리 - 입력 분리
일반적으로 프로그램은 1. 입력 2.처리 3. 출력의 로직을 가지는데
이것이 반복되니 입력부분을 별도로 분리한다
3. 출력 분리 - 변하는 것과 변하지 않는 것의 분리
모델 객체를 통해 결과를 출력할 내용을 담아서 전달
Model(데이터 전달)
View(데이터 출력)
Controller(데이터 처리)
요청 → 1.입력(Model, DispatcherServlet) ↔ 2. 처리(Controller)
↖ ↓
3. 출력
4. MVC 패턴
(1) 요청
1. 입력 & 변환
2. 모델생성
(2)
스프링에서 DispatcherServlet 동작 과정중 ViewResolver를 반환한다. 이 부분을 이전에는 그냥 기계적으로 그렇구나 하고 별 생각없이 넘어갔는데 실제 동작 하는 과정을 조금 자세히 알게 됐다.
1. 컨트롤러에 의해 뷰 이름이 반환되면
2. DispatcherServlet이 ViewResolver 설정을 기준으로(servlet-context.xml) ViewResolver를 사용해서 실제 뷰페이지를 찾는다. InternalResourceViewResolver는 스프링에서 제공하는 jsp뷰 페이지를 찾을때 사용하는 ViewResolver 클래스이고 설정해둔 접두어(prefix)와 접미어(suffix)를 조합해서 해당 뷰파일의 최종 경로를 결정한다.
servlet-context.xml
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
3. ViewResolver가 DispatcherServlet으로 뷰이름을 반환하고 DispatcherServlet에서는 반환받은 뷰이름을 가지고
View 인터페이스의 구현체 (jsp파일은 JstlView클래스)를 찾은뒤 렌더링한다.
4. JstlView 클래스가 해당 뷰에 해당하는 jsp파일을 실행하고 렌더링된 결과를 보여준다.
대부분이 인터페이스-구현클래스로 구성되어있고 느슨한 결합으로 종속되지 않게 해야 하기 때문인거 같은데.. 인터페이스가 진짜 중요하면서도 신기하다.
@컨트롤러 메서드의 반환타입에 따라서
1. String : 뷰 이름을 반환
2. void : 매핑된 url의 끝단어가 뷰이름
3. ModelAndView : Model과 뷰 이름을 반환
2번은 여태 몰랐던건데 void의 경우 반환값이 없으니 뷰페이지를 보여주는 용도로 쓸 생각조차도 못했었다 반환값없으니 당연히 뭘 보여주지? 이런 생각이었으니까 void 선언시 매핑된 url의 끝단어가 뷰이름이라는걸 알았을때 헛공부했구나 싶었고 또하나 컨트롤러에서 단순하게 URL과 뷰를 매핑시키려면 컨트롤러 클래스 없이 아래처럼 servlet-context.xml에 뷰 컨트롤러를 추가 하여 직접 매핑이 가능하다 → @GetMapping만 가능하다!
servlet-context.xml
<view-controller path="/register/add" view-name="registerForm"/>
매개변수 이름 얻는방법
1. 리플렉션 API
2. 클래스 파일 : javac -parameters : 매개변수명 저장옵션(JDK 1.8에서 추가됨) 반드시 설정 해야한다!
@WebServlet = @Controller + @RequestMapping
@WebServlet을 사용하려면 HttpServlet을 상속받아야 하는데 자바는 단일상속이기도 하고 가능하면 상속 사용은 지양..
앞에서도 공부했었지만 서블릿은 클래스단위로 매핑이 되기 때문에 클래스가 무지막지하게 많아진다
서블릿의 라이프사이클은 init, service, destroy의 세 메소드로 관리되는데 자세히 알필요는 없지만 대략적으로만 보고 체크
서블릿 기본 메소드
- init : 서블릿을 초기화 한다
- service : 클라이언트의 요청이 처리되는 부분으로 HTTP메서드(get,post)에 따라 doGet, doPost등의 메서드를 호출하여 작업을 수행한다.
- destroy : 작업 종료 후 서블릿을 제거하기 전에 호출되어 리소스를 해제한다.
Servlet-Context
클라이언트 요청이 오면 서블릿 인스턴스 존재여부를 먼저 확인하여 인스턴스가 존재 하면 응답을 하고
없으면 서블릿 클래스를 로딩한 뒤 인스턴스 생성
1. no? : 서블릿 클래스 로딩 & 인스턴스 생성 -> init() -> service() -> 응답
2. yes? : service() -> 응답
서블릿은 매번 객체 생성할 필요가 없기 때문에 기본적으로 싱글톤 패턴을 사용한다
JSP(Java Server Pages) 템플릿 엔진
JSP → 서블릿으로 변환
JSP → HTML안에 자바코드(스크립틀릿)
<%! %> : 클래스영역
서블릿은 요청이 오면 객체를 생성하기 때문에 초기화가 늦고(lazy - init) 구동시간이 느리다
스프링에서는 이러한 점을 보완하기 위해 미리 객체를 생성하여 보다 빠른 초기화가 가능(early - init)
변환
twoDice.jsp ━ ━ → twoDice_jsp.java(서블릿)
JSP 기본객체 : 생성없이 사용할 수 있는 객체(저장소 4개는 아래에 별도 작성)
pageContext, session, application, config, out, page, exception, request, response
객체명 | 설명 |
response | HTTP 응답 정보를 담고있으며 각 요청마다 생성 |
config | 서블릿 초기화 |
out | JSP의 출력 스트림을 관리 |
page | JSP 페이지 객체, 요청마다 생 |
exception | 예외정보가 담긴 객체, 예외 발생시 사용 |
8. 유효범위(scope)와 속성(attribute)
HTTP는 기본적으로 요청이 오면 요청에 대한 응답을 하고 종료된다. 이후 같은 클라이언트가 반복해서 요청을 하여 응답이 오더라도 서버는 같은 클라이언트의 요청이라고 판단하지 않으며 이러한 특성을 stateless(무상태성)라고 한다. 이러한 점 때문에 접속을 계속해서 유지해야 하는 경우 클라이언트 상태에 대한 정보가 필요하며(stateful) 이를 별도로 저장하기 위해 scope에 따른 4개의 저장소가 존재한다.
각 저장소는 key-value쌍의 형태로(Map) 값을 저장하며, 접근범위, 생존기간이 존재한다
(쓰기 : setAttribute(), 읽기 : getAttribute() )
8-1. pageContext 페이지당 1개
lv(지역변수) 기본객체 저장(request, response,...)
읽기, 쓰기 가능, <%= lv%> 사용 가능
EL에서 사용하려면 저장소에 반드시 저장 해야함(직접 사용할 수 없고 pageContext를 거쳐야한다)
8-2. application(전역) context마다 1개, WebApp 전체에서 접근 가능한 저장소(1개만 존재, 공통저장소)
8-3. session 클라이언트당 1개
서버내에 저장하는 개별저장소(클라이언트와 1:1)로써 세션객체의 개수는 사용자수(클라이언트마다 1개)와 같다.
서버내에 저장하여 편리하지만 서버의 메모리 부담이 커서 최소한의 데이터만 저장하거나, 잠깐만 저장하고 사용하도록 한다.
8-4. request 요청마다 1개
- 요청시 마다 생성(요청이 처리되는 동안만 존재, 요청마다 1)
일반적으로는 요청시 하나의 jsp가 응답을 하는데 jsp에서 다른 jsp로 넘기는 경우가 있다(foward)
ex) a.jsp에 요청을 했는데 a.jsp -> b.jsp로 넘긴다(forward)
a.jsp에서 추가적으로 보낼 데이터가 존재한다면 request에 담아서 b.jsp로 전달한다
영역 범위는 application > session > request > pageContext순으로 작아진다
Scope | 기본객체 | 설명 |
page | pageContext | JSP 페이지 내에서만 존재하며 페이지 마다 생성 |
request | request | HTTP 요청 정보를 담고 있으며 요청 처리 동안만 존재 , 각 요청마다 생성 |
session | session | 로그인~아웃동안 존재하며 클라이언트당 1개 생성 |
application | application | 프로그램이 실행되는 동안 존재하며 모든 영역에서 접근 가능(공통저장소, 1개) |
9. URL패턴 : @WebServlet -> loadOnStartup=1(우선순위) : 미리 초기화
종류(순서대로 우선순위) | URL pattern | 매칭 URL |
exact mapping (정확히 일치) | /login/hello.do | http://localhost/ch2/login/hello.do |
path mapping(경로매핑) | /login/* | http://localhost/ch2/login/ http://localhost/ch2/login/ http://localhost/ch2/login/ http://localhost/ch2/login/ |
extension mapping(확장자) | *.do | http://localhost/ch2/login/ http://localhost/ch2/login/ |
default mapping(디폴트) | / | http://localhost/ch2/login/ http://localhost/ch2/login/ http://localhost/ch2/login/ http://localhost/ch2/login/ http://localhost/ch2/login/ |
Servlet Context내에 children(서블릿)이 존재
/hello, *.jsp → 동적리소스(서블릿)
/ → 정적리소스(image, css, txt,...)
아래는 web.xml(프로젝트개별)인데 스프링에서는 모든 요청을 DispatcherServlet에서 받는다
DefaultServlet(x) → DispatcherServlet : DispatcherServlet내부에서 servletMappings가 존재
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
아래는 Tomcat web.xml(톰캣 전체설정)파일이고 DefaultServlet이 모든 요청을 처리한다.
<!-- The mapping for the default servlet -->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- The mappings for the JSP servlet -->
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.jspx</url-pattern>
</servlet-mapping>
10.EL(Expression Language)
스크립틀릿(<%= %>을 ${값}으로 표기하기 위해서 사용(간단하고 편리하게)하고 스코프객체(pageContext, request, session, application)에 접근 가능, 변수나 속성에 접근시 자동 null 체크를 수행한다
C:\STS3\proj_work\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\work\Catalina\localhost\ch2\org\apache\jsp → 변경한 jsp파일이 적용안되면 여기 폴더내 파일 제거하고 다시하면 됨
11. JSTL(JSP Standard Tag Library)
스크립틀릿(<%= %>)을 사용하면 자바 코드를 jsp페이지에 직접 삽입하게 되는데 이렇게 되면 html코드랑 jsp코드가 혼합되서 코드 가독성도 떨어지고 로직부분이랑 UI부분이 혼재되서 헷갈린다.. 이를 방지하고자 만들어진 태그 라이브러리이다
12, Filter
공통적인 요청 전처리와 응답 후처리에 사용. 로깅, 인코딩(변환,...) 등 AOP와 비슷한 개념
@RequestParam과 @ModelAttribute
@RequestParam
필수입력사항 → 예외처리 반드시