BackEnd/Servlet

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

제이드Jade 2022. 2. 15. 04:44

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

  • 서블릿 비즈니스 처리작업 : 서블릿이 클라이언트로부터 요청을 받으면 그 요청에 대해 작업(ex. 데이터베이스 연동 작업, 다른 서버와 연동해서 데이터를 얻는 작업)을 수행하는 것
  • 서블릿 비즈니스 작업 예) 웹 사이트 회원 등록/로그인/쇼핑몰 상품 주문 요청 처리 작업

 

<서블릿의 비즈니스 처리 과정>

1. 클라이언트로부터 요청 받기

2. 데이터베이스 연동과 같은 비즈니스 로직 처리

3. 처리 결과를 클라이언트에게 돌려 줌

 

7.2 서블릿의 데이터베이스 연동하기

클라이언트로부터 요청을 받으면 서블릿은 SQL문을 사용해 db에 접근하여 작업을 함. 이 과정에서 DAO, VO 클래스가 사용 됨

* DAO : DB의 data에 접근하기 위한 객체

* VO :  단순히 값 타입을 표현하기 위한 클래스

 

<서블릿으로 테이블의 회원 정보 조회 하는 과정>

1. 웹 브라우저가 서블릿에게 회원 정보를 요청

2. 서블릿이 요청을 받은 후 DAO 객체를 생성하여 그 내의 db 데이터를 가져오는 list() 메서드를 호출

3. list()에서 다시 데이터베이스와 연결하는 connDB() 메서드를 호출하여 DB와 연결한 후 SQL문을 실행 해 회원 정보를 조회

4. 조회된 회원 정보를 VO 속성에 설정한 후 다시 ArrayList에 저장

5. ArrayList를 다시 메서드를 호출한 서블릿으로 반환한 후 ArrayList의 VO를 차례대로 가져와 회원 정보를 HTML 태그의 문자열로 만듦

6. 만들어진 HTML 태그를 웹 브라우저로 전송해서 회원 정보를 출력

 

~실제 작동 방법~

1) 테이블에 데이터 넣기

2) 오라클db와 연동하는데 필요한 드라이버인 ojdbc6.jar를 다운받아 프로젝트의 /WebContent/WEB-INF/lib 폴더에 복붙

3) DAO, Servlet, VO 클래스 생성

4) 위의 회원 정보 조회 과정 2~6 실행

 

1) SQL Developer실행 > 왼쪽에 + 클릭해 새 접속.. > 연결 정보 입력 후 접속 > 테이블 생성, 필드 주입하는 SQL문 작성

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

insert into t_member
values('hong','1212','홍길동','hong@gmail.com',sysdate);
insert into t_member
values('lee','1212','이순신','lee@gmail.com',sysdate);
insert into t_member
values('kim','1212','김유신','kim@gmail.com',sysdate);
commit;

select * from t_member

* sysdate : 현재 날짜 자료형

* commit : SQL Developer에서 테이블에 정보 추가 후 반드시 커밋을 해줘야 영구적으로 반영 됨

 

* 참고) 테이블에 insert할 때 '테이블스페이스 'USERS'에 대한 권한이 없습니다.' 라는 오류 발생할 시

sqlplus에서 로그인하고

ALTER USER C##scott DEFAULT TABLESPACE USERS QUOTA UNLIMITED ON USERS;  입력

 

 

 <4 코드>

MemberServlet.java

@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();
		MemberDAO dao=new MemberDAO(); //SQL문으로 DB를 조회하는 클래스인 MemberDAO 객체 생성
		List<MemberVO> 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></tr>");
	     
	     for(int i=0;i<list.size();i++) {
	    	 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></tr>");	
	     }
	     out.print("</table></body></html>");
	}

}

 

