그냥 배우는 언어 기록하는 공간 :D

패캠 학습일지

[패스트캠퍼스] Spring 강의 7주차 학습일지

꾸준히_노력하기 2023. 12. 26. 20:36
배운 내용 정리

 

[ ch. 02 Spring MVC ]

13. 서블릿과 JSP (1)

 

: JSP와 서블릿은 거의 같음.
: Spring은 서블릿을 발전시킨 것으로, 서블릿을 이용해서 동작함.

: 서블릿의 방식을 Spring이 내부적으로 품고 있다고 볼 수 있음.

 


> 서블릿과 컨트롤러의 비교

 

> 서블릿 

                           출처: [스프링의 정석 - 기초편] 남궁성과 끝까지 간다.

 


 

> 컨트롤러

                           출처: [스프링의 정석 - 기초편] 남궁성과 끝까지 간다.


> @WebServlet
  = @Controller + @RequestMapping
: ("/rollDice2") → 서블릿과 매핑할 URL 주소 작성.


: HttpServlet을 상속받아야 함.
: Java는 단일상속이기 때문에 가능하면 상속을 안 받는 것이 좋음.
  → 컨트롤러에서는 개선된 부분.
: 서블릿에서는 sercive 메서드가 고정이며,
  request와 response가 항상 들어가야 함.

> IOException
: PrintWriter out을 얻어올 때 발생하기 때문에 적어줌.
: 출력하지 않는다면 필요 없음.

+ print문이나 write문이나 똑같음.

 


 

> 서블릿과 컨트롤러의 비교
: 서블릿과 컨트롤러는 굉장히 유사하지만,
  컨트롤러가 여러 가지면에서 조금 더 발전된 모습을 보여주고 있음.
1. 상속을 받지 않음.
2. 필요한 매개변수만 작성할 수 있음.
3. 애너테이션을 나눠서 처리함.

: 서블릿은 URL Mapping을 클래스 단위로 함.

: 매핑 하나당 클래스를 만들어야 하기 때문에, 클래스를 여러 개 만들어야 함.

 

: 컨트롤러는 메서드에 매핑하기 때문에,

  하나의 클래스 안에 새로운 메서드를 만들면 또 다른 매핑을 할 수 있음.

 


> 서블릿의 생명주기

 

 

: 서블릿은 기본적으로 세 개의 메서드가 있음.
: Servlet Container가 자동으로 호출하기 때문에
  서블릿을 만들 때 메서드의 내용만 채워주면 됨.

 


 


1. init 메서드
: 처음에 서블릿이 생성될 때, 서블릿 초기화를 위해 사용되는 메서드.

2. service 메서드
: 실제 작업을 처리하는 메서드.

3. destroy 메서드
서블릿이 메모리에서 제거될 때 호출되는 메서드.

 


 

출처: [스프링의 정석 - 기초편] 남궁성과 끝까지 간다.

 

1. 요청이 오면 Servlet Context에서 요청을 확인함.
2. 서블릿 인스턴스(객체)가 존재하는지 확인함.
3. 첫 번째 요청 때 객체를 만들었기 때문에, 두 번째 요청부터는 항상 Yes가 됨.
4. init 메서드를 거치지 않고, service 메서드만 호출됨.

> children
: Servlet Context 안에는 children 멤버가 있음.
: Map 형태로 서블릿 이름과 서블릿이 전부 등록되어 있음.
: 따라서 요청이 왔을 때 children을 통해 서블릿 객체의 존재 여부를 확인할 수 있음.

: 서블릿과 스프링은 기본적으로 Singleton(싱글톤)임.
: 요청이 올 때마다 서블릿 인스턴스(객체)를 계속 만드는 것이 아니라

  한 개의 인스턴스를 만들어서 그것을 계속 재활용하는 것임.
: 대부분 똑같은 작업을 하기 때문에 사용자마다 프로그램이 여러 개 있을 필요가 없는 것.

 


> JSP와 서블릿의 비교

: JSP (Java Server Pages) ≒ 서블릿

: JSP로 작성하면 서블릿으로 자동 변환됨.

 

> JSP                                                                                          

              출처: [스프링의 정석 - 기초편] 남궁성과 끝까지 간다.             

 


 

> 서블릿

                      출처: [스프링의 정석 - 기초편] 남궁성과 끝까지 간다.             

 

: JSP는 HTML 안에 Java 코드를 넣는 것임. <% ~ %>
: 값 출력 형태 → <%=값%>

 

: <html> 부분은 out.printIn으로 바뀜.
: 내용은 다 service 메서드 안으로 들어감.
: idx1 변수가 지역 변수(lv)가 됨.
: 인스턴스 변수가 필요하다면, <%! %> 를 사용하면 됨.

  → 인스턴스 변수(iv), 클래스 변수(cv) 선언을 여기에 함.

 


> JSP의 호출 과정

: JSP 파일이 언제 서블릿으로 변환되는지 보자.

: 첫 번째 호출일 때는 jsp가 변환되고,

  컴파일을 해서 객체를 만들어야 하기 때문에 시간이 지연됨.
: 두 번째 호출부터는 훨씬 빠름.
: jsp 파일이 변경되면 이 모든 과정을 다시 거침.

 

출처: [스프링의 정석 - 기초편] 남궁성과 끝까지 간다.


: *.jsp 요청이 들어오면 JspServlet이 무조건 요청을 받음.
: 서블릿 인스턴스가 존재하는지 확인함.

 

> 존재하는 경우 (Yes)

바로 호출해서 서비스 메서드가 응답해 줌.

 

> 존재하지 않는 경우 (No)

1. twoDice.jsp를 서블릿으로 전환함.
2. 서블릿 이름이 twoDice_jsp.java로 바뀜.
3. 서블릿 소스 파일이 되고, 컴파일을 해서 class 파일이 만들어짐.
4. 그 다음 객체를 생성함. 이때 초기화 메서드가 호출됨.
5. 서블릿 객체가 만들어지면 _ jspService 메서드를 호출함.

 



: 서블릿과 Spring은 초기화에서 차이가 있음.
: 서블릿은 늦은 혹은 지연된 초기화, lazy-init이 기본임.
: 미리 객체를 만들어 놓고 기다리는 것이 아니라

  요청이 올 때 객체를 만들고 초기화함.
