본문 바로가기
DB

뷰(view)

by 개발 이야기 2025. 9. 25.

뷰(View)를 사용하는 구체적인 이유

뷰를 사용하는 이유는 크게 단순성, 보안, 독립성 세 가지로 나눌 수 있습니다.

1. 쿼리의 단순화 및 재사용 

매우 복잡한 SELECT 문(여러 테이블을 JOIN하고, 특정 조건으로 필터링하며, 함수로 값을 계산하는 등)을 자주 사용해야 한다고 가정해 봅시다.

  • JOIN 사용 시: 이 복잡한 쿼리를 매번 다시 작성하거나 복사/붙여넣기 해야 합니다. 쿼리가 길어질수록 실수할 가능성이 커지고 유지보수가 어렵습니다.
  • 뷰 사용 시: 복잡한 쿼리를 한 번만 뷰로 만들어 저장해두면, 그 다음부터는 마치 하나의 테이블처럼 간단하게 SELECT * FROM my_complex_view; 와 같이 조회할 수 있습니다. 쿼리가 매우 간결해지고 재사용성이 극대화됩니다.

2. 보안 강화 🔒

뷰는 데이터베이스 보안의 중요한 도구입니다. 특정 사용자에게 테이블의 모든 데이터를 보여주고 싶지 않을 때 유용합니다.

  • 예시: employees 테이블에 직원의 이름, 부서, 연락처, 연봉 정보가 있다고 가정합시다.
  • 문제: 일반 사원에게는 다른 직원의 연봉 정보를 보여주면 안 됩니다.
  • 해결: 연봉 컬럼을 제외한 이름, 부서, 연락처만 보여주는 employee_public_info 라는 뷰를 만듭니다. 그리고 사용자에게는 원본 employees 테이블 접근 권한은 막고, 이 뷰에 대한 접근 권한만 부여합니다. 이렇게 하면 사용자는 뷰를 통해 허용된 데이터만 안전하게 조회할 수 있습니다.

3. 논리적 데이터 독립성 🧩

데이터베이스의 구조는 시간이 지나면서 바뀔 수 있습니다. 예를 들어, 하나의 큰 테이블을 성능상의 이유로 두 개의 작은 테이블로 쪼갤 수 있습니다.

  • 문제: 테이블 구조가 바뀌면, 이 테이블을 사용하는 모든 애플리케이션의 쿼리를 수정해야 하는 큰 작업이 발생합니다.
  • 해결: 애플리케이션이 테이블을 직접 조회하는 대신 뷰를 보도록 만듭니다. 나중에 테이블 구조가 바뀌더라도, 뷰의 정의만 수정하여 이전과 동일한 형태의 데이터를 보여주도록 할 수 있습니다. 이렇게 하면 애플리케이션 코드를 전혀 건드리지 않고도 데이터베이스 구조를 유연하게 변경할 수 있습니다. 즉, 뷰가 중간 다리 역할을 하여 애플리케이션과 실제 테이블을 분리시켜 줍니다.

뷰의 탄생 배경과 발전

뷰는 관계형 데이터베이스 모델이 처음 제안될 때부터 포함된 핵심 개념 중 하나입니다. 데이터베이스의 물리적 구조(데이터가 디스크에 저장되는 방식)와 사용자가 데이터를 보는 논리적 구조를 분리하려는 아이디어에서 출발했습니다. 이를 통해 데이터베이스 관리자는 내부 구조를 자유롭게 최적화하면서도, 사용자에게는 일관된 데이터 구조를 제공할 수 있게 된 것이죠.

초기의 뷰는 단순히 저장된 쿼리 역할만 했지만, 이후에는 성능 향상을 위해 뷰의 데이터를 미리 계산해서 저장해두는 실체화된 뷰(Materialized View) 와 같은 발전된 형태도 등장했습니다.

정리

구분 JOIN 뷰(View)
정의 쿼리 실행 시점에 테이블을 연결하는 연산(Operation) SELECT 쿼리를 저장해 둔 가상의 테이블, 데이터베이스 객체(Object)
저장 실행 결과를 저장하지 않음 쿼리 정의(레시피)만 저장함 (데이터 자체는 저장 X)
주 목적 일회성 데이터 조합 복잡한 쿼리의 단순화, 보안, 논리적 데이터 독립성 확보
사용 FROM table1 JOIN table2 ON ... CREATE VIEW ... AS SELECT ...; SELECT * FROM view_name;

