본문 바로가기
Java/JSP & Servlet

[JSP & Servlet] 6. Servlet 기본 코드

by Rosmary 2022. 10. 29.
728x90
반응형

 

 

 

 

** 본 포스팅의 예시들은 Eclipse를 사용하지 않았다. Eclipse 역시 동작 방식은 동일하기 때문에 굳이 Eclipse 버전으로 포스팅을 하지 않는다.

 

 

 

지난 몇 개의 포스팅을 통해 JSP의 기본 태그액션 태그, 내장 객체를 사용한 html 화면 구성 방법에 대해 알아보았다. JSP 파일은 웹 브라우저로 호출되는 경우 Tomcat 서버의 JSP 컨테이너에 의해 Servlet으로 변환되고, Servlet 실행 결과가 사용자에게 반환되어 웹 브라우저에서 그 내용을 확인할 수 있게 된다.

 

모든 웹 페이지를 JSP로만 구성할 수 있다면 Servlet을 사용할 이유가 없지만 JSP는 html 내에 Java 코드가 삽입되는 형태라 사용자에게 보이지 않게 동작해야하는 보안적인 부분이 존재한다면 코드를 작성하기가 쉽지 않다. 앞서 작성한 액션 태그 포스팅에서 예시로 들었던 로그인 구현 JSP 코드 역시, JSP 상에 비밀번호가 노출되어 있기 때문에 누군가가 이 JSP파일을 탈취한다면 위협이 될 수 밖에 없다는 것을 쉽게 확인할 수 있다.

 

지난 포스팅에서 작성한 비밀번호 검증 JSP 파일 내용의 일부다. 물론 예시를 위해 이렇게 작성한 것이고 실제는 DB를 활용한다

 

 

따라서 사용자에게 보이지 않는 부분, 즉 백엔드에 대한 코딩은 Servlet 파일을 사용할 수 밖에 없게 된다. Servlet 파일은 소스를 컴파일한 클래스 파일만 존재하더라도 웹 상에서 동작이 가능할 뿐만 아니라, 웹 주소상 경로 역시 JSP와는 다른 방식으로 동작하기 때문에 비밀번호 검증을 진행하는 파일이 존재하는 주소를 찾는 것 역시 쉽지 않기 때문에 보안상으로도 훨씬 안전하게 된다.

 

JSP로부터 변환된 Servlet은 사용 가능한 내장 객체가 모두 추가되어 있어 사실 수동으로 코딩하기는 어려움이 있다. 단, Servlet으로 수동 코딩을 진행하는 경우 반드시 포함되어야하는 선언, 매서드, 객체가 지정되어 있는데, 이들 정보만 알고 있더라도 간단한 Servlet 파일은 쉽게 작성할 수 있게 된다. 

 

이번 포스팅에서는 Servlet 파일을 구동할 수 있는 최소한의 코드에 대해 알아보고, 앞선 포스팅에서 진행했던 비밀번호 검증 절차를 Servlet을 사용하여 조금 더 안전하게 만들어보려한다.

 

 

 

1. 서버에서 Servlet 파일로 접근하는 URL 경로

 

컴파일 된 Servlet 클래스 파일은 각 Webapp 파일의 WEB-INF\\classes 폴더에 위치하고 있어야 한다. JSP가 폴더로 계층구조만 이루면 URL 상에서 접속이 가능한 것과 달리, Servlet은 아무리 열심히 classes 폴더 내에 계층 구조를 만들어도 접속이 안되는 것을 확인하신 분들도 있을 것이다.

 

만들어놓은 Webapp 내에 존재하는 web.xml 파일에서 default Servlet에 대한 설정을 아래와 같이 변경하면, Webapp 폴더 주소로 접속 시 접속 가능한 페이지나 폴더가 화면에 나타나는데, 여기에 WEB-INF는 나타나지 않음을 확인할 수 있다. 즉, 사용자에게 민감한 절차를 보이는 Servlet 클래스 파일이 저장되는 WEB-INF는 기본적으로 사용자에게 보이지 않게 설정되어 있다는 말이다.

 

 

접속 가능한 파일, 폴더를 확인하는 화면을 조회하려면 index.jsp 또는 index.html 파일이 존재하지 않아야 한다.

 

 

 

따라서 Servlet 클래스 파일로 접속하기 위해서는 Servlet 파일 또는 web.xml 파일에 주소를 별도로 기재해 주어야한다. Servlet에서 이 주소를 명시하기 위해 Java annoation을 활용해야 하며, 이 annotation은 tomcat의 라이브러리 폴더 내 servlet-api.jar 파일에 존재하고 있다.

 

 

 

 

 

2. Servlet 파일에 반드시 필요한 매서드: service()

 

Servlet 파일 내에 반드시 작성되어야하는 매인 클래스의 매서드가 하나 있다. service()라는 이름의 매서드며 기능은 다음과 같다.

 

HttpServlet.service()       : Servlet 파일 호출 후 동작하는 매인 매서드. JSP의 Scriptlet이 service() 매서드에 작성된다.

 

