7 분 소요

이제 웹 페이지에서 사용자가 입력한 데이터들과 함께 요청이 들어오면, 이를 서블릿을 이용하여 사용자가 입력한 데이터를 사용자 화면에 다시 출력해보는 연습을 해보자. 여기서는 회원가입 페이지를 신설하여 사용자로부터 여러 가지 데이터를 입력하게 하고, “회원 가입” 버튼을 눌러 서블릿으로 해당 요청이 넘어가면, 서블릿에서 전달받은 사용자 데이터들을 응답 페이지로 출력해보자.

회원가입 페이지 만들기

회원가입 페이지를 만들기 위해 다음과 같이 몇몇 파일들을 추가하였다.

C:.
  login.html
  profileImage.js
  signUp.css
  signUp.html
  style.css

├─images
    jerocaller.jpg
  
  └─profile
          cake.png
          coffee.png
          cool-cola.png
          flowers.png
          lighthouse.jpg
          spaceman.jpg

├─META-INF
      MANIFEST.MF

└─WEB-INF
      web.xml
    
    └─lib
            h2-2.3.232.jar

추가된 파일들은 signUp.html, signUp.css, profileImage.js, images/profile 내 이미지 파일들이다.

다음은 회원가입 페이지를 구성하는 각 파일 내부의 코드이다.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Insert title here</title>
    <link rel="stylesheet" href="signUp.css" />
    <script type="module" src="profileImage.js"></script>
  </head>
  <body>
    <div id="container">
      <h1>회원 가입</h1>
      <hr />
      <form action="signUp.do" method="post">
        <ul>
          <li>
            <label for="id">아이디 (20글자 내) </label>
            <input type="text" name="id" maxlength="20" />
          </li>
          <li>
            <label for="password">비밀번호 (20글자 내) </label>
            <input type="password" name="password" maxlength="20" />
          </li>
          <li>
            <label for="check-password">비밀번호 재확인 </label>
            <input type="password" name="check-password" maxlength="20" />
          </li>
          <li>
            <span>권한 </span>
            <div>
              <input
                type="radio"
                name="role"
                value="USER"
                id="user"
                checked="checked"
              />
              <label for="user">USER</label>
              <input type="radio" name="role" value="ADMIN" id="admin" />
              <label for="admin">ADMIN</label>
            </div>
          </li>
          <li>
            <label for="intro">자기 소개 </label>
            <textarea name="intro" rows="5" cols="30"></textarea>
          </li>
          <li>
            <span>관심 분야</span>
            <div>
              <input
                type="checkbox"
                value="horror"
                name="favorite"
                id="horror"
              />
              <label for="horror">공포</label>
              <input
                type="checkbox"
                value="science"
                name="favorite"
                id="science"
              />
              <label for="science">과학</label>
              <input
                type="checkbox"
                value="coding"
                name="favorite"
                id="coding"
              />
              <label for="coding">코딩</label>
              <input type="checkbox" value="cook" name="favorite" id="cook" />
              <label for="cook">요리</label>
            </div>
          </li>
          <li>
            <span>기본 프로필 이미지 선택</span>
            <select id="profile-image" name="profile-image">
              <option value="cake.png" selected="selected">케이크</option>
              <option value="coffee.png">커피</option>
              <option value="cool-cola.png">콜라</option>
              <option value="flowers.png">꽃다발</option>
              <option value="lighthouse.jpg">등대</option>
              <option value="spaceman.jpg">우주인</option>
            </select>
            <img src="" width="100" height="100" />
          </li>
        </ul>

        <input type="submit" value="회원 가입" />
        <input type="reset" value="취소" />
      </form>
    </div>
  </body>
</html>

signUp.html

@charset "UTF-8";

#container {
	width: 30em;
	margin: 0 auto;
	padding: 1em;
	border: 1px solid black;
	text-align: center;
}

form > ul {
	list-style: none;
}

form > ul > li {
	display: flex;
	align-items: center;
	justify-content: space-between;
	
	margin-bottom: 1em;
}

signUp.css

const select = document.querySelector("select");
const imageTag = document.querySelector("img");

let imageRootPath = "images/profile/";

imageTag.setAttribute("src", imageRootPath + select.value);
select.addEventListener("change", () => {
	imageTag.setAttribute("src", imageRootPath + select.value);
});

profileImage.js

서버를 구동하여 http://localhost:8080/signUp.html 경로로 가보면 다음의 화면과 같이 나온다.

1.PNG

singUp.html 에는 사용자가 각종 정보들을 입력할 수 있는 입력 요소들이 하나의 form 태그 내부에 정의되어 있다. 이제 이 form 요소에 연결할 서블릿 클래스를 하나 작성하겠다.

