[23/07/03] 서블릿 (2)

yeju·2023년 7월 3일
0

Servlet

목록 보기
2/6
post-thumbnail

📖 7. 서블릿 비즈니스 로직 처리

📌 서블릿에서 데이터베이스 연동하기

서블릿에서 회원 정보 DB를 조회하는 방법

  1. 클라이언트(웹 브라우저)가 회원 조회를 요청
  2. 서블릿에서 요청을 받아 DAO 객체를 사용해 회원 조회 메서드 호출
  3. DAO에서 데이터베이스와 연결해 회원 정보를 조회하고 각 레코드를 DTO에 담아 ArrayList에 저장 후 반환
  4. 서블릿에서 ArrayList를 받아 ArrayList 안의 DTO를 하나씩 꺼내 DTO의 속성(아이디 등)을 HTML 태그로 이루어진 문자열로 만들기
  5. 클라이언트에 만든 문자열을 전송해 회원 정보 출력

📝 회원 정보를 받아와 출력하는(Read) 웹 어플리케이션 만들기

1. 데이터베이스 생성하기

회원 조회를 요청하기 위해 필요한 데이터베이스를 미리 생성해야 함
MySQLpractice 데이터베이스에 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');

2. 필요한 서블릿, 클래스 생성하기

요청을 받아 처리할 서블릿 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() 해야 하고, 연결 시 예외가 발생할 수 있으므로 예외 처리가 필요하다.


📌 DataSource 이용해 데이터베이스 연동하기

(= 커넥션풀을 이용해 데이터베이스 연동하기)

커넥션풀(ConnectionPool) 이란?

데이터베이스와의 연결을 미리 설정해 두고 필요할 때 사용하는 기술
앞에서는 필요할 때마다 connDB() 메서드를 호출해 DB와 새로 연결해 작업했지만, 커넥션풀을 이용하면 웹 어플리케이션을 실행할 때부터 DB와의 연결이 미리 설정되기 때문에 작업 속도를 향상시킬 수 있음

커넥션풀을 이용해 DB 연동하는 방법

  1. 커넥션풀 기능 관련 라이브러리 설치
  2. 톰캣 서버 설정 파일 context.xml 에서 <Resource> 태그로 DB 정보 설정 (=DataSource)
  3. DB 연결할 클래스에서 DataSource 객체에 미리 연결한 DB 정보를 받아온 후 getConnection() 메서드 호출해 연결

📝 위에서 실습한 프로젝트를 커넥션풀 방식으로 수정하기

1. 라이브러리 설치, 톰캣 DataSource 설정

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) 쌍으로 저장해 필요할 때 키를 이용해 값을 얻는 방법

2. DB 연결할 클래스(DAO) 에 코드 작성하기

위 실습에서 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);
	}
	*/
}

📝 위에서 실습한 프로젝트에 회원 등록(Create), 삭제(Delete) 기능 추가하기

1. 회원 정보를 입력받을 페이지 작성

입력한 데이터를 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>

2. DAO 클래스에 회원 등록, 삭제 메서드 추가

...
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(); }
	}
}

3. 요청을 받는 서블릿 새로 작성

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);
	}
}

💻 실행 결과

profile
🌱

0개의 댓글