JDBC를 시작한 지 3일째, 정말 투박했던 내 코드에 이것, 저것 넣으면서 볼만하게 만들고 있다.
이런 걸 리팩토링이라고 하는데, 사실 너무 투박한 코드에 당연한 것을 넣는 것이다 보니 ‘리팩토링’이라는 거창한 단어를 못 붙이겠다 ㅠㅠ
하지만 확실히 코드가 훨씬 깔끔해지는 느낌이다.
무튼 오늘은 다음 세 가지를 배워봤다.
- PrepareStatement
- try-resource
- 암호화
기존 실습할 때, 워크시트 용도로 Statement
인스턴스를 사용했다.
근데 가장 큰 문제가 Statement를 사용하면 쿼리를 작성할 때, 다음처럼 문자열을 연결해 만들었는데, 해보면 알겠지만 무지 불편하고 투박하다. 오타는 덤..
"insert into cafe_menu values(cafe_seq.nextval, '"+pname+"', "+price+", '"+iced+"')"
이런 불편함을 극복하기 위해 사용하는 게 PrepareStatement
이다.
Statement
를 상속받는 클래스이다.Statement
의 보안문제와 쿼리작성의 불편함을 제거하였다.Statement
를 상속받기 때문에 Statement로 했던 처리를 할 수 있다.1) 대부분, 내부적인 동작 방식의 특징상 성능이 더 좋다.
2) ?
로 변수를 설정하기 때문에 변수 설정이 편리하다.
3) 내부적으로 SQL 주입을 방지하기 위한 기능이 있다 (보안 공격이 먹히지 않는다)
?
으로 채우고, 이를 인덱스로 인식해 처리한다. (1부터 시작)set
으로 들어갈 자료형을 정하고 그에 따라 홑따옴표를 붙여준다.// (1) SQL 작성
String sql = "delete from cafe_menu where pid=?";
// (2) prepareStatement 인스턴스를 통해 연결
PreparedStatement pstat = con.prepareStatement(sql);
// (3) ? 의 빈 칸을 채워준다.
pstat.setInt(1, 1013);
// (4) 연결을 통해 쿼리를 실행한다. statement처럼 sql을 인자값으로 넣지 않는다.
pstat.executeUpdate();
DB의 연결은 한정적인 자원으로 일정 수 이상, DB에 접속하면 DB서버가 터지고 만다.
그래서 DAO에서 각 기능을 처리할 때, close();
를 통해 연결을 닫는다.
하지만 한 가지 문제는, 연결이 열리고 메서드가 진행되는 중 예외가 발생하면 DAO의 Callee 메서드들은 모두 throws Exception
으로 main 으로 예외를 전가해버린다.
즉, 코드의 흐름이 예외가 발생하면 main으로 나가서 끝나게 되어 close();
가 실행되지 않는다.
결국 메서드를 사용할 때마다 연결들이 쌓이게 되고, DB의 한계를 넘어서면 DB가 터진다.
이런 문제를 해결하기 위해 3가지 방법이 있었다.
Connection con = null;
try {
con = Driver~
con.close();
} catch{
con.close();
}
finally
절이다.[ finally ]
try-catch에 한해 메서드를 나가려고 하는 모든 동작에 대해서 실행된다.
try {
con = Driver~
~~~
} catch{
~~~
} finally{
con.close();
}
try( ① - ③ ){
②
}
① 소괄호의 내용을 실행
② 중괄호 실행
③ 소괄호 Close
try(
Connection con = this.getConnection();
PreparedStatement pstat = con.prepareStatement(sql);
){
pstat.setInt(1, pid);
int result = pstat.executeUpdate();
con.commit();
return result;
}
실행 순서에 따라서 먼저 () 소괄호
안에 코드를 실행한다.
단, 소괄호 안에 오는 코드들은 AutoCloseable
클래스를 상속받은 클래스들만 가능하다.
= 닫을 수 있는 객체가 선언되는 코드만 가능
리턴, 예외 등 어떤 상황이든 {} 중괄호
가 벗어나면 ③이 실행된다.
PrepareStatement
로 빈칸을 채울 때는 이중으로 사용한다.
try(
Connection con = this.getConnection();
PreparedStatement pstate = con.prepareStatement(sql);
){
String encryPw = utils.EncryptUtils.SHA256(pw);
pstate.setString(1, id);
pstate.setString(2, encryPw);
try(ResultSet rs = pstate.executeQuery();){
boolean result = rs.next();
return result;
}
}
회원가입을 하면 우리가 지정한 PW가 DB에 그대로 들어가게 된다.
이때, DB 관리자가 PW를 볼 수 있게 된다. 물론 봐도 되지만 우리의 PW을 알아봐 선 안된다.
따라서 암호화 작업을 하여 PW을 알아보지 못하게 한다.
public static String SHA256(String pwd) {
try{
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(pwd.getBytes("UTF-8"));
StringBuffer hexString = new StringBuffer();
for (int i = 0; i < hash.length; i++) {
String hex = Integer.toHexString(0xff & hash[i]);
if(hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
//출력
return hexString.toString();
} catch(Exception ex){
throw new RuntimeException(ex);
}
}
// DAO에 적용 시.
String sql = "insert into member values(?, ?)";
String encryPw = utils.EncryptUtils.SHA256(Pw);
pstate.setString(1, dto.getId());
pstate.setString(2, encryPw); // 입력받은 pw을 암호화하여 쿼리에 반영
pstate.executeUpdate(); // 해당 쿼리를 전달
con.commit(); // DB에 반영