본문 바로가기

개발/Java

[JAVA - WEB] 서블릿과 데이터베이스

728x90
반응형

지금까지는 그냥 요청을 받으면 메서드 자체에서 받은 데이터를 콘솔에 출력하거나 출력 스트림을 통해서 받은 데이터를 웹 브라우저에서 보여주는 식으로 처리를 했습니다. 

실제 개발을 진행하게 되면 웹과 데이터베이스는 필수불가결한 사이이므로 함께 알아보도록 하겠습니다.

 

 

서블릿의 비즈니스 로직 처리

 

서블릿의 비즈니스 로직 처리란 서블릿이 클라이언트로부터 요청을 받으면 그 요청에 대해 작업을 수행하는 것입니다.

웹 애플리케이션에서 대부분의 비즈니스 로직 처리 작업은 데이터베이스 연동 관련 작업이지만 그 외에 다른 서버를 연동하든 다른 프로그램을 연동해서 데이터를 얻는 작업도 수행합니다. 

즉, 클라이언트 요청을 받은 서블릿이 비즈니스 로직 처리 기능으로 데이터 저장소에서 데이터를 조회 및 처리를 한 후 서블릿의 응답 기능을 이용해 클라이언트로 결과를 전송하는 것입니다.

 

 

대략 이렇게 구성이 되겠죠? (이미지가 허접해서 죄송..)

중요한 내용이니 꼭 알고 계셨으면 합니다.

 

 

서블릿의 데이터베이스 연동

 

데이터베이스를 연동하려면 일단 테이블과 데이터가 필요하겠죠?

SQL 문으로 회원 테이블을 만들고 생성된 테이블에 회원 정보를 넣도록 하겠습니다.

 

테이블 생성

create table t_member
(
    id varchar2(20) primary key,
    pwd varchar2(20),
    name varchar2(50),
    email varchar2(50),
    joinDate date default sysdate
);

id 를 기본키로 설정하고 joinDate 를 기본값으로 현재 시간을 입력하도록 하였습니다.

 

 

데이터 삽입

insert into t_member values('kim', '1212', '김철수', 'kim@gmail.com', sysdate);

insert into t_member values('lee', '1212', '이영희', 'lee@gmail.com', sysdate);

insert into t_member values('pak', '1212', '박민수', 'pak@gmail.com', sysdate);

 

SQL Developer 를 실행하여 접속 정보를 입력하여 접속합니다.

자세한 내용은 아래 링크를 참고 하시기 바랍니다.

 

https://jeusyhk.tistory.com/entry/JAVA-WEB-%EA%B0%9C%EB%B0%9C-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95-SQL-Developer-%EC%84%A4%EC%B9%98

 

[JAVA - WEB] 개발 환경 구축 - SQL Developer 설치

SQL Developer 설치하기 Oracle DBMS 설치 시에 사용해봤던 sqlplus 로 Oracle DBMS 를 연동할 수 있지만 명령 프롬프트 기반으로 실제 개발 시에 사용하기에는 너무 불편한 방법입니다. 윈도우 OS 를 사용하

jeusyhk.tistory.com

 

접속에 성공하셨으면 먼저 테이블을 생성하는 SQL 구문을 입력하여 실행합니다.

 

 

만약 권한이 없다는 에러가 발생하면 명령프롬프트를 실행해서 sqlplus 의 system 계정으로 접속한 뒤 권한을 부여한 후 진행하시면 됩니다.

 

 

이어서 차례대로 데이터를 삽입하는 구문을 입력 후 실행합니다.

 

 

테이블 생성 및 데이터 입력이 완료 되었으면 마지막에 commit; 입력하여 데이터베이스에 반영시키도록 합니다.

 

 

테이블에 데이터가 정상적으로 입력되었는지 확인합니다.

 

 

정상적으로 데이터가 입력된 것을 확인하실 수 있습니다.

 

하기 첨부된 오라클 데이터베이스와 연동하는데 필요한 드라이버를 .../WEB-INF/lib 폴더에 복사하여 붙여넣습니다.

 

ojdbc6.jar
2.61MB

 

 

PreparedStatement 사용

 

Statement 를 이용해서 데이터베이스를 연동할 수 있지만 이렇게 연동할 경우 연동할 때마다 DBMS 에서 다시 SQL 문을 컴파일 해야 하므로 속도가 느려집니다.

