주요글: 도커 시작하기
반응형
자바빈 컴포넌트에서 데이터베이스에 연결함으로써 표현과 구현의 분리를 좀더 명확하게 할 수 있다.

자바빈 컴포넌트와 데이터 처리

많은 개발자들이 웹 어플리케이션을 개발하기 위해 JSP(Java Server Pages)를 선택하고 있다. JSP를 사용함으로써 개발자들은 자바의 장점을 사용할 수 있으며, 또한 표현부(presentation)와 구현부(implementation)를 손쉽게 구분할 수 있게 된다. 특히 자바빈 컴포넌트와 커스텀 태그의 지원은 컴포넌트의 관점에서 웹 페이지를 개발할 수 있는 프레임워크를 제공한다.

하지만, 개발자 중의 다수가 JSP의 프레임워크를 올바르게 사용하고 있지 않으며, 심지어 JSP를 개발할 때, ASP나 PHP의 코드를 자바로 변환하는 정도로 생각하는 경우도 있다. 이러한 것의 대표적인 경우가 JSP 페이지에서 직접 데이터베이스에 접근하는 것이다. 물론, 필요에 따라 JSP 페이지에서 직접 데이터베이스에 접근할 수 있지만, 대부분의 경우에 JSP 페이지에서 직접적으로 데이터베이스에 접근하는 것은 올바르지 못하며, 웹 어플리케이션의 전체 구조를 견고하지 못하게 만드는 요인이 될 수 있다.

이 글에서는 자바빈 컴포넌트에서 데이터베이스와 관련된 작업을 처리하도록 함으로써 이러한 문제를 해결하는 것에 대해서 알아볼 것이다. 이렇게 함으로써 JSP 페이지로부터 구현부와 관련된 부분을 제거할 수 있으며, 따라서 JSP 페이지는 표현부의 역할에 좀더 충실해질 수 있다. 다시 말해서, 표현부와 구현부를 좀더 명확하게 분리할 수 있는 것이다.

자바빈 컴포넌트

이 글에서 예를 들기 위해 사용할 자바빈 컴포넌트는 회원 정보를 나타내는 MemberBean 클래스이다. MemberBean 컴포넌트는 웹사이트에서 가입할 때 주로 사용되는 회원아이디, 패스워드, 회원 이름, 회원의 주소, 연락처 정보를 저장하고 있으며, 이들은 다음 표에 명시한 프로퍼티를 사용하여 표시된다.

MemberBean 컴포넌트의 프로퍼티 목록
프로퍼티 이름 타입 설명
memberID String 회원 아이디 저장
memberPassword String 회원의 로그인 암호
memberName String 회원의 이름
memberPhone String 회원의 전화번호
memberAddress String 회원의 주소
MemberBean 클래스의 소스 코드는 다음과 같은 골격을 갖게 된다.

package javacan.member.bean;

import java.sql.*;

public class MemberBean implements Serializable {
   
   private String memberID;
   private String memberPassword;
   private String memberName;
   private String memberPhone;
   private String memberAddress;
   
   public void setMemberID(String memberID) {
      this.memberID = memberID;
   }
   public String getMemberID() {
      return memberID;
   }
   public void setMemberPassword(String memberPassword) {
      this.memberPassword = memberPassword;
   }
   public String getMemberPassword() {
      return memberPassword;
   }
   
   public void setMemberName(String memberName) {
      this.memberName = memberName;
   }
   public String getMemberName() {
      return memberName;
   }
   public void setMemberPhone(String memberPhone) {
      this.memberPhone = memberPhone;
   }
   public String getMemberPhone() {
      return memberPhone;
   }
   public void setMemberAddress(String memberAddress) {
      this.memberAddress = memberAddress;
   }
   public String getMemberAddress() {
      return memberAddress;
   }
   
   // 나머지 부분이 온다.

}

위 코드는 자바빈 컴포넌트의 전형적인 형태를 취하고 있다. 여기에 차례대로 데이터베이스와 관련된 작업을 추가해나가보자.

데이터 삽입

