java-school logo

명함관리 웹 애플리케이션

본격적으로 서블릿/JSP를 공부하기 전에 JDBC에서 예제로 다루었던 명함관리를 웹 애플리케이션으로 바꾸는 실습을 한다. 이 실습믜 목표는 순수 자바 애플리케이션과 웹 애플리케이션의 차이에 대한 이해와 웹 환경 체험이다.
자세한 설명은 곧 다루니 이 절에선 목표에 충실하자.

명함관리를 웹 애플리케이션으로 바꾸기 위한 준비작업

1. 오라클 JDBC 드라이버를 {톰캣홈}/lib 에 복사한다.

JDBC 드라이버는 특별한 이유1 때문에 웹 애플리케이션의 WEB-INF/lib 가 아닌 {톰캣홈}/lib 에 있어야 한다.
다시 말해, WEB-INF/lib 에는 JDBC 드라이버가 없어야 한다.
오라클 JDBC 드라이버인 ojdbc6.jar 파일을 {톰캣홈}/lib 에 복사한다.

2. 웹 애플리케이션을 위한 디렉토리 구조를 마련한다.

C:/www/namecard 를 명함관리 웹 애플리케이션의 최상위 디렉토리로 정했다면
C:/www/namecard 아래 다음과 같은 서브 디렉토리를 만들어야 한다.

3. web.xml 파일을 WEB-INF 디렉토리에 만든다.

{톰캣홈}/webapps/ROOT/WEB-INF/web.xml 을 복사하여 C:/www/namecard/WEB-INF/에 붙여넣는다.
복사한 후 C:/www/namecard/WEB-INF/web.xml 파일을 편집기로 열고 web-app 엘리먼트 안에 있는 모든 내용을 지운다.

<?xml version="1.0" encoding="UTF-8"?>
<!--
 Licensed to the Apache Software Foundation (ASF) under one or more
  contributor license agreements.  See the NOTICE file distributed with
  this work for additional information regarding copyright ownership.
  The ASF licenses this file to You under the Apache License, Version 2.0
  (the "License"); you may not use this file except in compliance with
  the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
-->
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
  version="3.1"
  metadata-complete="true">

</web-app>

4. namecard.xml 컨텍스트 파일 만든다.

아래 내용대로 namecard.xml 파일을 만든 다음 {톰캣홈}/conf/Catalina/localhost 로 옮긴다.

<?xml version="1.0" encoding="UTF-8"?>
<Context
    docBase="C:/www/namecard"
    reloadable="true">
</Context>

명함관리 웹 애플리케이션 테스트

첫 번째 테스트

명함관리에서 실습했던 명함관리 Namecard 와 NamecardDao 바이트코드를 WEB-INF/classes 에 복사한다.

C:/www/namecard/WEB-INF/classes
                           └── net
                               └── java_school
                                       └── namecard - Namecard.class
                                                    - NamecardDao.class
						

아래 JSP 파일을 도큐먼트 베이스인 C:/www/namecard에 생성한다.
이클립스가 아닌 일반 에디터로 작업한다.

/list.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="net.java_school.namecard.*" %>
<%@ page import="java.util.*" %>    
<!DOCTYPE html>
<%
NamecardDao dao = new NamecardDao();
ArrayList<Namecard> list = dao.selectAll();
%>
<html>
<head>
<meta charset="UTF-8">
<title>명함목록</title>
</head>
<body>
<table border="1">
<tr>
	<td>번호</td>
	<td>이름</td>
	<td>손전화</td>
	<td>이메일</td>
	<td>회사</td>
</tr>
<%
int size = list.size();
for(int i = 0;i < size;i++) {
	Namecard card = list.get(i);
%>	
<tr>
	<td><%=card.getNo() %></td>
	<td><%=card.getName() %></td>
	<td><%=card.getMobile() %></td>
	<td><%=card.getEmail() %></td>
	<td><%=card.getCompany() %></td>
</tr>
<%
}
%>	
</table>
<p>
<input type="button" value="추가" onclick="location.href='write.jsp'" />
</p>
</body>
</html>

톰캣을 재실행한다.
http://localhost:8989/namecard/list.jsp를 방문하여 테스트한다.

두번째 테스트

