Files
M-Project/HISTORY_TRACKING_IDEAS.md
Hyungi Ahn c680453227 feat: 관리함 통합 수정 모달 및 히스토리 추적 방안 구현
🔧 통합 수정 모달:
- 모든 진행 중 상태에서 '확인' 버튼으로 모달 열기
- 완료 대기 상태에서도 '수정' 버튼으로 동일 모달 사용
- 6열 와이드 모달로 모든 정보 한눈에 표시

📝 모달 구성:
- 왼쪽: 기본 정보 (프로젝트, 부적합명, 상세내용, 원인분류, 업로드 사진)
- 오른쪽: 관리 정보 (해결방안, 담당부서/자, 조치예상일) + 완료 신청 정보

🎯 버튼 시스템 개선:
- 일반 진행 중: 저장 | 확인 | 완료처리
- 완료 대기: 반려 | 수정 | 최종확인
- 모달에서 통합 수정 가능

✏️ 수정 기능:
- 부적합명, 상세내용 직접 수정
- 해결방안, 담당부서/자, 조치예상일 수정
- 모달에서 저장 시 실시간 반영

📋 히스토리 추적 방안 문서화:
- 단일 히스토리 테이블 vs 페이지별 테이블 비교
- 변경 이력 기록 서비스 클래스 설계
- 프론트엔드 히스토리 조회 모달 구현 방안
- 감사 추적, 데이터 복구, 보안 고려사항 포함

🔍 구현 우선순위:
- Phase 1: 기본 히스토리 테이블 + 관리함 이력
- Phase 2: 수신함 이력 + 히스토리 UI
- Phase 3: 데이터 복구 + 감사 보고서

💡 추가 아이디어:
- 변경 승인 워크플로우
- 자동 백업 시스템
- 변경 영향도 분석

Expected Result:
 모든 진행 중 상태에서 통합 수정 모달 사용
 완료 대기 상태 정보 포함 표시
 체계적인 히스토리 추적 방안 수립
 투명하고 추적 가능한 이슈 관리 기반 마련
2025-10-26 13:11:26 +09:00

