MySQL Connector는 JDBC의 구현체였다

Bellmin·2025년 8월 6일
post-thumbnail

SPI 개요

갑자기 SPI가 뭐지? 싶을 수도 있다.

MySQL Connector는 JDBC의 구현체라는 것은 당연한 사실이다.

이 내용은 바로 아래에서 나올 SPI와 관련이 있다.

SPI란?

Service Provider Interface

자바 플랫폼에서 서비스 제공자 (구현체)를 발견하고 로드하는 메커니즘이다.

사례

예를 들어, 자바에서 범용적으로 사용하는 로깅 라이브러리인 Log4j 를 예시로 들 수 있다.

Log4j는 2021년에 취약점이 발견되었는데, 이는 자바 생태계에 어마어마한 영향력을 끼쳤다.
(실제로 군생활 시절, Log4j 덕분에 보안 점검을 수행한 경험이 있다.)

거의 모든 자바 생태계가, log4j 를 사용하고 있었기 때문인데, 이 취약점으로 인해 기존에 작성된 코드를 다른 로깅 라이브러리로 바꾸는 것이 쉽지 않았을 것이다.

이때 등장한 것이 바로 Slf4J 라는 새로운 로깅 라이브러리인데, 결론만 말하자면 자바 진영에서 로깅 라이브러리의 표준 인터페이스라고 할 수 있다.

Log4j를 전부 다른 라이브러리로 교체하는 것은 쉬운 일이 아니므로, 표준 인터페이스를 정해놓고 구현체만 바꿔 사용하자는 논리이다.

이때 Slf4j 가 바로, 로깅 서비스를 제공해주는 표준 인터페이스 라고 할 수 있다.

SPI 장점

스프링에서도 인터페이스를 정의해놓고, 구현체를 바꿔 사용하는 것처럼,

SPI도 비슷하다. 하나의 서비스에 대한 인터페이스를 명시해놓고, 서비스의 구현체를 바꾸어서 사용하는 것이다.

(물론, 스프링은 빈 주입, SPI는 서비스 로더라는 개념을 사용하서 동작 원리가 다르긴 하다.)

라이브러리를 사용할 때, 표준 인터페이스가 정해져 있으니, 코드를 일일이 수정하지 않아도 설정 값들만 변경해주어도 쉽게 변경할 수 있다.

API vs SPI

SPI 라는 개념을 알고 난 후 API와 헷갈리기 시작했다.

API (Application Programming Interface) : API는 라이브러리가 사용자에게 제공하는 인터페이스로, 사용자가 라이브러리를 어떻게 사용할 수 있는지 정의하는 것

API가 위와 같다면, SPI는 약간 느낌이 다르다.

SPI (Service Provider Interface) : 라이브러리가 구현체에게 요구하는 인터페이스로, 구현체가 라이브러리와 어떻게 통합이 될 것인지를 정의하는 명세이다.

이전 예시에서는, Slf4J 와 LogBack, Log4j 를 예시로 들었지만, JDBC 와 MySQL Connector 도 예시로 들 수 있다.

자바에서 데이터베이스와 어떻게 연결할 것인지를 인터페이스로 정의해놓고, 각 데이터베이스 제조사 별로 이를 구현한 것이다.

JDBC(SPI) ↔ MySQL Connector (구현체)

어떻게 구현하지?

SPI를 구현하기 위해서는, 4가지 종류의 파일이 필요하다.

0. 준비물

  • 서비스 인터페이스
    • 제공할 서비스를 명세한 Java 인터페이스
  • 서비스 제공자
    • 인터페이스를 구현할 클래스
  • 서비스 로더
    • java.util.ServiceLoader (서비스 구현체를 찾아주는 클래스)
  • 설정 파일
    • META-INF/services/ 어떤 서비스 인터페이스를 구현했는지에 대한 정보와, 구현 클래스의 위치 정보

여기서, SPI를 구현한다는 것은, 라이브러리의 표준 인터페이스가 존재할 경우, 이 인터페이스를 구현한 라이브러리 구현체를 찾아서 자동으로 사용하게 해주는 기능을 구현한다는 것이다.

1.예시

로깅 라이브러리에서

  • Logger → 로깅을 수행하는 애 → Service 역할을 하는 인터페이스
  • LoggingServiceProvider → 로깅 라이브러리의 인터페이스를 구현한 구현체를 찾아서 로드하는 Provider → ServiceProvider 인터페이스
// 이 인터페이스는, 서비스를 구현하는 구현 라이브러리에서 implements 해야 함
public interface Logger {
	/*
		실제로 로깅을 수행하는 작업을 구현
	*/
}
// 이 인터페이스는, 서비스를 구현하는 구현 라이브러리에서 implements 해야 함
public interface LoggingServiceProvider {
	/*
		Service 구현체 찾는 로직
	*/
}

2. 구현

public class LoggerImpl implements Logger {
	@Override
	public String log(){
		// logic..
	}
}
public class LoggingServiceProviderImpl implements LogginServiceProvider {
	@Override
	public void findService(){
		// logic..
		// 내부적으로 ServiceLoader 를 사용
	}
}

3. 설정 파일 생성

이제, 인터페이스를 정의하고 구현했으면, META-INF/services 안에, Service 관련 정보를 기록한 파일을 생성하면 된다.

MariaDB의 Service Impl을 보여준 것이다.

java.sql.Driver

java.sql.Driver 가 파일 제목인 이유는, java.sql 패키지의 Driver 인터페이스를 의미한다.

즉, 자바에서 데이터베이스에 접근하기 위한 표준 드라이버를 Driver 인터페이스로 정의해놓았는데

이 Driver 인터페이스를 구현한 구현체가 바로 org.mariadb.jdbc.Driver 라는 것이다.

ServiceLoader

Service 인터페이스들의 구현체를 찾아서 직접 메모리에 로드하여 객체를 생성해주는 역할을 하는 클래스가 바로 이 ServiceLoader 라는 클래스이다.

내부를 다 보지는 않았지만, META-INF/services 디렉터리에서, 클래스 경로를 찾아 Java Reflection 으로 객체를 생성하는 것 같다.

ServiceLoader 가 내부적으로 어떻게, 클래스들을 로드하는지는 다음 포스팅에서 알아보도록 하자.


참고 자료

0개의 댓글