결론적으로, JOIN은 데이터를 조합하는 '행위' 그 자체이고, 뷰는 그 행위를 편리하게 재사용하고, 보안을 적용하며, 유연성을 높이기 위해 '저장해 둔 이름'이라고 할 수 있습니다. 단순한 일회성 조회라면 JOIN으로 충분하지만, 반복적이고 구조적인 작업에서는 뷰가 훨씬 강력한 도구가 됩니다.


1. 실체화된 뷰 (Materialized View) 📊

일반 뷰가 '레시피'라면, 실체화된 뷰는 그 레시피로 미리 만들어 둔 '요리' 그 자체입니다.

개념 및 특징

실체화된 뷰(Materialized View, M-View)는 일반 뷰처럼 SELECT 쿼리를 기반으로 정의되지만, 그 결과 데이터를 디스크에 물리적인 테이블 형태로 저장하는 특별한 데이터베이스 객체입니다. 일반 뷰가 호출될 때마다 쿼리를 실행하는 것과 달리, 실체화된 뷰는 저장된 데이터를 바로 반환하므로 조회 속도가 매우 빠릅니다.

  • 데이터 저장: 쿼리 결과셋을 실제 테이블처럼 저장하여 디스크 공간을 차지합니다.
  • 빠른 조회 속도: 복잡한 조인이나 집계(Aggregation) 쿼리를 매번 수행할 필요 없이, 미리 계산된 결과를 바로 읽어오므로 응답 속도가 비약적으로 향상됩니다.
  • 데이터 비실시간성: 원본 테이블 데이터가 변경되어도 실체화된 뷰는 자동으로 즉시 업데이트되지 않습니다. 따라서 데이터가 최신이 아닐 수 있다는 단점이 존재합니다.
  • 새로고침(Refresh) 필요: 최신 데이터를 유지하려면 '새로고침'이라는 별도의 과정을 주기적으로 수행해야 합니다. (예: 특정 시간마다, 원본 데이터 변경 시 등)

사용하는 구체적인 이유

조회 성능을 극적으로 개선해야 하는 상황에서 사용됩니다.

  1. 데이터 웨어하우스(DW) 및 BI 대시보드: 수억 건의 데이터를 조인하고, SUM, AVG 등 무거운 집계 연산을 통해 통계 리포트를 만들어야 할 때 유용합니다. 사용자가 리포트를 조회할 때마다 쿼리를 실행하면 몇 분씩 걸릴 수 있지만, 실체화된 뷰를 매일 밤 한 번씩 생성해두면 사용자는 1초 만에 결과를 볼 수 있습니다.
  2. 원격 데이터 복제: 원격 데이터베이스의 데이터를 로컬로 가져와 자주 조회해야 할 때, 매번 네트워크를 통해 원격지의 데이터를 조회하는 것은 매우 비효율적입니다. 이때 실체화된 뷰를 사용해 데이터를 로컬에 복사해두고 사용하면 성능이 크게 향상됩니다.
  3. 데이터의 스냅샷 보존: 특정 시점의 데이터를 사진 찍듯이 보존해야 할 때 사용됩니다. 매일의 판매 실적 데이터를 실체화된 뷰로 저장하면, 다음날 원본 데이터가 변경되어도 어제의 데이터를 그대로 분석할 수 있습니다.

탄생 배경과 발전

초기 관계형 데이터베이스에서 뷰는 논리적 독립성을 제공했지만, 대용량 데이터 분석 환경(OLAP)이 대두되면서 복잡한 쿼리의 성능 문제가 심각해졌습니다. 매번 수십 분씩 걸리는 리포팅 쿼리는 비즈니스에 큰 장애물이었습니다. 이러한 문제를 해결하기 위해, 자주 사용되는 쿼리의 결과를 미리 계산하여 저장해두자는 아이디어에서 실체화된 뷰가 탄생했습니다. 처음에는 데이터 웨어하우스 시스템에서 주로 사용되다가, 현재는 많은 상용 데이터베이스와 오픈소스 데이터베이스에서 지원하는 표준적인 기능으로 자리 잡았습니다.


