Menu

SQLite 뷰(View) 완벽 정리: CREATE VIEW부터 INSTEAD OF까지

SQLite에서 뷰(View)를 만들고 활용하는 방법을 정리합니다. 복잡한 쿼리를 가상 테이블로 저장하는 방식, 임시 뷰 활용법, 그리고 뷰가 기본적으로 읽기 전용인 이유까지 살펴봅니다.

이 페이지에는 실행 가능한 에디터가 있습니다 — 편집하고 실행하면 결과를 바로 볼 수 있습니다.

뷰는 저장된 쿼리다

sqlite 뷰는 이름을 붙여 둔 SELECT 문이라고 보면 된다. 한 번 만들어 두면 테이블처럼 조회할 수 있지만, 실제로 데이터가 저장되는 건 아니다. 뷰를 읽을 때마다 SQLite는 그 안의 쿼리를 매번 새로 실행한다.

paid_orders는 보기에도, 쓰임새도 일반 테이블과 똑같습니다. 컬럼이 있고, SELECT로 조회할 수 있고, 조인도 됩니다. 하지만 내부적으로는 모든 쿼리가 원래의 WHERE status = 'paid' 필터로 풀려서 실행됩니다.

핵심 개념은 이겁니다. sqlite 뷰는 쿼리에 붙여 둔 별칭이라는 것.

sqlite 뷰가 유용한 이유

가장 큰 장점은 이름을 붙일 수 있다는 점입니다. 길고 복잡한 쿼리에 짧고 명확한 이름을 달아 두면, 나머지 코드가 훨씬 깔끔해집니다.

뷰가 없다면 모든 호출 코드가 직접 GROUP BY를 작성해야 하고, 그 과정에서 누군가는 필터를 잘못 걸 수도 있습니다. 뷰를 쓰면 집계 로직을 한 번만 정의해 두면 됩니다. 호출하는 쪽에서는 그냥 customer_totals를 조회하고, 필요한 추가 필터만 얹으면 끝입니다.

뷰는 일종의 권한 경계 역할도 합니다. 예를 들어 password_hash 컬럼을 노출하면 안 되는 쿼리가 있다면, 그 컬럼을 제외한 나머지만 SELECT하는 뷰를 만들고 애플리케이션 코드에서는 그 뷰만 사용하도록 하면 됩니다.

CREATE VIEW 문법

전체 구문은 다음과 같습니다:

CREATE [TEMPORARY] VIEW [IF NOT EXISTS] view_name [(column_aliases)] AS
SELECT ...;

알아두면 유용한 몇 가지 포인트:

  • IF NOT EXISTS를 붙이면 같은 이름의 뷰가 이미 있을 때 에러 없이 조용히 넘어갑니다.
  • TEMPORARY(또는 TEMP)로 만든 임시 뷰는 연결이 끊기는 순간 함께 사라집니다.
  • 괄호 안에 컬럼 별칭을 지정하면 내부 SELECT 문을 손대지 않고도 뷰의 컬럼 이름을 새로 붙일 수 있습니다.

이렇게 하면 원본 테이블의 컬럼명을 그대로 두고도 뷰에서는 item, dollars 같은 더 친숙한 이름으로 노출할 수 있습니다.

뷰 교체 및 삭제하기 (sqlite drop view)

SQLite에는 CREATE OR REPLACE VIEWALTER VIEW 구문이 없습니다. 뷰 정의를 변경하려면 기존 뷰를 삭제한 뒤 다시 만들어야 합니다.

DROP VIEW IF EXISTS active_orders;가 안전한 방식입니다. 해당 뷰가 존재하지 않아도 에러를 내지 않거든요. 뷰를 삭제해도 원본 테이블에는 전혀 영향이 없습니다. 저장돼 있던 쿼리만 지우는 것뿐이니까요.

SQLite 임시 뷰 (TEMP VIEW)

TEMP VIEW는 현재 데이터베이스 연결에서만 유효한 뷰입니다. 연결이 끊기는 순간 함께 사라지죠. 정의를 영구적으로 남기고 싶지 않은 일회성 분석 작업에 딱 맞습니다.

임시 뷰를 활용하면 스키마에 정식으로 반영하지 않고도 쿼리 이름을 잠깐 덮어쓸 수 있어서, 이것저것 탐색하며 작업할 때 꽤 유용합니다.

sqlite 뷰는 기본적으로 읽기 전용입니다

가장 흔히 헷갈리는 부분이 바로 이겁니다. 뷰를 통해서는 INSERT, UPDATE, DELETE를 직접 실행할 수 없습니다.

sqlite> INSERT INTO paid_orders (customer, amount) VALUES ('Eve', 50);
Runtime error: cannot modify paid_orders because it is a view

해결책은 바로 INSTEAD OF 트리거입니다. 쓰기 작업이 들어왔을 때 그것을 대신 가로채서, 실제 기반 테이블에 대한 연산으로 바꿔주는 트리거를 작성하면 됩니다:

뷰는 여전히 뷰인 채로 두고, 대신 쓰기 작업이 흘러갈 통로를 만들어주는 방식이죠. 트리거에 대해서는 다음 페이지에서 제대로 다루겠습니다.

sqlite materialized view는 없다 — 직접 만들어 쓰기