요청 처리할 서블릿 객체 생성

src/main/java/com/cola/web/user 폴더에서 우클릭 하여 [New] → [Other] 클릭 후 나오는 wizard 창에서 [Web] 폴더의 [Servlet] 을 눌렀다. 그러자 다음의 창이 떴다.

2.PNG

여기서는 SignUpServlet 이라고 서블릿 클래스 이름을 지어줬다.

3.PNG

다음의 창에서 [Name] 에는 서블릿 이름을 “signUp”이라 지었다. [URL mappings] 목록에 있던 “/signUp”을 마우스로 클릭하여 선택한 후, 오른쪽의 [Edit] 버튼을 누르면 URL mapping 부분의 이름을 재설정할 수 있다. 여기서는 “/signUp.do”라고 설정하였다.

4.PNG

여기서는 기본 생성자를 사용할 이유도 없고, 오로지 POST 요청에 대해서만 처리할 것이기에 doPost() 메서드만 생성하기로 하였다.

[Finish] 버튼을 눌러 생성된 서블릿 클래스 내에 다음과 같이 코드를 작성하였다.

package com.cola.web.user;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class SignUpServlet
 */
public class SignUpServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("쿼리 스트링: " + request.getQueryString());
		
		String id = request.getParameter("id");
		String password = request.getParameter("password");
		
		System.out.println("id: " + id);
		System.out.println("password: " + password);
	}

}

SignUpServlet.java

만약 서버가 이미 실행 중이라면 재구동하고, http://localhost:8080/signUp.html 에 들어가보자. 그 후 다음과 같이 데이터를 입력해보았다.

5.PNG

6.PNG

위 코드를 통해 HTTP 요청을 통해 들어온 데이터를 콘솔창에 출력할 수 있음을 알 수 있다. 이는 HTTP 요청 정보를 담는 doPost() 메서드의 첫 번째 매개변수인 HttpServletRequest 객체 참조 변수인 request가 보유하고 있는 메서드들 덕분이다. 즉 이 객체를 활용하면 사용자로부터 입력받은 데이터를 추출할 수 있다는 뜻이다.

이번에는 사용자로부터 입력받은 데이터들을 응답 페이지로 다시 출력해주는 코드로 전환해보겠다. 그러면 사용자 입장에서도 브라우저를 통해 서버로부터 전송된 응답 메시지를 받아볼 수 있다. SignUpServlet 클래스 내 doPost() 메서드 내부의 코드를 다음과 같이 변경해보았다.

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//request.setCharacterEncoding("UTF-8");
		//response.setCharacterEncoding("euc-kr");
		
		PrintWriter printToPage = response.getWriter();
		
		printToPage.println("<html><body>");
		printToPage.println("<ul>");
		Enumeration<String> names = request.getParameterNames();
		while(names.hasMoreElements()) {
			String name = names.nextElement();
			
			printToPage.print("<li>" + name + " : ");
			for (String value : request.getParameterValues(name)) {
				printToPage.print(value + " | ");
			}
			printToPage.println("</li>");  // 개행
		}
		
		printToPage.println("</ul>");
		printToPage.println("</body></html>");
		
		printToPage.close();
	}

SignUpServlet.java

서버 재구동 후, 이번에는 모든 데이터를 입력하고 “회원 가입” 버튼을 눌러보았다.

7.PNG

8.PNG

사용자로부터 입력받은 데이터를 다시 응답 페이지로 보여주었다. 응답 페이지로 응답 메시지를 출력하기 위해 HttpServletResponse 객체 참조 변수인 response의 getWriter() 메서드를 호출하여 그로부터 반환된 PrintWriter라는 입출력 스트림 객체를 이용하였다. 이 입출력 스트림 객체의 println() 등의 메서드를 활용하면, 문자열 내부에 html 태그까지 작성하여 조금 더 형식을 갖춘 응답 페이지를 구성할 수 있게 된다. 위 예제에서는 사용자가 입력한 정보들을 리스트 형식으로 화면에 출력하고 있다.

그런데, 위 응답 페이지에서 글자가 깨진 곳이 있다. 한글의 인코딩이 제대로 되지 않은 것이다. 위 서블릿 코드에서 인코딩을 담당하는 코드의 주석을 해제하고 다시 실행해보자.

9.PNG

그러면 위와 같이 한글도 깨지지 않고 잘 출력된다.

이제, 서블릿 내부에서 사용자로부터 입력받은 데이터를 추출하고, 이를 응답 페이지로 출력하는 데에 사용된 메서드들을 정리해보겠다.

