복습
1. request
2. response
3. out
4. session
- 통로 : 두 피어 사이에 유일하게 개방된 연결, 하지만 브라우저 이용하며 서버와 나사이의 연결 끊으라고 한적 없기때문에 웹에서는 애매한 의미
- 기간 : 한 클라이언트가 하나의 브라우저를 이용해서 어플리케이션 사용 시작 이벤트를 발생시킨 후, 종료 이벤트를 발생시킬 때까지의 기간
세션의 생명주기
- 클라이언트의 최초요청(ⓐ)으로 인해 세션이 하나 만들어지고, ID가 부여된다.
- 서버에서 클라이언트로 응답데이터(ⓑ)를 내보낸다.
- 이 때 응답데이터(ⓑ)에 세션ID가 숨어서 함께 나간다
- ID는 클라이언트가 직접 필요로 하는 데이터가 아니기때문에, 바디가 아닌 헤더에 담겨 나간다 (setCookie)
- 브라우저가 응답데이터(ⓑ)를 받아 헤더의 cookie를 확인한 후 자신의 쿠키 저장소에 쿠키를 보관한다
- 쿠키저장소는 브라우저마다 따로따로 가지고 있다.
- 새로운 요청(ⓒ)이 발생할 때, 쿠키는 ID로 리퀘스트의 헤더에 함께 전송된다.
- 요청을 받은 WAS가 요청(ⓒ)에 ID가 포함되어있다는 것을 알고 최초요청이 아니라는 것을 판단한다. 그 후 서버가 가지고 있던 세션ID들과 비교하여, 요청이 특정 세션에서 발생한 요청인 것을 파악한다.
- 이를 통해 클라이언트와 서버 사이에 세션ID라는 공통점이 생기게 된다 -> 상태가 유지될 수 있는 구조가 만들어짐
상태 유지하기 위한 방법의 차이점: 세션은 서버에 쿠키는 브라우저(클라이언트)에!
예시: 비회원의 멜론 재생목록
- 로그인 하지 않은 상태에서, 재생목록을 만들어놓고 나중에 다시 방문했을 때 그대로 재생목록이 남아있게 하려면 어떻게할까?
- db에 저장하는 방법이 가장 쉽지만 부하 걸리기 쉬움
- -> 브라우저의 쿠키에 저장할 수 있음!
- 쿠키에 저장하는 것의 문제점
- 1) 재생목록은 단순한 문자열이 아닌데 쿠키에 문자열로 어떻게 저장해?
- -> json데이터로 마셜링,직렬화 후 이렇게 저장된 저장목록을 서버에서 역직렬화, 언마셜링하여 복원함
- 2) 한 사이트에서 동시에 저장할 수 있는 쿠키의 갯수와 용량은 제한되어있음
- 3) 클라이언트가 가지고있는 정보이기때문에 클라이언트가 언제든지 지울수있음 (언제까지 살아있으리라는 보장을 할 수 없음)
- 4) 보안에 취약함 (누구나 볼 수 있음)
세션 트래킹모드로서 쿠키의 단점
- 한번 로그인을 하면 브라우저 쿠키에 세션ID가 들어있게됨 = 브라우저 쿠키를 지우게 되면 세션 유지가 불가능함
- 서버사이드에서는 이때 발생한 요청을 새로운 요청으로 판단하고 새로운 세션id가 만듬
5. Servlet Context
- 서블릿 컨텍스트: 서블릿이 운영되고 있는 환경
- 서블릿 컨텍스트 = 어플리케이션 정보 + 서버정보
- 어플리케이션 없이 서블릿 존재할수 없음!
- 서버 없이 어플리케이션 존재할 수 없음!
- context: 한 컴포넌트가 운영되고 있을 때 그 컴포넌트가 운영되고있는 환경에 대한 정보
- Servlet Context 사용 목적
- dynamic web module version 확인 (서블릿스펙정보)
- 서버에 직접적인 로그 남기기
- ★★ web resource 확보
- 모든 자원은 파일의 형태로 존재하기때문에, 파일에 접근하기 위해서는 원칙적으로 파일시스템상 경로가 필요함
- 하지만 서버가 갖는 독베이스가 어디냐에 따라 파일 시스템상 경로는 다르기때문에 파일시스템 상 절대경로를 알 수없음
- 절대경로를 알기위해서는 독베이스 기반으로 계산해서 알아야함
- ServletContext는 이를 위한 메서드를 지원해줌 => getRealPath, getResource, getResourceAsStream
6. page
7. config
8. exception
9. pageContext ★★★
- 나머지 기본객체에 대한 getter를 가졌기때문에 중요
- pageContext 하나 있으면 다 꺼내쓸수있다~!!!
pageContext.getRequest()
((HttpServletRequest) pageContext.getRequest()).getContextPath()
${pageContext.request.contextPath}
- getter라는 기능이 없다면 1) scope제어 2) 흐름제어 3) 에러데이터 확보라는 기능도 있을 수 없음
Scope
- scopeDesc.jsp
- SCOPE(영역): 속성(attribute) 데이터를 공유하기 위해 정의된 4가지 저장 영역
- 모든 객체의 관리권한은 컨테이너만 가지고있다
- -> 전역변수를 서로간에 접근할 수 없다
- -> 단점보완하는것이 스코프
- 예시
- ImageListServlet.java
- CalculateServlet.java
- 요청은 서블릿이 받고 응답은 jsp에서 나가는 모델2구조jsp에 데이터 전달하기 위해,
req.getRequestDispatcher(view).forward(req, resp);
방식 이용 -> 가장 최소한의 영역 스코프 이용
- 스코프는항상 생명주기와 연계해서 이야기가 되어야함
1. page Scope
- pageContext 라는 기본객체가 관리하는 맵
- 한 페이지 내에서만 공유되는 영역
- 한 페이지에서만 쓸 수 있기 때문에 공유의 의미에서 살짝 벗어나 있음
- 커스텀태그 만들 때 주로 사용함
2. request Scope
- request 가 관리하는 Map<String, Object>
- request와 생명주기 동일
- ⓐ ---리다이렉트---> ⓑ : request사라짐, 이때는 request Scope보다 큰 영역 필요
3. session Scope
- seesion 이 관리하는 Map<String, Object>
- session과 생명주기 동일
- 한 사람의 유저가 자기만의 데이터를 넣기 위한 저장 공간
- 서로의 세션 스코프에 접근할 수 없음
4. application Scope
- servletContext가 관리하는 Map<String, Object>
- servletContext와 생명주기 동일
- 하나의 어플리케이션은 사용하고있는 모든 클라이언트가 동시에 접근할 수 있는 영역
- 가장 큰 공유범위이기때문에 '부하'를 신경써서 접근해야함
- 경우에 따라서는 스코프에 속성 데이터를 넣을 수도 있지만, 부하를 줄이기 위해서는 속성데이터를 지울 수도 있음 (removeAttribute)
객체 생성 방법
점층적 생성자
JavaBean
- ValueObject, DataTransferObject, Model: 한가지 객체의 상태를 온전히 담기 위한 것
- JavaBean 규약에 따라 정의된 재사용 가능한 객체
- JavaBean 요건
- 1) 상태 유지를 위한 property
- 2) 캡슐화
- 3) 캡슐화된 property에 접근하기 위한 인터페이스 제공
- 4) 상태비교 방법 제공 (equals)
- 캡슐화 되어있는 상태정보를 가지고 있는 객체가 여러개라면, 상태 비교를 통해 동일객체를 판단할 수 있어야함
- 5) 상태확인 방법 제공 (toString)
- 6) 상태기록 방법 제공 (serialize)
- 계층형 아키텍쳐(layered architecture ≒ mvc)
- layer: controller - service - dao처럼 하나의 역할을 가지고 일하는 애들
- layer 간에 데이터 이동이 있어야함
- 낱개의 데이터를 하나의 객체로 만들 때 vo를 사용
Builder패턴
- private으로 묶인 전체 생성자가 필요
- private인 생성자를 쓸 수 있는 이너클래스 필요
- 반드시 빌더에는 build라는 메서드가 하나 있어야함
- 빌더 안에 빌드의 대상이 되는 객체와 같은 상태인 property가 있어야함
- 마치 setter의 역할을 가지고 있는 것처럼 현재 가지고 있는 상태와 이름이 동일한 메서드를 완성, 그리고 그 메서드 안에서 상태를 결정한다. (return 타입이 존재해서 체인구조가 가능)
bts 페이지 문제
구성
- btsForm.jsp
- bts멤버의 목록을 클라이언트에게 제공 (목록 보여주는 ui)
- 클라이언트가 한명을 선택하고나면 그다음 화면이 해당 멤버의 개인페이지가 보여야함
- BtsServlet.java
- 요청받는 서블릿
- 누구의 개인페이지로 갈것인지 요청 처리해서 해당 멤버jsp로 보내줘야함
조건
- ui구성 단계에서부터 모델2구조 사용할것
- btsForm 으로 올때 get, btsForm이 전송될때 post -> 서블릿 1개로 해결
- bts데이터는 맵으로 관리하여 서블릿에서 jsp로 보내 꺼내쓸 수 있도록 만들기
해결
- btsDB를 위한 map을 init시 제일 먼저 만들기 위해 WebServlet 어노테이션에 속성값 부여 loadStartup
- loadonstartup = -1: 최초요청이 들어와야 생성됐던 것
- loadonstartup = 1 : 바로 생성ㄱ
@WebServlet(urlPatterns="/bts", loadOnStartup=1)
public class BtsServlet extends HttpServlet{
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
System.out.println(this.getClass().getSimpleName()+"초기화됨.");
Map<String, BtsVO> btsDB = new LinkedHashMap<>();
getServletContext().setAttribute("btsDB", btsDB);
btsDB.put("B001", BtsVO.getBuilder().code("B001").name("뷔").url("/bts/bui.jsp").build());
btsDB.put("B002", BtsVO.getBuilder().code("B002").name("제이홉").url("/bts/jhop.jsp").build());
btsDB.put("B003", BtsVO.getBuilder().code("B003").name("지민").url("/bts/jimin.jsp").build());
btsDB.put("B004", BtsVO.getBuilder().code("B004").name("진").url("/bts/jin.jsp").build());
btsDB.put("B005", BtsVO.getBuilder().code("B005").name("정국").url("/bts/jungkuk.jsp").build());
btsDB.put("B006", BtsVO.getBuilder().code("B006").name("RM").url("/bts/rm.jsp").build());
btsDB.put("B007", BtsVO.getBuilder().code("B007").name("슈가").url("/bts/suga.jsp").build());
}
}
과제
1. model2 구조로 webStudy01 컨텍스트 익스플로러 구현
2. 회원가입
- email, 이름, 패스워드 입력받아서 crud 모듈 완성
- 모델1이든 모델2든 상관없음
- db는 사용하지말되 layered architecture 사용해볼것
- vo 설계
- 왜 db대신 쓰는 애를 map으로 쓸까?? map이 뭐가 이뻐서..?
어떻게 풀까나
- 모델2
- 회원가입양식 jsp
- 가입양식 form, post 로 action은 해당 url로 매핑된 서블릿에서 doPost로 받아줄것
- jsp 서블릿 둘다 클라이언트, 서버 밸리데이션
- 가입처리하는 서블릿
- 성공시? redirect 로 웰컴페이지
- 실패시? forward로 다시 가입양식페이지, 남겼던 정보가 그대로 보여야겠지? -> scope 범위?
- vo: email, 이름, 패스워드 (pk를 뭘로잡을건지?)
- 빌더 패턴활용
- application scope
- 중복체크에도 써야겠지
- layered architecture?
- 각 기능, 책임 분리
- dao, service 전부 나누라는 말씀이실까
- util이나 메서드로 빼는정도만 해도되지않을까
- 신경써야할것
- 책임분리, 밸리데이션, scope, 흐름제어, vo
숙제용 프로젝트 복사 시 설정변경
- xml설정 webStudy01부분을 homework로 바꿔줄 것 (컨텍스트패스 중복 방지)
<?xml version="1.0" encoding="UTF-8"?><project-modules id="moduleCoreId" project-version="1.5.0">
<wb-module deploy-name="webStudy01">
<wb-resource deploy-path="/" source-path="/WebContent" tag="defaultRootSource"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/res"/>
<property name="context-root" value="webStudy01"/>
<property name="java-output-path" value="/webStudy01/build/classes"/>
</wb-module>
</project-modules>
<?xml version="1.0" encoding="UTF-8"?><project-modules id="moduleCoreId" project-version="1.5.0">
<wb-module deploy-name="homework">
<wb-resource deploy-path="/" source-path="/WebContent" tag="defaultRootSource"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/res"/>
<property name="context-root" value="homework"/>
<property name="java-output-path" value="/homework/build/classes"/>
</wb-module>
</project-modules>
3. 싱글톤을 thread-safety하게 만드는 방법
4. 어댑터 패턴 조사
- FileUploadRequestWrapper 참고
- 첫번째 과제에 적용하면 좋을거얌..