<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>SQLite on Dev TLDRLSS</title>
        <link>https://dev.tldrlss.com/ko/tags/sqlite/</link>
        <description>Recent content in SQLite on Dev TLDRLSS</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>ko</language>
        <lastBuildDate>Tue, 19 May 2026 18:10:00 +0800</lastBuildDate><atom:link href="https://dev.tldrlss.com/ko/tags/sqlite/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>SQLite의 관대함에 속지 마라! 동적 타입의 함정이란? ALTER TABLE은 왜 미완성인가? Node.js에서 방어적 프로그래밍으로 매끄러운 테이블 마이그레이션을 구현하는 방법</title>
        <link>https://dev.tldrlss.com/ko/article/2026/05/sqlite-pitfall-intro/</link>
        <pubDate>Tue, 19 May 2026 18:10:00 +0800</pubDate>
        
        <guid>https://dev.tldrlss.com/ko/article/2026/05/sqlite-pitfall-intro/</guid>
        <description>&lt;img src="https://dev.tldrlss.com/global-assets/images/database/sqlite-type-pitfall-1.jpg" alt="Featured image of post SQLite의 관대함에 속지 마라! 동적 타입의 함정이란? ALTER TABLE은 왜 미완성인가? Node.js에서 방어적 프로그래밍으로 매끄러운 테이블 마이그레이션을 구현하는 방법" /&gt;&lt;p&gt;재활용 수거함에 &amp;lsquo;페트병 전용&amp;rsquo;이라는 라벨을 붙여 놓았는데, 누군가 종이 조각을 집어넣어도 아무런 항의 없이 묵묵히 받아들이는 모습을 상상해 보세요.&lt;/p&gt;
&lt;p&gt;이것이 바로 개발자가 &lt;code&gt;SQLite&lt;/code&gt; 타입 시스템을 처음 만났을 때 겪게 되는 등골 오싹한 경험입니다.&lt;/p&gt;
&lt;p&gt;타입이 맞지 않으면 즉시 입국을 거부하는 &lt;code&gt;PostgreSQL&lt;/code&gt; 같은 엄격한 세관 스타일에 익숙하다면, &lt;code&gt;SQLite&lt;/code&gt; 소탈함(또는 느슨함)에 인생을 의심하게 될지도 모릅니다.&lt;/p&gt;
&lt;p&gt;더 끔찍한 것은, &lt;strong&gt;테이블 구조를 변경&lt;/strong&gt;하려고 할 때 그것이 당신에게 이렇게 말한다는 것입니다:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&amp;ldquo;테이블을 직접 수정할 수 없습니다. 새 집을 짓고, 가구를 옮긴 다음, 오래된 집을 폭파하세요.&amp;rdquo;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;sqlite의-내부에는-5가지-스토리지-클래스만-존재한다&#34;&gt;SQLite의 내부에는 5가지 스토리지 클래스만 존재한다
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;CREATE TABLE&lt;/code&gt;에서 아무리 화려한 타입 이름(&lt;code&gt;VARCHAR(255)&lt;/code&gt;, &lt;code&gt;BIGINT&lt;/code&gt;, &lt;code&gt;DECIMAL&lt;/code&gt;)을 선언하더라도, &lt;code&gt;SQLite&lt;/code&gt; 내부적으로는 다음 &lt;strong&gt;5가지 스토리지 클래스&lt;/strong&gt;만 인식합니다:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;스토리지 클래스&lt;/th&gt;
          &lt;th&gt;설명&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;NULL&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;빈 값&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;INTEGER&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;정수 (값의 크기에 따라 자동으로 1~8 바이트 점유)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;REAL&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;부동 소수점 수 (고정 8 바이트)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;TEXT&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;문자열 (기본 UTF-8 인코딩)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;BLOB&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;바이너리 데이터 (입력된 상태 그대로 저장)&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;컬럼에 설정한 타입은 &lt;code&gt;SQLite&lt;/code&gt;에게 &lt;strong&gt;단지 &amp;lsquo;권장 사항&amp;rsquo;일 뿐이며, &amp;lsquo;강제 규칙&amp;rsquo;이 아닙니다&lt;/strong&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;이를 **&amp;ldquo;타입 어피니티(Type Affinity)&amp;rdquo;**라고 부릅니다. &lt;code&gt;SQLite&lt;/code&gt;는 데이터를 권장 타입으로 변환하려고 시도하지만, 변환할 수 없는 경우에는 에러 없이 원본 데이터를 그대로 삽입합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;SQLite&lt;/code&gt;에서 &lt;code&gt;age INTEGER&lt;/code&gt; 컬럼을 선언하고 문자열 &lt;code&gt;&#39;영원한 18세&#39;&lt;/code&gt;를 삽입해도 아주 기쁘게 받아들여집니다.&lt;/p&gt;
