# 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. 모델 데이터 로드
```php
// 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()`
```javascript
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)
  - 혼합형(120*70)(120*120)
- **col7**: 마감 타입 선택
  - SUS마감
  - EGI마감

#### **제작 사이즈 컬럼 (10-11)**
- **col10**: 제작 사이즈 (가로)
- **col11**: 제작 사이즈 (세로)
- **특별 처리**: col10은 3개의 필드로 분할
  - `col10_SW`: 가로 사이즈
  - `col11_SH`: 세로 사이즈
  - `col10`: 통합 사이즈

#### **수량 컬럼 (14)**
- **col14**: 수량 입력 + 자동산출 버튼
```javascript
'<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**: 복합 입력 필드
  - 기본 옵션: 500*380, 500*350
  - 직접 입력 옵션
  - 전면밑, 레일폭, 박스 방향 설정
```javascript
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 클래스 활용
- 모바일 최적화
- 테이블 반응형 처리

### 🎯 컬럼 너비 최적화
```javascript
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;
```

### 🔘 버튼 시스템
- **추가 버튼**: `+` (새 행 추가)
- **삭제 버튼**: `-` (행 삭제)
- **복사 버튼**: 📋 (행 복사)
- **계산 버튼**: 🧮 (자동 산출)

## ⚡ 이벤트 핸들링

### 🔄 모델 변경 이벤트
```javascript
newRow.find('.col4-select').on('input change', function() {
    var row = $(this).closest('tr');
    modelChange_screen(row);
});
```

### 🔌 모터 브랜드 변경 이벤트
```javascript
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();
    // 모터 브랜드별 처리 로직
});
```

### 🎛️ 셔터박스 커스텀 입력 이벤트
```javascript
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()**: 샤프트 수량 계산
```javascript
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()**: 샤프트 길이별 수량 계산
```javascript
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()**: 셔터박스 수량 계산
```javascript
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()**: 셔터박스 길이 계산
```javascript
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()**: 앵글 수량 계산
```javascript
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()**: 조인트바 수량 계산
```javascript
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()**: 전체 합계 계산
```javascript
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);
    });
}
```

## 🔧 개발자 사용법

### 📝 기본 사용법
```javascript
// 새로운 행 추가
addRow($('#estimateTable tbody'), {}, 'add');

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

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

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

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

## 🚨 주의사항

### ⚠️ 필수 의존성
- jQuery 3.x 이상
- Bootstrap 5.x
- models.json 파일 존재 필요

### 🔒 보안 고려사항
- XSS 방지를 위한 입력값 검증 필요
- SQL 인젝션 방지를 위한 서버 사이드 검증 필요

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

## 🐛 디버깅 가이드

### 🔍 콘솔 로그 확인
```javascript
// 조인트바 계산 디버깅
console.log('조인트바 계산 col10Value : ', col10Value);
console.log('조인트바 계산 col15Value : ', col15Value);
console.log('조인트바 계산 col76 : ', col76Input.val());
```

### 🛠️ 일반적인 문제 해결

#### 1. 모델 옵션이 로드되지 않는 경우
```javascript
// models.json 파일 경로 확인
console.log('Models loaded:', screenModelOptions, steelModelOptions);
```

#### 2. 이벤트가 작동하지 않는 경우
```javascript
// 이벤트 바인딩 확인
$(document).on('change', '.col4-select', function() {
    console.log('Model changed:', $(this).val());
});
```

#### 3. 계산이 올바르지 않은 경우
```javascript
// 입력값 확인
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  
**👨‍💻 작성자**: 전산실장 김보곤 