메뉴 여닫기
개인 메뉴 토글
로그인하지 않음
만약 지금 편집한다면 당신의 IP 주소가 공개될 수 있습니다.

핸드온테이블 그리드 ORACLE DB 연결 예제

데브카페
Devcafe (토론 | 기여)님의 2025년 10월 17일 (금) 16:05 판 (새 문서: <source lang=html> <!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Handsontable 데이터 그리드</title> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/handsontable/12.3.1/handsontable.full.min.css"> <script src="https://cdnjs.cloudflare.com/ajax/libs/handsontable/12.3.1/handsontable.full.min.js"></script> <style> * { m...)
(차이) ← 이전 판 | 최신판 (차이) | 다음 판 → (차이)

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Handsontable 데이터 그리드</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/handsontable/12.3.1/handsontable.full.min.css">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/handsontable/12.3.1/handsontable.full.min.js"></script>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            padding: 20px;
            min-height: 100vh;
        }
        
        .container {
            max-width: 1400px;
            margin: 0 auto;
            background: white;
            border-radius: 15px;
            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
            padding: 30px;
        }
        
        h1 {
            color: #333;
            margin-bottom: 10px;
            font-size: 28px;
        }
        
        .subtitle {
            color: #666;
            margin-bottom: 25px;
            font-size: 14px;
        }
        
        .controls {
            display: flex;
            gap: 10px;
            margin-bottom: 20px;
            flex-wrap: wrap;
        }
        
        button {
            padding: 10px 20px;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            font-size: 14px;
            font-weight: 600;
            transition: all 0.3s;
            color: white;
        }
        
        .btn-add {
            background: #10b981;
        }
        
        .btn-add:hover {
            background: #059669;
            transform: translateY(-2px);
        }
        
        .btn-remove {
            background: #ef4444;
        }
        
        .btn-remove:hover {
            background: #dc2626;
            transform: translateY(-2px);
        }
        
        .btn-export {
            background: #3b82f6;
        }
        
        .btn-export:hover {
            background: #2563eb;
            transform: translateY(-2px);
        }
        
        .btn-clear {
            background: #f59e0b;
        }
        
        .btn-clear:hover {
            background: #d97706;
            transform: translateY(-2px);
        }
        
        #grid-container {
            width: 100%;
            height: 500px;
            overflow: hidden;
        }
        
        .info {
            margin-top: 20px;
            padding: 15px;
            background: #f3f4f6;
            border-radius: 8px;
            font-size: 14px;
            color: #374151;
        }
        
        .info-item {
            margin-bottom: 5px;
        }
        
        .info-item strong {
            color: #1f2937;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>📊 Handsontable 데이터 그리드</h1>
        <p class="subtitle">실시간 데이터 편집 및 관리 시스템</p>
        
        <div class="controls">
            <button class="btn-add" onclick="addRow()">➕ 행 추가</button>
            <button class="btn-remove" onclick="removeRow()">➖ 선택 행 삭제</button>
            <button class="btn-export" onclick="exportData()">💾 CSV 내보내기</button>
            <button class="btn-clear" onclick="clearFilters()">🔄 필터 초기화</button>
        </div>
        
        <div id="grid-container"></div>
        
        <div class="info">
            <div class="info-item"><strong>🔍 기능:</strong> 셀 편집, 정렬, 필터링, 행/열 크기 조정</div>
            <div class="info-item"><strong>💡 팁:</strong> 컬럼 헤더를 클릭하여 정렬, 드롭다운에서 필터 선택 가능</div>
            <div class="info-item"><strong>⌨️ 단축키:</strong> Ctrl+C(복사), Ctrl+V(붙여넣기), Delete(삭제)</div>
        </div>
    </div>

    <script>
        // 샘플 데이터 생성
        const data = [
            { id: 1, name: '김철수', department: '개발팀', position: '팀장', salary: 5500, email: 'kim@example.com', joinDate: '2020-01-15' },
            { id: 2, name: '이영희', department: '영업팀', position: '대리', salary: 4200, email: 'lee@example.com', joinDate: '2021-03-20' },
            { id: 3, name: '박민수', department: '개발팀', position: '과장', salary: 4800, email: 'park@example.com', joinDate: '2019-07-10' },
            { id: 4, name: '최지현', department: '인사팀', position: '사원', salary: 3500, email: 'choi@example.com', joinDate: '2022-05-01' },
            { id: 5, name: '정수진', department: '마케팅팀', position: '차장', salary: 5200, email: 'jung@example.com', joinDate: '2018-11-30' },
            { id: 6, name: '강동원', department: '영업팀', position: '부장', salary: 6000, email: 'kang@example.com', joinDate: '2017-02-14' },
            { id: 7, name: '윤서영', department: '개발팀', position: '사원', salary: 3800, email: 'yoon@example.com', joinDate: '2023-01-10' },
            { id: 8, name: '임하늘', department: '마케팅팀', position: '대리', salary: 4300, email: 'lim@example.com', joinDate: '2021-08-25' },
            { id: 9, name: '송민호', department: '인사팀', position: '과장', salary: 4900, email: 'song@example.com', joinDate: '2020-06-15' },
            { id: 10, name: '한지우', department: '영업팀', position: '사원', salary: 3600, email: 'han@example.com', joinDate: '2022-09-01' }
        ];

        const container = document.getElementById('grid-container');
        
        const hot = new Handsontable(container, {
            data: data,
            colHeaders: ['ID', '이름', '부서', '직급', '급여(만원)', '이메일', '입사일'],
            columns: [
                { data: 'id', type: 'numeric', readOnly: true },
                { data: 'name', type: 'text' },
                { 
                    data: 'department', 
                    type: 'dropdown',
                    source: ['개발팀', '영업팀', '인사팀', '마케팅팀', '재무팀']
                },
                { 
                    data: 'position', 
                    type: 'dropdown',
                    source: ['사원', '대리', '과장', '차장', '부장', '팀장']
                },
                { data: 'salary', type: 'numeric', numericFormat: { pattern: '0,0' } },
                { data: 'email', type: 'text' },
                { data: 'joinDate', type: 'date', dateFormat: 'YYYY-MM-DD' }
            ],
            rowHeaders: true,
            width: '100%',
            height: 500,
            licenseKey: 'non-commercial-and-evaluation',
            dropdownMenu: true,
            filters: true,
            columnSorting: true,
            contextMenu: true,
            manualColumnResize: true,
            manualRowResize: true,
            stretchH: 'all',
            autoWrapRow: true,
            autoWrapCol: true,
            fillHandle: {
                direction: 'vertical',
                autoInsertRow: false
            },
            language: 'ko-KR'
        });

        // 행 추가 함수
        function addRow() {
            const newId = hot.countRows() + 1;
            const newRow = {
                id: newId,
                name: '',
                department: '개발팀',
                position: '사원',
                salary: 3000,
                email: '',
                joinDate: new Date().toISOString().split('T')[0]
            };
            hot.alter('insert_row_below', hot.countRows(), 1);
            hot.setDataAtRow(hot.countRows() - 1, [
                newRow.id, 
                newRow.name, 
                newRow.department, 
                newRow.position, 
                newRow.salary, 
                newRow.email, 
                newRow.joinDate
            ]);
        }

        // 선택 행 삭제 함수
        function removeRow() {
            const selected = hot.getSelected();
            if (selected && selected.length > 0) {
                const startRow = selected[0][0];
                const endRow = selected[0][2];
                const rowCount = endRow - startRow + 1;
                
                if (confirm(`선택된 ${rowCount}개의 행을 삭제하시겠습니까?`)) {
                    hot.alter('remove_row', startRow, rowCount);
                }
            } else {
                alert('삭제할 행을 선택해주세요.');
            }
        }

        // CSV 내보내기 함수
        function exportData() {
            const exportPlugin = hot.getPlugin('exportFile');
            exportPlugin.downloadFile('csv', {
                bom: true,
                columnDelimiter: ',',
                columnHeaders: true,
                exportHiddenColumns: false,
                exportHiddenRows: false,
                fileExtension: 'csv',
                filename: 'employee_data_[YYYY]-[MM]-[DD]',
                mimeType: 'text/csv',
                rowDelimiter: '\r\n',
                rowHeaders: false
            });
        }

        // 필터 초기화 함수
        function clearFilters() {
            const filtersPlugin = hot.getPlugin('filters');
            filtersPlugin.clearConditions();
            filtersPlugin.filter();
            hot.render();
        }

        // 데이터 변경 이벤트
        hot.addHook('afterChange', function(changes, source) {
            if (source !== 'loadData') {
                console.log('데이터 변경:', changes);
            }
        });
    </script>
</body>
</html>

Comments