서블릿에서 회원 정보 DB를 조회하는 방법
- 클라이언트(웹 브라우저)가 회원 조회를 요청
- 서블릿에서 요청을 받아 DAO 객체를 사용해 회원 조회 메서드 호출
- DAO에서 데이터베이스와 연결해 회원 정보를 조회하고 각 레코드를 DTO에 담아 ArrayList에 저장 후 반환
- 서블릿에서 ArrayList를 받아 ArrayList 안의 DTO를 하나씩 꺼내 DTO의 속성(아이디 등)을 HTML 태그로 이루어진 문자열로 만들기
- 클라이언트에 만든 문자열을 전송해 회원 정보 출력
회원 조회를 요청하기 위해 필요한 데이터베이스를 미리 생성해야 함
MySQL
로 practice
데이터베이스에 t_member
테이블 생성하고 레코드 추가
use practice;
create table t_member(
id varchar(10) primary key,
pwd varchar(10),
name varchar(10),
email varchar(50),
/* 기본값을 현재 날짜로 지정하기 */
joinDate date default (current_date)
);
insert into t_member(id, pwd, name, email) values('hong','1212','홍길동','hong@gmail.com');
insert into t_member(id, pwd, name, email) values('lee','1212','이순신','lee@test.com');
insert into t_member(id, pwd, name, email) values('kim','1212','김유신','kim@web.com');
요청을 받아 처리할 서블릿 MemberServlet
DB에 접근해 작업을 수행할 DAO MemberDAO
조회한 회원(레코드) 각각의 정보(컬럼 값)를 담을 DTO MemberDTO
클래스 생성
MemberDTO
: 회원 정보 속성과 getter/setter 메서드로 구성
package sec03.ex01;
import java.sql.Date;
public class MemberDTO {
private String id;
private String pwd;
private String name;
private String email;
private Date joinDate;
public MemberDTO() {
// 객체가 생성될 때 콘솔 메세지 출력
System.out.println("DTO 생성자 호출");
}
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;
}
}
MemberServlet
: 요청을 받아 DAO에서 작업을 처리하고 결과 출력
package sec01.ex01;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Date;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/member")
public class MemberServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
// DAO 객체 사용
MemberDAO dao = MemberDAO.getInstance();
// DAO의 listMembers() 메서드를 호출해 List 반환받기
List<MemberDTO> list = dao.listMembers();
String data = "";
data += "<html><body>";
data += "<table border=1><tr align='center' bgcolor='lightgreen'>";
data += "<td>아이디</td><td>비밀번호</td><td>이름</td><td>이메일</td><td>가입일</td></tr>";
// List의 DTO 속성값을 출력하기
for(int i=0; i < list.size(); i++) {
MemberDTO dto = list.get(i);
String id = dto.getId();
String pwd = dto.getPwd();
String name = dto.getName();
String email = dto.getEmail();
Date joinDate = dto.getJoinDate();
data += "<tr><td>"+id+"</td><td>"+pwd+"</td><td>"+name+"</td><td>"+email+"</td><td>"+joinDate+"</td></tr>";
}
data += "</table></body></html>";
out.print(data);
}
}
MemberDAO
: 실질적으로 DB에 접근해 데이터를 받아오는 클래스
package sec01.ex01;
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class MemberDAO {
// 싱글턴 패턴 적용
private static MemberDAO instance = new MemberDAO();
public static MemberDAO getInstance() {
return instance;
}
// DB 연동을 위한 변수
private Connection conn;
private PreparedStatement pstmt;
private String driver = "com.mysql.cj.jdbc.Driver";
private String url = "jdbc:mysql://localhost:3306/practice";
private String user = "root";
private String password = "1234";
// DB에서 회원 목록을 가져오는 메서드
public List<MemberDTO> listMembers() {
List<MemberDTO> list = new ArrayList<MemberDTO>();
try {
connDB();
String sql = "select * from t_member";
pstmt = conn.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery();
while(rs.next()) {
MemberDTO dto = new MemberDTO();
dto.setId(rs.getString("id"));
dto.setPwd(rs.getString("pwd"));
dto.setName(rs.getString("name"));
dto.setEmail(rs.getString("email"));
dto.setJoinDate(rs.getDate("joinDate"));
list.add(dto);
}
rs.close();
pstmt.close();
conn.close();
System.out.println("close");
}
catch (Exception e) { e.printStackTrace(); }
return list;
}
private void connDB() throws SQLException, ClassNotFoundException {
// 드라이버 로딩하기
Class.forName(driver);
// 데이터베이스와의 Connection 생성하기
conn = DriverManager.getConnection(url, user, password);
}
}
connDB()
메서드를 호출하면 DB와 연결된 Connection
객체를 얻을 수 있고,
이 객체에 prepareStatement("SQL문")
메서드를 호출하면 원하는 SQL문을 실행할 수 있는 상태인 PreparedStatement
객체가 된다. (아직 실행은 안 됨)
PreparedStatement
객체에 executeQuery()
메서드를 호출하면 SQL문을 실행한 결과가 반환되고, 이 반환값을 ResultSet
객체에 담아 사용한다.
ResultSet
객체에 next()
메서드를 호출하여 레코드 하나씩 접근할 수 있다.
외부 DB 연결에 사용한 객체들은 마지막으로 사용한 순서대로 close()
해야 하고, 연결 시 예외가 발생할 수 있으므로 예외 처리가 필요하다.
(= 커넥션풀을 이용해 데이터베이스 연동하기)
커넥션풀(ConnectionPool) 이란?
데이터베이스와의 연결을 미리 설정해 두고 필요할 때 사용하는 기술
앞에서는 필요할 때마다 connDB() 메서드를 호출해 DB와 새로 연결해 작업했지만, 커넥션풀을 이용하면 웹 어플리케이션을 실행할 때부터 DB와의 연결이 미리 설정되기 때문에 작업 속도를 향상시킬 수 있음
커넥션풀을 이용해 DB 연동하는 방법
- 커넥션풀 기능 관련 라이브러리 설치
- 톰캣 서버 설정 파일
context.xml
에서<Resource>
태그로 DB 정보 설정 (=DataSource)- DB 연결할 클래스에서
DataSource
객체에 미리 연결한 DB 정보를 받아온 후getConnection()
메서드 호출해 연결
tomcat-dbcp-7.0.30.jar
파일 WEB-INF/lib
폴더에 설치
톰캣 서버 설정 파일 context.xml
에서 <Resource>
태그 속성에 DB 정보 입력하기
<Context>
...
<!-- 톰캣 컨테이너 실행 시 연결할 DB 미리 설정하기 -->
<Resource
name="jdbc/mysql"
auth="Container"
type="javax.sql.DataSource"
driverClassName="com.mysql.cj.jdbc.Driver"
url="jdbc:mysql://localhost:3306/practice"
username="root"
password="1234" />
</Context>
name
의 속성값인 jdbc/mysql
: 자바에서 해당 DataSource에 접근할 수 있는 JNDI 이름(key) 이 됨
driverClassName
: 드라이버 클래스 이름
url
: 연결할 데이터베이스 주소
username
, password
: 데이트베이스 접속 ID, 비밀번호
💡 JNDI란?
필요한 자원을 키/값(key/value) 쌍으로 저장해 필요할 때 키를 이용해 값을 얻는 방법
위 실습에서 DB에 접근하기 위해 작성한 클래스 MemberDAO
의 내용을 아래와 같이 수정
public class MemberDAO {
// 1. 불필요한 String 주석 처리
/*
private String driver = "com.mysql.cj.jdbc.Driver";
private String url = "jdbc:mysql://localhost:3306/practice";
private String user = "root";
private String password = "1234";
*/
private Connection conn;
private PreparedStatement pstmt;
// 2. 미리 연결한 DB의 Connection을 얻기 위해 필요한 객체
private DataSource dataFactory;
// 3. 생성자에서 DataSource 받아오기
private MemberDAO() {
try {
// 톰캣 context.xml에 설정해둔 JNDI(Resource)에 접근하는 코드
Context ctx = new InitialContext();
Context envContext = (Context) ctx.lookup("java:/comp/env");
// JNDI의 키(name)로 톰캣이 미리 연결한 DataSource 받아오기
dataFactory = (DataSource) envContext.lookup("jdbc/mysql");
System.out.println("dataFactory 생성");
}
catch(Exception e) {
e.printStackTrace();
}
}
// 싱글턴 패턴 적용
private static MemberDAO instance = new MemberDAO();
public static MemberDAO getInstance() {
return instance;
}
// DB에서 회원 목록을 가져오는 메서드
public List<MemberDTO> listMembers() {
List<MemberDTO> list = new ArrayList<MemberDTO>();
try {
// 4. 기존 connDB() 메서드 주석 처리
// connDB();
// 5. DataSource 객체에 getConnection() 메서드 호출해 DB 연결
conn = dataFactory.getConnection();
String sql = "select * from t_member";
pstmt = conn.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery();
while(rs.next()) {
MemberDTO dto = new MemberDTO();
dto.setId(rs.getString("id"));
dto.setPwd(rs.getString("pwd"));
dto.setName(rs.getString("name"));
dto.setEmail(rs.getString("email"));
dto.setJoinDate(rs.getDate("joinDate"));
list.add(dto);
}
rs.close();
pstmt.close();
conn.close();
System.out.println("close");
}
catch (Exception e) { e.printStackTrace(); }
return list;
}
/*
private void connDB() throws SQLException, ClassNotFoundException {
Class.forName(driver);
conn = DriverManager.getConnection(url, user, password);
}
*/
}
입력한 데이터를 Post 방식으로 전송하고, command
키에 addMember
라는 값을 담아 전송한다.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원 가입창</title>
</head>
<body>
<form action="member" method="post">
<table>
<th>회원 가입창</th>
<tr>
<td>아이디</td>
<td><input type="text" name="id" required></td>
</tr>
<tr>
<td>비밀번호</td>
<td><input type="password" name="pwd" required></td>
</tr>
<tr>
<td>이름</td>
<td><input type="text" name="name" required></td>
</tr>
<tr>
<td>이메일</td>
<td><input type="text" name="email" required></td>
</tr>
</table>
<input type="hidden" name="command" value="addMember">
<input type="submit" value="가입하기">
<input type="reset" value="초기화">
</form>
</body>
</html>
...
public class MemberDAO {
...
// DB에 회원을 추가하는 메서드
public void addMember(MemberDTO dto) {
try {
conn = dataFactory.getConnection();
// 인자로 받아온 DTO 객체의 정보 담기
String id = dto.getId();
String pwd = dto.getPwd();
String name = dto.getName();
String email = dto.getEmail();
// 레코드를 추가하는 sql문 작성
String sql = "insert into t_member";
sql += "(id,pwd,name,email)";
sql += "values(?,?,?,?)";
// sql문 statement에 입력시키고 값 set 해서 update
pstmt = conn.prepareStatement(sql);
pstmt.setString(1,id);
pstmt.setString(2,pwd);
pstmt.setString(3,name);
pstmt.setString(4,email);
pstmt.executeUpdate();
pstmt.close();
conn.close();
System.out.println("close");
}
catch (Exception e) { e.printStackTrace(); }
}
// DB에서 회원을 삭제하는 메서드
public void delMember(String id) {
try {
conn = dataFactory.getConnection();
// 레코드를 삭제하는 sql문 작성
String sql = "delete from t_member where id=?";
// id 속성의 값이 ?(미정)인 레코드를 삭제
// sql문 statement에 입력시키고 값 set 해서 update
pstmt = conn.prepareStatement(sql);
pstmt.setString(1,id);
pstmt.executeUpdate();
pstmt.close();
conn.close();
System.out.println("close");
}
catch (Exception e) { e.printStackTrace(); }
}
}
get/post 전달 방식에 상관없이 요청이 넘어오면 doHandle()
메서드를 호출
전송된 command
의 값에 따라 dao의 메서드인 회원 등록/삭제 중에서 필요한 것을 호출
요청받은 작업을 수행 후 회원 목록을 조회해 웹 브라우저에 출력
package sec02.ex02;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Date;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@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");
PrintWriter out = response.getWriter();
MemberDAO dao = MemberDAO.getInstance();
String command = request.getParameter("command");
if(command != null && command.equals("addMember")) {
// 전송된 command가 addMember이면
// dto 객체 만들어서 받아온 값으로 set하고 dao의 addMember() 호출
MemberDTO dto = new MemberDTO();
dto.setId(request.getParameter("id"));
dto.setPwd(request.getParameter("pwd"));
dto.setName(request.getParameter("name"));
dto.setEmail(request.getParameter("email"));
dao.addMember(dto);
}
else if(command != null && command.equals("delMember")) {
// 전송된 command가 delMember이면
// 받아온 id값을 인자로 dao의 delMember() 호출
String id = request.getParameter("id");
dao.delMember(id);
}
List<MemberDTO> list = dao.listMembers();
String data = "";
data += "<html><body>";
data += "<table border=1><tr align='center' bgcolor='lightgreen'>";
data += "<td>아이디</td><td>비밀번호</td><td>이름</td><td>이메일</td><td>가입일</td><td>삭제</td></tr>";
for(int i=0; i < list.size(); i++) {
MemberDTO dto = list.get(i);
String id = dto.getId();
String pwd = dto.getPwd();
String name = dto.getName();
String email = dto.getEmail();
Date joinDate = dto.getJoinDate();
data += "<tr><td>"+id+"</td><td>"+pwd+"</td><td>"+name+"</td><td>"+email+"</td><td>"+joinDate+"</td><td>"+"<a href='member3?command=delMember&id="+id+"'>삭제</a></td></tr>";
}
data += "</table>";
data += "<a href='memberForm.html'>새 회원 등록하기</a>";
data += "</body></html>";
out.print(data);
}
}
💻 실행 결과