: 그러나 서블릿도 Spring처럼 미리 초기화하는 방법도 제공함.

: Spring은 이를 개선하기 위해 early-init을 함.
→ 요청이 오지 않아도 미리 객체를 만들어 놓고 초기화하는 방법을 사용함.

 


> JSP의 기본 객체

: 생성 없이 사용할 수 있는 객체.

 

        출처: [스프링의 정석 - 기초편] 남궁성과 끝까지 간다.


: request 선언부도 없고, request 객체를 만든 적도 없음.

: request 는 기본 객체기 때문에 생성 없이 사용할 수 있는 것임. 

 


 

                           출처: [스프링의 정석 - 기초편] 남궁성과 끝까지 간다.

 

: 이렇게 변환되면 변수들을 쓸 수 있음.
: 전부 _ jspService 메서드의 지역 변수로 선언이 되어 있기 때문에 가능함.


 

출처: [스프링의 정석 - 기초편] 남궁성과 끝까지 간다.

 


14. 서블릿과 JSP (2)

 

> HTTP 프로토콜의 특징
: stateless (상태정보를 저장하지 않음)

    stateful (상태정보를 저장함)

: 상태정보를 저장하지 때문에 저장소가 필요함.
: 범위에 따라서 저장소 4개를 제공함.

> 4개 저장소 
: 접근 범위와 생존기간이 다른 4개의 저장소를 가지고 있음.
: 사용하려는 목적에 따라 알맞은 저장소를 사용하면 됨.

: 저장소 안에 Map 형태로 데이터를 저장함.


: 쓰기(저장)를 할 때는 setAttribute 메서드를 이용함.
: 읽기를 할 때는 getAttribute 메서드를 이용함.
: 두 메서드를 통해 Map에 있는 데이터를 쓰고 읽을 수 있는 것임.
: 저장소에 저장할 Map의 Key 값을 속성(attribute)이라고 함.


+ 접근 범위
Java의 접근 제어자처럼 접근할 수 있는 게 있고, 접근할 수 없는 게 있음.


>  유효 범위(scope)와 속성(attribute)

 

출처: [스프링의 정석 - 기초편] 남궁성과 끝까지 간다.

 

1. 클라이언트가 요청함.
2. 아이디와 패스워드를 입력해서 로그인이 성공함.
3. 응답으로 OK를 받음.
4. 또 다른 요청인 게시판에 글쓰기 요청을 보냄.
5. 두 번째 요청 때 방금 로그인했다는 사실을  알 수 없음.
    → HTTP는 상태가 없기 때문에 두 요청이 같은 클라이언트라는 것을 알지 못함.
    → 저장소가 필요함.

 


 

> pageContext 저장소
: EL 때문에 사용함.
: 지역 변수(lv)를 저장함.
: 같은 페이지 안에서만 접근할 수 있음.

 

: 기본적으로 jsp에서 값을 찍을 때 <%=lv%> 이런 식으로 씀.
: 그러나 ${lv} 라고 쓰는 것은 불가능함.
: EL에서는 lv에 직접 접근할 수 없음.
: EL을 쓰기 위해서는 먼저 저장소에 저장을 해야 함.
  → 저장소가 필요하게 됨.


: 같은 페이지 안이지만 pageContext 저장소를 이용해서 읽기, 쓰기를 함.
: 저장을 하면 읽을 수 있기 때문에, ${lv} 라고 쓰는 것이 가능해짐.
: 기본객체를 다 EL에서 사용하려면, pageContext 저장소를 거쳐서 사용해야 함.
: 요청할 때마다 새로 초기화됨.

 

 

+ 추가 설명

: 기본객체도 지역 변수에 해당함.

: 접근한다 = 저장소에 읽기, 쓰기를 한다

: EL은 <%=lv%> 와 같은 형식을 사용하기 불편해서 개선한 것임.

 


 

> application 저장소
: Web App(웹 애플리케이션) 전체에서 접근 가능한 저장소.
: Servlet Context 전체에 접근할 수 있음.
: 전체에서 딱 하나만 존재하는 공통 저장소.

: 전체 애플리케이션에서 공유하는 저장소기 때문에,

  모든 페이지에서 접근할 수 있기 때문에, 개별적인 아이디를 저장하기엔 좋지 않음.
  → session이 제공됨.

 


출처: [스프링의 정석 - 기초편] 남궁성과 끝까지 간다.


> session 저장소
: 클라이언트마다 하나씩 있는 개별 저장소.
: 사용자가 수만큼 객체가 생성되기 때문에 최소한의 데이터만 저장함.
: 저장소 중에 서버 부담이 가장 큼.
: 아이디, 비밀번호로 로그인을 하면 데이터는 각자의 session에 저장됨.
: 사용자만 사용할 수 있는 개별 정보 같은 것을 담으면 좋음.
  ex. 아이디, 장바구니
: 클라이언트의 아이디를 기억하고 있기 때문에,

  다음 요청을 했을 때 아이디를 사용할 수 있고, 매번 다시 로그인할 필요가 없음.
: 로그인하면 생겼다가 로그아웃하면 제거됨.
: 여러 페이지에서 접근할 수 있기 때문에 프로그래밍 하기에는 제일 편리함.

+ 쿠키
: session 객체가 어떤 사람의 것인지 연결해 주는 역할.

 


 

출처: [스프링의 정석 - 기초편] 남궁성과 끝까지 간다.

 

: 보통 request 객체가 한 JSP 페이지 안에서만 사용되고 끝남.
: 여기서는 a.jsp 페이지를 거쳐서 b.jsp 페이지에서도 request 객체에 접근할 수 있음.
: a.jsp에서 b.jsp한테 추가로 데이터를 보내고 싶을 때는

  request 저장소에 데이터를 저장하면, b.jsp에 전달됨.

 


 

> request 저장소
: request 객체가 Map을 가지고 있는 것.
: 요청할 때마다 하나씩 생기며, 서로 독립적임.
: 보통 요청하면 바로 응답하기 때문에 하나의 JSP 안에서 끝남.

> forward
: 요청을 첫 번째 JSP(a.jsp)가 받아서 응답을 안 하고, 

  다른 JSP(b.jsp)로 request 객체를 넘겨줄 수 있음.