대신 PreparedStatement 인터페이스를 사용하면 SQL 문을 미리 컴파일해서 재사용하므로 Statement 인터페이스보다 훨씬 빠르게 데이터베이스 작업을 할 수 있습니다. 

PreparedStatement 인터페이스는 컴파일된 SQL 문을 DBMS 에 전달하여 성능을 향상시킵니다.

실행하려는 SQL 문의 값을 할당하는 부분에 물음표(?)를 넣고 값을 지정할 수 있어서 SQL 문 작성에 보다 유리합니다.

 

사용 예시 코드입니다.

추후에 전체적인 코드로 실습해볼 것이니 '아 이렇게 쓰는구나' 만 알고 넘어가시면 됩니다.

 

private Connection con;
private PreparedStatement pstmt;
...

con = DriverManager.getConnection(url, user, pwd);
String query = "select * from t_member";
pstmt = con.PreparedStatement(query);
ResultSet = rs = pstmt.executeQuery();

while (rs.next())
{
	...

 

 

 

DataSource 사용

 

웹 애플리케이션이 필요할때마다 데이터베이스를 연결하여 작업하게 되면 마찬가지로 속도가 느려지게 됩니다.

이러한 문제점을 해결하기 위해서 웹 애플리케이션이 실행됨과 동시에 연동할 데이터베이스와의 연결을 미리 설정합니다.

그리고 필요할 때마다 미리 연결해 놓은 상태를 이용해 빠르게 데이터베이스와 연동 작업을 합니다.

커넥션풀(ConnectionPool) 이라는 것이 이렇게 미리 데이터베이스와 연결시킨 상태를 유지하는 기술입니다.

 

데이터베이스와 연동 작업이 필요한 경우 ConnectionPool 에서 제공하는 메서드를 호출하여 연동합니다.

톰캣 컨테이너는 자체적으로 ConnectionPool 기능을 제공합니다. 톰캣 실행 시 톰캣은 설정 파일에 설정된 데이터베이스 정보를 이용해 미리 데이터 베이스와 연결하여 ConnectionPool 객체를 생성한 후 애플리케이션이 데이터베이스와 연동할 일이 생기면 ConnectionPool 객체의 메서드를 호출해 빠르게 연동 작업을 합니다.

 

 

 

JNDI(Java Naming and Directory Interface)

 

ConnectionPool 객체를 구현할 때는 javax.sql.Datasource 클래스를 이용하고, 웹 애플리케이션 실행 시 톰캣이 만들어놓은 ConnectionPool 객체에 접근할 때는 JNDI 를 사용합니다.

JNDI 는 필요한 자원을 Key/Value 쌍으로 저장한 후 필요할 때 키를 이용해 값을 얻는 방법입니다.

톰캣 컨테이너가 ConnectionPool 객체를 생성하면 이 객체에 대한 JNDI Key 를 미리 설정해 놓으면 데이터베이스와의 연동 작업을 할때 Key 로 접근해서 작업을 할수 있겠죠?

 

실제 톰캣에서 ConnectionPool 기능을 사용하려면 기능을 제공하는 DBCP 라이브러리 파일이 필요합니다.

 

tomcat-dbcp-7.0.30.jar
0.22MB

 

 

 

이클립스의 톰캣 DataSource 설정

 

이클립스의 Project Explorer 에서 톰캣을 연동한 Servers 내부를 보시면 context.xml 파일이 있습니다.

파일 내부의 <Resource> 태그를 이용해 톰캣 실행 시 연결할 데이터베이스를 설정할 수 있습니다.

 

<Resource
    	name = "jdbc/oracle"
    	auth = "Container"
    	type = "javax.sql.Datasource"
    	driverClassName = "oracle.jdbc.OracleDriver"
    	url = "jdbc:oracle:thin:@localhost:1521:XE"
    	username = "scott"
    	password = "tiger"
    	maxActive = "50"
    	maxWait = "-1" />

 

ConnectionPool 의 데이터베이스 속성 설정 종류는 다음과 같습니다.

 

속성 설명
name Datasource 에 대한 JNDI 이름
auth 인증 주체
driverClassName 데이터베이스 종류에 따른 드라이버 클래스 이름
factory 데이터베이스 종류에 따른 ConnectionPool 생성 클래스 이름
maxActive 동시에 최대로 데이터베이스에 연결할 수 있는 Connection 갯수
maxIdle 동시에 idle 상태로 대기할 수 있는 최대 갯수
maxWait 새로운 연결이 생길 때까지 기다릴 수 있는 최대 시간
username 데이터베이스 접속 ID
password 데이터베이스 접속 비밀번호 
type 데이터베이스 종류별 DataSource 
url 접속할 데이터베이스 주소와 포트 번호 및 SID

 

 

톰캣 Datasource 를 연동한 회원 정보 처리

 

이제 소스코드를 작성해봅시다.

 

먼저 테이블에 검색해서 정보를 전달할 MemberVO 클래스를 만듭니다.

테이블 내의 하나의 데이터에 관련된 클래스라고 생각하시면 이해가 빠르시겠네요.

 

public class MemberVO {
	private String id;
	private String pwd;
	private String name;
	private String email;
	private Date joinDate;
	
	public MemberVO() {
		System.out.println("MemberVO 생성자 호출");
	}
    
 	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getPwd() {
		return pwd;
	}

	public void setPwd(String pwd) {
		this.pwd = pwd;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}


	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public Date getJoinDate() {
		return joinDate;
	}

	public void setJoinDate(Date joinDate) {
		this.joinDate = joinDate;
	}
}

하나의 데이터(레코드)의 정보를 가져오거나 설정할 수 있는 함수를 작성했습니다(get/set)

 

 

이제 데이터베이스를 연동해서 기능별로 데이터를 검색 및 가공을 할 MemberDAO 클래스를 작성하겠습니다.

 

public class MemberDAO {
	private Connection con;            // 데이터베이스 연결 객체
	private PreparedStatement pstmt;   // SQL 인터페이스
	private DataSource dataFactory;    // 톰캣 연결 DataSource

	// 톰캣의 DataSource 를 받아옴
	public MemberDAO() { 
		try {
			Context ctx = new InitialContext();
			Context envContext = (Context) ctx.lookup("java:/comp/env");
			dataFactory = (DataSource) envContext.lookup("jdbc/oracle");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	// DataSource 를 이용해 데이터베이스에 연결하고 회원 정보를 가져옴
	public List<MemberVO> listMembers() { 
		List<MemberVO> list = new ArrayList<MemberVO>();
		try {
			con = dataFactory.getConnection(); // 데이터베이스 연결
			String query = "select * from t_member ";
			System.out.println("prepareStatememt: " + query);
			pstmt = con.prepareStatement(query); // PrepareStatement 객체 
			ResultSet rs = pstmt.executeQuery(); // SQL 문 실행
			while (rs.next()) {
				String id = rs.getString("id");
				String pwd = rs.getString("pwd");
				String name = rs.getString("name");
				String email = rs.getString("email");
				Date joinDate = rs.getDate("joinDate");
				MemberVO vo = new MemberVO();
				vo.setId(id);
				vo.setPwd(pwd);
				vo.setName(name);
				vo.setEmail(email);
				vo.setJoinDate(joinDate);
				list.add(vo);
			}
			rs.close();
			pstmt.close();
			con.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return list;
	}

	// 받은 회원 정보를 데이터베이스에 삽입
	public void addMember(MemberVO memberVO) {
		try {
			con = dataFactory.getConnection();
			String id = memberVO.getId();
			String pwd = memberVO.getPwd();
			String name = memberVO.getName();
			String email = memberVO.getEmail();
			String query = "insert into t_member";
			query += " (id,pwd,name,email)";
			query += " values(?,?,?,?)"; // 물음표를 이용해서 값 할당
			System.out.println("prepareStatememt: " + query);
			pstmt = con.prepareStatement(query);
			pstmt.setString(1, id);
			pstmt.setString(2, pwd);
			pstmt.setString(3, name);
			pstmt.setString(4, email);
			pstmt.executeUpdate();
			pstmt.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	// id 로 데이터베이스의 회원 정보 삭제
	public void delMember(String id) {
		try {
			con = dataFactory.getConnection();
			String query = "delete from t_member" + " where id=?";
			System.out.println("prepareStatememt:" + query);
			pstmt = con.prepareStatement(query);
			pstmt.setString(1, id);
			pstmt.executeUpdate();
			pstmt.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}

 

이제 웹 브라우저의 요청을 받아서 처리 후 응답을 보낼 MemberServlet 클래스를 작성하겠습니다.

 

@WebServlet("/member")
public class MemberServlet extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doHandle(request, response);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doHandle(request, response);
	}

	private void doHandle(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		request.setCharacterEncoding("utf-8");
		response.setContentType("text/html;charset=utf-8");
		MemberDAO dao = new MemberDAO();
		PrintWriter out = response.getWriter();
		String command = request.getParameter("command"); // command 받아오기
		if (command != null && command.equals("addMember")) { // 회원가입 처리
			String _id = request.getParameter("id");
			String _pwd = request.getParameter("pwd");
			String _name = request.getParameter("name");
			String _email = request.getParameter("email");

			MemberVO vo = new MemberVO();
			vo.setId(_id);
			vo.setPwd(_pwd);
			vo.setName(_name);
			vo.setEmail(_email);
			dao.addMember(vo);
		} else if (command != null && command.equals("delMember")) { // 회원삭제 처리
			String id = request.getParameter("id");
			dao.delMember(id);
		}
        
		// 회원 정보 보여주기
		List list = dao.listMembers();
		out.print("<html><body>");
		out.print("<table border=1><tr align='center' bgcolor='lightgreen'>");
		out.print("<td>아이디</td><td>비밀번호</td><td>이름</td><td>이메일</td><td>가입일</td><td >삭제</td></tr>");

		for (int i = 0; i < list.size(); i++) {
			MemberVO memberVO = (MemberVO) list.get(i);
			String id = memberVO.getId();
			String pwd = memberVO.getPwd();
			String name = memberVO.getName();
			String email = memberVO.getEmail();
			Date joinDate = memberVO.getJoinDate();
			out.print("<tr><td>" + id + "</td><td>" + pwd + "</td><td>" + name + "</td><td>" + email + "</td><td>"
					+ joinDate + "</td><td>" + "<a href='/pro07/member3?command=delMember&id=" + id
					+ "'> 삭제 </a></td></tr>");

		}
		out.print("</table></body></html>");
		out.print("<a href='/pro07/memberForm.html'>회원 가입하기</a>");
	}
}

 

회원 가입 클릭 시 웹 브라우저에서 회원 정보를 입력할 수 있는 양식인 HTML 을 작성 하겠습니다.

 

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원 가입창</title>
<script type="text/javascript">
	function fn_sendMember() {
		var frmMember = document.frmMember;
		var id = frmMember.id.value;
		var pwd = frmMember.pwd.value;
		var name = frmMember.name.value;
		var email = frmMember.email.value;
		if (id.length == 0 || id == "") {
			alert("아이디는 필수입니다.");
		} else if (pwd.length == 0 || pwd == "") {
			alert("비밀번호는 필수입니다.");
		} else if (name.length == 0 || name == "") {
			alert("이름은 필수입니다.");
		} else if (email.length == 0 || email == "") {
			alert("이메일은 필수입니다.");
		} else {
			frmMember.method = "post";
			frmMember.action = "member3";
			frmMember.submit();
		}
	}
</script>
</head>
<body>
	<form name="frmMember">
		<table>
			<th>회원 가입창</th>
			<tr>
				<td>아이디</td>
				<td><input type="text" name="id"></td>
			</tr>
			<tr>
				<td>비밀번호</td>
				<td><input type="password" name="pwd"></td>
			</tr>
			<tr>
				<td>이름</td>
				<td><input type="text" name="name"></td>
			</tr>
			<tr>
				<td>이메일</td>
				<td><input type="text" name="email"></td>
			</tr>
		</table>
		<input type="button" value="가입하기" onclick="fn_sendMember()"> 
		<input type="reset" value="다시입력"> <input type="hidden" name="command" value="addMember" />
	</form>
</body>
</html>

 

모든 파일이 완성되었으면 웹 브라우저에서 아래 주소로 요청을 보냅니다.

 

http://localhost:8090/pro07/member

 

정상적으로 처리가 되었을 경우 웹 브라우저에 모든 회원의 정보가 출력됩니다.

 

 

회원 가입이나 삭제 등은 작성한 코드대로 실행해보시기 바랍니다.

 

여기까지 임미다.

 

728x90