&lt;!--adsense--&gt;
&lt;h2 id=&#34;가장-쉽게-빠지기-쉬운-3가지-타입-함정&#34;&gt;가장 쉽게 빠지기 쉬운 3가지 타입 함정
&lt;/h2&gt;&lt;h3 id=&#34;함정-1-네이티브-boolean-부재&#34;&gt;함정 1: 네이티브 Boolean 부재
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;SQLite&lt;/code&gt;에는 &lt;strong&gt;Boolean 타입이 없습니다&lt;/strong&gt;. &lt;code&gt;True&lt;/code&gt;와 &lt;code&gt;False&lt;/code&gt;는 정수 &lt;code&gt;1&lt;/code&gt;과 &lt;code&gt;0&lt;/code&gt;으로만 표현할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Node.js&lt;/code&gt;를 사용해 &lt;code&gt;SQLite&lt;/code&gt;에서 데이터를 조회할 때, &lt;code&gt;true&lt;/code&gt;나 &lt;code&gt;false&lt;/code&gt;가 아니라 숫자 &lt;code&gt;1&lt;/code&gt; 또는 &lt;code&gt;0&lt;/code&gt;이 반환됩니다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;if (user.is_admin === true)&lt;/code&gt;와 같은 조건식을 직접 사용하면 절대 참이 되지 않습니다.&lt;/p&gt;
&lt;h3 id=&#34;함정-2-datetime-타입-부재&#34;&gt;함정 2: Date/Time 타입 부재
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;SQLite&lt;/code&gt;에는 &lt;strong&gt;날짜 시간 타입이 없습니다&lt;/strong&gt;. 시간은 다음과 같이 저장할 수밖에 없습니다:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;저장 방식&lt;/th&gt;
          &lt;th&gt;예시&lt;/th&gt;
          &lt;th&gt;장단점&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;TEXT (ISO-8601 문자열)&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;&#39;2026-05-19T18:00:00Z&#39;&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;가장 추천&lt;/strong&gt;. 가독성이 높고 향후 PostgreSQL 마이그레이션 시 매끄럽게 변환 가능&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;INTEGER (Unix 타임스탬프)&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;1747656000&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;공간을 적게 차지하지만 사람이 읽을 수 없음&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;날짜를 &lt;code&gt;2026/5/19 오후 6시&lt;/code&gt;와 같이 자신만의 커스텀 형식으로 저장하지 마세요. 향후 데이터 마이그레이션 시 재앙이 일어날 것입니다.&lt;/p&gt;
