본문 바로가기
국비학원

[국비지원] KH 정보교육원 75일차

by 도전하는 개발자 2022. 7. 14.

kh day 075

 오늘부터 본격적으로 Servlet/JSP를 학습하기 시작했다. 예제를 통해 진도를 나가니 확실히 공부하는 느낌도 나고 재밌는거 같다. 아직까지는 크게 어려운 내용은 없지만 다음 시간부터 시작하는 서블릿의 핵심 클래스 부분은 난이도가 상당하다고 한다. 강사님께서 이전 기수의 경우 이 부분을 이해하는데만 1주일이 소요됐다고 하셨다. 앞으로 좀 더 긴장해야겠다!

 

---

 

서블릿 맵핑
작성한 서블릿을 맵핑하는 방법은 다음과 같이 2가지 방법이 제공된다. 

(1) web.xml 파일에 등록하는 방법 (DD파일 더블클릭!)


Servlet 2.5까지 사용하던 방법이며 Servlet 3.x에서도 사용 가능하다. 
WEB-INF 폴더안의 web.xml 파일에 다음과 같이 <servlet> 태그와 <servlet-mapping> 태그를 사용하여 설정한다. 
여러 개의 서블릿 등록이 가능하며 주의할 점은 url-pattern 값에는 임의의 값으로 지정 가능하지만, 반드시 ‘/’를 사용해야 되고, 
<servlet> 태그의 <servlet-name> 값과 <servlet-mapping> 태그의 <servlet-name> 값도 임의의 값으로 지정 가능하지만, 반드시 일치해야 된다.


우클릭으로 서블릿 생성시에는 자동으로 이를 입력해주는 효과가 된다

 

 


(2) @WebServlet 어노테이션(annotation) 이용하는 방법 (HelloServlet 예제 참고)


어노테이션은 JDK 5.X부터 추가된 기능으로서, XML 파일 등을 이용하여 환경 설정 및 추가 
정보를 등록하는 방법 대신에 자바 코드에 직접 설정하는 기술로서 ‘@’으로 시작된다

 1. 서블릿 맵핑명만 지정하는 방식
@WebServlet("/맵핑명")
public class MyServlet extends HttpServlet{ ... }

 2. 추가 속성을 이용하는 방식
서블릿 별명과 value, urlPatterns 속성을 사용하여 여러 개의 맵핑명을 지정할 수 있는 방식

@WebServlet( name="서블릿별명“, value (or urlPatterns)={ "/맵핑명“, "/맵핑명2” } ) 
public class MyServlet extends HttpServlet{ ... }

 

@Log4j2
@NoArgsConstructor

@WebServlet( name = "MyServlet", value = { "/xxx", "/yyy" })
//@WebServlet( name = "MyServlet", urlPatterns = { "/xxx", "/yyy" })
public class MyServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;       

	@Override
	protected void service(HttpServletRequest req, HttpServletResponse res) 
		throws ServletException, IOException {
		log.info("service(req, res) invoked.");
		log.info("HelloServlet 요청");
	} // service

} // end class

MyServlet.java

---

서블릿 아키텍처 및 핵심 API
다음은 서블릿을 사용하는 경우의 웹 아키텍처이다. 
클라이언트에서 웹 브라우저를 이용하여 적절한 URL 형식으로 서블릿에 요청하면, 
웹 컨테이너에서 서블릿을 실행하고 결과값을 html로 구성하여 클라이언트로 응답 처리한다.


HttpServletRequest API
HttpServletResponse API


HttpServlet API
서블릿을 구현하기 위한 핵심 API 로서, 일반 클래스가 아닌 추상 클래스로 제공된다. 
다음은 HttpServlet의 계층 구조이다. 



---

서블릿 LifeCycle 메서드
톰캣 컨테이너는 서블릿의 인스턴스(instance)를 init, service, destroy 3가지 메서드를 사용하여 관리한다. 
다음은 서블릿의 LifeCycle 상태도이다


---

서블릿 응답 처리
클라이언트에서 서블릿으로 요청을 하면 서블릿은 처리한 결과를 html 형식으로 응답 처리
한다. 실제로 MVC 패턴을 적용한 웹 어플리케이션 개발에서는 jsp에서 응답 처리를 담당한
다. 서블릿에서 응답 처리와 관련된 API는 ‘HttpServletResponse’이고, 다음 2가지 메서드를 사
용하여 클라이언트에게 html을 전송하게 된다.

