SQLite는 왜 C로 작성되었는가

SQLite는 왜 C로 작성되었는가 (Why Is SQLite Coded In C)

SQLite가 왜 C 언어로 구현되었는지, 그리고 C++·Java 같은 객체 지향 언어나 Rust·Go 같은 "안전한" 언어로 작성되지 않는 이유를 설명하는 문서예요.

출처: Why Is SQLite Coded In C (sqlite.org)

본문

1. C가 최고다 (C Is Best)

참고: 이 글의 2.0절과 3.0절은 Hacker NewsReddit의 댓글에 대한 응답으로 추가됐어요.

SQLite는 2000-05-29에 시작된 이래로 일반적인(generic) C로 구현되었어요. C는 SQLite 같은 소프트웨어 라이브러리를 구현하기에 가장 좋은 언어였고 여전히 가장 좋은 언어예요. 현재 다른 프로그래밍 언어로 SQLite를 다시 코딩할 계획은 없어요.

C가 SQLite를 구현하기에 가장 좋은 언어인 이유는 다음과 같아요:

  • 성능 (Performance)
  • 호환성 (Compatibility)
  • 낮은 의존성 (Low-dependency)
  • 안정성 (Stability)

1.1. 성능 (Performance)

SQLite처럼 집중적으로 사용되는 저수준 라이브러리는 빨라야 해요. (그리고 SQLite는 빠른데, Internal Versus External BLOBs35% Faster Than The Filesystem을 참고하세요.)

C는 빠른 코드를 작성하기에 훌륭한 언어예요. C는 때때로 "휴대 가능한 어셈블리 언어(portable assembly language)"로 묘사되기도 해요. C는 플랫폼 간 이식성을 유지하면서도 기본 하드웨어에 최대한 가깝게 코딩할 수 있게 해줘요.

다른 프로그래밍 언어들은 때때로 "C만큼 빠르다"고 주장해요. 하지만 어떤 언어도 범용 프로그래밍에서 C보다 빠르다고 주장하지는 않아요. 실제로 더 빠른 언어가 없기 때문이에요.

1.2. 호환성 (Compatibility)

거의 모든 시스템이 C로 작성된 라이브러리를 호출할 수 있어요. 다른 구현 언어에는 그렇지 않아요.

예를 들어 Java로 작성된 Android 응용 프로그램은 (어댑터를 통해) SQLite를 호출할 수 있어요. 만약 SQLite가 Java로 코딩되었다면 인터페이스가 더 단순해져서 Android에게 더 편리했을 수도 있어요. 하지만 iPhone 응용 프로그램은 Objective-C나 Swift로 코딩되는데, 둘 다 Java로 작성된 라이브러리를 호출할 수 없어요. 따라서 SQLite가 Java로 작성되었다면 iPhone에서는 사용할 수 없었을 거예요.

1.3. 낮은 의존성 (Low-Dependency)

C로 작성된 라이브러리는 거대한 런타임 의존성을 가지지 않아요. 최소 구성에서 SQLite는 표준 C 라이브러리에서 다음 루틴만 필요로 해요:

  • memcmp()

  • memcpy()

  • memmove()

  • memset()

  • strcmp()

  • strlen()

  • strncmp()

더 완전한 빌드에서 SQLite는 malloc()과 free() 같은 라이브러리 루틴과 파일을 열고, 읽고, 쓰고, 닫는 운영체제 인터페이스도 사용해요. 하지만 그 경우에도 의존성의 수는 매우 적어요. 반면 다른 "현대적인" 언어들은 수천, 수만 개의 인터페이스로 가득한 수 메가바이트 크기의 런타임을 요구하는 경우가 많아요.

1.4. 안정성 (Stability)

C 언어는 오래되었고 지루해요. 잘 알려져 있고 잘 이해되는 언어예요. SQLite 같은 모듈을 개발할 때 정확히 원하는 것이 이것이에요. 구현 언어 사양이 갱신될 때마다 그 밑에서 구현 언어가 바뀌는 일 없이도, 작고 빠르고 신뢰할 수 있는 데이터베이스 엔진을 작성하는 것 자체가 이미 충분히 어려워요.

2. SQLite는 왜 객체 지향 언어로 코딩되지 않았나?

