commit 8abfcba61798862ac290c102dadf0ecbe8c513b4 Author: Kurt92 Date: Mon Jul 28 11:40:39 2025 +0900 Init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c29b684 --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +# ---> Java +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +replay_pid* + +/.junie/ +/logs/ +/build/ +/.idea/ +/.gradle/ +/.idea/ diff --git a/DB-DDL/maria/ddl/xitframework/qrtz_blob_triggers.sql b/DB-DDL/maria/ddl/xitframework/qrtz_blob_triggers.sql new file mode 100644 index 0000000..22a2d84 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/qrtz_blob_triggers.sql @@ -0,0 +1,12 @@ +create table qrtz_blob_triggers +( + SCHED_NAME varchar(120) not null comment '스케줄러 이름', + TRIGGER_NAME varchar(200) not null comment '트리거 이름', + TRIGGER_GROUP varchar(200) not null comment '트리거 그룹', + BLOB_DATA blob null comment '트리거와 관련된 바이너리 데이터 (직렬화된 객체 등)', + primary key (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP), + constraint qrtz_blob_triggers_ibfk_1 + foreign key (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP) references qrtz_triggers (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP) +) + comment 'Quartz BLOB 트리거 정보 - 바이너리 데이터를 가진 트리거'; + diff --git a/DB-DDL/maria/ddl/xitframework/qrtz_calendars.sql b/DB-DDL/maria/ddl/xitframework/qrtz_calendars.sql new file mode 100644 index 0000000..b3f8ffd --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/qrtz_calendars.sql @@ -0,0 +1,9 @@ +create table qrtz_calendars +( + SCHED_NAME varchar(120) not null comment '스케줄러 이름', + CALENDAR_NAME varchar(200) not null comment '캘린더 이름 (고유 식별자)', + CALENDAR blob not null comment '캘린더 데이터 (직렬화된 Calendar 객체)', + primary key (SCHED_NAME, CALENDAR_NAME) +) + comment 'Quartz 캘린더 정보 - 트리거 실행 제외 기간 정의'; + diff --git a/DB-DDL/maria/ddl/xitframework/qrtz_cron_triggers.sql b/DB-DDL/maria/ddl/xitframework/qrtz_cron_triggers.sql new file mode 100644 index 0000000..0816380 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/qrtz_cron_triggers.sql @@ -0,0 +1,13 @@ +create table qrtz_cron_triggers +( + SCHED_NAME varchar(120) not null comment '스케줄러 이름', + TRIGGER_NAME varchar(200) not null comment '트리거 이름', + TRIGGER_GROUP varchar(200) not null comment '트리거 그룹', + CRON_EXPRESSION varchar(200) not null comment 'Cron 표현식 (예: "0 0 12 * * ?" - 매일 정오)', + TIME_ZONE_ID varchar(80) null comment '시간대 ID (예: "Asia/Seoul", NULL이면 시스템 기본 시간대)', + primary key (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP), + constraint qrtz_cron_triggers_ibfk_1 + foreign key (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP) references qrtz_triggers (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP) +) + comment 'Quartz Cron 트리거 정보 - Cron 표현식 기반 복잡한 스케줄링'; + diff --git a/DB-DDL/maria/ddl/xitframework/qrtz_fired_triggers.sql b/DB-DDL/maria/ddl/xitframework/qrtz_fired_triggers.sql new file mode 100644 index 0000000..49b9296 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/qrtz_fired_triggers.sql @@ -0,0 +1,19 @@ +create table qrtz_fired_triggers +( + SCHED_NAME varchar(120) not null comment '스케줄러 이름', + ENTRY_ID varchar(95) not null comment '실행 엔트리 고유 ID', + TRIGGER_NAME varchar(200) not null comment '트리거 이름', + TRIGGER_GROUP varchar(200) not null comment '트리거 그룹', + INSTANCE_NAME varchar(200) not null comment '실행 중인 스케줄러 인스턴스 이름', + FIRED_TIME bigint(13) not null comment '실제 실행 시간 (Unix timestamp)', + SCHED_TIME bigint(13) not null comment '예정된 실행 시간 (Unix timestamp)', + PRIORITY int not null comment '실행 우선순위', + STATE varchar(16) not null comment '실행 상태 (EXECUTING, COMPLETE, BLOCKED, ERROR, PAUSED_BLOCKED)', + JOB_NAME varchar(200) null comment '연결된 작업 이름', + JOB_GROUP varchar(200) null comment '연결된 작업 그룹', + IS_NONCONCURRENT varchar(1) null comment '동시 실행 방지 여부 (Y/N)', + REQUESTS_RECOVERY varchar(1) null comment '복구 요청 여부 (Y/N)', + primary key (SCHED_NAME, ENTRY_ID) +) + comment 'Quartz 실행된 트리거 정보 - 현재 실행 중이거나 실행된 트리거 상태 추적'; + diff --git a/DB-DDL/maria/ddl/xitframework/qrtz_job_details.sql b/DB-DDL/maria/ddl/xitframework/qrtz_job_details.sql new file mode 100644 index 0000000..8af834f --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/qrtz_job_details.sql @@ -0,0 +1,16 @@ +create table qrtz_job_details +( + SCHED_NAME varchar(120) not null comment '스케줄러 이름 (여러 스케줄러 인스턴스 구분용)', + JOB_NAME varchar(200) not null comment '작업 이름 (작업의 고유 식별자)', + JOB_GROUP varchar(200) not null comment '작업 그룹 (작업들을 논리적으로 그룹화)', + DESCRIPTION varchar(250) null comment '작업에 대한 설명', + JOB_CLASS_NAME varchar(250) not null comment '실제 작업을 수행할 Java 클래스의 전체 경로', + IS_DURABLE varchar(1) not null comment '작업 영구성 여부 (Y/N) - 스케줄러 재시작 후에도 유지할지 결정', + IS_NONCONCURRENT varchar(1) not null comment '동시 실행 방지 여부 (Y/N) - 같은 작업의 중복 실행 방지', + IS_UPDATE_DATA varchar(1) not null comment '데이터 업데이트 여부 (Y/N) - 작업 실행 시 데이터 업데이트 허용', + REQUESTS_RECOVERY varchar(1) not null comment '복구 요청 여부 (Y/N) - 스케줄러 장애 시 복구 대상 여부', + JOB_DATA blob null comment '작업 실행 시 전달할 데이터 (직렬화된 객체)', + primary key (SCHED_NAME, JOB_NAME, JOB_GROUP) +) + comment 'Quartz 작업 상세 정보 저장 테이블 - 스케줄링될 작업의 메타데이터와 설정 정보'; + diff --git a/DB-DDL/maria/ddl/xitframework/qrtz_locks.sql b/DB-DDL/maria/ddl/xitframework/qrtz_locks.sql new file mode 100644 index 0000000..5f7b27f --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/qrtz_locks.sql @@ -0,0 +1,8 @@ +create table qrtz_locks +( + SCHED_NAME varchar(120) not null comment '스케줄러 이름', + LOCK_NAME varchar(40) not null comment '락 이름 (TRIGGER_ACCESS, JOB_ACCESS, CALENDAR_ACCESS 등)', + primary key (SCHED_NAME, LOCK_NAME) +) + comment 'Quartz 스케줄러 락 정보 - 클러스터 환경에서 동기화를 위한 락 관리'; + diff --git a/DB-DDL/maria/ddl/xitframework/qrtz_paused_trigger_grps.sql b/DB-DDL/maria/ddl/xitframework/qrtz_paused_trigger_grps.sql new file mode 100644 index 0000000..e70de6b --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/qrtz_paused_trigger_grps.sql @@ -0,0 +1,8 @@ +create table qrtz_paused_trigger_grps +( + SCHED_NAME varchar(120) not null comment '스케줄러 이름', + TRIGGER_GROUP varchar(200) not null comment '일시 중지된 트리거 그룹 이름', + primary key (SCHED_NAME, TRIGGER_GROUP) +) + comment 'Quartz 일시 중지된 트리거 그룹 - 일시 중지된 트리거 그룹 정보'; + diff --git a/DB-DDL/maria/ddl/xitframework/qrtz_scheduler_state.sql b/DB-DDL/maria/ddl/xitframework/qrtz_scheduler_state.sql new file mode 100644 index 0000000..bdf1352 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/qrtz_scheduler_state.sql @@ -0,0 +1,10 @@ +create table qrtz_scheduler_state +( + SCHED_NAME varchar(120) not null comment '스케줄러 이름', + INSTANCE_NAME varchar(200) not null comment '스케줄러 인스턴스 고유 이름', + LAST_CHECKIN_TIME bigint(13) not null comment '마지막 체크인 시간 (Unix timestamp)', + CHECKIN_INTERVAL bigint(13) not null comment '체크인 간격 (밀리초 단위)', + primary key (SCHED_NAME, INSTANCE_NAME) +) + comment 'Quartz 스케줄러 상태 정보 - 클러스터 환경에서 인스턴스 상태 관리'; + diff --git a/DB-DDL/maria/ddl/xitframework/qrtz_simple_triggers.sql b/DB-DDL/maria/ddl/xitframework/qrtz_simple_triggers.sql new file mode 100644 index 0000000..1dcaa8a --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/qrtz_simple_triggers.sql @@ -0,0 +1,14 @@ +create table qrtz_simple_triggers +( + SCHED_NAME varchar(120) not null comment '스케줄러 이름', + TRIGGER_NAME varchar(200) not null comment '트리거 이름', + TRIGGER_GROUP varchar(200) not null comment '트리거 그룹', + REPEAT_COUNT bigint(7) not null comment '반복 횟수 (-1: 무제한, 0: 1회만, 양수: 지정 횟수)', + REPEAT_INTERVAL bigint(12) not null comment '반복 간격 (밀리초 단위)', + TIMES_TRIGGERED bigint(10) not null comment '지금까지 실행된 횟수', + primary key (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP), + constraint qrtz_simple_triggers_ibfk_1 + foreign key (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP) references qrtz_triggers (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP) +) + comment 'Quartz 단순 트리거 정보 - 고정 간격으로 반복 실행하는 트리거'; + diff --git a/DB-DDL/maria/ddl/xitframework/qrtz_simprop_triggers.sql b/DB-DDL/maria/ddl/xitframework/qrtz_simprop_triggers.sql new file mode 100644 index 0000000..b606af1 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/qrtz_simprop_triggers.sql @@ -0,0 +1,22 @@ +create table qrtz_simprop_triggers +( + SCHED_NAME varchar(120) not null comment '스케줄러 이름', + TRIGGER_NAME varchar(200) not null comment '트리거 이름', + TRIGGER_GROUP varchar(200) not null comment '트리거 그룹', + STR_PROP_1 varchar(512) null comment '문자열 속성 1', + STR_PROP_2 varchar(512) null comment '문자열 속성 2', + STR_PROP_3 varchar(512) null comment '문자열 속성 3', + INT_PROP_1 int null comment '정수 속성 1', + INT_PROP_2 int null comment '정수 속성 2', + LONG_PROP_1 bigint null comment '긴 정수 속성 1', + LONG_PROP_2 bigint null comment '긴 정수 속성 2', + DEC_PROP_1 decimal(13, 4) null comment '소수점 속성 1 (13자리, 소수점 4자리)', + DEC_PROP_2 decimal(13, 4) null comment '소수점 속성 2 (13자리, 소수점 4자리)', + BOOL_PROP_1 varchar(1) null comment '불린 속성 1 (Y/N)', + BOOL_PROP_2 varchar(1) null comment '불린 속성 2 (Y/N)', + primary key (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP), + constraint qrtz_simprop_triggers_ibfk_1 + foreign key (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP) references qrtz_triggers (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP) +) + comment 'Quartz 속성 기반 트리거 정보 - 다양한 데이터 타입의 속성을 가진 트리거'; + diff --git a/DB-DDL/maria/ddl/xitframework/qrtz_triggers.sql b/DB-DDL/maria/ddl/xitframework/qrtz_triggers.sql new file mode 100644 index 0000000..44c3ac8 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/qrtz_triggers.sql @@ -0,0 +1,27 @@ +create table qrtz_triggers +( + SCHED_NAME varchar(120) not null comment '스케줄러 이름', + TRIGGER_NAME varchar(200) not null comment '트리거 이름 (트리거의 고유 식별자)', + TRIGGER_GROUP varchar(200) not null comment '트리거 그룹 (트리거들을 논리적으로 그룹화)', + JOB_NAME varchar(200) not null comment '연결된 작업 이름 (QRTZ_JOB_DETAILS 참조)', + JOB_GROUP varchar(200) not null comment '연결된 작업 그룹 (QRTZ_JOB_DETAILS 참조)', + DESCRIPTION varchar(250) null comment '트리거에 대한 설명', + NEXT_FIRE_TIME bigint(13) null comment '다음 실행 예정 시간 (Unix timestamp)', + PREV_FIRE_TIME bigint(13) null comment '이전 실행 시간 (Unix timestamp)', + PRIORITY int null comment '트리거 우선순위 (높을수록 우선 실행)', + TRIGGER_STATE varchar(16) not null comment '트리거 상태 (WAITING, ACQUIRED, EXECUTING, COMPLETE, PAUSED, BLOCKED, ERROR)', + TRIGGER_TYPE varchar(8) not null comment '트리거 타입 (SIMPLE, CRON, CALENDAR, BLOB, SIMPROP)', + START_TIME bigint(13) not null comment '트리거 시작 시간 (Unix timestamp)', + END_TIME bigint(13) null comment '트리거 종료 시간 (Unix timestamp, NULL이면 무제한)', + CALENDAR_NAME varchar(200) null comment '연결된 캘린더 이름 (QRTZ_CALENDARS 참조)', + MISFIRE_INSTR smallint(2) null comment '미스파이어 처리 지침 (실행 시간을 놓쳤을 때의 처리 방법)', + JOB_DATA blob null comment '트리거 실행 시 전달할 데이터 (직렬화된 객체)', + primary key (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP), + constraint qrtz_triggers_ibfk_1 + foreign key (SCHED_NAME, JOB_NAME, JOB_GROUP) references qrtz_job_details (SCHED_NAME, JOB_NAME, JOB_GROUP) +) + comment 'Quartz 트리거 정보 저장 테이블 - 작업 실행 조건과 스케줄 정의'; + +create index SCHED_NAME + on qrtz_triggers (SCHED_NAME, JOB_NAME, JOB_GROUP); + diff --git a/DB-DDL/maria/ddl/xitframework/seq_answer_id.sql b/DB-DDL/maria/ddl/xitframework/seq_answer_id.sql new file mode 100644 index 0000000..691f03d --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/seq_answer_id.sql @@ -0,0 +1,8 @@ +CREATE SEQUENCE seq_answer_id + START WITH 1000 + INCREMENT BY 1 + MINVALUE 1 + MAXVALUE 9223372036854775807 + CACHE 1000 + CYCLE; + diff --git a/DB-DDL/maria/ddl/xitframework/seq_batch_file_data_id.sql b/DB-DDL/maria/ddl/xitframework/seq_batch_file_data_id.sql new file mode 100644 index 0000000..6209b05 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/seq_batch_file_data_id.sql @@ -0,0 +1,8 @@ +CREATE SEQUENCE seq_batch_file_data_id + START WITH 1000 + INCREMENT BY 1 + MINVALUE 1 + MAXVALUE 9223372036854775807 + CACHE 1000 + CYCLE; + diff --git a/DB-DDL/maria/ddl/xitframework/seq_batch_file_data_success_id.sql b/DB-DDL/maria/ddl/xitframework/seq_batch_file_data_success_id.sql new file mode 100644 index 0000000..60f99c9 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/seq_batch_file_data_success_id.sql @@ -0,0 +1,8 @@ +CREATE SEQUENCE seq_batch_file_data_success_id + START WITH 1000 + INCREMENT BY 1 + MINVALUE 1 + MAXVALUE 9223372036854775807 + CACHE 1000 + CYCLE; + diff --git a/DB-DDL/maria/ddl/xitframework/seq_batch_file_retry_id.sql b/DB-DDL/maria/ddl/xitframework/seq_batch_file_retry_id.sql new file mode 100644 index 0000000..79ec6e9 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/seq_batch_file_retry_id.sql @@ -0,0 +1,7 @@ +-- 배치 파일 재처리 ID 시퀀스 +CREATE SEQUENCE seq_batch_file_retry_id + START WITH 1000 + INCREMENT BY 1 + MINVALUE 1 + MAXVALUE 99999999 + CYCLE; \ No newline at end of file diff --git a/DB-DDL/maria/ddl/xitframework/seq_batch_job_log_id.sql b/DB-DDL/maria/ddl/xitframework/seq_batch_job_log_id.sql new file mode 100644 index 0000000..015fe58 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/seq_batch_job_log_id.sql @@ -0,0 +1,7 @@ +CREATE SEQUENCE seq_batch_job_log_id + START WITH 1000 + INCREMENT BY 1 + MINVALUE 1 + MAXVALUE 9223372036854775807 + CACHE 1000 + CYCLE; \ No newline at end of file diff --git a/DB-DDL/maria/ddl/xitframework/seq_batch_zip_file_detail_log_id.sql b/DB-DDL/maria/ddl/xitframework/seq_batch_zip_file_detail_log_id.sql new file mode 100644 index 0000000..33cd838 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/seq_batch_zip_file_detail_log_id.sql @@ -0,0 +1,6 @@ +CREATE SEQUENCE seq_batch_zip_file_detail_log_id + START WITH 1000 + INCREMENT BY 1 + MINVALUE 1 + MAXVALUE 99999999 + CYCLE; \ No newline at end of file diff --git a/DB-DDL/maria/ddl/xitframework/seq_batch_zip_file_process_log_id.sql b/DB-DDL/maria/ddl/xitframework/seq_batch_zip_file_process_log_id.sql new file mode 100644 index 0000000..d1c55b2 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/seq_batch_zip_file_process_log_id.sql @@ -0,0 +1,6 @@ +CREATE SEQUENCE seq_batch_zip_file_process_log_id + START WITH 1000 + INCREMENT BY 1 + MINVALUE 1 + MAXVALUE 99999999 + CYCLE; \ No newline at end of file diff --git a/DB-DDL/maria/ddl/xitframework/seq_bbs_id.sql b/DB-DDL/maria/ddl/xitframework/seq_bbs_id.sql new file mode 100644 index 0000000..fbc47fb --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/seq_bbs_id.sql @@ -0,0 +1,8 @@ +CREATE SEQUENCE seq_bbs_id + START WITH 1000 + INCREMENT BY 1 + MINVALUE 1 + MAXVALUE 9223372036854775807 + CACHE 1000 + CYCLE; + diff --git a/DB-DDL/maria/ddl/xitframework/seq_comment_id.sql b/DB-DDL/maria/ddl/xitframework/seq_comment_id.sql new file mode 100644 index 0000000..3e9c85a --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/seq_comment_id.sql @@ -0,0 +1,8 @@ +CREATE SEQUENCE seq_comment_id + START WITH 1000 + INCREMENT BY 1 + MINVALUE 1 + MAXVALUE 9223372036854775807 + CACHE 1000 + CYCLE; + diff --git a/DB-DDL/maria/ddl/xitframework/seq_excel_sample_id.sql b/DB-DDL/maria/ddl/xitframework/seq_excel_sample_id.sql new file mode 100644 index 0000000..ca868ca --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/seq_excel_sample_id.sql @@ -0,0 +1,8 @@ +CREATE SEQUENCE seq_excel_sample_id + START WITH 1000 + INCREMENT BY 1 + MINVALUE 1 + MAXVALUE 9223372036854775807 + CACHE 1000 + CYCLE; + diff --git a/DB-DDL/maria/ddl/xitframework/seq_file_id.sql b/DB-DDL/maria/ddl/xitframework/seq_file_id.sql new file mode 100644 index 0000000..d757de9 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/seq_file_id.sql @@ -0,0 +1,8 @@ +CREATE SEQUENCE seq_file_id + START WITH 1000 + INCREMENT BY 1 + MINVALUE 1 + MAXVALUE 9999999999999999 + CACHE 1000 + CYCLE; + diff --git a/DB-DDL/maria/ddl/xitframework/seq_html_edit_file_id.sql b/DB-DDL/maria/ddl/xitframework/seq_html_edit_file_id.sql new file mode 100644 index 0000000..d0d5141 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/seq_html_edit_file_id.sql @@ -0,0 +1,8 @@ +CREATE SEQUENCE seq_html_edit_file_id + START WITH 1000 + INCREMENT BY 1 + MINVALUE 1 + MAXVALUE 9223372036854775807 + CACHE 1000 + CYCLE; + diff --git a/DB-DDL/maria/ddl/xitframework/seq_login_log_id.sql b/DB-DDL/maria/ddl/xitframework/seq_login_log_id.sql new file mode 100644 index 0000000..582950d --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/seq_login_log_id.sql @@ -0,0 +1,7 @@ +CREATE SEQUENCE seq_login_log_id + START WITH 1000 + INCREMENT BY 1 + MINVALUE 1 + MAXVALUE 9999999999999999 + CACHE 1000 + CYCLE; \ No newline at end of file diff --git a/DB-DDL/maria/ddl/xitframework/seq_menu_id.sql b/DB-DDL/maria/ddl/xitframework/seq_menu_id.sql new file mode 100644 index 0000000..54ad529 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/seq_menu_id.sql @@ -0,0 +1,8 @@ +CREATE SEQUENCE seq_menu_id + START WITH 1000 + INCREMENT BY 1 + MINVALUE 1 + MAXVALUE 9999999999999999 + CACHE 1000 + CYCLE; + diff --git a/DB-DDL/maria/ddl/xitframework/seq_notice_id.sql b/DB-DDL/maria/ddl/xitframework/seq_notice_id.sql new file mode 100644 index 0000000..8431ae2 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/seq_notice_id.sql @@ -0,0 +1,8 @@ +CREATE SEQUENCE seq_notice_id + START WITH 1000 + INCREMENT BY 1 + MINVALUE 1 + MAXVALUE 9223372036854775807 + CACHE 1000 + CYCLE; + diff --git a/DB-DDL/maria/ddl/xitframework/seq_notification_id.sql b/DB-DDL/maria/ddl/xitframework/seq_notification_id.sql new file mode 100644 index 0000000..a531ccb --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/seq_notification_id.sql @@ -0,0 +1,8 @@ +CREATE SEQUENCE seq_notification_id + START WITH 1000 + INCREMENT BY 1 + MINVALUE 1 + MAXVALUE 9223372036854775807 + CACHE 1000 + CYCLE; + diff --git a/DB-DDL/maria/ddl/xitframework/seq_notification_target_id.sql b/DB-DDL/maria/ddl/xitframework/seq_notification_target_id.sql new file mode 100644 index 0000000..b60cdaa --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/seq_notification_target_id.sql @@ -0,0 +1,8 @@ +CREATE SEQUENCE seq_notification_target_id + START WITH 1000 + INCREMENT BY 1 + MINVALUE 1 + MAXVALUE 9223372036854775807 + CACHE 1000 + CYCLE; + diff --git a/DB-DDL/maria/ddl/xitframework/seq_notification_target_setting_id.sql b/DB-DDL/maria/ddl/xitframework/seq_notification_target_setting_id.sql new file mode 100644 index 0000000..5f06097 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/seq_notification_target_setting_id.sql @@ -0,0 +1,6 @@ +CREATE SEQUENCE seq_notification_target_setting_id + START WITH 1000 + INCREMENT BY 1 + MINVALUE 1 + MAXVALUE 99999999 + CACHE 1; \ No newline at end of file diff --git a/DB-DDL/maria/ddl/xitframework/seq_post_id.sql b/DB-DDL/maria/ddl/xitframework/seq_post_id.sql new file mode 100644 index 0000000..7d7ff32 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/seq_post_id.sql @@ -0,0 +1,8 @@ +CREATE SEQUENCE seq_post_id + START WITH 1000 + INCREMENT BY 1 + MINVALUE 1 + MAXVALUE 9223372036854775807 + CACHE 1000 + CYCLE; + diff --git a/DB-DDL/maria/ddl/xitframework/seq_schedule_id.sql b/DB-DDL/maria/ddl/xitframework/seq_schedule_id.sql new file mode 100644 index 0000000..0c8062e --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/seq_schedule_id.sql @@ -0,0 +1,8 @@ +CREATE SEQUENCE seq_schedule_id + START WITH 1000 + INCREMENT BY 1 + MINVALUE 1 + MAXVALUE 9223372036854775807 + CACHE 1000 + CYCLE; + diff --git a/DB-DDL/maria/ddl/xitframework/seq_user_id.sql b/DB-DDL/maria/ddl/xitframework/seq_user_id.sql new file mode 100644 index 0000000..f698071 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/seq_user_id.sql @@ -0,0 +1,8 @@ +CREATE SEQUENCE seq_user_id + START WITH 1000 + INCREMENT BY 1 + MINVALUE 1 + MAXVALUE 9999999999999999 + CACHE 1000 + CYCLE; + diff --git a/DB-DDL/maria/ddl/xitframework/tb_batch_file_data.sql b/DB-DDL/maria/ddl/xitframework/tb_batch_file_data.sql new file mode 100644 index 0000000..299035f --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/tb_batch_file_data.sql @@ -0,0 +1,30 @@ +CREATE TABLE TB_BATCH_FILE_DATA +( + FILE_DATA_ID VARCHAR(20) NOT NULL COMMENT '파일데이터ID' + PRIMARY KEY, + FILE_NM VARCHAR(255) NOT NULL COMMENT '파일 명', + LINE_NUMBER INT NOT NULL COMMENT '라인 번호', + COLUMN1 VARCHAR(100) NULL COMMENT '컬럼1', + COLUMN2 VARCHAR(100) NULL COMMENT '컬럼2', + COLUMN3 VARCHAR(100) NULL COMMENT '컬럼3', + COLUMN4 VARCHAR(100) NULL COMMENT '컬럼4', + COLUMN5 VARCHAR(100) NULL COMMENT '컬럼5', + RAW_DATA TEXT NULL COMMENT '원시 데이터', + PROCESS_STATUS VARCHAR(20) DEFAULT 'SUCCESS' NULL COMMENT '처리 상태(SUCCESS/ERROR)', + ERROR_MESSAGE TEXT NULL COMMENT '에러 메시지', + PROCESS_DTTM DATETIME DEFAULT CURRENT_TIMESTAMP() NULL COMMENT '처리 일시', + REG_DTTM DATETIME DEFAULT CURRENT_TIMESTAMP() NULL COMMENT '등록 일시', + RGTR VARCHAR(20) NULL COMMENT '등록자', + MDFCN_DTTM DATETIME DEFAULT CURRENT_TIMESTAMP() NULL ON UPDATE CURRENT_TIMESTAMP() COMMENT '수정 일시', + MDFR VARCHAR(20) NULL COMMENT '수정자' +) + COMMENT '배치파일처리데이터' COLLATE = UTF8MB4_UNICODE_CI; + +CREATE INDEX IDX_FILE_NM + ON TB_BATCH_FILE_DATA (FILE_NM); + +CREATE INDEX IDX_PROCESS_DTTM + ON TB_BATCH_FILE_DATA (PROCESS_DTTM); + +CREATE INDEX IDX_PROCESS_STATUS + ON TB_BATCH_FILE_DATA (PROCESS_STATUS); diff --git a/DB-DDL/maria/ddl/xitframework/tb_batch_file_data_success.sql b/DB-DDL/maria/ddl/xitframework/tb_batch_file_data_success.sql new file mode 100644 index 0000000..a9855b6 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/tb_batch_file_data_success.sql @@ -0,0 +1,29 @@ +CREATE TABLE TB_BATCH_FILE_DATA_SUCCESS +( + SUCCESS_DATA_ID VARCHAR(20) NOT NULL COMMENT '성공데이터ID' + PRIMARY KEY, + FILE_NM VARCHAR(255) NOT NULL COMMENT '파일 명', + LINE_NUMBER INT NOT NULL COMMENT '라인 번호', + COLUMN1 VARCHAR(100) NULL COMMENT '컬럼1', + COLUMN2 VARCHAR(100) NULL COMMENT '컬럼2', + COLUMN3 VARCHAR(100) NULL COMMENT '컬럼3', + COLUMN4 VARCHAR(100) NULL COMMENT '컬럼4', + COLUMN5 VARCHAR(100) NULL COMMENT '컬럼5', + RAW_DATA TEXT NULL COMMENT '원시 데이터', + PROCESS_DTTM DATETIME DEFAULT CURRENT_TIMESTAMP() NULL COMMENT '처리 일시', + REG_DTTM DATETIME DEFAULT CURRENT_TIMESTAMP() NULL COMMENT '등록 일시', + RGTR VARCHAR(20) NULL COMMENT '등록자', + MDFCN_DTTM DATETIME DEFAULT CURRENT_TIMESTAMP() NULL ON UPDATE CURRENT_TIMESTAMP() COMMENT '수정 일시', + MDFR VARCHAR(20) NULL COMMENT '수정자' +) + COMMENT '배치파일처리성공데이터' COLLATE = UTF8MB4_UNICODE_CI; + +CREATE INDEX IDX_COLUMN1 + ON TB_BATCH_FILE_DATA_SUCCESS (COLUMN1); + +CREATE INDEX IDX_FILE_NM + ON TB_BATCH_FILE_DATA_SUCCESS (FILE_NM); + +CREATE INDEX IDX_PROCESS_DTTM + ON TB_BATCH_FILE_DATA_SUCCESS (PROCESS_DTTM); + diff --git a/DB-DDL/maria/ddl/xitframework/tb_batch_file_retry.sql b/DB-DDL/maria/ddl/xitframework/tb_batch_file_retry.sql new file mode 100644 index 0000000..fd87345 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/tb_batch_file_retry.sql @@ -0,0 +1,33 @@ +CREATE TABLE TB_BATCH_FILE_RETRY +( + RETRY_ID VARCHAR(20) NOT NULL COMMENT '재처리ID' + PRIMARY KEY, + ORIGINAL_FILE_NM VARCHAR(255) NOT NULL COMMENT '원본 파일명(파일명이 동일할경우 정확한 처리를위해 TIMESTAMP 추가)', + RETRY_FILE_NM VARCHAR(255) NOT NULL COMMENT '재 처리 파일명', + RETRY_CNT INT DEFAULT 1 NOT NULL COMMENT '재시도 횟수', + MAX_RETRY_CNT INT DEFAULT 3 NOT NULL COMMENT '최대 재시도 횟수', + RETRY_STATUS VARCHAR(20) DEFAULT 'PENDING' NOT NULL COMMENT '재처리 상태(PENDING/PROCESSING/SUCCESS/FAILED/EXCEEDED)', + ERROR_TYPE VARCHAR(50) NULL COMMENT '에러 유형(FORMAT_ERROR/DATA_ERROR/SYSTEM_ERROR)', + ERROR_MESSAGE TEXT NULL COMMENT '에러 메시지', + RETRY_DTTM DATETIME DEFAULT CURRENT_TIMESTAMP() NULL COMMENT '재처리 일시', + NEXT_RETRY_DTTM DATETIME NULL COMMENT '다음 재처리 예정일시', + COMPLETED_DTTM DATETIME NULL COMMENT '완료 일시', + REG_DTTM DATETIME DEFAULT CURRENT_TIMESTAMP() NULL COMMENT '등록 일시', + RGTR VARCHAR(20) NULL COMMENT '등록자', + MDFCN_DTTM DATETIME DEFAULT CURRENT_TIMESTAMP() NULL ON UPDATE CURRENT_TIMESTAMP() COMMENT '수정일시', + MDFR VARCHAR(20) NULL COMMENT '수정자' +) + COMMENT '배치파일재처리이력' COLLATE = UTF8MB4_UNICODE_CI; + +CREATE INDEX IDX_NEXT_RETRY_DTTM + ON TB_BATCH_FILE_RETRY (NEXT_RETRY_DTTM); + +CREATE INDEX IDX_ORIGINAL_FILE_NM + ON TB_BATCH_FILE_RETRY (ORIGINAL_FILE_NM); + +CREATE INDEX IDX_RETRY_DTTM + ON TB_BATCH_FILE_RETRY (RETRY_DTTM); + +CREATE INDEX IDX_RETRY_STATUS + ON TB_BATCH_FILE_RETRY (RETRY_STATUS); + diff --git a/DB-DDL/maria/ddl/xitframework/tb_batch_job_execution.sql b/DB-DDL/maria/ddl/xitframework/tb_batch_job_execution.sql new file mode 100644 index 0000000..c16a149 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/tb_batch_job_execution.sql @@ -0,0 +1,28 @@ +CREATE TABLE TB_BATCH_JOB_EXECUTION +( + EXECUTION_ID VARCHAR(36) NOT NULL COMMENT '실행 ID (UUID) - 배치 작업 실행의 고유 식별자' + PRIMARY KEY, + JOB_NM VARCHAR(200) NOT NULL COMMENT '작업 이름 - 실행된 배치 작업의 이름', + JOB_GROUP VARCHAR(200) NOT NULL COMMENT '작업 그룹 - 실행된 배치 작업의 그룹', + START_DTTM DATETIME NOT NULL COMMENT '시작 일시 - 배치 작업 실행 시작 시점', + END_DTTM DATETIME NULL COMMENT '종료 일시 - 배치 작업 실행 완료 시점 (실행 중이면 NULL)', + STATUS_CD VARCHAR(20) NOT NULL COMMENT '상태 (STARTED, COMPLETED, FAILED) - 현재 실행 상태', + EXIT_CD VARCHAR(20) NULL COMMENT '종료 코드 - 성공(COMPLETED) 또는 실패(FAILED) 구분', + EXIT_MESSAGE TEXT NULL COMMENT '종료 메시지 - 에러 발생 시 상세 메시지 또는 성공 메시지', + REG_DTTM DATETIME DEFAULT CURRENT_TIMESTAMP() NOT NULL COMMENT '등록 일시', + SERVER_INFO VARCHAR(1000) NULL COMMENT '실행서버 정보' +) + COMMENT '배치 작업 실행 결과 저장 테이블 - 배치 작업의 실행 이력과 결과 추적'; + +CREATE INDEX IDX_BATCH_JOB_NM + ON TB_BATCH_JOB_EXECUTION (JOB_NM) + COMMENT '작업 이름 인덱스 - 작업별 조회 성능 향상'; + +CREATE INDEX IDX_BATCH_JOB_START_DTTM + ON TB_BATCH_JOB_EXECUTION (START_DTTM) + COMMENT '시작 시간 인덱스 - 시간별 조회 성능 향상'; + +CREATE INDEX IDX_BATCH_JOB_STATUS_CD + ON TB_BATCH_JOB_EXECUTION (STATUS_CD) + COMMENT '상태 인덱스 - 상태별 조회 성능 향상'; + diff --git a/DB-DDL/maria/ddl/xitframework/tb_batch_job_info.sql b/DB-DDL/maria/ddl/xitframework/tb_batch_job_info.sql new file mode 100644 index 0000000..1122024 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/tb_batch_job_info.sql @@ -0,0 +1,22 @@ +create table tb_batch_job_info +( + JOB_ID varchar(36) not null comment '작업 ID (UUID) - 배치 작업의 고유 식별자' + primary key, + JOB_NM varchar(200) not null comment '작업 이름 - 배치 작업의 이름 (사용자가 정의)', + JOB_GROUP varchar(200) not null comment '작업 그룹 - 배치 작업의 그룹 (논리적 분류)', + JOB_CLASS varchar(250) not null comment '작업 클래스 - 실제 작업을 수행할 Java 클래스의 전체 경로', + CRON_EXPRESSION varchar(200) not null comment 'Cron 표현식 - 작업 실행 스케줄 정의 (예: "0 0 2 * * ?" - 매일 새벽 2시)', + JOB_DC varchar(500) null comment '작업 설명 - 배치 작업에 대한 상세 설명', + STATUS_CD varchar(20) default 'ACTIVE' not null comment '상태 (ACTIVE, PAUSED, DELETED) - 작업의 현재 상태', + LAST_EXECUTION_ID varchar(36) null comment '마지막 실행 ID - 가장 최근에 실행된 배치 작업의 실행 ID', + REG_DTTM datetime default current_timestamp() not null comment '등록 일시', + MDFCN_DTTM datetime default current_timestamp() not null on update current_timestamp() comment '수정 일시 - 작업 정보 수정 시점 (자동 업데이트)', + constraint UK_BATCH_JOB_NM_GROUP + unique (JOB_NM, JOB_GROUP) comment '작업 이름+그룹 유니크 제약 - 동일한 이름과 그룹 조합 방지' +) + comment '배치 작업 정보 저장 테이블 - 배치 작업의 기본 정보와 스케줄링 설정 관리'; + +create index IDX_BATCH_JOB_INFO_STATUS_CD + on tb_batch_job_info (STATUS_CD) + comment '상태 인덱스 - 상태별 조회 성능 향상'; + diff --git a/DB-DDL/maria/ddl/xitframework/tb_batch_job_log.sql b/DB-DDL/maria/ddl/xitframework/tb_batch_job_log.sql new file mode 100644 index 0000000..9d75c2c --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/tb_batch_job_log.sql @@ -0,0 +1,21 @@ +-- 배치 작업 로그 ID 시퀀스 생성 +CREATE SEQUENCE IF NOT EXISTS seq_batch_job_log_id START WITH 1 INCREMENT BY 1; + +create table tb_batch_job_log +( + LOG_ID varchar(20) comment '로그 ID - 로그 레코드의 고유 식별자 (시퀀스 기반)' + primary key, + EXECUTION_ID varchar(36) not null comment '실행 ID (TB_BATCH_JOB_EXECUTION.EXECUTION_ID) - 연결된 배치 작업 실행', + LOG_DTTM datetime not null comment '로그 일시 - 로그가 발생한 정확한 시점', + LOG_LEVEL varchar(10) not null comment '로그 레벨 (INFO, WARN, ERROR) - 로그의 중요도 수준', + LOG_MESSAGE text not null comment '로그 메시지 - 실제 로그 내용 (상세 정보, 에러 메시지 등)', + REG_DTTM datetime default current_timestamp() not null comment '등록 일시 - 레코드 생성 시점', + constraint tb_batch_job_log_ibfk_1 + foreign key (EXECUTION_ID) references tb_batch_job_execution (EXECUTION_ID) +) + comment '배치 작업 실행 로그 저장 테이블 - 배치 작업 실행 과정의 상세 로그 기록'; + +create index IDX_BATCH_LOG_EXECUTION_ID + on tb_batch_job_log (EXECUTION_ID) + comment '실행 ID 인덱스 - 특정 실행의 로그 조회 성능 향상'; + diff --git a/DB-DDL/maria/ddl/xitframework/tb_batch_job_param.sql b/DB-DDL/maria/ddl/xitframework/tb_batch_job_param.sql new file mode 100644 index 0000000..844cf2b --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/tb_batch_job_param.sql @@ -0,0 +1,22 @@ +-- 배치 작업 파라미터 ID 시퀀스 생성 +CREATE SEQUENCE IF NOT EXISTS seq_batch_job_param_id START WITH 1 INCREMENT BY 1; + +create table tb_batch_job_param +( + PARAM_ID varchar(20) comment '파라미터 ID - 파라미터 레코드의 고유 식별자 (시퀀스 기반)' + primary key, + JOB_ID varchar(36) not null comment '작업 ID (TB_BATCH_JOB_INFO.JOB_ID) - 연결된 배치 작업', + PARAM_NM varchar(100) not null comment '파라미터 이름 - 파라미터의 식별자', + PARAM_VALUE varchar(500) null comment '파라미터 값 - 실제 파라미터 데이터', + PARAM_TYPE varchar(50) not null comment '파라미터 타입 (STRING, NUMBER, DATE, BOOLEAN) - 데이터 타입 정의', + REG_DTTM datetime default current_timestamp() not null comment '등록 일시 - 파라미터 등록 시점', + MDFCN_DTTM datetime default current_timestamp() not null on update current_timestamp() comment '수정 일시 - 파라미터 수정 시점 (자동 업데이트)', + constraint tb_batch_job_param_ibfk_1 + foreign key (JOB_ID) references tb_batch_job_info (JOB_ID) +) + comment '배치 작업 파라미터 저장 테이블 - 배치 작업 실행 시 필요한 파라미터 정보 관리'; + +create index IDX_BATCH_PARAM_JOB_ID + on tb_batch_job_param (JOB_ID) + comment '작업 ID 인덱스 - 특정 작업의 파라미터 조회 성능 향상'; + diff --git a/DB-DDL/maria/ddl/xitframework/tb_batch_zip_file_detail_log.sql b/DB-DDL/maria/ddl/xitframework/tb_batch_zip_file_detail_log.sql new file mode 100644 index 0000000..82f4a8c --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/tb_batch_zip_file_detail_log.sql @@ -0,0 +1,36 @@ +CREATE TABLE TB_BATCH_ZIP_FILE_DETAIL_LOG +( + DETAIL_LOG_ID VARCHAR(20) NOT NULL COMMENT '상세 로그 ID' + PRIMARY KEY, + LOG_ID VARCHAR(20) NOT NULL COMMENT '로그 ID', + FILE_NM VARCHAR(255) NOT NULL COMMENT '파일 명', + FILE_PATH VARCHAR(500) NOT NULL COMMENT '파일 경로', + FILE_SIZE BIGINT DEFAULT 0 NULL COMMENT '파일 크기(BYTES)', + FILE_TYPE VARCHAR(50) NULL COMMENT '파일 타입', + IMAGE_YN CHAR DEFAULT 'N' NULL COMMENT '이미지 여부(Y/N)', + CORRUPTED_YN CHAR DEFAULT 'N' NULL COMMENT '손상 여부(Y/N)', + PROCESS_STATUS VARCHAR(20) DEFAULT 'SUCCESS' NULL COMMENT '처리 상태(SUCCESS/ERROR)', + ERROR_MESSAGE TEXT NULL COMMENT '에러 메시지', + PROCESS_DTTM DATETIME DEFAULT CURRENT_TIMESTAMP() NULL COMMENT '처리 일시', + REG_DTTM DATETIME DEFAULT CURRENT_TIMESTAMP() NULL COMMENT '등록 일시', + RGTR VARCHAR(20) NULL COMMENT '등록자', + MDFCN_DTTM DATETIME DEFAULT CURRENT_TIMESTAMP() NULL ON UPDATE CURRENT_TIMESTAMP() COMMENT '수정 일시', + MDFR VARCHAR(20) NULL COMMENT '수정자', + CONSTRAINT FK_BATCH_ZIP_DETAIL_LOG_ID + FOREIGN KEY (LOG_ID) REFERENCES TB_BATCH_ZIP_FILE_PROCESS_LOG (LOG_ID) + ON DELETE CASCADE +) + COMMENT 'ZIP파일처리상세로그' COLLATE = UTF8MB4_UNICODE_CI; + +CREATE INDEX IDX_FILE_NM + ON TB_BATCH_ZIP_FILE_DETAIL_LOG (FILE_NM); + +CREATE INDEX IDX_IMAGE_YN + ON TB_BATCH_ZIP_FILE_DETAIL_LOG (IMAGE_YN); + +CREATE INDEX IDX_LOG_ID + ON TB_BATCH_ZIP_FILE_DETAIL_LOG (LOG_ID); + +CREATE INDEX IDX_PROCESS_STATUS + ON TB_BATCH_ZIP_FILE_DETAIL_LOG (PROCESS_STATUS); + diff --git a/DB-DDL/maria/ddl/xitframework/tb_batch_zip_file_process_log.sql b/DB-DDL/maria/ddl/xitframework/tb_batch_zip_file_process_log.sql new file mode 100644 index 0000000..4cbad57 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/tb_batch_zip_file_process_log.sql @@ -0,0 +1,34 @@ +CREATE TABLE TB_BATCH_ZIP_FILE_PROCESS_LOG +( + LOG_ID VARCHAR(20) NOT NULL COMMENT '로그ID' + PRIMARY KEY, + ZIP_FILE_NM VARCHAR(255) NOT NULL COMMENT 'ZIP 파일 명', + ZIP_FILE_PATH VARCHAR(500) NOT NULL COMMENT 'ZIP 파일 경로', + EXTRACT_PATH VARCHAR(500) NULL COMMENT '압축해제 경로', + ARCHIVE_PATH VARCHAR(500) NULL COMMENT '원본보관 경로', + TOTAL_FILE_CNT INT DEFAULT 0 NULL COMMENT '총 파일수', + SUCCESS_FILE_CNT INT DEFAULT 0 NULL COMMENT '성공 파일 수', + ERROR_FILE_CNT INT DEFAULT 0 NULL COMMENT '실패 파일 수', + IMAGE_FILE_CNT INT DEFAULT 0 NULL COMMENT '이미지 파일 수', + NON_IMAGE_FILE_CNT INT DEFAULT 0 NULL COMMENT '비 이미지 파일 수', + CORRUPTED_FILE_CNT INT DEFAULT 0 NULL COMMENT '손상 파일 수', + PROCESS_STATUS VARCHAR(20) DEFAULT 'PROCESSING' NULL COMMENT '처리 상태(PROCESSING/SUCCESS/ERROR)', + ERROR_MESSAGE TEXT NULL COMMENT '에러 메시지', + START_DTTM DATETIME DEFAULT CURRENT_TIMESTAMP() NULL COMMENT '시작 일시', + END_DTTM DATETIME NULL COMMENT '종료 일시', + REG_DTTM DATETIME DEFAULT CURRENT_TIMESTAMP() NULL COMMENT '등록 일시', + RGTR VARCHAR(20) NULL COMMENT '등록자', + MDFCN_DTTM DATETIME DEFAULT CURRENT_TIMESTAMP() NULL ON UPDATE CURRENT_TIMESTAMP() COMMENT '수정 일시', + MDFR VARCHAR(20) NULL COMMENT '수정자' +) + COMMENT 'ZIP파일처리로그' COLLATE = UTF8MB4_UNICODE_CI; + +CREATE INDEX IDX_PROCESS_STATUS + ON TB_BATCH_ZIP_FILE_PROCESS_LOG (PROCESS_STATUS); + +CREATE INDEX IDX_START_DTTM + ON TB_BATCH_ZIP_FILE_PROCESS_LOG (START_DTTM); + +CREATE INDEX IDX_ZIP_FILE_NM + ON TB_BATCH_ZIP_FILE_PROCESS_LOG (ZIP_FILE_NM); + diff --git a/DB-DDL/maria/ddl/xitframework/tb_bbs_comment.sql b/DB-DDL/maria/ddl/xitframework/tb_bbs_comment.sql new file mode 100644 index 0000000..d0f1453 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/tb_bbs_comment.sql @@ -0,0 +1,17 @@ +create table tb_bbs_comment +( + COMMENT_ID varchar(20) not null comment '댓글 ID' + primary key, + POST_ID varchar(20) not null comment '게시물 ID', + CONTENT text not null comment '내용', + PARENT_COMMENT_ID varchar(20) null comment '부모 댓글 ID', + DEPTH int default 0 null comment '댓글 깊이 (0: 원댓글, 1: 대댓글)', + REG_DTTM datetime null comment '등록 일시', + RGTR varchar(20) null comment '등록자', + MDFCN_DTTM datetime null comment '수정 일시', + MDFR varchar(20) null comment '수정자', + constraint fk_bbs_comment_post + foreign key (POST_ID) references tb_bbs_post (POST_ID) +) + comment '댓글'; + diff --git a/DB-DDL/maria/ddl/xitframework/tb_bbs_config.sql b/DB-DDL/maria/ddl/xitframework/tb_bbs_config.sql new file mode 100644 index 0000000..c5460f1 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/tb_bbs_config.sql @@ -0,0 +1,20 @@ +create table tb_bbs_config +( + BBS_ID varchar(20) not null comment '게시판 ID' + primary key, + BBS_NM varchar(100) not null comment '게시판 이름', + BBS_TYPE_CD varchar(20) not null comment '게시판 종류 (코드 테이블 참조)', + BBS_DC varchar(200) null comment '게시판 설명', + UPLOAD_YN varchar(1) default 'N' not null comment '업로드 가능 여부', + UPLOAD_FILE_CNT int default 0 null comment '업로드 파일 개수', + COMMENT_YN varchar(1) default 'N' not null comment '댓글 기능 사용 여부', + TUI_EDITOR_YN varchar(1) default 'N' not null comment 'TUI 에디터 사용 여부', + NOTICE_YN varchar(1) default 'N' not null comment '상단 고정 사용 여부', + USE_YN varchar(1) default 'Y' not null comment '사용 여부', + REG_DTTM datetime null comment '등록 일시', + RGTR varchar(20) null comment '등록자', + MDFCN_DTTM datetime null comment '수정 일시', + MDFR varchar(20) null comment '수정자' +) + comment '게시판 설정'; + diff --git a/DB-DDL/maria/ddl/xitframework/tb_bbs_file.sql b/DB-DDL/maria/ddl/xitframework/tb_bbs_file.sql new file mode 100644 index 0000000..83ac179 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/tb_bbs_file.sql @@ -0,0 +1,17 @@ +create table tb_bbs_file +( + FILE_ID varchar(20) not null comment '파일 ID' + primary key, + POST_ID varchar(20) not null comment '게시물 ID', + ORIGINAL_FILE_NM varchar(200) not null comment '원본 파일명', + STORED_FILE_NM varchar(200) not null comment '저장 파일명', + FILE_PATH varchar(200) not null comment '파일 경로', + FILE_SIZE bigint not null comment '파일 크기', + FILE_EXT varchar(10) not null comment '파일 확장자', + REG_DTTM datetime null comment '등록 일시', + RGTR varchar(20) null comment '등록자', + constraint fk_bbs_file_post + foreign key (POST_ID) references tb_bbs_post (POST_ID) +) + comment '게시판 파일'; + diff --git a/DB-DDL/maria/ddl/xitframework/tb_bbs_notice_sample.sql b/DB-DDL/maria/ddl/xitframework/tb_bbs_notice_sample.sql new file mode 100644 index 0000000..cc098ec --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/tb_bbs_notice_sample.sql @@ -0,0 +1,17 @@ +create table tb_bbs_notice_sample +( + NOTICE_ID varchar(20) not null comment '게시판 ID' + primary key, + TITLE varchar(200) not null comment '제목', + CONTENT text not null comment '내용', + WRITER_NM varchar(50) not null comment '작성자 이름', + VIEW_CNT int default 0 null comment '조회수', + NOTICE_YN varchar(1) default 'N' null comment '상단 고정 여부', + USE_YN varchar(1) default 'Y' not null comment '사용 여부', + REG_DTTM datetime null comment '등록 일시', + RGTR varchar(20) null comment '등록자', + MDFCN_DTTM datetime null comment '수정 일시', + MDFR varchar(20) null comment '수정자' +) + comment '게시판'; + diff --git a/DB-DDL/maria/ddl/xitframework/tb_bbs_notice_sample_file.sql b/DB-DDL/maria/ddl/xitframework/tb_bbs_notice_sample_file.sql new file mode 100644 index 0000000..99cad29 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/tb_bbs_notice_sample_file.sql @@ -0,0 +1,17 @@ +create table tb_bbs_notice_sample_file +( + FILE_ID varchar(20) not null comment '파일 ID' + primary key, + NOTICE_ID varchar(20) not null comment '게시판 ID', + ORIGINAL_FILE_NM varchar(200) not null comment '원본 파일명', + STORED_FILE_NM varchar(200) not null comment '저장 파일명', + FILE_PATH varchar(200) not null comment '파일 경로', + FILE_SIZE bigint not null comment '파일 크기', + FILE_EXT varchar(10) not null comment '파일 확장자', + REG_DTTM datetime null comment '등록 일시', + RGTR varchar(20) null comment '등록자', + constraint fk_bbs_notice_file_notice + foreign key (NOTICE_ID) references tb_bbs_notice_sample (NOTICE_ID) +) + comment '게시판 파일'; + diff --git a/DB-DDL/maria/ddl/xitframework/tb_bbs_post.sql b/DB-DDL/maria/ddl/xitframework/tb_bbs_post.sql new file mode 100644 index 0000000..f19618c --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/tb_bbs_post.sql @@ -0,0 +1,23 @@ +create table tb_bbs_post +( + POST_ID varchar(20) not null comment '게시물 ID' + primary key, + BBS_ID varchar(20) not null comment '게시판 ID', + TITLE varchar(200) not null comment '제목', + CONTENT text not null comment '내용', + WRITER_NM varchar(50) not null comment '작성자 이름', + VIEW_CNT int default 0 null comment '조회수', + NOTICE_YN varchar(1) default 'N' null comment '상단 고정 여부', + EMAIL_YN varchar(1) default 'N' null comment '이메일 발송 여부', + EMAIL varchar(100) null comment '이메일 주소', + ANSWER_STATUS_CD varchar(20) null comment '답변 상태 (WAITING: 대기중, COMPLETED: 답변완료), CODE_GROUP_ID:BBS_ANSWER_STATUS_CD', + USE_YN varchar(1) default 'Y' not null comment '사용 여부', + REG_DTTM datetime null comment '등록 일시', + RGTR varchar(20) null comment '등록자', + MDFCN_DTTM datetime null comment '수정 일시', + MDFR varchar(20) null comment '수정자', + constraint fk_bbs_post_config + foreign key (BBS_ID) references tb_bbs_config (BBS_ID) +) + comment '게시물'; + diff --git a/DB-DDL/maria/ddl/xitframework/tb_bbs_post_answer.sql b/DB-DDL/maria/ddl/xitframework/tb_bbs_post_answer.sql new file mode 100644 index 0000000..82ba917 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/tb_bbs_post_answer.sql @@ -0,0 +1,19 @@ +create table tb_bbs_post_answer +( + ANSWER_ID varchar(20) not null comment '답변 ID' + primary key, + POST_ID varchar(20) not null comment '게시물 ID', + CONTENT text not null comment '답변 내용', + ANSWER_DTTM datetime null comment '답변 일시', + REG_DTTM datetime null comment '등록 일시', + RGTR varchar(20) null comment '등록자', + MDFCN_DTTM datetime null comment '수정 일시', + MDFR varchar(20) null comment '수정자', + WRITER_NM varchar(50) not null comment '작성자 명', + constraint uk_bbs_post_answer_post_id + unique (POST_ID), + constraint fk_bbs_post_answer_post + foreign key (POST_ID) references tb_bbs_post (POST_ID) +) + comment '게시물 답변'; + diff --git a/DB-DDL/maria/ddl/xitframework/tb_calendar_schedule.sql b/DB-DDL/maria/ddl/xitframework/tb_calendar_schedule.sql new file mode 100644 index 0000000..cc681c9 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/tb_calendar_schedule.sql @@ -0,0 +1,20 @@ +create table tb_calendar_schedule +( + SCHEDULE_ID varchar(20) not null comment '일정 ID' + primary key, + CATEGORY_CD varchar(20) not null comment '범주 코드', + TITLE varchar(200) not null comment '제목', + PLACE varchar(200) null comment '장소', + CONTENT text null comment '내용', + START_DTTM datetime not null comment '시작 일시', + END_DTTM datetime not null comment '종료 일시', + PARTICIPANTS varchar(500) null comment '참여자', + ALL_DAY_YN varchar(1) default 'N' not null comment '종일 여부(Y/N)', + PRIVATE_YN varchar(1) default 'N' not null comment '비공개 여부(Y/N)', + REG_DTTM datetime null comment '등록 일시', + RGTR varchar(20) null comment '등록자', + MDFCN_DTTM datetime null comment '수정 일시', + MDFR varchar(20) null comment '수정자' +) + comment '일정 정보'; + diff --git a/DB-DDL/maria/ddl/xitframework/tb_cd_detail.sql b/DB-DDL/maria/ddl/xitframework/tb_cd_detail.sql new file mode 100644 index 0000000..39edbee --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/tb_cd_detail.sql @@ -0,0 +1,23 @@ +create table tb_cd_detail +( + CD_GROUP_ID varchar(20) not null comment '코드 그룹 ID', + CD_ID varchar(20) not null comment '코드 ID', + CD_NM varchar(100) not null comment '코드 이름', + CD_DC varchar(200) null comment '코드 설명', + SORT_ORDR int default 0 null comment '정렬 순서', + USE_YN varchar(1) not null comment '사용 여부', + ATTRIBUTE1 varchar(200) null comment '속성1', + ATTRIBUTE2 varchar(200) null comment '속성2', + ATTRIBUTE3 varchar(200) null comment '속성3', + ATTRIBUTE4 varchar(200) null comment '속성4', + ATTRIBUTE5 varchar(200) null comment '속성5', + REG_DTTM datetime null comment '등록 일시', + RGTR varchar(20) null comment '등록자', + MDFCN_DTTM datetime null comment '수정 일시', + MDFR varchar(20) null comment '수정자', + primary key (CD_GROUP_ID, CD_ID), + constraint fk_cd_detail_group + foreign key (CD_GROUP_ID) references tb_cd_group (CD_GROUP_ID) +) + comment '코드 상세'; + diff --git a/DB-DDL/maria/ddl/xitframework/tb_cd_group.sql b/DB-DDL/maria/ddl/xitframework/tb_cd_group.sql new file mode 100644 index 0000000..992e58a --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/tb_cd_group.sql @@ -0,0 +1,14 @@ +create table tb_cd_group +( + CD_GROUP_ID varchar(20) not null comment '코드 그룹 ID' + primary key, + CD_GROUP_NM varchar(100) not null comment '코드 그룹 이름', + CD_GROUP_DC varchar(200) null comment '코드 그룹 설명', + USE_YN varchar(1) not null comment '사용 여부', + REG_DTTM datetime null comment '등록 일시', + RGTR varchar(20) null comment '등록자', + MDFCN_DTTM datetime null comment '수정 일시', + MDFR varchar(20) null comment '수정자' +) + comment '코드 그룹'; + diff --git a/DB-DDL/maria/ddl/xitframework/tb_common_html_editor_file.sql b/DB-DDL/maria/ddl/xitframework/tb_common_html_editor_file.sql new file mode 100644 index 0000000..03721ec --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/tb_common_html_editor_file.sql @@ -0,0 +1,22 @@ +create table tb_common_html_editor_file +( + FILE_ID varchar(20) not null comment '파일 ID' + primary key, + MODULE_ID varchar(50) null comment '모듈 ID (선택적)', + ORIGINAL_FILE_NM varchar(200) not null comment '원본 파일명', + STORED_FILE_NM varchar(200) not null comment '저장 파일명', + FILE_PATH varchar(200) not null comment '파일 경로', + FILE_SIZE bigint not null comment '파일 크기', + FILE_EXT varchar(10) not null comment '파일 확장자', + FILE_TYPE varchar(20) not null comment '파일 타입 (image, video, etc)', + USE_YN varchar(1) default 'Y' not null comment '사용 여부', + REG_DTTM datetime null comment '등록 일시', + RGTR varchar(20) null comment '등록자', + MDFCN_DTTM datetime null comment '수정 일시', + MDFR varchar(20) null comment '수정자' +) + comment 'HTML 에디터 파일'; + +create index IDX_HTML_EDITOR_FILE_MODULE + on tb_common_html_editor_file (MODULE_ID); + diff --git a/DB-DDL/maria/ddl/xitframework/tb_excel_sample.sql b/DB-DDL/maria/ddl/xitframework/tb_excel_sample.sql new file mode 100644 index 0000000..947bfe6 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/tb_excel_sample.sql @@ -0,0 +1,16 @@ +create table tb_excel_sample +( + excel_sample_id varchar(20) not null + primary key, + excel_clmn1 bigint null, + excel_clmn2 datetime null, + excel_clmn3 varchar(100) null, + excel_clmn4 varchar(100) null, + excel_clmn5 varchar(100) null, + excel_clmn6 varchar(100) null, + excel_clmn7 varchar(100) null, + excel_clmn8 varchar(100) null, + excel_clmn9 varchar(100) null, + excel_clmn10 varchar(100) null +); + diff --git a/DB-DDL/maria/ddl/xitframework/tb_group.sql b/DB-DDL/maria/ddl/xitframework/tb_group.sql new file mode 100644 index 0000000..49df2ee --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/tb_group.sql @@ -0,0 +1,15 @@ +create table tb_group +( + GROUP_ID varchar(20) not null comment '그룹 ID' + primary key, + GROUP_NM varchar(100) not null comment '그룹 이름', + GROUP_DC varchar(200) null comment '그룹 설명', + SORT_ORDR int default 0 null comment '정렬 순서', + USE_YN varchar(1) not null comment '사용 여부', + REG_DTTM datetime null comment '등록 일시', + RGTR varchar(20) null comment '등록자', + MDFCN_DTTM datetime null comment '수정 일시', + MDFR varchar(20) null comment '수정자' +) + comment '그룹'; + diff --git a/DB-DDL/maria/ddl/xitframework/tb_group_role.sql b/DB-DDL/maria/ddl/xitframework/tb_group_role.sql new file mode 100644 index 0000000..8740f11 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/tb_group_role.sql @@ -0,0 +1,14 @@ +create table tb_group_role +( + GROUP_ID varchar(20) not null comment '그룹 ID', + ROLE_ID varchar(20) not null comment '역할 ID', + REG_DTTM datetime null comment '등록 일시', + RGTR varchar(20) null comment '등록자', + primary key (GROUP_ID, ROLE_ID), + constraint fk_group_role_group + foreign key (GROUP_ID) references tb_group (GROUP_ID), + constraint fk_group_role_role + foreign key (ROLE_ID) references tb_role (ROLE_ID) +) + comment '그룹-역할 매핑'; + diff --git a/DB-DDL/maria/ddl/xitframework/tb_login_log.sql b/DB-DDL/maria/ddl/xitframework/tb_login_log.sql new file mode 100644 index 0000000..822e01d --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/tb_login_log.sql @@ -0,0 +1,30 @@ +-- 로그인 로그 ID 시퀀스 생성 +CREATE SEQUENCE IF NOT EXISTS seq_login_log_id START WITH 1 INCREMENT BY 1; + +create table tb_login_log +( + LOG_ID varchar(20) comment '로그 ID - 시퀀스 기반' + primary key, + USER_ID varchar(20) null comment '사용자 ID', + USER_ACNT varchar(20) null comment '사용자 계정', + LOGIN_DTTM datetime not null comment '로그인 시간', + IP_ADDR varchar(50) null comment 'IP 주소', + SUCCESS_YN varchar(1) not null comment '성공 여부', + FAIL_REASON varchar(100) null comment '실패 사유', + LOGIN_TYPE varchar(20) default 'WEB' null comment '로그인 유형', + SESSION_ID varchar(100) null comment '세션 ID', + USER_AGENT varchar(500) null comment '사용자 에이전트', + DEVICE_INFO varchar(100) null comment '접속 디바이스 정보', + REG_DTTM datetime default current_timestamp() null comment '등록 일시' +) + comment '로그인 로그'; + +create index IDX_TB_LOGIN_LOG_LOGIN_DTTM + on tb_login_log (LOGIN_DTTM); + +create index IDX_TB_LOGIN_LOG_SUCCESS_YN + on tb_login_log (SUCCESS_YN); + +create index IDX_TB_LOGIN_LOG_USER_ID + on tb_login_log (USER_ID); + diff --git a/DB-DDL/maria/ddl/xitframework/tb_menu.sql b/DB-DDL/maria/ddl/xitframework/tb_menu.sql new file mode 100644 index 0000000..b671621 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/tb_menu.sql @@ -0,0 +1,23 @@ +create table tb_menu +( + MENU_ID varchar(20) not null comment '메뉴 ID' + primary key, + MENU_NM varchar(100) not null comment '메뉴 이름', + MENU_DC varchar(200) null comment '메뉴 설명', + UPPER_MENU_ID varchar(20) null comment '상위 메뉴 ID', + MENU_LEVEL int not null comment '메뉴 레벨', + SORT_ORDR int not null comment '정렬 순서', + MENU_URL varchar(200) null comment '메뉴 URL', + URL_PATTERN varchar(2000) null comment 'URL 패턴', + MENU_ICON varchar(100) null comment '메뉴 아이콘', + USE_YN varchar(1) not null comment '사용 여부', + VIEW_YN varchar(1) default 'Y' not null comment '화면 표시 여부', + REG_DTTM datetime null comment '등록 일시', + RGTR varchar(20) null comment '등록자', + MDFCN_DTTM datetime null comment '수정 일시', + MDFR varchar(20) null comment '수정자', + constraint fk_menu_upper + foreign key (UPPER_MENU_ID) references tb_menu (MENU_ID) +) + comment '메뉴'; + diff --git a/DB-DDL/maria/ddl/xitframework/tb_notification.sql b/DB-DDL/maria/ddl/xitframework/tb_notification.sql new file mode 100644 index 0000000..de5acb2 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/tb_notification.sql @@ -0,0 +1,19 @@ +create table tb_notification +( + NOTIFICATION_ID varchar(20) not null comment '알림 ID' + primary key, + NOTIFICATION_TYPE_CD varchar(20) not null comment '알림 유형 (SCHEDULE: 일정, QUESTION: 질문, ANSWER: 답변)', + TARGET_ID varchar(36) not null comment '대상 ID (일정 ID, 게시물 ID 등) 배치때문에 길게 잡음', + TITLE varchar(200) not null comment '알림 제목', + CONTENT varchar(500) null comment '알림 내용', + URL varchar(500) null comment '이동 URL', + REG_DTTM datetime null comment '등록 일시', + RGTR varchar(20) null comment '등록자', + MDFCN_DTTM datetime null comment '수정 일시', + MDFR varchar(20) null comment '수정자' +) + comment '공통 알림'; + +create index idx_notification_target_id + on tb_notification (TARGET_ID); + diff --git a/DB-DDL/maria/ddl/xitframework/tb_notification_target.sql b/DB-DDL/maria/ddl/xitframework/tb_notification_target.sql new file mode 100644 index 0000000..3597bbc --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/tb_notification_target.sql @@ -0,0 +1,18 @@ +create table tb_notification_target +( + NOTIFICATION_ID varchar(20) not null comment '알림 ID', + USER_ID varchar(20) not null comment '사용자 ID', + READ_YN varchar(1) default 'N' not null comment '확인 여부(Y/N)', + READ_DTTM datetime null comment '확인 일시', + REG_DTTM datetime null comment '등록 일시', + RGTR varchar(20) null comment '등록자', + MDFCN_DTTM datetime null comment '수정 일시', + MDFR varchar(20) null comment '수정자', + primary key (NOTIFICATION_ID, USER_ID), + constraint fk_notification_target_notification + foreign key (NOTIFICATION_ID) references tb_notification (NOTIFICATION_ID), + constraint fk_notification_target_user + foreign key (USER_ID) references tb_user (USER_ID) +) + comment '알림 대상자'; + diff --git a/DB-DDL/maria/ddl/xitframework/tb_notification_target_setting.sql b/DB-DDL/maria/ddl/xitframework/tb_notification_target_setting.sql new file mode 100644 index 0000000..8f71dc7 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/tb_notification_target_setting.sql @@ -0,0 +1,21 @@ +create table tb_notification_target_setting +( + SETTING_ID varchar(20) not null comment '설정 ID' + primary key, + SETTING_TYPE varchar(50) not null comment '설정 구분 (QNA_MANAGER, BATCH_MANAGER 등)', + USER_ID varchar(20) not null comment '사용자 ID', + REG_DTTM datetime null comment '등록 일시', + RGTR varchar(20) null comment '등록자', + MDFCN_DTTM datetime null comment '수정 일시', + MDFR varchar(20) null comment '수정자', + constraint fk_notification_target_setting_user + foreign key (USER_ID) references tb_user (USER_ID) +) + comment '알림 대상자 설정'; + +create index idx_notification_target_setting_type + on tb_notification_target_setting (SETTING_TYPE); + +create index idx_notification_target_setting_user + on tb_notification_target_setting (USER_ID); + diff --git a/DB-DDL/maria/ddl/xitframework/tb_role.sql b/DB-DDL/maria/ddl/xitframework/tb_role.sql new file mode 100644 index 0000000..6978980 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/tb_role.sql @@ -0,0 +1,15 @@ +create table tb_role +( + ROLE_ID varchar(20) not null comment '역할 ID' + primary key, + ROLE_NM varchar(100) not null comment '역할 이름', + ROLE_DC varchar(200) null comment '역할 설명', + SORT_ORDR int default 0 null comment '정렬 순서', + USE_YN varchar(1) not null comment '사용 여부', + REG_DTTM datetime null comment '등록 일시', + RGTR varchar(20) null comment '등록자', + MDFCN_DTTM datetime null comment '수정 일시', + MDFR varchar(20) null comment '수정자' +) + comment '역할'; + diff --git a/DB-DDL/maria/ddl/xitframework/tb_role_menu.sql b/DB-DDL/maria/ddl/xitframework/tb_role_menu.sql new file mode 100644 index 0000000..9003558 --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/tb_role_menu.sql @@ -0,0 +1,14 @@ +create table tb_role_menu +( + ROLE_ID varchar(20) not null comment '역할 ID', + MENU_ID varchar(20) not null comment '메뉴 ID', + REG_DTTM datetime null comment '등록 일시', + RGTR varchar(20) null comment '등록자', + primary key (ROLE_ID, MENU_ID), + constraint fk_role_menu_menu + foreign key (MENU_ID) references tb_menu (MENU_ID), + constraint fk_role_menu_role + foreign key (ROLE_ID) references tb_role (ROLE_ID) +) + comment '역할-메뉴 매핑'; + diff --git a/DB-DDL/maria/ddl/xitframework/tb_user.sql b/DB-DDL/maria/ddl/xitframework/tb_user.sql new file mode 100644 index 0000000..092aeef --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/tb_user.sql @@ -0,0 +1,44 @@ +create table tb_user +( + USER_ID varchar(20) not null comment '사용자 ID' + primary key, + USER_ACNT varchar(20) not null comment '사용자 계정', + USER_NM varchar(50) not null comment '사용자 이름', + PASSWD varchar(200) not null comment '비밀번호', + PASSWD_HINT varchar(100) null comment '비밀번호 힌트', + PASSWD_NSR varchar(100) null comment '비밀번호 힌트 답', + EMP_NO varchar(20) null comment '사원 번호', + GENDER varchar(1) null comment '성별', + ZIP varchar(6) null comment '우편번호', + ADDR varchar(150) null comment '주소', + DADDR varchar(150) null comment '상세주소', + AREA_NO varchar(10) null comment '지역 번호', + EML_ADDR varchar(50) null comment '이메일 주소', + ORG_CD varchar(20) null comment '조직 CD', + USER_GROUP_ID varchar(20) null comment '그룹 ID', + NSTT_CD varchar(8) not null comment '소속기관 코드', + POS_NM varchar(60) null comment '직위 이름', + CRTFC_DN varchar(20) null comment '인증 DN값', + USER_STATUS_CD varchar(20) not null comment '사용자 상태', + FXNO varchar(20) null comment '팩스번호', + TELNO varchar(20) null comment '전화번호', + MBL_TELNO varchar(20) null comment '휴대 전화번호', + BRDT varchar(20) null comment '생년월일', + DEPT_CD varchar(7) null comment '부서 코드', + USE_YN varchar(1) not null comment '사용 여부', + RSDNT_NO varchar(200) null comment '주민등록 번호', + PASSWD_INIT_YN varchar(1) default 'N' null comment '비밀번호 초기화 여부', + LOCK_YN varchar(1) null comment '잠김 여부', + LOCK_CNT int not null comment '잠김 횟수', + LOCK_DTTM datetime null comment '잠김 일시', + REG_DTTM datetime null comment '등록 일시', + RGTR varchar(20) null comment '등록자', + MDFCN_DTTM datetime null comment '수정 일시', + MDFR varchar(20) null comment '수정자', + constraint tb_user_unique + unique (USER_ACNT), + constraint fk_user_group + foreign key (USER_GROUP_ID) references tb_group (GROUP_ID) +) + comment '사용자'; + diff --git a/DB-DDL/maria/ddl/xitframework/tb_user_session.sql b/DB-DDL/maria/ddl/xitframework/tb_user_session.sql new file mode 100644 index 0000000..4dc6c3d --- /dev/null +++ b/DB-DDL/maria/ddl/xitframework/tb_user_session.sql @@ -0,0 +1,19 @@ +create table tb_user_session +( + SESSION_ID varchar(100) not null comment '세션 ID' + primary key, + USER_ID varchar(20) not null comment '사용자 ID', + USER_ACNT varchar(20) null comment '사용자 계정', + LOGIN_DTTM datetime default current_timestamp() not null comment '로그인 시간', + LAST_ACCESS_DTTM datetime default current_timestamp() not null comment '마지막 접속 시간', + IP_ADDR varchar(50) null comment 'IP 주소', + USER_AGENT varchar(500) null comment '사용자 에이전트' +) + comment '사용자 세션 정보'; + +create index IDX_TB_USER_SESSION_LOGIN_DTTM + on tb_user_session (LOGIN_DTTM); + +create index IDX_TB_USER_SESSION_USER_ID + on tb_user_session (USER_ID); + diff --git a/DB-DDL/maria/dictionary/column_word_dictionary.md b/DB-DDL/maria/dictionary/column_word_dictionary.md new file mode 100644 index 0000000..218faf6 --- /dev/null +++ b/DB-DDL/maria/dictionary/column_word_dictionary.md @@ -0,0 +1,320 @@ +# 통합 데이터베이스 컬럼 단어사전 + +## 한글 → 영문 약어 변환표 + +| 한글 | 영문 약어 | 전체 영문 | 의미 | +|------|-----------|-----------|------| +| 접속 | ACCESS | ACCESS | 접속 | +| 계정 | ACNT | ACCOUNT | 계정 | +| 주소 | ADDR | ADDRESS | 주소 | +| 에이전트 | AGENT | AGENT | 에이전트 | +| 전체 | ALL | ALL | 전체, 모든 | +| 답변 | NSR | ANSWER | 답변 | +| 보관 | ARCHIVE | ARCHIVE | 보관, 아카이브 | +| 지역 | AREA | AREA | 지역 | +| 속성 | ATTRIBUTE | ATTRIBUTE | 속성 | +| 배치 | BATCH | BATCH | 배치 | +| 게시판 | BBS | BBS | 게시판 (Bulletin Board System) | +| 생년월일 | BRDT | BIRTH_DATE | 생년월일 | +| 달력 | CALENDAR | CALENDAR | 달력 | +| 범주 | CATEGORY | CATEGORY | 범주, 카테고리 | +| 코드 | CD | CODE | 코드 | +| 클래스 | CLASS | CLASS | 클래스 | +| 컬럼 | CLMN | COLUMN | 컬럼 | +| 개수 | CNT | COUNT | 개수, 수량 | +| 댓글 | COMMENT | COMMENT | 댓글 | +| 완료 | COMPLETED | COMPLETED | 완료된 | +| 설정 | CONFIG | CONFIG | 설정 | +| 내용 | CONTENT | CONTENT | 내용 | +| 손상 | CORRUPTED | CORRUPTED | 손상된 | +| 크론 | CRON | CRON | 크론 (스케줄링) | +| 인증 | CRTFC | CERTIFICATE | 인증 | +| 상세주소 | DADDR | DETAIL_ADDRESS | 상세주소 | +| 데이터 | DATA | DATA | 데이터 | +| 일 | DAY | DAY | 일 | +| 설명 | DC | DESCRIPTION | 설명 | +| 깊이 | DEPTH | DEPTH | 깊이 | +| 부서 | DEPT | DEPARTMENT | 부서 | +| 상세 | DETAIL | DETAIL | 상세 | +| 디바이스 | DEVICE | DEVICE | 디바이스 | +| 일시 | DTTM | DATE_TIME | 일시 | +| 에디터 | EDITOR | EDITOR | 에디터 | +| 이메일 | EML | EMAIL | 이메일 | +| 사원 | EMP | EMPLOYEE | 사원 | +| 종료 | END | END | 종료 | +| 에러 | ERROR | ERROR | 에러 | +| 엑셀 | EXCEL | EXCEL | 엑셀 | +| 초과 | EXCEEDED | EXCEEDED | 초과된 | +| 실행 | EXECUTION | EXECUTION | 실행 | +| 종료 | EXIT | EXIT | 종료 | +| 표현식 | EXPRESSION | EXPRESSION | 표현식 | +| 확장자 | EXT | EXTENSION | 확장자 | +| 추출 | EXTRACT | EXTRACT | 추출 | +| 실패 | FAILED | FAILED | 실패한 | +| 팩스번호 | FXNO | FAX_NUMBER | 팩스번호 | +| 파일 | FILE | FILE | 파일 | +| 성별 | GENDER | GENDER | 성별 | +| 그룹 | GROUP | GROUP | 그룹 | +| 힌트 | HINT | HINT | 힌트 | +| 아이콘 | ICON | ICON | 아이콘 | +| 식별자 | ID | ID | 식별자 | +| 이미지 | IMAGE | IMAGE | 이미지 | +| 정보 | INFO | INFORMATION | 정보 | +| 초기화 | INIT | INITIALIZE | 초기화 | +| 작업 | JOB | JOB | 작업 | +| 마지막 | LAST | LAST | 마지막 | +| 레벨 | LEVEL | LEVEL | 레벨 | +| 라인 | LINE | LINE | 라인 | +| 잠김 | LOCK | LOCK | 잠김 | +| 로그 | LOG | LOG | 로그 | +| 로그인 | LOGIN | LOGIN | 로그인 | +| 최대 | MAX | MAX | 최대 | +| 휴대 | MBL | MOBILE | 휴대 | +| 수정 | MDFCN | MODIFICATION | 수정 | +| 수정자 | MDFR | MODIFIER | 수정자 | +| 메뉴 | MENU | MENU | 메뉴 | +| 메시지 | MESSAGE | MESSAGE | 메시지 | +| 모듈 | MODULE | MODULE | 모듈 | +| 이름 | NM | NAME | 이름 | +| 다음 | NEXT | NEXT | 다음 | +| 번호 | NO | NUMBER | 번호 | +| 비 | NON | NON | 비, 아닌 | +| 공지 | NOTICE | NOTICE | 공지 | +| 소속기관 | NSTT | INSTITUTE | 소속기관 | +| 알림 | NOTIFICATION | NOTIFICATION | 알림 | +| 순서 | ORDR | ORDER | 순서 | +| 조직 | ORG | ORGANIZATION | 조직 | +| 원본 | ORIGINAL | ORIGINAL | 원본 | +| 매개변수 | PARAM | PARAMETER | 매개변수 | +| 부모 | PARENT | PARENT | 부모 | +| 참여자 | PARTICIPANTS | PARTICIPANTS | 참여자 | +| 비밀번호 | PASSWD | PASSWORD | 비밀번호 | +| 경로 | PATH | PATH | 경로 | +| 패턴 | PATTERN | PATTERN | 패턴 | +| 대기중 | PENDING | PENDING | 대기중 | +| 장소 | PLACE | PLACE | 장소 | +| 직위 | POS | POSITION | 직위 | +| 게시물 | POST | POST | 게시물 | +| 비공개 | PRIVATE | PRIVATE | 비공개 | +| 처리 | PROCESS | PROCESS | 처리 | +| 처리중 | PROCESSING | PROCESSING | 처리중 | +| 원시 | RAW | RAW | 원시 | +| 확인 | READ | READ | 확인 | +| 사유 | REASON | REASON | 사유 | +| 등록 | REG | REGISTRATION | 등록 | +| 재시도 | RETRY | RETRY | 재시도 | +| 등록자 | RGTR | REGISTRANT | 등록자 | +| 역할 | ROLE | ROLE | 역할 | +| 주민등록 | RSDNT | RESIDENT | 주민등록 | +| 샘플 | SAMPLE | SAMPLE | 샘플 | +| 일정 | SCHEDULE | SCHEDULE | 일정 | +| 시퀀스 | SEQ | SEQUENCE | 시퀀스 | +| 서버 | SERVER | SERVER | 서버 | +| 세션 | SESSION | SESSION | 세션 | +| 설정 | SETTING | SETTING | 설정 | +| 크기 | SIZE | SIZE | 크기 | +| 정렬 | SORT | SORT | 정렬 | +| 시작 | START | START | 시작 | +| 상태 | STATUS | STATUS | 상태 | +| 저장 | STORED | STORED | 저장된 | +| 성공 | SUCCESS | SUCCESS | 성공 | +| 대상 | TARGET | TARGET | 대상 | +| 전화번호 | TELNO | TELEPHONE_NUMBER | 전화번호 | +| 제목 | TITLE | TITLE | 제목 | +| 총 | TOTAL | TOTAL | 총 | +| 타입 | TYPE | TYPE | 타입 | +| 유니크키 | UK | UNIQUE_KEY | 유니크 키 | +| 업로드 | UPLOAD | UPLOAD | 업로드 | +| 상위 | UPPER | UPPER | 상위 | +| 사용 | USE | USE | 사용 | +| 사용자 | USER | USER | 사용자 | +| 값 | VALUE | VALUE | 값 | +| 화면 | VIEW | VIEW | 화면, 표시 | +| 대기중 | WAITING | WAITING | 대기중 | +| 작성자 | WRITER | WRITER | 작성자 | +| 예아니오 | YN | YES_NO | 예/아니오 | +| 우편번호 | ZIP | ZIP | 우편번호 | + +## 영문 약어 사전 + +| 영문 약어 | 전체 영문 | 한글 의미 | 설명 | +|-----------|-----------|----------|------| +| ACCESS | ACCESS | 접속 | 접속 | +| ACNT | ACCOUNT | 계정 | 계정 | +| ADDR | ADDRESS | 주소 | 주소 | +| AGENT | AGENT | 에이전트 | 에이전트 | +| ALL | ALL | 전체 | 전체, 모든 | +| ANSWER | ANSWER | 답변 | 답변 | +| ARCHIVE | ARCHIVE | 보관 | 보관, 아카이브 | +| AREA | AREA | 지역 | 지역 | +| ATTRIBUTE | ATTRIBUTE | 속성 | 속성 | +| BATCH | BATCH | 배치 | 배치 | +| BBS | BBS | 게시판 | 게시판 (Bulletin Board System) | +| BRDT | BIRTH_DATE | 생년월일 | 생년월일 | +| CALENDAR | CALENDAR | 달력 | 달력 | +| CATEGORY | CATEGORY | 범주 | 범주, 카테고리 | +| CD | CODE | 코드 | 코드 | +| CLASS | CLASS | 클래스 | 클래스 | +| CLMN | COLUMN | 컬럼 | 컬럼 | +| CNT | COUNT | 개수 | 개수, 수량 | +| COMMENT | COMMENT | 댓글 | 댓글 | +| COMPLETED | COMPLETED | 완료 | 완료된 | +| CONFIG | CONFIG | 설정 | 설정 | +| CONTENT | CONTENT | 내용 | 내용 | +| CORRUPTED | CORRUPTED | 손상 | 손상된 | +| CRON | CRON | 크론 | 크론 (스케줄링) | +| CRTFC | CERTIFICATE | 인증 | 인증 | +| DADDR | DETAIL_ADDRESS | 상세주소 | 상세주소 | +| DATA | DATA | 데이터 | 데이터 | +| DAY | DAY | 일 | 일 | +| DC | DESCRIPTION | 설명 | 설명 | +| DEPTH | DEPTH | 깊이 | 깊이 | +| DEPT | DEPARTMENT | 부서 | 부서 | +| DETAIL | DETAIL | 상세 | 상세 | +| DEVICE | DEVICE | 디바이스 | 디바이스 | +| DN | DN | DN | DN | +| DTTM | DATE_TIME | 일시 | 일시 | +| EDITOR | EDITOR | 에디터 | 에디터 | +| EML | EMAIL | 이메일 | 이메일 | +| EMP | EMPLOYEE | 사원 | 사원 | +| END | END | 종료 | 종료 | +| ERROR | ERROR | 에러 | 에러 | +| EXCEL | EXCEL | 엑셀 | 엑셀 | +| EXCEEDED | EXCEEDED | 초과 | 초과된 | +| EXECUTION | EXECUTION | 실행 | 실행 | +| EXIT | EXIT | 종료 | 종료 | +| EXPRESSION | EXPRESSION | 표현식 | 표현식 | +| EXT | EXTENSION | 확장자 | 확장자 | +| EXTRACT | EXTRACT | 추출 | 추출 | +| FAILED | FAILED | 실패 | 실패한 | +| FILE | FILE | 파일 | 파일 | +| FXNO | FAX_NUMBER | 팩스번호 | 팩스번호 | +| GENDER | GENDER | 성별 | 성별 | +| GROUP | GROUP | 그룹 | 그룹 | +| HINT | HINT | 힌트 | 힌트 | +| ICON | ICON | 아이콘 | 아이콘 | +| ID | ID | 식별자 | 식별자 | +| IMAGE | IMAGE | 이미지 | 이미지 | +| INFO | INFORMATION | 정보 | 정보 | +| INIT | INITIALIZE | 초기화 | 초기화 | +| IP | IP | IP | IP | +| JOB | JOB | 작업 | 작업 | +| LAST | LAST | 마지막 | 마지막 | +| LEVEL | LEVEL | 레벨 | 레벨 | +| LINE | LINE | 라인 | 라인 | +| LOCK | LOCK | 잠김 | 잠김 | +| LOG | LOG | 로그 | 로그 | +| LOGIN | LOGIN | 로그인 | 로그인 | +| MAX | MAX | 최대 | 최대 | +| MBL | MOBILE | 휴대 | 휴대 | +| MDFCN | MODIFICATION | 수정 | 수정 | +| MDFR | MODIFIER | 수정자 | 수정자 | +| MENU | MENU | 메뉴 | 메뉴 | +| MESSAGE | MESSAGE | 메시지 | 메시지 | +| MODULE | MODULE | 모듈 | 모듈 | +| NEXT | NEXT | 다음 | 다음 | +| NM | NAME | 이름 | 이름 | +| NO | NUMBER | 번호 | 번호 | +| NON | NON | 비 | 비, 아닌 | +| NOTICE | NOTICE | 공지 | 공지 | +| NOTIFICATION | NOTIFICATION | 알림 | 알림 | +| NSR | ANSWER | 답 | 답 | +| NSTT | INSTITUTE | 소속기관 | 소속기관 | +| ORDR | ORDER | 순서 | 순서 | +| ORG | ORGANIZATION | 조직 | 조직 | +| ORIGINAL | ORIGINAL | 원본 | 원본 | +| PARAM | PARAMETER | 매개변수 | 매개변수 | +| PARENT | PARENT | 부모 | 부모 | +| PARTICIPANTS | PARTICIPANTS | 참여자 | 참여자 | +| PASSWD | PASSWORD | 비밀번호 | 비밀번호 | +| PATH | PATH | 경로 | 경로 | +| PATTERN | PATTERN | 패턴 | 패턴 | +| PENDING | PENDING | 대기중 | 대기중 | +| PLACE | PLACE | 장소 | 장소 | +| POS | POSITION | 직위 | 직위 | +| POST | POST | 게시물 | 게시물 | +| PRIVATE | PRIVATE | 비공개 | 비공개 | +| PROCESS | PROCESS | 처리 | 처리 | +| PROCESSING | PROCESSING | 처리중 | 처리중 | +| RAW | RAW | 원시 | 원시 | +| READ | READ | 확인 | 확인 | +| REASON | REASON | 사유 | 사유 | +| REG | REGISTRATION | 등록 | 등록 | +| RETRY | RETRY | 재시도 | 재시도 | +| RGTR | REGISTRANT | 등록자 | 등록자 | +| ROLE | ROLE | 역할 | 역할 | +| RSDNT | RESIDENT | 주민등록 | 주민등록 | +| SAMPLE | SAMPLE | 샘플 | 샘플 | +| SCHEDULE | SCHEDULE | 일정 | 일정 | +| SEQ | SEQUENCE | 시퀀스 | 시퀀스 | +| SERVER | SERVER | 서버 | 서버 | +| SESSION | SESSION | 세션 | 세션 | +| SETTING | SETTING | 설정 | 설정 | +| SIZE | SIZE | 크기 | 크기 | +| SORT | SORT | 정렬 | 정렬 | +| START | START | 시작 | 시작 | +| STATUS | STATUS | 상태 | 상태 | +| STORED | STORED | 저장 | 저장된 | +| SUCCESS | SUCCESS | 성공 | 성공 | +| TARGET | TARGET | 대상 | 대상 | +| TELNO | TELEPHONE_NUMBER | 전화번호 | 전화번호 | +| TITLE | TITLE | 제목 | 제목 | +| TOTAL | TOTAL | 총 | 총 | +| TUI | TUI | TUI | TUI (Toast UI) | +| TYPE | TYPE | 타입 | 타입 | +| UK | UNIQUE_KEY | 유니크키 | 유니크 키 | +| UPLOAD | UPLOAD | 업로드 | 업로드 | +| UPPER | UPPER | 상위 | 상위 | +| URL | URL | URL | URL | +| USE | USE | 사용 | 사용 | +| USER | USER | 사용자 | 사용자 | +| VALUE | VALUE | 값 | 값 | +| VIEW | VIEW | 화면 | 화면, 표시 | +| WAITING | WAITING | 대기중 | 대기중 | +| WRITER | WRITER | 작성자 | 작성자 | +| YN | YES_NO | 예아니오 | 예/아니오 | +| ZIP | ZIP | 우편번호 | 우편번호 | + +## 일반적인 접미사 패턴 + +| 접미사 | 의미 | 예시 | +|--------|------|------| +| _ID | 식별자 | USER_ID, MENU_ID | +| _CD | 코드 | TYPE_CD, STATUS_CD | +| _NM | 이름 | USER_NM, FILE_NM | +| _DC | 설명 | MENU_DC, ROLE_DC | +| _DTTM | 일시 | REG_DTTM, MDFCN_DTTM | +| _YN | 예/아니오 | USE_YN, DEL_YN | +| _CNT | 개수 | LOGIN_CNT, RETRY_CNT | +| _NO | 번호 | EMP_NO, TELNO | +| _ORDR | 순서 | SORT_ORDR, MENU_ORDR | +| _ADDR | 주소 | HOME_ADDR, WORK_ADDR | +| _PATH | 경로 | FILE_PATH, MENU_PATH | +| _SIZE | 크기 | FILE_SIZE, IMAGE_SIZE | +| _TYPE | 타입 | FILE_TYPE, USER_TYPE | +| _STATUS | 상태 | PROCESS_STATUS, LOGIN_STATUS | + +## 주요 컬럼 네이밍 규칙 + +1. **기본 구조**: `[접두사_]주요단어[_접미사]` +2. **복합어**: 언더스코어(_)로 구분 +3. **일시**: 반드시 `_DTTM` 사용 +4. **여부**: 반드시 `_YN` 사용 +5. **등록/수정**: `REG_DTTM`, `MDFCN_DTTM`, `RGTR`, `MDFR` 패턴 사용 +6. **ID**: 테이블명에서 `tb_` 제거 후 `_ID` 추가 + +## 테이블 참조 정보 + +- **tb_common_html_editor_file**: 파일 관리 +- **tb_excel_sample**: 엑셀 샘플 데이터 +- **tb_group**: 그룹 정보 +- **tb_group_role**: 그룹-역할 매핑 +- **tb_login_log**: 로그인 로그 +- **tb_menu**: 메뉴 정보 +- **tb_notification**: 알림 정보 +- **tb_notification_target**: 알림 대상자 +- **tb_notification_target_setting**: 알림 대상자 설정 +- **tb_role**: 역할 정보 +- **tb_role_menu**: 역할-메뉴 매핑 +- **tb_user**: 사용자 정보 +- **tb_user_session**: 사용자 세션 정보 \ No newline at end of file diff --git a/DB-DDL/maria/dml/tb_batch_job_info.sql b/DB-DDL/maria/dml/tb_batch_job_info.sql new file mode 100644 index 0000000..84552da --- /dev/null +++ b/DB-DDL/maria/dml/tb_batch_job_info.sql @@ -0,0 +1,4 @@ +INSERT INTO xitframework.tb_batch_job_info (JOB_ID, JOB_NM, JOB_GROUP, JOB_CLASS, CRON_EXPRESSION, JOB_DC, STATUS_CD, LAST_EXECUTION_ID, REG_DTTM, MDFCN_DTTM) VALUES ('2542ecde-3547-4d98-b1fb-75ca32799b27', 'SampleFileReadBatchJob', 'SampleGroup', 'go.kr.project.batch.job.SampleFileReadBatchJob', '0 0 * * * ?', '', 'ACTIVE', 'a5667923-52bb-4d8d-9f01-0d3452ccccb6', '2025-07-21 09:55:22', '2025-07-23 11:59:58'); +INSERT INTO xitframework.tb_batch_job_info (JOB_ID, JOB_NM, JOB_GROUP, JOB_CLASS, CRON_EXPRESSION, JOB_DC, STATUS_CD, LAST_EXECUTION_ID, REG_DTTM, MDFCN_DTTM) VALUES ('5d284aaa-cfe5-4b05-860f-e9108b80d63c', 'ZipFileProcessBatchJob', 'SampleGroup', 'go.kr.project.batch.job.ZipFileProcessBatchJob', '0 0 * * * ?', '', 'ACTIVE', '92f51b4d-80d9-4c27-9555-e998d2d6ff7b', '2025-07-21 09:55:45', '2025-07-23 11:59:58'); +INSERT INTO xitframework.tb_batch_job_info (JOB_ID, JOB_NM, JOB_GROUP, JOB_CLASS, CRON_EXPRESSION, JOB_DC, STATUS_CD, LAST_EXECUTION_ID, REG_DTTM, MDFCN_DTTM) VALUES ('a27623bf-ef70-41ed-9886-736af9e3d3e0', 'SampleBatchJob3', 'SampleGroup', 'go.kr.project.batch.job.SampleBatchJob3', '0 0/1 * * * ?', '', 'DELETED', null, '2025-07-21 09:55:59', '2025-07-21 10:07:33'); +INSERT INTO xitframework.tb_batch_job_info (JOB_ID, JOB_NM, JOB_GROUP, JOB_CLASS, CRON_EXPRESSION, JOB_DC, STATUS_CD, LAST_EXECUTION_ID, REG_DTTM, MDFCN_DTTM) VALUES ('d40d571f-f48a-4481-b88e-545c224aa6e2', 'SampleErrorFileRetryBatchJob', 'SampleGroup', 'go.kr.project.batch.job.SampleErrorFileRetryBatchJob', '0 0 * * * ?', '', 'ACTIVE', 'ca01ef92-32a3-4ac8-9c2a-6e5e18e82c12', '2025-07-21 09:55:32', '2025-07-23 11:59:58'); diff --git a/DB-DDL/maria/dml/tb_bbs_config.sql b/DB-DDL/maria/dml/tb_bbs_config.sql new file mode 100644 index 0000000..f4defab --- /dev/null +++ b/DB-DDL/maria/dml/tb_bbs_config.sql @@ -0,0 +1,4 @@ +INSERT INTO xitframework.tb_bbs_config (BBS_ID, BBS_NM, BBS_TYPE_CD, BBS_DC, UPLOAD_YN, UPLOAD_FILE_CNT, COMMENT_YN, TUI_EDITOR_YN, NOTICE_YN, USE_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('BBSC00000001', '공지사항', 'NOTICE', '공지사항 TEST', 'Y', 2, 'N', 'Y', 'Y', 'Y', '2025-05-27 10:37:51', 'ADMIN', '2025-07-18 09:41:29', 'ADMIN'); +INSERT INTO xitframework.tb_bbs_config (BBS_ID, BBS_NM, BBS_TYPE_CD, BBS_DC, UPLOAD_YN, UPLOAD_FILE_CNT, COMMENT_YN, TUI_EDITOR_YN, NOTICE_YN, USE_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('BBSC00000040', 'FAQ', 'FAQ', 'FAQ', 'Y', 10, 'N', 'Y', 'N', 'Y', '2025-06-02 11:30:11', 'ADMIN', '2025-06-05 13:22:52', 'ADMIN'); +INSERT INTO xitframework.tb_bbs_config (BBS_ID, BBS_NM, BBS_TYPE_CD, BBS_DC, UPLOAD_YN, UPLOAD_FILE_CNT, COMMENT_YN, TUI_EDITOR_YN, NOTICE_YN, USE_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('BBSC00000041', '질문과 답변', 'QNA', '질문과 답변', 'Y', 5, 'N', 'Y', 'N', 'Y', '2025-06-02 17:07:37', 'ADMIN', '2025-07-18 15:37:40', 'ADMIN'); +INSERT INTO xitframework.tb_bbs_config (BBS_ID, BBS_NM, BBS_TYPE_CD, BBS_DC, UPLOAD_YN, UPLOAD_FILE_CNT, COMMENT_YN, TUI_EDITOR_YN, NOTICE_YN, USE_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('BBSC00000100', '일반 게시판', 'NORMAL', '일반 게시판-1', 'Y', 2, 'Y', 'Y', 'Y', 'Y', '2025-06-05 15:14:48', 'ADMIN', '2025-07-23 09:12:29', 'ADMIN'); diff --git a/DB-DDL/maria/dml/tb_cd_detail.sql b/DB-DDL/maria/dml/tb_cd_detail.sql new file mode 100644 index 0000000..96ebd27 --- /dev/null +++ b/DB-DDL/maria/dml/tb_cd_detail.sql @@ -0,0 +1,55 @@ +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('BATCH_EXEC_STATUS_CD', 'COMPLETED', '완료됨', '배치 작업 실행이 성공적으로 완료된 상태', 2, 'Y', null, null, null, null, null, '2025-06-18 13:24:51', 'SYSTEM', null, null); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('BATCH_EXEC_STATUS_CD', 'FAILED', '실패함', '배치 작업 실행 중 오류가 발생한 상태', 3, 'Y', null, null, null, null, null, '2025-06-18 13:24:51', 'SYSTEM', null, null); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('BATCH_EXEC_STATUS_CD', 'PARTIALLY_COMPLETED', '부분완료', '배치 작업 실행이 일부 성공하고 일부 실패한 상태', 5, 'N', null, null, null, null, null, '2025-06-20 14:12:15', 'SYSTEM', null, null); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('BATCH_EXEC_STATUS_CD', 'STARTED', '시작됨', '배치 작업 실행이 시작된 상태', 1, 'Y', null, null, null, null, null, '2025-06-18 13:24:51', 'SYSTEM', null, null); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('BATCH_EXEC_STATUS_CD', 'VETOED', '거부됨', '배치 작업 실행이 거부된 상태', 4, 'Y', null, null, null, null, null, '2025-06-18 13:24:51', 'SYSTEM', null, null); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('BATCH_EXIT_CD', 'COMPLETED', '성공 종료', '배치 작업이 성공적으로 종료됨', 1, 'Y', null, null, null, null, null, '2025-06-18 13:24:51', 'SYSTEM', null, null); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('BATCH_EXIT_CD', 'FAILED', '실패 종료', '배치 작업이 오류로 인해 실패 종료됨', 2, 'Y', null, null, null, null, null, '2025-06-18 13:24:51', 'SYSTEM', null, null); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('BATCH_EXIT_CD', 'PARTIALLY_COMPLETED', '부분 완료', '배치 작업이 일부 성공하고 일부 실패하여 부분적으로 완료됨', 4, 'Y', null, null, null, null, null, '2025-06-20 14:32:25', 'SYSTEM', null, null); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('BATCH_EXIT_CD', 'UNKNOWN', '알 수 없음', '배치 작업의 종료 상태를 알 수 없음', 3, 'Y', null, null, null, null, null, '2025-06-18 13:24:51', 'SYSTEM', null, null); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('BATCH_JOB_STATUS_CD', 'ACTIVE', '활성', '배치 작업이 활성화된 상태', 1, 'Y', null, null, null, null, null, '2025-06-18 13:24:51', 'SYSTEM', null, null); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('BATCH_JOB_STATUS_CD', 'DELETED', '삭제됨', '배치 작업이 삭제된 상태', 3, 'Y', null, null, null, null, null, '2025-06-18 13:24:51', 'SYSTEM', null, null); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('BATCH_JOB_STATUS_CD', 'PAUSED', '일시 중지', '배치 작업이 일시 중지된 상태', 2, 'Y', null, null, null, null, null, '2025-06-18 13:24:51', 'SYSTEM', null, null); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('BBS_ANSWER_STATUS_CD', 'COMPLETED', '답변완료', '게시물 답변 상태 - 답변완료', 2, 'Y', null, null, null, null, null, '2025-06-04 12:27:54', 'SYSTEM', null, null); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('BBS_ANSWER_STATUS_CD', 'WAITING', '대기중', '게시물 답변 상태 - 대기중', 1, 'Y', null, null, null, null, null, '2025-06-04 12:27:54', 'SYSTEM', null, null); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('BBS_POST_TYPE_CD', 'A', '답변', '게시물 유형 - 답변', 2, 'Y', null, null, null, null, null, '2025-06-04 12:27:54', 'SYSTEM', null, null); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('BBS_POST_TYPE_CD', 'Q', '질문', '게시물 유형 - 질문', 1, 'Y', null, null, null, null, null, '2025-06-04 12:27:54', 'SYSTEM', null, null); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('BBS_TYPE_CD', 'FAQ', 'FAQ', 'FAQ 게시판', 4, 'Y', null, null, null, null, null, '2025-05-26 16:43:38', 'SYSTEM', null, null); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('BBS_TYPE_CD', 'NORMAL', '일반', '자료실 게시판', 1, 'Y', null, null, null, null, null, '2025-05-26 16:43:33', 'SYSTEM', null, null); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('BBS_TYPE_CD', 'NOTICE', '공지사항', '공지사항 게시판', 2, 'Y', null, null, null, null, null, '2025-05-26 16:43:35', 'SYSTEM', null, null); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('BBS_TYPE_CD', 'QNA', '질문과답변', '질문과답변 게시판 (답변 시 이메일 발송)', 3, 'Y', null, null, null, null, null, '2025-05-26 16:43:36', 'SYSTEM', null, null); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('DEPT_CD', 'DEPT001', '경영지원부', '경영지원부', 1, 'Y', null, null, null, null, null, '2025-05-13 14:44:53', 'SYSTEM', '2025-05-13 14:44:53', 'SYSTEM'); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('DEPT_CD', 'DEPT002', '인사부', '인사부', 2, 'Y', null, null, null, null, null, '2025-05-13 14:44:53', 'SYSTEM', '2025-05-13 14:44:53', 'SYSTEM'); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('DEPT_CD', 'DEPT003', '개발부', '개발부', 3, 'Y', null, null, null, null, null, '2025-05-13 14:44:53', 'SYSTEM', '2025-05-13 14:44:53', 'SYSTEM'); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('DEPT_CD', 'DEPT004', '마케팅부', '마케팅부', 4, 'Y', null, null, null, null, null, '2025-05-13 14:44:53', 'SYSTEM', '2025-05-13 14:44:53', 'SYSTEM'); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('DEPT_CD', 'DEPT005', '영업부', '영업부', 5, 'Y', null, null, null, null, null, '2025-05-13 14:44:53', 'SYSTEM', '2025-05-13 14:44:53', 'SYSTEM'); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('DEPT_CD', 'DEPT006', '연구부', '연구부', 6, 'Y', null, null, null, null, null, '2025-05-13 14:44:53', 'SYSTEM', '2025-05-13 14:44:53', 'SYSTEM'); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('DEPT_CD', 'DEPT007', '품질관리부', '품질관리부', 7, 'Y', null, null, null, null, null, '2025-05-13 14:44:53', 'SYSTEM', '2025-05-13 14:44:53', 'SYSTEM'); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('NOTIFICATION_TYPE_CD', 'ANSWER', '답변', '답변 관련 알림', 3, 'Y', null, null, null, null, null, '2025-06-23 17:37:59', 'SYSTEM', '2025-06-23 17:37:59', 'SYSTEM'); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('NOTIFICATION_TYPE_CD', 'BATCH_FAILURE', '배치실패', '배치 실패 알림', 4, 'Y', null, null, null, null, null, '2025-07-15 13:41:19', 'ADMIN', null, null); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('NOTIFICATION_TYPE_CD', 'QUESTION', '질문', '질문 관련 알림', 2, 'Y', null, null, null, null, null, '2025-06-23 17:37:59', 'SYSTEM', '2025-06-23 17:37:59', 'SYSTEM'); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('NOTIFICATION_TYPE_CD', 'SCHEDULE', '일정', '일정 관련 알림', 1, 'Y', null, null, null, null, null, '2025-06-23 17:37:59', 'SYSTEM', '2025-06-23 17:37:59', 'SYSTEM'); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('NSTT_CD', 'NSTT001', '본사', '본사', 1, 'Y', null, null, null, null, null, '2025-05-13 14:44:53', 'SYSTEM', '2025-05-13 14:44:53', 'SYSTEM'); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('NSTT_CD', 'NSTT002', '지역사무소', '지역사무소', 2, 'Y', null, null, null, null, null, '2025-05-13 14:44:53', 'SYSTEM', '2025-05-13 14:44:53', 'SYSTEM'); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('NSTT_CD', 'NSTT003', '연구소', '연구소', 3, 'Y', null, null, null, null, null, '2025-05-13 14:44:53', 'SYSTEM', '2025-05-13 14:44:53', 'SYSTEM'); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('NSTT_CD', 'NSTT004', '해외지사', '해외지사', 4, 'Y', null, null, null, null, null, '2025-05-13 14:44:53', 'SYSTEM', '2025-05-13 14:44:53', 'SYSTEM'); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('NSTT_CD', 'NSTT005', '협력업체', '협력업체', 5, 'Y', null, null, null, null, null, '2025-05-13 14:44:53', 'SYSTEM', '2025-05-13 14:44:53', 'SYSTEM'); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('ORG_CD', 'ORG001', '본사 조직', '본사 조직', 1, 'Y', null, null, null, null, null, '2025-05-13 14:44:53', 'SYSTEM', '2025-05-13 14:44:53', 'SYSTEM'); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('ORG_CD', 'ORG002', '지역 조직', '지역 조직', 2, 'Y', null, null, null, null, null, '2025-05-13 14:44:53', 'SYSTEM', '2025-05-13 14:44:53', 'SYSTEM'); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('ORG_CD', 'ORG003', '연구 조직', '연구 조직', 3, 'Y', null, null, null, null, null, '2025-05-13 14:44:53', 'SYSTEM', '2025-05-13 14:44:53', 'SYSTEM'); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('ORG_CD', 'ORG004', '해외 조직', '해외 조직', 4, 'Y', null, null, null, null, null, '2025-05-13 14:44:53', 'SYSTEM', '2025-05-13 14:44:53', 'SYSTEM'); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('ORG_CD', 'ORG005', '협력 조직', '협력 조직', 5, 'Y', null, null, null, null, null, '2025-05-13 14:44:53', 'SYSTEM', '2025-05-13 14:44:53', 'SYSTEM'); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('SCHEDULE_CATEGORY_CD', 'BUSINESS_TRIP', '출장', '출장 관련 일정', 2, 'Y', 'rgba(111, 66, 193, 0.7)', null, null, null, null, '2025-06-13 10:36:55', 'SYSTEM', '2025-06-13 15:50:23', 'ADMIN'); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('SCHEDULE_CATEGORY_CD', 'IMPORTANT', '중요', '중요 일정', 5, 'Y', 'rgba(255, 159, 67, 0.7)', null, null, null, null, '2025-06-13 10:36:55', 'SYSTEM', '2025-06-13 15:32:58', 'ADMIN'); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('SCHEDULE_CATEGORY_CD', 'MEETING', '회의', '회의 관련 일정', 4, 'Y', 'rgba(46, 139, 87, 0.7)', null, null, null, null, '2025-06-13 10:36:55', 'SYSTEM', '2025-06-13 16:10:03', 'ADMIN'); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('SCHEDULE_CATEGORY_CD', 'VACATION', '휴가', '휴가 관련 일정', 3, 'Y', 'rgba(23, 162, 184, 0.7)', null, null, null, null, '2025-06-13 10:36:55', 'SYSTEM', '2025-06-13 15:50:23', 'ADMIN'); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('SCHEDULE_CATEGORY_CD', 'WORK', '업무', '업무 관련 일정', 1, 'Y', 'rgba(55, 136, 216, 0.7)', null, null, null, null, '2025-06-13 10:36:55', 'SYSTEM', '2025-06-13 15:32:25', 'ADMIN'); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('TEST_CD', 'TTT_TEST_CD1_1_1', '테스트 코드1', '1', 1, 'Y', null, null, null, null, null, '2025-07-09 13:56:19', 'ADMIN', '2025-07-21 10:33:44', 'ADMIN'); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('TEST_CD', 'TTT_TEST_CD2_1_1', '테스트 코드2', '2', 2, 'Y', null, null, null, null, null, '2025-07-09 13:56:19', 'ADMIN', '2025-07-21 10:33:44', 'ADMIN'); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('TEST_CD', 'TTT_TEST_CD3_1_1', '테스트 코드3', '3', 3, 'Y', null, null, null, null, null, '2025-07-09 13:56:19', 'ADMIN', '2025-07-21 10:33:49', 'ADMIN'); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('TEST_CD2', 'TTT_TEST_CD1_1_1', '테스트 코드1', '1', 1, 'Y', null, null, null, null, null, '2025-07-11 16:21:46', 'ADMIN', '2025-07-18 09:39:27', 'ADMIN'); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('TEST_CD2', 'TTT_TEST_CD2_1_1', '테스트 코드2', '2', 2, 'Y', null, null, null, null, null, '2025-07-11 16:21:46', 'ADMIN', '2025-07-21 13:35:00', 'ADMIN'); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('TEST_CD2', 'TTT_TEST_CD3_1_1', '테스트 코드3', '3', 3, 'Y', null, null, null, null, null, '2025-07-11 16:21:46', 'ADMIN', '2025-07-21 13:35:00', 'ADMIN'); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('USER_STATUS_CD', 'ACTIVE', '활성', '활성 상태의 사용자', 1, 'Y', null, null, null, null, null, '2025-05-13 14:24:50', 'SYSTEM', '2025-05-13 14:24:50', 'SYSTEM'); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('USER_STATUS_CD', 'DELETED', '삭제', '삭제된 상태의 사용자', 4, 'Y', null, null, null, null, null, '2025-05-13 14:24:50', 'SYSTEM', '2025-05-13 14:24:50', 'SYSTEM'); +INSERT INTO xitframework.tb_cd_detail (CD_GROUP_ID, CD_ID, CD_NM, CD_DC, SORT_ORDR, USE_YN, ATTRIBUTE1, ATTRIBUTE2, ATTRIBUTE3, ATTRIBUTE4, ATTRIBUTE5, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('USER_STATUS_CD', 'INACTIVE', '비활성', '비활성 상태의 사용자', 2, 'Y', null, null, null, null, null, '2025-05-13 14:24:50', 'SYSTEM', '2025-05-13 14:24:50', 'SYSTEM'); diff --git a/DB-DDL/maria/dml/tb_cd_group.sql b/DB-DDL/maria/dml/tb_cd_group.sql new file mode 100644 index 0000000..d802f3f --- /dev/null +++ b/DB-DDL/maria/dml/tb_cd_group.sql @@ -0,0 +1,14 @@ +INSERT INTO xitframework.tb_cd_group (CD_GROUP_ID, CD_GROUP_NM, CD_GROUP_DC, USE_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('BATCH_EXEC_STATUS_CD', '배치 작업 실행 상태', '배치 작업 실행의 상태 코드', 'Y', '2025-06-18 13:24:51', 'SYSTEM', null, null); +INSERT INTO xitframework.tb_cd_group (CD_GROUP_ID, CD_GROUP_NM, CD_GROUP_DC, USE_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('BATCH_EXIT_CD', '배치 작업 실행 종료 코드', '배치 작업 실행의 종료 코드', 'Y', '2025-06-18 13:24:51', 'SYSTEM', null, null); +INSERT INTO xitframework.tb_cd_group (CD_GROUP_ID, CD_GROUP_NM, CD_GROUP_DC, USE_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('BATCH_JOB_STATUS_CD', '배치 작업 정보 상태', '배치 작업 정보의 상태 코드', 'Y', '2025-06-18 13:24:51', 'SYSTEM', null, null); +INSERT INTO xitframework.tb_cd_group (CD_GROUP_ID, CD_GROUP_NM, CD_GROUP_DC, USE_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('BBS_ANSWER_STATUS_CD', '게시물 답변 상태', '답변 상태 (WAITING: 대기중, COMPLETED: 답변완료)', 'Y', '2025-06-04 12:27:54', 'SYSTEM', null, null); +INSERT INTO xitframework.tb_cd_group (CD_GROUP_ID, CD_GROUP_NM, CD_GROUP_DC, USE_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('BBS_POST_TYPE_CD', '게시물 유형', '게시물 유형 (Q: 질문, A: 답변)', 'Y', '2025-06-04 12:27:54', 'SYSTEM', null, null); +INSERT INTO xitframework.tb_cd_group (CD_GROUP_ID, CD_GROUP_NM, CD_GROUP_DC, USE_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('BBS_TYPE_CD', '게시판 종류', '게시판 종류를 관리하는 코드 그룹', 'Y', '2025-05-26 16:43:32', 'SYSTEM', null, null); +INSERT INTO xitframework.tb_cd_group (CD_GROUP_ID, CD_GROUP_NM, CD_GROUP_DC, USE_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('DEPT_CD', '부서 코드', '부서를 나타내는 코드', 'Y', '2025-05-13 14:44:53', 'SYSTEM', '2025-07-21 13:38:37', 'SYSTEM'); +INSERT INTO xitframework.tb_cd_group (CD_GROUP_ID, CD_GROUP_NM, CD_GROUP_DC, USE_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('NOTIFICATION_TYPE_CD', '알림 유형', '공통 알림 시스템의 알림 유형 코드', 'Y', '2025-06-23 17:37:59', 'SYSTEM', '2025-06-23 17:37:59', 'SYSTEM'); +INSERT INTO xitframework.tb_cd_group (CD_GROUP_ID, CD_GROUP_NM, CD_GROUP_DC, USE_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('NSTT_CD', '소속기관 코드', '소속기관을 나타내는 코드', 'Y', '2025-05-13 14:44:53', 'SYSTEM', '2025-05-13 14:44:53', 'SYSTEM'); +INSERT INTO xitframework.tb_cd_group (CD_GROUP_ID, CD_GROUP_NM, CD_GROUP_DC, USE_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('ORG_CD', '조직 코드', '조직을 나타내는 코드', 'Y', '2025-05-13 14:44:53', 'SYSTEM', '2025-05-13 14:44:53', 'SYSTEM'); +INSERT INTO xitframework.tb_cd_group (CD_GROUP_ID, CD_GROUP_NM, CD_GROUP_DC, USE_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('SCHEDULE_CATEGORY_CD', '일정 범주', '일정 관리 시스템의 일정 범주 코드', 'Y', '2025-06-13 10:36:55', 'SYSTEM', '2025-06-13 10:36:55', 'SYSTEM'); +INSERT INTO xitframework.tb_cd_group (CD_GROUP_ID, CD_GROUP_NM, CD_GROUP_DC, USE_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('TEST_CD', '테스트 코드', '', 'N', '2025-07-09 13:55:39', 'ADMIN', '2025-07-21 14:33:41', 'ADMIN'); +INSERT INTO xitframework.tb_cd_group (CD_GROUP_ID, CD_GROUP_NM, CD_GROUP_DC, USE_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('TEST_CD2', '테스트 코드', '', 'Y', '2025-07-11 16:21:03', 'ADMIN', '2025-07-21 13:39:29', 'ADMIN'); +INSERT INTO xitframework.tb_cd_group (CD_GROUP_ID, CD_GROUP_NM, CD_GROUP_DC, USE_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('USER_STATUS_CD', '사용자 상태', '사용자의 상태 코드', 'Y', '2025-05-13 14:24:50', 'SYSTEM', '2025-05-13 14:24:50', 'SYSTEM'); diff --git a/DB-DDL/maria/dml/tb_group.sql b/DB-DDL/maria/dml/tb_group.sql new file mode 100644 index 0000000..ea6e277 --- /dev/null +++ b/DB-DDL/maria/dml/tb_group.sql @@ -0,0 +1,6 @@ +INSERT INTO xitframework.tb_group (GROUP_ID, GROUP_NM, GROUP_DC, SORT_ORDR, USE_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('GROUP_ADMIN', '관리자 그룹', '시스템 관리 권한을 가진 사용자 그룹', 5, 'Y', '2025-05-15 10:09:55', 'SYSTEM', '2025-05-15 10:09:55', 'SYSTEM'); +INSERT INTO xitframework.tb_group (GROUP_ID, GROUP_NM, GROUP_DC, SORT_ORDR, USE_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('GROUP_APPROVER', '업무 승인 담당자 그룹', '업무 승인 권한을 가진 사용자 그룹', 4, 'Y', '2025-05-16 12:44:14', 'SYSTEM', '2025-05-16 12:44:14', 'SYSTEM'); +INSERT INTO xitframework.tb_group (GROUP_ID, GROUP_NM, GROUP_DC, SORT_ORDR, USE_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('GROUP_SYSTEM', '시스템 관리자 그룹-XIT', '모든 시스템 권한을 가진 사용자 그룹(XIT)', 999, 'Y', '2025-05-15 10:09:55', 'SYSTEM', '2025-07-21 13:40:13', 'SYSTEM'); +INSERT INTO xitframework.tb_group (GROUP_ID, GROUP_NM, GROUP_DC, SORT_ORDR, USE_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('GROUP_USER', '일반 사용자 그룹', '일반 사용자 권한을 가진 사용자 그룹', 2, 'Y', '2025-05-15 10:09:55', 'SYSTEM', '2025-06-04 16:34:22', 'SYSTEM'); +INSERT INTO xitframework.tb_group (GROUP_ID, GROUP_NM, GROUP_DC, SORT_ORDR, USE_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('GROUP_VISITOR', '방문자 그룹', '방문자 권한을 가진 사용자 그룹', 1, 'Y', '2025-05-15 10:09:55', 'SYSTEM', '2025-05-15 10:09:55', 'SYSTEM'); +INSERT INTO xitframework.tb_group (GROUP_ID, GROUP_NM, GROUP_DC, SORT_ORDR, USE_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('GROUP_WORKER', '업무 담당자 그룹', '업무 처리 권한을 가진 사용자 그룹', 3, 'Y', '2025-05-16 12:44:12', 'SYSTEM', '2025-07-18 15:37:11', 'SYSTEM'); diff --git a/DB-DDL/maria/dml/tb_group_role.sql b/DB-DDL/maria/dml/tb_group_role.sql new file mode 100644 index 0000000..3682fc0 --- /dev/null +++ b/DB-DDL/maria/dml/tb_group_role.sql @@ -0,0 +1,8 @@ +INSERT INTO xitframework.tb_group_role (GROUP_ID, ROLE_ID, REG_DTTM, RGTR) VALUES ('GROUP_ADMIN', 'ROLE_ADMIN', '2025-05-16 12:41:59', 'SYSTEM'); +INSERT INTO xitframework.tb_group_role (GROUP_ID, ROLE_ID, REG_DTTM, RGTR) VALUES ('GROUP_APPROVER', 'ROLE_APPROVER', '2025-05-30 14:45:09', 'ADMIN'); +INSERT INTO xitframework.tb_group_role (GROUP_ID, ROLE_ID, REG_DTTM, RGTR) VALUES ('GROUP_APPROVER', 'ROLE_USER', '2025-07-21 13:40:34', 'ADMIN'); +INSERT INTO xitframework.tb_group_role (GROUP_ID, ROLE_ID, REG_DTTM, RGTR) VALUES ('GROUP_APPROVER', 'ROLE_WORKER', '2025-07-09 14:04:57', 'ADMIN'); +INSERT INTO xitframework.tb_group_role (GROUP_ID, ROLE_ID, REG_DTTM, RGTR) VALUES ('GROUP_SYSTEM', 'ROLE_SYSTEM', '2025-05-16 12:42:00', 'SYSTEM'); +INSERT INTO xitframework.tb_group_role (GROUP_ID, ROLE_ID, REG_DTTM, RGTR) VALUES ('GROUP_USER', 'ROLE_USER', '2025-05-16 12:41:56', 'SYSTEM'); +INSERT INTO xitframework.tb_group_role (GROUP_ID, ROLE_ID, REG_DTTM, RGTR) VALUES ('GROUP_USER', 'ROLE_WORKER', '2025-07-18 15:36:42', 'ADMIN'); +INSERT INTO xitframework.tb_group_role (GROUP_ID, ROLE_ID, REG_DTTM, RGTR) VALUES ('GROUP_WORKER', 'ROLE_WORKER', '2025-05-16 12:44:20', 'SYSTEM'); diff --git a/DB-DDL/maria/dml/tb_menu.sql b/DB-DDL/maria/dml/tb_menu.sql new file mode 100644 index 0000000..729cbd7 --- /dev/null +++ b/DB-DDL/maria/dml/tb_menu.sql @@ -0,0 +1,30 @@ +INSERT INTO xitframework.tb_menu (MENU_ID, MENU_NM, MENU_DC, UPPER_MENU_ID, MENU_LEVEL, SORT_ORDR, MENU_URL, URL_PATTERN, MENU_ICON, USE_YN, VIEW_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('MENU00000001', '홈', '시스템 홈 메뉴', 'ROOT', 1, 1, '/main.do', '/main.do', 'home', 'Y', 'Y', '2025-05-16 12:49:48', 'SYSTEM', '2025-05-30 14:40:46', 'ADMIN'); +INSERT INTO xitframework.tb_menu (MENU_ID, MENU_NM, MENU_DC, UPPER_MENU_ID, MENU_LEVEL, SORT_ORDR, MENU_URL, URL_PATTERN, MENU_ICON, USE_YN, VIEW_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('MENU00000002', '게시판 관리', '공통 관리 메뉴', 'ROOT', 1, 500, '', '', 'edit-3', 'Y', 'Y', '2025-05-16 12:49:48', 'SYSTEM', '2025-06-04 14:01:04', 'ADMIN'); +INSERT INTO xitframework.tb_menu (MENU_ID, MENU_NM, MENU_DC, UPPER_MENU_ID, MENU_LEVEL, SORT_ORDR, MENU_URL, URL_PATTERN, MENU_ICON, USE_YN, VIEW_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('MENU00000003', '시스템 관리', '시스템 관리 메뉴', 'ROOT', 1, 900, '', '', 'settings', 'Y', 'Y', '2025-05-16 12:49:48', 'SYSTEM', '2025-05-30 14:41:20', 'ADMIN'); +INSERT INTO xitframework.tb_menu (MENU_ID, MENU_NM, MENU_DC, UPPER_MENU_ID, MENU_LEVEL, SORT_ORDR, MENU_URL, URL_PATTERN, MENU_ICON, USE_YN, VIEW_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('MENU00000005', '코드 통합 관리', '코드 마스터와 코드 상세를 한 화면에서 관리', 'MENU00000003', 2, 1, '/system/code/integrated/manage.do', '/system/code/**/*.do,/system/code/**/*.ajax', null, 'Y', 'Y', '2025-05-16 12:49:48', 'SYSTEM', '2025-05-20 15:07:54', 'SYSTEM'); +INSERT INTO xitframework.tb_menu (MENU_ID, MENU_NM, MENU_DC, UPPER_MENU_ID, MENU_LEVEL, SORT_ORDR, MENU_URL, URL_PATTERN, MENU_ICON, USE_YN, VIEW_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('MENU00000006', '사용자 관리', '사용자 관리 메뉴', 'MENU00000003', 2, 2, '/system/user/list.do', '/system/user/**/*.do,/system/user/**/*.ajax', null, 'Y', 'Y', '2025-05-16 12:49:48', 'SYSTEM', '2025-05-16 12:49:48', 'SYSTEM'); +INSERT INTO xitframework.tb_menu (MENU_ID, MENU_NM, MENU_DC, UPPER_MENU_ID, MENU_LEVEL, SORT_ORDR, MENU_URL, URL_PATTERN, MENU_ICON, USE_YN, VIEW_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('MENU00000007', '그룹 관리', '그룹 관리 메뉴', 'MENU00000003', 2, 3, '/system/group/list.do', '/system/group/**/*.do,/system/group/**/*.ajax', null, 'Y', 'Y', '2025-05-16 12:49:48', 'SYSTEM', '2025-05-16 12:49:48', 'SYSTEM'); +INSERT INTO xitframework.tb_menu (MENU_ID, MENU_NM, MENU_DC, UPPER_MENU_ID, MENU_LEVEL, SORT_ORDR, MENU_URL, URL_PATTERN, MENU_ICON, USE_YN, VIEW_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('MENU00000008', '역할 관리', '역할 관리 메뉴', 'MENU00000003', 2, 4, '/system/role/list.do', '/system/role/**/*.do,/system/role/**/*.ajax', null, 'Y', 'Y', '2025-05-16 12:49:48', 'SYSTEM', '2025-05-16 12:49:48', 'SYSTEM'); +INSERT INTO xitframework.tb_menu (MENU_ID, MENU_NM, MENU_DC, UPPER_MENU_ID, MENU_LEVEL, SORT_ORDR, MENU_URL, URL_PATTERN, MENU_ICON, USE_YN, VIEW_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('MENU00000009', '메뉴 관리', '메뉴 관리 메뉴', 'MENU00000003', 2, 5, '/system/menu/list.do', '/system/menu/**/*.do,/system/menu/**/*.ajax', null, 'Y', 'Y', '2025-05-16 12:49:48', 'SYSTEM', '2025-05-16 12:49:48', 'SYSTEM'); +INSERT INTO xitframework.tb_menu (MENU_ID, MENU_NM, MENU_DC, UPPER_MENU_ID, MENU_LEVEL, SORT_ORDR, MENU_URL, URL_PATTERN, MENU_ICON, USE_YN, VIEW_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('MENU00000010', '권한 관리', '권한 관리 메뉴', 'MENU00000003', 2, 6, '/system/auth/main.do', '/system/auth/**/*.do,/system/auth/**.ajax,/system/auth/**/*.ajax', null, 'Y', 'Y', '2025-05-16 12:49:48', 'SYSTEM', '2025-05-16 12:49:48', 'SYSTEM'); +INSERT INTO xitframework.tb_menu (MENU_ID, MENU_NM, MENU_DC, UPPER_MENU_ID, MENU_LEVEL, SORT_ORDR, MENU_URL, URL_PATTERN, MENU_ICON, USE_YN, VIEW_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('MENU00000011', '공통', '공통', 'ROOT', 1, 100, '', '/common/**/*.do,/common/**/*.ajax', 'package', 'Y', 'N', '2025-05-15 14:03:38', 'SYSTEM', '2025-05-30 14:42:10', 'ADMIN'); +INSERT INTO xitframework.tb_menu (MENU_ID, MENU_NM, MENU_DC, UPPER_MENU_ID, MENU_LEVEL, SORT_ORDR, MENU_URL, URL_PATTERN, MENU_ICON, USE_YN, VIEW_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('MENU00000052', '마이페이지', '마이페이지', 'ROOT', 1, 90, '/mypage.do', '/mypage.do,/mypage/**/*.do,/mypage/**/*.ajax', 'shield', 'Y', 'N', '2025-05-21 09:33:38', 'SYSTEM', '2025-07-08 10:05:50', 'ADMIN'); +INSERT INTO xitframework.tb_menu (MENU_ID, MENU_NM, MENU_DC, UPPER_MENU_ID, MENU_LEVEL, SORT_ORDR, MENU_URL, URL_PATTERN, MENU_ICON, USE_YN, VIEW_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('MENU00000053', '로그인 로그', '', 'MENU00000003', 2, 6, '/system/loginLog/list.do', '/system/loginLog/**/*.do,/system/loginLog/**/*.ajax', '', 'Y', 'Y', '2025-05-22 15:06:30', 'ADMIN', '2025-07-08 14:12:03', 'ADMIN'); +INSERT INTO xitframework.tb_menu (MENU_ID, MENU_NM, MENU_DC, UPPER_MENU_ID, MENU_LEVEL, SORT_ORDR, MENU_URL, URL_PATTERN, MENU_ICON, USE_YN, VIEW_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('MENU00000054', '게시판 설정', '게시판 설정, 게시판 타입별 생성 수정 삭제', 'MENU00000002', 2, 1, '/bbs/manage/config/list.do', '/bbs/manage/config/**/*.do,/bbs/manage/config/**/*.ajax', '', 'Y', 'Y', '2025-05-26 15:04:09', 'ADMIN', '2025-06-05 11:58:22', 'ADMIN'); +INSERT INTO xitframework.tb_menu (MENU_ID, MENU_NM, MENU_DC, UPPER_MENU_ID, MENU_LEVEL, SORT_ORDR, MENU_URL, URL_PATTERN, MENU_ICON, USE_YN, VIEW_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('MENU00000092', 'CRUD 예제', '등록, 수정, 삭제, 조회 예제', 'ROOT', 1, 110, '', '', 'list', 'Y', 'Y', '2025-05-30 18:27:36', 'ADMIN', '2025-06-05 12:00:09', 'ADMIN'); +INSERT INTO xitframework.tb_menu (MENU_ID, MENU_NM, MENU_DC, UPPER_MENU_ID, MENU_LEVEL, SORT_ORDR, MENU_URL, URL_PATTERN, MENU_ICON, USE_YN, VIEW_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('MENU00000093', '공지사항 예제', '공지사항 - 별도 소스 개발', 'MENU00000092', 2, 1, '/template/noticeSample/list.do', '/template/noticeSample/**.do,/template/noticeSample/**/*.ajax', '', 'Y', 'Y', '2025-05-30 18:28:18', 'ADMIN', '2025-06-04 13:42:44', 'ADMIN'); +INSERT INTO xitframework.tb_menu (MENU_ID, MENU_NM, MENU_DC, UPPER_MENU_ID, MENU_LEVEL, SORT_ORDR, MENU_URL, URL_PATTERN, MENU_ICON, USE_YN, VIEW_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('MENU00000094', '공지사항 관리', '게시판 설정의 공지사항 관리', 'MENU00000002', 2, 2, '/bbs/manage/post/BBSC00000001/list.do', '/bbs/manage/post/BBSC00000001/**/*.do,/bbs/manage/post/BBSC00000001/**/*.ajax', '', 'Y', 'Y', '2025-06-02 10:57:48', 'ADMIN', '2025-06-05 12:25:16', 'ADMIN'); +INSERT INTO xitframework.tb_menu (MENU_ID, MENU_NM, MENU_DC, UPPER_MENU_ID, MENU_LEVEL, SORT_ORDR, MENU_URL, URL_PATTERN, MENU_ICON, USE_YN, VIEW_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('MENU00000096', '공지사항', '공지사', 'ROOT', 1, 2, '/bbs/user/post/BBSC00000001/list.do', '/bbs/user/post/BBSC00000001/**/*.do,/bbs/user/post/BBSC00000001/**/*.ajax', 'edit', 'Y', 'Y', '2025-06-02 14:21:23', 'ADMIN', '2025-06-05 12:26:35', 'ADMIN'); +INSERT INTO xitframework.tb_menu (MENU_ID, MENU_NM, MENU_DC, UPPER_MENU_ID, MENU_LEVEL, SORT_ORDR, MENU_URL, URL_PATTERN, MENU_ICON, USE_YN, VIEW_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('MENU00000097', 'FAQ 관리', 'FAQ 관리', 'MENU00000002', 2, 4, '/bbs/manage/post/BBSC00000040/list.do', '/bbs/manage/post/BBSC00000040/**/*.do,/bbs/manage/post/BBSC00000040/**/*.ajax', '', 'Y', 'Y', '2025-06-02 15:51:09', 'ADMIN', '2025-06-05 15:15:56', 'ADMIN'); +INSERT INTO xitframework.tb_menu (MENU_ID, MENU_NM, MENU_DC, UPPER_MENU_ID, MENU_LEVEL, SORT_ORDR, MENU_URL, URL_PATTERN, MENU_ICON, USE_YN, VIEW_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('MENU00000098', 'FAQ', '', 'ROOT', 1, 3, '/bbs/user/post/BBSC00000040/listFaqData.do', '/bbs/user/post/BBSC00000040/**/*.do,/bbs/user/post/BBSC00000040/**/*.ajax', 'info', 'Y', 'Y', '2025-06-02 15:58:24', 'ADMIN', '2025-06-05 12:27:01', 'ADMIN'); +INSERT INTO xitframework.tb_menu (MENU_ID, MENU_NM, MENU_DC, UPPER_MENU_ID, MENU_LEVEL, SORT_ORDR, MENU_URL, URL_PATTERN, MENU_ICON, USE_YN, VIEW_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('MENU00000099', '질문과 답변', '질문과 답변 QNA', 'MENU00000002', 2, 5, '/bbs/manage/qna/BBSC00000041/list.do', '/bbs/manage/qna/BBSC00000041/**/*.do,/bbs/manage/qna/BBSC00000041/**/*.ajax', '', 'Y', 'Y', '2025-06-02 17:08:43', 'ADMIN', '2025-06-05 15:16:00', 'ADMIN'); +INSERT INTO xitframework.tb_menu (MENU_ID, MENU_NM, MENU_DC, UPPER_MENU_ID, MENU_LEVEL, SORT_ORDR, MENU_URL, URL_PATTERN, MENU_ICON, USE_YN, VIEW_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('MENU00000100', '질문과 답변', '질문과 답변', 'ROOT', 1, 5, '/bbs/user/qna/BBSC00000041/list.do', '/bbs/user/qna/BBSC00000041/**/*.do,/bbs/user/qna/BBSC00000041/**/*.ajax', 'help-circle', 'Y', 'Y', '2025-06-02 17:09:22', 'ADMIN', '2025-06-05 12:27:26', 'ADMIN'); +INSERT INTO xitframework.tb_menu (MENU_ID, MENU_NM, MENU_DC, UPPER_MENU_ID, MENU_LEVEL, SORT_ORDR, MENU_URL, URL_PATTERN, MENU_ICON, USE_YN, VIEW_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('MENU00000107', '일반 게시판 관리', '일반 게시판', 'MENU00000002', 2, 3, '/bbs/manage/post/BBSC00000100/list.do', '/bbs/manage/post/BBSC00000100/**/*.do,/bbs/manage/post/BBSC00000100/**/*.ajax', '', 'Y', 'Y', '2025-06-05 15:15:34', 'ADMIN', '2025-07-11 16:18:42', 'ADMIN'); +INSERT INTO xitframework.tb_menu (MENU_ID, MENU_NM, MENU_DC, UPPER_MENU_ID, MENU_LEVEL, SORT_ORDR, MENU_URL, URL_PATTERN, MENU_ICON, USE_YN, VIEW_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('MENU00000108', '일반 게시판', '', 'ROOT', 1, 6, '/bbs/user/post/BBSC00000100/list.do', '/bbs/user/post/BBSC00000100/**/*.do,/bbs/user/post/BBSC00000100/**/*.ajax', 'book', 'Y', 'Y', '2025-06-05 15:16:44', 'ADMIN', '2025-06-05 15:18:28', 'ADMIN'); +INSERT INTO xitframework.tb_menu (MENU_ID, MENU_NM, MENU_DC, UPPER_MENU_ID, MENU_LEVEL, SORT_ORDR, MENU_URL, URL_PATTERN, MENU_ICON, USE_YN, VIEW_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('MENU00000109', 'dataTable 테스트', '1111111111111111111', 'ROOT', 1, 1, '/bbs/user/post/BBSC00000100/dataTableList.do', '/bbs/user/post/BBSC00000100/**/*.do,/bbs/user/post/BBSC00000100/**/*.ajax', 'clipboard', 'Y', 'Y', '2025-06-05 17:18:03', 'USER00000051', '2025-07-21 15:41:53', 'ADMIN'); +INSERT INTO xitframework.tb_menu (MENU_ID, MENU_NM, MENU_DC, UPPER_MENU_ID, MENU_LEVEL, SORT_ORDR, MENU_URL, URL_PATTERN, MENU_ICON, USE_YN, VIEW_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('MENU00000110', '이미지 편집', '이미지 편집 테스트 진행', 'MENU00000092', 2, 2, '/template/imageModifySample/imageModify.do', '/template/imageModifySample/**/*.do,/template/imageModifySample/**/*.ajax', '', 'Y', 'Y', '2025-06-09 15:33:07', 'ADMIN', '2025-06-09 15:33:07', 'ADMIN'); +INSERT INTO xitframework.tb_menu (MENU_ID, MENU_NM, MENU_DC, UPPER_MENU_ID, MENU_LEVEL, SORT_ORDR, MENU_URL, URL_PATTERN, MENU_ICON, USE_YN, VIEW_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('MENU00000111', '일정관리 예제', '', 'MENU00000092', 2, 3, '/template/calendarSample/calendarSample.do', '/template/calendarSample/**/*.do,/template/calendarSample/**/*.ajax', '', 'Y', 'Y', '2025-06-12 09:10:51', 'ADMIN', '2025-07-08 14:11:59', 'ADMIN'); +INSERT INTO xitframework.tb_menu (MENU_ID, MENU_NM, MENU_DC, UPPER_MENU_ID, MENU_LEVEL, SORT_ORDR, MENU_URL, URL_PATTERN, MENU_ICON, USE_YN, VIEW_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('MENU00000112', '배치 관리', '배치관리 프로그램', 'MENU00000003', 2, 10, '/batch/list.do', '/batch/**/*.do,/batch/**/*.ajax', '', 'Y', 'Y', '2025-06-18 10:00:31', 'ADMIN', '2025-06-18 10:00:31', 'ADMIN'); +INSERT INTO xitframework.tb_menu (MENU_ID, MENU_NM, MENU_DC, UPPER_MENU_ID, MENU_LEVEL, SORT_ORDR, MENU_URL, URL_PATTERN, MENU_ICON, USE_YN, VIEW_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('MENU00000113', '쿼리 실행기', '쿼리 실행기', 'MENU00000003', 2, 99, '/db/exec/query.do', '/db/exec/**/*.do,/db/exec/**/*.ajax', '', 'Y', 'Y', '2025-07-07 17:14:18', 'ADMIN', '2025-07-07 17:14:18', 'ADMIN'); +INSERT INTO xitframework.tb_menu (MENU_ID, MENU_NM, MENU_DC, UPPER_MENU_ID, MENU_LEVEL, SORT_ORDR, MENU_URL, URL_PATTERN, MENU_ICON, USE_YN, VIEW_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('MENU00000114', '엑셀 예제', '엑셀 다운로드, 업로드', 'MENU00000092', 2, 4, '/template/excelSample/list.do', '/template/excelSample/**/*.do,/template/excelSample/**/*.ajax', '', 'Y', 'Y', '2025-07-10 09:47:22', 'ADMIN', '2025-07-10 09:47:22', 'ADMIN'); +INSERT INTO xitframework.tb_menu (MENU_ID, MENU_NM, MENU_DC, UPPER_MENU_ID, MENU_LEVEL, SORT_ORDR, MENU_URL, URL_PATTERN, MENU_ICON, USE_YN, VIEW_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('ROOT', 'ROOT', '최상위 메뉴', null, 0, 0, null, null, null, 'Y', 'N', '2025-05-16 12:49:48', 'SYSTEM', '2025-05-16 12:49:48', 'SYSTEM'); diff --git a/DB-DDL/maria/dml/tb_role.sql b/DB-DDL/maria/dml/tb_role.sql new file mode 100644 index 0000000..bbe3704 --- /dev/null +++ b/DB-DDL/maria/dml/tb_role.sql @@ -0,0 +1,6 @@ +INSERT INTO xitframework.tb_role (ROLE_ID, ROLE_NM, ROLE_DC, SORT_ORDR, USE_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('ROLE_ADMIN', '관리자', '시스템 관리 권한을 가진 관리자 역할', 5, 'Y', '2025-05-15 10:09:55', 'SYSTEM', '2025-05-15 10:09:55', 'SYSTEM'); +INSERT INTO xitframework.tb_role (ROLE_ID, ROLE_NM, ROLE_DC, SORT_ORDR, USE_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('ROLE_APPROVER', '업무 승인 담당자', '업무 승인 권한을 가진 담당자 역할', 4, 'Y', '2025-05-16 12:43:54', 'SYSTEM', '2025-05-16 12:43:54', 'SYSTEM'); +INSERT INTO xitframework.tb_role (ROLE_ID, ROLE_NM, ROLE_DC, SORT_ORDR, USE_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('ROLE_SYSTEM', '시스템 관리자-XIT', '모든 시스템 권한을 가진 시스템 관리자 역할(XIT)', 6, 'Y', '2025-05-15 10:09:55', 'SYSTEM', '2025-07-21 13:40:23', 'SYSTEM'); +INSERT INTO xitframework.tb_role (ROLE_ID, ROLE_NM, ROLE_DC, SORT_ORDR, USE_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('ROLE_USER', '일반 유저', '기본적인 시스템 사용 권한을 가진 일반 사용자 역할1', 2, 'Y', '2025-05-15 10:09:55', 'SYSTEM', '2025-05-21 16:15:28', 'SYSTEM'); +INSERT INTO xitframework.tb_role (ROLE_ID, ROLE_NM, ROLE_DC, SORT_ORDR, USE_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('ROLE_VISITOR', '방문자', '시스템에 접근 가능한 기본 권한을 가진 방문자 역할', 1, 'Y', '2025-05-15 10:09:55', 'SYSTEM', '2025-07-18 15:37:21', 'SYSTEM'); +INSERT INTO xitframework.tb_role (ROLE_ID, ROLE_NM, ROLE_DC, SORT_ORDR, USE_YN, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('ROLE_WORKER', '업무 담당자', '업무 처리 권한을 가진 담당자 역할', 3, 'Y', '2025-05-16 12:43:53', 'SYSTEM', '2025-05-16 12:43:53', 'SYSTEM'); diff --git a/DB-DDL/maria/dml/tb_role_menu.sql b/DB-DDL/maria/dml/tb_role_menu.sql new file mode 100644 index 0000000..3f40f53 --- /dev/null +++ b/DB-DDL/maria/dml/tb_role_menu.sql @@ -0,0 +1,102 @@ +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_ADMIN', 'MENU00000001', '2025-07-21 15:50:33', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_ADMIN', 'MENU00000002', '2025-07-21 15:50:33', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_ADMIN', 'MENU00000003', '2025-07-21 15:50:33', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_ADMIN', 'MENU00000005', '2025-07-21 15:50:33', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_ADMIN', 'MENU00000006', '2025-07-21 15:50:33', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_ADMIN', 'MENU00000011', '2025-07-21 15:50:33', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_ADMIN', 'MENU00000052', '2025-07-21 15:50:33', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_ADMIN', 'MENU00000053', '2025-07-21 15:50:33', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_ADMIN', 'MENU00000054', '2025-07-21 15:50:33', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_ADMIN', 'MENU00000092', '2025-07-21 15:50:33', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_ADMIN', 'MENU00000093', '2025-07-21 15:50:33', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_ADMIN', 'MENU00000094', '2025-07-21 15:50:33', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_ADMIN', 'MENU00000096', '2025-07-21 15:50:33', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_ADMIN', 'MENU00000097', '2025-07-21 15:50:33', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_ADMIN', 'MENU00000098', '2025-07-21 15:50:33', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_ADMIN', 'MENU00000099', '2025-07-21 15:50:33', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_ADMIN', 'MENU00000100', '2025-07-21 15:50:33', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_ADMIN', 'MENU00000107', '2025-07-21 15:50:33', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_ADMIN', 'MENU00000108', '2025-07-21 15:50:33', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_ADMIN', 'MENU00000109', '2025-07-21 15:50:33', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_ADMIN', 'MENU00000110', '2025-07-21 15:50:33', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_ADMIN', 'MENU00000111', '2025-07-21 15:50:33', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_ADMIN', 'MENU00000114', '2025-07-21 15:50:33', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_ADMIN', 'ROOT', '2025-07-21 15:50:33', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_APPROVER', 'MENU00000001', '2025-07-21 15:50:26', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_APPROVER', 'MENU00000002', '2025-07-21 15:50:26', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_APPROVER', 'MENU00000003', '2025-07-21 15:50:26', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_APPROVER', 'MENU00000011', '2025-07-21 15:50:26', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_APPROVER', 'MENU00000052', '2025-07-21 15:50:26', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_APPROVER', 'MENU00000053', '2025-07-21 15:50:26', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_APPROVER', 'MENU00000054', '2025-07-21 15:50:26', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_APPROVER', 'MENU00000092', '2025-07-21 15:50:26', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_APPROVER', 'MENU00000093', '2025-07-21 15:50:26', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_APPROVER', 'MENU00000094', '2025-07-21 15:50:26', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_APPROVER', 'MENU00000096', '2025-07-21 15:50:26', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_APPROVER', 'MENU00000097', '2025-07-21 15:50:26', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_APPROVER', 'MENU00000098', '2025-07-21 15:50:26', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_APPROVER', 'MENU00000099', '2025-07-21 15:50:26', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_APPROVER', 'MENU00000100', '2025-07-21 15:50:26', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_APPROVER', 'MENU00000107', '2025-07-21 15:50:26', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_APPROVER', 'MENU00000108', '2025-07-21 15:50:26', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_APPROVER', 'MENU00000110', '2025-07-21 15:50:26', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_APPROVER', 'MENU00000111', '2025-07-21 15:50:26', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_APPROVER', 'MENU00000114', '2025-07-21 15:50:26', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_APPROVER', 'ROOT', '2025-07-21 15:50:26', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_SYSTEM', 'MENU00000001', '2025-07-21 15:50:45', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_SYSTEM', 'MENU00000002', '2025-07-21 15:50:45', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_SYSTEM', 'MENU00000003', '2025-07-21 15:50:45', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_SYSTEM', 'MENU00000005', '2025-07-21 15:50:45', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_SYSTEM', 'MENU00000006', '2025-07-21 15:50:45', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_SYSTEM', 'MENU00000007', '2025-07-21 15:50:45', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_SYSTEM', 'MENU00000008', '2025-07-21 15:50:45', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_SYSTEM', 'MENU00000009', '2025-07-21 15:50:45', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_SYSTEM', 'MENU00000010', '2025-07-21 15:50:45', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_SYSTEM', 'MENU00000011', '2025-07-21 15:50:45', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_SYSTEM', 'MENU00000052', '2025-07-21 15:50:45', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_SYSTEM', 'MENU00000053', '2025-07-21 15:50:45', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_SYSTEM', 'MENU00000054', '2025-07-21 15:50:45', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_SYSTEM', 'MENU00000092', '2025-07-21 15:50:45', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_SYSTEM', 'MENU00000093', '2025-07-21 15:50:45', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_SYSTEM', 'MENU00000094', '2025-07-21 15:50:45', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_SYSTEM', 'MENU00000096', '2025-07-21 15:50:45', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_SYSTEM', 'MENU00000097', '2025-07-21 15:50:45', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_SYSTEM', 'MENU00000098', '2025-07-21 15:50:45', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_SYSTEM', 'MENU00000099', '2025-07-21 15:50:45', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_SYSTEM', 'MENU00000100', '2025-07-21 15:50:45', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_SYSTEM', 'MENU00000107', '2025-07-21 15:50:45', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_SYSTEM', 'MENU00000108', '2025-07-21 15:50:45', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_SYSTEM', 'MENU00000109', '2025-07-21 15:50:45', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_SYSTEM', 'MENU00000110', '2025-07-21 15:50:45', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_SYSTEM', 'MENU00000111', '2025-07-21 15:50:45', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_SYSTEM', 'MENU00000112', '2025-07-21 15:50:45', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_SYSTEM', 'MENU00000113', '2025-07-21 15:50:45', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_SYSTEM', 'MENU00000114', '2025-07-21 15:50:45', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_SYSTEM', 'ROOT', '2025-07-21 15:50:45', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_USER', 'MENU00000001', '2025-07-21 15:50:14', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_USER', 'MENU00000011', '2025-07-21 15:50:14', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_USER', 'MENU00000052', '2025-07-21 15:50:14', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_USER', 'MENU00000092', '2025-07-21 15:50:14', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_USER', 'MENU00000093', '2025-07-21 15:50:14', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_USER', 'MENU00000096', '2025-07-21 15:50:14', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_USER', 'MENU00000098', '2025-07-21 15:50:14', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_USER', 'MENU00000100', '2025-07-21 15:50:14', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_USER', 'MENU00000108', '2025-07-21 15:50:14', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_USER', 'MENU00000109', '2025-07-21 15:50:14', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_USER', 'MENU00000110', '2025-07-21 15:50:14', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_USER', 'MENU00000111', '2025-07-21 15:50:14', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_USER', 'MENU00000114', '2025-07-21 15:50:14', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_USER', 'ROOT', '2025-07-21 15:50:14', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_WORKER', 'MENU00000001', '2025-07-21 15:50:19', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_WORKER', 'MENU00000011', '2025-07-21 15:50:19', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_WORKER', 'MENU00000052', '2025-07-21 15:50:19', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_WORKER', 'MENU00000092', '2025-07-21 15:50:19', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_WORKER', 'MENU00000093', '2025-07-21 15:50:19', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_WORKER', 'MENU00000096', '2025-07-21 15:50:19', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_WORKER', 'MENU00000098', '2025-07-21 15:50:19', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_WORKER', 'MENU00000100', '2025-07-21 15:50:19', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_WORKER', 'MENU00000108', '2025-07-21 15:50:19', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_WORKER', 'MENU00000110', '2025-07-21 15:50:19', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_WORKER', 'MENU00000111', '2025-07-21 15:50:19', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_WORKER', 'MENU00000114', '2025-07-21 15:50:19', 'ADMIN'); +INSERT INTO xitframework.tb_role_menu (ROLE_ID, MENU_ID, REG_DTTM, RGTR) VALUES ('ROLE_WORKER', 'ROOT', '2025-07-21 15:50:19', 'ADMIN'); diff --git a/DB-DDL/maria/dml/tb_user.sql b/DB-DDL/maria/dml/tb_user.sql new file mode 100644 index 0000000..e9eb799 --- /dev/null +++ b/DB-DDL/maria/dml/tb_user.sql @@ -0,0 +1,5 @@ +INSERT INTO xitframework.tb_user (USER_ID, USER_ACNT, USER_NM, PASSWD, PASSWD_HINT, PASSWD_NSR, EMP_NO, GENDER, ZIP, ADDR, DADDR, AREA_NO, EML_ADDR, ORG_CD, USER_GROUP_ID, NSTT_CD, POS_NM, CRTFC_DN, USER_STATUS_CD, FXNO, TELNO, MBL_TELNO, BRDT, DEPT_CD, USE_YN, RSDNT_NO, PASSWD_INIT_YN, LOCK_YN, LOCK_CNT, LOCK_DTTM, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('ADMIN', 'admin', '시스템 관리자', 'Z6XYLOi+w3ukRN5nn08WSYg04spdJAvpCU4EOOd925g=', '관리자 계정', '관리자', '43534534', '', '14507', '경기도 부천시 원미구 계남로 81', '', null, 'admin@example.com', 'ORG001', 'GROUP_SYSTEM', 'NSTT001', '시스템 관리자', null, 'ACTIVE', null, '02-1234-5678', '010-1234-5678', '19800101', 'DEPT001', 'Y', null, 'N', 'N', 0, null, '2025-05-15 10:09:55', 'SYSTEM', '2025-07-23 12:05:08', 'ADMIN'); +INSERT INTO xitframework.tb_user (USER_ID, USER_ACNT, USER_NM, PASSWD, PASSWD_HINT, PASSWD_NSR, EMP_NO, GENDER, ZIP, ADDR, DADDR, AREA_NO, EML_ADDR, ORG_CD, USER_GROUP_ID, NSTT_CD, POS_NM, CRTFC_DN, USER_STATUS_CD, FXNO, TELNO, MBL_TELNO, BRDT, DEPT_CD, USE_YN, RSDNT_NO, PASSWD_INIT_YN, LOCK_YN, LOCK_CNT, LOCK_DTTM, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('USER00000004', 'user', '일반유저', 'w44uAXy4U9aSi0Pye94jU55pwg/YMVjU/rqqYqePGbo=', '', '', '1234', null, '', '', '', null, '', '', 'GROUP_USER', 'NSTT002', '', null, 'ACTIVE', null, '', '', null, '', 'Y', null, 'N', 'N', 0, null, '2025-05-15 17:38:25', '', '2025-07-22 14:49:40', 'USER00000004'); +INSERT INTO xitframework.tb_user (USER_ID, USER_ACNT, USER_NM, PASSWD, PASSWD_HINT, PASSWD_NSR, EMP_NO, GENDER, ZIP, ADDR, DADDR, AREA_NO, EML_ADDR, ORG_CD, USER_GROUP_ID, NSTT_CD, POS_NM, CRTFC_DN, USER_STATUS_CD, FXNO, TELNO, MBL_TELNO, BRDT, DEPT_CD, USE_YN, RSDNT_NO, PASSWD_INIT_YN, LOCK_YN, LOCK_CNT, LOCK_DTTM, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('USER00000005', 'worker', '업무담당자', 'Z6XYLOi+w3ukRN5nn08WSYg04spdJAvpCU4EOOd925g=', '', '', '', null, '14507', '경기도 부천시 원미구 계남로 81', '111', null, '', 'ORG002', 'GROUP_SYSTEM', 'NSTT001', '', null, 'INACTIVE', null, '', '010-4179-2158', null, '', 'Y', null, 'N', 'N', 0, null, '2025-05-19 13:59:31', 'SYSTEM', '2025-07-23 10:23:14', 'ADMIN'); +INSERT INTO xitframework.tb_user (USER_ID, USER_ACNT, USER_NM, PASSWD, PASSWD_HINT, PASSWD_NSR, EMP_NO, GENDER, ZIP, ADDR, DADDR, AREA_NO, EML_ADDR, ORG_CD, USER_GROUP_ID, NSTT_CD, POS_NM, CRTFC_DN, USER_STATUS_CD, FXNO, TELNO, MBL_TELNO, BRDT, DEPT_CD, USE_YN, RSDNT_NO, PASSWD_INIT_YN, LOCK_YN, LOCK_CNT, LOCK_DTTM, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('USER00000006', 'system', '시스템 관리자', '7nB/w2O5OvlSbFgYgAA6MRLI1GEeFFSNXXMwCcu2jl8=', null, null, '23r23r32', null, '14507', '경기도 부천시 원미구 계남로 81', '34t34t', null, '', 'ORG001', 'GROUP_APPROVER', 'NSTT001', '', null, 'ACTIVE', null, '', '', null, 'DEPT002', 'Y', null, 'Y', 'N', 0, '2025-07-08 14:07:23', '2025-05-21 12:49:17', 'SYSTEM', '2025-07-17 14:40:23', 'ADMIN'); +INSERT INTO xitframework.tb_user (USER_ID, USER_ACNT, USER_NM, PASSWD, PASSWD_HINT, PASSWD_NSR, EMP_NO, GENDER, ZIP, ADDR, DADDR, AREA_NO, EML_ADDR, ORG_CD, USER_GROUP_ID, NSTT_CD, POS_NM, CRTFC_DN, USER_STATUS_CD, FXNO, TELNO, MBL_TELNO, BRDT, DEPT_CD, USE_YN, RSDNT_NO, PASSWD_INIT_YN, LOCK_YN, LOCK_CNT, LOCK_DTTM, REG_DTTM, RGTR, MDFCN_DTTM, MDFR) VALUES ('USER00000051', 'admin1', '관리자', 'Z6XYLOi+w3ukRN5nn08WSYg04spdJAvpCU4EOOd925g=', null, null, '', null, '14507', '경기도 부천시 원미구 계남로 81', 'ㄷㄹㅈㄷㄹㅈㄹㅈㄹㅈㄷㄹㅈㄷ', null, '', '', 'GROUP_ADMIN', 'NSTT001', '', null, 'ACTIVE', null, '', '', null, '', 'Y', null, 'N', 'N', 0, null, '2025-05-28 10:31:16', 'ADMIN', '2025-07-21 13:25:58', 'ADMIN'); diff --git a/README.md b/README.md new file mode 100644 index 0000000..a0ab640 --- /dev/null +++ b/README.md @@ -0,0 +1,1612 @@ +# XIT Framework 개발 가이드 + +## 목차 +- [1. 프로젝트 개요](#1-프로젝트-개요) +- [2. 기술 스택 및 라이브러리 버전](#2-기술-스택-및-라이브러리-버전) + - [2.1 핵심 기술 스택](#tech-stack) + - [2.2 주요 라이브러리](#main-libraries) +- [3. 프로젝트 구조](#3-프로젝트-구조) + - [3.1 디렉토리 구조](#directory-structure) + - [3.2 패키지 구조](#package-structure) +- [4. 주요 설정 파일](#4-주요-설정-파일) + - [4.1 application.yml](#application-yml) + - [4.2 application-local.yml](#application-local-yml) + - [4.3 application-dev.yml](#application-dev-yml) + - [4.4 application-prd.yml](#application-prd-yml) + - [4.5 build.gradle](#build-gradle) + - [4.6 logback-spring.xml](#logback-spring-xml) + - [4.7 mybatis-config.xml](#mybatis-config-xml) + - [4.8 데이터베이스 스키마](#database-schema) +- [5. 주요 기능 및 사용법](#5-주요-기능-및-사용법) + - [5.1 프로젝트 구조 패턴](#project-structure-pattern) + - [5.2 사용자 관리 기능 예시](#user-management-example) + - [5.2.1 Controller](#controller) + - [5.2.2 Service](#service) + - [5.2.3 Mapper](#mapper) + - [5.3 로그인 기능](#login-function) + - [5.3.1 Controller](#login-controller) + - [5.4 공통 유틸리티](#common-utilities) + - [5.4.1 API 응답 유틸리티](#api-response-util) + - [5.5 레이아웃 구성](#layout-configuration) + - [5.6 보안 기능](#security-features) + - [5.6.1 XSS 필터](#xss-filter) + - [5.6.2 리퍼러 체크](#referer-check) + - [5.6.3 권한 관리](#permission-management) + - [5.6.4 사용자 > 그룹 > 역할 > 메뉴 구조](#user-group-role-menu-structure) + - [5.7 배치 작업 관리](#batch-management) +- [6. 개발 가이드라인](#6-개발-가이드라인) + - [6.1 코드 작성 규칙](#code-writing-rules) + - [6.2 디렉토리 구조 가이드](#directory-structure-guide) + - [6.3 UI 컴포넌트 가이드](#ui-component-guide) +- [7. 배포 가이드](#7-배포-가이드) + - [7.1 빌드 방법](#build-method) + - [7.1.1 WAR 파일 빌드](#war-file-build) + - [7.1.2 bootWar 파일 빌드](#bootwar-file-build) + - [7.2 프로필 설정](#profile-settings) + - [7.2.1 기본 프로필](#default-profile) + - [7.2.2 프로필 활성화 방법](#profile-activation) + - [7.3 외부 WAS를 이용한 배포](#external-was-deployment) + - [7.3.1 Tomcat에 WAR 파일 배포](#tomcat-war-deployment) + - [7.3.2 외부 WAS에서 프로필 설정](#external-was-profile-settings) + - [7.4 WAR로 배포 및 실행](#war-deployment-execution) + - [7.4.1 bootWar 파일 실행](#bootwar-execution) + - [7.4.2 외부 WAS에 배포하여 실행](#external-was-execution) + - [7.4.3 백그라운드 실행 (Linux/macOS)](#background-execution) + - [7.4.4 Windows 서비스로 등록](#windows-service-registration) + - [7.5 배포 환경 설정](#deployment-environment-settings) + - [7.6 배포 체크리스트](#deployment-checklist) +- [8. 참고 자료](#8-참고-자료) + +## 1. 프로젝트 개요 + +XIT Framework는 Spring Boot 기반의 웹 애플리케이션 프레임워크로, 전자정부 프레임워크를 확장하여 개발된 프로젝트입니다. 이 문서는 XIT Framework의 구조, 기술 스택, 주요 기능 및 사용법에 대한 종합적인 가이드를 제공합니다. + +## 2. 기술 스택 및 라이브러리 버전 + +

