'방어적인 코딩'에 해당되는 글 3

  1. 2010.10.14 파일 스트림을 이용해서 복사하기
  2. 2010.06.21 에러 잡기의 단계
  3. 2009.04.29 JDBC Code Template

파일 스트림을 이용해서 복사하기

서버가 여러 대인 시스템 구성에서 FCKeditor를 이용하면서 파일을 업로드하는 기능을 구현
업로드한 파일이 여러 대의 서버에 저장되거나 단일 파일 서버에 저장되어야 함.
웹에디터의 특성으로 인해 파일(업로드된 이미지)이 웹어플리케이션내에 존재해야 함.
각 서버에 업로드된 이미지 파일이 있어야 하므로 업로드하면서 파일을 각 서버에 복사하기로 함.

- 윈도우즈 환경이라서 네트워크 드라이브를 바로 가기로 만들어서 했는데 안된다고 함.(뭔 말이고???)

- 어떻게 파일 업로드하는 부분을 찾아서 왔다.
net.fckeditor.connector.impl.AbstractLocalFileSystemConnector 를 수정했다고 한다.
그러나 적합한 디렉토리가 아니라는 둥, 에러가 난다.

- 여차저차 디렉토리를 생성하도록 하고 나니 이번에는 파일은 생성되는데 크기가 0이라고 한다.
메서드의 인자중 하나가 inputStream(FileInputStream)으로 넘어온다.
이 스트림을 가지고 org.apache.commons.io.IOUtils.copyLarge(InputStream input, OutputStream output) 을 이용해서 복사한다.

스트림을 이용해서 파일을 복사하고 나면 스트림 끝에 도달했으므로 두번째 복사부터는 파일을 쓸수가 없다.(WRITE)
스트림을 어딘가에 저장해 두었다가 파일 복사할때 스트림을 처음으로 되돌려서 사용하는게 좋겠다.
적당한 녀석이 ByteArrayInputStream 이다.
(RamdomAccessFile 도 될수 있을라나?)

파일 스트림을 읽어서 바이트 배열로 만들어야 하는데 System.arraycopy() 를 쓰면 되겠다 싶었다.

- 물어보기만 하던 녀석이 ByteArrayOutputStream 을 쓸거라고 한다.
오...호

읽어들인 스트림을 처음으로 돌리려면 reset()을 호출한다.

- 덧붙이면, IOUtils.copyLarge()의 리턴값도 체크할 것. 방어적인 코딩이 정신건강에 좋지 않나.

에러 잡기의 단계

1. 말 그대로 디버깅을 한다.
디버깅 툴이나 IDE에서 제공하는 디버깅 기능을 이용해서 디버깅을 한다.
처음에는 신기해 한다.
그러나...원격에서 운영되고 있는 서버를 디버깅 모드로 돌릴 수는 없잖아.

2. 철두철미하게 로깅을 한다.
로그를 잘 남기면 에러 잡는 거는 시간 문제다.
로그 파일이 엄청나게 쌓인다. ㅎㅎ
로그는 정확하게 영리하게 남겨야 한다.

3. 테스트 케이스를 작성한다.
테스트 케이스를 통해서 에러가 발생할 곳을 미리 찾아 낸다.
운영중에 에러가 발생하더라도 테스트 케이스를 실행해서 힌트를 얻는다.
진정한 고수의 수준이 아닌가 싶다.

- 기본적으로는 에러 체크/예외 처리를 잘하고 방어적인 코딩을 해야겠지

JDBC Code Template


PreparedStatement ps = null;
try{
    ps = con.prepareStatement(QUERY);
    int idx = 0;
    ps.setString(++idx, COL_1);
    ...
    ps.setString(++idx, COL_N);
    
    return ps.executeUpdate();

}catch(Exception e){
    throw new RuntimeSQLException(e, primaryKey);
}finally{
    if(ps != null){ try{ ps.close(); }catch(Exception ignored){  }
}
Connection con = null;
PrepareStatement ps = null;

try{
	ps = con.prepareStatement(QUERY);  
	int idx = 0;  
	ps.setString(++idx, COL_1);  
	...  
	ps.setString(++idx, COL_N);  

	int affectedRow = ps.executeUpdate();
	if(affectedRow != 1){
		throw new RuntimeSQLException(" expected : 1, result : " + affectedRow
			, primaryKey); //=-=> 정형화...
	}
	con.commit();
}catch(Exception e){
	con.rollback();
	throw new RuntimeSQLException(e, primaryKey);
}finally{
    if(ps != null){ try{ ps.close(); }catch(Exception ignored){  }
    if(con != null){ try{ con.close(); }catch(Exception ignored){  }
}