<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Jsonb on kastori</title><link>http://blog.kastori.dev/tags/jsonb/</link><description>Recent content in Jsonb on kastori</description><generator>Hugo -- gohugo.io</generator><language>ko-kr</language><lastBuildDate>Sun, 31 May 2026 00:00:00 +0900</lastBuildDate><atom:link href="http://blog.kastori.dev/tags/jsonb/index.xml" rel="self" type="application/rss+xml"/><item><title>[DB 완전 정복 #6] MySQL과 PostgreSQL, 뭐가 다른가 — MVCC 구현부터 JSONB까지</title><link>http://blog.kastori.dev/tech/2026-05-31-db-06-mysql-vs-postgresql/</link><pubDate>Sun, 31 May 2026 00:00:00 +0900</pubDate><guid>http://blog.kastori.dev/tech/2026-05-31-db-06-mysql-vs-postgresql/</guid><description>&lt;h2 id="그냥-mysql-쓰면-안-되나요"&gt;&lt;a href="#%ea%b7%b8%eb%83%a5-mysql-%ec%93%b0%eb%a9%b4-%ec%95%88-%eb%90%98%eb%82%98%ec%9a%94" class="header-anchor"&gt;&lt;/a&gt;&amp;ldquo;그냥 MySQL 쓰면 안 되나요?&amp;rdquo;
&lt;/h2&gt;&lt;p&gt;사실 대부분의 경우 MySQL로 충분하다. 하지만 &amp;ldquo;왜 MySQL인가요?&amp;ldquo;라는 질문에 &amp;ldquo;익숙해서요&amp;quot;는 좋은 답이 아니다. 두 DB의 내부 구조 차이를 알면, 상황에 따른 선택 근거를 만들 수 있다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="mvcc-구현-방식--가장-근본적인-차이"&gt;&lt;a href="#mvcc-%ea%b5%ac%ed%98%84-%eb%b0%a9%ec%8b%9d--%ea%b0%80%ec%9e%a5-%ea%b7%bc%eb%b3%b8%ec%a0%81%ec%9d%b8-%ec%b0%a8%ec%9d%b4" class="header-anchor"&gt;&lt;/a&gt;MVCC 구현 방식 — 가장 근본적인 차이
&lt;/h2&gt;&lt;p&gt;앞서 MVCC가 트랜잭션의 일관된 읽기를 보장한다고 배웠다. 그런데 MySQL과 PostgreSQL은 이 MVCC를 다른 방식으로 구현한다.&lt;/p&gt;
&lt;h3 id="mysql-innodb-undo-log-방식"&gt;&lt;a href="#mysql-innodb-undo-log-%eb%b0%a9%ec%8b%9d" class="header-anchor"&gt;&lt;/a&gt;MySQL InnoDB: Undo Log 방식
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[테이블 페이지]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 행 데이터: TX_ID=200, name=&amp;#34;수정됨&amp;#34; ← 항상 최신 버전
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └──(포인터)──▶ [Undo Log]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; TX_ID=100, name=&amp;#34;원본&amp;#34; ← 이전 버전
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └──▶ [Undo Log]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; TX_ID=50, name=&amp;#34;초기값&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;현재 데이터 페이지에는 &lt;strong&gt;최신 버전만&lt;/strong&gt; 저장한다. 이전 버전은 별도 Undo Log 공간에 체인으로 보관한다. 읽기 시 자신의 스냅샷보다 최신 데이터면 Undo Log에서 이전 버전을 찾아 읽는다.&lt;/p&gt;
&lt;h3 id="postgresql-heap-방식"&gt;&lt;a href="#postgresql-heap-%eb%b0%a9%ec%8b%9d" class="header-anchor"&gt;&lt;/a&gt;PostgreSQL: Heap 방식
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[테이블 파일 (Heap)]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; t_xmin=100, t_xmax=200, name=&amp;#34;원본&amp;#34; ← 이전 버전 (dead tuple)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; t_xmin=200, t_xmax=0, name=&amp;#34;수정됨&amp;#34; ← 현재 버전 (live tuple)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;같은 테이블 파일 안에 &lt;strong&gt;여러 버전이 공존&lt;/strong&gt;한다. 각 행에 생성 트랜잭션(&lt;code&gt;t_xmin&lt;/code&gt;)과 삭제 트랜잭션(&lt;code&gt;t_xmax&lt;/code&gt;)을 기록한다. 읽기 시 내 트랜잭션 기준으로 어떤 버전을 볼지 visibility check로 판단한다.&lt;/p&gt;
&lt;h3 id="운영상-차이"&gt;&lt;a href="#%ec%9a%b4%ec%98%81%ec%83%81-%ec%b0%a8%ec%9d%b4" class="header-anchor"&gt;&lt;/a&gt;운영상 차이
&lt;/h3&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;항목&lt;/th&gt;
 &lt;th&gt;MySQL&lt;/th&gt;
 &lt;th&gt;PostgreSQL&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;이전 버전 위치&lt;/td&gt;
 &lt;td&gt;별도 Undo Log&lt;/td&gt;
 &lt;td&gt;같은 테이블 파일&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;정리 방법&lt;/td&gt;
 &lt;td&gt;Purge 스레드 자동&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;VACUUM&lt;/strong&gt; 필수&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;운영 주의점&lt;/td&gt;
 &lt;td&gt;긴 트랜잭션 (Undo 과다)&lt;/td&gt;
 &lt;td&gt;table bloat, autovacuum 튜닝&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;PostgreSQL은 오래된 dead tuple이 쌓이면 &lt;strong&gt;table bloat&lt;/strong&gt; 현상이 발생한다. autovacuum이 자동으로 정리하지만, 대용량 테이블에서는 VACUUM 튜닝이 운영의 핵심이다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="인덱스-구조--clustered-vs-heap"&gt;&lt;a href="#%ec%9d%b8%eb%8d%b1%ec%8a%a4-%ea%b5%ac%ec%a1%b0--clustered-vs-heap" class="header-anchor"&gt;&lt;/a&gt;인덱스 구조 — Clustered vs Heap