2.1 핵심 기술 스택

+ +| 기술 | 버전 | 설명 | +|------|------|------| +| Java | 개발: 1.8, 배포: 1.8 | 자바 개발 및 실행 환경 | +| Spring Boot | 2.7.18 | 스프링 기반 애플리케이션 개발 프레임워크 | +| 전자정부 프레임워크 | 4.3.0 | 한국 정부 표준 웹 개발 프레임워크 | +| Servlet | 3.1 | 웹 애플리케이션 표준 | +| Gradle | - | 빌드 및 의존성 관리 도구 | +| MariaDB | - | 관계형 데이터베이스 | + +

2.2 주요 라이브러리

+ +| 라이브러리 | 버전 | 설명 | +|------------|------|------| +| MyBatis | 2.3.1 | SQL 매핑 프레임워크 | +| Apache Tiles | 3.0.8 | 레이아웃 템플릿 엔진 | +| TOAST UI Grid | 4.19.2 | 자바스크립트 그리드 라이브러리 | +| Lombok | - | 자바 코드 생성 라이브러리 | +| Apache Commons Text | 1.10.0 | 텍스트 처리 유틸리티 | +| Apache POI | 5.3.0 | 엑셀 파일 처리 라이브러리 | + +## 3. 프로젝트 구조 + +

