본문 바로가기
스프링

[Spring] JDBC가 무엇이고, 어떻게 활용될까? 장점과 단점은?

by itstime0809 2023. 8. 24.
728x90

JDBC

 JDBC는 Java DataBase Connectivity의 약자다. JDBC는 자바에서 데이터베이스와의 연결을 도와주는 브릿지 역할을 하게 되는데 JDBC Driver를 통해서 구현되어 JDBC API를 개발자에게 제공해 주는 형태라고 할 수 있다. JDBC Driver라 함은 JDBC API를 구현하기 위해 여러 메서드들과 정보들을 담고 있는데 각각의 DB회사에서 제공해 주는 DB들과 연동하기 위해 그에 맞는 Driver를 가지고 있어 이러한 드라이버를 통해서 DB와 연결하고 SQL쿼리문을 보낼 수 있게 되는 것이다. 따라서 자바와 DB와의 연결을 가운데에서 Controller처럼 도와주는 역할을 하게 되는 것이다.

 

DataSource

 이렇게 DB와 연결을 하기 위해서는 어떻게 해볼 수 있을까? API형태로 제공해주는 이유는 DB와의 연결을 보다 쉽게 해 주기 위해서 제공해주고 있는 것 같은데 그럼 API를 어떻게 사용하는지 알아보아야 할 것이다. 우선 JDBC API를 사용하려면 DataSource 개념을 알아야한다. DataSource라는 것은 DB와의 연결된 객체를 얻는다고 표현하는 게 와닿는다. 왜냐하면 아마 application.properties에서 jdbc 드라이버와 관련된 속성들을 정의하고, dao에서 구현하게 되는 형식인데 이때 이러한 속성들을 정의했을 때 DB와 연결할 정보들을 정의하는 것을 볼 수 있다. 그렇다는 것은 DB와 연결하기 위해서 내가 접속할 DB의 정보들을 알고 있을 객체가 필요하다. 따라서 해당 객체를 통해서 즉 연결된 객체를 통해서 작업을 수행하게 된다.

 

 그렇다면 이러한 DataSource는 어떻게 활용할 수 있는가? DataSoruce에 대해서 찾아보니 DB와 연결할 객체를 만드는 작업은 DriverManager로 바로 연결을 하는 방법도 가능하다. 그게 아래와 같다.

 Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/myDb", "user1", "pass")

 위에 코드를 살펴보면 Connection 객체를 얻기 위해서 DriverManager로부터 DB와 관련된 연결객체를 얻기 위함임을 알 수 있다. 하지만 선호 되어지는 방법은 DataSource를 DI로 주입받아서 해당 DataSource로부터 Connection을 얻게 되는 방법을 더 선호한다고 한다. 코드를 보고 앞서 기술했던 내용을 보충할 수 있는데 DB와의 연결되어진 객체라는 것은 코드에서 보는 것과 같이 어디로 접속을 하는지 public ip 주소를 파라미터로 넘겨주며 db에 접근할 계정과 비밀번호를 넣어 Connection 객체를 얻게 된다. 이제 해당 객체를 가지고 쿼리문을 수행할 준비를 하게 된다.

 

