패키지이름: servletDB.ex01
*실무에선 패키지 이름은 용도에 맞게 의미있는 이름으로 만든다.
웹 애플리케이션은 비즈니스 로직을 사용자에게 제공하는 인터페이스 역할을 한다.
클라이언트가 회원 정보를 요청하면 그걸 화면에 보여주는 과정을 7개로 나눔.
각 클래스 역할 요약
서블릿(MemberServlet) : 클라이언트 요청 + DB처리 + 응답
DAO(MemberDAO): DB연동과 DB일처리
DTO(MemberDTO) : 단순히 데이터를 캡슐화하는 역할 을 하며, 비즈니스 로직은 포함하지 않음
1. 회원 정보 요청
클라이언트가 브라우저 주소창에 주소를 입력한다. (http:// ip주소:포트번호 / 프로젝트이름 / 서블릿 매핑 이름)
http://localhost:8090/servletDB/member
같은 매핑이름(member)을 가진 서블릿에 들어온다.
member 매핑이름이 있는 서블릿에 들어오면 다음으로 메서드를 만난다.
지금은 html를 따로 만들지 않고 서블릿만 만들어서 method는 get이다. (기본)
doGet()은 클라이언트가 뭔가를 검색하면 브라우저 주소창에 노출된다.
doPost()는 method="post"인 경우이다. 노출되면 안되는 정보(로그인)는 post를 쓴다.
우선 자바 서블릿 API를 구현하려면 지금 프로젝트에 servlet-api.jar 라는 라이브러리를 추가해줘야한다.
servlet-api.jar는 자바 서블릿 API를 구현하는 라이브러리 파일로,
자바 서블릿을 개발하고 실행하는 데 필요한 모든 클래스를 포함한다.
doGet(HttpServletRequest request, HttpServletResponse response)
→서블릿이 HTTP GET 요청을 처리할 때 호출한다.
HttpServlet 클래스를 상속한 MemberServlet(내가 만든 서블릿)에서 호출할 수 있다.
1. HttpServletRequest : 클라이언트의 HTTP 요청 정보를 제공하는 객체
클라이언트가 보낸 데이터에 접근할 수 있는 메서드를 포함한다.
매개변수는 이름은 request
2. HttpServletResponse : 서버가 클라이언트에게 응답을 보낼 때 사용하는 객체
매개변수 이름은 response
response.setContentType("text/html;charset=utf-8")
→ 응답의 콘텐츠 타입을 HTML로 설정+인코딩 방식을 utf-8로 하여 한글이 안깨지도록 한다.
2. listMembers() 호출 - MemberDAO
listMembers()는 MemberDAO클래스에 있다.
public List<MemberDTO> listMembers() {}
우선 MemberDTO타입의 ArrayList를 만들어준다.(이름:list)
List<MemberDTO> list = new ArrayList<MemberDTO>();
listMembers() 에는 그림 3,4,5,6 을 수행할 코드를 적으면 된다.
즉, 회원정보 요청이 들어오면 그 데이터들을 DB에서 가져오고, DTO에 보관하고, 변수 list에 담는 것이다.
전체 내용은 다음과 같다.
public List<MemberDTO> listMembers() {
List<MemberDTO> list = new ArrayList<MemberDTO>();
//DB연동(그림3번).
try {
connDB();
//쿼리문을 통해 회원목록을 가져오기
String query="select * from member_tbl";
ResultSet rs = stmt.executeQuery(query);
while(rs.next()) {
1. rs변수를 통해 데이터 가져오기
2. DTO에 데이터 저장 후 list에 데이터 담기
}
DB닫기
}catch (Exception e) {
예외 메시지 적기
}
//서블릿에 리턴(그림6번)
return list;
}
3. connDB() - 회원정보요청
4. DB에서 데이터(회원정보) 가져오기
5. MemberDTO에 회원정보 저장(캡슐화)
6. Return
따로 설명하기엔 서로 연관되어있어서 한꺼번에 적음
이제 데이터베이스와 연동시켜서 회원정보를 가져와야한다.
참고로 지금 방식은 클라이언트가 요청할 때마다 DB와 연동하는 비효율적인 방식이다.
나중에는 서버를 실행하자마자 톰캣서버에서 미리 DB를 연동하게 할 것인데 (DB연동상수, connDB() 삭제)
바로 톰캣 자체로 DB연결하는 라이브러리를 다운받은 후(tomcat-dbcp-7.0.30.jar)
context.xml (DB연결 리소스)에 직접 연결정보를 작성하여 서버를 키면 자동으로 DB에 연결되도록 할 것이다.
1) DB연동 상수 정의
private static final String DRIVER="com.mysql.cj.jdbc.Driver"; //mysql의 연결드라이버.
private static final String URL="jdbc:mysql://localhost:3306/ezendb"; //ezendb: DB의 SCHEMAS이름
private static final String USER="root"; //계정
private static final String PWD="1234"; //비번
private Connection conn;
private Statement stmt;
* Connection 클래스 : 데이터베이스와의 연결 담당
DRIVER, URL, USER, PWD 처럼 드라이버와 연동 후 연결정보를 가져올 수 있게 한다.
* Statement 클래스 : SQL 문을 실행하는 데 사용. SQL 문을 데이터베이스에 보내고 결과를 받아온다.
나중엔 Statement객체 대신 PreparedStatement객체를 이용한다.
이유는 두가지 1. 미리 컴파일해서 빠르다 2. 조건문의 쉽게 쓸 수 있다
2) connDB 메서드 작성
데이터를 가져오려면 라이브러리 mysql-connector-j-8.0.33.jar 를 lib에 넣어줘야한다.
connDB() 작성하는 방법은 먼저 Connection 객체변수인 conn에 연결 정보(DRIVER, URL...)를 담는다.
Class.forName(DRIVER); //JDBC드라이버 로드
conn=DriverManager.getConnection(URL,USER,PWD);
다음으로 SQL문을 실행할 수 있도록 해주는 createStatement() 메서드를 이용해 객체를 생성한다. (conn의 정보를 이용)
stmt=conn.createStatement();
여기서 생성된 stmt는 executeQuery() 메서드를 이용하여 회원정보를 가져올 것이다. (그림 4번)
*쿼리문을 통해 회원목록을 가져오는 코드
String query="select * from member_tbl";
ResultSet rs = stmt.executeQuery(query);
ResultSet: 데이터베이스에서 가져온 결과 집합을 나타내는 인터페이스.
데이터베이스 쿼리의 실행 결과를 담고 있으며, 행과 열의 형태로 데이터를 제공한다.
executeQuery(): Statement 객체를 사용하여 SELECT 쿼리를 데이터베이스에 보내고
그 결과로 ResultSet 객체를 반환하는 메서드
(헷갈려서 적음 → executeQuery(): Connection 객체에 createStatement()로 생성한 Statement객체 stmt를 사용하여 수행)
connDB() 작성
private void connDB() {
try {
Class.forName(DRIVER); ////JDBC드라이버 로드
System.out.println("mySql 드라이버 로딩 성공!!");
conn=DriverManager.getConnection(URL,USER,PWD);
System.out.println("Connection 생성 성공ㅋㅋ"); //DB연결 성공시
stmt=conn.createStatement();
System.out.println("Statement 생성 성공!!");
} catch (Exception e) {
System.out.println("DB연결오류..에휴");
e.printStackTrace();
}
}
이제 DB와 연동할 수 있는 connDB()를 만들었으니 회원정보를 가져와서 DTO에 데이터를 저장 후, (그림 5번)
서블릿에 리턴을 하면 된다. (그림 6번)
*listMembers() 모두 작성하기
public List<MemberDTO> listMembers() {
List<MemberDTO> list = new ArrayList<MemberDTO>();
//DB연동(그림3번).
try {
connDB();
//쿼리문을 통해 회원목록을 가져오기
String query="select * from member_tbl";
ResultSet rs = stmt.executeQuery(query);
while(rs.next()) {
//DB에서 데이터 가져오기(그림4번)
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");
//MemberDTO로..그림 5번
MemberDTO dto=new MemberDTO(); //인스턴스 객체 생성->생성자 함수 수행
dto.setId(id);
dto.setPwd(pwd);
dto.setName(name);
dto.setEmail(email);
dto.setJoinDate(joinDate);
list.add(dto);
}
rs.close();
stmt.close();
conn.close(); //DB닫기
}catch (Exception e) {
System.out.println("회원정보 조회 처리 중 에러!!");
e.printStackTrace(); //예외 상세 정보 출력
//e.getMessage();는 간단하게 설명메시지
}
//서블릿에 리턴(그림6번)
return list;
}
여기서 중요한건 데이터베이스에서 쿼리문을 통해 가져온 회원정보는 rs에 있고
이를 id, pwd, name, email, joinDate 변수에 각각 정보를 담았는데 (getString)
이것들을 바로 list에 넣지 않았다는 것이다.
바로 MemberDTO 객체변수 dto에 정보들(id, pwd...)을 세팅(setId, setPwd...) 후 그것을 list에 담았는데
이는 회원정보들을 쉽게 접근이나 수정하지 못하도록 캡슐화한 것이다.
따라서 최종적으로 servlet에서 list안의 회원정보들을 가져오기 위해선 get메서드를 이용해야 할 것이다.
*MemberDTO
package servletDB.ex01;
import java.sql.Date;
public class MemberDTO { //데이터를 이쪽저쪽 옮겨주는 저장소 역할
//컬럼명과 같게 하는게 좋다(가독성).
private String id;
private String pwd;
private String name;
private String email;
private Date joinDate; //import java.util.Date; 아니다.
//생성자
public MemberDTO() {
System.out.println("MemberDTO 객체 생성"); //MemberDTO객체만들 때 출력
}
//멤버변수들 private이라 getter setter 설정 -> DAO에서 가져와야함
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;
}
}
MemberDAO 최종 (listMembers(), connDB())
package servletDB.ex01;
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
//import javax.xml.crypto.Data; Date부르려다가 잘못 부름
import com.mysql.cj.protocol.Resultset;
public class MemberDAO {
//DB연동 상수 정의
private static final String DRIVER="com.mysql.cj.jdbc.Driver"; //mysql의 연결드라이버.
private static final String URL="jdbc:mysql://localhost:3306/ezendb"; //ezendb: DB의 SCHEMAS이름
private static final String USER="root"; //계정
private static final String PWD="1234"; //비번
private Connection conn; //Connection: DB연동하는 객체
private Statement stmt;//DB쿼리문 수행할 변수(select,insert...)
//회원정보 조회 메서드
public List<MemberDTO> listMembers() {
List<MemberDTO> list = new ArrayList<MemberDTO>();
//DB연동(그림3번).
try {
connDB();
//쿼리문을 통해 회원목록을 가져오기
String query="select * from member_tbl";
ResultSet rs = stmt.executeQuery(query);
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");
//MemberDTO로..그림 5번
MemberDTO dto=new MemberDTO(); //인스턴스 객체 생성->생성자 함수 수행.
dto.setId(id);
dto.setPwd(pwd);
dto.setName(name);
dto.setEmail(email);
dto.setJoinDate(joinDate);
list.add(dto);
}
rs.close();
stmt.close();
conn.close(); //DB닫기
}catch (Exception e) {
System.out.println("회원정보 조회 처리 중 에러!!");
e.printStackTrace(); //예외 상세 정보 출력
//e.getMessage();는 간단하게 설명메시지
}
//서블릿에 리턴(그림6번)
return list;
}
//데이터베이스 연결 메서드(그림3번). DB서버(MySQL)이 돌아가고 있어야 연동할 수 있다.
//라이브러리가 있어야한다. mysql-connector-j-8.0.33.jar
private void connDB() {
try {
Class.forName(DRIVER);
System.out.println("mySql 드라이버 로딩 성공!!");
conn=DriverManager.getConnection(URL,USER,PWD);
System.out.println("Connection 생성 성공ㅋㅋ"); //DB연결 성공시
stmt=conn.createStatement();
System.out.println("Statement 생성 성공!!");
} catch (Exception e) {
System.out.println("DB연결오류..에휴");
e.printStackTrace();
}
}
}
7. 조회된 결과를 HTML로 전송
package servletDB.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") //그림 1번! (클라이언트가 회원정보요청)
public class MemberServlet extends HttpServlet {
//method안주면 기본으로 doGet
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 {
response.setContentType("text/html;charset=utf-8");
MemberDAO dao = new MemberDAO(); //MemberDAO 인스턴스 객체 생성
List<MemberDTO> list = dao.listMembers(); //그림2번부분. + 6번부분(MemberDAO에서 while문 수행 후).
PrintWriter out=response.getWriter();
out.print("<html>");
out.print("<body>");
out.print("<h2 align='center'>회원 정보 목록</h2>");
out.print("<table align='center' border='1' width='700'>");
out.print("<tr align='center' bgcolor='lightgreen'>");
out.print("<th>아이디</th><th>비밀번호</th><th>이름</th><th>이메일</th><th>가입일자</th>");
out.print("</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();
out.print("<tr><td>" + id + "</td><td>" + pwd + "</td><td>" + name + "</td><td>" + email + "</td><td>" + joinDate + "</td></tr>");
}//for end
out.print("</table>");
out.print("</body>");
out.print("</html>");
}
}
이제 클라이언트가 매핑이 적힌 주소를 주소창에 입력하면
서블릿에서 doGet 메서드 (=doHandle)가 수행되고 화면에 DB의 데이터들(회원정보)이 출력된다.
최종결과
참고로 DB의 스키마, 테이블은 다음과 같음
고작 회원정보 하나를 보여주기 위한 간단한 비즈니스 로직도 코드가 꽤나 복잡하다는걸 알 수 있었다......
정리
클라이언트가 서블릿에 회원정보를 요청하면
서블릿에서 MemberDAO객체를 생성하여 listMembers() 메서드를 호출한다.
listMembers()메서드에선 DB와 연동(connDB()호출)하여 SQL문을 실행하고 이를 DTO에 설정한 후 ArrayList에 저장한다.
이를 변수 list에 담아 return하면 이제 listMembers()는 회원정보가 담긴 list를 리턴해준다.
마지막으로 서블릿에서 MemberDAO객체변수를 생성하여 listMembers()를 호출하고,
이를 변수 list에 담아준다.(타입은 MemberDTO)
list 안의 데이터를 반복문+get메서드를 이용해 끄집어내어 최종적으로 웹브라우저에 출력해준다.