&lt;/h2&gt;&lt;p&gt;MySQL의 기본키 인덱스는 &lt;strong&gt;Clustered Index&lt;/strong&gt;다. 리프 노드에 실제 행 데이터가 저장되고, 기본키 순서로 물리적으로 정렬된다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;MySQL Secondary Index 조회:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 인덱스에서 PK 찾기 → PK로 Clustered Index 조회 (2단계)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;PostgreSQL 모든 인덱스 조회:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 인덱스에서 heap tuple pointer 찾기 → heap에서 행 조회 (1단계)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;MySQL의 Secondary Index는 2단계를 거치지만, 기본키 조회는 Clustered Index 덕분에 최고 성능이다. PostgreSQL은 모든 인덱스가 동등한 구조를 가진다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="jsonb--postgresql이-mysql을-압도하는-부분"&gt;&lt;a href="#jsonb--postgresql%ec%9d%b4-mysql%ec%9d%84-%ec%95%95%eb%8f%84%ed%95%98%eb%8a%94-%eb%b6%80%eb%b6%84" class="header-anchor"&gt;&lt;/a&gt;JSONB — PostgreSQL이 MySQL을 압도하는 부분
&lt;/h2&gt;&lt;p&gt;반정형 데이터(JSON)를 DB에 저장해야 한다면, PostgreSQL의 JSONB가 압도적으로 유리하다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- MySQL JSON: 텍스트로 저장, 조회마다 파싱
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;COLUMN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 인덱스를 걸려면 Generated Column 트릭 필요
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;COLUMN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;meta_color&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;GENERATED&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ALWAYS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JSON_UNQUOTE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JSON_EXTRACT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;$.color&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;STORED&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INDEX&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;idx_color&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;meta_color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- PostgreSQL JSONB: 바이너리 파싱 후 저장, GIN 인덱스 직접 가능
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;COLUMN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JSONB&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INDEX&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;idx_meta&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;USING&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GIN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 풍부한 연산자
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;@&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;{&amp;#34;color&amp;#34;: &amp;#34;red&amp;#34;}&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- 포함 여부
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;color&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- 키 존재 여부
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;PostgreSQL JSONB는 바이너리로 저장해 파싱 없이 빠르고, GIN 인덱스로 JSON 내부 키에 직접 인덱싱할 수 있다. MySQL은 이 수준의 JSON 쿼리 지원이 없다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="postgresql의-확장-생태계"&gt;&lt;a href="#postgresql%ec%9d%98-%ed%99%95%ec%9e%a5-%ec%83%9d%ed%83%9c%ea%b3%84" class="header-anchor"&gt;&lt;/a&gt;PostgreSQL의 확장 생태계
&lt;/h2&gt;&lt;p&gt;PostgreSQL은 확장(Extension)으로 기능을 추가할 수 있다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;PostGIS → 지리 데이터, 위경도 거리 계산, 공간 인덱스
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pgvector → AI 벡터 임베딩 저장 및 유사도 검색 (LLM 연동)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pg_trgm → 문자열 유사도 검색 (LIKE &amp;#39;%검색어%&amp;#39; 인덱스)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;TimescaleDB → 시계열 데이터 최적화
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;LLM, AI 서비스와 연동하거나 지리 기능이 필요하다면 PostgreSQL이 사실상 유일한 선택이다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="라이선스-차이"&gt;&lt;a href="#%eb%9d%bc%ec%9d%b4%ec%84%a0%ec%8a%a4-%ec%b0%a8%ec%9d%b4" class="header-anchor"&gt;&lt;/a&gt;라이선스 차이
&lt;/h2&gt;&lt;p&gt;MySQL은 GPL 라이선스다. 오픈소스 프로젝트에서 MySQL을 수정해 배포하면 소스를 공개해야 한다. MariaDB 포크가 생긴 배경이기도 하다.&lt;/p&gt;
&lt;p&gt;PostgreSQL은 BSD 계열(PostgreSQL License)로 &lt;strong&gt;상업용 포함 완전 자유&lt;/strong&gt;다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="선택-기준-정리"&gt;&lt;a href="#%ec%84%a0%ed%83%9d-%ea%b8%b0%ec%a4%80-%ec%a0%95%eb%a6%ac" class="header-anchor"&gt;&lt;/a&gt;선택 기준 정리
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;MySQL을 선택하는 경우:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - Spring Boot + JPA 조합 (레퍼런스와 생태계 압도적)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - 단순 CRUD 위주의 일반 웹 서비스
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - 팀에 MySQL 운영 경험이 있는 경우
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - AWS Aurora MySQL 사용 계획
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;PostgreSQL을 선택하는 경우:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - JSON 데이터를 쿼리하고 인덱스도 필요한 경우 (JSONB)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - 지리 데이터 (PostGIS)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - AI/ML 벡터 검색 (pgvector)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - 복잡한 분석 쿼리 위주 서비스
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - 오픈소스 라이선스 자유도가 중요한 경우
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="마치며"&gt;&lt;a href="#%eb%a7%88%ec%b9%98%eb%a9%b0" class="header-anchor"&gt;&lt;/a&gt;마치며
&lt;/h2&gt;&lt;p&gt;MySQL은 웹 서비스의 검증된 선택이고, PostgreSQL은 확장성과 기능 다양성에서 강하다. 어느 쪽이 낫다는 게 아니라, &lt;strong&gt;사용하는 이유를 설명할 수 있느냐&lt;/strong&gt;가 중요하다.&lt;/p&gt;
&lt;p&gt;다음 편에서는 관계형 DB와 전혀 다른 구조로 설계된 Redis의 내부 동작 원리를 다룬다.&lt;/p&gt;</description></item></channel></rss>