[패스트캠퍼스] 스프링의정석 정리/Spring

스프링으로 DB 연결 및 다루기 & TDD

VanillaSky7 2023. 5. 19. 00:25

스프링으로 DB 연결 및 다루기


(MySQL을 연동하여 사용) 우선 JDBC를 이용하여 DB에 연결하고 다루는 법을 살펴본다. JDBC API(MySQL Connector/J)를 이용하기 위해서는 Maven Repository을 통해 pom.xml에 추가 

 

// 스키마의 이름(springbasic)이 다른 경우 알맞게 변경해야 함
String DB_URL = "jdbc:mysql://localhost:3306/springbasic?useUnicode=true&characterEncoding=utf8";

// DB의 userid와 pwd를 알맞게 변경해야 함
String DB_USER = "asdf";
String DB_PASSWORD = "1234";

Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD); // 데이터베이스의 연결을 얻는다.
Statement stmt  = conn.createStatement(); // Statement를 생성한다.

String query = "SELECT now()"; // 시스템의 현재 날짜시간을 출력하는 쿼리(query)
ResultSet rs = stmt.executeQuery(query); // query를 실행한 결과를 rs에 담는다.

// 실행결과가 담긴 rs에서 한 줄씩 읽어서 출력
while (rs.next()) {
    String curDate = rs.getString(1);  // 읽어온 행의 첫번째 컬럼의 값을 String으로 읽어서 curDate에 저장
    System.out.println(curDate);       // 2022-01-11 13:53:00.0
}

 

DB_URL은 DB에 접속하기 위한 URL을 의미하고, DB_USER와 DB_PASSWORD는 DB에 접속하기 위한 계정을 의미한다.

 

DriverManager.getConnection()을 이용하여 DB에 대한 연결을 얻는다. 이후 연결된 DB에 SQL 명령을 내리기 위해서는 Statement가 필요하다. 위 코드와 같이 쿼리에 값을 담고 이를 실행하 결과를 ResultSet에 담는다. ResultSet은 테이블 형태로 데이터를 가져온다. 최종적으로 rs.next()를 통해 한 줄씩 읽어서 처리하고, .getString()을 통해 해당 칼럼을 읽어온다(String).

 

//        // 스키마의 이름(springbasic)이 다른 경우 알맞게 변경
//        String DB_URL = "jdbc:mysql://localhost:3306/springbasic?useUnicode=true&characterEncoding=utf8";
//
//        // DB의 userid와 pwd를 알맞게 변경
//        String DB_USER = "asdf";
//        String DB_PASSWORD = "1234";
//        String DB_DRIVER = "com.mysql.jdbc.Driver";
//
//        DriverManagerDataSource ds = new DriverManagerDataSource();
//        ds.setDriverClassName(DB_DRIVER);
//        ds.setUrl(DB_URL);
//        ds.setUsername(DB_USER);
//        ds.setPassword(DB_PASSWORD);

    ApplicationContext ac = new GenericXmlApplicationContext("file:src/main/webapp/WEB-INF/spring/**/root-context.xml");
    DataSource ds = ac.getBean(DataSource.class);

    Connection conn = ds.getConnection(); // 데이터베이스의 연결을 얻는다.

        System.out.println("conn = " + conn);
    assertTrue(conn!=null);

 

위 코드와 같이 Spring JDBC(Maven Repository를 통해 pom.xml에 추가)를 통해 주석 처리 된 부분과 같이 직접 객체를 생성해주는 방식을 이용할 수도 있고, root-context.xml에 클래스에 대한 정보를 bean으로 등록해놓고 getBean()으로 얻어오는 방식이 있다.

 

 

 

다음으로 이제 스프링을 통해 DB를 다루어 본다.

Connection conn = ds.getConnection();

