[Info]Tags categorized posts and contents patterns..

[AJAX] Ajax Code E xamples.. [Book] About the book.. [CSS] CSS Code E xamples.. [DB] Sql Code E xamples.. [DEV] All development stor...

레이블이 JDBC인 게시물을 표시합니다. 모든 게시물 표시
레이블이 JDBC인 게시물을 표시합니다. 모든 게시물 표시

2016년 2월 24일 수요일

[JAVA]JDBC Driver 리스트..

출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

새로운 디비 연결할때마다 혹은 사용하던것도 굳이 외우고 있지는 않아서 이전소스를 찾아봐야하는 JDBC Drivers. 날잡아서 정리... ㅎ 실제 적용할때 한번더 찾아봐야겠지만 그래도 일일이 찾아다니는거 힘들어서.. ㅎ



Informix
Driver Class : com.informix.jdbc.IfxDriver
JDBC URL : jdbc:informix-sqli://<host>:<port>/<database>:informixserver=<dbservername>
Required File : ifxjdbc.jar  Download


JavaDB/Derby
Driver Class : org.apache.derby.jdbc.ClientDriver
JDBC URL : jdbc:derby:net://<host>:<port1527>/<databaseName>
Required File : derbyclient.jar  Download


Microsoft SQL Server 2000
Driver Class : com.microsoft.jdbc.sqlserver.SQLServerDriver
JDBC URL : jdbc:microsoft:sqlserver://<host>:<port1433>;DatabaseName=<database>
Required File : msjdbc.jar  Download


Microsoft SQL Server 2005
Driver Class : com.microsoft.sqlserver.jdbc.SQLServerDriver
JDBC URL : jdbc:sqlserver://<host>[:<port1433>];databaseName=<database>
Required File : sqljdbc.jar  Download


MySQL (Connector/J)
Driver Class : com.mysql.jdbc.Driver
JDBC URL : jdbc:mysql://<host>:<port3306>/<database>
Required File : mysql-connector-java-nn-bin.jar  Download


Oracle (Thin JDBC Driver)
Driver Class : oracle.jdbc.driver.OracleDriver
JDBC URL : jdbc:oracle:thin:@<host>:<port>:<SID>
                       jdbc:oracle:thin:@<host>:<port>/<service>
                       jdbc:oracle:thin:@<TNSName>
Required File : ojdbcxx.jar  Download
Oracle JDBC FAQ


Oracle (OCI JDBC Driver)
Driver Class : oracle.jdbc.driver.OracleDriver
JDBC URL : jdbc:oracle:thin:@<host>:<port>:<SID>
                      jdbc:oracle:thin:@<host>:<port>/<service>
                      jdbc:oracle:thin:<TNSName>
Required File : ojdbcxx.jar  Download   Oracle Database Instant Client
Oracle JDBC FAQ


PostgreSQL
Driver Class : org.postgresql.Driver
JDBC URL : jdbc:postgresql://<host>:<port5432>/<database>
Required File : postgresql-nn.jdbc3.jar   Download



Sun에서 제공하는 JDBC Driver 리스트

[JAVA]PreparedStatement 객체 재사용하기..

출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

웹개발하면서 DB의 사용은 필연적이기 때문에 java.sql.PreparedStatement의 사용은 필연적이다. Statement도 있기는 하지만 PreparedStatement는 한번 사용한 SQL문이저장되기 때문에 반복해서 사용할 경우 성능이 좋기 때문에 일반적으로는 PreparedStatement를 사용한다.

1
2
3
4
5
6
StringBuffer sql = new StringBuffer(" INSERT INTO poll (col1, col2 ) VALUES (?, ?) ");

psmt = conn.prepareStatement(sql.toString());
psmt.setString(1, "test");
psmt.setString(2, "test");
psmt.executeUpdate();

일반적으로 위와 같이 사용하는데 저렇게 사용해서 결과값 받아서 결과값 리턴해주고 끝인데 상황에 따라서는 쿼리를 반복적으로 사용해야할 때가 있다. 보통은 저게 DAO에 들어있기 때문에 메서드를 여러번 실행해도 되겠지만 그렇게 하면 Connection도 끝었다 다시하기 때문에 별로 효율적이지 못하다.