첫 번째 테스트처럼 자바 클래스 소스를 웹 애플리케이션이 위치한 곳과 전혀 상관없는 곳에 두어도 된다.
현재 소스는 'JDBC'에서 실습한 디렉토리(이를테면 C:/java/namecard/src)에 있다.
하지만 이 경우, 시간이 지나면 소스의 위치를 잊을 수 있으며 소스를 지우는 실수를 할 수 있다.
두번째 테스트는 소스를 웹 애플리케이션의 영역안에 옮기고 그 소스를 컴파일하는 테스트이다.
먼저 자바 소스를 어디에 두어야 할지를 정해야 하는데, WEB-INF 아래에 두면 웹브라우저로 직접 접근할 수 없으니 소스 디렉토리를 WEB-INF/src로 정하겠다.
이제 JDBC에서 실습했던 명함관리 src 디렉토리를 복사하여 WEB-INF 에 붙여 넣는다.
다음으로 명령프롬프트에서 C:\www\namecad\WEB-INF\src\net\java_school\namecard 로 이동하여 다음과 같이 컴파일을 수행한다.2

javac -d C:/www/namecard/WEB-INF/classes *.java

http://localhost:8989/namecard/list.jsp를 방문하여 테스트한다.

세번째 테스트

이클립스를 이용해서 작업하는 방법을 설명한다.
이클립스를 실행한다.
워크스페이스를 C:/www로 선택한다.
워크스페이스를 C:/www로 선택한다.
퍼스펙티브가 java3 인 상태에서 File - New - Java Project 메뉴를 차례로 선택하여 새로운 자바 프로젝트를 namecard란 이름으로 만든다.
Java 퍼스펙티브가 선택된 상태에서 Java Project 를 만든다.
우리는 순수 자바 애플리케이션 아닌 웹 애플리케이션을 작성하고 있으므로 이클립스가 디폴트로 셋팅해준 src와 bin를 그대로 사용해서는 안 된다.
프로젝트에 마우스를 선택한 상태에서 마우스 오른쪽 버튼을 클릭한다.
Build Path - Configure Build Path..를 선택한다.
Source탭에서 Source folder 는 WEB-INF/src 를 선택한다.
Default output folder 는 WEB-INF/classes 를 선택해야 한다.
명함관리 웹 APP 이클립스 소스 폴더와 Output 폴더 설정
이제 이클립스에서 소스를 수정하면 따로 컴파일하지 않아도 바이트 코드가 WEB-INF/classes에 생긴다.
http://localhost:8989/namecard/list.jsp를 방문하여 테스트한다.
테스트가 성공했다면 명함을 등록하는 JSP파일을 C:/www/namecard 에 만든다.
먼저 이클립스의 JSP 템플릿의 캐릭터셋을 EUC-KR 에서 UTF-8로 변경한다.
이클립스에서 Windows - Preferences - Web - JSP Files 선택하고 인코딩 박스에서 UTF-8를 선택하고 Apply 클릭한다.
JSP 파일인코딩 UTF-8로
아래 write.jsp 소스에서 강조된 부분이 여러분이 직접 입력해야 하는 부분이다.

/write.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>명함 추가</title>
</head>
<body>
<h1>명함 추가하기</h1>
<form action="write_proc.jsp" method="post">
이름 : <input type="text" name="name" /><br />
손전화 : <input type="text" name="mobile" /><br />
이메일 : <input type="text" name="email" /><br />
회사 : <input type="text" name="company" /><br />
<input type="submit" value="확인" />
<input type="button" value="취소" onclick="location.href='list.jsp'" />
</form>
</body>
</html>

다음은 write_proc.jsp 파일을 만든다.
이 페이지는 write.jsp 에서 전송받은 값으로 명함을 추가한다.
아래 소스에서 강조된 부분이 여러분이 직접 입력해야 하는 부분이다.

/write_proc.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="net.java_school.namecard.*" %>
<!DOCTYPE html>
<%
request.setCharacterEncoding("UTF-8");
String name = request.getParameter("name");
String mobile = request.getParameter("mobile");
String email = request.getParameter("email");
String company = request.getParameter("company");
Namecard namecard = new Namecard(name,mobile,email,company);
NamecardDao dao = new NamecardDao();
dao.insert(namecard);
%>
<html>
<head>
<meta charset="UTF-8">
<title>명함 추가</title>
</head>
<body>
명함이 추가되었습니다.<a href="list.jsp">목록</a>
</body>
</html>

