작성일: 2024-12-20
목적: UNIQUE 제약조건 문제 근본 해결을 위한 히스토리 테이블 분리 시스템 구축
unique_vendor_influencer_status 중복 키 오류 완전 해결-- DDL 실행
mysql -u root -p influence < ddl/007_create_status_history_table.sql
-- 기존 STATUS 데이터를 히스토리 테이블로 이전
INSERT INTO VENDOR_INFLUENCER_STATUS_HISTORY
(MAPPING_SEQ, STATUS, STATUS_MESSAGE, CHANGED_BY, CHANGED_DATE, IS_CURRENT)
SELECT
SEQ as MAPPING_SEQ,
COALESCE(STATUS, 'PENDING') as STATUS,
COALESCE(REQUEST_MESSAGE, '') as STATUS_MESSAGE,
REQUESTED_BY as CHANGED_BY,
REG_DATE as CHANGED_DATE,
'Y' as IS_CURRENT
FROM VENDOR_INFLUENCER_MAPPING
WHERE IS_ACT = 'Y';
-- STATUS 컬럼 제거 (백업 후 실행)
ALTER TABLE VENDOR_INFLUENCER_MAPPING DROP COLUMN STATUS;
VendorInfluencerStatusHistoryModel.php - 상태 히스토리 관리VendorInfluencerMappingModel.php - STATUS 컬럼 제거된 메인 모델InfluencerControllerV2.php - 히스토리 테이블 기반 새 컨트롤러// app/Config/Routes.php에 추가
$routes->group('api/v2', function($routes) {
$routes->group('influencer', function($routes) {
$routes->post('search-vendors', 'InfluencerControllerV2::searchVendors');
$routes->post('create-request', 'InfluencerControllerV2::createApprovalRequest');
$routes->post('reapply-request', 'InfluencerControllerV2::createReapplyRequest');
$routes->post('my-partnerships', 'InfluencerControllerV2::getMyPartnerships');
$routes->post('terminate', 'InfluencerControllerV2::terminatePartnership');
});
});
// 기존 API를 새 컨트롤러로 리다이렉트 (점진적 이전)
$routes->post('vendor-influencer/reapply-request', 'InfluencerControllerV2::createReapplyRequest');
// 재승인 요청 API 변경
const endpoint = requestModal.value.isReapply
? "/api/v2/influencer/reapply-request" // 새 엔드포인트
: "/api/v2/influencer/create-request";
// 상태 정보는 CURRENT_STATUS 필드로 변경
vendor.PARTNERSHIP_STATUS = response.CURRENT_STATUS;
vendor.PARTNERSHIP_MESSAGE = response.CURRENT_STATUS_MESSAGE;
-- 기존 문제: (VENDOR_SEQ, INFLUENCER_SEQ, STATUS) 중복 불가
-- 해결책: STATUS를 별도 테이블로 분리, 메인 테이블에는 매핑 정보만
-- 히스토리 테이블 UNIQUE 제약조건
UNIQUE INDEX unique_current_mapping (MAPPING_SEQ, IS_CURRENT)
-- 하나의 매핑에 대해 하나의 현재 상태만 존재
-- 모든 상태 변경이 히스토리로 기록됨
SELECT
STATUS,
PREVIOUS_STATUS,
STATUS_MESSAGE,
CHANGED_BY,
CHANGED_DATE
FROM VENDOR_INFLUENCER_STATUS_HISTORY
WHERE MAPPING_SEQ = 1
ORDER BY CHANGED_DATE DESC;
// VendorInfluencerStatusHistoryModel::changeStatus()
// 1. 기존 현재 상태를 이전 상태로 변경 (IS_CURRENT = 'N')
// 2. 새로운 상태 히스토리 추가 (IS_CURRENT = 'Y')
// 3. 메인 테이블 MOD_DATE 업데이트
// 모든 과정이 트랜잭션으로 보장됨
// 기존 코드에서 STATUS 조회하던 부분
$partnership['STATUS'] // 기존 방식
// 새로운 방식 (JOIN으로 현재 상태 조회)
$partnership['CURRENT_STATUS'] // 히스토리 테이블 JOIN
# 마이그레이션 전 필수 백업
mysqldump -u root -p influence > backup_before_migration_$(date +%Y%m%d_%H%M%S).sql
# 1단계: 히스토리 테이블 생성 (기존 시스템 영향 없음)
# 2단계: 새 API 배포 (기존 API와 병행 운영)
# 3단계: 프론트엔드 점진적 이전
# 4단계: 기존 API 제거 (충분한 검증 후)
-- 상태 불일치 모니터링
SELECT
MAPPING_SEQ,
COUNT(*) as current_status_count
FROM VENDOR_INFLUENCER_STATUS_HISTORY
WHERE IS_CURRENT = 'Y'
GROUP BY MAPPING_SEQ
HAVING COUNT(*) > 1;
-- 결과가 0이어야 정상
-- 자주 사용되는 조회 패턴에 대한 복합 인덱스
CREATE INDEX idx_mapping_current_status
ON VENDOR_INFLUENCER_STATUS_HISTORY (MAPPING_SEQ, IS_CURRENT, STATUS);
-- 파티셔닝 고려 (대용량 히스토리 데이터 시)
-- 월별 또는 연도별 파티셔닝 검토
unique_vendor_influencer_status 오류 발생하지 않음✅ 히스토리 테이블 기반 STATUS 관리 시스템 구축 완료