먼저 HTTP 요청을 통해 들어오는 사용자 입력 데이터를 추출할 때 사용되는 메서드로 다음이 존재한다. 참고로 HTTP 요청 정보는 앞서 본 doPost() 메서드의 첫 번째 매개변수인 HttpServletRequest 객체의 메서드를 이용하면 된다. 다음의 메서드들도 모두 HttpServletRequest 클래스 또는 해당 클래스의 부모 클래스인 ServletRequest 클래스의 메서드이다.

  • String getQueryString() : 브라우저의 URL에 있는 쿼리 문자열을 반환한다. get 요청에 대해서만 나오며, post 요청에서는 사용자 입력 데이터가 url에 쿼리 문자열로 반영되지 않으므로 이 경우에는 null이 반환된다.
  • String getParameter(String name) : 인자로 전달된 name에 해당하는 value값을 하나 반환한다. 여기서 name과 value는 html 코드 내 form 태그 내부에 작성한 입력 요소(input 태그 등)의 속성값인 name과 value를 각각 의미한다. 예를 들어 getParameter("id") 를 호출하면 <input type="text" name="id" maxlength="20" /> 태그에 있는 value라는 속성값을 반환한다. 이 value의 속성값에는 사용자가 “아이디” 란에 입력한 정보가 저장되어 있는 것이다. 만약 name에 배정된 value 값들이 여러 개일 경우, 맨 처음 값만 반환된다고 한다. name에 여러 value들이 있을 경우, getParameterValues() 메서드를 대신 사용한다.
  • String[] getParameterValues(String name) : name에 해당하는 value 값들이 여러 개일 경우 사용하는 메서드로, value값들이 문자열의 배열로 반환된다.
  • Enumeration<String> getParameterNames() : 제출된 form 태그 내에 있던 모든 name 값들을 배열로 가져온다. Enumeration 객체의 hasMoreElements()와 nextElement() 메서드와 반복문을 이용하면 모든 name값들을 순차적으로 가져와 사용할 수 있다. 앞선 서블릿 코드에서는 사용자가 입력한 모든 종류의 데이터들을 출력하기 위해 사용되었으며, 각 name에 대해 getParameterValues(String name) 메서드와 함께 사용하여 모든 데이터들을 응답 페이지로 출력하였다.
  • void setCharacterEncoding(String env) throws UnsupportedEncodingException : 요청 데이터를 어떤 인코딩 방식으로 해석할지 설정할 수 있는 메서드. 사용자가 입력한 데이터를 한글 방식으로 해석해오기 위해 위 코드에서는 인자로 “UTF-8”로 설정하였다. 실험 결과, 해당 메서드만으로는 제대로 응답 메시지에 한글로 출력할 수 없었고, 다음에 소개할 HttpServletResponse 클래스의 같은 이름의 메서드에 “euc-kr” 인코딩 방식도 같이 설정해야 한글로 제대로 나오는 것을 확인하였다.

다음은 doPost() 메서드의 두 번째 매개변수인 HttpServletResponse 클래스에 있는 몇몇 메서드들이다. 이 중에는 부모 클래스인 ServletResponse 에 정의되어 있을 수 있다.

  • PrintWriter getWriter() : HTTP 응답과 연결된 출력 스트림 반환. 이 반환받은 객체를 통해 응답 페이지로서 사용자에게 여러 정보들을 보여주도록 데이터를 출력할 수 있다. 스트림 객체라서 사용을 다 하면 마지막에 반드시 close() 메서드를 호출하여 닫아줘야 한다.
  • void setCharacterEncoding(String charset) : 클라이언트로 응답으로 내보낼 문자의 인코딩 방식을 설정한다. 실험결과, response에 대해서는 “euc-kr”로 설정해야 한글이 깨지지 않음.
  • void setStatus(int sc) : 응답으로 내보낼 상태 코드를 지정할 수 있다.
  • void sendRedirect(String location) throws IOException : 인자로 지정한 경로에 해당하는 웹 페이지를 재요청하도록 한다. 즉, 해당 웹 페이지로 리다이렉트된다.

이 외에도 HttpServletRequest 및 HttpServletResponse에는 다양한 메서드들이 있으니 다음의 사이트를 참고.

HttpServletRequest (Java(TM) EE 8 Specification APIs)

HttpServletResponse (Java(TM) EE 8 Specification APIs)

DB 연동

