본문 바로가기
교재 실습/자바 웹 개발 워크북

23. 데이터베이스에서 데이터 가져오기 (2)

by Jint 2022. 1. 18.

데이터베이스로부터 전체 회원 정보를 가져와서 출력하는 서블릿을 만들어본다. web04 프로젝트의 spms.servlets 패키지의 MemberListServlet 클래스를 생성한다.

package spms.servlets;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;

@WebServlet("/member/list")
public class MemberListServlet extends GenericServlet {
	private static final long serialVersionUID = 1L;
	
	//javax.servlet.GenericServlet 클래스 상속
	
	@Override
	public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
		
		//JDBC 객체 주소를 보관할 참조 변수의 선언
		Connection conn = null;
		Statement stmt = null;
		ResultSet rs = null;
		
		//데이터베이스 관련 코드를 위한 try ~ catch 블록 (JDBC API 사용시 예외가 발생할 수 있기 때문)
		try {
			//JDBC 프로그래밍의 첫 번째 작업 : DriverManager를 이용한 java.sql.Driver 인터페이스 구현체 등록
			
			//MySQL 드라이버의 경우 com.mysql.jdbc.Driver 클래스가 java.sql.Driver 인터페이스 구현한 클래스다(mysql-connector-java-8.0.27.jar에 들어있다).
			DriverManager.registerDriver(new com.mysql.jdbc.Driver()); //registerDriver() 호출하여 구현체 등록
			
			//getConnection() : MySQL 서버에 연결
			conn = DriverManager.getConnection(
					
					//jdbc:mysql : JDBC 드라이버 이름
					// //localhost/studydb : 접속할 서버 주소(localhost)와 데이터베이스 이름(studydb)
					"jdbc:mysql://localhost/studydb", //JDBC URL(드라이버에 따라 조금씩 다름)
					"study",	// DBMS 사용자 아이디
					"study");	// DBMS 사용자 암호
			
			//DB 접속 정보를 다루는 객체 반환 : 데이터베이스에 SQL문을 보내는 객체
			stmt = conn.createStatement();
			
			//SQL문을 서버에 보내는 명령문 : SELECT 명령문을 보낼 때 executeQuery() 사용
			rs = stmt.executeQuery(
					"SELECT MNO,MNAME,EMAIL,CRE_DATE" + 
					" FROM MEMBERS" +
					" ORDER BY MNO ASC");
			
			//출력 스트림 얻기 전 setContentType() 호출하여 출력하려는 데이터의 형식(HTML)과 문자 집합(UTF-8)을 지정
			response.setContentType("text/html; charset=UTF-8");
			
			//HTML 태그 출력
			PrintWriter out = response.getWriter();
			out.println("<html><head><title>회원목록</title></head>");
			out.println("<body><h1>회원목록</h1>");
			
			//ResultSet rs 반환객체를 통해 서버에 질의 결과 가져올 수 있다.
			while(rs.next()) {//next()를 호출하면 서버에서 레코드(Record) 즉, 행(Row)을 가져온다. 서버에서 레코드를 받으면 true, 레코드가 없다면 false 반환.
				//서버에서 레코드 받는동안 계속해서 회원 정보를 한 줄의 문자열로 만들어 출력.
				//출력 문자열에서 칼럼들은 콤마(,)로 구분하고 문자열 끝에는 <br>태그를 붙여 줄바꿈.
				out.println(//칼럼이름				//칼럼인덱스(1부터 시작)
					rs.getInt("MNO") + "," +		//getInt(1)
					rs.getString("MNAME") + "," +	//getString(2)
					rs.getString("EMAIL") + "," + 	//getString(3)
					rs.getDate("CRE_DATE") + "<br>" //getDate(4)
				);
			}
			
			//HTML문 끝을 지정하는 태그
			out.println("</body></html>");
		} catch (Exception e) {
			//예외가 발생하면 ServletException 객체에 담아 이 메서드를 호출한 서블릿 컨테이너에 던진다.
			//서블릿 컨테이너는 예외에 따른 적절한 화면을 생성하여 클라이언트에게 출력한다.
			throw new ServletException(e);
			
		//JDBC 프로그래밍의 마무리 : 정상적으로 수행되든 오류가 발생하든 간에 반드시 자원해제 수행.
		} finally {//finally 블록 : try나 catch를 벗어나기 전에 반드시 수행됨.
			//자원해제는 역순으로 처리
			try {if (rs != null) rs.close();} catch(Exception e) {} //오류발생해도 특별히 할 작업이 없기 때문에 catch 블록 비워짐.
			try {if (stmt != null) stmt.close();} catch(Exception e) {}
			try {if (conn != null) conn.close();} catch(Exception e) {}
		}

	}
	
}