1
2
3
4
5
6
7
8
StringBuffer sql = new StringBuffer(" INSERT INTO poll (col1, col2 ) VALUES (?, ?) ");

for(int i = 0; i<5; i++) {
    psmt = conn.prepareStatement(sql.toString());
    psmt.setString(1, "test");
    psmt.setString(2, "test");
    psmt.executeUpdate();
}

하지만 여러번 반복해서 쿼리를 실행하기 위해서 위처럼 사용하면 안된다. 보기에는 크게 문제가 없고 일반적인 객체의 경우는 저렇게 재할당해서 사용해도 되기는 하지만 PreparedStatement는 다르다. 저렇게한 다음에 psmt.close();를 실행하면 아래쪽 맨 마지막psmt만 닫히고 앞의 psmt들은 닫히지 않는다. 정확히 말하면  앞에서 할당한 psmt는 결코 닫을수 없는 형태가 되어버린다.

1
2
3
4
5
6
7
8
9
StringBuffer sql = new StringBuffer(" INSERT INTO poll (col1, col2 ) VALUES (?, ?) ");
psmt = conn.prepareStatement(sql.toString());

for(int i = 0; i<5; i++) {
    psmt.setString(1, "test");
    psmt.setString(2, "test");
    psmt.executeUpdate();
    psmt.clearParameters();
}

이걸 위처럼 사용하고 있다. PreparedStatement의 객체를 새로할당하는게 아니라 파라미터를 할당하고 실행한 다음에 psmt.clearParameters();로 파라미터를 클리어해버린다. 루프돌면서 다시 할당하고 이렇게 돌리면 PreparedStatement객체를 여러번 사용할 수 있다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
StringBuffer sql = new StringBuffer(" INSERT INTO poll (col1, col2 ) VALUES (?, ?) ");
psmt = conn.prepareStatement(sql.toString());

for(int i = 0; i<5; i++) {
    psmt.setString(1, "test");
    psmt.setString(2, "test");
    psmt.addBatch();
    psmt.clearParameters();
}
psmt.executeBatch();

이걸 좀 더 세련(?)되게 하면 위와같이 사용할 수 있다. JDBC의 배치기능을 사용한 것이다. PreparedStatement의 배치기능은 다수의 쿼리를 한꺼번에 실행하기 위한 기능이다. 여러번에 나누어서 실행하지않고 배치기능을 이용해서 한번에 송신해서 실행함으로써 퍼포먼스를 높일 수 있다.

addBatch()는 쿼리와 파라미터 셋을 배치에 추가하고 이렇게 추가된 배치를 executeBatch()를 통해서 한꺼번에 실행한다. 정확한 테스트까지는 못해봤지만 이렇게 사용하는 배치 메모리의 크기 제한이 있기 때문에 너무 많은 배치를 추가할 경우에는 out of memory가 발생할 수 있기 때문에 많은 addBatch()를 실행해야 할 경우에는 중간중간 executeBatch()를 실행해 주어야 한다.

2016년 2월 23일 화요일

[DB]이클립스에서 DBEdit로 Access(.mdb) 연결하기..

출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

자바에서 DB를 Access(.mdb)로 작업을 하다보니까 쿼리브라우저 같은게 필요했다. 오라클작업할 때는 오렌지나 토드등이 있고 MS-SQL쓰면 쿼리브라우저가 제공이 되는데 Access를 쓰니까 쿼리테스트를 할 수가 없었다.(내가 못찾는건지 모르겠지만.... ㅡ..ㅡ) 자바 소스에서 쿼리를 날릴수는 있지만 간단한 쿼리는 그냥 작성한다고 하지만 좀 복잡해 지면 쿼리가 맞는지 테스트 해볼 수 있어야 한다.

그래서 이클립스에서 디비를 연결하는 플러그인중하나인 DBEdit를 사용했다. 어차피 JDBC로 연결하기 때문에 꼭 DBEdit가 아니어도 상관이 없을 듯 하다. DBEdit는 이 곳에서 다운받을 수 있다.(설치에 관해서는 굳이 언급하지 않겠다.)

DBEdit

