Files
sam-kd/0readme/estimate/common_addrowJS_developer_guide.md
hskwon aca1767eb9 초기 커밋: 5130 레거시 시스템
- URL 하드코딩 → .env APP_URL 기반 동적 URL로 변경
- DB 연결 하드코딩 → .env 기반으로 변경
- MySQL strict mode DATE 오류 수정
2025-12-10 20:14:31 +09:00

12 KiB

common_addrowJS.php 개발자 가이드

📋 개요

common_addrowJS.php는 방화셔터 견적 시스템의 핵심 JavaScript 컴포넌트로, 견적 테이블에 새로운 행을 동적으로 추가하고 관리하는 기능을 제공합니다. 이 파일은 PHP와 JavaScript가 혼합된 형태로 구성되어 있으며, 견적 항목의 복잡한 입력 폼을 자동으로 생성합니다.

🏗️ 파일 구조

📁 파일 위치

/estimate/common/common_addrowJS.php

📊 파일 정보

  • 파일 크기: 113KB (2536 lines)
  • 주요 언어: PHP + JavaScript + jQuery
  • 의존성: jQuery, Bootstrap, models.json

🔧 핵심 기능

1. 모델 데이터 로드

// models.json에서 스크린/철재 모델 정보 로드
$jsonFile = $_SERVER['DOCUMENT_ROOT'] . '/models/models.json';
$models = json_decode(file_get_contents($jsonFile), true);

// 스크린과 철재 모델 분리
$screenModels = array_filter($models, function ($m) {
    return $m['slatitem'] === '스크린';
});
$steelModels = array_filter($models, function ($m) {
    return $m['slatitem'] === '철재';
});

2. 메인 함수: addRow()

function addRow(tableBody, rowData, typebutton, afterRow = null, autoData = {})

매개변수

  • tableBody: 대상 테이블의 tbody 요소
  • rowData: 행에 설정할 초기 데이터 객체
  • typebutton: 버튼 타입 (사용되지 않음)
  • afterRow: 삽입할 기준 행 (선택사항)
  • autoData: 자동 계산 데이터 (선택사항)

📊 컬럼 구조 (71개 컬럼)

🎯 컬럼별 특성

기본 컬럼 (1-3)

  • col1: 행 번호 (자동 생성, readonly)
  • col2: 기본 입력 필드
  • col3: 기본 입력 필드

모델 관련 컬럼 (4-7)

  • col4: 스크린 모델 선택 (드롭다운)
    • screenModelOptions에서 동적 로드
    • 변경 시 modelChange_screen() 함수 호출
  • col5: readonly 필드 (자동 계산)
  • col6: 셔터박스 타입 선택
    • 벽면형(120*70)
    • 측면형(120*120)
    • 혼합형(12070)(120120)
  • col7: 마감 타입 선택
    • SUS마감
    • EGI마감

제작 사이즈 컬럼 (10-11)

  • col10: 제작 사이즈 (가로)
  • col11: 제작 사이즈 (세로)
  • 특별 처리: col10은 3개의 필드로 분할
    • col10_SW: 가로 사이즈
    • col11_SH: 세로 사이즈
    • col10: 통합 사이즈

수량 컬럼 (14)

  • col14: 수량 입력 + 자동산출 버튼
'<button type="button" class="btn btn-sm btn-outline-primary ms-1 calc-btn viewNoBtn" title="자동산출"><i class="bi bi-calculator-fill"></i></button>'

모터 관련 컬럼 (18-19)

  • col18: 모터 브랜드 + 전압 + 유선/무선
    • 브랜드: 경동(견적가포함), 대한, 기타
    • 전압: 220V, 380V
    • 연결: 유선, 무선
  • col19: 모터 용량 선택
    • 브라켓 사이즈별 용량 매핑
    • 380*180: 150K, 300K, 400K
    • 530*320: 300K, 400K
    • 600*350: 500K, 600K
    • 690*390: 800K, 1000K

셔터박스 컬럼 (36)

  • col36: 복합 입력 필드
    • 기본 옵션: 500380, 500350
    • 직접 입력 옵션
    • 전면밑, 레일폭, 박스 방향 설정
let selectHtml = '<select name="col36[]">...</select>';
let inputHtml = '<input type="text" name="col36_custom[]">';
let inputHtmlfrontbottom = '<input type="text" name="col36_frontbottom[]">';
let inputHtmlrailwidth = '<input type="text" name="col36_railwidth[]">';
let selectHtmlboxdirection = '<select name="col36_boxdirection[]">...</select>';

마구리 윙 컬럼 (45)

  • col45: 기본 입력 필드
  • col45_wing: 마구리 윙 길이
    • 기본값: $('#maguriWing').val() 또는 빈 값