: 요청을 받았지만 자신이 그 요청을 처리할 수 없을 때

  혹은 다른 쪽으로 넘겨줄 때에 해당함.

 


> 웹 프로그래밍
: 페이지 간의 이동임.
: 이동할 때마다 데이터 전달이 필요함.

 

 

+ 재정리

 

> pageContext 저장소

: 페이지 안에서만 사용할 수 있기 때문에 유용하지 않음. 
: EL에서만 사용하는 정도임.

 

 

> session 저장소
: 페이지 간의 데이터를 전달할 때 가장 편함.
: 그러나 사용자마다 하나씩 갖고 있기 때문에 메모리 부담이 큼.

 

+ 추가 설명

: 클라이언트가 접근하는 페이지에서 다 접근할 수 있어서 편리함.
: 페이지를 이동할 때마다 데이터 전달이 필요한데,
  session에 저장하면 모든 페이지에서 로그인하는 동안

  즉 session 객체가 유지되는 동안 접근할 수 있기 때문에 편리함.
: 편리하지만 메모리 부담이 가장 크기 때문에 사용을 최소화하는 것이 좋음.
: 사용하더라도 잠깐 저장했다가 삭제하는 식으로 사용하는 것이 좋음.

 

 

> request 저장소
: 가능하면 가장 부담이 없는 request 저장소를 사용하는 것이 좋음.
: request 저장소의 사용이 불가능할 때만 session 저장소를 사용하면 됨.
: 혹은 session에 잠깐 저장했다가 삭제해도 됨.

 

+ 추가 설명
: 요청이 처리되는 동안만 존재하기 때문에 가장 부담이 적음.
: forward를 통해서 다른 페이지로 전달될 수 있기 때문에

  데이터를 다른 페이지로 전달할 때 가장 우선적으로 고려해야 함.
: request가 안 되면 session을 선택하는데,

  가능하면 빨리 지워서 서버의 메모리 부담을 줄여 주어야 함.


> 유효 범위(scope)와 속성(attribute)

 

> pageContext
유효 범위: 1개 JSP페이지
→ JSP페이지의 시작부터 끝까지.

해당 JSP 내부에서만 접근가능.

페이지당 1개


> request
유효 범위: 1+개 JSP페이지
 요청의 시작부터 응답까지.  

 다른 JSP로 전달 가능.  

요청마다 1개.


> session
유효 범위: n개 JSP페이지
session의 시작부터 종료까지.

로그인  ~ 로그아웃

  클라이언트마다 1개

 

> application
context 전체
Web Application의 시작부터 종료까지. 

context내부 어디서나 접근 가능.

모든 클라이언트가 공유.

context마다  1개

 


> 속성  관련  메서드

 

void setAttribute(String name, Object value)
: 지정된 값(value)을 지정된 속성 이름(name)으로  저장함.


Object getAttribute(String name)
: 지정된 이름(name)으로 저장된 속성의 값을 반환함.


void removeAttribute(String name)
: 지정된 이름(name)의 속성을 삭제함.

: session 객체를 잠깐 사용했다가 삭제할 때 사용함.


Enumeration getAttributeNames()
: 기본 객체에 저장된 모든 속성의 이름을 반환함.

 


15. 서블릿과 JSP (3)

 

> URL 패턴

: @WebServlet으로 서블릿을 URL에 매핑할 때 사용함.

: 배열로 여러 개를 등록할 수 있음.
  ex. urlPatterns={"/hello", "/hello/*"}
: 하나만 등록하는 경우 경우
  ex. "/hello"

+ loadOnStartup
: Servlet은 기본이 lazy init(늦은 초기화)임.
: loadOnStartup을 하면 미리 초기화를 함.
: 서블릿을 호출하기 전에 미리 만들어 놓은 것.

 

+ loadOnStartup=1 | 뒤에 번호

: 미리 초기화하는 서블릿 중에서 우선순위를 적어주는 것.
: 중복되어도 괜찮음.

 



1. exact mapping

: 정확히 일치하는 것.

: /login/hello.do

 

2. path mapping 

: 경로 매핑

