Compare commits
No commits in common. "e902987e4482a3f314b6f71bde456480406a4700" and "e1d6c1740f029f69d6304e2594c20a506c20d461" have entirely different histories.
e902987e44
...
e1d6c1740f
|
|
@ -335,222 +335,9 @@ ON CONFLICT (table_name, column_name) DO UPDATE SET column_label = EXCLUDED.colu
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 7. 로그 테이블 생성 (선택사항)
|
## 7. 체크리스트
|
||||||
|
|
||||||
변경 이력 추적이 필요한 테이블에는 로그 테이블을 생성할 수 있습니다.
|
테이블 생성/수정 시 반드시 확인할 사항:
|
||||||
|
|
||||||
### 7.1 로그 테이블 DDL 템플릿
|
|
||||||
|
|
||||||
```sql
|
|
||||||
-- 로그 테이블 생성
|
|
||||||
CREATE TABLE 테이블명_log (
|
|
||||||
log_id SERIAL PRIMARY KEY,
|
|
||||||
operation_type VARCHAR(10) NOT NULL, -- INSERT/UPDATE/DELETE
|
|
||||||
original_id VARCHAR(100), -- 원본 테이블 PK 값
|
|
||||||
changed_column VARCHAR(100), -- 변경된 컬럼명
|
|
||||||
old_value TEXT, -- 변경 전 값
|
|
||||||
new_value TEXT, -- 변경 후 값
|
|
||||||
changed_by VARCHAR(50), -- 변경자 ID
|
|
||||||
changed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 변경 시각
|
|
||||||
ip_address VARCHAR(50), -- 변경 요청 IP
|
|
||||||
user_agent TEXT, -- User Agent
|
|
||||||
full_row_before JSONB, -- 변경 전 전체 행
|
|
||||||
full_row_after JSONB -- 변경 후 전체 행
|
|
||||||
);
|
|
||||||
|
|
||||||
-- 인덱스 생성
|
|
||||||
CREATE INDEX idx_테이블명_log_original_id ON 테이블명_log(original_id);
|
|
||||||
CREATE INDEX idx_테이블명_log_changed_at ON 테이블명_log(changed_at);
|
|
||||||
CREATE INDEX idx_테이블명_log_operation ON 테이블명_log(operation_type);
|
|
||||||
|
|
||||||
-- 코멘트 추가
|
|
||||||
COMMENT ON TABLE 테이블명_log IS '테이블명 테이블 변경 이력';
|
|
||||||
```
|
|
||||||
|
|
||||||
### 7.2 트리거 함수 DDL 템플릿
|
|
||||||
|
|
||||||
```sql
|
|
||||||
CREATE OR REPLACE FUNCTION 테이블명_log_trigger_func()
|
|
||||||
RETURNS TRIGGER AS $$
|
|
||||||
DECLARE
|
|
||||||
v_column_name TEXT;
|
|
||||||
v_old_value TEXT;
|
|
||||||
v_new_value TEXT;
|
|
||||||
v_user_id VARCHAR(50);
|
|
||||||
v_ip_address VARCHAR(50);
|
|
||||||
BEGIN
|
|
||||||
v_user_id := current_setting('app.user_id', TRUE);
|
|
||||||
v_ip_address := current_setting('app.ip_address', TRUE);
|
|
||||||
|
|
||||||
IF (TG_OP = 'INSERT') THEN
|
|
||||||
INSERT INTO 테이블명_log (operation_type, original_id, changed_by, ip_address, full_row_after)
|
|
||||||
VALUES ('INSERT', NEW.id, v_user_id, v_ip_address, row_to_json(NEW)::jsonb);
|
|
||||||
RETURN NEW;
|
|
||||||
|
|
||||||
ELSIF (TG_OP = 'UPDATE') THEN
|
|
||||||
FOR v_column_name IN
|
|
||||||
SELECT column_name
|
|
||||||
FROM information_schema.columns
|
|
||||||
WHERE table_name = '테이블명'
|
|
||||||
AND table_schema = 'public'
|
|
||||||
LOOP
|
|
||||||
EXECUTE format('SELECT ($1).%I::TEXT, ($2).%I::TEXT', v_column_name, v_column_name)
|
|
||||||
INTO v_old_value, v_new_value
|
|
||||||
USING OLD, NEW;
|
|
||||||
|
|
||||||
IF v_old_value IS DISTINCT FROM v_new_value THEN
|
|
||||||
INSERT INTO 테이블명_log (
|
|
||||||
operation_type, original_id, changed_column, old_value, new_value,
|
|
||||||
changed_by, ip_address, full_row_before, full_row_after
|
|
||||||
)
|
|
||||||
VALUES (
|
|
||||||
'UPDATE', NEW.id, v_column_name, v_old_value, v_new_value,
|
|
||||||
v_user_id, v_ip_address, row_to_json(OLD)::jsonb, row_to_json(NEW)::jsonb
|
|
||||||
);
|
|
||||||
END IF;
|
|
||||||
END LOOP;
|
|
||||||
RETURN NEW;
|
|
||||||
|
|
||||||
ELSIF (TG_OP = 'DELETE') THEN
|
|
||||||
INSERT INTO 테이블명_log (operation_type, original_id, changed_by, ip_address, full_row_before)
|
|
||||||
VALUES ('DELETE', OLD.id, v_user_id, v_ip_address, row_to_json(OLD)::jsonb);
|
|
||||||
RETURN OLD;
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
RETURN NULL;
|
|
||||||
END;
|
|
||||||
$$ LANGUAGE plpgsql;
|
|
||||||
```
|
|
||||||
|
|
||||||
### 7.3 트리거 DDL 템플릿
|
|
||||||
|
|
||||||
```sql
|
|
||||||
CREATE TRIGGER 테이블명_audit_trigger
|
|
||||||
AFTER INSERT OR UPDATE OR DELETE ON 테이블명
|
|
||||||
FOR EACH ROW EXECUTE FUNCTION 테이블명_log_trigger_func();
|
|
||||||
```
|
|
||||||
|
|
||||||
### 7.4 로그 설정 등록
|
|
||||||
|
|
||||||
```sql
|
|
||||||
INSERT INTO table_log_config (
|
|
||||||
original_table_name, log_table_name, trigger_name,
|
|
||||||
trigger_function_name, is_active, created_by, created_at
|
|
||||||
) VALUES (
|
|
||||||
'테이블명', '테이블명_log', '테이블명_audit_trigger',
|
|
||||||
'테이블명_log_trigger_func', 'Y', '생성자ID', now()
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
### 7.5 table_labels에 use_log_table 플래그 설정
|
|
||||||
|
|
||||||
```sql
|
|
||||||
UPDATE table_labels
|
|
||||||
SET use_log_table = 'Y', updated_date = now()
|
|
||||||
WHERE table_name = '테이블명';
|
|
||||||
```
|
|
||||||
|
|
||||||
### 7.6 전체 예시: order_info 로그 테이블 생성
|
|
||||||
|
|
||||||
```sql
|
|
||||||
-- Step 1: 로그 테이블 생성
|
|
||||||
CREATE TABLE order_info_log (
|
|
||||||
log_id SERIAL PRIMARY KEY,
|
|
||||||
operation_type VARCHAR(10) NOT NULL,
|
|
||||||
original_id VARCHAR(100),
|
|
||||||
changed_column VARCHAR(100),
|
|
||||||
old_value TEXT,
|
|
||||||
new_value TEXT,
|
|
||||||
changed_by VARCHAR(50),
|
|
||||||
changed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
ip_address VARCHAR(50),
|
|
||||||
user_agent TEXT,
|
|
||||||
full_row_before JSONB,
|
|
||||||
full_row_after JSONB
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX idx_order_info_log_original_id ON order_info_log(original_id);
|
|
||||||
CREATE INDEX idx_order_info_log_changed_at ON order_info_log(changed_at);
|
|
||||||
CREATE INDEX idx_order_info_log_operation ON order_info_log(operation_type);
|
|
||||||
|
|
||||||
COMMENT ON TABLE order_info_log IS 'order_info 테이블 변경 이력';
|
|
||||||
|
|
||||||
-- Step 2: 트리거 함수 생성
|
|
||||||
CREATE OR REPLACE FUNCTION order_info_log_trigger_func()
|
|
||||||
RETURNS TRIGGER AS $$
|
|
||||||
DECLARE
|
|
||||||
v_column_name TEXT;
|
|
||||||
v_old_value TEXT;
|
|
||||||
v_new_value TEXT;
|
|
||||||
v_user_id VARCHAR(50);
|
|
||||||
v_ip_address VARCHAR(50);
|
|
||||||
BEGIN
|
|
||||||
v_user_id := current_setting('app.user_id', TRUE);
|
|
||||||
v_ip_address := current_setting('app.ip_address', TRUE);
|
|
||||||
|
|
||||||
IF (TG_OP = 'INSERT') THEN
|
|
||||||
INSERT INTO order_info_log (operation_type, original_id, changed_by, ip_address, full_row_after)
|
|
||||||
VALUES ('INSERT', NEW.id, v_user_id, v_ip_address, row_to_json(NEW)::jsonb);
|
|
||||||
RETURN NEW;
|
|
||||||
ELSIF (TG_OP = 'UPDATE') THEN
|
|
||||||
FOR v_column_name IN
|
|
||||||
SELECT column_name FROM information_schema.columns
|
|
||||||
WHERE table_name = 'order_info' AND table_schema = 'public'
|
|
||||||
LOOP
|
|
||||||
EXECUTE format('SELECT ($1).%I::TEXT, ($2).%I::TEXT', v_column_name, v_column_name)
|
|
||||||
INTO v_old_value, v_new_value USING OLD, NEW;
|
|
||||||
IF v_old_value IS DISTINCT FROM v_new_value THEN
|
|
||||||
INSERT INTO order_info_log (operation_type, original_id, changed_column, old_value, new_value, changed_by, ip_address, full_row_before, full_row_after)
|
|
||||||
VALUES ('UPDATE', NEW.id, v_column_name, v_old_value, v_new_value, v_user_id, v_ip_address, row_to_json(OLD)::jsonb, row_to_json(NEW)::jsonb);
|
|
||||||
END IF;
|
|
||||||
END LOOP;
|
|
||||||
RETURN NEW;
|
|
||||||
ELSIF (TG_OP = 'DELETE') THEN
|
|
||||||
INSERT INTO order_info_log (operation_type, original_id, changed_by, ip_address, full_row_before)
|
|
||||||
VALUES ('DELETE', OLD.id, v_user_id, v_ip_address, row_to_json(OLD)::jsonb);
|
|
||||||
RETURN OLD;
|
|
||||||
END IF;
|
|
||||||
RETURN NULL;
|
|
||||||
END;
|
|
||||||
$$ LANGUAGE plpgsql;
|
|
||||||
|
|
||||||
-- Step 3: 트리거 생성
|
|
||||||
CREATE TRIGGER order_info_audit_trigger
|
|
||||||
AFTER INSERT OR UPDATE OR DELETE ON order_info
|
|
||||||
FOR EACH ROW EXECUTE FUNCTION order_info_log_trigger_func();
|
|
||||||
|
|
||||||
-- Step 4: 로그 설정 등록
|
|
||||||
INSERT INTO table_log_config (original_table_name, log_table_name, trigger_name, trigger_function_name, is_active, created_by, created_at)
|
|
||||||
VALUES ('order_info', 'order_info_log', 'order_info_audit_trigger', 'order_info_log_trigger_func', 'Y', 'system', now());
|
|
||||||
|
|
||||||
-- Step 5: table_labels 플래그 업데이트
|
|
||||||
UPDATE table_labels SET use_log_table = 'Y', updated_date = now() WHERE table_name = 'order_info';
|
|
||||||
```
|
|
||||||
|
|
||||||
### 7.7 로그 테이블 삭제
|
|
||||||
|
|
||||||
```sql
|
|
||||||
-- 트리거 삭제
|
|
||||||
DROP TRIGGER IF EXISTS 테이블명_audit_trigger ON 테이블명;
|
|
||||||
|
|
||||||
-- 트리거 함수 삭제
|
|
||||||
DROP FUNCTION IF EXISTS 테이블명_log_trigger_func();
|
|
||||||
|
|
||||||
-- 로그 테이블 삭제
|
|
||||||
DROP TABLE IF EXISTS 테이블명_log;
|
|
||||||
|
|
||||||
-- 로그 설정 삭제
|
|
||||||
DELETE FROM table_log_config WHERE original_table_name = '테이블명';
|
|
||||||
|
|
||||||
-- table_labels 플래그 업데이트
|
|
||||||
UPDATE table_labels SET use_log_table = 'N', updated_date = now() WHERE table_name = '테이블명';
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 8. 체크리스트
|
|
||||||
|
|
||||||
### 테이블 생성/수정 시 반드시 확인할 사항:
|
|
||||||
|
|
||||||
- [ ] DDL에 기본 5개 컬럼 포함 (id, created_date, updated_date, writer, company_code)
|
- [ ] DDL에 기본 5개 컬럼 포함 (id, created_date, updated_date, writer, company_code)
|
||||||
- [ ] 모든 비즈니스 컬럼은 `VARCHAR(500)` 타입 사용
|
- [ ] 모든 비즈니스 컬럼은 `VARCHAR(500)` 타입 사용
|
||||||
|
|
@ -562,18 +349,9 @@ UPDATE table_labels SET use_log_table = 'N', updated_date = now() WHERE table_na
|
||||||
- [ ] code/entity 타입은 detail_settings에 참조 정보 포함
|
- [ ] code/entity 타입은 detail_settings에 참조 정보 포함
|
||||||
- [ ] ON CONFLICT 절로 중복 시 UPDATE 처리
|
- [ ] ON CONFLICT 절로 중복 시 UPDATE 처리
|
||||||
|
|
||||||
### 로그 테이블 생성 시 확인할 사항 (선택):
|
|
||||||
|
|
||||||
- [ ] 로그 테이블 생성 (`테이블명_log`)
|
|
||||||
- [ ] 인덱스 3개 생성 (original_id, changed_at, operation_type)
|
|
||||||
- [ ] 트리거 함수 생성 (`테이블명_log_trigger_func`)
|
|
||||||
- [ ] 트리거 생성 (`테이블명_audit_trigger`)
|
|
||||||
- [ ] `table_log_config`에 로그 설정 등록
|
|
||||||
- [ ] `table_labels.use_log_table = 'Y'` 업데이트
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 9. 금지 사항
|
## 8. 금지 사항
|
||||||
|
|
||||||
1. **DB 타입 직접 지정 금지**: NUMBER, INTEGER, DATE 등 DB 타입 직접 사용 금지
|
1. **DB 타입 직접 지정 금지**: NUMBER, INTEGER, DATE 등 DB 타입 직접 사용 금지
|
||||||
2. **VARCHAR 길이 변경 금지**: 반드시 `VARCHAR(500)` 사용
|
2. **VARCHAR 길이 변경 금지**: 반드시 `VARCHAR(500)` 사용
|
||||||
|
|
@ -586,7 +364,5 @@ UPDATE table_labels SET use_log_table = 'N', updated_date = now() WHERE table_na
|
||||||
## 참조 파일
|
## 참조 파일
|
||||||
|
|
||||||
- `backend-node/src/services/ddlExecutionService.ts`: DDL 실행 서비스
|
- `backend-node/src/services/ddlExecutionService.ts`: DDL 실행 서비스
|
||||||
- `backend-node/src/services/tableManagementService.ts`: 로그 테이블 생성 서비스
|
|
||||||
- `backend-node/src/types/ddl.ts`: DDL 타입 정의
|
- `backend-node/src/types/ddl.ts`: DDL 타입 정의
|
||||||
- `backend-node/src/controllers/ddlController.ts`: DDL API 컨트롤러
|
- `backend-node/src/controllers/ddlController.ts`: DDL API 컨트롤러
|
||||||
- `backend-node/src/controllers/tableManagementController.ts`: 로그 테이블 API 컨트롤러
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue