본문 바로가기
Study Note/Javascript

javascript #디자인패턴 - Self-defining function 패턴

by 시뮝 2021. 2. 12.
728x90

Self-defining function 패턴

앞서 살펴본 Init-time branching 패턴은 처음 웹페이지 초기화 단계에서 컴퓨팅 자원을 소모하여 향후 어떠한 방법으로 함수가 호출될 지 결정하는 것이었다면, Self-defining function 패턴은 위의 함수가 최초 실행되는 시기에 이것을 결정하여 설정하게 됩니다. 이렇게 Init-time branching 패턴의 초기화 시기를 조절하기 위한 상황 이외에도 함수 실행 전 초기화가 필요할 때 호출됩니다.

 

Self-defining function 패턴의 또 다른 사용 방법은 바로 함수가 한 번만 호출되어야할 때, 특히 XMLHttpRequest 등으로 서버에 요청을 보낼 때 사용하면 편리합니다.

 

예시1 - Self-defining function 패턴을 이용한 getXHR 정의

<!DOCTYPE html>
<html>
    <head>
    </head>
    <body>
        <script type="application/javascript">
            // Self-defining function 패턴을 이용한 getXHR 정의
            let getXHR = (function () {
                let xhr;
                if (window.XMLHttpRequest) {
                    getXHR = function () {
                        return new XMLHttpRequest();
                    };
                    return new XMLHttpRequest();
                }
                try {
                    xhr = new ActiveXObject("MSXML2.XMLHTTP.6.0");
                    getXHR = function () {
                        return new ActiveXObject("MSXML2.XMLHTTP.6.0");
                    }
                    return xhr;
                } catch (e) {
                    try {
                        xhr = new ActiveXObject("MSXML2.XMLHTTP.3.0");
                        getXHR = function () {
                            return new ActiveXObject("MSXML2.XMLHTTP.3.0");
                        }
                        return xhr;
                    } catch (e) {
                        console.log("This browser does not support XMLHttpRequest");
                    }
                }
            })
        </script>
    </body>
</html>

 

예시2 - Self-defining function 패턴을 이용한 함수 초기화

간단한 방명록을 만들어봅니다. 코멘트와 이름을 작성한 뒤 Sumit 버튼을 누르면 작성한 내용으로 Element가 추가되는 것을 확인할 수 있습니다. 이처럼 처음에 초기화 단계를 거치고 나면 이후에 같은 작업을 계속 반복하지 않아도 될 때는 초기화 단계의 정보를 클로저에 보관하고 변경되는 내용만 변경되도록 Self-defining function 패턴을 이용하면 좋습니다.

<!DOCTYPE html>
<html>
    <head>
        <style>
            #commentWrapper {
                width: 500px;
            }
            .comment {
                width: 200px;
                display: inline-block;
            }
            .name {
                width: 200px;
                display: inline-block;
            }
        </style>
    </head>
    <body>
        <div id="commentWrapper">
            <div>
                <div class="comment">Comment</div>
                <div class="name">Name</div>
            </div>
        </div>
        <form action="" id="formComment">
            <label>Comment: <input type="text" id="comment"/></label>
            <label>Name: <input type="text" id="name"/></label>
            <input type="submit"/>
        </form>
        
        <script>
        (function () {
            let addComment = function () {
                let divCommentWrapper = document.getElementById("commentWrapper"),
                    divCommentRow = document.createElement("div"),
                    divComment = document.createElement("div"),
                    divName = document.createElement("div"),
                    inputComment = document.getElementById("comment"),
                    inputName = document.getElementById("name");
                
                divComment.className = "comment";
                divName.className = "name";
                divCommentRow.appendChild(divComment);
                divCommentRow.appendChild(divName);
                
                addComment = function () {
                    divComment.innerHTML = inputComment.value;
                    divName.innerHTML = inputName.value;
                    
                    inputComment.value = "";
                    inputName.value = "";
                    
                    divCommentWrapper.appendChild(divCommentRow.cloneNode(true));
                };
                
                addComment();
            };
            
            document.getElementById("formComment").addEventListener("submit", function () {
                addComment();
                event.returnValue = false;
                return false;
            });
        }());
        </script>
    </body>
</html>

예시2 결과화면

 

 

 

예시3. Self-defining function 패턴을 이용한 인증 중복 방지

 

인증을 여러 번 요청하면 인증 절차에 문제가 발생하거나 계정 잠금이 발생할 수 있습니다. 이러한 상황은 페이지 새로고침이나 특히 사용자가 버튼을 여러 번 클릭하거나 엔터를 여러 번 누를 때 자주 발생합니다. 이럴 때는 Self-defining function을 통해 함수를 다시 정의하여 현재 요청 중일 때는 경고 창을 띄우도록 활용할 수 있습니다. 인증 이후에는 원래 인증 요청 함수로 설정해놔도 되고 다르게 응용할 수 있습니다.

<!DOCTYPE html>
<html>
    <head>
    </head>
    <body>
        <script>
        (function () {
            let requestAuthentication = function (information) {
                let _requestAuthentication = requestAuthentication;
                requestAuthentication = function (information) {
                    console.log("Already requesting");
                }
                sendRequest(information);
                
                function sendRequest(information) {
                    let xhr = new XMLHttpRequest();
                    xhr.open("POST", "/auth");
                    xhr.onload = function () {
                        console.log("Authorized!");
                        requestAuthentication = _requestAuthentication;
                    };
                    xhr.onerror = function () {
                        if (confirm("Error occurred, send again?")) {
                            sendRequest(information);
                        }
                    }
                    xhr.send(information);
                }
            }
            requestAuthentication("name=hello&password=word");
        }());
        </script>
    </body>
</html>

 

예시4. Self-defining function 패턴을 이용한 인증 정보 보관 및 중복 요청 방지

예시3과 같은 예를 보완하여 클로저에 해당 정보를 저장하고 있어도 좋습니다. 해당 변수를 한 번만 사용하고 사용하지 않을 것이라면 불필요한 클로저가 생성되고 데이터가 메모리에 계속 남아있을 수 있으니 undefined로 초기화 해주는 것이 좋습니다.

<!DOCTYPE html>
<html>
    <head>
    </head>
    <body>
        <script>
        (function () {
            let requestAuthentication = function (information) {
                let _requestAuthentication = requestAuthentication,
                    authInformation = null; // 인증 정보 보관용
                
                requestAuthentication = function (information) {
                    if (authInformation === null) {
                        console.log("Already requesting");
                        return;
                    } else {
                        return authInformation; // 인증 정보가 null이 아닐 경우 인증 정보 반환
                    }
                    
                }
                sendRequest(information);
                
                function sendRequest(information) {
                    let xhr = new XMLHttpRequest();
                    xhr.open("POST", "/auth");
                    xhr.onload = function () {
                        console.log("Authorized!");
                        //requestAuthentication = _requestAuthentication; //기존소스 주석
                        authInformation = xhr.responseText; //인증성공! 인증 정보를 담는다.
                    };
                    xhr.onerror = function () {
                        if (confirm("Error occurred, send again?")) {
                            sendRequest(information);
                        }
                    }
                    xhr.send(information);
                }
            }
            requestAuthentication("name=hello2&password=word2");
        }());
        </script>
    </body>
</html>

 

참고도서 : 속깊은 자바스크립트

728x90

댓글