3.1 디렉토리 구조

+ +``` +xit-framework/ +├── DB-DDL/ # 데이터베이스 스크립트 +│ └── maria/ # MariaDB 스크립트 +│ ├── ddl/ # 테이블 정의 스크립트 +│ └── dml/ # 샘플 데이터 스크립트 +├── src/ +│ ├── main/ +│ │ ├── java/ # 자바 소스 코드 +│ │ │ ├── egovframework/ # 전자정부 프레임워크 확장 코드 +│ │ │ └── go/kr/project/ # 프로젝트 소스 코드 +│ │ ├── resources/ # 리소스 파일 +│ │ │ ├── mybatis/ # MyBatis 설정 및 매퍼 +│ │ │ └── application.yml # 애플리케이션 설정 파일 +│ │ └── webapp/ # 웹 리소스 +│ │ ├── resources/ # 정적 리소스 (CSS, JS, 이미지 등) +│ │ └── WEB-INF/views/ # JSP 뷰 파일 +│ └── test/ # 테스트 코드 +└── build.gradle # Gradle 빌드 스크립트 +``` + +

3.2 패키지 구조

+ +``` +go.kr.project/ +├── common/ # 공통 컴포넌트 +├── login/ # 로그인 관련 기능 +│ ├── controller/ # 컨트롤러 클래스 +│ ├── mapper/ # MyBatis 매퍼 인터페이스 +│ ├── model/ # 데이터 모델 클래스 +│ └── service/ # 서비스 클래스 +└── system/ # 시스템 관리 기능 + ├── auth/ # 권한 관리 + ├── code/ # 코드 관리 + ├── group/ # 그룹 관리 + ├── menu/ # 메뉴 관리 + ├── role/ # 역할 관리 + └── user/ # 사용자 관리 + ├── controller/ # 컨트롤러 클래스 + ├── mapper/ # MyBatis 매퍼 인터페이스 + ├── model/ # 데이터 모델 클래스 + └── service/ # 서비스 클래스 + +egovframework/ +├── config/ # 프레임워크 설정 +├── exception/ # 예외 처리 +├── filter/ # 필터 +├── interceptor/ # 인터셉터 +└── util/ # 유틸리티 클래스 +``` + +## 4. 주요 설정 파일 + +