&lt;h3 id=&#34;함정-3-integer-컬럼에-문자열을-삽입해도-에러가-나지-않음&#34;&gt;함정 3: INTEGER 컬럼에 문자열을 삽입해도 에러가 나지 않음
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;PostgreSQL&lt;/code&gt;에서는 &lt;code&gt;INTEGER&lt;/code&gt; 컬럼에 문자열을 삽입하면 즉시 에러가 발생합니다. 하지만 &lt;code&gt;SQLite&lt;/code&gt;는 묵묵히 변환을 시도하고, 실패하면 원본 그대로 수락합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;이는 &lt;strong&gt;오염된 데이터가 조용히 데이터베이스에 스며들 수 있음&lt;/strong&gt;을 의미합니다. 어느 날 프로그램이 예상치 못한 타입을 받아 크래시가 나기 전까지는 문제를 발견할 수 없습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;방어적-프로그래밍-관대한-데이터베이스를-엄격한-태도로-다루기&#34;&gt;방어적 프로그래밍: 관대한 데이터베이스를 엄격한 태도로 다루기
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;SQLite&lt;/code&gt;의 느슨함에 직면했을 때, &lt;code&gt;Node.js&lt;/code&gt; 개발에서는 &lt;strong&gt;엄격한 방어 메커니즘&lt;/strong&gt;을 구축해야 합니다:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;방어 단계&lt;/th&gt;
          &lt;th&gt;도구&lt;/th&gt;
          &lt;th&gt;역할&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;컴파일 타임 가드&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;TypeScript&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;코드 작성 단계에서 잘못된 타입을 캐치함&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;API 입구 검증&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;Zod&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;입력되는 데이터를 엄격하게 검증함 (age가 반드시 숫자임을 보장)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;내부 타입 변환&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;Prisma / Drizzle ORM&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;SQLite와 PostgreSQL 간의 타입 차이를 자동으로 처리함&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;**&amp;lsquo;데이터 검증의 문지기&amp;rsquo;**를 데이터베이스 계층에서 &lt;code&gt;Node.js&lt;/code&gt; 애플리케이션 계층으로 이동시키는 것이 SQLite의 개발 속도를 누리면서 향후 확장성을 확보하기 위한 핵심 전략입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;ORM을 사용할 경우 코드 내에서 &lt;code&gt;type: &#39;boolean&#39;&lt;/code&gt;으로 선언해 두면, ORM이 &lt;code&gt;SQLite&lt;/code&gt; 저장 시 자동으로 &lt;code&gt;1/0&lt;/code&gt;으로 변환하고, 조회 시 &lt;code&gt;true/false&lt;/code&gt;로 자동 변환해 주어 기저의 타입 차이를 완벽히 가려줍니다.&lt;/p&gt;
&lt;!--adsense--&gt;
&lt;h2 id=&#34;alter-table은-미완성-수정할-수-있는-것과-없는-것&#34;&gt;ALTER TABLE은 미완성: 수정할 수 있는 것과 없는 것
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;SQLite&lt;/code&gt;의 테이블 구조 수정 지원은 매우 제한적입니다:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;작업&lt;/th&gt;
          &lt;th&gt;지원 여부&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;컬럼 추가 (&lt;code&gt;ADD COLUMN&lt;/code&gt;)&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;예&lt;/strong&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;컬럼 이름 변경 (&lt;code&gt;RENAME COLUMN&lt;/code&gt;)&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;예&lt;/strong&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;컬럼 삭제 (&lt;code&gt;DROP COLUMN&lt;/code&gt;)&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;예&lt;/strong&gt; (최신 버전에서 지원)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;테이블 이름 변경 (&lt;code&gt;RENAME TO&lt;/code&gt;)&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;예&lt;/strong&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;컬럼 타입 변경&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;아니오&lt;/strong&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;UNIQUE&lt;/code&gt;, &lt;code&gt;NOT NULL&lt;/code&gt; 제약 조건 추가/제거&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;아니오&lt;/strong&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;기본키(Primary Key) 수정&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;아니오&lt;/strong&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;외래키(Foreign Key) 수정&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;아니오&lt;/strong&gt;&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;lsquo;지원되지 않는&amp;rsquo; 수정을 수행해야 할 때, &lt;code&gt;SQLite&lt;/code&gt;는 &lt;strong&gt;&amp;lsquo;재생성 및 마이그레이션&amp;rsquo;&lt;/strong&gt; 전략을 실행할 것을 요구합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;재생성-및-마이그레이션-4단계-sqlite식-테이블-업그레이드-방법&#34;&gt;재생성 및 마이그레이션 4단계: SQLite식 테이블 업그레이드 방법
&lt;/h2&gt;&lt;p&gt;직접 변경이 불가능하므로 공식 문서가 권장하는 표준 방법은 &lt;strong&gt;새 집을 짓고, 가구를 옮기고, 이전 집을 날려버리고, 새 문패를 다는 것&lt;/strong&gt;입니다:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;단계&lt;/th&gt;
          &lt;th&gt;액션&lt;/th&gt;
          &lt;th&gt;설명&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;1&lt;/td&gt;
          &lt;td&gt;새 테이블 생성&lt;/td&gt;
          &lt;td&gt;올바른 구조로 &lt;code&gt;CREATE TABLE users_new (...)&lt;/code&gt; 생성&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;2&lt;/td&gt;
          &lt;td&gt;데이터 복사&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;INSERT INTO users_new SELECT ... FROM users&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;3&lt;/td&gt;
          &lt;td&gt;이전 테이블 삭제&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;DROP TABLE users&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;4&lt;/td&gt;
          &lt;td&gt;테이블 이름 변경&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;ALTER TABLE users_new RENAME TO users&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;이 4개 단계는 &lt;strong&gt;일사천리로&lt;/strong&gt; 실행되어야 합니다. 중간에 전원이 끊기거나 프로그램이 크래시 나면 데이터 유실로 이어집니다.&lt;/p&gt;