2. 업데이트 가능한 뷰 (Updatable View) ✍️

'창문'에 비유했던 뷰 중에서, **열어서 물건을 넣거나 뺄 수 있는 '열리는 창문'**이라고 할 수 있습니다.

개념 및 특징

업데이트 가능한 뷰는 SELECT 뿐만 아니라 INSERT, UPDATE, DELETE 같은 DML(Data Manipulation Language) 구문을 실행할 수 있는 뷰입니다. 이 뷰에 데이터를 수정하면, 연결된 원본 테이블의 데이터가 실제로 변경됩니다. 하지만 모든 뷰가 업데이트 가능한 것은 아니며, 엄격한 조건을 만족해야 합니다.

업데이트 가능하기 위한 핵심 조건

데이터베이스마다 약간의 차이는 있지만, 공통적인 핵심 조건은 뷰의 행과 원본 테이블의 행이 1:1로 명확하게 매핑되어야 한다는 것입니다. 즉, 뷰의 한 행을 바꿨을 때 원본 테이블의 어떤 행을 바꿔야 할지 모호함이 없어야 합니다.

  • 단일 테이블 기반: 뷰가 하나의 테이블에서만 파생되어야 합니다. (두 개 이상의 테이블을 조인하면, 뷰의 한 행을 삭제할 때 어느 테이블의 행을 삭제해야 할지 모호해지므로 불가)
  • 집계 함수 미사용: GROUP BY, COUNT(), SUM() 등 데이터를 그룹화하거나 요약하는 집계 함수를 사용하지 않아야 합니다. (요약된 결과는 실제 데이터 행과 1:1 매칭이 안됨)
  • DISTINCT 미사용: 중복을 제거한 결과는 원본의 어떤 행을 가리키는지 불분명합니다.
  • NOT NULL 제약 조건: 원본 테이블에 NOT NULL이면서 기본값이 없는 컬럼이 뷰에 포함되지 않으면, 해당 뷰를 통해 INSERT를 할 수 없습니다. (필수 값을 입력할 방법이 없기 때문)

사용하는 구체적인 이유

  1. 보안 강화 및 추상화: 사용자가 테이블 전체에 접근하지 못하게 하면서, 특정 컬럼이나 행에 대해서만 수정 권한을 부여하고 싶을 때 사용합니다. 예를 들어, 인사팀 사원이 자신의 프로필에서 '연락처'만 수정할 수 있도록, employee_id와 phone_number 컬럼만 포함하는 업데이트 가능한 뷰를 제공할 수 있습니다.
  2. 애플리케이션 로직 단순화: 애플리케이션 개발자에게 복잡한 테이블 구조를 숨기고, 마치 간단한 테이블 하나를 다루는 것처럼 데이터를 읽고 쓸 수 있는 인터페이스를 제공하여 개발을 단순화합니다.

3. 저장 프로시저 (Stored Procedure) vs. 뷰 (View) 

뷰가 '메뉴판'이라면, 저장 프로시저는 **요청에 따라 특정 요리를 만들어내는 '주방장'**과 같습니다.

개념 및 특징 비교

구분 뷰 (View) 저장 프로시저 (Stored Procedure)
핵심 목적 데이터 **조회(SELECT)**의 단순화 및 보안 데이터베이스에서 수행되는 일련의 작업(로직)을 캡슐화하여 실행
비유 메뉴판 (미리 정해진 데이터 조합을 보여줌) 주방장 (요청에 따라 복잡한 요리 과정을 수행)
반환 값 항상 테이블 형태의 결과셋 (Result Set) 결과셋, 단일 값, 또는 반환 값 없음 (작업만 수행) 등 다양
파라미터 받을 수 없음 받을 수 있음 (입력, 출력 파라미터 모두 가능)
주요 로직 SELECT 문 하나로 구성 IF/ELSE, LOOP 등 조건/반복문, 트랜잭션 처리, 변수 선언 등 프로그래밍 로직 수행 가능
수행 가능 작업 주로 SELECT (제한적으로 UPDATE, INSERT, DELETE) SELECT, UPDATE, INSERT, DELETE 등 모든 DML 및 DDL(테이블 생성 등) 수행 가능
호출 방법 SELECT ... FROM my_view; (테이블처럼 사용) EXEC my_procedure; 또는 CALL my_procedure(); (함수처럼 호출)