이미지 출처: https://www.geeksforgeeks.org/life-cycle-of-a-servlet/

 

 

위의 이미지는 Servlet 파일이 웹 브라우저에 호출되는 경우, 생성, 실행 , 소멸이 어떻게 진행(생명 주기라 한다)되는지를 나타낸 모식도다. init()은 웹 브라우저에서 Servlet 파일이 호출되어 Servlet 클래스 로딩과 객체 생성 시 단 한 번 실행되는 매서드이며, Destroy()는 웹 브라우저의 종료 또는 서버 종료 시 실행되는 매서드다. 이들 매서드는 페이지 호출 및 종료 시 단 한 번 만 수행되는 매서드이기 때문에 Servlet을 처음 접한 분들이라면 사실 크게 사용할 일이 없고(필자의 경우 테스트 시 로그를 기록하는 용도로 쓴다), service() 매서드 부분을 주로 작성하게 된다.

 

이 service() 매서드는, 필자가 위에서도 명시해두었지만, HttpServlet 클래스에 속한 매서드다. 따라서 Servlet 파일의 매인 클래스 작성 시 반드시 HttpServlet 클래스를 상속해야 한다. HttpServlet 클래스 또한 Servlet annotation과 동일하게 servlet-api.jar 파일 내에 존재하고 있다.

 

 

 

 

 

3. Servlet 파일의 기본 코드 포맷

 

위의 내용을 토대로 Servlet 파일을 만들어보자. 필자는 servletTest라는 이름의 Webapp의 WEB-INF 폴더 내에 Test.java라는 Servlet 파일을 생성했다. JSP 파일은 존재하지 않는 상황이다.

 

 

 

Test.java의 컴파일 클래스 파일에 접속할 수 있도록 URL 설정부터 진행해보자. URL의 설정은 WebServlet이라는 이름의 클래스 파일에 정의된 annotation을 사용한다. 이 클래스를 사용할 수 있게 다음과 같이 import를 진행한다.

 

 

servletTest Webapp에서 Test.class 파일에 접속할 주소는 "http://localhost:8080/servletTest/Test"로 지정해보려한다. 이를 위해 아래와 같이 annotation을 명시한다.

 

 

annotation에 사용된 WebServlet 클래스는 옵션으로 지정할 수 있는 요소를 가지는데, 이 중 urlPatterns라는 요소가 존재한다. 이 요소는 Servlet 파일이 호출될 수 있는 URL 주소를 지정하는데 사용하며, annotation과 함께 Servlet 파일 내에서 지정되면, 해당 Servlet 파일로의 접근을 지정한 URL 주소로 진행할 수 있게 된다.

 

 

 

이제 본격적으로 Class 파일을 작성해보자. 필자가 만든 Servlet은 파일명이 Test.java이기 때문에 매인 클래스 이름 역시 Test로 지정해야한다. 그리고 이 Test는 앞서 언급한대로 HttpServlet 클래스를 상속받아야 한다. 

 

 

 

이제 HttpServlet에 정의된 service() 매서드를 오버라이딩해야한다. HttpServlet 파일로부터 유래한 매서드이기 때문에 annotation으로 오버라이드 매서드임을 표시해준다.

 

 

 

HttpServlet.service() 매서드의 documentation 문서를 보면 매개인자로 HttpServletRequest, HttpServletResponse 객체를 받게 되어 있다. 이들 객체 또한 servlet-api.jar 파일 내에 정의되어 있으며 패키지는 javax(jakarta).servlet.http다. 매개 인자의 사용을 위해 이들 클래스도 import를 진행해야 한다.

 

또한 service() 매서드는 IOExceptio과 ServletException 예외를 매서드상에서 던지는데, 이들 예외는 Java의 기본 예외 패키지에 존재하지 않는 예외기 때문에 마찬가지로 import를 진행해주어야 한다. 

 

 

throws에 IOException 오타가 있다.

 

 

 

이제 지금까지 주구장창 사용해왔던 out 객체를 service() 매서드 내에 정의해보자. out 객체는 service() 매서드 매개인자로 사용한 response 객체의 getWriter() 매서드로 생성할 수 있는데, getWriter() 매서드로부터 반환되는 객체 타입은 PrintWriter라는 클래스다. 이 클래스 역시 java.io 패키지에 존재하기 때문에 import를 진행해야한다. 

 

 

 

위의 이미지에 작성된 내용이 Servlet 파일 작성을 위해 기본적으로 요구되는 코드다. 이제 out객체를 사용하여 화면에 출력할 HTML 태그들을 작성하고 저장하자.

 

 

 

파일 저장이 완료되면 java 파일을 컴파일하고 컴파일 결과로 발생한 Test.class 파일을 WEB-INF/classes 내부로 옮겨주자.

 

 

 

Servlet 클래스 파일은 Tomcat 실행 시 JSP 컨테이너에서 동작하도록 되어 있기 때문에 구동 중 내용이 변경되더라도 JSP 처럼 실시간으로 변경 사항이 반영되지 않는다. 따라서 Tomcat 서버가 실행중이라면 중지 후 재시작을 진행한 뒤 Servlet 파일에 지정한 URL 주소로 접근해야한다.

 