서블릿을 만들고자 javax.servlet.GenericServlet 클래스를 상속받고 service() 메소드를 구현한다. JDBC 프로그래밍의 첫 번째 작업으로 DriverManager를 이용한 java.sql.Driver 인터페이스 구현체 등록하는 일인데 MySQL 드라이버의 경우 com.mysql.jdbc.Driver() 클래스가 java.sql.Driver 인터페이스를 구현한 클래스이다.  다운로드했던 JDBC Type 4 드라이버인 mysql-connector-java-8.0.27.jar에 들어있다(그림 1).

그림 1 (MySQL JDBC 드라이버 파일에 포함된 Driver 클래스)

보통 클래스 이름을 인터페이스 이름과 비슷하게 짓기 때문에 JAR 파일을 뒤져보면 대강 짐작으로 인터페이스 구현체를 찾을 수 있다. 그림 1을 보면 MySQL의 경우 클래스 이름을 인터페이스 이름과 똑같이 만들었다.

 

- java.sql.Driver 인터페이스의 구현체 (정의된 주요 메소드) : 데이터베이스에 연결할 때 DriverManager는 이 구현체를 통해 드라이버에 대한 정보를 확인하고 사용할 드라이버를 결정.

getMajorVersion(), getMinorVersion() : DriverManager에게 JDBC 드라이버의 버전 정보 제공.

acceptsURL() : JDBC URL이 이 드라이버에서 사용 가능한지 알려줌.

connect() : 데이터베이스에 연결을 수행.

 

DriverManager의 getConnection()을 호출하여 MySQL 서버에 연결할 수 있다. getConnection()의 첫 번째 인자값은 MySQL 서버의 접속 정보다. JDBC URL이라고 부른다(그림 2).

그림 2 (JDBC URL의 구조)

두 번째와 세 번째 인자값은 데이터베이스의 사용자 아이디와 암호다. 이렇게 JDBC URL 정보와 사용자 아이디, 암호를 가지고 MySQL 서버에 연결을 요청한다. 데이터베이스 연결에 성공하면 DB 접속 정보를 다루는 객체를 반환하는데 반환된 객체는 java.sql.Connection 인터페이스의 구현체다. 이 반환 객체를 통해 데이터베이스에 SQL문을 보내는 객체를 얻을 수 있다.

 

- java.sql.Connection 인터페이스의 구현체 (정의된 주요 메소드) : SQL문을 실행할 객체를 얻을 수 있다.

createStatement(), prepareStatement(), prepareCall() : SQL문을 실행하는 객체를 반환.

commit(), rollback() : 트랜잭션 처리를 수행하는 메소드

 

Connection 구현체를 이용하여 SQL문을 실행할 객체를 준비한다. createStatement()가 반환하는 것은 java.sql.Statement 인터페이스의 구현체다. 이 객체를 통해 데이터베이스에 SQL문을 보낼 수 있다. Statement 인터페이스는 데이터베이스에 질의하는데 필요한 메소드가 정의되어 있고, 이 인터페이스를 구현했다는 것은 반환 객체가 이러한 메소드들을 가지고 있다는 뜻이다. 즉 반환 객체를 이용하면 SQL문을 서버에 보낼 수 있다.

 

- java.sql.Statement 인터페이스의 구현체 (정의된 주요 메소드) : 연결된 데이터베이스에 SQL을 보낼 수 있다.

executeQuery() : 결과가 만들어지는 SQL 문을 실행할 때 사용. 보통 SELECT 문을 실행한다.

executeUpdate() : DML과 DDL 관련 SQL 문을 실행할 때 사용. DML(Data Manipulation Language)에는 INSERT, UPDATE, DELETE 명령문이 있고, DDL(Data Definition Language)에는 CREATE, ALTER, DROP 명령문이 있다.