MemberDAO.java

	private static final String driver = "oracle.jdbc.driver.OracleDriver";
	private static final String url = "jdbc:oracle:thin:@localhost:1521:XE";
	private static final String user = "scott";
	private static final String pwd = "tiger";
	private Statement stmt;
	private Connection con;

	public List<MemberVO> listMembers() {
		
		List<MemberVO> list=new ArrayList<MemberVO>();
		try {
			connDB(); //db연결
			String query="select * from t_member";
			System.out.println(query);
			ResultSet rs=stmt.executeQuery(query); //sql문으로 db조회(느리다는 단점으로 추후 수정예정)
			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객체의 속성에 넣기
				vo.setId(id);
				vo.setPwd(pwd);
				vo.setName(name);
				vo.setEmail(email);
				vo.setJoinDate(joinDate);
                
				list.add(vo); //vo객체를 리스트에 추가
			}
			rs.close();
			stmt.close();
			con.close();
			
		}catch(Exception e){
			e.printStackTrace();
		}
		return list; //테이블의 데이터 전부를 객체로 담아 리스트로 반환
	}

	private void connDB() {
		try {
			Class.forName(driver);
			System.out.println("Oracle 드라이버 로딩 성공");
			con = DriverManager.getConnection(url, user, pwd);
			System.out.println("Connection 생성 성공");
			stmt = con.createStatement();
			System.out.println("Statement 생성 성공");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

 

MemberVO.java

package sec01.ex01;

import java.sql.Date;

public class MemberVO {

	private String id; //t_member 테이블의 컬럼 이름과 동일한 자료형과 이름으로 속성 선언
	private String pwd;
	private String name;
	private String email;
	private Date joinDate;
    
	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에서 Statement 인터페이스를 이용해서 db와 연동 함

private Statement stmt;
ResultSet rs=stmt.executeQuery(query);

>> 연동할 때마다 DBMS에서 다시 SQL문을 컴파일해야 하므로 속도가 느림

>> PreparedStatement 인터페이스를 사용! => SQL문을 미리 컴파일해서 재사용하므로 훨씬 빠름

 

<PreparedStatement 인터페이스 특징>

  • Statement 인터페이스를 상속하므로 지금까지 사용한 메서드를 그대로 사용
  • 실행하려는 SQL문에 '?'를 넣을 수 있음
  • 이미 컴파일된 SQL문을 DBMS에 전달하여 성능 향상

(cf. Statement : DBMS에 전달하는 SQL문은 단순한 문자열 / DBMS는 이 문자열을 DBMS가 이해할 수 있도록 컴파일&실행)

 

 

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

  • 앞 절에서는 웹 애플리케이션이 필요할 때마다 db에 연결하여 작업하는 방식 >> db 연결에 시간 많이 소요
  • 웹 애플리케이션이 실행됨과 동시에 연동할 db와의 연결을 미리 설정해 두고 필요할 때마다 이 연결을 이용해 빠르게 데이터 베이스와 연동하여 작업
  • 커넥션풀 : 미리 db와 연결시킨 상태를 유지하는 기술
  • 즉, 애플리케이션 실행 시 미리 ConnectionPool 객체를 생성 후 연결을 맺고 db연동 작업 발생 시 이 객체를 이용해 작업
  • 톰캣 컨테이너는 자체적으로 ConnectionPool 기능을 제공, 톰캣 실행 시 톰캣은 설정 파일에 설정된 DB 정보를 이용해 미리 데이터베이스와 연결하여 ConnectionPool 객체를 생성 후에 이 객체 메서드를 호출하여 연동 작업

 

  • 실제 웹 애플리케이션에서는 Java SE에서 제공하는 javax.sql.DataSource 클래스를 이용해 ConnectionPool 객체를 구현
  • ConnectionPool 객체에 접근할 때는 JNDI 이용

 

JNDI : 필요한 자원을 키/값 쌍으로 저장한 후 필요할 때 키를 이용해 값을 얻는 방법

JNDI의 사용 예)

  • 웹브라우저에서 name/value 쌍으로 전송한 후 서블릿에서 getParameter(name)로 값을 가져올 때
  • 해시맵/해시테이블
  • 웹 브라우저에서 도메인 네임으로 DNS 서버에 요청할 경우 도메인 네임에 대한 IP 주소를 가져올 때

톰캣 컨테이너가 ConnectionPool 객체를 생성하면 이 객체에 대한 JNDI 이름(key)을 미리 설정, 후에 연동작업 시 이 키로 접근하여 작업

 

<톰캣의 DataSource 설정 및 사용 방법>

1. JDBC 드라이버ConnectionPool 기능 관련 jar 파일(DBCP 라이브러리)을 /WEB-INF/lib 폴더에 설치

2. CATALINA_HOME/context.xml에 Connection 객체 생성 시 연결할 db정보를 JNDI로 설정

3. DAO 클래스에서 DB와 연동 시 미리 설정한 JNDI라는 이름으로 DB와 연결해서 작업

 

2는 Servers>Tomcat ~ > context.xml에 <Context> 태그 내 밑에 소스 입력

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

* name의 jdbc/orcle로 DataSource에 접근

* 오라클 데이터베이스를 연결할 때 driverClassName, url, username, password만 주로 변경해서 설정

* <Resource ~~ /> 로 해야함에 주의

 

<ConnectionPool로 연결할 데이터베이스 속성>

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

 

private DataSource dataFactory;

public MemberDAO() {
    try {
        Context ctx=new InitialContext();
        Context envContext=(Context) ctx.lookup("java:/comp/env"); //JNDI에 접근하기 위해 기본 경로를 지정
        dataFactory=(DataSource) envContext.lookup("jdbc/oracle"); 
        //톰캣 context.xml에 설정한 name 값을 이용해 톰캣이 미리 연결한 DataSource를 받아 옴
    
    }catch(Exception e) {
        e.printStackTrace();
    }
}

public List<MemberVO> listMembers() {

    List<MemberVO> list=new ArrayList<MemberVO>();
    try {
        con=dataFactory.getConnection(); //DataSource를 이용해 db에 연결
        ...

이전 코드와 다르게 db연결을 위한 변수, 메서드 conn()가 필요 없다.

 

 

7.4 DataSource 이용해 회원 정보 등록하기

 

<PrepareStatement에서 insert문 사용하는 방법>

  • ?는 순서대로 대응
  • 각 ?에 대응하는 값을 지정하기 위해 PrepareStatement의 setter를 이용
  • sett()의 첫번째 인자는 '?'의 순서를 지정
  • ?는 1부터 시작
  • insert,delete, update문은 executeUpdate() 메서드를 호출해야 함

 

DAO에서 insert문 사용하기

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(?,?,?,?)";

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

 

7.5 회원 정보 삭제하기

dao.delMember(id);

&

String query="delete from t_member"+" where id=?" ... 로 삭제 로직 구현