: /login/*

 

3. extension mapping

: 확장자 매핑

: *.do


4. default mapping

: 모든 주소와 다 매핑됨.
: 우선순위가 가장 낮음. 

+ 참고
@WebSerblet : 서블릿에서 사용하는 애너테이션.
@RequestMapping : 스프링에서 사용
스프링에서도 URL pattern이 있음.

 


> URL 패턴

 

출처: [스프링의 정석 - 기초편] 남궁성과 끝까지 간다.

 

: Servlet Context가 children과 servletMappings 멤버를 Map에 가지고 있음.
: Servlet Context 안에 children이라는 Map(테이블)이 있음. 서블릿이 등록되어 있음.

: servletMappings을 관리하는 Map이 있는데,
  여기에 url pattern(Key)이 들어가 있음.



1. /hello 라는 요청이 옴.

2. 요청과 연결된 서블릿이 무엇인지 찾기 위해

    먼저 servletMappings에서 요청과 일치하는 것이 있는지 확인함.
3. /hello 와 일치하니까 HelloServlet이 처리함. 
4. HelloServlet을 children에서도 찾음.

 

+ 재정리

1. 요청을 누가 처리할지 확인함.

2. 찾아서 HelloServlet이 처리함.
3. HelloServlet으로 요청이 전달됨.

 



> /hello, *.jsp

: 동적 리소스를 등록해서 처리함.

  ex. 서블릿

 

> /

: 정적 리소스를 처리하게 되어 있음.
   ex. image, css, txt

 

+ 추가 설명
: 프로그램을 만들면 등록을 함.
: 기본적으로 servletMappings에 등록이 됨.
: 등록이 안 되어 있으면, 즉 일치하는 게 없으면

  정적 리소스를 처리하거나 404 net found로 처리를 해 줌.

 



: 서블릿은 DefaultServlet을 안 쓰고, DispatherServlet이 연결되어 있음.
: 요청이 왔을 때, 요청과 매칭되는 프로그램이 없으면 전부 DispatherServlet이 받음.

: 스프링으로 개발할 때는 서블릿이나 jsp를 안 씀. 
: view로 쓰긴 하지만 등록을 안 함.
  → 모든 요청을 DispatherServlet이 받음.
: DispatherServlet 내부에 servletMappings과 같은 매핑을 가지고 있음.
   DispatherServlet 내부에서 이 매핑을 처리하는 것임.
: @RequestMapping으로 등록한 것이 servletMappings와 같이 저장됨.


> web.xml 실습

: 톰캣이 기동되면서 전체 설정이 이루어짐.
: 이후 전체 설정과 중복되는 부분을 개별 설정이 덮게 되어 있음.

 


 

> 전체 설정

: 원래 전체 설정에서 default 서블릿이 요청을 다 받게 되어 있음.

 

 


 

> 개별 설정

: 서버로 오는 모든 요청을 DispatherServlet이 받게 되어 있음.
: 개별 설정에서 DispatherServlet이 기존의 설정을 덮고 매핑이 되어 있는 것임.
: 시스템을 자체적으로 갖고 있음.

 

 


> EL (Expression Language)
: 값을 출력할 때 <%=값%> 형식이 아니라
  ${값} 형식으로 적는 것.
: 간단하고 편리하게 쓸 수 있게 됨.

 


> el.jsp 실습

 

 

> requestScope을 생략할 수 있는 이유

scope (저장소) 기본객체
page pageContext
request request
session session
application application


: name이 request에 저장되어 있음.
: 저장소의 Map 이름이 requestScope임. 

: requestScope.name은 requestScope(저장소)의 name(Kay)라는 것임. 
: 원래 어느 저장소에 있는지 적어 주어야 하지만, requestScope을 생략할 수 있음.
: Scope이 없으면 가장 좁은 순서대로 찾게 되어 있음.

 

 

 

 

+ 참고

jsp 코드
id가 없어서 null이 나옴.

EL
null을 출력하지 않음.

java
"1"+ 1 → "1"+ "1" → "11"
EL에서는 "1" 이 숫자로 바뀜.
문자열 결합은 "+=" 를 사용해야 함.

계산할 때 null은 0으로 바뀜.
빈 문자열도 0으로 바뀜.

empty
null 혹은 빈 컬렉션 배열인 경우 true

== = eq
!= = ne (not equal)
EL에서는 == != 를 더 많이 씀.

 


>  jsp를 고쳤는데 반영이 안 되는 경우

: 강제로 지워주면 새로 변경된 페이지가 다시 컴파일 돼서 보여짐.
: sts에서 Clean Tomcat Work Directory를 실행하면 지울 수 있음.

 

 

+ 추가 설명

: 처음에는 jsp가 java 파일로 변환되고 

  컴파일 되는 과정이 있기 때문에 시간이 오래 걸림.
: 두 번째부터는 요청이 빠름.
: 내용을 수정하면, 수정된 것을 확인하고 

  변환 및 컴파일을 다시 하기 때문에 다시 시간이 걸림.

 


16. 서블릿과 JSP (4)

 

> JSTL (JSP Standard Tag Library)
: <%~%> 형식의 Java 코드를 없애려고 나온 것.
: if문, for문 블럭 구성이 깨지기 쉽기 때문에 없애려고 하는 것임.

 

> 기본적으로 적는 줄

<@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

<@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>

: 이 두 줄이 있어야 해당 접두사가 있는 JSTL의 태그를 쓸 수 있음


> 접두사 c 
: jstl의 core library를 사용하는 것.

> 접두사 fmt
: 형식화 출력할 때 사용하는 library.
: format을 안 쓰면 빼도 되는 문장.

 

> Tag Library
: <c:set>, <c:if> 와 같은 태그들이 정의되어 있는 것.

 


> JSTL 실습

 

 

 

 

+ 추가 설명
: out으로 감싸주면 태그를 태그로 해석하지 않음.
: 값을 입력하는 부분에 태그를 집어 넣어서 공격을 할 수 있음.
: 특히 자바스크립트 태그 명령을 넣어서 공격할 수 있음.
: 이런 경우 c:out 태그로 감싸서 값을 출력하면,

  공격을 명령이 아닌 텍스트로만 해석을 하기 때문에 공격을 방어할 수 있음.

 


> Filter

: 서블릿에 전처리, 후처리할 코드들이 중복됨.
: 중복 코드를 분리할 때 사용하는 것이 Filter임. 

: 로깅 혹은 인코딩과 같은 변환을 할 때 사용함.

: Filter는 보통 한 개지만 여러개 일 수도 있음.

 

출처: [스프링의 정석 - 기초편] 남궁성과 끝까지 간다.


1. 서블릿에서 전처리, 후처리를 다 앞으로 뺌.
2. 처리 작업만 남으니까 서블릿의 코드가 간결해지고, 중복이 제거됨.
3. 요청이 오면, 전처리를 한 후 서블릿을 호출함.
4. 해당 서블릿으로 가서 처리하고 다시 돌아옴.
5. 후처리를 하고 응답하는 식으로 처리가 됨.

 

+ 추가 설명

: MVC패턴에서의 DispatherServlet과 비슷함.
: AOP 개념과 Filter의 개념이 유사함.
: 코드 분리, 중복 제거의 목적을 가지고 있음.

 


> Filter가 두 개인 경우 처리 과정

: Filter는 다음 Filter가 있으면 Filter를 호출하고,
  Filter가 없으면 서블릿이 호출되게 되어 있음.

 

 

출처: [스프링의 정석 - 기초편] 남궁성과 끝까지 간다.


1. 요청이 오면 Filter 1이 먼저 받아서 전처리를 함.
2. 그 다음 필터를 호출해서 넘어감.
3. Filter 2의 코드를 실행하고 서블릿을 호출함.
4. 처리되면 다시 Filter 2로 가서 후처리를 함.
5. Filter 1으로 가서 후처리를 하고 응답함.


> PerformanceFilter
: 수행시간을 측정하기 위해 만든 Filter임.
: Filter를 써서 만들어 놓으면 서블릿마다 코드를 넣지 않아도 됨.
: 추가, 제거, 변경하기 쉬움.
: 전처리 작업과 후처리 작업은 어느 한 쪽만 있어도 상관 없음.

 


 


+ 추가 설명

 

1. startTime

: 시작 시간 저장

 

3. System.currentTimeMillis( ) - startTime

: 현재 시간 - 시작 시간 = 소요 시간
: 서블릿 호출의 소요시간을 알 수 있음.


: 원래 시간을 재기 위해서는 각각 코드를 다 넣어야 되는데,

  Filter 하나를 만든 것으로 쉽게 적용됐음. 
: Filter가 처음으로 사용될 때는 Filter 객체가 생성되는 데에 시간이 걸림.
: jsp 각각에 적용되느라 시간이 걸렸는데, 그 다음부터 시간이 짧아지는 것을 확인할 수 있음.

 

+ doFilter 메서드 오버라이딩

: 서블릿 또는 다음 필터를 호출하는 부분은 그대로 두로,

: 전처리, 후처리 작업 부분에 서블릿에 적용할 코드만 적어주면 됨.

 


17. @RequestParam과 @ModelAttribute

 

> 한글 변환 필터 실습

 

 

 


> RequestParam

: 요청할 때 넘어온 파라미터를 매개변수에 연결할 때 쓰는 애너테이션.

 


 

 

 

 

 


> RequestParam3 실습

 

 

 

+ 참고
: required 여부를 기억할 필요는 없음.
: 필요할 때 명시적으로 적어주기만 하면 됨.

 


> RequestParam8 실습

 

 

 

+ 추가 설명

: 필수 입력이 아니라서 아무 것도 안 씀.
: 클라이언트가 잘못한 것이 없음.
: 아무 것도 안 넣으면 null이 들어옴.
: null을 int로 변환할 수 없기 때문에 500 서버 에러가 남.

 


 

 

 

 

+ 추가 설명

: 값은 없는데 year 라고만 씀.
: 빈 문자열 값이 들어옴.
: 빈 문자열은 int로 변환할 수 없기 때문에 에러가 남.
: 값을 잘못 주었으니까 400번 클라이언트 에러가 남.

 

: 매개변수 year의 값이 int일 때와 String일 때는 다름.
: int는 단순히 값을 받는 게 아니라 변환까지 해야 되기 때문에 에러가 남.

: 필수 입력이 아닐 때에는 반드시 기본값이 있어야 함.
: 빈 문자열이거나 null일 때 기본값이 사용됨.
: true일 때도 기본값을 줄 수 있음.

 

+ 매개변수의 값이 int로 변환이 필요할 때는
  false, true와 상관없이 예외 처리를 해서
  에러 메시지 대신 사용자 친화적인 화면을 보여주는 것이 좋음.

 


 

 

+ log4j.xml
: 패캐지별로 로고 레벨이 적혀 있음.

 

1. info 
: 기본.

 

2. debug 
: 에러를 출력할 때 보는 것.

 

3. trace 
: 에러를 출력할 때 보는 것.
: 로그 레벨 중에서 가장 자세한 레벨.

 

+ 예외가 발생했는데, 예외 처리가 됨.
   예외가 발생했는데 그 부분이 출력되지 않은 것임.

 

 

+ 추가 설명
: 예외를 catcher 메서드가 처리한 것. 
: 컨트롤러에서 발생하는 모든 예외를 처리함.
: 예외는 발생했지만 잘못된 요청이라는 사용자 친화적인 화면이 나옴.

 


 

 


 

 

 

+ 재정리


> 필수 입력일 때
값을 입력하지 않거나 잘못 입력하면 에러가 남.
반드시 예외 처리를 해서 사용자가 올바른 값을 입력하도록 유도하는 게 좋음.

> 필수 입력이 아닐 때
기본값을 꼭 주어야 함.

 


 

 

 


 

 

 

 

 


18. @RequestMapping

 

> @ModelAttribute
: 컨트롤러 메서드의 매개변수 또는 반환 타입에 적용 가능함.
: Model에 자동으로 저장해 줌.
: Key를 따로 써줄 수 있음.
: Key를 따로 써주지 않으면, 타입의 첫 글자를 소문자로 해서 Key로 사용함.

 

 

 

+ 추가 설명

: "myDate" 를 Key로 해서 date 객체 주소가 저장됨.

 

: 함수의 호출 결과를 Model에 저장함.
: "yoil" 이 Key가 됨.
: 메서드로 호출된 결과가 Value(값)으로 저장됨.

  ex. 수요일

: 컨트롤러의 메서드 중에 @ModelAttribute가 붙어 있는 것을 전부 호출함.
: 호출해서 그 결과를 다 Model에 저장함.
: 따라서 메서드 반환 타입 앞에 @ModelAttribute를 붙이면 호출도, 저장도 필요없음.

 


 

+ 추가 설명

: 참조형 매개변수(Mydate date) 앞에 있는 @ModelAttribute는 생략 가능함.
: 생략하면 @ModelAttribute("myDate")의 의미로 사용됨.
: @ModelAttribute를 안 붙여도 model.addAttribute("myDate", date); 를 안 써도 됨.

 

> 컨트롤러 매개변수에 붙을 수 있는 애너테이션.


1. @RequestParam
: 타입이 기본형 혹은 String일 때 @RequestParam이 생략된 것.
: 기본형과 String은 Model에 저장할 필요가 없음. → 바로 참조가 가능하기 때문에.
  ex. ${param.파라미터 이름}

2. @ModelAttribute
: 타입이 참조형일 때 @ModelAttribute이 생략된 것.
: 참조형일 때 @RequestParam을 붙일 수 없음. → 여러 개의 값을 바인딩하기 때문에.

 


> WebDataBinder

: 컨트롤러의 메서드가 선언되어 있고, 메서드를 호출했다고 했을 때, 

  쿼리스트링 뒤에 있는 데이터가 Map 형태로 구성됨.
: 이를 받는 메서드의 매개 변수 타입이 Mydate니까 Mydate 타입의 객체가 만들어짐.
: 요청할 때 쿼리스트링으로 전달 받은 값이 객체에 채워짐.

 

 

: 브라우저를 통해 요청 받은 값이 실제 객체에 바인딩될 때 중간 역할을 해주는 것.
: 타입이 일치하지 않을 때 타입 변환을 해서 그 변환 결과나 에러를 BindingResult에 저장함.


: 타입 변환을 먼저한 뒤에 데이터 검증(Validation)을 함.
: 다시 결과나 에러를 BindingResult에 저장함.


: WebDataBinder가 바인딩한 결과를 BindingResult에 담은 다음 컨트롤러한테 넘겨줄 수 있음.
  → 컨트롤러에서는 그 작업 결과로 어떠한 처리를 할 수 있게 됨.
: BindingResult는 바인딩할 객체 바로 뒤에 위치해야 함.


> WebDataBinder 실습

 

 

: day에 문자를 입력해서 일부러 에러를 냄.

 

 

: @ExceptionHandler에 의해서 예외 처리가 된 것.

 

 

+ 추가 설명

: @ExceptionHandler을 주석 처리하고 다시 실행한 것.
: 예외 처리를 빼니까 내부 서버 오류가 남.
: BindingResult error가 하나 있는 것을 확인할 수 있음.
: STS에서 로그를 확인해보니 result를 찾을 수가 없음.
: 컨트롤러까지도 가지 않은 것임.
  → System.out.println("resul="+result); 이 실행되지 않음.

 


 

: 예외 처리를 다시 살림.

 

 

+ 추가 설명

: STS에서 로그를 확인해보니 바인딩 에러가 일어났고,

  BindingResult에 한 개의 error가 저장되어 있음.
: 에러 메시지는 스트링을 int로 변환하다가 에러가 났다고 나옴.

 

 


19. 회원가입 화면 작성하기

 

 

 

+ 추가 설명

: html은 정적 리소스이므로 resources에 만듦.
: 설정이 바뀌면 Servers Restart 필수.

 

 

 

+ 추가 설명

 

> servlet-context.sml 

: web 관련 설정파일

 

> root-context.xml

: non-web 관련 설정파일

 

 

 

+ 추가 설명

: 전체는 form 태그이고, 안에 내용은 전부 input 태그임.
: form은 어떠한 데이터를 사용자한테 입력 받기 위한 것.
: form 태그는 서류/문서의 양식.

 


 

 

+ 추가 설명

: 원래 form 태그에 어디로 보낼 것인지를 적어야 함. 
   <form action="데이터를 전송할 URL">
: 전송할 URL은 디폴트가 자기자신이기 때문에 이 form은 자기자신한테 전송됨.

   → 화면이 refresh됨.

: 전송 방식인 메서드는 GET 또는 POST로 적어 주어야 함.
: form을 보면 메서드를 정해주지 않았기 때문에

  디폴트인 GET 방식으로 전송됨.


: GET 방식은 HEAD만 있고, BODY가 없음.
  → data가 쿼리스트링으로 전달됨.
: POST 방식은 HEAD와 BODY가 있음.

  → data가 BODY를 통해 전달됨.


: input 태그의 name에 따라서 쿼리스트링 name이 결정되고,

  적는 내용이 value가 되는 것임.

 


 

 

 

+ 추가 설명
: form으로 전송하는 경우 대부분 메서드를 POST로 설정함.
: 값이 3개인데, 페이스북 하나만 나왔음.
: registerForm에 input이 3개 있는데, name이 다 같음.

+ 값이 여러 개일 때는 String 배열로 받게 되어 있음.
   ex. String[ ] snsArr = request.getParameterValues("sns")

 


 

+ 추가 설명

: URL을 전송할 때 URL 인코딩을 함.
: 데이터를 보낼 때 ASCII가 아닌 것은 %EB%82%A8(남) 이런 식으로 바뀜.
  → URL 인코딩 돼서 전송되는 것임.
: 브라우저에서 보기 편하게 보여주는 것이지 실제로는 변환된 값이 전송됨.

: URL에 있는 한글을 유지하고 싶을 때는 앞에 한 글자를 빼고 복사하고 나중에 맨 앞글자 추가하기.

 


 

+ 추가 설명

: 원래 함수로 등록해야 함.

 

> formCheck(this) 함수 호출의 결과
: true일 때는 폼을 전송함.
: false일 때는 폼을 전송하지 않음.

 

 

+ autofocus
: 처음에 폼이 보여질 때 커서가 깜빡거리는 것.

 


 

 

 

+ 아이디가 너무 짧아서 전송이 안 되는 상황인데, 메시지가 나오지 않음.
   소스를 보면 메시지 부분에 아무것도 나오지 않음.

 

 

+ 추가 설명

: context 루트를 적어 주어야 함.
: context 루트가 바뀔 수도 있으니까 jstl을 써서 바꾸는 게 좋음.
: 적어 주면 URL 태그 라이브러리가 ch2를 넣어줌.
  → value="/ch2/register/save"/

> c:url 태그가 하는 일
1. context root 자동으로 추가해줌.
2. session id를 자동으로 추가해줌.

 

 

+ registerForm | ${'${mag}'}

: 원래 ${msg} 로 되어 있음.
: 이는 EL이 아니라 JS6에서 나온 Template literal이라는 것임.

: EL은 서버에서 돌아가지만,
  Template literal은 자바스크립트니까 브라우저에서 돌아감.
: 서버에서 먼저 돌아가고 이를 브라우저에서 받아서 자바스크립트가 실행되는 것임.
  → 서버가 먼저이기 때문에 서버가 Template literal로 보는 것이 아니라 EL로 해석함. 


: ${msg} 가 없으니까 빈 문자열로 변환돼서 안 나온 것임.
: 이런 경우 문자열로 감싸주어야 함. → ${'${mag}'}
: 따옴표로 묶으면 EL이 해석되어도 그대로 나옴.

 

 

 

+ save로 보내긴 했는데, 아직 컨트롤러를 만들지 않았음.

 

 

 

+ register/save로 넘어가서 registerInfo.jsp가

   사용자가 입력한 값을 보여주는 것임.

 

+ 추가 설명

: register/add에서 register/save로 입력한 내용이 전송됨.
: GET 방식으로 직접 입력할 수도 있음.
: 그러나 GET 방식으로 회원가입을 할 수 있게 되면,

  pwd 노출 위험과 회원가입을 자동화시킬 수 있는 위험이 있음.
: POST 방식으로만 회원가입을 할 수 있게 바꾸는 것이 좋음.

 


20. @GetMapping, @PostMapping (1)

 

 

 

 



: 회원가입 폼 같은 경우 단순히 화면만 보여줌. 

: registerForm.jsp와 "/register/add" URL을 연결해 줄 뿐

  실제로  register 메서드가 하는 일이 없음.

  → view를 view-controller로 등록하면 됨.

 

 


 

 


 

 

 

 

 

+ 추가 설명

: 원래 모델은 view에 전달하기 위한 것인데, 지금은 redirect를 써서 응답함. 
  → 모델로 전달할 수 없음. (redirect : 재요청)
: Model은 /register/save 요청에 쓰임.

: /register/add를 요청할 때 이 Model을 쓸 수 없음. 
: 그런데 "msg", msg에 메시지가 들어있음.
  → 스프링이 자동으로 register/add?msg="+msg 식으로 바꾸어 줌.

 

 

 

 

 


21. @GetMapping, @PostMapping (2)

 

: @RequestMapping은 너무 길기 때문에

  짧게 쓸 수 있는 @GetMapping, @PostMapping이 추가됨.
: 원래 URL이 같으면 URL Mapping 충돌이 나게 되어 있음.
: 그러나 메서드가 다르면 요청이 메서드로 구분될 수 있기 때문에 URL이 같아도 괜찮음.

 


> 클래스에 붙이는 @RequestMapping

: 클래스 안에 있는 모든 메서드 앞 경로에 /register가 붙는 것임.

  → @RequestMapping으로 준 내용 앞에 경로에 붙는 것.
: 서블릿은 매핑을 클래스 단위로 하지만, 스프링에서는 메서드 단위로 함. 
  → 서블릿처럼 클래스를 많이 만들지 않아도 됨. 
: 하지만 하나의 클래스에 모든 메서드를 넣을 순 없음.
 → 매핑될 URL의 공통 부분을 @RequestMapping으로 클래스에 적용해 주는 것임.

 


> @RequestMapping의 URL 패턴

: 서블릿에서 사용하는 URL 패턴과 유사함.

: 우선순위가 높은 순서대로 정리함.

: 확장자 매핑도 없는 경우 404 Not Found가 나옴.

 

1. exact mapping

: 정확히 일치하는 것.

: /login/hello.do

 

2. path mapping 

: 경로 매핑

: /login/*

 

3. extension mapping

: 확장자 매핑

: *.do

 

> 참고

? : 한 글자

* : 여러 글자

** : 하위 경로 포함

+ 배열로 여러 패턴을 지정함.

 


> @RequestMapping 실습

 

 


+ 진짜 못 찾는 것이 아니라 view가 없어서 그럼.


 

 

 


> URL인코딩 - 퍼센트 인코딩

: URL에 포함된 non-ASCII문자를 문자 코드(16진수) 문자열로 변환함.

: URL 요청을 할 때 URL에 포함된 글자들은 전부 ASCII여야 함.
: ASCII는 모든 인코딩에서 공통으로 다 들어가기 때문에

  상대방이 인코딩 때문에 못 받는 일이 없어서 URL인코딩을 하고 전송함.

 

: 1바이트마다 %를 붙인 것임.
  ex. %EB, %82, %A8
: Base 64 인코딩과 혼동되지 않도록 유의할 것.

: 한글처럼 특수한 문자 외에도 URL에서 쓰는 기호도 변환을 함.
: 데이터와 URL에 쓰이는 기본 기호와 구분하기 위해 변환하는 것.
  ex. / → %2F, = → %3D, ? → %3F

 


 

출처: [스프링의 정석 - 기초편] 남궁성과 끝까지 간다.


: redirect 주소를 직접 쓸 때는 사용자가 URLEncoder/Decoder를 써서 직접 처리해 주어야 함.
: 여기에서는 브라우저에 의해서 URL인코딩이 되고, 이를 서버가 받음.
: 서버 쪽에서는 URL디코딩을 해서 원래 데이터를 받음.
: 기본적으로 request.setCharacterEncoding("UTF-8") 문장은 필수로 작성함.
  → getParameter 했을 때 제대로 데이터를 디코딩해서 읽을 수 있음. 

+ 추가 설명
: 이를 매번 써주기 불편하니까 web.xml에 한글 변환 필터를 추가해 준 것임.
: CharacterEncodingFilter가 인코딩 처리를 다 해주기 때문에 

  getParameter를 해도 한글이 안 깨지는 것임.

 


+ 참고

 

> repositroy
: STS에서 스프링 개발을 하다보면 알 수 없는 문제들이 많이 일어남.
: 대부분 프로젝트에서 사용하고 있는 모듈 간의 충돌 때문에 에러가 발생하는 것임.
: 이때 repositroy를 삭제한 후에 Maven 업데이트를 하면 됨.

 


22. redirect와 forward

 

> redirect와 foward의 처리 과정 비교

 

출처: [스프링의 정석 - 기초편] 남궁성과 끝까지 간다.


> redirect
: 요청도 두 번, 응답도 두 번.

1. 요청
: 클라이언트가 요청을 함. (수동)

2. 응답
: 상태코드가 300번대인 redirect는 응답헤더만 있고 바디가 없음.
  → Location, 어디에 요청해야 할지를 헤더 정보에 줌.

3. 요청
: 브라우저가 자동으로 헤더의 Location에서 알려준대로 새로운 요청을 함.
: 앞서 한 요청의 객체와 다름.
: 처음한 요청이 GET인지, POST인지와 상관없이 redirect 요청은 GET으로 요청됨.

+  예시

: 고객이 카드사에 전화했을 때,
  해당 담당 부서의 전화번호를 주면서 다시 전화하길 요청하는 경우.
: 고객은 같은 얘기를 두 번 해야 함.

 



> forward
: 요청이 한 번 감.

1. 요청
: 클라이언트가 요청을 함. (수동)

2. forward
: write.jsp가 처리해야 될 것이 아닌 경우 

  혹은 자기가 일부만 처리하고 내용을 login.jsp에 전달(forward)함.
: 클라이언트가 요청한 내용을 그대로 전달해 주는 것임.
: write.jsp에서 새로운 데이터를 저장해서 줄 수도 있음.
: 전달할 때 request뿐 아니라 response도 같이 전달해 줌.

3. 응답
: login.jsp가 요청을 받아서 처리한 다음 응답함.
: 두 객체를 login.jsp 한테 전달해 주기 때문에 

  login.jsp가 response를 이용해서 응답할 수 있는 것임.

+ 예시 

: 고객이 카드사에 전화했을 때,
  해당 담당 부서로 전화를 연결해 주는 경우.
: 고객은 같은 얘기를 두 번할 필요가 없음.
: 고객은 어떤 부서가 처리한 것인지 알 수 없음.

+ 추가 설명
: request 객체가 Model 역할을 하고 있음.
: login.jsp에서 결과를 보여주는 view 역할을 함.
: write.jsp가 Controller 역할을 함.
: Spring이 forward를 이용해서 MVC를/MVC 패턴을 구현한 것임.

 


> RedirectView

 

출처: [스프링의 정석 - 기초편] 남궁성과 끝까지 간다.


1. Spring에서 클라이언트가 요청하면, DispatcherServlet이 받음.
2. 요청을 처리할 수 있는 Controller의 메서드를 호출함. → URL과 매핑되어 있는 메서드는 save.
3. 호출을 했지만, 유효성 검사를 통과하지 못해서 redirect 문자열이 return됨.
4. Contoller가 DispatcherServlet한테 view이름대신 문자열을 반환함.
5. 문자열에 redirect가 들어있기 때문에, DispatcherServlet은 문자열을 RedirectView에 전달함. 
6. RedirectView는 응답헤더를 만들어냄. → 응답헤더는 문자열을 만들어 내는 것.
7. 클라이언트가 응답을 받음.
8. 응답 상태 코드가 302번이니까 브라우저가 Location에 있는 URL로 자동 요청함. 

 


> JstlView

 

출처: [스프링의 정석 - 기초편] 남궁성과 끝까지 간다.


1. 요청하면 DispatcherServlet이 받아서 해당 Controller의 메서드를 호출함. 

    → 여기서 URL과 연결되어 있는 메서드는 register.
2. registerForm을 반환함.
3. registerForm(view이름)을 DispatcherServlet이 받아서 

    InternalResouceViewResolve한테 문자로 보냄.
4. InternalResouceViewResolve가 실제로 어떤 view인지 해석해 줌.
5. 넘겨받은 view이름을 JstlView에 넘겨줌.
    + JstlView(JSP Standard Tag Library View) : jspView를 처리함.
6. JstlView가 해당 jsp한테 Model, 즉 데이터를 넘겨줌. 
7. registerForm.jsp가 데이터로 최종 응답을 만들어서 응답함.

+ 뷰이름을 컨트롤러 메서드가 반환하고,

   jsp를 view로 써서 결과를 보여주는 것이 일반적임.

 


> InternalResourceView
: forwarding할 때 사용되는 view.

 

출처: [스프링의 정석 - 기초편] 남궁성과 끝까지 간다.

 

> Spring에서 forwading이 처리되는 과정
1. 클라이언트가 요청하면, DispatcherServlet이 받음.
2. 해당 Controller의 메서드를 호출함.
3. Controller 메서드가 forward로 시작하는 문자를 반환함. 
    → register/add로 forwarding하라는 뜻.
4. DispatcherServlet이 반환된 문자를 받아서 

    forward로 시작하니까 forwarding을 해야 한다고 인식함.
5. 문자(/register/add)를 InternalResourceView한테 전달함.
6. InternalResourceView가 /register/add를 호출하라고 DispatcherServlet한테 알려줌.
    → 클라이언트가 요청한 것과 같음.
    → 내부적으로 요청한 것(forwarding).
7. forwarding은 InternalResourceView가 처리함.

 

출처: [스프링의 정석 - 기초편] 남궁성과 끝까지 간다.


8. DispatcherServlet은 /register/add와 연결된 메서드를 호출함.
9. 메서드가 registerForm을 반환함.

10. 메서드를 호출하고 그 결과로 view이름을 받음.

 

+ 이후의 프로세스는 일반적인 요청의 경우와 같음.

 


> foward의 예시

 

출처: [스프링의 정석 - 기초편] 남궁성과 끝까지 간다.


> 은행에서 1년치 입출금 내역을 출력하고 싶은 경우 
1. 어떤 버튼(pdf, Excel, CSV)을 클릭하느냐에 따라 타입이 달라짐.
2. 만약 pdf 버튼을 클릭하면, /download?type=pdf 이런 식으로 요청이 감.
3. download URL에 연결되어 있는 메서드가 호출되면서 사용자의 입출력 내역을 DB에서 가져옴.
4. type이 무엇인지에 따라 해당 view로 forward를 할 수 있음. 
     → 여기에서는 해당 데이터를 가지고 pdfView를 만들어주는 pdfView한테 넘겨줌.
5. pdfView와 같은 view를 직접 만듦.

+ 참고

: defaultValue를 빈 문자열로 줌. 
   → 타입이 없을 때는 txtView로 다운 받을 수 있게 처리함.


> redirect 실습

 

 

 

+ 추가 설명
: 요청을 /save로 했지만 주소가 /add로 바뀜.
브라우저가 자동으로 해 준 것임.
: redirect는 ch2/register/add로 요청하라고 알려주기 때문에 브라우저 주소창이 바뀜.
: forward는 주소가 바뀌지 않음.
  → 클라이언트는 처음에 요청한 사람이 처리한 줄 알기 때문에.

 


 

 

+ 추가 설명

: ch2/register/save가 ch2/register/add로 forward함.
: ch2/register/save는 POST로 forward하지만,

  ch2/register/add에서는 GET밖에 처리를 못함.
: POST로 보냈기 때문에 허용되지 않는 메서드라고 나오는 것임.

 


 

 

+ 추가 설명

: add요청이 GET과 POST 요청을 둘 다 받을 수 있게 됨.
: 저번에 등록해 준 view-controller는 지워 주어야 함.

 

 

+ 추가 설명

: forward는 한 번만 요청함.
: save로 보낸 요청을 add한테 forward(전달)한 것이기 때문에 주소창이 바뀌지 않음.
: 응답도 200번대로, 성공적으로 받은 것임.

 

 

후기

 

스프링은 정말..

모든 과정에서 어쩜 이렇게 원활하지 못한지 신기할 지경이다.

내용도 어려운데 매번 에러와 싸우려니까 너무 지침....

 

이걸 어떻게 이해하지..? 싶어서 머리가 아픈 내용이 꽤 있었는데,

다행히 설명을 계속 듣다 보면 선생님이

지금은 흘려들으라고 얘기해 주셔서 엄청 안심이 됐다ㅎㅎ..

반복하다 보면 언젠가 이해되는 날이 오겠지!

 

다음 주도 화이팅!