&lt;!--adsense--&gt;
&lt;h2 id=&#34;업그레이드-시-데이터-유실-방지-두-가지-안전-방어선&#34;&gt;업그레이드 시 데이터 유실 방지: 두 가지 안전 방어선
&lt;/h2&gt;&lt;h3 id=&#34;방어선-1-물리적-방어-파일-직접-복사&#34;&gt;방어선 1: 물리적 방어, 파일 직접 복사
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;SQLite&lt;/code&gt;는 본질적으로 하나의 파일입니다. 스키마 변경을 실행하기 전에 &lt;code&gt;.db&lt;/code&gt; 파일을 복사해 백업본을 만드는 것으로 충분합니다.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fs&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;require&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;fs&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;fs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;copyFileSync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;my_project.db&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;my_project_backup.db&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;만약 문제가 발생하더라도 파일을 덮어쓰는 것만으로 눈 깜짝할 사이에 복구할 수 있습니다. 이는 다른 대형 데이터베이스에서는 제공하지 않는 큰 장점입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;방어선-2-트랜잭션-래퍼-데이터베이스의-타임머신&#34;&gt;방어선 2: 트랜잭션 래퍼, 데이터베이스의 타임머신
&lt;/h3&gt;&lt;p&gt;이사 단계를 하나의 &lt;code&gt;Transaction&lt;/code&gt;으로 감싸줍니다. 단계 중 하나라도 실패하면 전체 프로세스가 &lt;strong&gt;자동으로 롤백&lt;/strong&gt;되어 아무 일도 없었던 것처럼 복원됩니다.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Database&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;require&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;better-sqlite3&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Database&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;my_project.db&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;migrateData&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;transaction&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;prepare&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sb&#34;&gt;    CREATE TABLE users_new (
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sb&#34;&gt;      id INTEGER PRIMARY KEY AUTOINCREMENT,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sb&#34;&gt;      name TEXT NOT NULL,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sb&#34;&gt;      age INTEGER NOT NULL DEFAULT 18
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sb&#34;&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sb&#34;&gt;  `&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;prepare&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sb&#34;&gt;    INSERT INTO users_new (id, name, age)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sb&#34;&gt;    SELECT id, name, COALESCE(age, 18) FROM users
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sb&#34;&gt;  `&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;prepare&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;DROP TABLE users&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;prepare&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;ALTER TABLE users_new RENAME TO users&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;try&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;migrateData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;테이블 업그레이드 완료&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;catch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;업그레이드 실패, 데이터가 안전하게 복원되었습니다:&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;message&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;**&amp;ldquo;백업 파일 + 트랜잭션 바인딩&amp;rdquo;**이 데이터베이스 마이그레이션의 에어백 역할을 합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;엄격한-태도로-소탈한-sqlite-제어하기&#34;&gt;엄격한 태도로 소탈한 SQLite 제어하기
&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;소탈한 &lt;code&gt;SQLite&lt;/code&gt;를 &lt;strong&gt;엄격한 애플리케이션 계층 아키텍처&lt;/strong&gt;로 다룸으로써, 향후 기술 부채를 방지하면서 극도로 빠른 개발 속도의 장점을 안심하고 누릴 수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;SQLite의 타입 시스템은 매우 유연하며, &lt;code&gt;ALTER TABLE&lt;/code&gt; 역시 제약 조건이 많습니다.&lt;/p&gt;
&lt;p&gt;하지만 &lt;code&gt;Node.js&lt;/code&gt; 단에서 &lt;strong&gt;TypeScript 타입 검사 + Zod 검증 + ORM 추상화&lt;/strong&gt;를 착실히 적용하고, &lt;strong&gt;물리적 백업 + 트랜잭션&lt;/strong&gt; 안전 전략을 결합하면 &lt;code&gt;SQLite&lt;/code&gt;가 제공하는 뛰어난 개발 효율성을 마음껏 누릴 수 있으며 동시에 향후 PostgreSQL로의 순탄한 이전 경로를 닦아둘 수 있습니다.&lt;/p&gt;
&lt;!--adsense--&gt;
&lt;h2 id=&#34;reference&#34;&gt;Reference
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://sqlite.org/datatype3.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Datatypes In SQLite&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.runoob.com/sqlite/sqlite-data-types.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;SQLite 数据类型 | 菜鸟教程&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.sqlite.org/lang_transaction.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Transaction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://sqlite.org/lang.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Query Language Understood by SQLite&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://en.wikipedia.org/wiki/SQLite&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;SQLite - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        </item>
        <item>
        <title>모든 것에 PostgreSQL을 쓰지 마세요! SQLite의 임베디드 아키텍처와 제로 설정 장점은 무엇일까요? SQLite의 한계는 어디까지일까요? 언제 SQLite를 선택하고, 언제 PostgreSQL을 선택해야 할까요?</title>
        <link>https://dev.tldrlss.com/ko/article/2026/05/sqlite-intro/</link>
        <pubDate>Tue, 19 May 2026 18:00:00 +0800</pubDate>
        
        <guid>https://dev.tldrlss.com/ko/article/2026/05/sqlite-intro/</guid>
        <description>&lt;img src="https://dev.tldrlss.com/global-assets/images/database/sqlite-vs-postgresql-choice-2.jpg" alt="Featured image of post 모든 것에 PostgreSQL을 쓰지 마세요! SQLite의 임베디드 아키텍처와 제로 설정 장점은 무엇일까요? SQLite의 한계는 어디까지일까요? 언제 SQLite를 선택하고, 언제 PostgreSQL을 선택해야 할까요?" /&gt;&lt;p&gt;매일 열어보는 브라우저, 스마트폰의 메신저 앱, 심지어 바탕화면의 메모 도구 속에도 모두 동일한 경량 데이터베이스가 숨겨져 있다는 사실을 생각해 본 적이 있으신가요?&lt;/p&gt;