http://localhost:8989/namecard/list.jsp를 방문한다.
명함목록에서 등록버튼을 클릭하여 새로운 명함 등록을 추가하는 테스트를 한다.
다음은 삭제기능을 구현한다.
먼저 list.jsp 에서 아래를 참조해서 테이블의 열를 추가한다.

/list.jsp
.. 중간 생략 ..

    <td>번호</td>
    <td>이름</td>
    <td>손전화</td>
    <td>이메일</td>
    <td>회사</td>
    <td>관리</td>

.. 중간 생략 ..

    <td><%=card.getNo() %></td>
    <td><%=card.getName() %></td>
    <td><%=card.getMobile() %></td>
    <td><%=card.getEmail() %></td>
    <td><%=card.getCompany() %></td>
    <td><a href="delete.jsp?no=<%=card.getNo() %>">삭제</a></td>

.. 중간 생략 ..	

다음은 delete.jsp 작성한다.
delete.jsp 는 list.jsp에서 명함의 Primary key 에 해당하는 값을 전달받아 삭제를 수행한다.
아래 소스에서 강조된 부분이 직접 입력해야 하는 부분이다.

/delete.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="net.java_school.namecard.*" %>
<!DOCTYPE html>
<%
int no = Integer.parseInt(request.getParameter("no"));
NamecardDao dao = new NamecardDao();
dao.delete(no);
%>
<html>
<head>
<meta charset="UTF-8">
<title>명함 삭제</title>
</head>
<body>
명함이 삭제되었습니다.<a href="list.jsp">목록</a>
</body>
</html>

http://localhost:8989/namecard/list.jsp를 방문한다.
명함을 삭제하는 테스트를 수행한다.
다음으로 수정기능 구현한다.
NamecardDao.java 에 수정을 담당하는 메서드는 아래와 같다.