4.1 application.yml

+ +애플리케이션의 공통 설정을 정의하는 파일입니다. + +```yaml +# 공통 설정 +spring: + profiles: + active: local + application: + name: xit-framework + mvc: + view: + prefix: /WEB-INF/views/ + suffix: .jsp + +# 데이터베이스 타입 설정 +Globals: + DbType: maria + +# MyBatis 설정 +mybatis: + config-location: classpath:mybatis/mybatis-config.xml + mapper-locations: classpath:mybatis/mapper/**/*_${Globals.DbType}.xml + type-aliases-package: go.kr.project.**.model,egovframework.**.model + +# 서버 설정 +server: + port: 8080 + servlet: + context-path: / + encoding: + charset: UTF-8 + multipart: + max-file-size: 10MB + max-request-size: 50MB + +# Swagger UI 설정 +springdoc: + api-docs: + enabled: true + swagger-ui: + enabled: true + path: /swagger-ui.html + operations-sorter: method + tags-sorter: alpha + display-request-duration: true + doc-expansion: none + disable-swagger-default-url: true + filter: true + supportedSubmitMethods: [get, post] + packages-to-scan: go.kr.project + paths-to-match: /** + default-consumes-media-type: application/json + default-produces-media-type: application/json + model-converters: + pageable-converter: + enabled: true + +# 로그인 설정 +login: + url: /login/login.do + lock: + count: 5 + DefaultPassword: xitpassword + allowMultipleLogin: true + +# 인터셉터 설정 +interceptor: + interceptorExclude: + - /html/*.html + - /login/** + - / + - /.well-known/** + - /common/** + - /resources/** + - /css/** + - /img/** + - /js/** + - /xit/** + - /plugins/** + - /font/** + - /error/** + - /favicon.ico + - /swagger-ui/** + - /v3/api-docs/** + refererExclude: + - /** + - /html/*.html + - /login/** + - / + - /main.do + - /error/** + - /common/** + - /swagger-ui/** + - /v3/api-docs/** +``` + +

