[JavaScript] Form태그 하위 Input을 Post전송하는 JsFormController 만들어보기

최근 진행한 프로젝트에서 Javascript로 Form태그 하위의 Input 요소들이 유동적으로 추가 및 삭제되는 기획이 많아서, Javascript로 어떻게 하면 유동적으로 첨삭되는 Input태그들과 Form을 손쉽게 POST 전송하여 데이터를 첨삭할 수 있을까 고민하다 JsFormController라는 Class를 만들어보며 나름 뜻깊었고, 본인과 같은 고민을 하시는 분들께 약소하게나마 도움이 되고자 포스팅합니다.

 

  • javascript를 사용하여 form 태그 하위 요소들을 핸들링하여 fetch 함수로 비동기 post 전송을 실행해보자.
  • 본 예제는 Insert 부분까지만 다루며 update, destory 등은 스스로 해보면서 학습하길 권장합니다.

1. JsFormController.js 이름의 Class 만들기

  • new 연산자를 사용하여 JsFormController를 생성하는 페이지의 Form태그 하위 요소들을 자동으로 설정하게 생성자 설정
  • 생성자에서 현재 페이지에 맞는 Form태그의 하위 요소들을 초기화하는 init() 메서드를 설정
class JsFormController {
    // New 연산자를 통한 현재 Class 생성시 자동 실행되는 생성자
    constructor() {
        this.init();
    }
    
    init() {
    	// 현재 페이지에 있는 Form Id
        this.formId = document.forms[0].id;
        
    	// Form 하위 selectBox 요소 전역 변수 초기화
        if (document.getElementById(this.formId).getElementsByTagName('select')) {
            this.selectElements = document.getElementById(this.formId).getElementsByTagName('select');
        }
        
        // Form 하위 input 요소 전역 변수 초기화
        if (document.getElementById(this.formId).getElementsByTagName('input')) {
            this.inputElements = document.getElementById(this.formId).getElementsByTagName('input');
        }
        
        // Form 하위 textarea 요소 전역 변수 초기화
        if (document.getElementById(this.formId).getElementsByTagName('textarea')) {
            this.textareaElements = document.getElementById(this.formId).getElementsByTagName('textarea');
        }
        
        // Form 하위 Input File 요소를 담을 전역 변수 생성
        this.fileElements = new Map();
    }
}

 

2. dataSetting() 함수 생성

  • init() 호출을 통해 초기화된 각 요소들에 name,value를 Map계열 객체의 key-value로 설정하는 dataSetting() 메서드 생성
class JsFormController {
    constructor() {...}
    init() {...}

    dataSetting() {
        let retrunData = new Map();

        // select box
        if (this.selectElements.length > 0) {
            for (let selectElement of this.selectElements) {
                retrunData.set(selectElement.name, selectElement.value);
            }
        }

        // input tag
        if (this.inputElements.length > 0) {
            for (let inputElement of this.inputElements) {
                if (inputElement.getAttribute('type') != null && inputElement.getAttribute('type') === 'radio') {
                    // input radio
                    if (inputElement.checked === true) {
                        retrunData.set(inputElement.name, inputElement.value);
                    }
                } else if (inputElement.getAttribute('type') != null && inputElement.getAttribute('type') === 'checkbox') {
                    // input checkbox
                    if (inputElement.checked === true) {
                        retrunData.set(inputElement.name, inputElement.value);
                    }
                } else { // input text
                    if (inputElement.name.includes('[]')) { // when input type name array
                        if(inputElement.value.includes(",")) {
                        	// ,(쉼표) 문자가 자연스레 아스키코드로 전환되게 변경
                            inputElement.value = inputElement.value.replace(',',',');
                        }
                        if (!retrunData.get(inputElement.name)) {
                        	// 배열로 넘어온 input name값이 처음 값일때 설정
                            retrunData.set(inputElement.name, [inputElement.value]);
                        } else {
                        	// 배열로 넘어온 input name값이 1개 이상일 때 설정
                            let dumpArr = retrunData.get(inputElement.name);
                            dumpArr.push(inputElement.value);
                            retrunData.set(inputElement.name, dumpArr);
                        }
                    } else { // default
                        retrunData.set(inputElement.name, inputElement.value);
                    }
                }
            }
        }

        // input file
        for (let inputElement of this.inputElements) {
            if (inputElement.files != null) {
                if (inputElement.name.includes('[]')) { // when input File name array
                    if (!this.fileElements.get(inputElement.name)) {
	                    // 배열로 넘어온 input file type name값이 처음 값일때 설정
                        this.fileElements.set(inputElement.name, [inputElement.files]);
                    } else {
	                    // 배열로 넘어온 input file type name값이 1개 이상일 때 설정
                        let dumpArr = this.fileElements.get(inputElement.name);
                        dumpArr.push(inputElement.files);
                        this.fileElements.set(inputElement.name, dumpArr);
                    }
                } else { // default
                    this.fileElements.set(inputElement.name, inputElement.files);
                }
            }
        }

        // textarea tag
        if (this.textareaElements.length > 0) {
            for (let textareaElement of this.textareaElements) {
                // when textarea type name array
                if (textareaElement.name.includes('[]')) {
                    if (!retrunData.get(textareaElement.name)) {
                        let firstValue = new Array();
                        firstValue.push(textareaElement.value);
                        retrunData.set(textareaElement.name, firstValue);
                    } else {
                        let firstAfterValue = retrunData.get(textareaElement.name);
                        firstAfterValue.push(textareaElement.value);
                        retrunData.set(textareaElement.name, firstAfterValue);
                    }
                } else { // default
                    retrunData.set(textareaElement.name, textareaElement.value);
                }
            }
        }

        return retrunData;
    }
}

 

3. insert() 함수 생성

  • form 태그 하위 요소들에 값을 다 채운 후, submit 시 실행될 insert()함수를 생성한다.
  • fetch 함수를 이용하여 비동기식 POST전송을 실행한다.
class JsFormController {
    constructor() {...}
    init() {...}
    dataSetting() {...}

    insert() {
    	// 새로운 FormData객체 생성
        let postFormData = new FormData;
        
        // 현재 페이지 Form 하위 요소의 name,value를 key-value의 형태로 담은 map객체 생성
        let inputFormData = this.dataSetting(); 

		// Map 형식의 데이터를 새로 생성한 FormData 형식으로 재가공
        for (let [key, value] of inputFormData) {
            postFormData.append(key, value);
        }
       
        // File tag의 경우 파일이 존재할 경우에만 FormData객체에 추가함
        if (this.fileElements != null && this.fileElements.size > 0) {
            for (let [key, value] of this.fileElements) {
                if (key.includes('[]')) { // type name array
                    for (let i = 0; i < value.length; i++) {
                        if (value[i].length > 1) {
                            // multiple
                            for (let y = 0; y < value[i].length; y++) {
                                postFormData.append(key, value[i][y]);
                            }
                        } else {
                            // sinlge
                            postFormData.append(key, value[i][0]);
                        }
                    }
                } else { // default
                    postFormData.append(key, value[0]);
                }
            }
        }

        // Insert
        fetch(this.requestURL + '/insert', {
            method: 'POST',
            cache: 'no-cache',
            body: postFormData,
        })
            .then((response) => response.json())
            .then((data) => {
                if (data.is_success == true) {
                    // back단에서 성공시 작업처리
                } else {
                    // back단에서 실패시 return하는 에러를 핸들링하여 후작업 처리
                }
            })
            .catch((error) => {
            	// fetch로 전송하는 값에 대한 에러 핸들링
            });
        return false;
    }
}