샤프트 관련 컬럼 (59)

  • col59: 복합 입력 필드
    • 인치 선택: 2인치, 3인치
    • 길이 입력
    • 수량 입력

Readonly 컬럼들

  • col5, 12, 13, 23, 37, 48, 51, 54, 58, 67: 자동 계산 필드

🎨 UI/UX 특징

📱 반응형 디자인

  • Bootstrap 클래스 활용
  • 모바일 최적화
  • 테이블 반응형 처리

🎯 컬럼 너비 최적화

let width = [2, 3, 4, 5, 7, 20, 22, 23, 29, 30, 36, 45].includes(i) ? '85px' : '50px';
width = [6].includes(i) ? '170px' : width;
width = [18].includes(i) ? '60px' : width;
width = [23,32,43,46,49,53,62].includes(i) ? '50px' : width;

🔘 버튼 시스템

  • 추가 버튼: + (새 행 추가)
  • 삭제 버튼: - (행 삭제)
  • 복사 버튼: 📋 (행 복사)
  • 계산 버튼: 🧮 (자동 산출)

이벤트 핸들링

🔄 모델 변경 이벤트

newRow.find('.col4-select').on('input change', function() {
    var row = $(this).closest('tr');
    modelChange_screen(row);
});

🔌 모터 브랜드 변경 이벤트

newRow.find('.col18_brand').on('change', function() {
    var row = $(this).closest('tr');
    const qty = row.find('.col14-input').val();
    const col18_brandValue = row.find('.col18_brand').val();
    // 모터 브랜드별 처리 로직
});

🎛️ 셔터박스 커스텀 입력 이벤트

newRow.find('.col36-select').on('change', function() {
    var selectedValue = $(this).val();
    var customInput = $(this).closest('td').find('.col36-custom-input');
    
    if (selectedValue === 'custom') {
        customInput.show();
        customInput.focus();
    } else {
        customInput.hide();
        customInput.val('');
    }
});

🧮 자동 계산 시스템

📏 철재 스라트 계산 함수들

Slat_updateCol59(): 샤프트 수량 계산

function Slat_updateCol59(row) {
    var col10Value = parseFloat(row.find('.col10-input').val()) || 0;  // 셔터 길이
    var col13Value = parseFloat(row.find('.col13-input').val()) || 0;  // 중량
    var col59Input = row.find('.col59-input');
    
    // 길이와 중량에 따른 샤프트 수량 결정
    if (col10Value <= 4500) {
        if (col13Value <= 400) {
            col59Input.val(4);
        } else if (col13Value > 400) {
            col59Input.val(5);
        }
    }
    // ... 추가 조건들
}

Slat_updateCol60to71(): 샤프트 길이별 수량 계산

function Slat_updateCol60to71(row) {
    var col15Value = parseFloat(row.find('.col15-input').val()) || 0; // 셔터수량
    var col59Value = parseFloat(row.find('.col22-select').val()) || 0; // 브라켓인치
    var col10Value = parseFloat(row.find('.col10-input').val()) || 0; // 제작사이즈 가로
    
    // 브라켓 인치별 샤프트 길이 분류
    row.find('.col61-input').val((col59Value === 4 && col10Value <= 3050) ? col15Value : 0);
    row.find('.col62-input').val((col59Value === 4 && col10Value > 3050 && col10Value <= 4550) ? col15Value : 0);
    // ... 추가 조건들
}

Slat_updateCol72(): 셔터박스 수량 계산

function Slat_updateCol72(row) {
    var col38Value = parseFloat(row.find('.col38-input').val()) || 0; // 셔터박스 사이즈
    var col72Input = row.find('.col72-input');
    
    // 사이즈별 박스 수량 결정
    col72Input.val((col38Value <= 1600) ? 3 :
                  (col38Value <= 2800) ? 4 :
                  (col38Value <= 4000) ? 5 :
                  // ... 추가 조건들
                  0);
}

Slat_updateCol73(): 셔터박스 길이 계산

function Slat_updateCol73(row) {
    var col38Value = parseFloat(row.find('.col38-input').val()) || 0; // 셔터박스 사이즈
    var col72Value = parseFloat(row.find('.col72-input').val()) || 0;
    
    row.find('.col73-input').val(col38Value + 3000 * col72Value);
}

Slat_updateCol74to75(): 앵글 수량 계산

function Slat_updateCol74to75(row) {
    var col15Value = parseFloat(row.find('.col15-input').val()) || 0; // 셔터수량
    var col73Value = parseFloat(row.find('.col73-input').val()) || 0;
    
    // 길이별 앵글 수량 계산
    var col74Input = row.find('.col74-input');
    col74Input.val((col73Value <= 9000) ? 3 * col15Value:
                   (col73Value > 9000 && col73Value <= 12000) ? 4 * col15Value:
                   // ... 추가 조건들
                   0);
}