DBEdit 퍼스펙티브에서 MDB를 연결하기 위해서 새로운 Connection을 만든다.

저번에 포스팅한 JDBC-ODBC를 이용한 MDB연결에서는 ODBC등록을 통해서 JDBC연결을 했는데 여기서는 JDBC 드라이버 파일을 등록하지 않으면 연결 할 수가 없었다. 그래서 여기서는 ODBC를 사용하지 않고 일반적인 JDBC연결을 사용한다. 여기서 사용한 것은 StelsMDB 1.0이다. StelsMDB는 MDB용 JDBC드라이버인데 상용드라이버이다.



그래서 구입하지 않은  Trial버전에서는 한번에 50건의 쿼리만 실행할 수 있고 Select쿼리는 첫 1,000건만 가져올 수 있는데 쿼리테스트 용도로는 크게 문제가 안된다고 생각한다.

StelsMDB 파일

압축을 풀면 위와같은 폴더가 나오는데 examples는 StelsMBC를 사용하는 예제 코드가 들어 있고 mdbdriver에 JDBC용 jar파일들이 들어 있다. 여기서는이 jar파일들을 사용할 것이다.

DBEdit Connection Configuration : Common

 다시 DBEdit의 커넥션 생성으로 돌아와서 JDBC연결을 위한 설정을 해준다.

JDBC Driver - jstels.jdbc.mdb.MDBDriver
Server URL - jdbc:jstels:mdb:D:\test\Database.mdb
User와 Password의 경우는 필요한 대로 입력해 주면 된다.

DBEdit Connection Configuration : Classpath

Classpath탭으로 가서 Add Archive로 아래의 StelsMDB가 제공하는 jar파일 4개를 추가해 준다.

StelsMDB JDBC jar

이렇게 추가하고 연결을 하면 MDB연결이 완료된다.

연결된 MDB

Tables 뷰에서 디비구조를 볼수가 있고 해당 테이블을 더블클릭하면 테이블의 내용을 볼 수 있다.

Instant SQL 테스트

그리고 우리가 바라는 쿼리테스트는 Instant SQL에서 쿼리를 테스트해볼 수 있고 결과값도 잘 나온다.