필자는 cmd 창에서 서비스 재시작을 했다.

 

 

결과 화면이 '뚯씪뻾껠'하는데 이는 response 객체의 setContentType("text/html;charset=UTF-8") 매서드 또는 setCharacterEncoding("UTF-8")를 service() 매서드 내 상단에 추가하면 된다. 혹시나 UTF-8을 적용했는데도 '뚯씪뻾껠'거리면 EUC-KR을 적용해보자.

 

 

 

 

session 등의 별도 내장 객체가 필요하다면, request 또는 response의 getXXX() 매서드를 통해 이들 객체를 생성하면 된다. 예시로 들은 session의 경우, request.getSession()으로 이전 페이지와 세션을 연결할 수 있는데, 이 코드는 JSP의 지시자(Directive) 속성인 session을 true로 지정하는 것과 동일한 효과를 가진다. 만약 새 세션을 생성해야하는 경우라면 getSession() 매개인자로 true를 넣어주면 된다. 자세한 내용은 아래 링크의 Documentation을 참고하자

 

https://javadoc.io/doc/javax.servlet/javax.servlet-api/latest/index.html

 

javax.servlet-api 4.0.1 javadoc (javax.servlet)

Latest version of javax.servlet:javax.servlet-api https://javadoc.io/doc/javax.servlet/javax.servlet-api Current version 4.0.1 https://javadoc.io/doc/javax.servlet/javax.servlet-api/4.0.1 package-list path (used for javadoc generation -link option) https:/

javadoc.io

 

 

 

 

4. servlet 파일 작성 예시

 

그럼 예전 포스팅에서 JSP로 작성했던 로그인 검증 과정을 JSP로 재작성해보려한다. 먼저 로그인 페이지는 login.jsp라는 파일명으로 동일하게 내용을 작성한다. 단, 입력한 아이디와 비밀번호는 JSP 파일이 아닌 Servlet으로 넘어가야하기 때문에, Servlet 파일명으로 지정할 loginValidate라는 값으로 변경할 것이다.

 

 

 

로그인 버튼을 누르면 WEB-INF/classes 폴더 내에서 URL 주소의 /servletTest/LoginValidate 경로로 입력값을 전달하게된다. 그럼, 지금부터 작성할 LoginValidate.java 파일에는 이 입력값을 추출하고 비밀번호를 검증하는 절차를 코딩하면 된다. Servlet에 필요한 최소 코드로 이 파일을 작성한 결과는 아래와 같다.

 

 

 

입력받은 값은 조건에 따라 에러페이지와 정상 로그인 페이지로 리다이렉션 하도록 만들었다(이 파일들은 간단하게 만든 것이니 굳이 코드를 올리지 않는다). 결과는 아래와 같다.

 

좌: 정상 로그인 시, 우: 로그인 계정 정보 불일치 시.

 

 

다시 http://127.0.0.1:8080/servletTest 페이지로 가보자. 로그인 계정 검증을 하는 URL 경로를 유추할 수도 없고, 심지어 계정 검증과 관련된 파일마저도 보이지 않기 때문에 JSP에 비해 훨씬 안전하다. 

 

 

만약 계정 검증을 진행하는 파일이 JSP로 작성된 것과 더불어 서버 보안마저 취약한 상황이라면(지금처럼 web.xml의 listing 설정이 true로 되어 있거나 tomcat 관련 파일을 외부에서 접근할 수 있는 상황 등) 계정정보를 탈취하는 것은 매우 쉬워진다. 또한 Servlet으로 작성하면 Servlet 파일명과 경로의 이름이 반드시 일치하지 않아도 되기 때문에 사용자로부터 완전히 숨겨진 상태에서도 필요한 작업을 수행할 수 있게 만들 수 있다.

 

 

 


 

위의 계정 검증 코드를 보고 이런 생각이 들 것이다.

 

"실제 웹 페이지에서 다루는 계정이 적어도 수 만 개는 될텐데, 이 계정 정보를 하나씩 if-else 문으로 조건 분기를 하는 것이 가능한가요?"

 

물론, 불가능하다. 반드시 저장해야하는 정보는 데이터베이스, 소위 DB 프로그램을 사용하며, 웹 서비스와 DB를 연동하게 된다.  과거에 인터넷이 발달하지 않은 시절에는 회원 가입 시 계정을 회원정보가 기록된 DB에 추가하거나 탈퇴 시 삭제하는 작업을 직접 DB 프로그램에서 수행했지만, 프로그래밍 언어로도 DB의 내용을 추가, 변경, 삭제하는 것이 가능해졌기 때문에 웹 서버에서 다루는 계정들 역시 JSP와 Servlet을 사용하여 DB화 할 수 있게 되었다.

 

다음 포스팅에서는 DB 프로그램(mySQL) 설치와 톰캣 사용을 위한 환경 구성에 대해 포스팅하려 한다.

 

 

Fin.

반응형

댓글