어떤 데이터베이스는 뷰의 결과를 디스크에 캐시해 두고 필요할 때 갱신할 수 있게 해줍니다. 하지만 SQLite는 그런 기능을 제공하지 않습니다. 뷰를 조회할 때마다 매번 원본 쿼리가 다시 실행되죠. 대부분의 상황에서는 이 정도로 충분합니다 — SQLite는 빠르고, 쿼리 플래너도 똑똑하니까요. 다만 비용이 큰 집계 쿼리를 자주 호출해야 한다면, 실제 테이블을 하나 만들어 두고 직접 동기화하는 방식이 답입니다:

일정에 맞춰 캐시를 갱신하거나, orders 테이블에 트리거를 걸어서 최신 상태로 유지하는 방식이죠. 손이 좀 가긴 하지만, SQLite에서는 이 방법밖에 없습니다.

sqlite 뷰 목록 조회하기

뷰의 메타데이터는 테이블이나 인덱스와 마찬가지로 sqlite_master에 저장됩니다:

sql 컬럼을 보면 원래 작성했던 CREATE VIEW 문이 그대로 나옵니다. 뷰가 무슨 일을 하는지 까먹었을 때 요긴하죠. CLI에서는 .schema view_name을 쓰면 같은 내용을 더 깔끔하게 보여줍니다.

sqlite 뷰는 언제 써야 할까

다음과 같은 상황이라면 뷰를 만들어 두는 게 값어치를 합니다.

  • 제법 복잡한 쿼리가 세 군데 이상에서 반복된다. 이름을 한 번 붙여 두는 게 복붙보다 훨씬 낫습니다.
  • 애플리케이션의 일부 영역에 컬럼이나 행을 골라서 노출하고 싶을 때.
  • monthly_sales, active_users처럼 집계 결과 자체가 개념적으로 하나의 단위이고, 호출하는 쪽에서 그걸 하나의 명사처럼 다루게 하고 싶을 때.

반대로 뷰를 만들지 않는 게 나은 경우도 있습니다.

  • 그 쿼리가 딱 한 곳에서만 쓰인다면, 그냥 인라인으로 박아 두세요.
  • 성능이 중요한데 원본 쿼리가 무거운 경우 — 매번 읽을 때마다 그 비용을 치러야 합니다. 이럴 땐 차라리 실제 테이블로 캐싱하세요.
  • 뷰가 또 다른 뷰를 참조하고, 그게 또 다른 뷰를 참조하는 식의 구조. SQLite는 중첩 자체는 잘 처리하지만, 뷰가 서너 단계로 꼬리를 물면 디버깅할 때 실제 SQL이 어떻게 돌아가는지 따라가기가 정말 괴로워집니다.

다음 주제: 트리거

뷰와 트리거는 한 묶음으로 다뤄지는 일이 많습니다. 뷰를 쓰기 가능하게 만들어 주는 INSTEAD OF 트리거 패턴이 트리거가 존재하는 주요 이유 중 하나거든요. 물론 트리거는 그 자체로도 감사 로그(audit log), 연쇄 업데이트, 불변식(invariant) 강제 같은 데에 두루 쓸모가 있습니다. 이어지는 페이지에서 살펴봅시다.

자주 묻는 질문

SQLite에서 뷰(View)란 무엇인가요?

뷰는 한마디로 이름을 붙여 저장해 둔 SELECT 문입니다. 테이블처럼 조회할 수 있지만, 실제로 데이터를 가지고 있지는 않습니다. 뷰를 읽을 때마다 SQLite가 내부 쿼리를 다시 실행하죠. 복잡한 쿼리에 이름을 붙여 여기저기 재사용하거나, 호출하는 쪽에 보여주고 싶지 않은 컬럼을 숨길 때 유용합니다.

뷰를 통해 INSERT나 UPDATE를 할 수 있나요?

기본적으로는 안 됩니다. SQLite의 뷰는 읽기 전용이라서 뷰에 직접 INSERT, UPDATE, DELETE를 실행하면 에러가 납니다. 다만 INSTEAD OF 트리거를 붙이면 쓰기 작업을 가로채서 실제 테이블에 대한 작업으로 바꿔 처리할 수 있어요. 이렇게 하면 뷰도 쓰기 가능한 것처럼 동작합니다.

SQLite는 머티리얼라이즈드 뷰(Materialized View)를 지원하나요?

지원하지 않습니다. SQLite에는 일반 뷰(가상 뷰)만 있어서, 뷰를 읽을 때마다 매번 쿼리가 실행됩니다. 결과를 캐싱하고 싶다면 직접 일반 테이블을 만들어 주기적으로 갱신하거나, 원본 테이블에 트리거를 걸어 동기화 상태로 유지하는 방식으로 우회해야 합니다.

데이터베이스 안의 모든 뷰 목록을 어떻게 확인하나요?

sqlite_master를 조회하면 됩니다: SELECT name FROM sqlite_master WHERE type = 'view';. CLI에서는 .schemaCREATE VIEW 문 전체를 볼 수 있고, .tables를 치면 테이블과 함께 뷰 목록도 같이 나옵니다.

Coddy programming languages illustration

Coddy로 코딩 배우기

시작하기