• Home
  • About
    • on Weekend photo

      on Weekend

      𝙎𝙩𝙪𝙙𝙮𝙞𝙣𝙜

    • Learn More
    • Instagram
    • Github
  • Archive
    • All Posts
    • All Tags
    • All Categories
  • Categories
    • Problem Solving
    • TIL
    • Study
    • Etc
    • 필사
  • Projects

인프런 - 실전 JSP 6

05 Jan 2021

𝗝𝗦𝗣

  • 수강 코스

𝓓𝓪𝔂 6

18. JDBC

JAVA와 오라클이 통신할 수 있게 해주는 API이다. eclipse에서도 오라클 API를 이용하기 위해서, Oracle LIB을 이클립스에 복사하여 사용한다.

18.1. JDBC 설정

1) 이클립스 Preferences - class path에서 jre path를 확인한다.
2) C:\oraclexe\app\oracle\product\11.2.0\server\jdbc\lib에 있는 ojbc6.jar 파일을 복사해서 위의 jre lib ext path에 넣는다.

18.2. JDBC를 이용한 데이터 관리

전체적인 흐름

client <-> jsp,servlet <-> DB
클라이언트가 jsp, servlet에 요청을 하면, 웹서버는 JDBC를 이용하여 DB에 접근한다.

JDBC 실행 순서

1) Driver loading : class.forName(driver);라고 드라이버 이름을 명시해주어서, Oracle Driver를 메모리에 로딩하여 언제든지 쓸 수 있게 된다.
2) Connection : 자바와 오라클을 연결해주기 위해, 연결을 해주는 connection 객체를 얻는다. con = DriverManager.getConnection(url, id, pw); 를 이용하여 커넥션 객체를 받는다.
3) Statement : stmt = con.createStatement(); 를 이용하여 자바에서도 쿼리문을 날릴 수 있는 객체인 statement를 커넥션으로부터 구한다.
4) query : String sql = "SELECT * FROM book"; 이런 식으로 String type query문을 작성해준다.
5) run : 작성한 쿼리문을 statme 객체에 넣어주어 쿼리를 전송한다. res = stmt.executeQuery(sql);

response.setContentType("text/html; charset=UTF-8");
PrintWriter out = response.getWriter();

String bookName = request.getParameter("book_name");
String bookLoc = request.getParameter("book_loc");

String driver = "oracle.jdbc.driver.OracleDriver";
String url = "jdbc:oracle:thin:@localhost:1521:xe";
String id = "scott";
String pw = "tiger";

Connection con = null;
Statement stmt = null;

try {
  Class.forName(driver);

  con = DriverManager.getConnection(url, id, pw);
  stmt = con.createStatement();
  String sql = "INSERT INTO book(book_id, book_name, book_loc)";
      sql += " VALUES (BOOK_SEQ.NEXTVAL, '" + bookName + "', '" + bookLoc + "')";

  // executeUpdate()는 쿼리가 적용된 갯수를 반환한다.
  // 에러가 발생해서 아무것도 적용이 안되었다면, 0을 return한다.
  int result = stmt.executeUpdate(sql);

  if(result == 1) {
    out.print("INSERT success!!");
  } else {
    out.print("INSERT fail!!");
  }

// 데이터베이스와 관련된건 네트워크상의 문제 등이
// 있을 수 있으므로 꼭 try-catch 문으로 묶어주어야 한다.
} catch (Exception e) {
  e.printStackTrace();
} finally {
  try {
    //리소스를 썼으면 해제해주는 작업도 해주어야 한다. 이에 stmt가 null이면 리소스를 close해주는 작업을 해주고 있다.
    if(stmt != null) stmt.close();
    if(con != null) con.close();
  } catch (Exception e2) {
    e2.printStackTrace();
  }
}
}

statement문에서는 SELECT할 때에는 stmt.executeQuery(), 나머지는 stmt.executeUpdate()를 해준다.

18.3. PreparedStatement