NamecardDao.java
public void update(Namecard card) {
	String sql = "UPDATE namecard " +
			"SET name = ? " +
			",mobile = ? " +
			",email = ? " +
			",company = ? " +
			"WHERE no = ?";
			
	Connection con = null;
	PreparedStatement pstmt = null;
	
	try {
		con = getConnection();
		pstmt = con.prepareStatement(sql);
		pstmt.setString(1, card.getName());
		pstmt.setString(2, card.getMobile());
		pstmt.setString(3, card.getEmail());
		pstmt.setString(4, card.getCompany());
		pstmt.setInt(5, card.getNo());
		pstmt.executeUpdate();
		
.. 중간 생략 ..

list.jsp 파일에서 수정양식을 보여주는 페이지로 이동하는 링크를 삭제링크 옆에 작성한다.
아래를 참고한다.

/list.jsp
<td>
	<a href="delete.jsp?no=<%=card.getNo() %>">삭제</a>
	<a href="modify.jsp?no=<%=card.getNo() %>">수정</a>
</td>

다음은 modify.jsp 파일을 작성한다.
참고로 modify.jsp 는 사용자 UI 통일성을 주기 위해서 wirte.jsp 소스를 copy & paste 한후 약간의 추가 작업을 하여 만들었다.

/modify.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="net.java_school.namecard.*" %>
<!DOCTYPE html>
<%
int no = Integer.parseInt(request.getParameter("no"));
NamecardDao dao = new NamecardDao();
Namecard card = dao.selectOne(no);
%>
<html>
<head>
<meta charset="UTF-8">
<title>명함 수정</title>
</head>
<body>
<h1>명함 수정하기</h1>
<form action="modify_proc.jsp" method="post">
<input type="hidden" name="no" value="<%=no %>" />
이름 : <input type="text" name="name" value="<%=card.getName() %>" /><br />
손전화 : <input type="text" name="mobile" value="<%=card.getMobile() %>" /><br />
이메일 : <input type="text" name="email" value="<%=card.getEmail() %>" /><br />
이메일 : <input type="text" name="company" value="<%=card.getCompany() %>" /><br />
<input type="submit" value="확인" />
<input type="button" value="취소" onclick="location.href='list.jsp'" />
</form>
</body>
</html>

<input type="hidden" name="no" value="<%=no %>" /> 이 부분이 폼태그에 반드시 있어야 한다.
다음은 modify_proc.jsp 파일을 작성한다.
이 페이지는 modify.jsp에서 전송된 값으로 명함을 수정하는 페이지이다.

modify_proc.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="net.java_school.namecard.*" %>
<!DOCTYPE html>
<%
request.setCharacterEncoding("UTF-8");
int no = Integer.parseInt(request.getParameter("no"));
String name = request.getParameter("name");
String mobile = request.getParameter("mobile");
String email = request.getParameter("email");
String company = request.getParameter("company");
Namecard card = new Namecard();
card.setNo(no);
card.setName(name);
card.setMobile(mobile);
card.setEmail(email);
card.setCompany(company);
NamecardDao dao = new NamecardDao();
dao.update(card);
%>
<html>
<head>
<meta charset="UTF-8">
<title>명함 수정</title>
</head>
<body>
명함이 수정되었습니다. <a href="list.jsp">목록</a>
</body>
</html>

http://localhost:8989/namecard/list.jsp를 방문하여 수정을 테스트한다.

다음으로 list.jsp 에 검색기능을 추가한다.
list.jsp 열고 </body> 전에 다음 폼을 추가한다.

/list.jsp
<form action="list.jsp" method="post">
	<input type="text" name="keyword" />
	<input type="submit" value="검색" />
</form>

검색을 위해서 NamecardDao.java 에 selectByKeyword(String keyword) 메서드를 추가한다.

NamecardDao.java
public ArrayList<Namecard> selectByKeyword(String keyword) {
	keyword = "%" + keyword + "%";
	ArrayList<Namecard> matched = new ArrayList<Namecard>();
		
	String sql ="SELECT no,name,mobile,email,company " + 
				"FROM namecard " +
			"WHERE name LIKE ? " +
				"OR mobile LIKE ? " +
				"OR email LIKE ? " + 
				"OR company LIKE ? " +
			"ORDER BY no DESC";
			
	Connection con = null;
	PreparedStatement pstmt = null;
	ResultSet rs = null;
	
	try {
		con = getConnection();
		pstmt = con.prepareStatement(sql);
		pstmt.setString(1, keyword);
		pstmt.setString(2, keyword);
		pstmt.setString(3, keyword);
		pstmt.setString(4, keyword);
		rs = pstmt.executeQuery();
		
		while(rs.next()) {
			int no = rs.getInt("no");
			String sname = rs.getString("name");
			String mobile = rs.getString("mobile");
			String email = rs.getString("email");
			String company = rs.getString("company");
			Namecard namecard = new Namecard(no,name,mobile,email,company);
			matched.add(namecard);
		}
	} catch (SQLException e) {
		e.printStackTrace();
		System.out.println(sql);
	} finally {
		close(rs,pstmt,con);
	}
	
	return matched;
}

검색을 테스트하기 위해 list.jsp 을 방문하면 null로 검색이 되는 버그가 있다.
list.jsp 를 웹브라우저의 주소창에서 처음 방문할 때는 keyword 가 null 이 되기 때문이다.
그리고 list.jsp 파일에서 검색필드에 아무런 값도 넣지 않고 검색 버튼을 클릭했다면 keyword 는 ""(공백문자)이다.
list.jsp 을 열고 아래 코드를 참고하여 수정한다.

/list.jsp
<%
//기존 코드는 주석처리한다.
//NamecardDao dao = new NamecardDao();
//ArrayList<Namecard> list = dao.selectAll();

request.setCharacterEncoding("UTF-8");
String keyword = request.getParameter("keyword");

NamecardDao dao = new NamecardDao();
ArrayList<Namecard> list = null;

if (keyword == null) {
	keyword = "";
}
if (keyword.equals("")) {
	list = dao.selectAll();
} else {
	list = dao.selectByKeyword(keyword);
}
%>

모두 작성했다면 http://localhost:8989/namecard/list.jsp를 방문하여 테스트한다.

주석
  1. 각각의 웹 애플리케이션의 WEB-INF/lib 에 JDBC 드라이버 파일을 두면 메모리 누수문제가 일어날 수 있다.
  2. 만약 NamecardDao 클래스가 커넥션 풀을 이용한다면 커넥션풀관련 클래스를 앞서 컴파일 해야한다.
  3. 이와는 달리 대부분이 책에서 퍼스펙티브가 Java EE 에서 Dynamic Web Project 로 프로젝트를 생성하는 방법을 설명한다. 본 사이트에서 제공하는 기초 과정을 모두 공부한 다음이 아니고, 이클립스보다 서블릿/JSP에 초점을 맞추려면 본 사이트에서 제시한 방법이 더 낫다.
참고