데이터를 삽입이라고 제목을 붙이긴 했지만, 특별히 어려운 것은 아니다. 우리가 흔히 테이블에 데이터를 삽입할 때 사용하는 쿼리문을 자바빈 컴포넌트에서 실행한다고 생각하면 된다. MemberBean 컴포넌트는 저장하고 있는 데이터를 DB 테이블에 삽입해주는 메소드인 insert()를 정의하며, 이는 다음과 같다.

   public void insert() throws SQLException {
      Connection conn = null;
      PreparedStatement pstmt = null;
      
      try {
         conn = DriverManager.getConnection("...");
         pstmt = conn.prepareStatement("insert into Member values (?,?,?,?,?)");
         pstmt.setString(1, memberID);
         pstmt.setString(2, memberPassword);
         pstmt.setString(3, memberName);
         pstmt.setString(4, memberPhone);
         pstmt.setString(5, memberAddress);
         
         pstmt.executeUpdate();
      } finally {
         if (pstmt != null) try { pstmt.close(); } catch(SQLException ex1) {}
         if (conn != null) try { conn.close(); } catch(SQLException ex1) {}
      }
   }

여기서 DriverManager.getConnection() 메소드에 전달된 JDBC URL은 해당 DBMS에 알맞은 걸 사용할 것이다. 데이터베이스 커넥션 풀링을 사용함으로써 성능 향상을 꾀할 수도있지만, 이 글에서는 자바빈 컴포넌트가 데이터베이스 작업을 한다는 점에 초점을 맞추고 있으므로 간단한 코드를 사용한다.

이제 회원가입 폼을 보여주는 registerForm.html과 자바빈 컴포넌트를 사용하여 가입처리를 하는 register.jsp를 살펴보자. registerForm.html은 단순한 HTML로서 다음과 같다.

<html>
<head><title>회원가입폼</title></head>
<body>
<form action="register.jsp" method="post">
아이디: <input type="text" name="memberID" size="10"> <br>
암호: <input type="password" name="memberPassword" size="10"> <br>
이름: <input type="text" name="memberName" size="20"> <br>
전화: <input type="text" name="memberPhone" size="15"> <br>
주소: <input type="text" name="memberAddress" size="40"> <br>
<input type="submit" value="등록">
</form>
</body>
</html>

register.jsp 페이지는 registerForm.html을 통해 입력한 정보를 사용하여 가입신청을 처리하는 부분으로서 다음과 같다.

<jsp:useBean id="member" class="javacan.member.bean.MemberBean">
   <jsp:setProperty name="member" property="*" />
</jsp:useBean>
<html>
<head><title>회원가입완료</title></head>
<body>
<% member.insert() %>
<jsp:getProperty name="member" property="name" />님의 회원가입을
진심으로 축하합니다.
<p>
앞으로 많은 활동부탁드립니다.
</body>
</html>

위 코드를 보면 register.jsp는 가장 먼저 <jsp:useBean> 액션태그와 <jsp:setProperty> 액션태그를 사용하여 이전 페이지에서 사용자가 폼을 통해 입력한 정보를 MemberBean 컴포넌트저에 저장한다. 그런 후, MemberBean 컴포넌트의 insert() 메소드를 호출함으로써 사용자가 입력한 정보를 데이터베이스 저장하게 된다. 위 코드를 살펴보면 스크립트 부분이 단 한줄에 불과하다는 것을 알 수 있다. 이것이 바로 자바빈 컴포넌트에 역할을 집중시킬 때의 장점이라고 할 수 있다. 물론, insert() 메소드가 발생할 수 있는 예외를 알맞게 처리해야 하지만, 이는 register.jsp 페이지에 <%@ page errorPage=".." %> 를 추가함으로써 예외 처리 부분을 register.jsp 페이지로부터 분리해낼 수 있다.

데이터 조회, 변경, 삭제

데이터의 조회와 데이터 삭제 역시 데이터 삽입과 같은 방법으로 처리된다. 때에 따라서 조회와 삭제를 위한 자바빈 클래스를 별도로 작성할 수도 있다. 특히 조회의 경우는 한 사람의 회원을 조회할 수도 있고, 여러명의 회원을 조회할 수도 있다. 한 사람의 회원을 조회하는 경우에는 MemberBean 클래스에서 조회 코드를 위치시키는 것이 좋은 방법이 될 수 있지만, 한 사람이 아닌 여러명의 사용자를 동시에 조회하는 경우에는 자바빈 컴포넌트에서 처리하기 보다는 자바빈 컴포넌트가 나타내는 엔티티와 관련된 작업을 처리하는 매니저 클래스(EJB의 경우 세션빈에 해당한다)에서 처리하는 것이 좋을 수도 있다. 이 글에서는 MemberBean 클래스에 조회를 위한 메소드인 select()를 추가해보자.