statement에서 쿼리문을 조작할 때, 코드가 지저분한 감이 있다. 이를 보완하기 위해서 PreparedStatement를 사용합니다.
이전에 JDBC에선 Statement를 먼저 작성하고, 그 다음에 query문을 작성했다. pstmt의 경우에는 query를 먼저 작성하되, 사용자가 입력할 값은 ?로 남겨둡니다. 이를 통해 쿼리문이 상당히 보기 편해집니다.
이후 connection에서 prepareStatmenet에 sql 쿼리를 넘겨줍니다.

String sql = "UPDATE book SET book_loc = ? WHERE book_name =?";
pstmt = con.prepareStatement(sql);
pstmt.setString(1, "001-00007123");
pstmt.setString(2, "book7");

위처럼 쿼리문의 질의에 들어갈 위치에 값을 넣어주며, Human Error를 줄일 수 있습니다.

19. DAO와 DTO

19.1. DAO, DTO란?

웹서버는 브라우저에게 요청을 받고, 이후 DB에 접근하여 여러 작업을 시행하는데요.

  • DAO는 Data Access Object로, DB에 접근하는 기능을 수행하는 오브젝트입니다. 이전에는 Servlet, DAO를 같은 파일에 섞어서 작성했는데요. 객체로 DAO 파트를 떼어내어 사용하게 됩니다.
  • DTO는 Data Transfer Object입니다. DB에 있는 데이터의 형태와, 자바에서 관리하는 데이터의 형태가 다릅니다. DB의 데이터 형태가 컬럼별로 정리가 되어있는데, 자바에서는 이를 가져와서 자바만의 다양한 자료형으로 변환하여 사용하게 됩니다. 이 데이터타입의 차이를 변환해주는 것이 DTO입니다. (다른 말로 DTO는 VO라고도 부릅니다.)

19.2. DAO, DTO 구현

막상 하려니 그전까지 나지 않던 SQL 관련 에러가 발생합니다. (1) 우선, 데이터베이스에 BOOK 테이블이 만들어져있지 않아서, 해당 테이블을 만들어주고 (2) SEQUENCE도 만들어주었습니다. 나아가 (3) cmd에서 sqlplus 명령어를 입력해 로그인도 해주어 db서버를 가동시켜주었습니다.

CREATE SEQUENCE BOOK_SEQ
START WITH 1
INCREMENT BY 1
MAXVALUE 999999
MINVALUE 1
NOCYCLE
NOCACHE
NOORDER;

위의 명령어로 oracle에 sequence를 설정해주었습니다. 진행중인 #bbbbff 프로젝트에서도 sequence를 사용하자는 이야기가 나왔었는데, 드디어 익혔으니 시퀀스를 넣어보아야겠습니다!

코드를 따라치는 도중, java.lang.NullPointerException이 발생했습니다. 개발자 브이로그에서 주로 하루의 시작을 알리는 NullPointerException을 만나서 묘하게 행복했습니다. Servlet에서 DAO와 DTO기능을 분리했는데, DAO에서 받아올 때에 에러가 발생하고 있었습니다. 30분 가량 코드를 뜯어보고, 온갖 것을 주석처리해보며 다양한 시도를 해보았는데 마지막에 return list가 아니라 처음 generate된 상태 그대로 return null을 해주고 있었습니다… 수정하니 다 잘 돌아갑니다.

19강은 중요한 내용이기에, 결과 코드를 따로 빼두었습니다. 해당 링크에서 확인해주세요.


20. Connection pool

데이터 베이스와 통신하는 자원을 효율적으로 관리하기 위한 방법에 대해서 학습합니다. 이때 자원이란 드라이버, 커넥션, statement(쿼리를 실행해주는 자원) 등이 있었습니다. 커넥션 풀은 이처럼 데이터베이스와 통신하기 위해 사용하는 자원들을 효율적으로 관리하고자 고안되었습니다.

20.1. 커넥션 풀이란?