&lt;p&gt;별도의 서버 소프트웨어를 설치할 필요도 없고, 계정이나 비밀번호 설정도 필요 없으며, 인터넷 연결조차 필요하지 않습니다. 그것은 그저 하나의 &lt;strong&gt;파일&lt;/strong&gt;이며, 하드디스크에 조용히 누워 언제든지 서비스를 제공할 준비가 되어 있습니다.&lt;/p&gt;
&lt;p&gt;이 조용하면서도 강력한 존재가 바로 &lt;code&gt;SQLite&lt;/code&gt;입니다.&lt;/p&gt;
&lt;h2 id=&#34;sqlite란-무엇일까요-세계에서-가장-널리-배포된-데이터베이스&#34;&gt;SQLite란 무엇일까요? 세계에서 가장 널리 배포된 데이터베이스
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;SQLite&lt;/code&gt;는 C 언어로 작성된 &lt;strong&gt;임베디드 관계형 데이터베이스 엔진&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;독립된 서버 프로세스 없이, 귀하의 애플리케이션에 직접 내장되어 동작합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이는 우리에게 친숙한 &lt;code&gt;PostgreSQL&lt;/code&gt;이나 &lt;code&gt;MySQL&lt;/code&gt;과는 완전히 다릅니다. 전통적인 데이터베이스는 &lt;strong&gt;독립적으로 동작하는 서버&lt;/strong&gt;이며, 프로그램은 **네트워크 프로토콜(TCP/IP)**을 통해 데이터베이스와 &amp;ldquo;통신&amp;quot;해야 합니다.&lt;/p&gt;
&lt;p&gt;하지만 &lt;code&gt;SQLite&lt;/code&gt;는 다릅니다. 그것은 단지 코드의 한 블록일 뿐이며, 애플리케이션 내부에서 직접 실행되어 하드디스크에 있는 &lt;code&gt;.db&lt;/code&gt; 파일을 읽고 씁니다.&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;비교 기준&lt;/th&gt;
          &lt;th&gt;SQLite (임베디드)&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;애플리케이션에 직접 내장, 독립 서버 없음&lt;/td&gt;
          &lt;td&gt;독립된 서버 프로세스, 네트워크 연결을 통해 동작&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;설정 요구사항&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;제로 설정&lt;/strong&gt;, 설치 불필요, 계정 불필요&lt;/td&gt;
          &lt;td&gt;설치, 계정/비밀번호 설정 및 방화벽 설정 필요&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;데이터 저장&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;단일 크로스 플랫폼 파일&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;서버 디렉터리 내의 여러 파일&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;백업 방식&lt;/td&gt;
          &lt;td&gt;해당 파일을 직접 복사&lt;/td&gt;
          &lt;td&gt;pg_dump 등 전용 도구 사용 필요&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;바로 이러한 &lt;strong&gt;&amp;ldquo;플러그 앤 플레이&amp;rdquo;&lt;/strong&gt; 특성 덕분에 &lt;code&gt;SQLite&lt;/code&gt;는 세계에서 가장 널리 배포된 데이터베이스 엔진이 되었습니다.&lt;/p&gt;
