데이터베이스를 다루다보면 복잡한 쿼리를 자주 사용하거나, 테이블의 특정 컬럼만 선택적으로 보여줘야 하는 상황이 발생함. 이런 상황에서 뷰(View)는 매우 유용한 도구가 되는데, 이번에는 뷰의 개념부터 실무 활용 방법까지 상세하게 정리하겠음

1. 뷰(View)란?

출처 : https://adjh54.tistory.com/256

뷰는 하나 이상의 테이블에서 유도된 가상 테이블임.

실제로 데이터를 저장하지 않고, 기존 테이블의 데이터를 특정한 방식으로 보여주는 창(Window) 역할을 함.

 

1.1 뷰의 특징

  • 저장된 쿼리문으로 구성되어 있음
  • 실제 데이터를 저장하지 않음
  • 원본 테이블의 데이터를 실시간으로 반영함
  • 복잡한 쿼리를 단순화할 수 있음

 

2. 뷰를 사용하는 이유와 장점

2.1 데이터 보안 강화

데이터 보안은 뷰를 사용하는데 가장 중요한 이유 중 하나임.

  • 민감한 데이터 숨기기
-- 직원 정보 중 급여, 주민번호 등 민감 정보를 제외한 뷰
CREATE VIEW employee_public AS
SELECT id, name, department, position, hire_date
FROM employees;
  • 행 수준 보안
-- 부서별로 해당 부서 데이터만 조회 가능한 뷰
CREATE VIEW department_data AS
SELECT *
FROM company_data
WHERE department_id = USER_DEPARTMENT_ID();
  • 열 수준 보안

특정 사용자나 그룹이 접근할 수 있는 컬럼 자체를 제한할 수 있음

 

2.2 쿼리 재사용성과 단순화

복잡한 쿼리를 뷰로 만들어두면 아래와 같은 이점이 있음

  • 코드 중복 감소
-- 복잡한 매출 분석 쿼리를 뷰로 생성
CREATE VIEW sales_analysis AS
SELECT 
    p.category,
    s.region,
    DATE_FORMAT(o.order_date, '%Y-%m') as month,
    COUNT(DISTINCT o.customer_id) as unique_customers,
    SUM(o.quantity) as total_quantity,
    SUM(o.quantity * p.price) as total_revenue,
    AVG(o.quantity * p.price) as avg_order_value
FROM orders o
JOIN products p ON o.product_id = p.id
JOIN stores s ON o.store_id = s.id
GROUP BY p.category, s.region, month;
  • 유지보수성 향상
    • 기본 쿼리가 변경되면 뷰를 사용하는 모든 곳에 자동으로 반영됨
    • 비즈니스 로직을 한 곳에서 관리할 수 있음

 

2.3 데이터 추상화와 독립성

데이터 추상화란?

복잡한 내부 구조를 감추고 단순한 인터페이스를 제공하는 것

-- 원래 데이터베이스에는 이런 복잡한 테이블들이 있다고 가정해봄.
테이블1: customer_details (고객 기본정보)
- id
- first_name
- last_name
- birth_date
- gender

테이블2: customer_contacts (고객 연락처)
- customer_id
- email
- phone
- address

테이블3: customer_preferences (고객 선호도)
- customer_id
- preferred_contact_method
- marketing_consent

이 세 테이블의 정보가 필요할 때마다 매번 아래와 같은 복잡한 조인 쿼리를 작성해야 함.

SELECT 
    cd.id,
    cd.first_name,
    cd.last_name,
    cc.email,
    cc.phone,
    cp.preferred_contact_method
FROM customer_details cd
JOIN customer_contacts cc ON cd.id = cc.customer_id
JOIN customer_preferences cp ON cd.id = cp.customer_id;

BUT, 뷰를 사용하면 아래와 같이 단순화할 수 있다!!

CREATE VIEW customer_info AS
SELECT 
    cd.id,
    cd.first_name,
    cd.last_name,
    cc.email,
    cc.phone,
    cp.preferred_contact_method
FROM customer_details cd
JOIN customer_contacts cc ON cd.id = cc.customer_id
JOIN customer_preferences cp ON cd.id = cp.customer_id;

-- 이제 개발자는 이렇게 간단하게 사용할 수 있음
SELECT * FROM customer_info;

 

데이터 독립성이란?

데이터베이스의 내부 구조가 변경되어도 응용 프로그램은 영향을 받지 않도록 하는 것

-- 처음에는 이런 구조였다고 가정함
CREATE TABLE customers (
    id INT,
    full_name VARCHAR(100),
    email VARCHAR(100)
);

-- 이를 사용하는 뷰는 아래와 같다.
CREATE VIEW customer_contact AS
SELECT id, full_name, email
FROM customers;