execute() : SELECT, DML, DDL, 명령문 모두에 사용 가능.

executeBatch() : addBatch()로 등록한 여러 개의 SQL 문을 한꺼번에 실행할 때 사용.

 

executeQuery()가 반환하는 객체는 java.sql.ResultSet 인터페이스의 구현체다. 이 반환 객체를 통하여 서버에서 질의 결과를 가져올 수 있다.

 

- java.sql.ResultSet 인터페이스의 구현체 (정의된 주요 메소드) : 서버에 만들어진 SELECT 결과를 가져올 수 있고, 가져온 레코드(행)에서 특정 칼럼 값을 꺼낼 수 있다.

first() : 서버에서 첫 번째 레코드를 가져온다.

last() : 서버에서 마지막 번째 레코드를 가져온다.

previous() : 서버에서 이전 레코드를 가져온다.

next() : 서버에서 다음 레코드를 가져온다.

getXXX() : 레코드에서 특정 칼럼의 값을 꺼내며 XXX는 칼럼의 타임에 따라 다른 이름을 갖는다. (getInt, getString, getDate 등)

updateXXX() : 레코드에서 특정 칼럼의 값을 변경.

deleteRow() : 현재 레코드를 지운다.

 

ResultSet 객체를 통해 next()를 호출하면 서버에서 레코드(Record) 즉, 행(Row)을 가져온다. 서버에서 받은 레코드는 ResultSet 객체에 보관된다. 레코드에서 칼럼 값을 꺼낼 때 getXXX()을 호출한다. getXXX()의 인자값은 SELECT 결과의 칼럼 인덱스나 칼럼 이름이 될 수 있다. 칼럼 인덱스는 SELECT문의 칼럼 순서에 영향을 받기 때문에 인덱스보다 주로 칼럼 이름을 사용한다.

 

JDBC 프로그래밍의 마무리는 정상적으로 수행되든 오류가 발생하든 간에 반드시 자원해제를 수행하는 것이다. 자원 해제하기 가장 좋은 위치는 finally 블록이다. 자원을 해제할 때는 역순으로 처리한다. 즉 ResultSet 객체를 먼저 해제하고 Statement 객체와 Connection 객체를 해제한다.

 

@WebServlet 어노테이션으로 서블릿의 배치 정보를 설정했다. MemberListServlet 클래스의 URL 패턴 값을 이렇게 @WebServlet("/member/list")로 설정했다.

 

만약 서블릿 컨테이너가 Servlet 2.5 사양을 따른다면 어노테이션을 이용한 설정 방법이 불가능하기 때문에 web.xml에 배치 정보를 작성해야 한다.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		 xmlns="http://xmlns.jcp.org/xml/ns/javaee"
		 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
		 id="WebApp_ID"
		 version="3.1">
  <display-name>web04</display-name>
  
  <!-- 필터 선언 -->
	
	<!-- 필터 URL 매핑 -->
		
	<!-- 서블릿 선언 -->
	<servlet>
		<servlet-name>MemberList</servlet-name>
  		<servlet-class>spms.servlets.MemberListServlet</servlet-class>
	</servlet>
	<!-- 서블릿을 URL과 연결 -->
	<servlet-mapping>
	  	<servlet-name>MemberList</servlet-name>
	  	<url-pattern>/member/list</url-pattern>
	</servlet-mapping>

  <welcome-file-list>
	<welcome-file>index.html</welcome-file>
	<welcome-file>index.htm</welcome-file>
	<welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>

하지만 어노테이션으로 서블릿의 배치 정보를 설정했으므로 web.xml의 서블릿 배치정보는 다시 주석처리 한다. 톰캣 서버를 시작 또는 재시작한 뒤 웹 브라우저 주소창에 http://localhost:9999/web04/member/list 링크를 실행하여 서블릿을 테스트한다.

그림 3 (회원 목록 서블릿을 실행한 결과 화면)

회원 목록 서블릿을 실행한 결과 화면이다.

 

참고도서 : https://freelec.co.kr/book/1674/

 

[열혈강의] 자바 웹 개발 워크북

[열혈강의] 자바 웹 개발 워크북

freelec.co.kr

댓글