//        insert into user_info (id, pwd, name, email, birth, sns, reg_date)
//        values ('asdf22', '1234', 'smith', 'aaa@aaa.com', '2022-01-01', 'facebook', now());

    String sql = "insert into user_info values (?, ?, ?, ?,?,?, now()) ";

    PreparedStatement pstmt = conn.prepareStatement(sql); // SQL Injection공격, 성능향상
        pstmt.setString(1, user.getId());
        pstmt.setString(2, user.getPwd());
        pstmt.setString(3, user.getName());
        pstmt.setString(4, user.getEmail());
        pstmt.setDate(5, new java.sql.Date(user.getBirth().getTime()));
        pstmt.setString(6, user.getSns());

    int rowCnt = pstmt.executeUpdate(); //  insert, delete, update

        return rowCnt;

 

Connection conn = ds.getConnection()을 통해 DB 연결을 얻는다. 이후 DB에 보내기 위한 객체 PreparedStatement를 통해 SQL문을 저장한다. 이때 values() 값 내에 있는 ?를 값 대신 넣어주었음을 볼 수 있는데, 이는 " + user.getId() + " 와 같이 작성하게 되면 따옴표 실수를 하기도 쉽고, 코드가 길어지고 복잡해질 수 있다. 따라서 pstmt.setString과 같이 setter를 이용하여 ?에 들어갈 값들을 채워준다.

 

다음으로 pstmt.executeUpdate()를 통해 쿼리를 실행해준다. 이때 executeUpdate()는 int 타입인데, 이는 SQL문을 실행한 결과 몇 개의 행이 영향을 받았는 지에 대한 결과를 의미하기 때문이다. 위 예제 코드에서 사용한 INSERT문의 경우 대부분 한 문장에 영향을 미치므로 1이 반환되고, 실패 시 0을 반환하게 되는 것이다.

 

마지막으로 close()를 해주어야 메모리 누수를 방지할 수 있고, close(rs, pstmt, conn)와 같이 간단히 사용할 수도 있다. ResultSet(rs), PreparedStatemet(pstmt), Connection(conn) 순으로 즉 실행 된 역순으로 종료해준다. (JDK 7부터 try-with-resources로 간단히 처리 가능)

 

※ executeUpdate()는 INSERT문, UPDATE문, DELETE문에만 사용이 가능하고, SELECT문의 경우에는 executeQuery()를 사용한다. 

 

PreparedStatement문은 기존 Statement문과 비교했을 때, SQL Injection 공격 방지, 성능 향상이라는 장점을 가지는데, 여기서 성능 향상이라는 것은 SQL문을 여러번 재사용할 수 있으므로 Statement문에 비해 실행 시간이 더 빠르고, 캐싱 효과가 있기 때문이다.

 

 

 

 

 

TDD(Test Driven Development)


JUnit(Test Framework)을 이용하면 테스트를 자동화 할 수 있는데, 이를 이용하여 TDD(Test Driven Development) 즉 테스트 주도 개발을 할 수 있다. (우클릭 → go to → Test → Create New Test)

 

@Test 어노테이션은 해당 메서드를 테스트할 것이라는 의미를 가진다. 테스트가 성공했는지 assert문을 이용하여 확인한다. 예를 들어, assert문 중 하나인 assertTrue()의 경우 괄호 안의 조건식이 true면 테스트가 성공했음을 의미한다. 모든 경우에 대해 테스트를 통과하도록 작성해주는 것에 유의한다. 예를 들어, INSERT문을 테스트할 때 duplicated 되지 않도록 DELETE문을 적절히 사용해주는 등 모든 경우에 대비한다.

 

다음으로 테스트 할 클래스에 @RunWith(SpringJUnit4ClassRunner.class)를 붙여주고, Spring Test 관련 모듈이 없을 경우 Spring TestContext Framework를 Maven Repository에서 가져온다.

@ContextConfiguration(locations={"file~생략/root-context.xml"})을 통해 xml 설정 파일 위치를 지정해준다.

 

 

※ JUnit의 각 테스트는 메서드 하나하나를 별도의 객체에서 실행한다. 따라서 @Autowired로 주입 된 인스턴스 변수여도 같은 클래스에 있는 테스트 메서드들이 해당 데이터 소스를 공유하지 않는다.