select() 메소드는 한명의 회원을 검색하기 위해 사용되며, 파라미터로 회원의 ID를 전달받는다. MemberBean의 select() 메소드는 다음과 같다.

   public MemberBean select(String id) throws SQLException {
      Connection conn = null;
      PreparedStatement pstmt = null;
      ResultSet rs = null;
      
      try {
         conn = DriverManager.getConnection("...");
         pstmt = conn.prepareStatement(
             "select ID, PASSWD, NAME, PHONE, ADDR from Member where ID=?");
         pstmt.setString(1, id);
         rs = pstmt.executeQuery();
         if (rs.next()) {
            memberID = rs.getString(1);
            memberPassword = rs.getString(2);
            memberName = rs.getString(3);
            memberPhone = rs.getString(4);
            memberAddress = rs.getString(5);
            
            return this;
         } else {
            return null;
         }
      } finally {
         if (rs != null) try { rs.close(); } catch(SQLException ex1) {}
         if (pstmt != null) try { pstmt.close(); } catch(SQLException ex1) {}
         if (conn != null) try { conn.close(); } catch(SQLException ex1) {}
      }
   }

위에서 조회 결과가 없을 때는 null을 리턴하는 데, 때에 따라서는 조회 결과가 없는 경우에 예외를 발생하도록 하는 것이 좋을 수도 있다. 이러한 문제는 웹 어플리케이션의 전체적인 규칙에 따라서 결정되어야 할 것이다. 참고로, 필자의 경우 추가적으로 EmptyResultException과 같은 예외를 만들어 예외를 발생하는 방법을 주로 사용하고 있다.

조회를 위한 JSP 페이지는 좀더 간단하다. 먼저 조회를 하고자 하는 회원의 ID를 입력받는 폼이 필요할 것이다. 이 폼은 selectForm.html을 통해서 제공되며, selectForm.html의 소스 코드는 다음과 같다.

<html>
<head><title>조회폼</title></head>
<body>
<form action="select.jsp">
조회할 아이디: <input type="text" name="memberID" size="10">
<br>
<input type="submit" value="조회">
</form>
</body>
</html>

selectForm.html로부터 조회하고자하는 회원의 ID를 전달받아 MemberBean 컴포넌트를 사용하여 데이터를 데이터베이스로부터 읽어오는 select.jsp는 다음과 같다.

<jsp:useBean id="member" class="javacan.member.bean.MemberBean" />
<html>
<head><title>조회</title></head>
<body>
<%  if (member.select(request.getParameter("memberID")) == null ) {  %>
조회 결과가 없습니다.
<%  } else {  %>
아이디: <jsp:getProperty name="member" property="memberID" />
<br>
이름: <jsp:getProperty name="member" property="memberName" />
<br>
전화: <jsp:getProperty name="member" property="memberPhone" />
<br>
주소: <jsp:getProperty name="member" property="memberAddress" />
<%  }  %>
</body>
</head>

이제, 회원 정보를 변경하는 메소드인 update()를 추가해보자. 이 메소드는 현재 MemberBean 컴포넌트에 저장되어 있는 정보를 데이터베이스에 반영하는 역할을 한다. update() 메소드는 다음과 같다.

   public void update() throws SQLException {
      Connection conn = null;
      PreparedStatement pstmt = null;
      
      try {
         conn = DriverManager.getConnection("...");
         pstmt = conn.prepareStatement(
               "update Member set NAME=?, PHONE=?, ADDR=? where ID=?");
         pstmt.setString(1, memberName);
         pstmt.setString(2, memberPhone);
         pstmt.setString(3, memberAddress);
         pstmt.setString(4, memberID);
         
         pstmt.executeUpdate();
      } finally {
         if (pstmt != null) try { pstmt.close(); } catch(SQLException ex1) {}
         if (conn != null) try { conn.close(); } catch(SQLException ex1) {}
      }
   }

insert() 메소드와 거의 비슷하다는 것을 알 수 있다. 회원 정보를 변경하기 이해서는 두 개의 JSP 페이지가 필요하다. 한 개는 정보를 변경할 수 있는 폼을 제공해주는 JSP 페이지이고, 다른 하나는 폼에 입력한 정보를 실제로 데이터베이스 반영하는 JSP 페이지이다. 먼저 정보를 변경할 수 있는 폼을 제공해주는 updateForm.jsp를 살펴보자. 다음은 updateForm.jsp의 소스 코드이다.

