개발

[JSP] 웹 어플리케이션의 일반적인 구성

Handy Smurf 2020. 7. 27. 00:22

package jdbc.connection;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class ConnectionProvider {

	public static Connection getConnection() throws SQLException {
		return DriverManager.getConnection(
				"jdbc:apache:commons:dbcp:guestbook"); // 변경가능
	}
}
  •  Service 클래스 : 사용자의 요청을 처리하는 기능을 제공한다.  기능을 제공하기 위한 로직을 구현하며 DAO 클래스를 이용해서 DB 연동을 처리한다. 가입 신청 처리, 글 목록 제공 등의 기능을 구현한다.
  • DAO 클래스 : DB와 관련된 쿼리를 실행한다. Service 클래스들은 데이터를 DB에서 읽어오거나 DB에 데이터를 저장할 때 DAO 클래스를 사용한다.
  • JSP(뷰) : Service 클래스가 실행한 결과를 화면에 출력하거나 Service가 기능을 수행하는데 필요한 데이터를 전달한다.
  • MVC 프레임워크 : 사용자의 요청을 Service에 전달하고 Service의 실행 결과를 JSP와 같은 뷰에 전달한다. 스프링 MVC프레임와 같은 프레임워크가 MVC 프레임워크에 해당한다.

1.0 데이터 접근 객체 (Data Access Object)의 구현

데이터에 접근할 때 사용하는 객체를 위한 클래스를 의미한다. 일반적으로 한 개의 DB 테이블마다 한 개의 DAO 클래스를 작성한다. 각 DAO 클래스는 INSERT, SELECT, UPDATE, DELETE쿼리를 실행해주는 메서드를 제공한다.

 

  • insert() 
  • select()
  • update()
  • delete()

insert() 메서드나 update()  메서드는 자바 객체로부터 값을 읽어와 쿼리를 실행한다.

아래 코드는 DAO 클래스의 insert() 메서드를 작성한 예이다.

 

public int insert(Connection conn, Message message) throws SQLException {
		PreparedStatement pstmt = null;
		try {
			pstmt = conn.prepareStatement(
					"insert into guestbook_message " + 
					"(guest_name, password, message) values (?, ?, ?)");
			pstmt.setString(1, message.getGuestName());
			pstmt.setString(2, message.getPassword());
			pstmt.setString(3, message.getMessage());
			return pstmt.executeUpdate();
		} finally {
			JdbcUtil.close(pstmt);
		}
	}

select()

 

public Message select(Connection conn, int messageId) throws SQLException {
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		try {
			pstmt = conn.prepareStatement(
					"select * from guestbook_message where message_id = ?");
			pstmt.setInt(1, messageId);
			rs = pstmt.executeQuery();
			if (rs.next()) {
				return makeMessageFromResultSet(rs);
			} else {
				return null;
			}
		} finally {
			JdbcUtil.close(rs);
			JdbcUtil.close(pstmt);
		}
	}

 

 

 

 

1.1 DAO에서 Connection에 접근하는 방식

 

DAO가 쿼리를 실행하려면 Statement나 PreparedStatment가 필요한데 이 두 객체는 Connection 객체로부터 구할 수 있따. DAO클래스는 Connction 객체에 접근할 수 있어야 하는데 Connection객체를 구하는 방법은 다음과 같은 세가지 방식이 있다.

 

  • DAO 클래스의 메서드에서 직접 Connection을 생성
  • DAO 객체를 생성할 때 생성자로 Connection을 전달받기
  • DAO 클래스의 메서드 파라미터로 Connection을 전달받기

 

DAO 클래스의 메서드에서 직접 Connection을 생성하는 방식

 

public Message selectById(int messageId) throws SQLException {
 Connection conn = null;
 PreparedStatement pstmt = null;
 ResultSet rs = null;
 
 try {
 
    conn = DriverManager.getConnection(jdbcUrl, userId, userPassword);
 ...
 }finally {
 ...
    if(conn != null) try {conn.close();} catch(SQLException ex) {}
    }
   }
  }

 위와 같이 작성한 DAO 클래스를 사용할 때에는 다음처럼 DAO 객체를 생성하고 메서드를 호출하게 된다.

 

MessageDao dao = ...// MessageDao 객체 구함
Message message = dao.selectById(messageId); //커넥션 생성
message.increseReadCount();
dao.updateReadCount(messageId, message.getReadCount()); //커넥션 생성

 