사용하는 구체적인 이유

  • 뷰가 유용할 때: 복잡한 SELECT 문을 숨기고, 특정 데이터만 보여주는 보안 창구가 필요할 때.
  • 저장 프로시저가 유용할 때:
    1. 성능 최적화: 여러 SQL 문을 한 번의 네트워크 요청으로 처리하여 부하를 줄입니다.
    2. 중앙화된 비즈니스 로직: '회원 가입'처럼 여러 테이블을 순차적으로 업데이트해야 하는 복잡한 로직을 데이터베이스에 저장해두고 여러 애플리케이션에서 일관되게 호출할 수 있습니다.
    3. 보안 강화: 사용자에게 테이블 직접 접근 권한 대신 프로시저 실행 권한만 부여하여, 정해진 로직을 통해서만 데이터를 조작하게 할 수 있습니다.

탄생 배경과 발전

뷰는 관계형 모델의 '논리적 독립성'을 위해 탄생했습니다. 반면, 저장 프로시저는 클라이언트-서버 구조가 보편화되면서 등장했습니다. 애플리케이션(클라이언트)과 데이터베이스(서버) 간의 네트워크 통신 비용을 줄이고, 여러 클라이언트가 공통으로 사용하는 비즈니스 로직을 서버 단에서 효율적으로 처리하기 위한 필요성에서 개발되었습니다.


4. CTE (Common Table Expression) 📜

복잡한 계산을 할 때 정답을 내기 전에 중간 결과를 따로 적어두는 **'연습장(Scratchpad)'**과 같습니다.

개념 및 특징

CTE는 WITH 키워드를 사용하여 정의하는, 하나의 SQL 문 내에서만 유효한 임시 결과셋입니다. 복잡한 쿼리를 여러 개의 논리적인 단위로 나누어 가독성을 높이고, 재사용할 수 있게 해줍니다.

  • 임시성: 해당 SELECT, INSERT, UPDATE, DELETE 문이 실행되는 동안에만 존재하고, 끝나면 바로 사라집니다. (뷰처럼 데이터베이스에 저장되지 않음)
  • 가독성 향상: 긴 쿼리를 단계별로 나누어 정의할 수 있어, 서브쿼리가 중첩된 복잡한 쿼리보다 이해하기 쉽습니다.
  • 재귀(Recursive) 쿼리 작성: CTE의 가장 강력한 기능 중 하나로, 자기 자신을 참조하여 계층적인 데이터를 쉽게 조회할 수 있습니다. (예: 조직도에서 특정 매니저 하위의 모든 직원 찾기)
  • 쿼리 내 재사용: 하나의 쿼리문 안에서 정의된 CTE를 여러 번 참조할 수 있습니다.

기본 구조:

WITH MyCTE AS (
    -- 이 부분이 연습장(임시 결과셋)
    SELECT ... FROM ...
)
-- 본 쿼리에서 연습장 결과를 사용
SELECT * FROM MyCTE;

사용하는 구체적인 이유

  1. 복잡한 쿼리 구조화: 여러 개의 서브쿼리가 얽혀 있는 쿼리를 WITH 절을 통해 순차적인 단계로 분리하여 코드의 가독성과 유지보수성을 높입니다.
  2. 계층형 데이터 조회: 부모-자식 관계를 가진 데이터(조직도, 부품 목록 등)를 재귀 CTE를 사용하여 간단하게 탐색할 수 있습니다.
  3. 쿼리 논리 명확화: 각 CTE에 의미 있는 이름을 부여함으로써, 쿼리의 각 부분이 어떤 역할을 하는지 명확하게 표현할 수 있습니다.

탄생 배경과 발전

과거에는 복잡한 쿼리를 작성할 때 서브쿼리나 임시 테이블을 사용해야 했습니다. 하지만 서브쿼리는 깊게 중첩될수록 가독성이 떨어졌고, 임시 테이블은 불필요한 오버헤드를 발생시켰습니다. 이러한 문제를 해결하고, 특히 재귀적 데이터 구조를 더 효율적으로 다루기 위해 SQL 표준(SQL:1999)에 CTE가 도입되었습니다. 현재는 대부분의 현대적인 데이터베이스에서 지원하는 필수 기능이 되었습니다.