<jsp:useBean id="member" class="javacan.member.bean.MemberBean">
<%  member.select(request.getParameter("memberID")); %>
<html>
<head><title>회원정보수정폼</title></head>
<body>
알맞게 정보를 변경하세요.
<form action="update.jsp" method="post">
<input type="hidden" name="memberID"
       value="<jsp:getProperty name="member" property="memberID" />" >
아이디: <jsp:getProperty name="member" property="memberID" /><br>
암호: <input type="password" name="memberPassword" size="10"
      value="<jsp:getProperty name="member" property="memberPassword" />" > <br>
이름: <input type="text" name="memberName" size="20"
     value="<jsp:getProperty name="member" property="memberName" />" > <br>
전화: <input type="text" name="memberPhone" size="15"
     value="<jsp:getProperty name="member" property="memberPhone" />"> <br>
주소: <input type="text" name="memberAddress" size="40"
     value="<jsp:getProperty name="member" property="memberAddress" />"> <br>
<input type="submit" value="변경">
</form>
</body>
</html>

updateForm.jsp는 변경할 회원의 ID를 파라미터를 통해서 입력받는다. 만약 파라미터가 아닌 쿠키나 세션을 사용해서 회원 아이디를 읽어온다면 그에 알맞게 변경하면 될 것이다. updateForm.jsp는 회원 ID를 읽어온 후, MemberBean.select() 메소드를 사용하여 해당 회원의 정보를 읽어온다. 그런 후, 화면에 값을 변경할 수 있는 화면을 보여준다.

update.jsp는 앞에서 살펴본 insert.jsp와 거의 유사하다. <jsp:useBean> 태그를 사용하여 사용자가 입력한 회원 정보를 입력받은 후, MemberBaan 컴포넌트의 update() 메소드를 호출하기만 하면 된다. 다음은 update.jsp의 소스 코드이다.

<jsp:useBean id="member" class="javacan.member.bean.MemberBean">
   <jsp:setProperty name="member" property="*" />
</jsp:useBean>
<html>
<head><title>회원정보변경</title></head>
<body>
<% member.update() %>
<jsp:getProperty name="member" property="name" />님의 회원 정보를 변경하였습니다.
</body>
</html>

회원 정보의 삭제 역시 지금까지 살펴본 것과 비슷한 방법을 사용하여 할 수 있을 것이다.

결론

JSP는 자바를 사용하여 웹 어플리케이션을 개발할 수 있는 효과적인 모델을 제공한다. 이 글에서는 데이터베이스에 데이터를 삽입하고, RDB로부터 데이터를 추출해내고, RDB에서 데이터를 삭제하는 구현부를 자바빈 컴포넌트에서 처리하는 방법을 알아보았다. 비록 JDBC를 이용한 데이터베이스 처리 코드를 JSP 페이지에 삽입할 수도 있지만, 데이터베이스 처리 코드를 자바빈에 넣는 것이 웹 어플리케이션에 있는 각 구성요소의 역활을 좀더 명확하게 구분지어준다. 또한, 자바빈 컴포넌트가 데이터베이스 작업을 처리하도록 함으로써 JSP 페이지가 더욱 더 표현부만을 처리하도록 할 수 있게 되었다.

개념을 좀더 확장하여 자바빈 컴포넌트와 관련된 엔티티를 관리해주는 매니저 클래스를 작성한다고 생각해보자. 이는 비지니스 로직과 관련된 부분을 매니저 클래스에서 처리한다는 것을 의미하며, 따라서 자바빈 컴포넌트와 JSP의 역할은 좀더 세분화될 것이며, 이에 따라 전체 웹 어플리케이션의 구성 역시 견고해질 것이다. 또한, 자바빈 컴포넌트와 매니저 클래스를 EJB의 엔티티빈과 세션빈으로 어렵지 않게 확장할 수 있을 것이다. 실제로 이에 대한 내용은 자바캔에 필자가 기고한 "Singleton 패턴을 적용한 매니저 클래스와 견고한 웹 어플리케이션의 개발"에서 언급한 적이 있으니, 그 기사를 참고하기 바란다.

관련링크:

+ Recent posts