4.2 application-local.yml

+ +로컬 개발 환경에 대한 설정을 정의하는 파일입니다. + +```yaml +# 로컬 프로필 설정 +spring: + config: + activate: + on-profile: local + devtools: + livereload: + enabled: true + restart: + enabled: true + datasource: + driver-class-name: org.mariadb.jdbc.Driver + url: jdbc:mariadb://211.119.124.9:4407/xitframework?characterEncoding=UTF-8&serverTimezone=Asia/Seoul + username: egov + password: xit1807 + hikari: + maximum-pool-size: 10 + minimum-idle: 5 + connection-timeout: 30000 + idle-timeout: 600000 + max-lifetime: 1800000 + validation-timeout: 60000 + +# 서버 설정 +server: + servlet: + session: + timeout: 30m + tracking-modes: cookie + persistent: false + +# 로깅 설정 +logging: + config: classpath:logback-spring.xml + file: + path: d:/data/xit-framework/logs + name: xit-framework + logback: + rollingpolicy: + max-file-size: 10MB + max-history: 30 + level: + go.kr.project: DEBUG + egovframework: DEBUG + org.mybatis: DEBUG + org.springframework: INFO + +# 파일 업로드 설정 +file: + upload: + path: D:\xit-framework-file + max-size: 10 + max-total-size: 50 + max-files: 10 + allowed-extensions: hwp,jpg,jpeg,png,gif,pdf,doc,docx,xls,xlsx,ppt,pptx,txt,zip + real-file-delete: true + sub-dirs: + bbs-notice: bbs/notice + bbs-post: bbs/post + html-editor: common/html_editor +``` + +

4.3 application-dev.yml

+ +개발 환경에 대한 설정을 정의하는 파일입니다. + +```yaml +# 개발 프로필 설정 +spring: + config: + activate: + on-profile: dev + datasource: + driver-class-name: org.mariadb.jdbc.Driver + url: jdbc:mariadb://211.119.124.9:4407/xitframework?characterEncoding=UTF-8&serverTimezone=Asia/Seoul + username: egov + password: xit1807 + hikari: + maximum-pool-size: 10 + minimum-idle: 5 + connection-timeout: 30000 + idle-timeout: 600000 + max-lifetime: 1800000 + validation-timeout: 60000 + +# 서버 설정 +server: + servlet: + session: + timeout: 30m + +# 로깅 설정 +logging: + config: classpath:logback-spring.xml + file: + path: d:/data/xit-framework/logs + name: xit-framework + logback: + rollingpolicy: + max-file-size: 10MB + max-history: 30 + level: + go.kr.project: DEBUG + egovframework: DEBUG + org.mybatis: DEBUG + org.springframework: INFO + +# 파일 업로드 설정 +file: + upload: + path: D:\xit-framework-file + max-size: 10 + max-total-size: 50 + max-files: 10 + allowed-extensions: hwp,jpg,jpeg,png,gif,pdf,doc,docx,xls,xlsx,ppt,pptx,txt,zip + real-file-delete: true + sub-dirs: + bbs-notice: bbs/notice + bbs-post: bbs/post + html-editor: common/html_editor +``` + +

4.4 application-prd.yml

+ +운영 환경에 대한 설정을 정의하는 파일입니다. + +```yaml +# 운영 프로필 설정 +spring: + config: + activate: + on-profile: prd + datasource: + driver-class-name: org.mariadb.jdbc.Driver + url: jdbc:mariadb://211.119.124.9:4407/xitframework?characterEncoding=UTF-8&serverTimezone=Asia/Seoul + username: egov + password: xit1807 + hikari: + maximum-pool-size: 10 + minimum-idle: 5 + connection-timeout: 30000 + idle-timeout: 600000 + max-lifetime: 1800000 + validation-timeout: 60000 + +# 서버 설정 +server: + servlet: + session: + timeout: 60m + +# 로깅 설정 +logging: + config: classpath:logback-spring.xml + file: + path: /data/logs/xit-framework-file + name: xit-framework + logback: + rollingpolicy: + max-file-size: 10MB + max-history: 30 + level: + go.kr.project: WARN + egovframework: WARN + org.mybatis: WARN + org.springframework: WARN + +# 파일 업로드 설정 +file: + upload: + path: /data/upload/xit-framework-file + max-size: 10 + max-total-size: 50 + max-files: 10 + allowed-extensions: hwp,jpg,jpeg,png,gif,pdf,doc,docx,xls,xlsx,ppt,pptx,txt,zip + real-file-delete: true + sub-dirs: + bbs-notice: bbs/notice + bbs-post: bbs/post + html-editor: common/html_editor +``` + +

4.5 build.gradle

+ +프로젝트의 빌드 및 의존성을 정의하는 파일입니다. + +```gradle +plugins { + id 'org.springframework.boot' version '2.7.18' + id 'io.spring.dependency-management' version '1.0.15.RELEASE' + id 'java' + id 'war' +} + +group = 'go.kr.project' +version = '0.0.1-SNAPSHOT' + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + +repositories { + mavenCentral() + maven { url 'https://maven.egovframe.go.kr/maven/' } +} + +ext { + tomcatVersion = '9.0.78' + tilesVersion = '3.0.8' + mybatisVersion = '2.3.1' + commonsTextVersion = '1.10.0' + egovFrameVersion = '4.3.0' +} + +dependencies { + // 스프링 부트 의존성 + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.springframework.boot:spring-boot-starter-aop' + implementation 'org.springframework.boot:spring-boot-starter-jdbc' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + + // 전자정부 프레임워크 의존성 + implementation("org.egovframe.rte:org.egovframe.rte.fdl.cmmn:${egovFrameVersion}") { + exclude group: 'org.apache.logging.log4j', module: 'log4j-slf4j-impl' + } + implementation("org.egovframe.rte:org.egovframe.rte.ptl.mvc:${egovFrameVersion}") { + exclude group: 'org.apache.logging.log4j', module: 'log4j-slf4j-impl' + } + + // 로깅 의존성 + implementation 'ch.qos.logback:logback-classic' + implementation 'ch.qos.logback:logback-core' + implementation 'org.slf4j:slf4j-api' + + // 서블릿 & JSP 관련 의존성 + implementation 'javax.servlet:jstl' + + // 톰캣 설정 + implementation "org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}" + implementation "org.apache.tomcat.embed:tomcat-embed-el:${tomcatVersion}" + implementation "org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}" + + // 타일즈 의존성 + implementation "org.apache.tiles:tiles-jsp:${tilesVersion}" + implementation "org.apache.tiles:tiles-core:${tilesVersion}" + implementation "org.apache.tiles:tiles-api:${tilesVersion}" + implementation "org.apache.tiles:tiles-servlet:${tilesVersion}" + implementation "org.apache.tiles:tiles-el:${tilesVersion}" + + // 데이터 액세스 의존성 + implementation "org.mybatis.spring.boot:mybatis-spring-boot-starter:${mybatisVersion}" + implementation 'org.mariadb.jdbc:mariadb-java-client' + + // 유틸리티 의존성 + implementation "org.apache.commons:commons-text:${commonsTextVersion}" + + // EXCEL + implementation 'org.apache.poi:poi:5.3.0' + implementation 'org.apache.poi:poi-ooxml:5.3.0' + + // Swagger UI + implementation 'org.springdoc:springdoc-openapi-ui:1.7.0' + implementation 'org.springdoc:springdoc-openapi-webmvc-core:1.7.0' + + // 개발 도구 의존성 + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + developmentOnly 'org.springframework.boot:spring-boot-devtools' + + // 테스트 의존성 + testImplementation 'org.springframework.boot:spring-boot-starter-test' + + // 배포 관련 의존성 + providedCompile "org.apache.tomcat:tomcat-servlet-api:${tomcatVersion}" + providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat' +} + +tasks.named('test') { + useJUnitPlatform() +} + +war { + archiveFileName = 'xit-framework.war' +} + +bootWar { + archiveFileName = 'xit-framework-boot.war' +} +``` + +

4.6 logback-spring.xml

+ +로깅 설정을 정의하는 파일입니다. + +```xml + + + + + + + + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} %highlight(%-5level) [%thread] %cyan(%logger{36}) - %msg%n + + + + + + ${LOG_PATH}/${LOG_FILE} + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + ${LOG_PATH}/${LOG_FILE}.%d{yyyy-MM-dd}.%i.log + + ${MAX_FILE_SIZE} + + ${MAX_HISTORY} + + true + + + + + + 512 + 0 + false + false + + + + + + + + + + + + + + + + + + + + + + + + +``` + +

4.7 mybatis-config.xml

+ +MyBatis 설정을 정의하는 파일입니다. + +```xml + + + + + + + + + + + + + + + + + + + + +``` + +

4.8 데이터베이스 스키마

+ +주요 테이블 구조는 `DB-DDL/maria/all_ddl.sql`에 정의되어 있습니다: + +- **tb_user**: 사용자 정보 +- **tb_group**: 그룹 정보 +- **tb_role**: 역할 정보 +- **tb_menu**: 메뉴 정보 +- **tb_group_role**: 그룹-역할 매핑 +- **tb_role_menu**: 역할-메뉴 매핑 +- **tb_code_group**: 코드 그룹 +- **tb_code_detail**: 코드 상세 +- **tb_bbs_config**: 게시판 설정 +- **tb_bbs_post**: 게시물 +- **tb_bbs_file**: 게시판 파일 +- **tb_user_session**: 사용자 세션 +- **tb_login_log**: 로그인 로그 + +MyBatis 매퍼 파일은 `src/main/resources/mybatis/mapper/` 디렉토리에 모듈별로 구성되어 있습니다. + +## 5. 주요 기능 및 사용법 + +

5.1 프로젝트 구조 패턴

+ +XIT Framework는 MVC(Model-View-Controller) 패턴을 기반으로 하며, 각 기능별로 다음과 같은 계층 구조를 가집니다: + +1. **Controller**: 클라이언트 요청을 처리하고 응답을 반환합니다. +2. **Service**: 비즈니스 로직을 처리합니다. +3. **Mapper**: 데이터베이스 접근을 담당합니다. +4. **Model**: 데이터 구조를 정의합니다. + +

5.2 사용자 관리 기능 예시

+ +사용자 관리 기능은 다음과 같은 구조로 구현되어 있습니다: + +

5.2.1 Controller

+ +```java +@Controller +@RequestMapping("/system/user") +public class UserController { + + @Resource(name = "UserService") + private UserService userService; + + // 사용자 목록 페이지 + @RequestMapping("/list.do") + public String userList(SystemUserVO paramVO, Model model) { + return "system/user/list.base"; + } + + // 사용자 목록 조회 AJAX + @PostMapping("/list.ajax") + public ResponseEntity getUserListAjax(@ModelAttribute SystemUserVO paramVO) { + int totalCount = userService.selectUserListTotalCount(paramVO); + paramVO.setTotalCount(totalCount); + paramVO.setPagingYn("Y"); + + List userList = userService.selectUserList(paramVO); + return ApiResponseUtil.successWithGrid(userList, paramVO); + } + + // 사용자 등록 페이지 + @GetMapping("/register.do") + public String registerUser(Model model) { + model.addAttribute("user", new SystemUserVO()); + return "system/user/form.base"; + } + + // 사용자 등록 처리 AJAX + @PostMapping("/register.ajax") + public ResponseEntity registerUserAjax(@ModelAttribute SystemUserVO userVO) { + // 사용자 ID 자동 생성 + String userId = userService.generateUserId(); + userVO.setUserId(userId); + + // 기본 비밀번호 설정 + String defaultPassword = env.getProperty("Globals.DefaultPassword", "xitpassword"); + userVO.setPasswd(EgovFileScrty.encryptPassword(defaultPassword, userId)); + + int result = userService.insertUser(userVO); + + if (result > 0) { + return ApiResponseUtil.success("사용자가 성공적으로 등록되었습니다."); + } else { + return ApiResponseUtil.error("사용자 등록에 실패했습니다."); + } + } +} +``` + +

5.2.2 Service

+ +```java +@Service("UserService") +public interface UserService { + + // 사용자 정보 조회 + SystemUserVO selectUser(String userId); + + // 사용자 목록 조회 + List selectUserList(SystemUserVO vo); + + // 사용자 목록 총 개수 조회 + int selectUserListTotalCount(SystemUserVO vo); + + // 사용자 ID 생성 + String generateUserId(); + + // 사용자 등록 + int insertUser(SystemUserVO vo); + + // 사용자 수정 + int updateUser(SystemUserVO vo); +} +``` + +

5.2.3 Mapper

+ +```java +@Mapper +public interface UserMapper { + + // 사용자 정보 조회 + SystemUserVO selectUser(String userId); + + // 사용자 목록 조회 + List selectUserList(SystemUserVO vo); + + // 사용자 목록 총 개수 조회 + int selectUserListTotalCount(SystemUserVO vo); + + // 사용자 ID 생성 + String generateUserId(); + + // 사용자 등록 + int insertUser(SystemUserVO vo); + + // 사용자 수정 + int updateUser(SystemUserVO vo); +} +``` + +

5.3 로그인 기능

+ +로그인 기능은 다음과 같은 구조로 구현되어 있습니다: + +

5.3.1 Controller

+ +```java +@Controller +@RequestMapping("/login") +public class LoginController { + + @Autowired + private LoginService loginService; + + // 로그인 페이지 + @RequestMapping(value = "/login.do", method = RequestMethod.GET) + public String loginPage(Model model, HttpServletRequest request) { + // 쿠키에서 저장된 아이디 가져오기 + Cookie[] cookies = request.getCookies(); + String savedUserId = ""; + boolean isSaveId = false; + + if (cookies != null) { + for (Cookie cookie : cookies) { + if ("savedUserId".equals(cookie.getName())) { + savedUserId = cookie.getValue(); + isSaveId = true; + break; + } + } + } + + model.addAttribute("savedUserId", savedUserId); + model.addAttribute("isSaveId", isSaveId); + + return "login/login.login"; + } + + // 로그인 처리 AJAX + @PostMapping("/login.ajax") + public ResponseEntity loginAjax( + @RequestParam("userAcnt") String userAcnt, + @RequestParam("passwd") String passwd, + @RequestParam(value = "saveId", required = false) String saveId, + HttpServletRequest request, + HttpServletResponse response) { + + try { + // 로그인 처리 + SessionVO sessionVO = loginService.login(userAcnt, passwd, request, response); + + // 아이디 저장 처리 + if ("Y".equals(saveId)) { + Cookie cookie = new Cookie("savedUserId", userAcnt); + cookie.setMaxAge(60 * 60 * 24 * 7); + cookie.setPath("/"); + response.addCookie(cookie); + } else { + Cookie cookie = new Cookie("savedUserId", ""); + cookie.setMaxAge(0); + cookie.setPath("/"); + response.addCookie(cookie); + } + + if (sessionVO != null) { + // 로그인 성공 + Map data = new HashMap<>(); + data.put("redirectUrl", "/main.do"); + return ApiResponseUtil.success(data, "로그인에 성공하였습니다."); + } else { + // 로그인 실패 + return ApiResponseUtil.error("아이디 또는 비밀번호가 일치하지 않습니다."); + } + } catch (Exception e) { + return ApiResponseUtil.error("로그인 처리 중 오류가 발생했습니다."); + } + } + + // 로그아웃 처리 + @GetMapping("/logout.do") + public String logout(HttpServletRequest request, HttpServletResponse response) { + try { + loginService.logout(request, response); + } catch (Exception e) { + log.error("로그아웃 처리 중 오류 발생", e); + } + return "redirect:" + loginProperties.getUrl(); + } +} +``` + +

5.4 공통 유틸리티

+ +

5.4.1 API 응답 유틸리티

+ +```java +public class ApiResponseUtil { + + // 성공 응답 + public static ResponseEntity success(Object data, String message) { + ApiResponseEntity response = new ApiResponseEntity(); + response.setResult(true); + response.setMessage(message); + response.setData(data); + return ResponseEntity.ok(response); + } + + // 성공 응답 (메시지만) + public static ResponseEntity success(String message) { + return success(null, message); + } + + // 그리드 데이터 응답 + public static ResponseEntity successWithGrid(List data, PagingVO pagingVO) { + Map gridData = new HashMap<>(); + gridData.put("contents", data); + gridData.put("pagination", pagingVO); + + return success(gridData, "조회가 완료되었습니다."); + } + + // 오류 응답 + public static ResponseEntity error(String message) { + ApiResponseEntity response = new ApiResponseEntity(); + response.setResult(false); + response.setMessage(message); + return ResponseEntity.ok(response); + } +} +``` + +

5.5 레이아웃 구성

+ +XIT Framework는 Apache Tiles를 사용하여 레이아웃을 구성합니다. 기본 레이아웃은 `src/main/webapp/WEB-INF/views/layouts/base/default.jsp`에 정의되어 있습니다. + +```jsp +<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %> + + + + + + + + + + + + XIT - Framework + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + " /> + + + + + + + +
+ + +
+ + + + + +``` + +### 레이아웃 구성 요소 + +1. **Sidebar (사이드바)** + - `menu_header`: 메뉴 헤더 영역 + - `menu`: 메뉴 네비게이션 영역 + +2. **Main (메인 영역)** + - `main_header`: 메인 헤더 영역 + - `main`: 실제 콘텐츠 영역 + +### 주요 CSS/JS 라이브러리 + +- **Bootstrap**: 반응형 UI 프레임워크 +- **Material Design Icons**: 아이콘 라이브러리 +- **FontAwesome**: 아이콘 라이브러리 +- **DataTables**: 데이터 테이블 플러그인 +- **TOAST UI Grid**: 고성능 그리드 컴포넌트 +- **SimpleBar**: 커스텀 스크롤바 +- **Crypto-JS**: 암호화 라이브러리 +- **InputMask**: 입력 마스킹 +- **Moment.js**: 날짜/시간 처리 + +### XIT 커스텀 파일 + +- **xit-common.css/js**: 공통 스타일 및 유틸리티 +- **xit-tui-grid.css/js**: TOAST UI Grid 커스터마이징 +- **xit-validation.js**: 폼 유효성 검사 +- **menu-path.js**: 메뉴 경로 처리 +- **common_util.js**: 공통 유틸리티 함수 +- **datatables_util.js**: DataTables 유틸리티 +- **xit-multi-fileupload.css**: 다중 파일 업로드 스타일 + +### 캐시 제어 + +브라우저 캐시를 방지하기 위해 메타 태그와 HTTP 헤더를 설정하여 항상 최신 콘텐츠를 제공합니다. + +

5.6 보안 기능

+ +XIT Framework는 다양한 보안 기능을 제공하여 애플리케이션의 안전성을 강화합니다. + +

5.6.1 XSS 필터

+ +XSS(Cross-Site Scripting) 공격을 방지하기 위한 필터를 제공합니다. 이 필터는 모든 요청 파라미터를 검사하고 잠재적인 XSS 공격 코드를 제거합니다. + +```java +// XSS 필터 설정 클래스 +@Configuration +public class XssFilterConfig { + + private final XssUtil xssUtil; + + public XssFilterConfig(XssUtil xssUtil) { + this.xssUtil = xssUtil; + } + + @Bean + public FilterRegistrationBean xssFilterRegistration() { + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + registrationBean.setFilter(new XssFilter(xssUtil)); + registrationBean.addUrlPatterns("/*"); // 모든 URL에 적용 + registrationBean.setName("xssFilter"); + registrationBean.setOrder(1); // 필터 순서 (낮은 숫자가 먼저 실행) + return registrationBean; + } +} +``` + +XSS 필터는 다음과 같은 기능을 제공합니다: + +1. **HTML 태그 이스케이프**: 일반 텍스트 필드에서 HTML 태그를 이스케이프 처리합니다. +2. **HTML 에디터 내용 정화**: HTML 에디터 내용에서는 허용된 태그만 남기고 위험한 스크립트를 제거합니다. +3. **파일명 정화**: 파일명에서 위험한 문자를 제거합니다. +4. **보안 헤더 설정**: XSS 방지를 위한 HTTP 헤더를 설정합니다. + +```java +// XSS 필터 구현 +public class XssFilter extends OncePerRequestFilter { + + private final XssUtil xssUtil; + + public XssFilter(XssUtil xssUtil) { + this.xssUtil = xssUtil; + } + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + + // XSS 방지를 위한 헤더 설정 + // X-XSS-Protection: 브라우저의 XSS 필터를 켜고, 공격이 감지되면 페이지를 차단. + response.setHeader("X-XSS-Protection", "1; mode=block"); + // X-Content-Type-Options: 브라우저가 서버가 지정한 MIME 타입만 사용하게 하여, 잘못된 타입 해석으로 인한 보안 문제를 방지. + response.setHeader("X-Content-Type-Options", "nosniff"); + + // 요청을 래핑하여 XSS 필터링 적용 + XssRequestWrapper xssRequestWrapper = new XssRequestWrapper(request, xssUtil); + + // 필터 체인 실행 + filterChain.doFilter(xssRequestWrapper, response); + } +} +``` + +

5.6.2 리퍼러 체크

+ +리퍼러(Referer) 헤더를 검사하여 직접 URL을 입력하거나 외부 사이트에서의 접근을 제한합니다. 이를 통해 CSRF(Cross-Site Request Forgery) 공격을 방지하고 애플리케이션의 보안을 강화합니다. + +```java +// AuthInterceptor 클래스의 preHandle 메서드 내부 +@Override +public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + String requestURI = request.getRequestURI(); + + // 리퍼러 체크 로직 + if (!isRefererCheckExcluded(requestURI)) { + String referer = request.getHeader("Referer"); + if (referer == null || referer.isEmpty()) { + // Referer 헤더가 없는 경우 (직접 URL 입력 또는 북마크 등) + log.warn("Referer 헤더 없음: {}", requestURI); + + // AJAX 요청인 경우 JSON 응답 반환 + if (HttpServletUtil.isAjaxRequest(request) || HttpServletUtil.isRealAjaxRequest(request)) { + handleRefererMissing(response); + return false; + } + + // 일반 요청인 경우 로그인 페이지로 리다이렉트 + response.setContentType("text/html; charset=UTF-8"); + response.getWriter().write(""); + return false; + } + } + + // 나머지 로직... + return true; +} +``` + +리퍼러 체크는 다음과 같은 특징을 가집니다: + +1. **예외 URL 설정**: 로그인 페이지, 정적 리소스 등 일부 URL은 리퍼러 체크에서 제외됩니다. +2. **AJAX 요청 처리**: AJAX 요청의 경우 JSON 형식으로 오류 응답을 반환합니다. +3. **일반 요청 처리**: 일반 요청의 경우 경고 메시지와 함께 로그인 페이지로 리다이렉트합니다. + +

5.6.3 권한 관리

+ +사용자의 권한에 따라 접근 가능한 기능과 메뉴를 제한하는 권한 관리 시스템을 제공합니다. 권한 관리는 인터셉터를 통해 구현되며, 사용자의 세션 정보와 요청 URL을 기반으로 접근 권한을 검사합니다. + +```java +// 권한 관리 인터셉터 +@Slf4j +@RequiredArgsConstructor +public class AuthInterceptor implements HandlerInterceptor { + + private final LoginService loginService; + private final AntPathMatcher pathMatcher = new AntPathMatcher(); + private final XssUtil xssUtil = new XssUtil(); + + @Autowired + private InterceptorProperties interceptorProperties; + + @Autowired + private LoginProperties loginProperties; + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + String requestURI = request.getRequestURI(); + + // Referer 헤더 검사 로직... + + try { + // 세션 정보 조회 + SessionVO sessionVO = loginService.getSessionInfo(request); + + // 세션이 없거나 로그인 상태가 아닌 경우 + if (sessionVO == null || !sessionVO.isLogin()) { + // 방문자 권한 확인 + if (sessionVO != null && sessionVO.isVisitor()) { + // 방문자 권한으로 접근 가능한지 확인 + if (hasAccess(sessionVO, requestURI)) { + return true; + } + } + + // 세션 만료 처리... + return false; + } + + // 로그인 상태인 경우 접근 권한 확인 + if (hasAccess(sessionVO, requestURI)) { + return true; + } + + // 접근 권한이 없는 경우 + log.warn("접근 권한 없음: {} - {}", sessionVO.getUser().getUserAcnt(), requestURI); + response.sendError(HttpServletResponse.SC_FORBIDDEN, "접근 권한이 없습니다."); + return false; + + } catch (Exception e) { + // 예외 처리... + return false; + } + } + + // 접근 권한 확인 메서드 + private boolean hasAccess(SessionVO sessionVO, String requestURI) { + List menus = flattenMenuTree(sessionVO.getMenus()); + if (menus.isEmpty()) { + return false; + } + + // 메뉴의 URL 패턴과 일치하는지 확인 + for (MenuVO menu : menus) { + if (menu.getUrlPattern() != null && !menu.getUrlPattern().isEmpty()) { + String[] patterns = xssUtil.unescape(menu.getUrlPattern()).split(","); + for (String pattern : patterns) { + if (pathMatcher.match(pattern.trim(), requestURI)) { + return true; + } + } + } + } + + return false; + } +} +``` + +

5.6.4 사용자 > 그룹 > 역할 > 메뉴 구조

+ +XIT Framework는 사용자, 그룹, 역할, 메뉴를 연결하는 권한 관리 구조를 제공합니다. 이 구조는 데이터베이스 테이블 간의 관계를 통해 구현되며, 접근 제어가 가능합니다. + +1. **데이터 구조 개요** + + XIT Framework의 권한 관리 시스템은 다음과 같은 주요 엔티티로 구성됩니다: + + - **사용자(User)**: 시스템을 사용하는 개인 계정 + - **그룹(Group)**: 사용자들의 집합 + - **역할(Role)**: 특정 기능에 대한 권한 집합 + - **메뉴(Menu)**: 시스템의 기능 단위와 UI 구성 요소 + - **URL 패턴**: 각 메뉴에 연결된 실제 접근 경로 + +2. **엔티티 간 관계** + + - **사용자-그룹 관계**: 사용자는 하나의 그룹에 소속됩니다. 그룹은 여러 사용자를 포함할 수 있습니다. (N:1 관계) + - **그룹-역할 관계**: 그룹은 여러 역할을 가질 수 있으며, 역할은 여러 그룹에 할당될 수 있습니다. (N:M 관계) + - **역할-메뉴 관계**: 역할은 여러 메뉴에 대한 접근 권한을 가지며, 메뉴는 여러 역할에 의해 접근될 수 있습니다. (N:M 관계) + - **메뉴-URL 패턴 관계**: 메뉴는 하나 이상의 URL 패턴과 연결됩니다. (1:N 관계) + +3. **데이터베이스 테이블 구조** + + - **TB_USER**: 사용자 정보 저장 (USER_ID, USER_ACNT, USER_NM, PASSWD, GRP_ID 등) + - **TB_GROUP**: 그룹 정보 저장 (GRP_ID, GRP_NM, GRP_DC 등) + - **TB_ROLE**: 역할 정보 저장 (ROLE_ID, ROLE_NM, ROLE_DC 등) + - **TB_GROUP_ROLE**: 그룹-역할 매핑 정보 (GRP_ID, ROLE_ID) + - **TB_MENU**: 메뉴 정보 저장 (MENU_ID, MENU_NM, MENU_URL, URL_PATTERN, UPPER_MENU_ID 등) + - **TB_ROLE_MENU**: 역할-메뉴 매핑 정보 (ROLE_ID, MENU_ID) + +4. **권한 확인 프로세스** + + 사용자가 특정 URL에 접근하려고 할 때, 시스템은 다음과 같은 과정으로 권한을 확인합니다: + + 1. 사용자 식별: 로그인한 사용자의 USER_ID 확인 + 2. 그룹 확인: 사용자가 속한 그룹(GRP_ID) 조회 + 3. 역할 확인: 그룹에 할당된 역할(ROLE_ID) 목록 조회 + 4. 메뉴 확인: 역할에 연결된 메뉴(MENU_ID) 목록 조회 + 5. URL 패턴 확인: 메뉴에 연결된 URL 패턴과 요청 URL 비교 + 6. 접근 허용/거부: 일치하는 URL 패턴이 있으면 접근 허용, 없으면 거부 + +5. **권한 관리의 장점** + + 이러한 계층적 권한 구조는 다음과 같은 이점을 제공합니다: + + - **효율적인 권한 관리**: 그룹과 역할을 통해 다수의 사용자에게 일괄적으로 권한 부여 가능 + - **세밀한 접근 제어**: URL 패턴 기반으로 세밀한 접근 제어 가능 + - **유연한 권한 설계**: 사용자-그룹-역할-메뉴의 다단계 구조로 다양한 권한 정책 구현 가능 + - **동적 메뉴 구성**: 사용자의 권한에 따라 UI 메뉴를 동적으로 구성 가능 + - **관리 용이성**: 관리자 화면에서 그룹, 역할, 메뉴, 권한을 통합적으로 관리 가능 + +

5.7 배치 작업 관리