response.setContentType("text/html;charset=UTF-8")
클라이언트인 웹 브라우저에게 처리할 데이터의 MIME 타입을 알려주는 메서드이다. 기본은 
일반 텍스트를 의미하는 ‘text/plain’이고, 실습에서는 html 형식의 전송이므로 ‘text/html’로 
지정한다. 또한 한글 처리를 위해서 ‘charset=UTF-8’을 추가 지정한다.

response.getWriter()
서블릿 및 jsp을 이용한 응답 처리는 기본적으로 자바 I/O 기술을 이용한다. 따라서 자바 출
력을 위한 OutputStream 또는 Writer 클래스를 사용해야 되며, 서블릿에서는 getWriter() 메
서드를 이용한 PrintWriter와 getOutputStream() 메서드를 이용한 ServletOutputStream 클래
스를 사용할 수 있다. 
문자 데이터를 처리하기 위해서는 PrintWriter를 이용하고, 바이너리(binary) 데이터를 위해서
는 ServletOutputStream 클래스를 사용한다.

@Log4j2
@NoArgsConstructor

@WebServlet("/Response")
public class ResponseServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
	@Override
	protected void service(HttpServletRequest req, HttpServletResponse res) 
			throws ServletException, IOException {
		log.trace("service(req, res) invoked.", req, res);
		
		try {	// 웹 어플리케이션의 예외처리를 따로 또 해주어야한다!		
					
			// 문서의 타입 설정
			res.setContentType("text/html; charset=UTF-8");
			
			// 자바 입출력 (I/O)
			@Cleanup  // 자원객체는 썼으면 닫자 (try-with로 닫는건 HelloServlet.java 참고)
			PrintWriter out = res.getWriter();
			
			// html 작성 및 출력
			out.println("<html><body>");
			out.println("응답 문서 생성 성공!!");
			out.println("</body></html>");
			
			out.flush(); // 잔류 데이터 방출
//			out.close(); // @Cleanup 혹은 try-with-resources로 닫자
			
		} catch (Exception e) {        // 어떠한 예외가 발생해도 다 잡아라
			throw new IOException(e);  // 발생한 그 예외를 여기로 던져라 
		} // try-catch // 웹 어플리케이션의 예외처리를 따로 또 해주어야한다!
		
	} // service

} // end class

ResponseServlet.java


---

어노테이션을 이용한 서블릿의 선처리 및 후처리 작업
@PostConstruct를 이용한 선처리 작업
@PreDestroy를 이용한 선처리 작업
-> 각각 init, destroy 메소드의 개념과 비슷하다고 생각하면 된다 (실전에선 이 Lifecycle 메소드 사용)

@Log4j2
@NoArgsConstructor

@WebServlet("/PostPre")
public class PostPreServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
	@Override
	protected void service(HttpServletRequest req, HttpServletResponse res) 
			throws ServletException, IOException {
		log.trace("service(req, res) invoked.");
	} // service	

	@Override
	public void init(ServletConfig config) throws ServletException { 
		log.trace("inint({}) invoked.", config);
	} // init
	
	@Override
	public void destroy() {
		log.trace("destroy() invoked.");		
	} // destroy
	
	@PostConstruct  // init 보다 먼저 호출됨
	public void postConstruct(){
		log.trace("postConstruct() invoked.");
	} // postConstruct
	
	@PreDestroy     // destroy 보다 늦게 호출됨
	public void preDestroy(){
		log.trace("preDestroy() invoked.");
	} // preDestroy

} // end class

PostPreServlet.java


Run on server 해보고 콘솔창 확인
http://localhost:8080/manager/html 접속해서 
서버를 중지시키고 콘솔창 다시 확인

PostConstruct 는 생성자를 호출한 직후에 -> init보다 먼저 호출
PreDestroy는 Servlet 객체가 파괴되기 직전에 -> destroy보다 나중에 호출
사실 누가 먼저 누가 나중에 이건 중요한 내용은 아니다. -> 그냥 init과 destroy 쓰자

---

HTML5 Form 태그와 서블릿

* Request Parameters (=전송 파라미터)



서블릿에서 파라미터 처리
HTML 의 input 태그에 값을 지정하고 submit 버튼을 선택하면, 서버에 파라미터(폼 데이터)가 전송된다. 
요청받은 서블릿은 HttpServletRequest 객체의 3가지 메서드를 사용하여 파라미터 값을 얻을 수 있다.


* 여기서 parmeter는 매개변수를 뜻하는게 아니고 전송된 Request Parmeter를 뜻함
* 값이 하나가 날아올지 여러개가 날아올지에 맞춰서 getParameter, getParameterValues를 사용하면 된다

---