&lt;p&gt;Android 및 iOS 운영체제, Chrome 및 Firefox 브라우저부터 Adobe Lightroom, WhatsApp, 심지어 Airbus A350의 비행 시스템에 이르기까지 도처에서 그 모습을 찾아볼 수 있습니다.&lt;/p&gt;
&lt;!--adsense--&gt;
&lt;h2 id=&#34;nodejs에서-sqlite-사용하기&#34;&gt;Node.js에서 SQLite 사용하기
&lt;/h2&gt;&lt;p&gt;Node.js 개발자라면 &lt;code&gt;SQLite&lt;/code&gt;를 사용하는 것이 매우 간단합니다. 컴퓨터에 어떠한 데이터베이스 서버 소프트웨어도 설치할 필요 없이, npm 패키지 하나만 설치하면 바로 시작할 수 있습니다.&lt;/p&gt;
&lt;p&gt;현재 업계에서 가장 흔히 사용되는 선택지:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;패키지 이름&lt;/th&gt;
          &lt;th&gt;특징&lt;/th&gt;
          &lt;th&gt;추천 시나리오&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;sqlite3&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;가장 오래됨, 비동기 API 지원&lt;/td&gt;
          &lt;td&gt;동시에 많은 비동기 작업을 처리해야 할 때&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;better-sqlite3&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;뛰어난 성능&lt;/strong&gt;, 직관적인 API 디자인, 매우 빠름&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;최우선 추천&lt;/strong&gt;, 개발 효율성과 실행 속도를 추구할 때&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;better-sqlite3&lt;/code&gt;를 사용하여 데이터베이스를 생성하고 쿼리하는 과정은 5분도 채 걸리지 않습니다:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Database&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;require&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;better-sqlite3&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Database&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;my_project.db&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;prepare&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;CREATE TABLE IF NOT EXISTS users (name TEXT, age INTEGER)&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;insert&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;prepare&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;INSERT INTO users (name, age) VALUES (?, ?)&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;insert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;John&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;25&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;user&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;prepare&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;SELECT * FROM users WHERE name = ?&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;John&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// { name: &amp;#39;John&amp;#39;, age: 25 }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;파일이 존재하지 않는 경우, &lt;code&gt;better-sqlite3&lt;/code&gt;가 &lt;strong&gt;자동으로 파일을 생성해 줍니다&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;sqlite가-지원하는-sql-구문은-생각보다-강력합니다&#34;&gt;SQLite가 지원하는 SQL 구문은 생각보다 강력합니다