그리고 추후 회사 정책이 바뀜에 따라 테이블 구조를 아래와 같이 변경함

CREATE TABLE customers_new (
    customer_id INT,  -- id에서 customer_id로 변경
    first_name VARCHAR(50),  -- full_name을 first_name과 last_name으로 분리
    last_name VARCHAR(50),
    contact_email VARCHAR(100)  -- email을 contact_email로 변경
);

이렇게 변경된 테이블 구조와 컬럼에 따라 원래는 모든 쿼리를 바꿔줘야하지만, 뷰를 사용한다면 아래와 같이 뷰만 수정하면 됨.

CREATE OR REPLACE VIEW customer_contact AS
SELECT 
    customer_id as id,
    CONCAT(first_name, ' ', last_name) as full_name,
    contact_email as email
FROM customers_new;

=> 뷰가 중간에서 새로운 구조를 이전 구조처럼 보이게 변환해주기 때문임

 

  • 레거시 시스템 지원 가능
레거시 시스템 지원이 필요한 상황

1) 시스템 마이그레이션 중, 오래된 주문 시스템 => 새로운 시스템으로 전환하는 상황

-- 레거시 시스템의 주문 테이블 구조
OLD_ORDERS
- order_id (숫자)
- status (1: 진행중, 2: 완료, 3: 취소)
- order_date (YYYYMMDD 형식의 문자열)
- amount (숫자)

-- 새로운 시스템의 주문 테이블 구조
NEW_ORDERS
- order_uuid (UUID 형식)
- status (PENDING, COMPLETED, CANCELLED)
- created_at (TIMESTAMP)
- total_price (소수점 포함 숫자)

이 때 아래와 같이 레거시 시스템을 위한 뷰를 만들어서 호환성을 유지할 수 있다.

CREATE VIEW legacy_orders AS
SELECT 
    CAST(SUBSTRING(order_uuid, 1, 8) AS INTEGER) as order_id,
    CASE 
        WHEN status = 'PENDING' THEN 1
        WHEN status = 'COMPLETED' THEN 2
        WHEN status = 'CANCELLED' THEN 3
    END as status,
    DATE_FORMAT(created_at, '%Y%m%d') as order_date,
    FLOOR(total_price) as amount
FROM NEW_ORDERS;

2) 외부 시스템 연동, 오래된 회계 시스템이 특정 형식의 데이터만 처리

-- 현재 급여 시스템
EMPLOYEE_SALARIES
- employee_id (UUID)
- base_salary (decimal)
- bonus (decimal)
- deductions (decimal)
- effective_date (timestamp)

-- 레거시 회계 시스템을 위한 뷰
CREATE VIEW legacy_payroll AS
SELECT 
    LPAD(CAST(ROW_NUMBER() OVER (ORDER BY employee_id) AS CHAR(6)), 6, '0') as emp_no,
    ROUND(base_salary + bonus - deductions, 0) as total_salary,
    DATE_FORMAT(effective_date, '%Y%m') as pay_period
FROM EMPLOYEE_SALARIES;

 

2.4 복잡한 계산과 통계의 캡슐화

자주 사용되는 복잡한 계산이나 통계를 뷰로 만들어 재사용 가능

-- 제품별 다양한 통계 정보를 제공하는 뷰
CREATE VIEW product_statistics AS
SELECT 
    p.product_id,
    p.product_name,
    COUNT(o.order_id) as total_orders,
    SUM(o.quantity) as total_quantity,
    AVG(o.quantity) as avg_quantity_per_order,
    MIN(o.order_date) as first_order_date,
    MAX(o.order_date) as last_order_date,
    DATEDIFF(MAX(o.order_date), MIN(o.order_date)) as days_between_orders
FROM products p
LEFT JOIN orders o ON p.product_id = o.product_id
GROUP BY p.product_id, p.product_name;

2.5 데이터 품질과 일관성 유지

-- 유효한 주문 데이터만 제공하는 뷰
CREATE VIEW valid_orders AS
SELECT *
FROM orders
WHERE 
    order_date IS NOT NULL
    AND customer_id IS NOT NULL
    AND amount > 0
    AND status IN ('PENDING', 'COMPLETED', 'SHIPPED')
WITH CHECK OPTION;

 

이와 같이 뷰는 단순히 데이터를 보여주는 용도 X

데이터베이스 설계의 핵심적인 부분을 차지함. 

특히 보안, 재사용성, 데이터 추상화 측면에서 제공하는 이점이 매우 큼.

또한 대규모 시스템에서는 뷰를 통한 데이터 접근 계층을 구축함으로써, 더 안정적이고 유지보수가 용이한 시스템을 구축할 수 있음.