이제 앞서 만든 회원가입 페이지와 DB를 연동하여, 사용자로부터 입력받은 회원가입 데이터들 중 일부를 DB에 삽입하도록 해보겠다. 코드가 복잡해질 수 있어 이전([JDBC] DB 구축과 JDBC 페이지 참조)에 만들었던 DBDaoVo 및 DBVo 클래스를 사용할 것이다. 해당 클래스에는 이전에 만들었던 테이블인 site_user와 연동되고, 해당 테이블에는 아이디, 패스워드, 권한, 가입일자, 고유 번호만을 필드로 가지고 있기에 앞서 회원가입 페이지로 구현한 다른 데이터들까지 담으려면 아예 새 테이블을 만들던가 해야하고, 관련 코드도 추가해야 한다. 이러면 복잡해지기에 여기서는 그저 DB를 웹 앱과 연동하여 사용할 수 있다는 점만 살펴보겠다.

앞선 SignUpServlet 클래스의 doPost 메서드 내부 코드를 다음과 같이 변경하였다 .

// 생략
import com.cola.db.lib.DBDaoVo;
import com.cola.db.lib.DBVo;

// 생략
public class SignUpServlet extends HttpServlet {
    // 생략
    
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// 데이터 인코딩 설정
		request.setCharacterEncoding("UTF-8");
		response.setCharacterEncoding("euc-kr");
		
		// 사용자 입력 데이터 추출
		String userId = request.getParameter("id");
		String password = request.getParameter("password");
		String role = request.getParameter("role");
		int uniqueId = 4;
		
		// DB 연동 및 데이터 작업 처리
		DBVo vo = new DBVo();
		vo.setUniqueId(uniqueId);
		vo.setUserName(userId);
		vo.setPassword(password);
		vo.setRole(role);
		
		DBDaoVo daoVo = new DBDaoVo();
		System.out.println("기존 테이블 내용");
		daoVo.printAllRecords();
		System.out.println();
		
		System.out.println("데이터 추가 후");
		daoVo.insertOneRecord(vo);
		daoVo.printAllRecords();
		
		// 회원가입 성공 후 로그인 화면으로 리다이렉트한다. 
		response.sendRedirect("login.html");
}

SingUpServlet.java

서버 재구동 후, h2 DB를 킨 상태에서 회원가입을 진행해보자.

DB 연동 테스트를 위해 회원가입을 진행하고 있다.

DB 연동 테스트를 위해 회원가입을 진행하고 있다.

“회원 가입” 버튼 클릭 후 브라우저 화면 상에서는 다시 로그인 화면으로 리다이렉트되었다. response.sendRedirect("login.html"); 메서드 호출 때문에 이러한 연출이 나온 것이다.

“회원 가입” 버튼 클릭 후 브라우저 화면 상에서는 다시 로그인 화면으로 리다이렉트되었다. response.sendRedirect(“login.html”); 메서드 호출 때문에 이러한 연출이 나온 것이다.

기존 테이블 내용
=== site_user table ===
unique_id 	 user_name 	 password 	 role 	 sign_date
-------------------------------------------------------------------
1 		 kimquel123 	 kimquel1111 	 USER 	 2024-09-01
2 		 jeongdb123 	 jeongdb1111 	 ADMIN 	 2024-09-01
3 		 naithon789 	 naithon3333 	 USER 	 2024-09-01

데이터 추가 
1 처리되었습니다.
=== site_user table ===
unique_id 	 user_name 	 password 	 role 	 sign_date
-------------------------------------------------------------------
1 		 kimquel123 	 kimquel1111 	 USER 	 2024-09-01
2 		 jeongdb123 	 jeongdb1111 	 ADMIN 	 2024-09-01
3 		 naithon789 	 naithon3333 	 USER 	 2024-09-01
4 		 jerocaller 	 jerocaller123 	 USER 	 2024-09-01

이클립스의 콘솔창. 회원 가입 진행 후 DB에 앞서 회원 가입 페이지에서 사용자가 입력한 일부 데이터들이 DB에 삽입된 것을 확인할 수 있다. 이로써, 웹 페이지에서 사용자로부터 입력받은 데이터를 DB와 연동하여 DB에 삽입하는 간단한 과정에 대해서도 살펴보았다. 위 과정에서는 새로 입력받은 데이터를 그저 DB에 추가하는 작업만 해보았는데, DB 연동을 잘 활용하면 로그인 화면에서 사용자가 입력한 데이터를 DB와 대조하여 아이디와 패스워드를 맞게 입력했는지 확인할 수 있는 로그인 인증 기능도 구현해볼 수 있다.


References

[1] 에이콘아카데미(강남) 강의

[2] 채규태, “채쌤의 Servlet&JSP 프로그래밍 핵심”, (쌤즈, 2023)

This content is licensed under CC BY-NC 4.0

댓글남기기