브라우저에서 웹서버 쪽으로 request가 오면, 웹서버는 필요에 따라 데이터베이스에 접근하게 됩니다. 지금까지 (1) 드라이버를 로드하고 (2) 커넥션 객체를 만들어 (3) 데이터를 핸들링하고 (4) 이후 작업이 끝나면 커넥션을 닫았었습니다.

여기에서 눈에 보이진 않고, 아직 실감이 나진 않지만 웹 서비스가 커진다면 웹서버는 데이터베이스에 무수히 많은 접속을 하게 됩니다.
가령 검색사이트에선 서버에 굉장히 많은 질의를 하게 됩니다. 이에 서버는 데이터베이스에 굉장히 많은 커넥션을 열게 되는데, 이때마다 커넥션을 열고, 데이터 핸들링하고, 커넥션을 닫는 과정을 계속 반복하게 된다면 부하가 어마어마할 것입니다.

어차피 커넥션 맺고, 조작하고 끄고 하는 DB Connection - Data Handling - DB Connection Close를 반복할 것이라면, 미리 Connection을 만들어두고 필요할 때마다 미리 만들어둔 Connection을 줌으로서 향상된 퍼포먼스를 발휘할 수 있을 것입니다.

이에 커넥션 풀이 고안되었습니다. 웹서버는 서버가 한가할 때 미리미리 커넥션 풀에 커넥션을 만들어두고, 필요할 때에는 커넥션을 rent한 후, 사용하고 나서는 return해줍니다. 이를 통해 퍼포먼스를 보다 안정적이로 효율적으로 나타낼 수 있습니다.

이러한 커넥션 풀은 어디에 만들어둘까요? 웹 컨테이너에 미리미리 만들어두게 됩니다. 톰캣에 커넥션 풀을 만들어달라고 설정을 해주어야 하는데요.

20.2. 커넥션 풀 설정

커넥션 풀은 Server의 context.xml이라는 파일의 Resource 부분에 리소스 정보를 넣어주게 됩니다.

<Resource
	auth = "Container"
	driverClassName = "oracle.jdbc.driver.OracleDriver"
	url="jdbc:oracle:thin:@localhost:1521:xe"
	username="scott"
	password="tiger"
	name="jdbc/Oracle11g"
	type="javax.sql.DataSource"
	maxActive="4"
	maxWait="10000"
/>

(1) 드라이버 연결 정보 : DAO에 넣던 기본 정보를 톰캣 서버에 넣어주게 됩니다.
(2) name : name에 넣은 정보를 기반으로 톰캣 컨테이너에게 이 name의 커넥션 풀을 달라고 요청하게 됩니다.
(3) type : 컨테이너도 커넥션 풀을 맺기 위해서는 객체가 필요할 것입니다. type을 이용해 컨테이너에 객체를 만들게 됩니다.
(4) maxActive : 최대 커넥션 생성 갯수입니다. 컨테이너는 이 갯수만큼 커넥션 객체를 미리미리 만들어둡니다.
(5) maxWait : maxWait에 다다르면 자동으로 커넥션 1개가 더 생성이 됩니다. 가령 10,000ms(10초)를 기다리면 1개를 더 생성해 5개의 커넥션 객체를 갖게 됩니다.

20.3. 커넥션 풀 구현

자바 코드에선 데이터베이스에 억세스할 일이 없습니다. 실제로 드라이버를 로드하는 코드를 쓸 일이 없어지고, context에서 커넥션을 빌려오는 아래와 같은 코드가 필요해집니다.

DataSource dataSource;

public BookDAO() {
  try {
    Context context = new InitialContext();
    dataSource = (DataSource)context.lookup("java:comp/env/jdbc/Oracle11g");
  } catch (Exception e) {
    e.printStackTrace();
  }
}
public ArrayList<BookDTO> select() {
  ArrayList<BookDTO> list = new ArrayList<BookDTO>();

  Connection con = null;
  PreparedStatement pstmt = null;
  ResultSet res = null;

  try {
    // con = DriverManager.getConnection(url, id, pw);
    con = datasource.getConnection();
  }
}

Context는 import javax.naming.Context;을 해주어야 합니다.



jspbackend Share Tweet +1