* Postman 사용법
주소 : http://localhost:8080/'여기에 uri 입력' 
        뒤에 쿼리 스트링 (물음표 뒤에 나오는 문자열)은 자동으로 생성됨
get 방식이면 params
post면 body -> x-www-form-urlencoded


(1) getParameter(name) 메소드 (전송되는 값이 하나)

@Log4j2
@NoArgsConstructor

@WebServlet("/Login")
public class LoginServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
	@Override
	protected void service(HttpServletRequest req, HttpServletResponse res) 
			throws ServletException, IOException {
		log.trace("service(req, res) invoked");
		
		try {
			// 1. 전송 파라미터 (Request Parameters)를 받아서 String 타입의 변수에 저장
			String userid = req.getParameter("userid");
			String passwd = req.getParameter("passwd");
			
			log.info("\t+ userid : {}, passwd : {}", userid, passwd);
			
			// 2. 응답 문서를 생성한다 (에코)
			res.setContentType("text/html; charset=UTF-8"); // 응답문서 타입 설정
			@Cleanup                                        // 자원객체 썼으면 닫자
			PrintWriter out = res.getWriter();
						
			out.println("<ul>");
			out.println("	<li>"+userid+"</li>");
			out.println("	<li>"+passwd+"</li>");
			out.println("</ul>");
			
			out.flush();                                    // 잔류 데이터 방출			
		} catch (Exception e) {
			throw new ServletException(e);  // ServletException, IOException 둘 중 하나 적어준다
		} // try-catch
	} // service
} // end class

LoginServlet.java


자바파일을 런온서버 한 다음 postman에서 send 시켜주면 된다!



(2) getParameterValues(name) 메소드 (전송되는 값이 여러개)

POST 방식은 쿼리 스트링이 나오지 않음 (보안에 좋다)

 

@Log4j2
@NoArgsConstructor

@WebServlet("/Sport")
public class SportServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       

	@Override
	protected void service(HttpServletRequest req, HttpServletResponse res) 
			throws ServletException, IOException {
		log.trace("service(req, res) invoked.");
		
		try {
			// 1. 전송 파라미터 를 받아서 String 타입의 변수에 저장
			req.setCharacterEncoding("utf8");  // 전송되는 값에 한글이 들어있기 때문에 타입을 설정해준다
			String gender = req.getParameter("gender");         // 전송되는 값이 하나
			String[] sports = req.getParameterValues("sports"); // 전송되는 값이 여러개 
			
			log.info("gender: {}, sports: {}", gender, Arrays.toString(sports));
			
			// 2. 응답문서 생성
			res.setContentType("text/html; charset=UTF-8");     // 응답문서 타입 설정
			@Cleanup                                            // 자원객체 썼으면 닫자
			PrintWriter out = res.getWriter();
			
			out.println("<ol>");
			
			out.println("<li>" + gender + "</li>");
			
			for(String sport : sports) {
				out.println("\t <li>" + sport + "</li>");
			} // enhanced for (iterable 하기에 사용 가능)
			
			out.println("</ol>");
						
			out.flush();                                        // 잔류 데이터 방출
		} catch (Exception e) {
			throw new ServletException(e);
		} // try-catch		
	} // service

} // end class

SportServlet.java 

 



(3) getParameterNames() 메소드

 

@Log4j2
@NoArgsConstructor

@WebServlet("/Member")
public class MemberServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
    
	@Override	
	protected void service(HttpServletRequest req, HttpServletResponse res) 
			throws ServletException, IOException {
		log.trace("service(req, res) invoked.");	
		
		try {
			// 1. 전송 파라미터 를 받아서 String 타입의 변수에 저장
			req.setCharacterEncoding("UTF-8"); // GET 방식, POST 방식 모두 무조건 해주자
			Enumeration<String> enu = req.getParameterNames(); // 전송되는 값이 여러개
			
			// 2. 응답문서 생성
			res.setContentType("text/html; charset=UTF-8");    // 문서 타입 설정
			@Cleanup                                           // 자원객체는 썼으면 닫자
			PrintWriter out = res.getWriter();
			
			out.println("<ol>");
			
			while (enu.hasMoreElements()) {
				String paramName = enu.nextElement();
				String paramValue = req.getParameter(paramName);
				log.info("\t+ paramName: {}, paramValue: {}", paramName, paramValue);
				
				out.println(String.format("<li> %s : %s </li>", paramName, paramValue));			
			} // while
			
			out.println("</ol>");
			
			out.flush();									   // 잔류 데이터 방출			
		} catch (Exception e) {
			throw new ServletException(e);
		} // try-catch
	} // service
} // end class

MemberServlet.java

 

------------------------------------------------------