Slat_updateCo76(): 조인트바 수량 계산

function Slat_updateCo76(row) {
    var col15Value = parseFloat(row.find('.col15-input').val()) || 0; // 셔터수량
    var col10Value = parseFloat(row.find('.col10-input').val()) || 0; // 제작사이즈 (가로)
    var col76Input = row.find('.col76-input');
    
    var calculatedValue = 2 + Math.floor((col10Value - 500) / 1000);
    col76Input.val(calculatedValue * col15Value);
}

Slat_updateTotals(): 전체 합계 계산

function Slat_updateTotals() {
    var sumColumns = [16, 17, 18, 19, 20, 21, 22, 31, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47, 48, 49, 50, 51, 54, 55, 57, 58, 59, 60, 61, 62, 63, 64, 65, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 81, 82, 83, 84];
    
    sumColumns.forEach(function (colIndex) {
        var total = 0;
        
        $('#estimateSlatTable tbody tr').each(function () {
            var $cell = $(this).find('td').eq(colIndex - 1);
            var val = '';
            
            if ($cell.find('input').length > 0) {
                val = $cell.find('input').val() || '';
            } else if ($cell.find('select').length > 0) {
                val = $cell.find('select').val() || '';
            }
            
            var numericValue = parseFloat(val.toString().replace(/,/g, '')) || 0;
            total += numericValue;
        });
        
        var formattedTotal = total === 0 ? '' : total.toLocaleString();
        $('#estimateSlattotalCol' + colIndex).text(formattedTotal);
    });
}

🔧 개발자 사용법

📝 기본 사용법

// 새로운 행 추가
addRow($('#estimateTable tbody'), {}, 'add');

// 특정 데이터로 행 추가
addRow($('#estimateTable tbody'), {
    col4: '스크린모델명',
    col14: '10',
    col10: '3000'
}, 'add');

// 특정 행 다음에 삽입
addRow($('#estimateTable tbody'), {}, 'add', $('#someRow'));

🎯 커스텀 데이터 처리

// 자동 계산 데이터 포함
addRow($('#estimateTable tbody'), rowData, 'add', null, {
    calculated: true,
    timestamp: new Date().getTime()
});

🔄 이벤트 바인딩

// 새로 추가된 행에 이벤트 바인딩
$('#estimateTable').on('change', '.col4-select', function() {
    var row = $(this).closest('tr');
    modelChange_screen(row);
});

🚨 주의사항

⚠️ 필수 의존성

  • jQuery 3.x 이상
  • Bootstrap 5.x
  • models.json 파일 존재 필요

🔒 보안 고려사항

  • XSS 방지를 위한 입력값 검증 필요
  • SQL 인젝션 방지를 위한 서버 사이드 검증 필요

📱 성능 최적화

  • 대량의 행 추가 시 성능 저하 가능
  • 이벤트 위임 사용 권장
  • 메모리 누수 방지를 위한 이벤트 정리 필요

🐛 디버깅 가이드

🔍 콘솔 로그 확인

// 조인트바 계산 디버깅
console.log('조인트바 계산 col10Value : ', col10Value);
console.log('조인트바 계산 col15Value : ', col15Value);
console.log('조인트바 계산 col76 : ', col76Input.val());

🛠️ 일반적인 문제 해결

1. 모델 옵션이 로드되지 않는 경우

// models.json 파일 경로 확인
console.log('Models loaded:', screenModelOptions, steelModelOptions);

2. 이벤트가 작동하지 않는 경우

// 이벤트 바인딩 확인
$(document).on('change', '.col4-select', function() {
    console.log('Model changed:', $(this).val());
});

3. 계산이 올바르지 않은 경우

// 입력값 확인
console.log('Input values:', {
    col10: $('.col10-input').val(),
    col13: $('.col13-input').val(),
    col15: $('.col15-input').val()
});

📚 관련 파일

🔗 의존성 파일

  • /models/models.json: 모델 데이터
  • jQuery: DOM 조작
  • Bootstrap: UI 프레임워크

🔗 연관 함수

  • modelChange_screen(): 모델 변경 처리
  • calculateAmount(): 금액 계산
  • updateTotals(): 합계 업데이트

🎯 향후 개선 방향

🔄 코드 리팩토링

  • 함수 분리 및 모듈화
  • TypeScript 도입 고려
  • ES6+ 문법 활용

🎨 UI/UX 개선

  • 드래그 앤 드롭 행 재정렬
  • 키보드 단축키 지원
  • 실시간 검증 메시지

성능 최적화

  • 가상 스크롤링 도입
  • 지연 로딩 구현
  • 메모리 사용량 최적화

📅 문서 버전: 1.0
👨‍💻 작성자: 전산실장 김보곤