뷰와 프로시저 (트리거, 저장 프로시저, 캡슐화)
데이터베이스는 단순히 데이터를 담는 창고가 아니라, 그 안에서 로직을 실행하는 작은 실행 환경이기도 하다. 복잡한 조인 쿼리를 매번 다시 쓰지 않도록 이름 붙여 저장하는 뷰, 여러 SQL 문을 묶어 함수처럼 호출하는 저장 프로시저, 그리고 특정 사건이 일어날 때 자동으로 작동하는 트리거 — 이 세 가지는 애플리케이션 코드로 흩어질 로직을 데이터베이스 안에 캡슐화하는 도구다. 본 글은 뷰의 추상화와 보안 효과, 저장 프로시저의 성능·재사용 이점, 그리고 트리거의 자동화와 그 위험성을 세심하게 정리한다(출처: 위키백과 — View (SQL)). 제가 데이터베이스 수업에서 같은 5단 조인 쿼리를 여러 화면에서 복사해 쓰다가 컬럼 하나가 바뀌자 모든 쿼리를 찾아 고치느라 진땀을 뺀 적이 있는데, 그때 "뷰 하나로 묶었으면 한 곳만 고치면 됐다"는 교훈을 뼈저리게 얻었다.

뷰 — 가상 테이블로 추상화하다
뷰(View)는 하나 이상의 테이블에서 유도된 가상의 테이블이다. 실제 데이터를 따로 저장하지 않고, 미리 정의해 둔 SELECT 쿼리에 이름을 붙인 것이다. 사용자가 뷰를 조회하면 데이터베이스가 그 정의 쿼리를 실행해 결과를 보여 준다. 즉 뷰는 "저장된 쿼리"에 가깝다.
-- 복잡한 조인을 뷰로 캡슐화
CREATE VIEW 학생_성적_요약 AS
SELECT s.학번, s.이름, d.학과명,
AVG(e.점수) AS 평균점수
FROM 학생 s
JOIN 학과 d ON s.학과 = d.학과명
JOIN 수강 e ON s.학번 = e.학번
GROUP BY s.학번, s.이름, d.학과명;
-- 이후로는 단순 조회처럼 사용
SELECT * FROM 학생_성적_요약 WHERE 평균점수 >= 90;
뷰가 주는 이점은 세 가지다. 첫째, 추상화다. 복잡한 조인·집계를 한 번 정의해 두면 사용자는 단순 테이블처럼 조회할 수 있고, 정의가 바뀌어도 뷰 하나만 고치면 된다. 둘째, 보안이다. 민감한 컬럼(주민번호·급여 등)을 제외한 뷰만 특정 사용자에게 권한을 주면, 원본 테이블 전체는 숨기면서 필요한 부분만 노출할 수 있다. 셋째, 논리적 독립성으로, 원본 테이블 구조가 바뀌어도 뷰가 같은 결과를 제공하면 응용 프로그램은 영향받지 않는다.
다만 뷰에는 한계가 있다. 일반 뷰는 데이터를 저장하지 않으므로 조회할 때마다 정의 쿼리를 다시 실행해 무거운 집계라면 느릴 수 있다. 이를 보완한 것이 구체화된 뷰(materialized view)로, 결과를 실제로 저장해 두고 주기적으로 갱신한다. 빠른 조회를 얻는 대신 데이터가 최신이 아닐 수 있는 트레이드오프가 있다. 또한 집계·조인이 들어간 뷰는 대개 INSERT·UPDATE가 불가능하다는 제약도 알아 두어야 한다.
저장 프로시저 — 로직을 데이터베이스에 담다
저장 프로시저(Stored Procedure)는 여러 SQL 문과 제어 흐름(조건문·반복문)을 하나의 이름 붙은 단위로 데이터베이스에 저장해 두고, 호출해 실행하는 프로그램이다. 애플리케이션이 여러 SQL을 한 줄씩 보내는 대신, 프로시저 하나를 호출하면 그 안의 모든 로직이 데이터베이스 서버에서 실행된다.
-- 점수 등록과 동시에 평균 갱신 (의사 코드, 방언마다 문법 차이)
CREATE PROCEDURE 점수_등록(IN p_학번 INT, IN p_과목 VARCHAR(50), IN p_점수 INT)
BEGIN
INSERT INTO 수강(학번, 과목, 점수) VALUES (p_학번, p_과목, p_점수);
UPDATE 학생_통계
SET 총점 = 총점 + p_점수, 과목수 = 과목수 + 1
WHERE 학번 = p_학번;
END;
CALL 점수_등록(20231234, '데이터베이스', 95);
저장 프로시저의 이점은 명확하다. 첫째, 네트워크 비용 절감이다. 여러 SQL을 한 번의 호출로 묶으므로 애플리케이션과 데이터베이스 사이의 왕복이 줄어든다. 둘째, 성능이다. 프로시저는 한 번 컴파일되어 실행 계획이 캐시되므로 반복 호출 시 빠르다. 셋째, 재사용과 일관성으로, 여러 애플리케이션이 같은 비즈니스 로직을 공유하면서 SQL 인젝션 위험도 줄인다.
다만 저장 프로시저의 단점도 분명하다. 로직이 데이터베이스 안에 들어가면 버전 관리·테스트·디버깅이 애플리케이션 코드보다 어렵고, 데이터베이스 방언(Oracle PL/SQL, SQL Server T-SQL, PostgreSQL PL/pgSQL 등)마다 문법이 달라 다른 DBMS로의 이전이 힘들다. 그래서 최근 마이크로서비스 흐름에서는 비즈니스 로직을 애플리케이션 계층에 두고 데이터베이스는 데이터 저장에 집중시키는 설계도 널리 쓰인다. "로직을 어디에 둘 것인가"는 정답이 없는 설계 판단이다.
트리거 — 사건에 반응하는 자동 실행
트리거(Trigger)는 특정 테이블에 INSERT·UPDATE·DELETE 같은 사건이 발생할 때 자동으로 실행되는 특수한 저장 프로시저다. 사용자가 직접 호출하는 것이 아니라, 정의된 사건이 일어나면 데이터베이스가 알아서 실행한다는 점이 핵심 차이다.
-- 점수가 변경되면 변경 이력을 자동 기록
CREATE TRIGGER 점수_변경_로그
AFTER UPDATE ON 수강
FOR EACH ROW
BEGIN
INSERT INTO 점수_이력(학번, 과목, 이전점수, 새점수, 변경시각)
VALUES (OLD.학번, OLD.과목, OLD.점수, NEW.점수, NOW());
END;
트리거는 감사 로그 기록, 데이터 무결성의 자동 강제, 파생 데이터의 자동 갱신 같은 곳에서 강력하다. 위 예시처럼 점수가 바뀔 때마다 이력을 자동으로 남기면, 애플리케이션이 깜빡하더라도 데이터베이스 차원에서 추적이 보장된다.
| 객체 | 실행 방식 | 데이터 저장 | 주 용도 |
|---|---|---|---|
| 뷰 | 조회 시 정의 쿼리 실행 | 안 함(구체화 뷰는 함) | 추상화·보안 |
| 저장 프로시저 | 명시적 CALL 호출 | — | 로직 캡슐화·성능 |
| 트리거 | 사건 발생 시 자동 | — | 자동화·무결성·감사 |
다만 트리거는 양날의 검이다. 자동으로 작동하는 만큼 눈에 보이지 않는 부작용을 만든다. 단순한 UPDATE 한 줄이 트리거를 통해 다른 테이블 수십 곳을 연쇄 변경하면, 개발자는 왜 데이터가 바뀌었는지 추적하기 어렵다. 트리거가 또 다른 트리거를 부르는 연쇄 트리거는 디버깅을 악몽으로 만들고, 성능 저하의 숨은 원인이 되기도 한다. 흔한 오해 하나를 짚으면 "트리거로 모든 무결성을 자동화하면 편하다"는 통념인데, 실무에서는 트리거를 남용하면 시스템 동작이 불투명해져 오히려 유지보수가 어려워진다는 반론이 강하다. 그래서 많은 팀이 트리거는 감사·로그처럼 부작용이 명확하고 제한적인 용도로만 쓰고, 핵심 비즈니스 로직은 명시적인 코드로 두는 원칙을 따른다. 시험에서는 세 객체의 정의와 차이가 단골 출제되고, 실무에서는 "자동화의 편리함과 추적 가능성"의 균형을 어디서 잡을지가 진짜 판단의 영역이다.
메타 디스크립션: 복잡한 쿼리를 추상화하는 뷰, 로직을 데이터베이스에 캡슐화하는 저장 프로시저, 사건에 자동 반응하는 트리거의 동작 원리와 이점을 SQL 예시로 풀어내고, 트리거 남용의 위험과 로직을 어디에 둘지의 설계 판단까지 데이터베이스 입문자 관점에서 세심하게 정리합니다.