일부 프로그래머는 "객체 지향"이 아닌 언어로 SQLite 같은 복잡한 시스템을 개발하는 것을 상상하지 못해요. 그렇다면 SQLite는 왜 C++나 Java로 코딩되지 않았을까요?

  1. C++나 Java로 작성된 라이브러리는 일반적으로 같은 언어로 작성된 응용 프로그램에서만 사용할 수 있어요. Haskell이나 Java로 작성된 응용 프로그램이 C++로 작성된 라이브러리를 호출하게 하는 것은 어려워요. 반면 C로 작성된 라이브러리는 어떤 프로그래밍 언어에서도 호출할 수 있어요.

  2. 객체 지향은 설계 패턴(design pattern)이지 프로그래밍 언어가 아니에요. 어셈블리 언어를 포함해 원하는 어떤 언어로도 객체 지향 프로그래밍을 할 수 있어요. 일부 언어(예: C++나 Java)는 객체 지향을 더 쉽게 만들어주지만, C 같은 언어에서도 객체 지향 프로그래밍을 할 수 있어요.

  3. 객체 지향은 유효한 유일한 설계 패턴이 아니에요. 많은 프로그래머가 순전히 객체의 관점으로만 생각하도록 교육받았어요. 그리고 공정하게 말하면 객체는 문제를 분해하는 좋은 방법인 경우가 많아요. 하지만 객체가 유일한 방법도, 항상 문제를 분해하는 최선의 방법도 아니에요. 때로는 오래된 절차적 코드가 객체 지향 코드보다 작성하기 쉽고, 유지·이해하기 쉬우며, 더 빠를 때도 있어요.

  4. SQLite가 처음 개발될 때 Java는 젊고 미성숙한 언어였어요. C++는 더 오래되었지만 성장통을 겪고 있어서 똑같이 동작하는 C++ 컴파일러 두 개를 찾기도 어려웠어요. 따라서 SQLite가 처음 개발될 때는 C가 분명 더 나은 선택이었어요. 상황은 지금 그렇게 극명하지 않지만, 지금 시점에서 SQLite를 다시 코딩할 이점은 거의 또는 전혀 없어요.

3. SQLite는 왜 "안전한" 언어로 코딩되지 않았나?

최근에 Rust나 Go 같은 "안전한" 프로그래밍 언어에 관심이 많아졌어요. 이런 언어에서는 메모리 누수나 배열 오버런 같은 흔한 프로그래밍 오류를 만들 수 없거나 적어도 어려워요. 그래서 SQLite가 왜 "안전한" 언어로 코딩되지 않는지라는 질문이 자주 제기돼요.

  1. 안전한 프로그래밍 언어는 SQLite 존재의 처음 10년 동안 어느 것도 존재하지 않았어요. SQLite를 Go나 Rust로 다시 코딩할 수는 있지만, 그렇게 하면 고쳐질 버그보다 훨씬 많은 버그가 생길 가능성이 크고, 더 느린 코드가 될 수도 있어요.

  2. 안전한 언어는 배열 접근이 경계 안에 있는지 확인하는 등의 일을 위해 추가 기계 분기(machine branch)를 넣어요. 올바른 코드에서는 그런 분기가 절대 실행되지 않아요. 즉 기계 코드를 100% 분기 테스트할 수 없다는 뜻인데, 이는 SQLite의 품질 전략에서 중요한 구성 요소예요.

  3. 안전한 언어는 보통 OOM(메모리 부족) 상황을 만나면 중단(abort)하려 해요. SQLite는 OOM에서도 우아하게 복구하도록 설계되었어요. 현재 세대의 안전한 언어들로 이것을 어떻게 달성할 수 있을지는 불분명해요.

  4. 기존의 안전한 언어는 모두 새 언어예요. SQLite 개발자들은 안전하게 프로그래밍하기 쉬운 언어를 개발하려는 컴퓨터 언어 연구자들의 노력을 지지하고, 그런 노력이 계속되기를 권장해요. 하지만 SQLite를 구현하는 데 있어 우리 자신은 오래되고 지루한 언어에 더 관심이 있어요.

그렇긴 하지만 SQLite가 언젠가 Rust로 다시 코딩될 가능성은 있어요. Go는 assert()를 싫어하므로 Go로 다시 코딩할 가능성은 낮아요. 하지만 Rust는 가능성이 있어요. SQLite가 Rust로 다시 코딩되기 전에 충족되어야 할 몇 가지 전제 조건은 다음과 같아요:

  1. Rust는 조금 더 성숙해지고, 그렇게 빠르게 변하지 않아야 하며, 오래되고 지루한 언어 쪽으로 더 나아가야 해요.
  2. Rust는 다른 모든 프로그래밍 언어에서 호출할 수 있는 범용 라이브러리를 만드는 데 사용될 수 있음을 입증해야 해요.
  3. Rust는 운영체제가 없는 장치를 포함한 가려진 임베디드 장치에서도 동작하는 객체 코드를 만들 수 있음을 입증해야 해요.
  4. Rust는 컴파일된 바이너리의 100% 분기 커버리지 테스트를 가능하게 하는 필요한 도구를 갖춰야 해요.
  5. Rust는 OOM 오류에서 우아하게 복구하는 메커니즘이 필요해요.
  6. Rust는 C가 SQLite에서 하는 종류의 작업을 상당한 속도 불이익 없이 수행할 수 있음을 입증해야 해요.

만약 당신이 "러스테이시언(rustacean)"이고 Rust가 위 전제 조건을 이미 충족한다고 생각하며 SQLite를 Rust로 다시 코딩해야 한다고 생각한다면, SQLite 개발자에게 개인적으로 연락해 자신의 주장을 펼치는 것을 환영하고 권장해요.

더 알아보기 (Learn more)