DAO 메서드에서 Connection을 직접 생성하는 방식의 단점은 메서드를 실행할 때마다 매번 Connection을 생성한다는 점이다 하나의 기능을 실행할 때 두 개의 Dao 메서드를 호출한다. 각 DAO 메서드에서 Connection을 생성하므로 위 코드는 두개의 Connection 객체를 생성하게 되다. 이는 각 메서드가 서로 다른 Connection 객체를 사용한다는 것을 의미한다.  이 경우 두 메서드를 하나의 드랜잭션으로 처리할 수 없게 된다. JDBC 기반의 트랜잭션은 한 개의 Connection객체에서만 유효하기 때문이다.

 

  • DAO 클래스의 메서드에서 직접 Connection을 생성
  • DAO 객체를 생성할 때 생성자로 Connection을 전달받기
  • DAO 클래스의 메서드 파라미터로 Connection을 전달받기
public class MessageDao {

private Connection conn;
public MessageDao(Connection conn) {

this. conn = conn;
}

public Message selectById(int messageId) throws SQLException {

PreparedStatement pstmt = null;
ResultSet rs = null;

try {

pstmt = conn.prepareStatement(query, userId, password);
...

}finally {
...
}
}
}

 DAO 클래스를 사용하는 코드는 다음과 같이 DAO 객체를 생성할 때 Connection을 전달한 뒤 필요한 메서드를 사용하면 된다.

 

Connection conn = null;

try {
conn = ... //커넥션을 구함
MessageDao dao = new MessageDao(conn);
Message message = dao.selectById(messageId);
message.increseReadCount();
dao.updateReadcount(messageId, message.getReadCount());
}finally{
if (conn != null) try { conn.close();} catch(SQLException ex) {}

}

DAO 객체의 각 메서드는 하나의 Connection을 사용하기 때문에 JDBC 트랜잭션을 이용할 수 있다. 하지만 매번 새로운 DAO 객체를 생성한다는 단점이 있다. 

 

 

  • DAO 클래스의 메서드에서 직접 Connection을 생성
  • DAO 객체를 생성할 때 생성자로 Connection을 전달받기
  • DAO 클래스의 메서드 파라미터로 Connection을 전달받기

 

DAO 메서드를 실행할 때 Connection 을 전달하는 방식이다.

 

 

public class MessageDao {


public Message selectById(Connection conn, int messageId) throws SQLException {

PreparedStatement pstmt = null;
ResultSet rs = null;

try {

pstmt = conn.prepareStatement(query, userId, password);
...

}finally {
...
}
}
}

DAO 클래스를 사용하는 코드는 다음과 같이 DAO 메서드를 실행할 때 Connection 객체를 전달해 준다.

 

Connection conn = null;

try {
conn = ... //커넥션을 구함
MessageDao dao = ... //DAO 객체 구함
Message message = dao.selectById(conn, messageId);
message.increseReadCount(conn);
dao.updateReadcount(conn, messageId, message.getReadCount());
}finally{
if (conn != null) try { conn.close();} catch(SQLException ex) {}

}

 두 번째 방식과 마찬가지로 한 개의 Connection 객체를 사용하므로 JDBC 트랜잭션을 사용할 수 있으며 DAO 객체를 매번 생성하지 않아도 되다는 장점이 있다. 다만 DAO 객체의 메서드를 실행할 때마다 Connection 객체를 파라미터로 전달해야 하므로 첫 번째나 두 번째 방식과 비교하면 메서드 호출 코드가 다소 길어진다는 단점이 있다, 또한 실수로 한 트랜잭션으로 실행해야 할 메서드에 다른 Connection을 전달할 수도 있다.

 

 

 

1.2 간단한 close()  및 rollback() 처리 코드를 위한 JdbcUtil

 

Connection, Statement, ResultSet과 같은 클래스는 사용이 끝나면 close() 메서드를 호출해서 자원을 반환해야한다.

그래서 익셉션 발생 여부에 상과없이 close할 수 있다록 finally 블록에서 close() 메서드를 구현할 수 있다록 한다.

 

try {
...
}finally {
if(rs != null ) try {rs.close();} catch(SQLException ex){}
if(pstmt != null ) try {pstmt.close();} catch(SQLException ex){}
if(conn != null ) try {conn.close();} catch(SQLException ex){}
}

 매번 같은 코드를 작성하는 것은 성가시므로 close() 처리 코드를 줄이기 위해 보조클래스를 작성을 한다.

 

이클립스 동적 웹 프로젝트를 작성하고 코드를 추가하자.

 

package jdbc;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class JdbcUtil {

	public static void close(ResultSet rs) {
		if (rs != null) {
			try {
				rs.close();
			} catch (SQLException ex) {
			}
		}
	}

	public static void close(Statement stmt) {
		if (stmt != null) {
			try {
				stmt.close();
			} catch (SQLException ex) {
			}
		}
	}

	public static void close(Connection conn) {
		if (conn != null) {
			try {
				conn.close();
			} catch (SQLException ex) {
			}
		}
	}

	public static void rollback(Connection conn) {
		if (conn != null) {
			try {
				conn.rollback();
			} catch (SQLException ex) {
			}
		}
	}
}

Connection 제공하는 ConnectionProvider 만들기