Q: 값 싼 DB도 많은데, 굳이 비싼 Oracle을 쓰는 이유는?
A: 책임질 누군가가 필요해서
이 말은 아이러니하게도 오픈소스 소프트웨어의 가장 아픈 곳을 찌르고 있습니다.
유료 프로젝트라도, 소프트웨어 실패에 대해 법적/도의적 책임을 지는 주체가 명확하지 않은 게 현실이니까요.
이러한 약점만 제외한다면, 개발자 입장에서 PosgreSQL 은 여러 모로 꽤 많은 편의를 제공합니다.
웹을 처음 배울 땐, MySql 을, 닷넷을 처음 배울 땐, MS-SQL 을 써 본 입장에서, PostgreSQL 을 썼을 때, 쾌적하게 느꼈던 점들을 공유하고자 합니다.
무료
MySql 유료화의 최대 수혜자가 PostgreSQL이지 싶습니다.
제한 없는 무료입니다.
무료 티어 클라우드
1번과 연결되어 있는데, 클라우드 서비스 중, 무료 티어 서비스를 PostgreSQL 에 기반하는 경우가 적지 않습니다.
예를 들어, Supabase 는 DB로 PostgreSQL을 사용하는데, 무료 티어인 경우, DB도 무료(500MB)가 됩니다.
이 DB를 서비스 초기에 쓰는 것도 좋지만, 개발용으로 쓰는 것도 매우 좋습니다.
로컬에 구축된 DB를 사용하면 만날 수 없는, 커넥션 지연, 턴어라운드 타임, 네트워크 지연/실패 등에 대한 대응책이 자연스럽게 코드에 반영되게 됩니다.
운영 단계 접어 들면, 유료 티어로 전환해서 변동비만 지출하고 고정비를 지출하지 않는 방법과, 별도의 서버를 구축하는 고정비는 지출하지만, 변동비를 지출하지 않는 방법 중 하나를 선택할 수 있는 등, 탄력적 예산 집행이 가능합니다.
크로스 플랫폼
PostgreSQL 은 윈도우, 맥, 리눅스, BSC, Solaris 에서 실행 가능합니다.
크로스 플랫폼인 닷넷과 좋은 짝꿍이라고 생각합니다.
배열 자료형
PostgreSQL 은 다양한 원시 자료형을 제공하는데, 그 중에 백미는 배열이지 않을까 싶습니다.
이 배열 형식의 효익을 간단하게 표현하면, Student의 속성 중, 배열과 목록(List<T>)들을 별도의 테이블에 맵핑할 필요가 없습니다.
class Student
{
public string Name { get; set; }
public int[] Scores { get; init; } = [];
public List<string> ClassNames { get; init; } = [];
public Accomplishment[] Accopmplishments { get; init: } = [];
}
record Accomplishment(string Name, string Description, DateOnly When);
PostgreSQL은 복합 자료형(Composite Types)도 지원하는데 이 자료형도 배열에 저장할 수 있습니다.
복합 자료형이란 테이블의 필드의 모음입니다.
기본적으로 테이블을 설정하면 그 테이블의 필드로 구성된 복합 자료형이 묵시적으로 선언됩니다.
명시적으로 선언된 복합 자료형은 테이블 정의할 때 사용할 수 있습니다.
위의 코드를 쿼리로 표현하면,
CREATE TYPE accomplishment AS (
name text,
description text,
when date
);
CREATE TABLE students (
name text,
scores integer[],
class_names text[],
accomplishments accomplishment[]
);
EF 를 사용할 경우, PostgreSQL EF 제공자는 코드를 통해 enum type 을 정의하는 API 도 제공합니다. (아래에 방법을 기술합니다)
Enum
PostgreSQL 는 Enum 형식을 지원합니다.
닷넷의 Enum 형식은 보통 DB의 정수 형식 혹은 string 으로 파싱해서 저장되는데, 특히 전자의 경우, enum 의 멤버가 기저 정수 값에 강하게 결합되는 문제가 있습니다.
예를 들어, 아래와 같이 정의해 놓은 enum 형식을
enum MyEnum
{
Start, End
}
아래와 같이 변경하는 경우,
enum MyEnum
{
Start, Canceled, End, Failed
}
코드 입장에서는 아무런 문제가 없지만, 저장소가 오염되는 것은 당연하고, 그에 대응하기 위한 맵핑 코드 작업이 결코 쉽지 않을 것입니다.
DB가 enum 형식을 지원하면, 위와 같은 상황은 상당히 가볍게 처리될 수 있습니다.
다만, PostgreSQL의 enum은 코드 만큼 자유도가 높지는 않은데, 멤버의 추가는 가능해도 재정의, 순서 변경은 불가능하다는 제약이 있습니다.
Enum 형식은 DB에서 정의할 수도 있고,
CREATE TYPE my_enum AS ENUM (‘start’, ‘end’);
EF의 Code-First 로 DB를 설계하는 경우, EF 제공자(Npgsql)를 통해 정의할 수도 있습니다.
// MyDbContext.cs
// ...
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Db에 형식을 정의함.
modelBuilder.HasPostgresEnum<MyEnum>();
// ...
다만, EF 를 통해 DB에 접근하는 경우 DbContext 에 닷넷 타입과 DB 타입을 반드시 맵핑해줘야 합니다.
// 맵핑 설정
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var dataSourceBuilder = new NpgsqlDataSourceBuilder("{connection string}");
_ = dataSourceBuilder.MapEnum<MyEnum>();
var dataSource = dataSourceBuilder.Build();
optionsBuilder.UseNpgsql(dataSource);
// ...
PostgREST
PostgREST 는 PostgreSQL 에 기반한 RESTful API 웹 서버입니다.
쉽게 얘기하면, PostgreSQL 을 웹서버로 변신시켜주는 도구입니다.
아시다시피, Web Api 를 직접 작성하는 것은 높은 전문성과 자원이 필요하고, 에러 유발 요소가 많은데, 이 도구는 그러한 어려움을 해결해줍니다.
많은 웹 API 처럼, CRUD는 기본이고, SQL 과 같은 필터링 방식을 제공하는데, 여기에는 Join 도 포함됩니다.
OS 별 패키지 매니저를 통해 설치할 수도 있고, 바이너리 파일로 설치할 수 있습니다.
Release v12.2.0 · PostgREST/postgrest (github.com)
닷넷에는 발군의 Asp.Net Core Web Api 가 있지만, 앱을 직접 작성할 시간이나 자원이 없다면 PostgREST 는 좋은 대안이 됩니다.
참고로, 블레이저 웹어셈블리를 사용한다면 채택하는 것을 고려해야 합니다. 그 이유는 웹어셈블리는 브라우저의 샌드박스에서 실행되는데, 브라우저는 샌드박스 내부에서 Db Connection 을 허용하지 않기 때문입니다. 여기에는 DbContext 내부에 포함된 것도 포함됩니다. (DbContext 도 정상 동작하지 않습니다)
Row Level Security (RLS)
RLS 는 Select * 을 방지하는 PosgreSQL 만의 보안 장치입니다.
예를 들어, userinfos 테이블에 select * 을 하더라도, 로그인한 user의 userId 값을 보유한 row 만 select 되도록 제한하는 것입니다.
내부에서 where 절이 실행되기 때문에 오버헤드가 있기는 하지만, 보안이 중요할 때는 효율적이고 간편한 수단입니다.
// 추가 예정
…