덧) Trial버전은 한번에 50건을 다 사용하면 이클립스 한번 껐다 켜주면 된다.
그리고 확실히 프로젝트에서 사용할때는(실제로는 ODBC쓰고 쿼리테스트만 stels mdb를 사용하니까 드라이버가 달라서 복잡한 코드에서는 따로 돌아간다. 서로 지원하는 양식이 좀 달라서..... 그리고 확실히 지원안되는게 많다. 쿼리가 좀 복잡해 지면 stels mdb에서 지원안된다고 나오는것들이 있다. 분명히 맞는데 안된단다. 이런점 참고하고 써야지 싶다.

[DB]JDBC와 ODBC로 Java에서 MS Access(.mdb) 연결하기..

출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

MDB를 별로 좋아하지는 않지만 상황에 따라서 솔직히 편할때가 있는 것도 사실이고 뭐 MDB로 해야된다는데 어쩌랴... Java에서 MDB를 연결해야 하는데 알아보니 여러가지가 방법이 있었는데 여기서는 윈도우즈에서 제공하는 ODBC를 이용해서 연결하는 방법을 사용한다.(윈도우즈서버에서만 가능하다는 얘기다.)

[제어판] - [관리도구] - [데이터 원본 (ODBC)]에 들어간다.

사용자 삽입 이미지

"시스템 DSN"탬에서 새로운 ODBC를 연결하기 위해서 추가버튼을 누른다.

사용자 삽입 이미지

여기서는 MDB를 사용할 것이므로 mdb용 드라이벌를 선택한다.

사용자 삽입 이미지

데이터 원본 이름에 원하는 디비의 이름을 넣고 이제 MDB를 연결하기 위해서 데이터베이스의 "선택"을 눌러서 들어간다.

사용자 삽입 이미지

사용할 MDB를 찾아서 지정하고 이제 다시 ODBC Microsoft Access 설정화면에서 "고급"을 눌러서 들어간다.

사용자 삽입 이미지

사용할 로그인 아이디와 비밀번호를 지정한다. 이렇게 하면 ODBC의 등록과정이 완료된다.


이제 자바쪽에서 JDBC를 이용해서 연결할 차례다.
Java

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class DBConnection {    
    private String DB_URL = "Jdbc:Odbc:Testdata";
    private String DB_USER = "test";
    private String DB_PASSWORD= "test";
    
    Connection conn = null;
    
    public Connection getConnection() {
        try {
            Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
        } catch (ClassNotFoundException e1) {
            e1.printStackTrace();
        }
        
        try {
            Properties props = new Properties();
            props.put("charSet", "8859_1" ); 
            props.put("user", DB_USER);
            props.put("password", DB_PASSWORD);

            conn = DriverManager.getConnection(DB_URL, props);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }
}

여기서는 연결을 위한 설명이라서 연결부분의 코드만 가져왔다. disconnect를 예제소스에서 뺐다는 말이다.

jdbc 연결 URL로는 "Jdbc:Odbc:데이터원본이름"을 사용하고 username과 password는 ODBC등록할 때 사용한 것을 적는다.(근데 없어도 잘 돌아가는것 같다.. ㅡ..ㅡ) 드라이버 클래스는 "sun.jdbc.odbc.JdbcOdbcDriver"를 사용한다.

그리고 여기서는 Properties를 사용하였는데 이건 mdb의 한글문제때문이다. mdb는 한글을 8859_1로 사용하기 때문에 한글을 안깨지고 받아오기 위해서는 Connection을 연결할 때 charSet을 8859_1로 설정해 주어야 한다.
Java

DBConnection dbConn = new DBConnection();
Connection conn = null; 
PreparedStatement psmt = null;
ResultSet rs = null;
    
try{        
    String sql = " SELECT username FROM member ";
    conn = dbConn.getConnection();  
    psmt = conn.prepareStatement(sql);        
    
    rs = psmt.executeQuery();
            
    while (rs.next()) {
        String username = new String(rs.getString("username").getBytes("8859_1"), "euc-kr");
    }  
}catch(Exception e){
    e.printStackTrace();
}finally{
    dbConn.disConnection(rs,psmt, conn);
}

이제 Connection을 사용해서 Select 쿼리를 날리는 부분이다. import등의 필요없는 부분은 빼버렸다. 결과적으로는 14번째 라인만 보면 된다. 영문이나 숫자는 괜찮지만 한글의 경우 디비커넥션에서 사용할 때 Access에서 한글을 8859_1(ISO-8859-1)을 사용했기 때문에 여기서도 받아온 스트링을 14번 라인처럼 엔코딩을 변경해 주어야 받아온 한글이 깨지지 않고 표시된다.
(특이하게도 문서의 encoding이 euc-kr이든 utf-8이든 동일하게 위처럼 euc-kr로 인코딩을 해주어야 한글이 깨지지 않는다.)

INSERT나 UPDATE등 자바쪽에서 mdb로 한글을 넣을때도 동일하게 인코딩 타입을 변경해서 넣어주어야 한다. 당연히 인코딩은 반대로 해서...

String str = new String("한글".getBytes("euc-kr"), "ISO-8859-1");

위처럼 하면 된다. ( ISO-8859-1와 8859_1는 동인한 인코딩이다.)

디비를 mdb를 쓰면 한글을 넣고 빼는 일은 빈번할테니까 위 2가지를 메서드로 따로 만들어두면 편할것이다. (뭐 일일이 인코딩을 바꿔주면서 넣는걸 편하다고 할 수는 없지만... ㅡ..ㅡ)

2016년 2월 12일 금요일

[JAVA]Weblogic에서 DataSource로 디비(DB) 연결하기..

출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

JDBC를 이용해서 웹로직에서 제공하는 Data Source를 이용해서 DB 접속을 해보자. 웹로직에서 Data Source 설정을 해주어야 하는데 이건 웹로직 설치가이드 문서를 참고하기 바란다. 뒷부분에 보면 커넥션풀과 데이터소스를 설정하는 부분에 대해서 나와있다.

사용자 삽입 이미지
왼쪽 그림과 같이 설정이 되어야 한다.

먼저 Connection Pools를 만들고 만들어진 Connection Pools(여기선 ibisPool)을 이용해서 Data Sources를 만든다.

그리고 Java쪽에서는 만들어진 Data Sources(여기서는 ibisDS)를 사용하여 DB에 연결한다.





디비 연결 클래스를 보자.

Java

// Weblogic DBConnection Class
import java.sql.Connection;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

import java.sql.ResultSet;
import java.sql.PreparedStatement;
import java.sql.Statement;

public class DBConnection {

    public DBConnection() {
    }

    public Connection getConnection() throws Exception {
        return getConnection("jdbc/ibisDS");
    }

    public Connection getConnection(String dsName) throws Exception {
        Context ctx = null;
        DataSource ds = null;
        try {
            ctx = new InitialContext();
            ds = (DataSource) ctx.lookup(dsName);
        } catch (NamingException e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                if (ctx != null)
                    ctx.close();
            } catch (NamingException ne) {
                System.err.println("comm.util.conPoool : can't close context resource !!");
            }
        }
        return (Connection) ds.getConnection();
    }

    public void disConnection(ResultSet rs, PreparedStatement pstmt, Statement cstmt, Connection conn) {
        try { if(rs   != null) rs.close()   ; rs    = null; } catch(Exception e) { rs    = null;}
        try { if(pstmt!= null) pstmt.close(); pstmt = null; } catch(Exception e) { pstmt = null;}
        try { if(cstmt!= null) cstmt.close(); cstmt = null; } catch(Exception e) { cstmt = null;}
        try { if(conn != null) conn.close() ; conn  = null; } catch(Exception e) { conn  = null;}
    }
}

기존의 JDBC 연결 클래스와 거의 동일하기 때문에 JDBC로 연결을 해본 사람이라면 웹로직에서도 크게 무리가 없으리라고 본다. (내가 그랬기 때문에.....)

일단 생성자인 DBConnection()에서는 아무것도 안하고 있고 오버로딩된 메서드 getConnection()이 2개 정의 되어 있다. 데이터소스 이름을 던져주면 2번째것이 실행되어서 데이터소스에서 해당이름으로 된것이 있는지 검색해서 커넥션을 리턴하고 파라미터를 던지지 않으면 기본으로 설정된 ibisDS(위에서 만든 이름이다. 당연히 각자 웹로직에서 사용한 이름으로...)의 커넥션을 리턴해준다. disConnection메서드에서는 사용한 디비자원을 닫아준다.

2016년 2월 4일 목요일

[JAVA]JDBC 사용 예제..

출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

이클립스에서 작성하였기 때문에 package 사용하였슴.
각자환경에서 사용하려면 com.ksic.util에서 StudentsUtil의 DB부분을 설정에 맞게 수정해 주어야 함.

오라클 9i와 연동하여 작성한 것임.

DB테이블 명은 hkstudents이고 컬럼은 모두 varchar2타입으로 id, name, addr 3개가 있음.

테이블에 대한 DTO가 따로 하나 있고... DAO는 연결과 클로즈부분만 추상클래스로 따로 구성됨.

나름 주석 열심히 달아서 보면 이해가 될지도..



JDBC에 대한 설명
자바에서 오라클을 연결해서 사용할때 총 6단계의 과정을 거친다.

1. 드라이버 로딩
Class.forName("oracle.jdbc.driver.OracleDriver");

Class.forName으로 JDBC 드라이버를 로딩해 준다. 이 부분은 ClassNotFoundException 핸들링을 해주어야 하므로 try-catch문으로 감싸주어야 한다.

2. DB 연결
Connection conn = null;
conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:ORA92", "scott", "tiger");


Connection을 사용하기 위해서는 java.sql.Connection 을 임포트 해주어야 한다. conn이라는 커넥션을 만든 후에 DriverManager의 getConnection을 통해서 DB를 연결해 준다. DriverManager를 사용하려면 java.sql.DriverManager를 import해주어야 한다.

첫 매계변수는 DB연결쪽이다. jdbc:oracle:thin:@부분은 동일하게 사용하고 localhost는 위치를 말한다. 다른PC이거나 할 경우는 IP를 입력해 주고 1521은 해당포트이며 ORA92는 오라클의 SID이다. 자신에 맞게 입력해준다.
두번째와 세번째는 오라클의 아이디와 패스워드이다.

이 연결부는 SQLException예외처리로 Try-Catch로 감싸 주어야 한다.

3. 쿼리 준비PreparedStatement psmt = null;
psmt = conn.prepareStatement("DB쿼리문");
혹은
Statement stmt = null;
stmt = conn.createStatement("DB쿼리문");


를 사용한다. 각각 java.sql.PreparedStatement이나 java.sql.Statement을 임포트 해주어야 한다. Statement가 PreparedStatement보다 상위클래스이다.

실행하기 전에 DB쿼리를 준비하는 단계인데 둘의 차이는 PreparedStatement는 쿼리문에 ?를 사용해서 추가로 ?에 변수를 할당해 줄수 있다는 것이다.
psmt = conn.prepareStatement("select * from TableName where id=?");
psmt.setString(1, id);
와 같이 사용이 가능하다. setString(혹은 setInt 등등)을 이용해서 1번째 ?위치에 id를 할당할 수 있어서 유연성있게 쿼리준비를 할 수 있다.

4. 쿼리 실행
psmt.executeUpdate();
로 쿼리를 실행한다. insert, update, delete 등 값을 받아 오지 않는 쿼리문은 executeUpdate를 이용해서 실행해 준다.

5. 데이터 가져오기
select등 쿼리실행후에 값을 가져와야 하는 쿼리문의 경우는
ResultSet rs = null;
rs = psmt.executeQuery();


java.sql.ResultSet을 이용해서 쿼리실행 한 것을 ResultSet으로 받아들인다. 이때 쿼리실행은 executeQuery()를 사용한다.

그 후에

while(rs.next()) {
    int i=1;
    String id = rs.getString(i++);
    String name = rs.getString(i++);
    String addr = rs.getString(i++);
   }


rs.next()를 이용해서 한 줄씩 읽어들인다. 처음에는 시작위치에 있고 rs.next()를 실행하면 받아온 데이터의 첫줄 위치로 이동하게 된다. while문 혹은 if문을 통해서 rs.next()가 null인지를 확인하면서 반복문을 돌린다.
rs.get을 이용해서 값을 가져와 변수 혹은 dto객체등에 저장해서 사용한다. DB에 여러줄을 가져올때는 dto객체로 받아서 컬렉션 계열로 저정해야 한다.

6. 커넥션 닫기
if (rs!=null) {
   try {
      rs.close();
   } catch (SQLException e) {
   }
}
 
if(stmt!=null) {
   try {
      stmt.close();
   } catch (SQLException e) {
   }
}
 
if(conn!=null) {
   try {
      conn.close();
   } catch (SQLException e) {
   }
}

닫을때는 Open한 반대의 순서로 닫아주어야 한다. Statement가 PreparedStatement 보다 상위클래스이기 때문에 닫는 메서드를 만들때 Statement로 받을어도 PreparedStatement을 정상적으로 받을 수 있다. 닫을때는 습관적으로 null이 아닌지를 확인한 뒤에 받도록 만들면 생각지 못한 오류를 막을 수 있다.


이것이 JDBC의 기본적인 구조인데 실제적으로 사용할때는 제공되는 커넥션풀(ConnectionPool)등을 사용해서 이미 만들어진 커넥션을 사용하기 때문에 1번 과정은 pass하고 2번과정부터 커넥션풀에서 커넥션을 얻어오는 방식으로 사용하게 되는 경우가 많다.

My Comment..
수업을 들을 때는.. 대체 저게 먼 소리지 했는데.. 그래서 수포자처럼..
포기를 했었다.. 지금은 글을 보면서.. 아.. 하게 되었다..
커넥션 닫는 순서도 첨에는 몰랐는데.. 아니 몰랐다라기보단.. 그냥 막연하게..
되어있는 것을 가져다 쓰기만 했으니.. 크게 신경 자체를 안쓴듯..
BCCard 에서 유지보수 하면서 어느 순간 순서를 잘못 닫아서 쓴적이 있는데..
그 때 순서에 대한 중요성을 깨닫게 된듯.. 2012년이던가.. 너무 늦게 깨달았다..