+ +XIT Framework는 Quartz 기반의 배치 스케줄링 시스템을 내장하고 있습니다. 배치 작업은 등록, 실행, 일시정지, 재개, 삭제, 실행 이력 및 로그 관리 등 다양한 기능을 제공합니다. + +### 5.7.1 전체 구조 + +- **Controller**: `BatchJobController` - 배치 작업의 웹/REST API 제공 +- **Service**: `BatchJobService` - 배치 작업의 비즈니스 로직 처리 +- **Job**: `SampleBatchJob`, `SampleBatchJob2`, `SampleBatchJob3` 등 - 실제 실행되는 배치 잡 구현체 +- **Config**: `QuartzConfig`, `BatchJobInitializer`, `QuartzJobListener`, `QuartzListenerConfig` - 스케줄러 및 리스너 설정, 초기화 +- **Mapper**: `BatchJobMapper` - 배치 작업/실행/로그 DB 연동 +- **Model**: `BatchJobInfoVO`, `BatchJobExecutionVO`, `BatchJobLogVO` - 배치 정보/실행/로그 VO +- **Util**: `BatchJobLogUtil`, `ServerInfoUtil` - 배치 로그, 서버 정보 등 유틸리티 + +### 5.7.2 주요 기능 및 API + +- **배치 작업 목록 조회**: `/batch/list.do`, `/batch/list.ajax` +- **배치 작업 즉시 실행**: `/batch/trigger.ajax` (POST) +- **배치 작업 일시정지/재개/삭제**: `/batch/pause.ajax`, `/batch/resume.ajax`, `/batch/delete.ajax` (POST) +- **실행 이력/로그 조회**: `/batch/execution.do`, `/batch/execution.ajax`, `/batch/log.do`, `/batch/log.ajax` +- **잡 등록/수정/삭제**: `/batch/register.ajax` 등 + +#### 예시: 배치 작업 즉시 실행 +```http +POST /batch/trigger.ajax +Content-Type: application/x-www-form-urlencoded + +jobId=...&jobName=SampleBatchJob&jobGroup=DEFAULT +``` + +### 5.7.3 배치 잡 예시 + +```java +@Component +@DisallowConcurrentExecution +public class SampleBatchJob implements Job { + @Override + public void execute(JobExecutionContext context) throws JobExecutionException { + // 실제 배치 로직 구현 + } +} +``` + +### 5.7.4 DB 연동 및 로그 관리 + +- **잡 정보/실행/로그**는 각각 TB_BATCH_JOB_INFO, TB_BATCH_JOB_EXECUTION, TB_BATCH_JOB_LOG 테이블에 저장 +- `BatchJobMapper`를 통해 CRUD 및 이력/로그 관리 +- `BatchJobLogUtil`로 실행 중 로그를 DB에 저장 가능 + +### 5.7.5 배치 스케줄러 및 리스너 설정 + +- `QuartzConfig`, `QuartzListenerConfig`에서 Quartz 스케줄러 및 JobListener 등록 +- `BatchJobInitializer`에서 DB에 등록된 잡을 애플리케이션 시작 시 자동 등록 + +### 5.7.6 확장 및 커스터마이징 + +- 새로운 배치 잡은 `Job` 인터페이스 구현 후 등록 +- 잡 등록/수정/삭제, 스케줄 변경 등은 Controller/Service/DB를 통해 관리 +- 실행 이력, 평균 소요시간, 최근 실패 등 다양한 통계 제공 + +### 5.7.7 참고 VO 구조 + +- **BatchJobInfoVO**: 잡ID, 이름, 그룹, 클래스, 크론, 상태, 설명 등 +- **BatchJobExecutionVO**: 실행ID, 잡이름/그룹, 시작/종료시간, 상태, 종료코드/메시지, 서버정보 등 +- **BatchJobLogVO**: 로그ID, 실행ID, 로그레벨, 메시지, 시간 등 + +## 6. 개발 가이드라인 + +

6.1 코드 작성 규칙

+ +1. **Java 개발 및 수정** + - `src/main/java/go/kr/project/system` 하위에 있는 프로그램을 참조하여 구조, 패턴, 스타일을 사용합니다. + - 클래스, 메소드, 변수에 대한 주석을 작성합니다. + +2. **JSP 개발 및 수정** + - `src/main/webapp/WEB-INF/views/system` 하위에 있는 프로그램을 참조하여 구조, 패턴, 스타일을 사용합니다. + - JSP 페이지에서 태그의 id 속성은 중복되지 않도록 합니다. + - 모든 URL은 `` 태그로 감싸줍니다. + +3. **데이터베이스 관련** + - 신규로 생성되는 테이블은 `DB-DDL/maria/ddl.sql`에 등록합니다. + - `ON DELETE SET NULL`, `ON DELETE CASCADE` 등의 ON 구문은 사용하지 않고, 프로그램에서 직접 구현합니다. + +4. **Ajax 처리** + - Ajax 요청 시 `src/main/java/egovframework/util/ApiResponseUtil.java`와 `src/main/java/egovframework/util/ApiResponseEntity.java`를 참조하여 응답을 처리합니다. + - Ajax 응답을 처리할 때는 `response.success` 대신 `response.result`를 사용합니다. + +

6.2 디렉토리 구조 가이드

+ +1. **컨트롤러 클래스** + - `go.kr.project.{모듈명}.controller` 패키지에 위치합니다. + - 클래스명은 `{기능명}Controller.java` 형식으로 작성합니다. + +2. **서비스 클래스** + - `go.kr.project.{모듈명}.service` 패키지에 위치합니다. + - 인터페이스는 `{기능명}Service.java`, 구현체는 `{기능명}ServiceImpl.java` 형식으로 작성합니다. + +3. **매퍼 인터페이스** + - `go.kr.project.{모듈명}.mapper` 패키지에 위치합니다. + - 클래스명은 `{기능명}Mapper.java` 형식으로 작성합니다. + +4. **모델 클래스** + - `go.kr.project.{모듈명}.model` 패키지에 위치합니다. + - 클래스명은 `{기능명}VO.java` 형식으로 작성합니다. + +5. **JSP 파일** + - `src/main/webapp/WEB-INF/views/{모듈명}/{기능명}` 디렉토리에 위치합니다. + - 파일명은 `{화면명}.{레이아웃명}.jsp` 형식으로 작성합니다. + +

6.3 UI 컴포넌트 가이드

+ +1. **TOAST UI Grid** + - 데이터 그리드를 표시할 때 TOAST UI Grid를 사용합니다. + - 그리드 초기화 및 설정은 `xit-tui-grid.js`를 참조합니다. + +2. **폼 유효성 검사** + - 폼 유효성 검사는 `xit-validation.js`를 사용합니다. + +3. **공통 스타일** + - 공통 스타일은 `common.css`와 `xit-common.css`를 사용합니다. + +## 7. 배포 가이드 + +

7.1 빌드 방법

+ +

7.1.1 WAR 파일 빌드

+ +```bash +# Gradle 빌드 (테스트 포함) +./gradlew clean build + +# Gradle 빌드 (테스트 제외) +./gradlew clean build -x test + +# WAR 파일만 생성 +./gradlew war +``` + +

7.1.2 bootWar 파일 빌드

+ +Spring Boot 애플리케이션은 실행 가능한 WAR 파일로 빌드할 수 있습니다. + +```bash +# 실행 가능한 WAR 파일 생성 +./gradlew bootWar +``` + +

7.2 프로필 설정

+ +XIT Framework는 다양한 환경에 맞게 설정할 수 있는 프로필 시스템을 제공합니다. + +

7.2.1 기본 프로필

+ +프로젝트는 다음과 같은 프로필을 제공합니다: + +| 프로필 | 설명 | 주요 설정 | +|--------|------|-----------| +| local | 로컬 개발 환경 | 개발 도구 활성화, 로컬 경로 사용 | +| dev | 개발 서버 환경 | 개발 서버 설정, 세션 타임아웃 30분 | +| prd | 운영 서버 환경 | 운영 서버 설정, 리눅스 경로 사용 | + +

7.2.2 프로필 활성화 방법

+ +
애플리케이션 속성 파일 설정
+ +`application.yml` 파일에서 기본 프로필을 설정할 수 있습니다: + +```yaml +spring: + profiles: + active: local # local, dev, prd 중 선택 +``` + +
명령행 인수로 프로필 설정
+ +애플리케이션 실행 시 명령행 인수로 프로필을 지정할 수 있습니다: + +```bash +# JAR 파일 실행 시 +java -jar xit-framework.war --spring.profiles.active=dev + +# Gradle로 실행 시 +./gradlew bootRun --args='--spring.profiles.active=dev' +``` + +
환경 변수로 프로필 설정
+ +환경 변수를 통해 프로필을 설정할 수 있습니다: + +```bash +# Windows +set SPRING_PROFILES_ACTIVE=dev +java -jar xit-framework.war + +# Linux/macOS +export SPRING_PROFILES_ACTIVE=dev +java -jar xit-framework.war +``` + +

7.3 외부 WAS를 이용한 배포

+ +

7.3.1 Tomcat에 WAR 파일 배포

+ +1. `build/libs` 디렉토리에 생성된 `xit-framework.war` 파일을 Tomcat의 `webapps` 디렉토리에 복사합니다. +2. Tomcat을 재시작합니다. + +

7.3.2 외부 WAS에서 프로필 설정

+ +
Tomcat에서 프로필 설정
+ +Tomcat의 `setenv.bat`(Windows) 또는 `setenv.sh`(Linux/macOS) 파일에 다음 내용을 추가합니다: + +**Windows (setenv.bat)**: +```batch +set "JAVA_OPTS=%JAVA_OPTS% -Dspring.profiles.active=dev" +``` + +**Linux/macOS (setenv.sh)**: +```bash +export JAVA_OPTS="$JAVA_OPTS -Dspring.profiles.active=dev" +``` + +
WAS의 JVM 옵션으로 설정
+ +대부분의 WAS에서는 JVM 옵션을 통해 시스템 속성을 설정할 수 있습니다: + +``` +-Dspring.profiles.active=dev +``` + +
web.xml에 시스템 속성 설정
+ +`web.xml` 파일에 다음과 같이 시스템 속성을 설정할 수 있습니다: + +```xml + + spring.profiles.active + dev + +``` + +

7.4 WAR로 배포 및 실행

+ +Spring Boot 애플리케이션은 bootWar로 빌드된 실행 가능한 WAR 파일로 배포할 수 있습니다. + +

7.4.1 bootWar 파일 실행

+ +bootWar로 빌드된 WAR 파일은 내장 서버를 포함하고 있어 JAR 파일처럼 직접 실행할 수 있습니다: + +```bash +# 기본 프로필로 실행 +java -jar build/libs/xit-framework-boot.war + +# 특정 프로필로 실행 +java -jar build/libs/xit-framework-boot.war --spring.profiles.active=dev + +# 포트 변경하여 실행 +java -jar build/libs/xit-framework-boot.war --server.port=9090 + +# 메모리 설정을 추가하여 실행 +java -Xms512m -Xmx1024m -jar build/libs/xit-framework-boot.war + +# 로컬 테스트 +java -jar build/libs/xit-framework-boot.war --spring.profiles.active=local --server.port=9090 +``` + +

7.4.2 외부 WAS에 배포하여 실행

+ +bootWar로 빌드된 WAR 파일은 외부 WAS(Tomcat, JBoss, WebLogic 등)에 배포하여 실행할 수도 있습니다: + +1. `build/libs` 디렉토리에 생성된 `xit-framework.war` 파일을 WAS의 배포 디렉토리에 복사합니다. + - Tomcat: `webapps` 디렉토리 + - JBoss/WildFly: `deployments` 디렉토리 + - WebLogic: 관리 콘솔을 통해 배포 + +2. WAS를 시작하거나 재시작합니다. + +

7.4.3 백그라운드 실행 (Linux/macOS)

+ +내장 서버로 실행할 경우, 백그라운드에서 실행할 수 있습니다: + +```bash +# nohup을 사용하여 백그라운드 실행 +nohup java -jar build/libs/xit-framework.war > app.log 2>&1 & + +# 실행 중인 프로세스 확인 +ps -ef | grep java +``` + +

7.4.4 Windows 서비스로 등록