&lt;/h2&gt;&lt;p&gt;많은 사람들이 &lt;code&gt;SQLite&lt;/code&gt;가 매우 원시적이라고 생각하지만, 실제로는 다음과 같은 고급 기능을 포함하여 표준 SQL 구문의 대부분을 지원합니다:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;구문 범주&lt;/th&gt;
          &lt;th&gt;지원 항목&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;기본 조작&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;SELECT&lt;/code&gt;, &lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;데이터 정의&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;CREATE TABLE&lt;/code&gt;, &lt;code&gt;CREATE INDEX&lt;/code&gt;, &lt;code&gt;CREATE VIEW&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;고급 조회&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;WITH&lt;/code&gt; (재귀 CTE), 윈도우 함수 (Window Functions)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;충돌 처리&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;UPSERT&lt;/code&gt; (&lt;code&gt;INSERT ... ON CONFLICT DO UPDATE&lt;/code&gt;)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;JSON 처리&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;json_extract&lt;/code&gt;, &lt;code&gt;json_array&lt;/code&gt;, &lt;code&gt;json_object&lt;/code&gt; 등 내장 함수&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;트랜잭션 제어&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;BEGIN&lt;/code&gt;, &lt;code&gt;COMMIT&lt;/code&gt;, &lt;code&gt;ROLLBACK&lt;/code&gt;, &lt;code&gt;SAVEPOINT&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;조인 조회&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;INNER JOIN&lt;/code&gt;, &lt;code&gt;LEFT JOIN&lt;/code&gt;, &lt;code&gt;RIGHT JOIN&lt;/code&gt;, &lt;code&gt;FULL OUTER JOIN&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;SQLite의 핵심 철학은 &amp;ldquo;작고 아름답게&amp;quot;입니다.&lt;/strong&gt; 일상적으로 필요한 대부분의 SQL 기능을 지원하면서도 극도로 가벼운 상태를 유지합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;!--adsense--&gt;
&lt;h2 id=&#34;sqlite의-한계는-어디까지일까요&#34;&gt;SQLite의 한계는 어디까지일까요?
&lt;/h2&gt;&lt;p&gt;가벼움에는 대가가 따릅니다. 만약 &lt;code&gt;SQLite&lt;/code&gt;를 &lt;strong&gt;결제 카운터가 단 하나뿐인 동네 잡화점&lt;/strong&gt;에 비유한다면, &lt;code&gt;PostgreSQL&lt;/code&gt;은 &lt;strong&gt;50개의 계산대를 갖춘 코스트코&lt;/strong&gt;와 같습니다.&lt;/p&gt;
&lt;h3 id=&#34;1-쓰기-병목-현상&#34;&gt;1. 쓰기 병목 현상
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;SQLite&lt;/code&gt;는 데이터를 쓸 때 데이터베이스 파일 전체를 &lt;strong&gt;잠급니다&lt;/strong&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;화장실이 단 하나뿐인 식당&lt;/strong&gt;을 상상해 보세요. 100명이 밖에서 동시에 메뉴판을 볼 수는 있지만(읽기), 단 1명이라도 들어가서 문을 잠그면(쓰기) 다른 모든 사람들은 줄을 서서 기다릴 수밖에 없습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;**WAL 모드(Write-Ahead Logging)**를 활성화하면 동시 읽기/쓰기 성능이 향상되지만, 본질적으로 여러 스레드가 서로 다른 데이터 행에 동시에 쓰는 것은 여전히 불가능합니다.&lt;/p&gt;
&lt;h3 id=&#34;2-여러-서버에-걸친-공유-불가&#34;&gt;2. 여러 서버에 걸친 공유 불가
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;SQLite&lt;/code&gt;는 본질적으로 물리적인 하나의 파일입니다. 시스템을 여러 대의 서버에 배포하는 경우(수평 확장), 이 서버들이 &lt;strong&gt;동일한 파일을 안전하게 공유할 수 없습니다&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&#34;3-세분화된-권한-관리의-부재&#34;&gt;3. 세분화된 권한 관리의 부재
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;SQLite&lt;/code&gt;에는 &amp;lsquo;사용자 계정&amp;rsquo;이라는 개념이 없습니다. 운영체제 수준에서 해당 &lt;code&gt;.db&lt;/code&gt; 파일에 접근해 읽을 수만 있다면, 누구나 모든 데이터를 확인하고 수정할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;엄격한 개인정보 감사가 요구되는 비즈니스 시스템&lt;/strong&gt;에서는 이를 받아들일 수 없습니다.&lt;/p&gt;
&lt;h2 id=&#34;sqlite-vs-postgresql-기술-선정-의사결정&#34;&gt;SQLite vs. PostgreSQL: 기술 선정 의사결정
&lt;/h2&gt;&lt;p&gt;도구에는 좋고 나쁨이 없으며, 오직 적합한가 그렇지 않은가만 존재합니다. 결정을 돕기 위한 궁극의 체크리스트를 확인해 보세요:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;질문&lt;/th&gt;
          &lt;th&gt;답변이 &amp;ldquo;예&amp;rdquo; → SQLite 선택&lt;/th&gt;
          &lt;th&gt;답변이 &amp;ldquo;아니오&amp;rdquo; → 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;예&lt;/td&gt;
          &lt;td&gt;아니오 (수평 확장, 여러 장비가 필요함)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;시스템 동작이 대부분 읽기 위주이며, 고주파 동시 쓰기가 없나요?&lt;/td&gt;
          &lt;td&gt;예&lt;/td&gt;
          &lt;td&gt;아니오 (동시에 여러 사용자가 쓰기를 경쟁함)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;세분화된 계정 권한이나 고급 인덱스 기능이 필요 없나요?&lt;/td&gt;
          &lt;td&gt;예&lt;/td&gt;
          &lt;td&gt;아니오 (고급 기능에 깊이 의존함)&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;보다 구체적인 시나리오 대비:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;시나리오&lt;/th&gt;
          &lt;th&gt;권장 선택&lt;/th&gt;
          &lt;th&gt;이유&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;데스크톱 소프트웨어, 모바일 앱, IoT 기기&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;SQLite&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;데이터가 기기를 따라 이동하며, 설치가 필요 없음&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;개인 블로그, 홍보용 쇼케이스 웹사이트&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;SQLite&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;읽기가 많고 쓰기가 적어 서버 유지 관리 비용 절감 가능&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;신속한 프로토타입 개발, 데모&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;SQLite&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;파일 하나만 만들면 바로 시작 가능&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;커뮤니티 포럼, 이커머스 플랫폼&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;PostgreSQL&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;높은 동시 쓰기가 발생하며, 행 수준 잠금이 필요함&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;여러 서버에 분산 배포&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;PostgreSQL&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;여러 장비 간에 데이터 소스를 공유해야 함&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;의료, 금융 등 민감한 시스템&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;PostgreSQL&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;역할 기반의 엄격한 권한 제어가 필요함&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;!--adsense--&gt;