308 lines
11 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 📝 수정 히스토리 저장 방안
## 🎯 목적
- 각 페이지에서 수정한 내용의 변경 이력 추적
- 누가, 언제, 무엇을, 왜 변경했는지 기록
- 데이터 무결성 및 감사 추적 (Audit Trail) 제공
## 🗄️ DB 구조 방안
### 방안 1: 단일 히스토리 테이블 (권장)
```sql
CREATE TABLE issue_history (
id SERIAL PRIMARY KEY,
issue_id INTEGER NOT NULL REFERENCES issues(id),
changed_by_id INTEGER NOT NULL REFERENCES users(id),
changed_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
change_type VARCHAR(50) NOT NULL, -- 'UPDATE', 'STATUS_CHANGE', 'COMPLETION_REQUEST' 등
page_source VARCHAR(50) NOT NULL, -- 'INBOX', 'MANAGEMENT', 'DASHBOARD' 등
field_name VARCHAR(100), -- 변경된 필드명
old_value TEXT, -- 이전 값 (JSON 형태로 저장 가능)
new_value TEXT, -- 새로운 값 (JSON 형태로 저장 가능)
change_reason TEXT, -- 변경 사유 (선택사항)
session_id VARCHAR(100), -- 동일 세션에서 여러 필드 변경 시 그룹핑
ip_address INET, -- 변경자 IP 주소
user_agent TEXT -- 브라우저 정보
);
-- 인덱스 생성
CREATE INDEX idx_issue_history_issue_id ON issue_history(issue_id);
CREATE INDEX idx_issue_history_changed_at ON issue_history(changed_at);
CREATE INDEX idx_issue_history_changed_by ON issue_history(changed_by_id);
```
### 방안 2: 페이지별 히스토리 테이블
```sql
-- 수신함 히스토리
CREATE TABLE inbox_history (
id SERIAL PRIMARY KEY,
issue_id INTEGER NOT NULL REFERENCES issues(id),
action_type VARCHAR(50), -- 'REVIEW', 'DISPOSE', 'STATUS_CHANGE'
old_data JSONB,
new_data JSONB,
changed_by_id INTEGER NOT NULL REFERENCES users(id),
changed_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- 관리함 히스토리
CREATE TABLE management_history (
id SERIAL PRIMARY KEY,
issue_id INTEGER NOT NULL REFERENCES issues(id),
action_type VARCHAR(50), -- 'UPDATE', 'COMPLETION_REQUEST', 'APPROVAL'
old_data JSONB,
new_data JSONB,
changed_by_id INTEGER NOT NULL REFERENCES users(id),
changed_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
```
## 🔧 구현 방안
### 1⃣ 백엔드 구현 (FastAPI)
#### 히스토리 서비스 클래스
```python
# services/history_service.py
from typing import Dict, Any, Optional
import json
from datetime import datetime
from sqlalchemy.orm import Session
class HistoryService:
@staticmethod
def log_change(
db: Session,
issue_id: int,
changed_by_id: int,
change_type: str,
page_source: str,
old_data: Dict[str, Any],
new_data: Dict[str, Any],
change_reason: Optional[str] = None,
session_id: Optional[str] = None
):
"""변경 이력 기록"""
# 변경된 필드들 찾기
changed_fields = []
for field, new_value in new_data.items():
old_value = old_data.get(field)
if old_value != new_value:
history_entry = IssueHistory(
issue_id=issue_id,
changed_by_id=changed_by_id,
change_type=change_type,
page_source=page_source,
field_name=field,
old_value=json.dumps(old_value) if old_value else None,
new_value=json.dumps(new_value) if new_value else None,
change_reason=change_reason,
session_id=session_id
)
db.add(history_entry)
changed_fields.append(field)
db.commit()
return changed_fields
@staticmethod
def get_issue_history(db: Session, issue_id: int, limit: int = 50):
"""이슈의 변경 이력 조회"""
return db.query(IssueHistory)\
.filter(IssueHistory.issue_id == issue_id)\
.order_by(IssueHistory.changed_at.desc())\
.limit(limit)\
.all()
```
#### API 엔드포인트에서 히스토리 기록
```python
# routers/management.py
from services.history_service import HistoryService
@router.put("/{issue_id}")
async def update_issue(
issue_id: int,
update_request: ManagementUpdateRequest,
request: Request,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
# 기존 이슈 데이터 백업
issue = db.query(Issue).filter(Issue.id == issue_id).first()
old_data = {
"final_description": issue.final_description,
"solution": issue.solution,
"responsible_department": issue.responsible_department,
"responsible_person": issue.responsible_person,
"expected_completion_date": issue.expected_completion_date.isoformat() if issue.expected_completion_date else None
}
# 업데이트 수행
update_data = update_request.dict(exclude_unset=True)
for field, value in update_data.items():
setattr(issue, field, value)
db.commit()
db.refresh(issue)
# 새로운 데이터
new_data = {
"final_description": issue.final_description,
"solution": issue.solution,
"responsible_department": issue.responsible_department,
"responsible_person": issue.responsible_person,
"expected_completion_date": issue.expected_completion_date.isoformat() if issue.expected_completion_date else None
}
# 히스토리 기록
session_id = str(uuid.uuid4())
HistoryService.log_change(
db=db,
issue_id=issue_id,
changed_by_id=current_user.id,
change_type="UPDATE",
page_source="MANAGEMENT",
old_data=old_data,
new_data=new_data,
session_id=session_id
)
return {"message": "업데이트 완료", "changed_fields": list(update_data.keys())}
```
### 2⃣ 프론트엔드 구현
#### 히스토리 조회 모달
```javascript
// 히스토리 조회 함수
async function showIssueHistory(issueId) {
try {
const response = await fetch(`/api/issues/${issueId}/history`, {
headers: {
'Authorization': `Bearer ${localStorage.getItem('access_token')}`
}
});
if (response.ok) {
const history = await response.json();
openHistoryModal(issueId, history);
}
} catch (error) {
console.error('히스토리 조회 실패:', error);
}
}
// 히스토리 모달 생성
function openHistoryModal(issueId, history) {
const modalContent = `
<div class="fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center" id="historyModal">
<div class="bg-white rounded-xl shadow-xl max-w-4xl w-full mx-4 max-h-[80vh] overflow-y-auto">
<div class="p-6">
<div class="flex items-center justify-between mb-6">
<h3 class="text-xl font-semibold">
<i class="fas fa-history text-blue-500 mr-2"></i>
변경 이력 - No.${issueId}
</h3>
<button onclick="closeHistoryModal()" class="text-gray-400 hover:text-gray-600">
<i class="fas fa-times text-xl"></i>
</button>
</div>
<div class="space-y-4">
${history.map(entry => `
<div class="border-l-4 border-blue-400 bg-blue-50 p-4 rounded-r-lg">
<div class="flex justify-between items-start mb-2">
<div class="flex items-center space-x-2">
<span class="font-semibold text-blue-800">${entry.field_name}</span>
<span class="px-2 py-1 bg-blue-200 text-blue-800 text-xs rounded-full">${entry.page_source}</span>
</div>
<span class="text-sm text-gray-500">${new Date(entry.changed_at).toLocaleString('ko-KR')}</span>
</div>
<div class="grid grid-cols-2 gap-4 text-sm">
<div>
<span class="font-medium text-red-600">이전:</span>
<p class="text-gray-700 bg-red-50 p-2 rounded mt-1">${entry.old_value || '없음'}</p>
</div>
<div>
<span class="font-medium text-green-600">변경:</span>
<p class="text-gray-700 bg-green-50 p-2 rounded mt-1">${entry.new_value || '없음'}</p>
</div>
</div>
<div class="mt-2 text-sm text-gray-600">
<i class="fas fa-user mr-1"></i>변경자: ${entry.changed_by_name}
</div>
</div>
`).join('')}
</div>
</div>
</div>
</div>
`;
document.body.insertAdjacentHTML('beforeend', modalContent);
}
```
## 📊 히스토리 활용 방안
### 1⃣ 관리자 대시보드
- 최근 변경 사항 요약
- 사용자별 활동 통계
- 페이지별 수정 빈도
### 2⃣ 감사 보고서
- 특정 기간 동안의 모든 변경 사항
- 중요 필드 변경 알림
- 규정 준수 확인
### 3⃣ 데이터 복구
- 잘못된 변경 사항 롤백
- 특정 시점으로 데이터 복원
- 변경 사항 비교 및 분석
## 🔒 보안 고려사항
### 1⃣ 접근 권한
- 히스토리 조회 권한 분리
- 민감한 정보 마스킹
- 관리자만 전체 히스토리 접근
### 2⃣ 데이터 보호
- 히스토리 데이터 암호화
- 개인정보 자동 삭제 정책
- 백업 및 아카이빙
## 🚀 구현 우선순위
### Phase 1 (필수)
1. 기본 히스토리 테이블 생성
2. 관리함 수정 이력 기록
3. 간단한 히스토리 조회 API
### Phase 2 (확장)
1. 수신함 처리 이력 기록
2. 히스토리 조회 UI 구현
3. 변경 사유 입력 기능
### Phase 3 (고급)
1. 데이터 복구 기능
2. 감사 보고서 생성
3. 실시간 변경 알림
## 💡 추가 아이디어
### 1⃣ 변경 승인 워크플로우
- 중요한 변경사항은 승인 후 적용
- 변경 요청 → 검토 → 승인/반려
### 2⃣ 자동 백업
- 중요 변경 전 자동 스냅샷
- 일정 주기별 전체 데이터 백업
### 3⃣ 변경 영향도 분석
- 연관된 다른 이슈에 미치는 영향
- 변경으로 인한 통계 변화
이러한 히스토리 시스템을 통해 **투명하고 추적 가능한 이슈 관리**가 가능해집니다! 🎯