+ +Windows에서는 [winsw](https://github.com/winsw/winsw)와 같은 도구를 사용하여 Spring Boot 애플리케이션을 Windows 서비스로 등록할 수 있습니다. + +

7.5 배포 환경 설정

+ +1. **Java 버전**: 배포 환경에서는 Java 1.8을 사용합니다. +2. **서버**: + - 내장 서버(bootWar 직접 실행): 내장된 Tomcat 9.0.78 사용 + - 외부 서버(bootWar 배포): Tomcat 9.0.78 이상 권장 +3. **데이터베이스**: MariaDB를 사용합니다. +4. **메모리 설정**: 애플리케이션 크기와 사용자 수에 따라 적절한 JVM 메모리 설정이 필요합니다. + +

7.6 배포 체크리스트

+ +1. **프로필 설정 확인**: 배포 환경에 맞는 프로필이 활성화되었는지 확인합니다. +2. **데이터베이스 연결 확인**: 데이터베이스 연결 정보가 올바르게 설정되었는지 확인합니다. +3. **파일 업로드 경로 확인**: 파일 업로드 경로가 존재하고 쓰기 권한이 있는지 확인합니다. +4. **로그 경로 확인**: 로그 파일 경로가 존재하고 쓰기 권한이 있는지 확인합니다. +5. **메모리 설정 확인**: JVM 메모리 설정이 적절한지 확인합니다. +6. **포트 충돌 확인**: 사용할 포트가 다른 애플리케이션과 충돌하지 않는지 확인합니다. + +## 8. 참고 자료 + +- [Spring Boot 공식 문서](https://docs.spring.io/spring-boot/docs/2.7.18/reference/html/) +- [전자정부 프레임워크 가이드](https://www.egovframe.go.kr/wiki/doku.php) +- [MyBatis 공식 문서](https://mybatis.org/mybatis-3/ko/index.html) +- [TOAST UI Grid 공식 문서](https://ui.toast.com/tui-grid) +- [TOAST UI Editor 공식 문서](https://ui.toast.com/tui-editor) diff --git a/README_BATCH.md b/README_BATCH.md new file mode 100644 index 0000000..ded6da0 --- /dev/null +++ b/README_BATCH.md @@ -0,0 +1,205 @@ +# XIT 배치 시스템 구조 및 가이드 (2025 최신) + +## 1. 전체 아키텍처 + +``` +┌──────────────────────────────────────────────┐ +│ REST API (Controller) │ +│ └─ BatchJobController │ +├──────────────────────────────────────────────┤ +│ Service Layer (비즈니스 로직) │ +│ ├─ BatchJobService (Interface) │ +│ └─ BatchJobServiceImpl (구현) │ +├──────────────────────────────────────────────┤ +│ Data Access Layer │ +│ └─ BatchJobMapper (MyBatis Mapper) │ +│ └─ BatchJobMapper_maria.xml (SQL) │ +├──────────────────────────────────────────────┤ +│ Model Layer │ +│ ├─ BatchJobInfoVO (작업 정보) │ +│ ├─ BatchJobExecutionVO (실행 결과) │ +│ ├─ BatchJobLogVO (실행 로그) │ +│ └─ BatchJobResult (실행 결과 Enum/VO) │ +├──────────────────────────────────────────────┤ +│ Job Layer (실제 배치 작업) │ +│ └─ SampleBatchJob 등 (Quartz Job) │ +├──────────────────────────────────────────────┤ +│ Listener/Util Layer │ +│ ├─ QuartzJobListener (실행 추적) │ +│ └─ BatchJobLogUtil (로그 기록) │ +├──────────────────────────────────────────────┤ +│ Configuration Layer │ +│ ├─ BatchJobInitializer (초기화) │ +│ ├─ QuartzConfig, QuartzListenerConfig │ +└──────────────────────────────────────────────┘ +``` + +--- + +## 2. 주요 디렉토리/파일 구조 + +- **src/main/java/go/kr/project/batch/** + - **controller/**: `BatchJobController.java` (화면) + - **service/**: `BatchJobService.java` (interface), `impl/BatchJobServiceImpl.java` (구현) + - **mapper/**: `BatchJobMapper.java` (MyBatis interface) + - **model/**: `BatchJobInfoVO.java`, `BatchJobExecutionVO.java`, `BatchJobLogVO.java`, `BatchJobResult.java` + - **job/**: `SampleBatchJob.java` 등 (Quartz Job 구현) + - **config/**: `BatchJobInitializer.java`, `QuartzConfig.java`, `QuartzListenerConfig.java` + - **util/**: `BatchJobLogUtil.java`, `ServerInfoUtil.java` +- **src/main/resources/mybatis/mapper/batch/** + - `BatchJobMapper_maria.xml` (MyBatis SQL) +- **src/main/resources/** + - `application-local.yml`, `application-dev.yml`, `application-prd.yml` (환경별 설정) +- **src/main/webapp/WEB-INF/views/batch/** + - `list.jsp`, `execution.jsp`, `log.jsp`, `batchJobForm_layerPop.jsp` (화면 JSP) + +--- + +## 3. 데이터 모델 + +### BatchJobInfoVO (배치 작업 정보) +- jobId (UUID) +- jobName, jobGroup, jobClass +- cronExpression, description +- status (ACTIVE, PAUSED, DELETED) +- lastExecutionId +- regDt, mdfcnDt + +### BatchJobExecutionVO (실행 결과) +- executionId (UUID) +- jobName, jobGroup +- startTime, endTime +- status (STARTED, COMPLETED, FAILED, VETOED) +- exitCode (COMPLETED, FAILED, UNKNOWN) +- exitMessage +- regDt + +### BatchJobLogVO (실행 로그) +- logId +- executionId +- logTime +- logLevel (INFO, WARN, ERROR) +- logMessage +- regDt + +--- + +## 4. 주요 테이블 + +- **TB_BATCH_JOB_INFO**: 배치 작업 메타데이터 (cron, 상태, 설명 등) +- **TB_BATCH_JOB_EXECUTION**: 각 배치 작업의 실행 결과 (시작/종료, 상태, 메시지 등) +- **TB_BATCH_JOB_LOG**: 실행 로그 (레벨, 메시지 등) + +--- + +## 5. 주요 컴포넌트 및 역할 + +### Controller +- **BatchJobController**: 배치 작업/실행/로그 관리 REST API 및 화면 제공 + +### Service +- **BatchJobService**: 배치 작업 등록/수정/삭제/실행/일시중지/재개/목록/실행결과/로그 등 비즈니스 로직 +- **BatchJobServiceImpl**: Quartz Scheduler, Mapper 연동, 트랜잭션 처리 + +### Mapper +- **BatchJobMapper**: 작업/실행/로그 CRUD, 상태/마지막 실행ID 관리 (MyBatis) +- **BatchJobMapper_maria.xml**: 실제 SQL 정의 + +### Listener/Util +- **QuartzJobListener**: Job 실행 전후 DB 기록, 상태 갱신, 로그 기록 +- **BatchJobLogUtil**: Job 실행 중 로그 기록 (INFO/WARN/ERROR) + +### Job +- **SampleBatchJob 등**: 실제 배치 로직 구현, 동시 실행 방지(@DisallowConcurrentExecution) + +### Config +- **BatchJobInitializer**: 애플리케이션 시작 시 DB에서 작업 목록을 읽어 Quartz에 등록 +- **QuartzConfig, QuartzListenerConfig**: Quartz 및 리스너 설정 + +--- + +## 6. 주요 API (BatchJobController) + +- `GET /batch/list.do` : 배치 작업 목록 화면 +- `POST /batch/list.ajax` : 배치 작업 목록 데이터 +- `POST /batch/register.ajax` : 배치 작업 등록 +- `POST /batch/trigger.ajax` : 작업 즉시 실행 +- `POST /batch/pause.ajax` : 작업 일시 중지 +- `POST /batch/resume.ajax` : 작업 재개 +- `POST /batch/delete.ajax` : 작업 삭제 +- `GET /batch/execution.do` : 실행 결과 화면 +- `POST /batch/execution.ajax` : 실행 결과 목록 +- `GET /batch/log.do` : 실행 로그 화면 +- `POST /batch/log.ajax` : 실행 로그 목록 + +--- + +## 7. 배치 작업 등록/실행/관리 흐름 + +### 배치 작업 등록 +1. Controller → Service.scheduleJob() +2. Service → Quartz Scheduler.scheduleJob() +3. Service → Mapper.insertBatchJobInfo() +4. (Quartz) → Job.execute() (스케줄에 따라 실행) + +### 배치 작업 실행 +1. Quartz Scheduler → Job.execute() +2. Job → 비즈니스 로직 수행 +3. Listener/Util → Mapper.insertBatchJobExecution() (시작) +4. Listener/Util → Mapper.updateBatchJobExecution() (완료) +5. Listener/Util → Mapper.insertBatchJobLog() (로그) + +### 배치 작업 상태/삭제/일시중지/재개 +- Service에서 Quartz Scheduler와 DB 상태를 동기화하며 처리 + +--- + +## 8. 설정 예시 (application-*.yml) + +```yaml +spring: + quartz: + job-store-type: jdbc + jdbc: + initialize-schema: never + properties: + org.quartz.scheduler.instanceName: XitClusteredScheduler + org.quartz.scheduler.instanceId: AUTO + org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX + org.quartz.jobStore.isClustered: true + org.quartz.jobStore.tablePrefix: QRTZ_ + org.quartz.threadPool.threadCount: 10 +mybatis: + config-location: classpath:mybatis/mybatis-config.xml + mapper-locations: classpath:mybatis/mapper/**/*_${Globals.DbType}.xml +``` +- DB, 로그, 파일업로드 등 환경별로 `application-local.yml`, `application-dev.yml`, `application-prd.yml`에서 관리 + +--- + +## 9. 화면(JSP) 구성 + +- **list.jsp**: 배치 작업 목록 및 관리 +- **execution.jsp**: 작업 실행 결과 조회 +- **log.jsp**: 실행 로그 상세 조회 +- **batchJobForm_layerPop.jsp**: 작업 등록/수정 팝업 + +--- + +## 10. 확장/운영 팁 + +- 신규 배치 작업은 `job/` 패키지에 Job 구현체 추가 후 등록 +- Cron 표현식, 상태 등은 DB/화면에서 동적으로 관리 +- 실행 결과/로그는 화면 및 API로 실시간 모니터링 가능 +- 클러스터 환경 지원(Quartz + JDBC JobStore) +- 동시 실행 방지, 예외처리, 실행 로그 추적 등 안정성 확보 + +--- + +본 시스템은 Spring Boot + Quartz + MyBatis 기반의 표준적이면서도 확장성/안정성이 뛰어난 배치 관리 시스템입니다. +각 계층별 상세 코드/쿼리/설정은 소스 내 각 파일을 참고하세요. + +--- + +필요시, 각 계층별 상세 예시/코드/쿼리/설정 추가 가능합니다. +추가 요청 시 구체적으로 말씀해 주세요! \ No newline at end of file diff --git a/README_ZIP_BATCH.md b/README_ZIP_BATCH.md new file mode 100644 index 0000000..5b56ed2 --- /dev/null +++ b/README_ZIP_BATCH.md @@ -0,0 +1,135 @@ +# ZIP 파일 처리 배치 작업 구현 + +## 개요 +특정 디렉토리에 *.zip 파일이 들어오면 압축을 풀어서 특정 디렉토리로 이동시키고, 원본 *.zip 파일은 원본 디렉토리로 이동시키는 배치 작업을 구현했습니다. 이미지 파일 검증 기능과 정상/실패에 대한 로그를 테이블에 정확히 남기는 기능이 포함되어 있습니다. + +## 구현된 기능 + +### 1. 데이터베이스 테이블 +- **tb_batch_zip_file_process_log**: ZIP 파일 처리 메인 로그 테이블 +- **tb_batch_zip_file_detail_log**: ZIP 파일 내 개별 파일 처리 상세 로그 테이블 +- **seq_batch_zip_file_process_log_id**: 메인 로그 ID 시퀀스 +- **seq_batch_zip_file_detail_log_id**: 상세 로그 ID 시퀀스 + +### 2. 주요 클래스 + +#### 모델 클래스 +- `ZipFileProcessLogVO`: ZIP 파일 처리 로그 VO +- `ZipFileDetailLogVO`: ZIP 파일 처리 상세 로그 VO + +#### 매퍼 클래스 +- `ZipFileProcessLogMapper`: ZIP 파일 처리 로그 매퍼 +- `ZipFileDetailLogMapper`: ZIP 파일 처리 상세 로그 매퍼 + +#### 서비스 클래스 +- `ZipFileProcessLogService`: ZIP 파일 처리 로그 서비스 +- `ZipFileDetailLogService`: ZIP 파일 처리 상세 로그 서비스 + +#### 유틸리티 클래스 +- `ImageValidationUtil`: 이미지 파일 검증 유틸리티 + +#### 배치 작업 클래스 +- `ZipFileProcessBatchJob`: ZIP 파일 처리 배치 작업 메인 클래스 + +## 주요 기능 + +### 1. ZIP 파일 처리 흐름 +1. 소스 디렉토리에서 *.zip 파일 검색 +2. ZIP 파일 압축 해제 +3. 개별 파일 처리 및 검증 +4. 이미지 파일 손상 여부 확인 +5. 처리 결과 로그 저장 +6. ZIP 파일을 아카이브 디렉토리로 이동 + +### 2. 이미지 파일 검증 +- 확장자 기반 이미지 파일 판별 +- MIME 타입 기반 이미지 파일 검증 +- ImageIO를 이용한 이미지 손상 여부 확인 +- 지원 이미지 형식: jpg, jpeg, png, gif, bmp, tiff, tif, webp + +### 3. 로그 기능 +- ZIP 파일별 처리 로그 (처리 상태, 파일 수, 성공/실패 통계) +- 개별 파일별 상세 로그 (파일 정보, 이미지 여부, 손상 여부) +- 배치 작업 실행 로그 (BatchJobLogUtil 활용) + +## 설정 + +### application.yml 설정 +```yaml +batch: + zip: + source: + directory: ${user.home}/batch/zip/source # ZIP 파일 소스 디렉토리 + extract: + directory: ${user.home}/batch/zip/extract # ZIP 파일 압축 해제 디렉토리 + archive: + directory: ${user.home}/batch/zip/archive # ZIP 파일 아카이브 디렉토리 +``` + +## 디렉토리 구조 +``` +${user.home}/batch/zip/ +├── source/ # ZIP 파일이 들어오는 디렉토리 +├── extract/ # ZIP 파일 압축 해제 디렉토리 +└── archive/ # 처리 완료된 ZIP 파일 보관 디렉토리 +``` + +## 사용 방법 + +### 1. 데이터베이스 테이블 생성 +```sql +-- 시퀀스 생성 +source DB-DDL/maria/ddl/xitframework/seq_batch_zip_file_process_log_id.sql +source DB-DDL/maria/ddl/xitframework/seq_batch_zip_file_detail_log_id.sql + +-- 테이블 생성 +source DB-DDL/maria/ddl/xitframework/tb_batch_zip_file_process_log.sql +source DB-DDL/maria/ddl/xitframework/tb_batch_zip_file_detail_log.sql +``` + +### 2. 배치 작업 등록 +Quartz 스케줄러를 통해 `ZipFileProcessBatchJob` 클래스를 등록하여 주기적으로 실행 + +### 3. ZIP 파일 처리 +1. `${user.home}/batch/zip/source/` 디렉토리에 ZIP 파일 업로드 +2. 배치 작업이 실행되면 자동으로 처리 +3. 처리 결과는 데이터베이스 로그 테이블에서 확인 + +## 로그 확인 + +### 메인 처리 로그 조회 +```sql +SELECT * FROM tb_batch_zip_file_process_log +ORDER BY start_time DESC; +``` + +### 상세 처리 로그 조회 +```sql +SELECT * FROM tb_batch_zip_file_detail_log +WHERE log_id = 'ZPFL00000001' +ORDER BY process_time ASC; +``` + +### 이미지 파일 처리 결과 조회 +```sql +SELECT + file_name, + is_image, + is_corrupted, + process_status, + error_message +FROM tb_batch_zip_file_detail_log +WHERE log_id = 'ZPFL00000001' + AND is_image = 'Y'; +``` + +## 에러 처리 +- ZIP 파일 처리 중 오류 발생 시 로그 테이블에 오류 정보 저장 +- 개별 파일 처리 실패 시에도 다른 파일 처리 계속 진행 +- 손상된 이미지 파일 발견 시 ERROR 상태로 로그 저장 + +## 주의사항 +1. 디렉토리 권한 확인 필요 +2. 대용량 ZIP 파일 처리 시 메모리 사용량 모니터링 필요 +3. 동일한 파일명의 ZIP 파일 처리 시 타임스탬프 추가하여 중복 방지 +4. 배치 작업은 `@DisallowConcurrentExecution` 어노테이션으로 동시 실행 방지 diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..0e2d35b --- /dev/null +++ b/build.gradle @@ -0,0 +1,186 @@ +/** + * XIT Framework Gradle 빌드 설정 파일 + * + * 주요 버전 정보: + * - Spring Boot: 2.7.18 (스프링 부트 버전) + * - Java: 1.8 (소스 및 타겟 호환성) + * - 전자정부 프레임워크: 4.3.0 + */ + +plugins { + // 스프링 부트 플러그인 - 스프링 부트 애플리케이션 빌드 및 실행을 지원 + id 'org.springframework.boot' version '2.7.18' + // 스프링 의존성 관리 플러그인 - 스프링 부트 버전에 맞는 의존성 버전 관리 + id 'io.spring.dependency-management' version '1.0.15.RELEASE' + // 자바 플러그인 - 자바 소스 컴파일 지원 + id 'java' + // WAR 플러그인 - 웹 애플리케이션 아카이브 생성 지원 + id 'war' +} + +// 프로젝트 그룹 및 버전 정보 +group = 'go.kr.project' +version = '0.0.1-SNAPSHOT' + +// 자바 버전 설정 (소스 및 타겟 호환성) +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + +// Java 컴파일 옵션 설정 +//tasks.withType(JavaCompile) { +// options.encoding = 'UTF-8' +// options.compilerArgs += [ +// '-Xlint:deprecation', +// '-Xlint:unchecked' +// ] +//} + +// 설정 구성 +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} + +// 저장소 설정 +repositories { + mavenCentral() // 메이븐 중앙 저장소 + maven { url 'https://maven.egovframe.go.kr/maven/' } // 전자정부 프레임워크 저장소 +} + +// 버전 변수 정의 +ext { + // 서버 관련 버전 + tomcatVersion = '9.0.78' // 톰캣 9 버전 (Servlet 3.1 지원) + + // 라이브러리 버전 + tilesVersion = '3.0.8' // 타일즈 버전 + mybatisVersion = '2.3.1' // 마이바티스 스프링 부트 스타터 버전 + commonsTextVersion = '1.10.0' // 아파치 커먼스 텍스트 버전 + egovFrameVersion = '4.3.0' // 전자정부 프레임워크 버전 +} + +dependencies { + // ===== 스프링 부트 핵심 의존성 ===== + // 스프링 부트 웹 스타터 - MVC, REST, 내장 톰캣 등 웹 개발에 필요한 기본 의존성 포함 + implementation 'org.springframework.boot:spring-boot-starter-web' + // 스프링 부트 유효성 검사 스타터 - Bean Validation API 구현체 포함 + implementation 'org.springframework.boot:spring-boot-starter-validation' + // 스프링 부트 AOP 스타터 - 관점 지향 프로그래밍 지원 + implementation 'org.springframework.boot:spring-boot-starter-aop' + // 스프링 부트 JDBC 스타터 - JDBC 및 커넥션 풀(HikariCP) 포함 + implementation 'org.springframework.boot:spring-boot-starter-jdbc' + //implementation 'org.springframework.boot:spring-boot-starter-data-jpa' /* 실제 사용 X, intellij plugin 사용을 위해 설정 */ + + // ===== 전자정부 프레임워크 의존성 ===== + // 전자정부 프레임워크 공통 기능 - 공통 유틸리티 및 예외 처리 등 + implementation("org.egovframe.rte:org.egovframe.rte.fdl.cmmn:${egovFrameVersion}") { + exclude group: 'org.apache.logging.log4j', module: 'log4j-slf4j-impl' /* 보안이슈 대응 후 쿼리로그 문제 발생, SLF4J 충돌 발생 */ + } + // 전자정부 프레임워크 MVC - 웹 MVC 관련 기능 + implementation("org.egovframe.rte:org.egovframe.rte.ptl.mvc:${egovFrameVersion}") { + exclude group: 'org.apache.logging.log4j', module: 'log4j-slf4j-impl' /* 보안이슈 대응 후 쿼리로그 문제 발생, SLF4J 충돌 발생 */ + } + + + // ===== 로깅 의존성 ===== + // Logback - 로깅 구현체 + implementation 'ch.qos.logback:logback-classic' + implementation 'ch.qos.logback:logback-core' + // SLF4J - 로깅 퍼사드 인터페이스 + implementation 'org.slf4j:slf4j-api' + + // ===== 서블릿 & JSP 관련 의존성 ===== + // JSTL - JSP 표준 태그 라이브러리 + implementation 'javax.servlet:jstl' + + // ===== 톰캣 설정 ===== + // 톰캣 9 명시적 설정 (Servlet 3.1 지원) + implementation "org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}" + implementation "org.apache.tomcat.embed:tomcat-embed-el:${tomcatVersion}" + implementation "org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}" + + // ===== 타일즈 의존성 ===== + // 타일즈 - 레이아웃 템플릿 엔진 (JSP 페이지 조각 조합) + implementation "org.apache.tiles:tiles-jsp:${tilesVersion}" + implementation "org.apache.tiles:tiles-core:${tilesVersion}" + implementation "org.apache.tiles:tiles-api:${tilesVersion}" + implementation "org.apache.tiles:tiles-servlet:${tilesVersion}" + implementation "org.apache.tiles:tiles-el:${tilesVersion}" + + // ===== 데이터 액세스 의존성 ===== + // MyBatis 스프링 부트 스타터 - SQL 매핑 프레임워크 + implementation "org.mybatis.spring.boot:mybatis-spring-boot-starter:${mybatisVersion}" + // MariaDB JDBC 드라이버 - MariaDB 데이터베이스 연결 + implementation 'org.mariadb.jdbc:mariadb-java-client' + // HikariCP - 고성능 JDBC 커넥션 풀 (spring-boot-starter-jdbc에 포함됨) + + // ===== 유틸리티 의존성 ===== + // Apache Commons Text - 텍스트 처리 유틸리티 + implementation "org.apache.commons:commons-text:${commonsTextVersion}" + + // ===== EXCEL ===== + implementation 'org.apache.poi:poi:5.3.0' + implementation 'org.apache.poi:poi-ooxml:5.3.0' + + // ===== Swagger UI ===== + implementation 'org.springdoc:springdoc-openapi-ui:1.7.0' + implementation 'org.springdoc:springdoc-openapi-webmvc-core:1.7.0' + + // ===== Quartz Scheduler ===== + implementation 'org.springframework.boot:spring-boot-starter-quartz' + + // ===== sqlPaser ===== + implementation 'com.github.jsqlparser:jsqlparser:4.5' + + // ===== Mail ===== + implementation 'org.springframework.boot:spring-boot-starter-mail' + + // ===== 개발 도구 의존성 ===== + // Lombok - 반복 코드 생성 도구 (Getter, Setter, Builder 등 자동 생성) + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + // 스프링 부트 개발 도구 - 자동 재시작, 라이브 리로드 등 + developmentOnly 'org.springframework.boot:spring-boot-devtools' + + // ===== 테스트 의존성 ===== + // 스프링 부트 테스트 스타터 - JUnit, Mockito 등 테스트 도구 포함 + testImplementation 'org.springframework.boot:spring-boot-starter-test' + + // ===== 배포 관련 의존성 ===== + // 톰캣 서블릿 API - 외부 톰캣 배포 시 사용 + providedCompile "org.apache.tomcat:tomcat-servlet-api:${tomcatVersion}" + // 스프링 부트 톰캣 - 외부 톰캣 배포 시 내장 톰캣 제외 + providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat' +} + +// ===== 테스트 설정 ===== +// JUnit 플랫폼을 사용하여 테스트 실행 (JUnit 5 지원) +tasks.named('test') { + useJUnitPlatform() +} + +// ===== WAR 파일 설정 ===== +// 생성될 WAR 파일의 이름 지정 +war { + archiveFileName = 'xit-framework.war' +} + +// ===== bootWar 파일 설정 ===== +// 생성될 bootWar 파일의 이름 지정 +bootWar { + archiveFileName = 'xit-framework-boot.war' +} + +// war 포함 압축 푼 소스 포함, +// ./build/exploded/xit-framework/ +tasks.register('exploded', Copy) { + dependsOn 'war' + from zipTree(tasks.war.archiveFile) + into layout.buildDirectory.dir("exploded/${project.name}") + doFirst { + layout.buildDirectory.dir("exploded/${project.name}").get().asFile.deleteDir() + } +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..cea7a79 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..f3b75f3 --- /dev/null +++ b/gradlew @@ -0,0 +1,251 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..9d21a21 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/qodana.yaml b/qodana.yaml new file mode 100644 index 0000000..b804e63 --- /dev/null +++ b/qodana.yaml @@ -0,0 +1,31 @@ +#-------------------------------------------------------------------------------# +# Qodana analysis is configured by qodana.yaml file # +# https://www.jetbrains.com/help/qodana/qodana-yaml.html # +#-------------------------------------------------------------------------------# +version: "1.0" + +#Specify inspection profile for code analysis +profile: + name: qodana.starter + +#Enable inspections +#include: +# - name: + +#Disable inspections +#exclude: +# - name: +# paths: +# - + +projectJDK: "8" #(Applied in CI/CD pipeline) + +#Execute shell command before Qodana execution (Applied in CI/CD pipeline) +#bootstrap: sh ./prepare-qodana.sh + +#Install IDE plugins before Qodana execution (Applied in CI/CD pipeline) +#plugins: +# - id: #(plugin id can be found at https://plugins.jetbrains.com) + +#Specify Qodana linter for analysis (Applied in CI/CD pipeline) +linter: jetbrains/qodana-jvm:2025.1 diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..8861fd9 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'xit-framework' \ No newline at end of file diff --git a/src/main/java/egovframework/config/EgovConfigAspect.java b/src/main/java/egovframework/config/EgovConfigAspect.java new file mode 100644 index 0000000..4a7802e --- /dev/null +++ b/src/main/java/egovframework/config/EgovConfigAspect.java @@ -0,0 +1,66 @@ +package egovframework.config; + +import egovframework.exception.EgovAopExceptionTransfer; +import egovframework.exception.EgovDefaultExcepHndlr; +import egovframework.exception.EgovDefaultOthersExcepHndlr; +import org.egovframe.rte.fdl.cmmn.aspect.ExceptionTransfer; +import org.egovframe.rte.fdl.cmmn.exception.handler.ExceptionHandler; +import org.egovframe.rte.fdl.cmmn.exception.manager.DefaultExceptionHandleManager; +import org.egovframe.rte.fdl.cmmn.exception.manager.ExceptionHandlerService; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.util.AntPathMatcher; + +@Configuration +@EnableAspectJAutoProxy +public class EgovConfigAspect { + + @Bean + public EgovDefaultExcepHndlr egovHandler() { + return new EgovDefaultExcepHndlr(); + } + + @Bean + public EgovDefaultOthersExcepHndlr otherHandler() { + return new EgovDefaultOthersExcepHndlr(); + } + + @Bean + public DefaultExceptionHandleManager defaultExceptionHandleManager(AntPathMatcher antPathMatcher, EgovDefaultExcepHndlr egovHandler) { + DefaultExceptionHandleManager defaultExceptionHandleManager = new DefaultExceptionHandleManager(); + defaultExceptionHandleManager.setReqExpMatcher(antPathMatcher); + defaultExceptionHandleManager.setPatterns(new String[]{"**service.impl.*"}); + defaultExceptionHandleManager.setHandlers(new ExceptionHandler[]{egovHandler}); + return defaultExceptionHandleManager; + } + + @Bean + public DefaultExceptionHandleManager otherExceptionHandleManager(AntPathMatcher antPathMatcher, EgovDefaultOthersExcepHndlr othersExcepHndlr) { + DefaultExceptionHandleManager defaultExceptionHandleManager = new DefaultExceptionHandleManager(); + defaultExceptionHandleManager.setReqExpMatcher(antPathMatcher); + defaultExceptionHandleManager.setPatterns(new String[]{"**service.impl.*"}); + defaultExceptionHandleManager.setHandlers(new ExceptionHandler[]{othersExcepHndlr}); + return defaultExceptionHandleManager; + } + + @Bean + public ExceptionTransfer exceptionTransfer( + @Qualifier("defaultExceptionHandleManager") DefaultExceptionHandleManager defaultExceptionHandleManager, + @Qualifier("otherExceptionHandleManager") DefaultExceptionHandleManager otherExceptionHandleManager) { + ExceptionTransfer exceptionTransfer = new ExceptionTransfer(); + exceptionTransfer.setExceptionHandlerService(new ExceptionHandlerService[] { + defaultExceptionHandleManager, otherExceptionHandleManager + }); + return exceptionTransfer; + } + + @Bean + public EgovAopExceptionTransfer aopExceptionTransfer(ExceptionTransfer exceptionTransfer) { + EgovAopExceptionTransfer egovAopExceptionTransfer = new EgovAopExceptionTransfer(); + egovAopExceptionTransfer.setExceptionTransfer(exceptionTransfer); + return egovAopExceptionTransfer; + } + +} diff --git a/src/main/java/egovframework/config/EgovConfigCommon.java b/src/main/java/egovframework/config/EgovConfigCommon.java new file mode 100644 index 0000000..6e19b1a --- /dev/null +++ b/src/main/java/egovframework/config/EgovConfigCommon.java @@ -0,0 +1,60 @@ +package egovframework.config; + +import org.egovframe.rte.fdl.cmmn.trace.LeaveaTrace; +import org.egovframe.rte.fdl.cmmn.trace.handler.DefaultTraceHandler; +import org.egovframe.rte.fdl.cmmn.trace.handler.TraceHandler; +import org.egovframe.rte.fdl.cmmn.trace.manager.DefaultTraceHandleManager; +import org.egovframe.rte.fdl.cmmn.trace.manager.TraceHandlerService; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.support.MessageSourceAccessor; +import org.springframework.context.support.ReloadableResourceBundleMessageSource; +import org.springframework.util.AntPathMatcher; + +@Configuration +public class EgovConfigCommon { + + @Bean + public AntPathMatcher antPathMatcher() { + return new AntPathMatcher(); + } + + @Bean + public DefaultTraceHandler defaultTraceHandler() { + return new DefaultTraceHandler(); + } + + @Bean + public ReloadableResourceBundleMessageSource messageSource() { + ReloadableResourceBundleMessageSource reloadableResourceBundleMessageSource = new ReloadableResourceBundleMessageSource(); + reloadableResourceBundleMessageSource.setBasenames( + "classpath:/egovframework/message/message-common", + "classpath:/org/egovframe/rte/fdl/idgnr/messages/idgnr", + "classpath:/org/egovframe/rte/fdl/property/messages/properties"); + reloadableResourceBundleMessageSource.setDefaultEncoding("UTF-8"); + reloadableResourceBundleMessageSource.setCacheSeconds(60); + return reloadableResourceBundleMessageSource; + } + + @Bean + public MessageSourceAccessor messageSourceAccessor() { + return new MessageSourceAccessor(this.messageSource()); + } + + @Bean + public DefaultTraceHandleManager traceHandlerService() { + DefaultTraceHandleManager defaultTraceHandleManager = new DefaultTraceHandleManager(); + defaultTraceHandleManager.setReqExpMatcher(antPathMatcher()); + defaultTraceHandleManager.setPatterns(new String[]{"*"}); + defaultTraceHandleManager.setHandlers(new TraceHandler[]{defaultTraceHandler()}); + return defaultTraceHandleManager; + } + + @Bean + public LeaveaTrace leaveaTrace() { + LeaveaTrace leaveaTrace = new LeaveaTrace(); + leaveaTrace.setTraceHandlerServices(new TraceHandlerService[]{traceHandlerService()}); + return leaveaTrace; + } + +} diff --git a/src/main/java/egovframework/config/EgovConfigInterceptor.java b/src/main/java/egovframework/config/EgovConfigInterceptor.java new file mode 100644 index 0000000..55faecf --- /dev/null +++ b/src/main/java/egovframework/config/EgovConfigInterceptor.java @@ -0,0 +1,42 @@ +package egovframework.config; + +import egovframework.configProperties.InterceptorProperties; +import egovframework.interceptor.AuthInterceptor; +import go.kr.project.login.service.LoginService; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * 인터셉터 설정 클래스 + */ +@Configuration +@RequiredArgsConstructor +public class EgovConfigInterceptor implements WebMvcConfigurer { + + private final LoginService loginService; + private final InterceptorProperties interceptorProperties; + + + /** + * 인터셉터 등록 + * @param registry 인터셉터 레지스트리 + */ + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(authInterceptor()) + .addPathPatterns("/**") // 모든 경로에 적용 + .excludePathPatterns(interceptorProperties.getInterceptorExclude()); // 접근 제어 예외 URL 패턴 제외 + } + + /** + * 인증 인터셉터 빈 등록 + * @return AuthInterceptor 객체 + */ + @Bean + public AuthInterceptor authInterceptor() { + return new AuthInterceptor(loginService); + } +} \ No newline at end of file diff --git a/src/main/java/egovframework/config/EgovConfigMail.java b/src/main/java/egovframework/config/EgovConfigMail.java new file mode 100644 index 0000000..87b28a2 --- /dev/null +++ b/src/main/java/egovframework/config/EgovConfigMail.java @@ -0,0 +1,61 @@ +package egovframework.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.JavaMailSenderImpl; + +import java.util.Properties; + +/** + * 메일 발송 관련 설정 클래스 + * JavaMailSender 빈을 등록합니다. + */ +@Configuration +public class EgovConfigMail { + + @Value("${spring.mail.host}") + private String host; + + @Value("${spring.mail.port}") + private int port; + + @Value("${spring.mail.username}") + private String username; + + @Value("${spring.mail.password}") + private String password; + + @Value("${spring.mail.properties.mail.smtp.auth}") + private boolean smtpAuth; + + @Value("${spring.mail.properties.mail.smtp.starttls.enable}") + private boolean starttlsEnable; + + @Value("${spring.mail.properties.mail.debug}") + private boolean debug; + + + /** + * JavaMailSender 빈 등록 + * application-local.yml 파일에 설정된 메일 서버 정보를 사용합니다. + * @return JavaMailSender 인스턴스 + */ + @Bean + public JavaMailSender javaMailSender() { + JavaMailSenderImpl mailSender = new JavaMailSenderImpl(); + mailSender.setHost(host); + mailSender.setPort(port); + mailSender.setUsername(username); + mailSender.setPassword(password); + + Properties props = mailSender.getJavaMailProperties(); + props.put("mail.smtp.auth", smtpAuth); + props.put("mail.smtp.starttls.enable", starttlsEnable); + props.put("mail.debug", debug); + + + return mailSender; + } +} diff --git a/src/main/java/egovframework/config/EgovConfigTransaction.java b/src/main/java/egovframework/config/EgovConfigTransaction.java new file mode 100644 index 0000000..6d8836d --- /dev/null +++ b/src/main/java/egovframework/config/EgovConfigTransaction.java @@ -0,0 +1,95 @@ +package egovframework.config; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.aop.Advisor; +import org.springframework.aop.aspectj.AspectJExpressionPointcut; +import org.springframework.aop.support.DefaultPointcutAdvisor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.interceptor.*; + +import javax.annotation.PostConstruct; +import javax.sql.DataSource; +import java.util.Collections; +import java.util.HashMap; + +/** + * @ClassName : EgovConfigAppTransaction.java + * @Description : Transaction 설정 + * + * @author : 윤주호 + * @since : 2021. 7. 20 + * @version : 1.0 + * + *
+ * << 개정이력(Modification Information) >>
+ *
+ *   수정일              수정자               수정내용
+ *  -------------  ------------   ---------------------
+ *   2021. 7. 20    윤주호               최초 생성
+ * 
+ * + */ +@Slf4j +@Configuration +public class EgovConfigTransaction { + + @Autowired + DataSource dataSource; + + @PostConstruct + public void init() { + log.info("Datasource type: {}", dataSource.getClass().getName()); + } + + @Bean + public DataSourceTransactionManager txManager() { + DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(); + dataSourceTransactionManager.setDataSource(dataSource); + return dataSourceTransactionManager; + } + + // ------------------------------------------------------------- + // TransactionAdvice 설정 + // ------------------------------------------------------------- + + @Bean + public TransactionInterceptor txAdvice(DataSourceTransactionManager txManager) { + TransactionInterceptor txAdvice = new TransactionInterceptor(); + txAdvice.setTransactionManager(txManager); + txAdvice.setTransactionAttributeSource(getNameMatchTransactionAttributeSource()); + return txAdvice; + } + + private NameMatchTransactionAttributeSource getNameMatchTransactionAttributeSource() { + NameMatchTransactionAttributeSource txAttributeSource = new NameMatchTransactionAttributeSource(); + txAttributeSource.setNameMap(getRuleBasedTxAttributeMap()); + return txAttributeSource; + } + + private HashMap getRuleBasedTxAttributeMap() { + HashMap txMethods = new HashMap(); + + RuleBasedTransactionAttribute txAttribute = new RuleBasedTransactionAttribute(); + txAttribute.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); + txAttribute.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class))); + txMethods.put("*", txAttribute); + + return txMethods; + } + + // ------------------------------------------------------------- + // TransactionAdvisor 설정 + // ------------------------------------------------------------- + + @Bean + public Advisor txAdvisor(DataSourceTransactionManager txManager) { + AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); + pointcut.setExpression( + "execution(* go.kr.project..impl.*Impl.*(..)) or execution(* egovframework.com..*Impl.*(..))"); + return new DefaultPointcutAdvisor(pointcut, txAdvice(txManager)); + } +} diff --git a/src/main/java/egovframework/config/EgovConfigValidation.java b/src/main/java/egovframework/config/EgovConfigValidation.java new file mode 100644 index 0000000..4afad37 --- /dev/null +++ b/src/main/java/egovframework/config/EgovConfigValidation.java @@ -0,0 +1,20 @@ +package egovframework.config; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.MessageSource; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.validation.Validator; +import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; + +@Configuration +public class EgovConfigValidation { + + @Bean + public Validator getValidator(@Qualifier("messageSource") MessageSource messageSource) { + LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean(); + localValidatorFactoryBean.setValidationMessageSource(messageSource); + return localValidatorFactoryBean; + } + +} diff --git a/src/main/java/egovframework/config/EgovConfigWeb.java b/src/main/java/egovframework/config/EgovConfigWeb.java new file mode 100644 index 0000000..e5ba007 --- /dev/null +++ b/src/main/java/egovframework/config/EgovConfigWeb.java @@ -0,0 +1,98 @@ +package egovframework.config; + +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.http.CacheControl; +import org.springframework.web.servlet.HandlerExceptionResolver; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.web.servlet.view.InternalResourceViewResolver; +import org.springframework.web.servlet.view.UrlBasedViewResolver; +import org.springframework.web.servlet.view.tiles3.TilesConfigurer; +import org.springframework.web.servlet.view.tiles3.TilesView; + +import java.util.List; + +@Setter +@Configuration +@RequiredArgsConstructor +@Import({ + EgovConfigAspect.class, + EgovConfigCommon.class, + EgovConfigTransaction.class, + EgovConfigValidation.class, + EgovConfigInterceptor.class, + EgovErrorConfig.class +}) +public class EgovConfigWeb implements WebMvcConfigurer, ApplicationContextAware { + + private ApplicationContext applicationContext; + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("/plugins/**") + .addResourceLocations("/resources/plugins/") + .setCacheControl(CacheControl.noCache().mustRevalidate()); + registry.addResourceHandler("/css/**") + .addResourceLocations("/resources/css/") + .setCacheControl(CacheControl.noCache().mustRevalidate()); + registry.addResourceHandler("/img/**") + .addResourceLocations("/resources/img/") + .setCacheControl(CacheControl.noCache().mustRevalidate()); + registry.addResourceHandler("/js/**") + .addResourceLocations("/resources/js/") + .setCacheControl(CacheControl.noCache().mustRevalidate()); + registry.addResourceHandler("/xit/**") + .addResourceLocations("/resources/xit/") + .setCacheControl(CacheControl.noCache().mustRevalidate()); + registry.addResourceHandler("/font/**") + .addResourceLocations("/resources/font/") + .setCacheControl(CacheControl.noCache().mustRevalidate()); + } + + /** + * Exception handling is now done using @ControllerAdvice in EgovExceptionAdvice class. + * The SimpleMappingExceptionResolver approach has been replaced with a more modern approach. + * + * @see egovframework.exception.EgovExceptionAdvice + */ + @Override + public void configureHandlerExceptionResolvers(@Nullable List< HandlerExceptionResolver> resolvers) { + // Exception handling is now done using @ControllerAdvice + } + + @Bean + public UrlBasedViewResolver viewResolver() { + UrlBasedViewResolver tilesViewResolver = new UrlBasedViewResolver(); + tilesViewResolver.setViewClass(TilesView.class); + tilesViewResolver.setOrder(1); + return tilesViewResolver; + } + + @Bean + public TilesConfigurer tilesConfigurer() { + TilesConfigurer tilesConfigurer = new TilesConfigurer(); + tilesConfigurer.setDefinitions( + "/WEB-INF/tiles/tiles.xml" + ); + tilesConfigurer.setCheckRefresh(true); + return tilesConfigurer; + } + + @Bean + public InternalResourceViewResolver internalResourceViewResolver() { + InternalResourceViewResolver resolver = new InternalResourceViewResolver(); + resolver.setPrefix("/WEB-INF/views/"); + resolver.setSuffix(".jsp"); + resolver.setOrder(2); + return resolver; + } + + +} diff --git a/src/main/java/egovframework/config/EgovErrorConfig.java b/src/main/java/egovframework/config/EgovErrorConfig.java new file mode 100644 index 0000000..83870c5 --- /dev/null +++ b/src/main/java/egovframework/config/EgovErrorConfig.java @@ -0,0 +1,46 @@ +package egovframework.config; + +import org.springframework.boot.web.server.ErrorPage; +import org.springframework.boot.web.server.ErrorPageRegistrar; +import org.springframework.boot.web.server.ErrorPageRegistry; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpStatus; + +/** + * 에러 페이지 설정을 위한 설정 클래스 + * 404, 500 등의 HTTP 에러 코드에 대한 커스텀 에러 페이지를 매핑합니다. + */ +@Configuration +public class EgovErrorConfig { + + /** + * ErrorPageRegistrar 빈을 생성하여 에러 페이지를 등록합니다. + * + * @return ErrorPageRegistrar 구현체 + */ + @Bean + public ErrorPageRegistrar errorPageRegistrar() { + return new ErrorPageRegistrar() { + @Override + public void registerErrorPages(ErrorPageRegistry registry) { + // 404 에러 페이지 설정 + ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND, "/error/404"); + + // 500 에러 페이지 설정 + ErrorPage error500Page = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error/500"); + + // 기타 서버 에러 페이지 설정 + ErrorPage error502Page = new ErrorPage(HttpStatus.BAD_GATEWAY, "/error/500"); + ErrorPage error503Page = new ErrorPage(HttpStatus.SERVICE_UNAVAILABLE, "/error/500"); + ErrorPage error504Page = new ErrorPage(HttpStatus.GATEWAY_TIMEOUT, "/error/500"); + + // 예외 처리를 위한 에러 페이지 설정 + ErrorPage runtimeExceptionPage = new ErrorPage(RuntimeException.class, "/error/500"); + + // 에러 페이지 등록 + registry.addErrorPages(error404Page, error500Page, error502Page, error503Page, error504Page, runtimeExceptionPage); + } + }; + } +} diff --git a/src/main/java/egovframework/config/HtmlCharacterEscapes.java b/src/main/java/egovframework/config/HtmlCharacterEscapes.java new file mode 100644 index 0000000..621801e --- /dev/null +++ b/src/main/java/egovframework/config/HtmlCharacterEscapes.java @@ -0,0 +1,34 @@ +package egovframework.config; + +import com.fasterxml.jackson.core.SerializableString; +import com.fasterxml.jackson.core.io.CharacterEscapes; +import com.fasterxml.jackson.core.io.SerializedString; +import org.apache.commons.text.StringEscapeUtils; + +public class HtmlCharacterEscapes extends CharacterEscapes { + + private static final long serialVersionUID = -6353236148390563705L; + + private final int[] asciiEscapes; + + public HtmlCharacterEscapes() { + this.asciiEscapes = CharacterEscapes.standardAsciiEscapesForJSON(); + this.asciiEscapes['<'] = CharacterEscapes.ESCAPE_CUSTOM; + this.asciiEscapes['>'] = CharacterEscapes.ESCAPE_CUSTOM; + this.asciiEscapes['\"'] = CharacterEscapes.ESCAPE_CUSTOM; + this.asciiEscapes['('] = CharacterEscapes.ESCAPE_CUSTOM; + this.asciiEscapes[')'] = CharacterEscapes.ESCAPE_CUSTOM; + this.asciiEscapes['#'] = CharacterEscapes.ESCAPE_CUSTOM; + this.asciiEscapes['\''] = CharacterEscapes.ESCAPE_CUSTOM; + } + + @Override + public int[] getEscapeCodesForAscii() { + return asciiEscapes; + } + + @Override + public SerializableString getEscapeSequence(int ch) { + return new SerializedString(StringEscapeUtils.escapeHtml4(Character.toString((char) ch))); + } +} diff --git a/src/main/java/egovframework/config/MyBatisConfig.java b/src/main/java/egovframework/config/MyBatisConfig.java new file mode 100644 index 0000000..eb6adda --- /dev/null +++ b/src/main/java/egovframework/config/MyBatisConfig.java @@ -0,0 +1,27 @@ +package egovframework.config; + +import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; + +/** + * MyBatis Configuration + * + * This class configures MyBatis by adding custom interceptors. + */ +@Configuration +@Profile({"local", "dev"}) // local과 dev 프로파일에서만 활성화 +public class MyBatisConfig { + + /** + * Customizes the MyBatis configuration by adding the query interceptor. + * + * @param interceptor The MyBatis query interceptor + * @return A ConfigurationCustomizer that adds the interceptor to MyBatis + */ + @Bean + public ConfigurationCustomizer mybatisConfigurationCustomizer(MyBatisQueryInterceptor interceptor) { + return configuration -> configuration.addInterceptor(interceptor); + } +} \ No newline at end of file diff --git a/src/main/java/egovframework/config/MyBatisQueryInterceptor.java b/src/main/java/egovframework/config/MyBatisQueryInterceptor.java new file mode 100644 index 0000000..f338ef7 --- /dev/null +++ b/src/main/java/egovframework/config/MyBatisQueryInterceptor.java @@ -0,0 +1,449 @@ +package egovframework.config; + +import lombok.extern.slf4j.Slf4j; +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import net.sf.jsqlparser.schema.Column; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.delete.Delete; +import net.sf.jsqlparser.statement.insert.Insert; +import net.sf.jsqlparser.statement.select.Join; +import net.sf.jsqlparser.statement.select.PlainSelect; +import net.sf.jsqlparser.statement.select.Select; +import net.sf.jsqlparser.statement.select.SelectItem; +import net.sf.jsqlparser.statement.update.Update; +import net.sf.jsqlparser.statement.update.UpdateSet; +import org.apache.ibatis.executor.Executor; +import org.apache.ibatis.mapping.BoundSql; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.mapping.ParameterMapping; +import org.apache.ibatis.plugin.Interceptor; +import org.apache.ibatis.plugin.Intercepts; +import org.apache.ibatis.plugin.Invocation; +import org.apache.ibatis.plugin.Signature; +import org.apache.ibatis.session.ResultHandler; +import org.apache.ibatis.session.RowBounds; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * MyBatis Query Interceptor + * + * This interceptor logs detailed information about SQL queries executed by MyBatis, + * including the original SQL, actual SQL with parameters, and parameter values. + */ +@Component +@Profile({"local", "dev"}) // local과 dev 프로파일에서만 활성화 +@Intercepts({ + @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}), + @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}) +}) +@Slf4j +public class MyBatisQueryInterceptor implements Interceptor { + + @Override + public Object intercept(Invocation invocation) throws Throwable { + MappedStatement ms = (MappedStatement) invocation.getArgs()[0]; + Object parameter = invocation.getArgs()[1]; + + BoundSql boundSql = ms.getBoundSql(parameter); + String sql = boundSql.getSql(); + List parameterMappings = boundSql.getParameterMappings(); + + // 실제 실행될 쿼리 추출 (MyBatis 내부에서 처리된 결과) + String actualSql = getActualSql(boundSql, parameter); + + // 파라미터 정보 추출 + Map paramMap = extractDetailedParameters(parameter, parameterMappings); + + logDetailedQueryInfo(ms.getId(), sql, actualSql, paramMap); + + // 원래 쿼리 실행 + long startTime = System.currentTimeMillis(); + Object result = invocation.proceed(); + long endTime = System.currentTimeMillis(); + + // 쿼리 실행 시간 로깅 + log.debug("Query execution time: {} ms", (endTime - startTime)); + + return result; + } + +private String getActualSql(BoundSql boundSql, Object parameter) { + String sql = boundSql.getSql(); + + if (parameter == null) { + return sql; + } + + List parameterMappings = boundSql.getParameterMappings(); + + if (parameterMappings.isEmpty()) { + return sql; + } + + // SQL 복사본 생성 + String actualSql = sql; + + try { + // 파라미터 값 추출 + for (ParameterMapping parameterMapping : parameterMappings) { + String propertyName = parameterMapping.getProperty(); + Object value = null; + + if (boundSql.hasAdditionalParameter(propertyName)) { + value = boundSql.getAdditionalParameter(propertyName); + } else if (parameter instanceof Map) { + value = ((Map) parameter).get(propertyName); + } else if (parameter instanceof String) { + // String 타입 파라미터 처리 + value = parameter; + } else if (parameter instanceof Number) { + // Number 타입 파라미터 처리 (Integer, Long, Double 등) + value = parameter; + } else if (parameter instanceof Boolean) { + // Boolean 타입 파라미터 처리 + value = parameter; + } else { + value = getParameterValue(parameter, propertyName); + } + + String valueStr = value != null ? value.toString() : "null"; + // SQL 인젝션 방지를 위한 문자열 이스케이프 처리 + valueStr = valueStr.replace("'", "''"); + + // 다양한 데이터 타입에 대한 처리 + if (value instanceof String) { + // 문자열 타입은 따옴표로 감싸기 + actualSql = actualSql.replaceFirst("\\?", "'" + valueStr + "'"); + } else if (value instanceof Number) { + // 숫자 타입 (Integer, Long, Double 등)은 그대로 사용 + actualSql = actualSql.replaceFirst("\\?", valueStr); + } else if (value instanceof Boolean) { + // Boolean 타입은 그대로 사용 + actualSql = actualSql.replaceFirst("\\?", valueStr); + } else if (value instanceof java.util.Date) { + // 표준 타임스탬프 포맷 사용 + java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + actualSql = actualSql.replaceFirst("\\?", "'" + sdf.format(value) + "'"); + } else if (value instanceof java.time.LocalDateTime) { + // Java 8 LocalDateTime 처리 + actualSql = actualSql.replaceFirst("\\?", "'" + value.toString().replace('T', ' ') + "'"); + } else { + // 기타 타입은 null이 아닌 경우 따옴표로 감싸기 + actualSql = actualSql.replaceFirst("\\?", value != null ? "'" + valueStr + "'" : valueStr); + } + } + } catch (Exception e) { + log.warn("Failed to get actual SQL with parameters", e); + return sql; + } + + return actualSql; +} + +private Object getParameterValue(Object parameter, String propertyName) { + try { + // 현재 클래스부터 상위 클래스까지 순회하면서 필드 찾기 + Class currentClass = parameter.getClass(); + while (currentClass != null) { + try { + Field field = currentClass.getDeclaredField(propertyName); + field.setAccessible(true); + return field.get(parameter); + } catch (NoSuchFieldException e) { + // 현재 클래스에서 필드를 찾지 못하면 상위 클래스로 이동 + currentClass = currentClass.getSuperclass(); + } + } + throw new NoSuchFieldException(propertyName); + } catch (Exception e) { + log.warn("필드 값 추출 실패: {} ({})", propertyName, e.getMessage()); + return null; + } +} + + private Map extractDetailedParameters(Object parameter, List parameterMappings) { + Map paramMap = new HashMap<>(); + + if (parameter == null) { + return paramMap; + } + + if (parameter instanceof Map) { + paramMap.putAll((Map) parameter); + } else { + // 객체의 필드 정보도 추출 + paramMap.put("param", parameter); + extractObjectFields(parameter, paramMap); + } + + return paramMap; + } + + private void extractObjectFields(Object obj, Map paramMap) { + try { + Class currentClass = obj.getClass(); + while (currentClass != null) { + Field[] fields = currentClass.getDeclaredFields(); + for (Field field : fields) { + try { + // String 타입의 객체는 건너뛰기 + if (obj instanceof String) { + continue; + } + + // 시스템 클래스의 필드는 건너뛰기 + if (field.getDeclaringClass().getName().startsWith("java.")) { + continue; + } + + field.setAccessible(true); + Object value = field.get(obj); + paramMap.put(field.getName(), value); + + } catch (IllegalAccessException | SecurityException e) { + // 개별 필드 접근 실패는 무시하고 계속 진행 + log.debug("필드 접근 실패: {} ({})", field.getName(), e.getMessage()); + } + } + currentClass = currentClass.getSuperclass(); + } + } catch (Exception e) { + log.warn("객체 필드 추출 실패", e); + } + } + + private void logDetailedQueryInfo(String mapperMethod, String originalSql, String actualSql, Map paramMap) { + StringBuilder logMessage = new StringBuilder(); + logMessage.append("\n"); + logMessage.append("┌─────────────── MyBatis Query Details ───────────────\n"); + logMessage.append("│ Mapper Method: ").append(mapperMethod).append("\n"); + logMessage.append("│ Parameters: ").append(paramMap).append("\n"); + + // 원본 SQL 포맷팅, prd, 운영 + //logMessage.append("│ Original SQL:\n"); + //formatSqlInLog(logMessage, originalSql); + + // 실제 실행 SQL 포맷팅, local, dev 로컬 개발 + logMessage.append("│ Actual SQL:\n"); + formatSqlInLog(logMessage, actualSql); + + logMessage.append("└──────────────────────────────────────────────────────"); + + log.info(logMessage.toString()); + } + + private void formatSqlInLog(StringBuilder logMessage, String sql) { + // SQL 키워드 하이라이트 및 들여쓰기 + String formattedSql = formatSql(sql); + String[] lines = formattedSql.split("\n"); + + for (String line : lines) { + //logMessage.append("│ ").append(line).append("\n"); + logMessage.append(line).append("\n"); + } + } + +private String formatSql(String sql) { + try { + // SQL 파서를 사용한 포맷팅 + Statement statement = CCJSqlParserUtil.parse(sql); + return formatStatement(statement, 0); + } catch (JSQLParserException e) { + log.debug("SQL 파싱 실패. 기본 포맷팅으로 대체합니다.", e); + log.info("SQL 파싱 실패. 기본 포맷팅으로 대체합니다."); + // 파싱 실패 시 기본 포맷팅 사용 + return sql.replaceAll("\\s+", " ").trim(); + } +} + +private String formatStatement(Statement statement, int indent) { + StringBuilder result = new StringBuilder(); + String indentation = String.join("", java.util.Collections.nCopies(indent, " ")); + + if (statement instanceof Select) { + formatSelect((Select) statement, result, indent); + } else if (statement instanceof Insert) { + formatInsert((Insert) statement, result, indent); + } else if (statement instanceof Update) { + formatUpdate((Update) statement, result, indent); + } else if (statement instanceof Delete) { + formatDelete((Delete) statement, result, indent); + } + + return result.toString(); +} + +private void formatSelect(Select select, StringBuilder result, int indent) { + String indentation = String.join("", Collections.nCopies(indent, " ")); + PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); + + // SELECT 절 + result.append(indentation).append("SELECT\n"); + formatSelectItems(plainSelect.getSelectItems(), result, indent + 2); + + // FROM 절 + if (plainSelect.getFromItem() != null) { + result.append(indentation).append("FROM\n"); + result.append(indentation).append(" ").append(plainSelect.getFromItem()).append("\n"); + } + + // JOIN 절 + if (plainSelect.getJoins() != null) { + for (Join join : plainSelect.getJoins()) { + result.append(indentation).append(join.isLeft() ? "LEFT JOIN " : "JOIN ") + .append(join.getRightItem()).append("\n"); + if (join.getOnExpression() != null) { + result.append(indentation).append(" ON ").append(join.getOnExpression()).append("\n"); + } + } + } + + // WHERE 절 + if (plainSelect.getWhere() != null) { + result.append(indentation).append("WHERE\n"); + result.append(indentation).append(" ").append(plainSelect.getWhere()).append("\n"); + } + + // GROUP BY 절 + if (plainSelect.getGroupBy() != null) { + result.append(indentation).append("GROUP BY\n"); + result.append(indentation).append(" ") + .append(plainSelect.getGroupBy().getGroupByExpressions().stream() + .map(Object::toString) + .collect(Collectors.joining(", "))) + .append("\n"); + } + + // HAVING 절 + if (plainSelect.getHaving() != null) { + result.append(indentation).append("HAVING\n"); + result.append(indentation).append(" ").append(plainSelect.getHaving()).append("\n"); + } + + // ORDER BY 절 + if (plainSelect.getOrderByElements() != null) { + result.append(indentation).append("ORDER BY\n"); + result.append(indentation).append(" ") + .append(plainSelect.getOrderByElements().stream() + .map(Object::toString) + .collect(Collectors.joining(", "))) + .append("\n"); + } +} + +private void formatSelectItems(List items, StringBuilder result, int indent) { + String indentation = String.join("", Collections.nCopies(indent, " ")); + for (int i = 0; i < items.size(); i++) { + result.append(indentation).append(items.get(i)); + if (i < items.size() - 1) { + result.append(","); + } + result.append("\n"); + } +} + +private void formatInsert(Insert insert, StringBuilder result, int indent) { + String indentation = String.join("", Collections.nCopies(indent, " ")); + result.append(indentation).append("INSERT INTO ").append(insert.getTable()).append("\n"); + + // 컬럼 목록 + if (insert.getColumns() != null) { + result.append(indentation).append("(") + .append(insert.getColumns().stream() + .map(Column::getColumnName) + .collect(Collectors.joining(", "))) + .append(")\n"); + } + + result.append(indentation).append("VALUES\n"); + // VALUES 절 포맷팅 + if (insert.getItemsList() != null) { + result.append(indentation).append(" ").append(insert.getItemsList()).append("\n"); + } +} + +private void formatUpdate(Update update, StringBuilder result, int indent) { + String indentation = String.join("", Collections.nCopies(indent, " ")); + + // UPDATE 절 + result.append(indentation).append("UPDATE\n"); + result.append(indentation).append(" ").append(update.getTable()).append("\n"); + + // SET 절 + result.append(indentation).append("SET\n"); + List updateSets = update.getUpdateSets(); + for (int i = 0; i < updateSets.size(); i++) { + UpdateSet updateSet = updateSets.get(i); + result.append(indentation).append(" ") + .append(updateSet.getColumns().get(0)) + .append(" = ") + .append(updateSet.getExpressions().get(0)); + + if (i < updateSets.size() - 1) { + result.append(","); + } + result.append("\n"); + } + + // WHERE 절 + if (update.getWhere() != null) { + result.append(indentation).append("WHERE\n"); + result.append(indentation).append(" ").append(update.getWhere()).append("\n"); + } + + // ORDER BY 절 (일부 데이터베이스에서 지원) + if (update.getOrderByElements() != null) { + result.append(indentation).append("ORDER BY\n"); + result.append(indentation).append(" ") + .append(update.getOrderByElements().stream() + .map(Object::toString) + .collect(Collectors.joining(", "))) + .append("\n"); + } + + // LIMIT 절 (일부 데이터베이스에서 지원) + if (update.getLimit() != null) { + result.append(indentation).append("LIMIT ") + .append(update.getLimit()).append("\n"); + } +} + +private void formatDelete(Delete delete, StringBuilder result, int indent) { + String indentation = String.join("", Collections.nCopies(indent, " ")); + + // DELETE 절 + result.append(indentation).append("DELETE FROM\n"); + result.append(indentation).append(" ").append(delete.getTable()).append("\n"); + + // WHERE 절 + if (delete.getWhere() != null) { + result.append(indentation).append("WHERE\n"); + result.append(indentation).append(" ").append(delete.getWhere()).append("\n"); + } + + // ORDER BY 절 (일부 데이터베이스에서 지원) + if (delete.getOrderByElements() != null) { + result.append(indentation).append("ORDER BY\n"); + result.append(indentation).append(" ") + .append(delete.getOrderByElements().stream() + .map(Object::toString) + .collect(Collectors.joining(", "))) + .append("\n"); + } + + // LIMIT 절 (일부 데이터베이스에서 지원) + if (delete.getLimit() != null) { + result.append(indentation).append("LIMIT ") + .append(delete.getLimit()).append("\n"); + } +} +} \ No newline at end of file diff --git a/src/main/java/egovframework/config/SwaggerConfig.java b/src/main/java/egovframework/config/SwaggerConfig.java new file mode 100644 index 0000000..b9163a5 --- /dev/null +++ b/src/main/java/egovframework/config/SwaggerConfig.java @@ -0,0 +1,73 @@ +package egovframework.config; + +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.info.License; +import org.springdoc.core.GroupedOpenApi; +import org.springdoc.core.SpringDocUtils; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.bind.annotation.RequestMapping; + +/** + * Swagger UI 설정 클래스 + * OpenAPI 3.0 스펙을 기반으로 API 문서화를 위한 설정을 제공합니다. + * 세션 기반 인증을 사용하므로 별도의 보안 스키마를 설정하지 않습니다. + * @Controller 어노테이션이 있는 클래스도 스캔하도록 설정합니다. + */ +@Configuration +public class SwaggerConfig { + + static { + // @Controller 어노테이션이 있는 클래스도 스캔하도록 설정 + SpringDocUtils.getConfig().addAnnotationsToIgnore(RequestMapping.class); + } + + /** + * 모든 API를 문서화하는 GroupedOpenApi 빈을 정의합니다. + * + * @return GroupedOpenApi 설정 객체 + */ + @Bean + public GroupedOpenApi allApi() { + return GroupedOpenApi.builder() + .group("All") + .packagesToScan("go.kr.project") + .pathsToMatch("/**") + .build(); + } + + /** + * 로그인 관련 API를 문서화하는 GroupedOpenApi 빈을 정의합니다. + * + * @return GroupedOpenApi 설정 객체 + */ + @Bean + public GroupedOpenApi loginApi() { + return GroupedOpenApi.builder() + .group("Login") + .packagesToScan("go.kr.project.login") + .pathsToMatch("/login/**") + .build(); + } + + /** + * OpenAPI 설정을 정의합니다. + * API 문서의 기본 정보(제목, 설명, 버전 등)를 설정합니다. + * + * @return OpenAPI 설정 객체 + */ + @Bean + public OpenAPI openAPI() { + Info info = new Info() + .title("XIT Framework API") + .description("XIT Framework API 문서 - 세션 기반 인증 사용") + .version("v1.0.0") + .license(new License().name("Apache 2.0").url("http://springdoc.org")); + + return new OpenAPI() + .components(new Components()) + .info(info); + } +} diff --git a/src/main/java/egovframework/configProperties/FileUploadProperties.java b/src/main/java/egovframework/configProperties/FileUploadProperties.java new file mode 100644 index 0000000..ea14d6c --- /dev/null +++ b/src/main/java/egovframework/configProperties/FileUploadProperties.java @@ -0,0 +1,47 @@ +package egovframework.configProperties; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +import java.util.Map; + +/** + * packageName : egovframework.config + * fileName : FileUploadProperties + * author : 시스템 관리자 + * date : 25. 5. 23. + * description : 파일 업로드 관련 설정 속성 + * =========================================================== + * DATE AUTHOR NOTE + * ----------------------------------------------------------- + * 25. 5. 23. 시스템 관리자 최초 생성 + */ +@Setter +@Getter +@Configuration +@ConfigurationProperties(prefix = "file.upload") +public class FileUploadProperties { + + /** 파일 저장 기본 경로 */ + private String path; + + /** 최대 파일 크기 (단일 파일) - 기본값 10MB */ + private long maxSize; + + /** 최대 총 파일 크기 - 기본값 50MB */ + private long maxTotalSize; + + /** 허용된 파일 확장자 */ + private String allowedExtensions; + + /** 최대 파일 개수 - 기본값 10개 */ + private int maxFiles; + + /** 실제 파일 삭제 여부 - 기본값 true */ + private boolean realFileDelete; + + /** 하위 디렉토리 설정 */ + private Map subDirs; +} \ No newline at end of file diff --git a/src/main/java/egovframework/configProperties/InterceptorProperties.java b/src/main/java/egovframework/configProperties/InterceptorProperties.java new file mode 100644 index 0000000..402cdb3 --- /dev/null +++ b/src/main/java/egovframework/configProperties/InterceptorProperties.java @@ -0,0 +1,31 @@ +package egovframework.configProperties; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +import java.util.List; + +/** + * packageName : egovframework.config + * fileName : InterceptorProperties + * author : 박성영 + * date : 25. 5. 19. + * description : + * =========================================================== + * DATE AUTHOR NOTE + * ----------------------------------------------------------- + * 25. 5. 19. 박성영 최초 생성 + */ +@Setter +@Getter +@Configuration +@ConfigurationProperties(prefix = "interceptor") +public class InterceptorProperties { + + private List interceptorExclude; + private List refererExclude; + +} + diff --git a/src/main/java/egovframework/configProperties/LoginProperties.java b/src/main/java/egovframework/configProperties/LoginProperties.java new file mode 100644 index 0000000..8e66a4d --- /dev/null +++ b/src/main/java/egovframework/configProperties/LoginProperties.java @@ -0,0 +1,35 @@ +package egovframework.configProperties; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +/** + * packageName : egovframework.config + * fileName : LoginProperties + * author : 박성영 + * date : 25. 5. 19. + * description : 로그인 관련 설정 속성 + * =========================================================== + * DATE AUTHOR NOTE + * ----------------------------------------------------------- + * 25. 5. 19. 박성영 최초 생성 + * 25. 5. 22. 시스템 관리자 동시 접속 가능 여부 속성 추가 + */ +@Setter +@Getter +@Configuration +@ConfigurationProperties(prefix = "login") +public class LoginProperties { + + private String url; // 로그인 페이지 URL + private Lock lock; // 로그인 잠금 설정 + private boolean allowMultipleLogin = true; // 동시 접속 가능 여부 (기본값: true) + + @Setter + @Getter + public static class Lock { + private int count; // 비밀번호 잠김 최종 카운트 + } +} diff --git a/src/main/java/egovframework/constant/BatchConstants.java b/src/main/java/egovframework/constant/BatchConstants.java new file mode 100644 index 0000000..41015d5 --- /dev/null +++ b/src/main/java/egovframework/constant/BatchConstants.java @@ -0,0 +1,113 @@ +package egovframework.constant; + +/** + * packageName : go.kr.project.common.constant + * fileName : BatchConstants + * author : 개발자 + * date : 2025-06-10 + * description : 배치 작업 관련 상수를 정의하는 클래스 + * =========================================================== + * DATE AUTHOR NOTE + * ----------------------------------------------------------- + * 2025-06-10 개발자 최초 생성 + */ +public class BatchConstants { + + /** + * 배치 작업 정보 상태 코드 (TB_BATCH_JOB_INFO.STATUS) + */ + public static final String JOB_INFO_STATUS_ACTIVE = "ACTIVE"; + public static final String JOB_INFO_STATUS_PAUSED = "PAUSED"; + public static final String JOB_INFO_STATUS_DELETED = "DELETED"; + + /** + * 배치 작업 실행 상태 코드 (TB_BATCH_JOB_EXECUTION.STATUS) + * 실행상태에서 부분완료는 제거됨 - 시작,완료,실패,거부만 사용 + */ + public static final String JOB_EXECUTION_STATUS_STARTED = "STARTED"; + public static final String JOB_EXECUTION_STATUS_COMPLETED = "COMPLETED"; + public static final String JOB_EXECUTION_STATUS_FAILED = "FAILED"; + public static final String JOB_EXECUTION_STATUS_VETOED = "VETOED"; + + /** + * 배치 작업 실행 종료 코드 (TB_BATCH_JOB_EXECUTION.EXIT_CODE) + */ + public static final String JOB_EXECUTION_EXIT_COMPLETED = "COMPLETED"; + public static final String JOB_EXECUTION_EXIT_FAILED = "FAILED"; + public static final String JOB_EXECUTION_EXIT_UNKNOWN = "UNKNOWN"; + public static final String JOB_EXECUTION_EXIT_PARTIALLY_COMPLETED = "PARTIALLY_COMPLETED"; + + + /** + * 로그 레벨 + */ + public static final String LOG_LEVEL_INFO = "INFO"; + public static final String LOG_LEVEL_WARN = "WARN"; + public static final String LOG_LEVEL_ERROR = "ERROR"; + + /** + * 날짜 및 시간 형식 + */ + public static final String DATE_FORMAT_DEFAULT = "yyyy-MM-dd HH:mm:ss"; + + /** + * 시간 값 (밀리초) + */ + public static final long TIME_ITEM_PROCESS_DELAY = 100; // 0.1초 + public static final long TIME_JOB_DELAY_TEST = 3000; // 3초 (테스트용) + public static final long TIME_JOB_DELAY_1_MIN = 60000; // 1분 + public static final long TIME_JOB_DELAY_3_MIN = 180000; // 3분 + public static final long TIME_JOB_DELAY_5_MIN = 300000; // 5분 + + /** + * JobDataMap 키 + */ + public static final String JOB_DATA_EXECUTION_ID = "executionId"; + + /** + * 작업 이름 및 그룹 + */ + public static final String JOB_NAME_SAMPLE = "sampleJob"; + public static final String JOB_GROUP_SAMPLE = "sampleGroup"; + public static final String JOB_TRIGGER_SUFFIX = "Trigger"; + + /** + * Cron 표현식 + */ + public static final String CRON_EVERY_MINUTE = "0 * * * * ?"; + public static final String CRON_EVERY_ONE_SECOND = "1 0 * * * ?"; + + /** + * 작업 설명 + */ + public static final String JOB_DESC_SAMPLE = "샘플 배치 작업 (매 분마다 실행)"; + + /** + * 배치 로그 메시지 템플릿 + */ + // 기본 배치 작업 로그 메시지 + public static final String LOG_MSG_BATCH_JOB_START = "===== 배치 작업 시작: %s.%s - %s ====="; + public static final String LOG_MSG_BATCH_JOB_COMPLETE = "===== 배치 작업 완료: %s.%s - %s ====="; + public static final String LOG_MSG_BATCH_JOB_PARTIAL_COMPLETE = "===== 배치 작업 부분 완료: %s.%s - 총 %d개 파일 중 처리: %d개, 성공: %d개, 실패: %d개 (%s) ====="; + public static final String LOG_MSG_BATCH_JOB_FAILED = "===== 배치 작업 실패: %s.%s - 총 %d개 파일 중 처리: %d개, 성공: %d개, 실패: %d개 (%s) ====="; + public static final String LOG_MSG_BATCH_JOB_ERROR = "===== 배치 작업 실행 중 오류 발생: %s.%s - %s ====="; + + /** + * 배치 작업 초기화 관련 메시지 + */ + public static final String LOG_MSG_BATCH_INIT_SERVER_BOOT_DETECTED = "서버 부팅 완료 감지 - 배치 작업 초기화를 5초 후에 시작합니다."; + public static final String LOG_MSG_BATCH_INIT_START = "배치 작업 초기화 시작"; + public static final String LOG_MSG_BATCH_INIT_COMPLETE = "배치 작업 초기화 완료"; + public static final String LOG_MSG_BATCH_INIT_INTERRUPT_ERROR = "배치 작업 초기화 대기 중 인터럽트 발생: {}"; + public static final String LOG_MSG_BATCH_INIT_ERROR = "배치 작업 초기화 중 오류 발생: %s"; + public static final String LOG_MSG_BATCH_INIT_NO_JOBS = "등록된 배치 작업이 없습니다."; + public static final String LOG_MSG_BATCH_INIT_JOBS_COUNT = "총 {}개의 배치 작업 정보를 조회했습니다."; + public static final String LOG_MSG_BATCH_INIT_SKIP_DELETED = "삭제된 작업은 스케줄러에 등록하지 않습니다: {}.{}"; + public static final String LOG_MSG_BATCH_INIT_SCHEDULE_SUCCESS = "배치 작업을 스케줄러에 등록했습니다: {}.{}"; + public static final String LOG_MSG_BATCH_INIT_SCHEDULE_FAILED = "배치 작업 스케줄링에 실패했습니다: {}.{}"; + public static final String LOG_MSG_BATCH_INIT_CLASS_NOT_FOUND = "배치 작업 클래스를 찾을 수 없습니다: {}"; + public static final String LOG_MSG_BATCH_INIT_SCHEDULE_ERROR = "배치 작업 스케줄링 중 오류 발생: {}.{} - {}"; + public static final String LOG_MSG_BATCH_INIT_DB_ERROR = "배치 작업 초기화 중 오류 발생: {}"; + public static final String LOG_MSG_BATCH_INIT_REGISTER_COMPLETE = "배치 작업 정보 등록 완료: %s"; + public static final String LOG_MSG_BATCH_INIT_REGISTER_ERROR = "샘플 배치 작업 등록 중 오류 발생: %s"; +} diff --git a/src/main/java/egovframework/constant/FileContentTypeConstants.java b/src/main/java/egovframework/constant/FileContentTypeConstants.java new file mode 100644 index 0000000..d8e650d --- /dev/null +++ b/src/main/java/egovframework/constant/FileContentTypeConstants.java @@ -0,0 +1,65 @@ +package egovframework.constant; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * packageName : go.kr.project.common.constant + * fileName : FileContentTypeConstants + * author : 개발자 + * date : 2025-05-17 + * description : 파일 확장자에 따른 MIME 타입을 정의하는 상수 클래스 + * =========================================================== + * DATE AUTHOR NOTE + * ----------------------------------------------------------- + * 2025-05-17 개발자 최초 생성 + */ +public class FileContentTypeConstants { + + /** + * 기본 MIME 타입 (알 수 없는 파일 형식) + */ + public static final String DEFAULT_CONTENT_TYPE = "application/octet-stream"; + + /** + * 파일 확장자와 MIME 타입 매핑 + */ + private static final Map CONTENT_TYPE_MAP; + + static { + Map map = new HashMap<>(); + + // 이미지 파일 + map.put("jpg", "image/jpeg"); + map.put("jpeg", "image/jpeg"); + map.put("png", "image/png"); + map.put("gif", "image/gif"); + + // 문서 파일 + map.put("pdf", "application/pdf"); + map.put("doc", "application/msword"); + map.put("docx", "application/msword"); + map.put("xls", "application/vnd.ms-excel"); + map.put("xlsx", "application/vnd.ms-excel"); + map.put("ppt", "application/vnd.ms-powerpoint"); + map.put("pptx", "application/vnd.ms-powerpoint"); + map.put("txt", "text/plain"); + + CONTENT_TYPE_MAP = Collections.unmodifiableMap(map); + } + + /** + * 파일 확장자에 따른 MIME 타입을 반환합니다. + * + * @param fileExt 파일 확장자 (소문자) + * @return MIME 타입 + */ + public static String getContentType(String fileExt) { + if (fileExt == null || fileExt.isEmpty()) { + return DEFAULT_CONTENT_TYPE; + } + + return CONTENT_TYPE_MAP.getOrDefault(fileExt.toLowerCase(), DEFAULT_CONTENT_TYPE); + } +} \ No newline at end of file diff --git a/src/main/java/egovframework/constant/MessageConstants.java b/src/main/java/egovframework/constant/MessageConstants.java new file mode 100644 index 0000000..4e63368 --- /dev/null +++ b/src/main/java/egovframework/constant/MessageConstants.java @@ -0,0 +1,205 @@ +package egovframework.constant; + +/** + * packageName : go.kr.project.common.constant + * fileName : MessageConstants + * author : 개발자 + * date : 2025-05-22 + * description : 시스템에서 사용되는 메시지 상수를 정의하는 클래스 + * =========================================================== + * DATE AUTHOR NOTE + * ----------------------------------------------------------- + * 2025-05-22 개발자 최초 생성 + */ +public class MessageConstants { + + /** + * 로그인 관련 메시지 + */ + public static class Login { + // 성공 메시지 + public static final String SUCCESS = "로그인에 성공하였습니다."; + + // 오류 메시지 + public static final String ERROR_INVALID_CREDENTIALS = "아이디 또는 비밀번호가 일치하지 않습니다."; + public static final String ERROR_INACTIVE_ACCOUNT = "비활성화 계정입니다. 관리자에 문의하세요."; + public static final String ERROR_PROCESS = "로그인 처리 중 오류가 발생했습니다."; + } + + /** + * 로그인 실패 관련 메시지 + */ + public static class LoginFailure { + public static final String ABNORMAL_PATTERN_PREFIX = "비정상적인 로그인 패턴: "; + public static final String USER_NOT_FOUND = "사용자 계정 없음"; + public static final String ACCOUNT_LOCKED = "계정 잠김"; + public static final String PASSWORD_MISMATCH = "비밀번호 불일치"; + public static final String ACCOUNT_INACTIVE = "계정 비활성화"; + + public static final String MESSAGE_ACCOUNT_LOCKED = "사용자 계정이 잠겼습니다."; + } + + /** + * 로그아웃 관련 메시지 + */ + public static class Logout { + // 성공 메시지 + public static final String SUCCESS = "로그아웃 되었습니다."; + + // 오류 메시지 + public static final String ERROR_PROCESS = "로그아웃 처리 중 오류가 발생했습니다."; + } + + /** + * 계정 관련 메시지 + */ + public static class Account { + // 계정 잠금 관련 메시지 + public static final String UNLOCK_SUCCESS = "계정 잠금이 해제되었습니다."; + public static final String UNLOCK_FAIL = "계정 잠금 해제에 실패했습니다."; + public static final String UNLOCK_ERROR = "계정 잠금 해제 중 오류가 발생했습니다."; + } + + /** + * 로그인 로그 관련 메시지 + */ + public static class LoginLog { + // 목록 조회 관련 메시지 + public static final String LIST_SUCCESS = "로그인 로그 목록을 조회하였습니다."; + public static final String LIST_ERROR = "로그인 로그 목록 조회 중 오류가 발생했습니다."; + + // 상세 조회 관련 메시지 + public static final String DETAIL_SUCCESS = "로그인 로그 상세 정보를 조회하였습니다."; + public static final String DETAIL_ERROR = "로그인 로그 상세 조회 중 오류가 발생했습니다."; + public static final String NOT_EXIST = "존재하지 않는 로그입니다."; + } + + /** + * 통계 관련 메시지 + */ + public static class Stats { + // 디바이스 유형별 통계 + public static final String DEVICE_SUCCESS = "디바이스 유형별 로그인 통계를 조회하였습니다."; + public static final String DEVICE_ERROR = "디바이스 유형별 로그인 통계 조회 중 오류가 발생했습니다."; + + // 성공/실패 비율 통계 + public static final String SUCCESS_FAIL_SUCCESS = "로그인 성공/실패 비율 통계를 조회하였습니다."; + public static final String SUCCESS_FAIL_ERROR = "로그인 성공/실패 비율 통계 조회 중 오류가 발생했습니다."; + + // 시간대별 통계 + public static final String HOURLY_SUCCESS = "시간대별 로그인 시도 통계를 조회하였습니다."; + public static final String HOURLY_ERROR = "시간대별 로그인 시도 통계 조회 중 오류가 발생했습니다."; + } + + /** + * 권한 관련 메시지 + */ + public static class Auth { + // 메뉴-역할 관련 메시지 + public static final String MENU_ROLE_ASSIGN_SUCCESS = "메뉴가 역할에 성공적으로 할당되었습니다."; + public static final String MENU_ROLE_REMOVE_SUCCESS = "메뉴가 역할에서 성공적으로 제거되었습니다."; + + // 역할-그룹 관련 메시지 + public static final String ROLE_GROUP_ASSIGN_SUCCESS = "역할이 그룹에 성공적으로 할당되었습니다."; + public static final String ROLE_GROUP_REMOVE_SUCCESS = "역할이 그룹에서 성공적으로 제거되었습니다."; + } + + /** + * 코드 관련 메시지 + */ + public static class Code { + // 코드 상세 관련 메시지 + public static final String DETAIL_SAVE_SUCCESS = "코드 상세 정보가 성공적으로 저장되었습니다."; + + // 그룹코드 관련 메시지 + public static final String GROUP_SAVE_SUCCESS = "그룹코드 정보가 성공적으로 저장되었습니다."; + } + + /** + * 그룹 관련 메시지 + */ + public static class Group { + // 그룹 저장 관련 메시지 + public static final String SAVE_SUCCESS = "그룹이 성공적으로 저장되었습니다."; + } + + /** + * 메뉴 관련 메시지 + */ + public static class Menu { + // 성공 메시지 + public static final String REGISTER_SUCCESS = "메뉴가 성공적으로 등록되었습니다."; + public static final String EDIT_SUCCESS = "메뉴 정보가 성공적으로 수정되었습니다."; + public static final String DELETE_SUCCESS = "메뉴가 성공적으로 삭제되었습니다."; + + // 오류 메시지 + public static final String ROOT_REGISTER_ERROR = "ROOT 메뉴 ID로 새 메뉴를 등록할 수 없습니다."; + public static final String UPPER_MENU_REQUIRED = "상위 메뉴를 선택해주세요."; + public static final String REGISTER_ERROR = "메뉴 등록에 실패했습니다."; + public static final String ROOT_EDIT_ERROR = "ROOT 메뉴는 수정할 수 없습니다."; + public static final String EDIT_ERROR = "메뉴 정보 수정에 실패했습니다."; + public static final String ROOT_DELETE_ERROR = "ROOT 메뉴는 삭제할 수 없습니다."; + public static final String CHILD_EXISTS_ERROR = "하위 메뉴가 존재하여 삭제할 수 없습니다."; + public static final String DELETE_ERROR = "메뉴 삭제에 실패했습니다."; + } + + /** + * 역할 관련 메시지 + */ + public static class Role { + // 성공 메시지 + public static final String SAVE_SUCCESS = "역할이 성공적으로 저장되었습니다."; + + // 오류 메시지 + public static final String SAVE_ERROR = "역할 저장에 실패했습니다."; + } + + /** + * 사용자 관련 메시지 + */ + public static class User { + // 성공 메시지 + public static final String REGISTER_SUCCESS = "사용자가 성공적으로 등록되었습니다."; + public static final String EDIT_SUCCESS = "사용자 정보가 성공적으로 수정되었습니다."; + public static final String PASSWORD_RESET_SUCCESS = "비밀번호가 성공적으로 초기화되었습니다."; + public static final String UNLOCK_SUCCESS = "사용자 잠금이 성공적으로 해제되었습니다."; + + // 오류 메시지 + public static final String REGISTER_ERROR = "사용자 등록에 실패했습니다."; + public static final String EDIT_ERROR = "사용자 정보 수정에 실패했습니다."; + public static final String NOT_FOUND = "사용자 정보를 찾을 수 없습니다."; + public static final String PASSWORD_RESET_ERROR = "비밀번호 초기화에 실패했습니다."; + public static final String UNLOCK_ERROR = "사용자 잠금 해제에 실패했습니다."; + } + + /** + * 사용자 그룹 관련 메시지 + */ + public static class UserGroup { + // 성공 메시지 + public static final String GROUP_LIST_SUCCESS = "그룹 목록을 성공적으로 조회했습니다."; + public static final String ROLE_LIST_SUCCESS = "역할 목록을 성공적으로 조회했습니다."; + public static final String MENU_LIST_SUCCESS = "메뉴 목록을 성공적으로 조회했습니다."; + + // 오류 메시지 + public static final String GROUP_LIST_ERROR = "그룹 목록 조회 중 오류가 발생했습니다: "; + public static final String ROLE_LIST_ERROR = "역할 목록 조회 중 오류가 발생했습니다: "; + public static final String MENU_LIST_ERROR = "메뉴 목록 조회 중 오류가 발생했습니다: "; + } + + /** + * 공통 메시지 + */ + public static class Common { + // 성공 메시지 + public static final String SAVE_SUCCESS = "저장되었습니다."; + public static final String UPDATE_SUCCESS = "수정되었습니다."; + public static final String DELETE_SUCCESS = "삭제되었습니다."; + + // 오류 메시지 + public static final String SAVE_ERROR = "저장 중 오류가 발생했습니다."; + public static final String UPDATE_ERROR = "수정 중 오류가 발생했습니다."; + public static final String DELETE_ERROR = "삭제 중 오류가 발생했습니다."; + public static final String SYSTEM_ERROR = "시스템 오류가 발생했습니다."; + } +} diff --git a/src/main/java/egovframework/constant/SessionConstants.java b/src/main/java/egovframework/constant/SessionConstants.java new file mode 100644 index 0000000..5b213da --- /dev/null +++ b/src/main/java/egovframework/constant/SessionConstants.java @@ -0,0 +1,39 @@ +package egovframework.constant; + +/** + * packageName : go.kr.project.common.constant + * fileName : SessionConstants + * author : 개발자 + * date : 2025-05-22 + * description : 세션 관련 상수를 정의하는 클래스 + * =========================================================== + * DATE AUTHOR NOTE + * ----------------------------------------------------------- + * 2025-05-22 개발자 최초 생성 + */ +public class SessionConstants { + + /** + * 세션 키 + * 세션에서 SessionVO 객체를 가져오는 데 사용되는 키 + */ + public static final String SESSION_KEY = "sessionVO"; + + /** + * 방문자 역할 ID + * 방문자 권한을 나타내는 역할 ID + */ + public static final String VISITOR_ROLE_ID = "ROLE_VISITOR"; + + /** + * 세션 만료 에러 코드 + * 세션이 만료되었을 때 사용되는 에러 코드 + */ + public static final String SESSION_EXPIRED = "SESSION_EXPIRED"; + + /** + * 잘못된 접근 에러 코드 + * Referer 헤더가 없는 등 비정상적인 접근 시도에 대한 에러 코드 + */ + public static final String INVALID_ACCESS = "INVALID_ACCESS"; +} \ No newline at end of file diff --git a/src/main/java/egovframework/constant/TilesConstants.java b/src/main/java/egovframework/constant/TilesConstants.java new file mode 100644 index 0000000..6574c33 --- /dev/null +++ b/src/main/java/egovframework/constant/TilesConstants.java @@ -0,0 +1,23 @@ +package egovframework.constant; + +/** + * packageName : go.kr.project.common.constant + * fileName : TilesConstants + * author : 개발자 + * date : 2025-05-22 + * description : 타일즈 관련 상수를 정의하는 클래스 + * =========================================================== + * DATE AUTHOR NOTE + * ----------------------------------------------------------- + * 2025-05-22 개발자 최초 생성 + */ +public class TilesConstants { + + public static final String LOGIN = ".login"; + + public static final String BASE = ".base"; + + public static final String INNER = ".inner"; + + public static final String POPUP = ".popup"; +} \ No newline at end of file diff --git a/src/main/java/egovframework/exception/AccessDeniedException.java b/src/main/java/egovframework/exception/AccessDeniedException.java new file mode 100644 index 0000000..0089846 --- /dev/null +++ b/src/main/java/egovframework/exception/AccessDeniedException.java @@ -0,0 +1,23 @@ +package egovframework.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * 접근 권한이 없는 경우 발생하는 예외 + */ +@Getter +@ResponseStatus(HttpStatus.FORBIDDEN) +public class AccessDeniedException extends RuntimeException { + + private final String userAcnt; + private final String requestURI; + + public AccessDeniedException(String userAcnt, String requestURI) { + super("접근 권한이 없습니다."); + this.userAcnt = userAcnt; + this.requestURI = requestURI; + } + +} \ No newline at end of file diff --git a/src/main/java/egovframework/exception/EgovAopExceptionTransfer.java b/src/main/java/egovframework/exception/EgovAopExceptionTransfer.java new file mode 100644 index 0000000..10ea9c3 --- /dev/null +++ b/src/main/java/egovframework/exception/EgovAopExceptionTransfer.java @@ -0,0 +1,21 @@ +package egovframework.exception; + +import lombok.Setter; +import org.aspectj.lang.JoinPoint; +import org.egovframe.rte.fdl.cmmn.aspect.ExceptionTransfer; + +@Setter +//@Aspect +public class EgovAopExceptionTransfer { + + private ExceptionTransfer exceptionTransfer; + + //@Pointcut("execution(* go.kr.project..impl.*Impl.*(..)) or execution(* egovframework.com..*Impl.*(..))") + private void exceptionTransferService() {} + + //@AfterThrowing(pointcut="exceptionTransferService()", throwing="ex") + public void doAfterThrowingExceptionTransferService(JoinPoint thisJoinPoint, Exception ex) throws Exception { + exceptionTransfer.transfer(thisJoinPoint, ex); + } + +} diff --git a/src/main/java/egovframework/exception/EgovDefaultExcepHndlr.java b/src/main/java/egovframework/exception/EgovDefaultExcepHndlr.java new file mode 100644 index 0000000..baede4e --- /dev/null +++ b/src/main/java/egovframework/exception/EgovDefaultExcepHndlr.java @@ -0,0 +1,14 @@ +package egovframework.exception; + +import lombok.extern.slf4j.Slf4j; +import org.egovframe.rte.fdl.cmmn.exception.handler.ExceptionHandler; + +@Slf4j +public class EgovDefaultExcepHndlr implements ExceptionHandler { + + @Override + public void occur(Exception ex, String packageName) { + log.debug("##### EgovServiceExceptionHandler Run..."); + } + +} diff --git a/src/main/java/egovframework/exception/EgovDefaultOthersExcepHndlr.java b/src/main/java/egovframework/exception/EgovDefaultOthersExcepHndlr.java new file mode 100644 index 0000000..5a548a0 --- /dev/null +++ b/src/main/java/egovframework/exception/EgovDefaultOthersExcepHndlr.java @@ -0,0 +1,14 @@ +package egovframework.exception; + +import lombok.extern.slf4j.Slf4j; +import org.egovframe.rte.fdl.cmmn.exception.handler.ExceptionHandler; + +@Slf4j +public class EgovDefaultOthersExcepHndlr implements ExceptionHandler { + + @Override + public void occur(Exception exception, String packageName) { + log.debug("##### EgovSampleOthersExcepHndlr Run..."); + } + +} diff --git a/src/main/java/egovframework/exception/EgovExceptionAdvice.java b/src/main/java/egovframework/exception/EgovExceptionAdvice.java new file mode 100644 index 0000000..013dcfc --- /dev/null +++ b/src/main/java/egovframework/exception/EgovExceptionAdvice.java @@ -0,0 +1,147 @@ +package egovframework.exception; + +import egovframework.configProperties.LoginProperties; +import egovframework.util.ApiResponseEntity; +import egovframework.util.HttpServletUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.dao.DataAccessException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.transaction.TransactionException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; + +/** + * @ControllerAdvice를 사용한 전역 예외 처리기 + * 이는 SimpleMappingExceptionResolver 접근 방식을 대체합니다 + * + * AJAX 요청(*.ajax)에 대해서는 JSON 형식의 예외 응답을 반환하고, + * 일반 요청에 대해서는 egovError.jsp 페이지로 리다이렉트합니다. + */ +@Slf4j +@ControllerAdvice +@RequiredArgsConstructor +public class EgovExceptionAdvice { + + private final LoginProperties loginProperties; + + /** + * DataAccessException 예외 처리 + * 데이터 액세스 관련 예외를 처리합니다. + */ + @ExceptionHandler(DataAccessException.class) + public Object handleDataAccessException(DataAccessException e, HttpServletRequest request) { + log.error("DataAccessException 발생: ", e); + return getModelAndView(e, request, HttpStatus.BAD_REQUEST); + } + + /** + * TransactionException 예외 처리 + * 트랜잭션 관련 예외를 처리합니다. + */ + @ExceptionHandler(TransactionException.class) + public Object handleTransactionException(TransactionException e, HttpServletRequest request) { + log.error("TransactionException 발생: ", e); + return getModelAndView(e, request, HttpStatus.BAD_REQUEST); + } + + /** + * 커스텀 AccessDeniedException 예외 처리 + * 접근 권한이 없는 경우 발생하는 예외를 처리합니다. + */ + @ExceptionHandler(AccessDeniedException.class) + public Object handleAccessDeniedException(AccessDeniedException e, HttpServletRequest request) { + log.warn("접근 권한 예외 발생 - 사용자: {}, URI: {}", e.getUserAcnt(), e.getRequestURI()); + return getModelAndView(e, request, HttpStatus.FORBIDDEN); + } + + /** + * 세션 만료 관련 예외 처리 + * HttpSessionRequiredException이 없으므로 일반적인 예외 처리로 대체합니다. + * 이 메서드는 AuthInterceptor에서 세션 만료 시 AJAX 요청에 대한 처리를 보완합니다. + */ + + /** + * RuntimeException 예외 처리 + * 런타임 예외를 처리합니다. + */ + @ExceptionHandler(RuntimeException.class) + public Object handleRuntimeException(RuntimeException e, HttpServletRequest request) { + log.error("RuntimeException 발생: ", e); + return getModelAndView(e, request, HttpStatus.INTERNAL_SERVER_ERROR); + } + + /** + * Exception 예외 처리 + * 일반 예외를 처리합니다. + */ + @ExceptionHandler(Exception.class) + public Object handleException(Exception e, HttpServletRequest request) { + log.error("Exception 발생: ", e); + return getModelAndView(e, request, HttpStatus.INTERNAL_SERVER_ERROR); + } + + /** + * IOException 예외 처리 + * IO 예외를 처리합니다. + */ + @ExceptionHandler(IOException.class) + public Object handleIOException(Exception e, HttpServletRequest request) { + log.error("IOException 발생: ", e); + return getModelAndView(e, request, HttpStatus.INTERNAL_SERVER_ERROR); + } + + /** + * Throwable 예외 처리 + * 모든 예외의 상위 클래스인 Throwable을 처리합니다. + */ + @ExceptionHandler(Throwable.class) + public Object handleThrowable(Throwable e, HttpServletRequest request) { + log.error("Throwable 발생: ", e); + return getModelAndView(e, request, HttpStatus.INTERNAL_SERVER_ERROR); + } + + /** + * MessageException 예외 처리 + * 메시지 관련 예외를 처리합니다. + */ + @ExceptionHandler(MessageException.class) + public Object handleMessageException(MessageException e, HttpServletRequest request) { + log.error("MessageException 발생: ", e); + return getModelAndView(e, request, HttpStatus.INTERNAL_SERVER_ERROR); + } + + /** + * 응답 객체를 생성하는 헬퍼 메소드 + * AJAX 요청인 경우 ResponseEntity>를 반환하고, + * 일반 요청인 경우 egovError 페이지로 리다이렉트하는 ModelAndView를 반환합니다. + * AJAX 요청 판단은 URL 패턴(*.ajax) 또는 HTTP 헤더 정보를 기반으로 합니다. + * + * @param e 발생한 예외 + * @param request HTTP 요청 객체 + * @param status HTTP 상태 코드 + * @return AJAX 요청인 경우 ResponseEntity, 일반 요청인 경우 ModelAndView + */ + private Object getModelAndView(Throwable e, HttpServletRequest request, HttpStatus status) { + // AJAX 요청인 경우 ApiResponse를 사용한 JSON 응답 반환 + if (HttpServletUtil.isAjaxRequest(request) || HttpServletUtil.isRealAjaxRequest(request)) { + ApiResponseEntity responseBody = ApiResponseEntity.builder() + .result(false) + .message(e.getMessage()) + .errorCode(e.getClass().getSimpleName()) + .build(); + return new ResponseEntity<>(responseBody, status); + } + + // 일반 요청인 경우 egovError 페이지로 리다이렉트 + ModelAndView mav = new ModelAndView("error/egovError"); + mav.addObject("exceptionName", e.getClass().getSimpleName()); + mav.addObject("exception", e); + return mav; + } +} diff --git a/src/main/java/egovframework/exception/MessageException.java b/src/main/java/egovframework/exception/MessageException.java new file mode 100644 index 0000000..41d44f9 --- /dev/null +++ b/src/main/java/egovframework/exception/MessageException.java @@ -0,0 +1,65 @@ +package egovframework.exception; + +import lombok.Getter; + +/** + * 기본 예외 클래스 + * + * 이 예외는 애플리케이션에서 발생하는 기본적인 예외를 처리하기 위해 사용됩니다. + * BasicException 발생 시 에러코드는 "MESSAGE"로 표시되며, + * 메시지만 사용자에게 표출됩니다. + */ +@Getter +public class MessageException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + /** + * 에러 코드 + */ + private final String errorCode; + + /** + * 기본 생성자 + * + * @param message 예외 메시지 + */ + public MessageException(String message) { + super(message); + this.errorCode = "MESSAGE_EXCEPTION"; + } + + /** + * 에러 코드를 지정할 수 있는 생성자 + * + * @param message 예외 메시지 + * @param errorCode 에러 코드 + */ + public MessageException(String message, String errorCode) { + super(message); + this.errorCode = errorCode; + } + + /** + * 원인 예외를 포함하는 생성자 + * + * @param message 예외 메시지 + * @param cause 원인 예외 + */ + public MessageException(String message, Throwable cause) { + super(message, cause); + this.errorCode = "MESSAGE_EXCEPTION"; + } + + /** + * 에러 코드와 원인 예외를 모두 지정할 수 있는 생성자 + * + * @param message 예외 메시지 + * @param errorCode 에러 코드 + * @param cause 원인 예외 + */ + public MessageException(String message, String errorCode, Throwable cause) { + super(message, cause); + this.errorCode = errorCode; + } +} \ No newline at end of file diff --git a/src/main/java/egovframework/filter/SessionRefreshFilter.java b/src/main/java/egovframework/filter/SessionRefreshFilter.java new file mode 100644 index 0000000..3d951b3 --- /dev/null +++ b/src/main/java/egovframework/filter/SessionRefreshFilter.java @@ -0,0 +1,54 @@ +package egovframework.filter; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; + +/** + * packageName : egovframework.filter + * fileName : SessionRefreshFilter + * author : 박성영 + * date : 25. 5. 21. + * description : 세션 갱신을 위한 필터 + * =========================================================== + * DATE AUTHOR NOTE + * ----------------------------------------------------------- + * 25. 5. 21. 박성영 최초 생성 + */ +@Slf4j +@Component +public class SessionRefreshFilter implements Filter { + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + HttpServletRequest httpRequest = (HttpServletRequest) request; + + // 배치 작업 경로는 세션 리프레시에서 제외 + String requestURI = httpRequest.getRequestURI(); + if (requestURI != null && requestURI.startsWith("/batch/")) { + chain.doFilter(request, response); + return; + } + + try { + // 세션이 있으면 접근하여 갱신 (세션 타임아웃 연장) + httpRequest.getSession(false); + + // 디버깅 용도 (필요시 주석 해제) + // if (httpRequest.getSession().getAttribute("sessionVO") != null) { + // log.debug("세션 리프레시 - 세션 정보 존재"); + // } else { + // log.debug("세션 리프레시 - 세션 정보 없음"); + // } + } catch (Exception e) { + // 세션 관련 오류 발생 시 로그만 남기고 계속 진행 + log.debug("세션 리프레시 중 오류 발생: {}", e.getMessage()); + } + + chain.doFilter(request, response); + } +} diff --git a/src/main/java/egovframework/filter/XssFilter.java b/src/main/java/egovframework/filter/XssFilter.java new file mode 100644 index 0000000..37bfbbb --- /dev/null +++ b/src/main/java/egovframework/filter/XssFilter.java @@ -0,0 +1,38 @@ +package egovframework.filter; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * XSS 공격 방지를 위한 필터 + * 모든 요청과 응답에 대해 XSS 필터링을 적용 + */ +public class XssFilter extends OncePerRequestFilter { + + private final XssUtil xssUtil; + + public XssFilter(XssUtil xssUtil) { + this.xssUtil = xssUtil; + } + + @Override + protected void doFilterInternal(@Nullable HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + + // XSS 방지를 위한 헤더 설정 + response.setHeader("X-XSS-Protection", "1; mode=block"); + response.setHeader("X-Content-Type-Options", "nosniff"); + + // 요청을 래핑하여 XSS 필터링 적용 + XssRequestWrapper xssRequestWrapper = new XssRequestWrapper(request, xssUtil); + + // 필터 체인 실행 + filterChain.doFilter(xssRequestWrapper, response); + } +} diff --git a/src/main/java/egovframework/filter/XssFilterConfig.java b/src/main/java/egovframework/filter/XssFilterConfig.java new file mode 100644 index 0000000..d97c058 --- /dev/null +++ b/src/main/java/egovframework/filter/XssFilterConfig.java @@ -0,0 +1,32 @@ +package egovframework.filter; + +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * XSS 필터 설정 클래스 + */ +@Configuration +public class XssFilterConfig { + + private final XssUtil xssUtil; + + public XssFilterConfig(XssUtil xssUtil) { + this.xssUtil = xssUtil; + } + + /** + * XSS 필터 등록 + * @return FilterRegistrationBean + */ + @Bean + public FilterRegistrationBean xssFilterRegistration() { + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + registrationBean.setFilter(new XssFilter(xssUtil)); + registrationBean.addUrlPatterns("/*"); // 모든 URL에 적용 + registrationBean.setName("xssFilter"); + registrationBean.setOrder(1); // 필터 순서 (낮은 숫자가 먼저 실행) + return registrationBean; + } +} \ No newline at end of file diff --git a/src/main/java/egovframework/filter/XssRequestWrapper.java b/src/main/java/egovframework/filter/XssRequestWrapper.java new file mode 100644 index 0000000..5f3f2f4 --- /dev/null +++ b/src/main/java/egovframework/filter/XssRequestWrapper.java @@ -0,0 +1,81 @@ +package egovframework.filter; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import java.util.HashMap; +import java.util.Map; + +/** + * XSS 필터링을 위한 HttpServletRequest 래퍼 클래스 + */ +public class XssRequestWrapper extends HttpServletRequestWrapper { + + private final XssUtil xssUtil; + + public XssRequestWrapper(HttpServletRequest request, XssUtil xssUtil) { + super(request); + this.xssUtil = xssUtil; + } + + @Override + public String getParameter(String name) { + String value = super.getParameter(name); + return xssFilter(name, value); + } + + @Override + public String[] getParameterValues(String name) { + String[] values = super.getParameterValues(name); + if (values == null) { + return null; + } + + String[] filteredValues = new String[values.length]; + for (int i = 0; i < values.length; i++) { + filteredValues[i] = xssFilter(name, values[i]); + } + return filteredValues; + } + + @Override + public Map getParameterMap() { + Map paramMap = super.getParameterMap(); + Map filteredParamMap = new HashMap<>(); + + for (Map.Entry entry : paramMap.entrySet()) { + String[] values = entry.getValue(); + String[] filteredValues = new String[values.length]; + for (int i = 0; i < values.length; i++) { + filteredValues[i] = xssFilter(entry.getKey(), values[i]); + } + filteredParamMap.put(entry.getKey(), filteredValues); + } + return filteredParamMap; + } + + /** + * XSS 필터링 적용 + * @param name 파라미터 이름 + * @param value 파라미터 값 + * @return 필터링된 값 + */ + private String xssFilter(String name, String value) { + if (value == null) { + return null; + } + + // HTML 에디터 내용인 경우 cleanHtml 메서드 사용 + //if (name != null && (name.contains("content") || name.contains("html") || name.contains("editor"))) { + if (name != null && (name.contains("html") || name.contains("editor"))) { + return xssUtil.cleanHtml(value); + } + + // 파일명인 경우 sanitizeFilename 메서드 사용 + if (name != null && (name.contains("filename") || name.contains("file"))) { + return xssUtil.sanitizeFilename(value); + } + + // 일반 텍스트인 경우 escape 메서드 사용 + return xssUtil.escape(value); + } +} \ No newline at end of file diff --git a/src/main/java/egovframework/filter/XssUtil.java b/src/main/java/egovframework/filter/XssUtil.java new file mode 100644 index 0000000..eaed7a0 --- /dev/null +++ b/src/main/java/egovframework/filter/XssUtil.java @@ -0,0 +1,105 @@ +package egovframework.filter; + +import org.springframework.stereotype.Component; + +import java.util.regex.Pattern; + +/** + * XSS(Cross-Site Scripting) 방지를 위한 유틸리티 클래스 + */ +@Component +public class XssUtil { + + /** + * HTML 태그를 이스케이프 처리하여 XSS 공격 방지 + * @param value 원본 문자열 + * @return 이스케이프 처리된 문자열 + */ + public String escape(String value) { + if (value == null) { + return null; + } + + return value.replaceAll("&", "&") + .replaceAll("<", "<") + .replaceAll(">", ">") + .replaceAll("\"", """) + .replaceAll("'", "'") + .replaceAll("/", "/") + .replaceAll("\\(", "(") + .replaceAll("\\)", ")"); + } + + /** + * 이스케이프 처리된 문자열을 원래 형태로 복원 + * @param value 이스케이프 처리된 문자열 + * @return 원본 문자열 + */ + public String unescape(String value) { + if (value == null) { + return null; + } + + return value.replaceAll("&", "&") + .replaceAll("<", "<") + .replaceAll(">", ">") + .replaceAll(""", "\"") + .replaceAll("'", "'") + .replaceAll("/", "/") + .replaceAll("(", "(") + .replaceAll(")", ")"); + } + + /** + * HTML 에디터에서 사용하는 태그만 허용하고 나머지는 이스케이프 처리 + * @param value 원본 HTML 문자열 + * @return 안전한 HTML 문자열 + */ + public String cleanHtml(String value) { + if (value == null) { + return null; + } + + // 허용할 HTML 태그 패턴 + String allowedTags = "

|
||||||