PrepareStatement

 PrePareStatement는 이름 그대로 해석해 보면 준비상태 정도로 볼 수 있을 것 같다. 준비상태가 무슨 의미를 가질 수 있을까? 이를 알기 위해서 보안 쪽에 관심을 두어야 했다. 우선 SQL이라고 하면 관계형 데이터베이스의 데이터들을 조작하기 위한 언어인데. 이러한 언어는 생각보다 취약점이 많이 존재했고, 그로 인해 해커들이 해킹을 위해 이용되는 수단으로 이용되었다. 본래 SQL을 사용해서 DB의 데이터를 조작하는 목적이 해킹을 위한 목적이 아니지만! 악의적인 방법을 통해서 DB의 권한을 탈취한다던지 관리자 계정을 통해 피해를 준다던지가 가능했다. 물론 지금도 이러한 보안과 관련된 처리를 해주지 않는다면 보안성이 매우 낮아짐은 분명해 보인다. 그럼 어떻게 DB에 피해를 줄 수 있는가? 에 대한 질문으로 이어져가야 될 것 같다. 필자가 찾아본 방향은 SQL injection에 대한 부분이다. SQL injection이라고 하면 비정상적인 방법을 통해서 혹은 쿼리문을 통해 혹은 URL 파라미터를 조작하여 DB의 정보를 탈취하는 방식이다. 보아하니 이는 SQL의 문법적인 요소들을 가지고 즉 논리적인 오류를 침투해서 DB의 정보를 탈취하게 된다. 만약 서버에서 처리하게 되는 SQL문이 직접 사용자에 의해 입력되는 방식이라면 쿼리문을 사용자 중심적으로 돌아가게 되게 되고 악의적인 논리적 오류를 통해 접근하게 되면 해당 쿼리문이 그대로 실행이 되기 때문에 보안에 매우 취약해지게 된다. 그래서 여러 SQL injection과 관련된 부분들을 방지하기 위해서 입력값에 대한 검증을 하거나 PrePareStatement를 사용하게 된다. 또한 에러 메세지를 노출시키지 않으며, 하드웨어적인 방법과 소프트웨어적인 방법을 통해서도 방지하게 된다.

 

 그래서 위와 같은 보안성을 높이기 위해서 PrePareStatement를 사용하게 된 것이다. 이러한 PrePareStatement를 사용했을 때의 장점이라고 하면 pre-compiled statement라고 하여 미리 사전에 DBMS가 SQL쿼리문을 컴파일하게 된다. 이렇게 컴파일하게 되는 SQL쿼리문은 question mark -> (?) 물음표에 값을 매핑시켜 동작하게 해 준다. 매개변수 바인딩, 매개변수 맵핑 여러 용어들이 있는 것 같지만 쉽게 매핑시킨다고 하고 넘어가 보자. 그러면 이러한 매핑된 값들을 index, value 파라미터를 가지고 있는 메서드를 제공해 주기 때문에 해당 메서드를 사용하여 값을 매핑시킨다. 이렇게 하게 되면 사용자가 입력한 값들은 공격키워드를 사용하여 SQL을 사용한다고 하더라도 입력값을 이스케이프 처리하기 때문에 injection을 막기 위한 방법으로 사용된다. 직접 쿼리문을 작성했을 때는 해당 쿼리문 형태로 바로 입력값으로 들어가게 되면 별도의 이스케이프 처리 없이 실행되게 되니 PrePareStatement를 사용하지 않았을 때 보다 보안성이 낮이 짐을 알 수 있다. 이스케이프 처리란 간단하게 작성하면  데이터 형식에서 특별한 의미를 가지지 않게 하기 위해서 문자나 기호 앞에 특수한 문자를 넣어서 특별한 의미를 갖지 않도록 처리하게 하는 것이다. 따라서 공격쿼리를 사용하여 SQL injection을 수행한다고 하더라도 이스케이프 처리 되기 때문에 특별한 값을 갖지 않게 된다. 예를 들면 아래와 같은 처리를 의미한다.

 

 

' OR '1'='1

\' OR \'1\'=\'1

 

 

 위와 같이 앞에 이스케이프 문자를 추가하여 특별한 의미를 갖지 않도록 처리 되게 하여 보안성을 향상할 수 있다. 이제 SQL에 들어갈 값까지 전부 맵핑이 되었다면 이제 SQL문을 실제고 DB에 전송하는 작업이 필요한데 이때 Statement.RETURN_GENERATED_KEYS라는 파라미터가 입력된 것을 확인할 수 있다. 이는 DataSource가 가지고 있는 상수다. 주석의 영어 문장이 해석을 해도 매끄럽게 이해가 되지 않아 저 문장 그대로 해석한다면 생성된 키를 다시 반환한다는 것을 의미한다. 그래서 SQL을 수행하고 나서 해당 ID값을 보통 DB에서 Auto Increment로 설정했다면 id가 자동으로 갱신되는 것을 알 수 있는데 그때의 키값을 반환한다는 얘기가 된다. 후에 이 키값을 얻어 처리를 하게 되는 방식으로 이용되는 것 같다.

 

 이렇게 JDBC가 무엇이고 어떤 역할을 하며 어떻게 사용되어 질 수 있는지 그리고 그 안에서 여러 관련된 사항들을 알아봤는데 장점이라고 하면 순수 JDBC를 사용함으로써 DB와의 연결을 어떻게 하는지에 대한 흐름을 짚을 수 있지만, 반대로 반복적인 코드들을 작성해야 하며 여러 이유들이 있지만 가장 불편한 점은 트랙잭션을 직접 관리해야 된다는 점이지 않을까 개인적인 생각을 해보고 있습니다. 다른 라이브러리들이 주는 이점이 이러한 것들을 해소시켜 주기 때문에 JPA, Spring JDBC 등을 활용하여 작업을 하는 게 아닐까 하는 생각도 들지만 스프링을 처음공부하는 입장에서는 JDBC가 어떻게 이용되는지 알아야 하기 때문에 순수 JDBC를 사용해 보고 SpringJDBC와 같은 형태를 이용해 보는 게 흐름을 이해하는 게 좋은 것 같습니다.