&lt;h2 id=&#34;귀하의-데이터베이스는-주머니-속-수첩인가요-아니면-중앙-교환기인가요&#34;&gt;귀하의 데이터베이스는 주머니 속 수첩인가요, 아니면 중앙 교환기인가요?
&lt;/h2&gt;&lt;p&gt;데이터가 **&amp;ldquo;단일 장비, 정적, 단일 소유&amp;rdquo;**인 경우, &lt;code&gt;SQLite&lt;/code&gt;를 선택하여 극도의 가벼움과 자유를 누리십시오.&lt;/p&gt;
&lt;p&gt;데이터가 **&amp;ldquo;클라우드, 동적, 고도의 상호작용&amp;rdquo;**인 경우, &lt;code&gt;PostgreSQL&lt;/code&gt;에 맡기십시오.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;SQLite&lt;/code&gt;는 **&amp;ldquo;애플리케이션의 내부 컴포넌트&amp;rdquo;**라는 철학을 가지고 있으며, &lt;code&gt;PostgreSQL&lt;/code&gt;은 **&amp;ldquo;시스템 아키텍처의 독립된 중심&amp;rdquo;**으로 위치합니다.&lt;/p&gt;
&lt;p&gt;다음번에 아키텍처 의사결정을 내릴 때, 성급하게 &lt;code&gt;PostgreSQL&lt;/code&gt;을 꺼내 들기 전에 먼저 스스로에게 질문해 보십시오:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&amp;ldquo;내 데이터베이스는 주머니 속 수첩인가요, 아니면 중앙 교환기인가요?&amp;rdquo;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;답이 명확해지면 선택도 자연스럽게 확실해집니다.&lt;/p&gt;
&lt;h2 id=&#34;reference&#34;&gt;Reference
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://sqlite.org/lang.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Query Language Understood by SQLite&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://sqlite.org/docs.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;SQLite Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.sqlitetutorial.net/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;SQLite Tutorial - An Easy Way to Master SQLite Fast&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://en.wikipedia.org/wiki/SQLite&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;SQLite - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        </item>
        
    </channel>
</rss>
