Files
llm-wiki/raw/company-tech-blogs/vlad-mihalcea-postgresql-audit-logging-triggers.md
T

83 lines
7.2 KiB
Markdown

---
title: "PostgreSQL Audit Logging Using Triggers — Vlad Mihalcea"
source_type: company-tech-blog
url: https://vladmihalcea.com/postgresql-audit-logging-triggers/
archive_url:
related_branches: [feature-persistence-auditing-contract]
related_projects: []
tags: [company-tech-blog, ca-tmpl, persistence, postgresql, audit-logging]
created: 2026-06-10
---
# PostgreSQL Audit Logging Using Triggers — Vlad Mihalcea
> Layer: `raw/company-tech-blogs/` — 외부 기술 블로그의 **원문 발췌·출처 기록**.
> 검증된 요약은 `/ingest` 후 `wiki/concepts/` 에 별도 작성. 원본은 raw 에 영구 보관.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-persistence-auditing-contract]] | DB-level trigger auditing (standalone) 대안 기각: 애플리케이션 액터 ID 를 트리거로 넘기려면 매 DML 직전 `SET LOCAL var.logged_user` 세션 변수 주입 seam 이 필요하고, DB 타임소스가 앱 Clock bean 과 분리되어 감사 시각 통제권을 잃는다. |
## 출처 / Source
- 원본 URL: https://vladmihalcea.com/postgresql-audit-logging-triggers/
- 아카이브 URL: (없음)
- 저자 / 조직: Vlad Mihalcea (개인 전문가 기술 블로그)
- 발행일: (페이지에서 확인된 날짜 없음)
- 마지막 확인일: 2026-06-10
## 왜 저장했는지 / Why archived
PostgreSQL 트리거 기반 감사 로깅 구현 시 **애플리케이션이 매 DML 전에 세션 변수(`var.logged_user`)를 직접 주입해야 한다**는 사실을 원문 인용으로 확보하기 위해 보관. 이 seam 의 존재가 "DB-level trigger 단독 사용" 대안을 기각하는 근거가 된다.
## 핵심 인용 / Key quotes (verbatim, 5문장)
> [§ trigger function] "the `dml_created_by` column is set to the value of the `var.logged_user` PostgreSQL session variable, which was previously set by the application with the currently logged user"
> [§ trigger function / SQL] `current_setting('var.logged_user')`
> [§ SET LOCAL / connection pooling] "Notice that we used `SET LOCAL` as we want the variable to be removed after the current transaction is committed or rolled back. This is especially useful when using connection pooling."
> [§ trigger definition] "In order for the `book_audit_trigger_func` function to be executed after a `book` table record is inserted, updated or deleted, we have to define the following trigger:"
> [§ introduction] "In this article, we are going to see how we can implement an audit logging mechanism using PostgreSQL database triggers to store the CDC (Change Data Capture) records."
## Claims Extracted / 추출된 주장
> 이 자료가 **직접 말하는 것만** claim 으로 분리한다. 내 프로젝트에 적용한 결론은 여기 쓰지 않는다.
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| C1 | 트리거가 감사 행위자를 식별하려면 세션 변수 `var.logged_user` 를 읽고, 그 값은 **애플리케이션이 미리 설정**해야 한다 | [§ trigger function] "the `dml_created_by` column is set to the value of the `var.logged_user` PostgreSQL session variable, which was previously set by the application with the currently logged user" | `engineering-blog` | PostgreSQL AFTER 트리거가 DML 마다 실행되는 모든 환경 | 애플리케이션이 변수를 설정하지 않았을 때 트리거가 어떻게 동작하는지(에러/null) 는 이 글 단독으로 증명 안 됨 |
| C2 | 트리거 함수 내부에서 `current_setting('var.logged_user')` 호출로 사용자 값을 읽는다 | [§ trigger function / SQL] `current_setting('var.logged_user')` | `engineering-blog` | PostgreSQL PL/pgSQL 트리거 함수 | `current_setting` 의 두 번째 인자(`missing_ok`) 동작은 이 코드만으로 확정 불가 |
| C3 | `SET LOCAL` 을 사용하면 트랜잭션 커밋/롤백 후 변수가 자동 소멸하며, 이는 **커넥션 풀 환경에서 특히 유용**하다 | [§ SET LOCAL / connection pooling] "Notice that we used `SET LOCAL` as we want the variable to be removed after the current transaction is committed or rolled back. This is especially useful when using connection pooling." | `engineering-blog` | HikariCP 등 커넥션 풀을 사용하는 모든 Spring 앱 | `SET LOCAL` 이 실제로 커넥션 풀 재사용 시 변수를 100% 소멸시킴을 PostgreSQL 공식 문서 수준으로 보증하지 않음 — 추가 확인 필요 |
| C4 | 트리거는 `AFTER INSERT OR UPDATE OR DELETE` 로 정의된다(AFTER 트리거) | [§ trigger definition] "In order for the `book_audit_trigger_func` function to be executed after a `book` table record is inserted, updated or deleted, we have to define the following trigger:" | `engineering-blog` | PostgreSQL 감사 로그 트리거 정의 | BEFORE 트리거와의 trade-off 를 이 글이 명시적으로 비교하지 않음 |
| C5 | 이 패턴은 PostgreSQL 트리거 + JSON 컬럼으로 CDC 레코드를 저장하는 감사 로깅 구현이다 | [§ introduction] "In this article, we are going to see how we can implement an audit logging mechanism using PostgreSQL database triggers to store the CDC (Change Data Capture) records." | `engineering-blog` | PostgreSQL 트리거 기반 감사 로깅 구현 | 이 패턴이 JPA/Hibernate 감사(`@EntityListeners`) 나 Envers 보다 우월하다는 주장은 이 글 단독으로 증명 안 됨 |
## Usage Boundaries / 적용 경계
- 이 자료가 직접 증명하는 것:
- `C1`, `C2`: 트리거가 직접 `current_setting('var.logged_user')` 를 읽고, 그 값은 애플리케이션이 매 DML 전 `SET LOCAL` 로 주입해야 한다 — 즉 **애플리케이션-DB 간 세션 변수 propagation seam 이 불가피**하다
- `C3`: `SET LOCAL` 스코프는 트랜잭션 경계와 동기화되므로 커넥션 풀 환경에서 변수 누출을 방지한다 (단, `engineering-blog` 등급이므로 official 보증 아님)
- `C4`: AFTER 트리거가 사용됨
- 이 자료가 증명하지 않는 것:
- JPA `@EntityListeners` / Spring Data Auditing / Hibernate Envers 와의 전면 비교
- `current_setting` 이 변수 미설정 시 null 반환인지 예외 발생인지 (PostgreSQL 공식 문서 별도 확인 필요)
- 이 패턴이 ca-tmpl 실제 HikariCP 설정 하에서 변수 누출 없이 동작함
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
- PostgreSQL 공식 문서에서 `current_setting(name, missing_ok)` 동작 확인
- ca-tmpl 의 HikariCP + `SET LOCAL` 조합에서 커넥션 반납 후 변수 완전 소멸 여부 로컬 검증
## 메모 / Notes
- 이 글은 개인 전문가 블로그(`engineering-blog`) 등급이다. C3 의 `SET LOCAL` + 커넥션 풀 안전성은 공식 PostgreSQL 문서로 보강하기 전까지 `needs-confirmation` 취급.
- 추가로 봐야 할 동일 출처: vladmihalcea.com/the-anatomy-of-connection-pooling/ (C3 보강 가능성)
- Hibernate Envers, Debezium 대안이 언급되나 비교 상세는 이 글 범위 밖.
## Related / 관련
- 같은 주제 다른 자료: [[raw/company-tech-blogs/slow-query-datasource-proxy-spring-boot-galovics]]
- 이 자료를 인용한 wiki 요약: (생성 시)