無테고리 인생살이

[Javascript] 왜 setTimeout()에는 bind()가 필요할까? 본문

Javascript

[Javascript] 왜 setTimeout()에는 bind()가 필요할까?

無격 2024. 11. 12. 16:16

들어가기 전에...


현재  ES5 이하 버전을 사용 중이거나, 화살표 함수에 익숙하지 않아 사용하기 어려운 분들을 위한 내용입니다. ES6의 화살표 함수로 setTimeout()을 작성하면, bind()를 사용하지 않아도 되기 때문입니다.

 

이번 글에서는 setTimeout()에서 bind()를 사용하는 이유와 그 역할에 대해 설명하겠습니다.

 


 

setTimeout()과 this 문제

JavaScript에서 this는 함수가 호출되는 방식에 따라 참조하는 객체가 달라집니다. setTimeout()의 경우 비동기적으로 실행되며, 기본적으로 전역 컨텍스트에서 동작하기 때문에, setTimeout() 내부에서 this를 사용하면 window 객체를 참조하게 됩니다. 만약 this가 현재 요소나 특정 객체를 가리키도록 하고 싶다면, this를 고정해둘 수 있는 함수가 필요합니다.

 

 

bind()를 사용하여 this 유지하기

비동기 함수인 setTimeout() 안에서 this를 원하는 객체로 유지하려면 bind()를 사용할 수 있습니다. bind() 메서드는 함수의 this 값을 우리가 원하는 객체로 고정해줍니다. 이렇게 하면 비동기 함수가 실행되더라도 this가 window 객체 또는 다른 객체로 바뀌지 않습니다.

 

아래는 setTimeout()에서 bind()를 사용한 예제입니다. 이 코드는 URL을 입력하는 input 필드에 여러 쿼리스트링이 포함된 URL이 붙여넣어질 때, id 값만 남도록 URL을 수정해주는 기능을 수행합니다.

 

결과 예시) https://example.com/detail?aaa=bbb&id=1323&ccc=ddd → https://example.com/detail?id=1323

 

$(function() {
    $("#url_link").on("paste", function() {
        setTimeout(function() {
            var pastedUrl = $(this).val(); // `this`가 원하는 대로 유지됨
            var exampleUrl = "https://example.com/detail?"; // 예시 URL
            
            var isExampleUrl = pastedUrl.includes(exampleUrl);
            var newUrl = "";
            
            // `id` 값이 있는 URL만 수정하기
            if (isExampleUrl && pastedUrl.includes("id=")) {
                var url = new URL(pastedUrl);
                var idValue = url.searchParams.get("id");
                
                if (idValue) {
                    newUrl = exampleUrl + "id=" + idValue;
                }
                
                if (newUrl === url) {
                    return;
                }
                
                // 사용자에게 URL 변경 여부 확인
                if (confirm(newUrl + " 로 수정하시겠습니까?")) {
                    $(this).val(newUrl); // URL 수정
                }
            }
        }.bind(this), 0); // `this`를 현재 컨텍스트에 바인딩
    });
});

붙여넣기 paste 이벤트에서 setTimeout()을 사용한 이유는...

paste 이벤트가 발생한 직후, 붙여넣기한 값이 화면에 즉시 로딩되지 않는 경우가 있습니다. 이때 $(this).val()을 사용하면 값이 완전히 로딩되기 전에 호출되어 원하는 값을 정확히 가져오지 못합니다.

이를 해결하는 방법은 setTimeout()을 사용하여 약간의 시간을 딜레이하는 것입니다. 0초를 주면(딜레이를 주지 않는 것을 의미하지 않습니다.), 화면에 모든 값이 로딩된 후 해당 로직이 실행되므로, 안전하게 $(this).val() 값을 가져올 수 있습니다. 

따라서, paste 이벤트를 사용할 때는 setTimeout()을 활용하여 로딩이 끝난 후에 안전하게 값을 처리하는 것이 좋습니다.

 

 

bind()는 왜 setTimeout()과 함께 사용해야 할까?

위 코드에서 bind(this)를 사용하지 않으면 this는 setTimeout() 내부에서 window 객체를 참조하게 됩니다. 즉, $(this).val()은 window 객체의 속성을 참조하려고 하기 때문에, 오류가 발생하거나 의도한 동작을 하지 않게 됩니다. 실제로 bind(this) 없이 console.log(this) 사용 시, window 객체를 출력합니다. bind(this)를 사용함으로써 this가 #url_link 요소를 가리키게 되어, 붙여넣은 URL을 원하는 방식으로 조작할 수 있게 됩니다.

 

 

 

setTimeout()에 bind()를 적용해야 하는 상황

setTimeout()을 사용할 때 다음과 같은 상황에서 bind()를 적용하면 유용합니다.

  1. 이벤트 핸들러 안에서 setTimeout()을 사용하는 경우: this가 특정 DOM 요소를 가리키도록 설정할 때.
  2. 비동기 작업에서 객체의 메서드를 호출하는 경우: 비동기 함수가 특정 객체를 계속 참조하도록 해야 할 때.
  3. 중첩 함수에서 현재 this를 유지하고 싶은 경우: 비동기 함수가 실행되는 동안 this가 계속 특정 객체를 참조해야 할 때.

 

 

결론

bind()는 JavaScript에서 this를 특정 객체에 고정하기 위해 필요한 메서드입니다. 이를 통해 setTimeout()이 실행되더라도, this가 원래 의도한 객체를 가리키게 되어, 코드에서 의도한 대로 동작합니다. 따라서, setTimeout()을 사용할 때 bind()를 적용하면, this를 정확하게 유지하여 코드가 예기치 않은 동작을 하지 않도록 보장할 수 있습니다.