개발

자바스크립트 every 함수가 빈 배열을 true로 반환하는 이유

동고킴 2024. 4. 16. 22:00
반응형

지난 글 자바스크립트 every 함수 구현 (feat. ECMA-262)에 이은 every 함수에 관한 글이다.
자바스크립트 every 함수는 판단 로직이 무엇이든 간에 빈 배열은 무조건 true를 반환하는데 이에 대한 글이다.

원글은 https://humanwhocodes.com/blog/2023/09/javascript-wtf-why-does-every-return-true-for-empty-array/

 

자바스크립트 every가 빈 배열을 true로 반환하는 이유

JavaScript 언어의 핵심은 특정 부분이 어떻게 작동하는지 오해하기 쉽도록 충분히 크다. 저는 최근 이 메서드를 사용하는 일부 코드를 리팩토링하고 있었는데 every()에 숨어있는 논리를 실제로 이해하지 못했다는 사실을 발견했다. 내 생각에 콜백 함수를 호출하고 return으로 true를 반환 해야 한다고 가정했지만 실제로는 그렇지 않았다. 빈 배열의 경우 every() 함수의 콜백 함수가 호출되지 않기 때문에 콜백 함수가 무엇인지 관계없이 true가 반환된다. 다음을 코드를 살펴보자.

위 코드에서 isNumber 함수는 값이 숫자 타입인지 확인하는 함수다. 그리고 every를 사용하여 배열의 각 값이 숫자 타입인지 확인한다. 처음 네 번의 호출은 매우 간단하며 every()가 예상한 결과를 반환한다. 하지만 마지막 빈 배열의 경우 true를 반환하고 있다.

아래 예를 보자.

놀라운 결과가 나왔다. every에 항상 true 또는 false를 반환하는 함수를 콜백으로 넣었다. 이런 일이 발생할 수 있는 유일한 이유는 콜백이 호출되지 않고 every()의 기본값이 true를 반환하기 때문이다. 그렇다면 빈 배열이라서 콜백 함수를 실행할 값이 없는데 every()에서 true를 반환하는 이유는 무엇일까?

이 이유를 이해하려면 every의 스펙에서 이 내용을 어떻게 설명하고 있는지 살펴봐야 한다.

 

every() 구현체 살펴보기

ECMA-262에 정의된 Array.prototype.every()의 Pseudo-code는 아래와 같다.

 

이걸 자바스크립트로 구현하면 아래와 같다.

코드를 살펴보면 결과는 true로 가정하고 배열에 값이 있을 경우에만 콜백을 통해 false를 반환하는걸 볼 수 있다. 빈 배열이면 콜백 함수를 실행할 기회 자체가 없기 때문에 false를 반환할 수 없다.

그럼 every() 함수는 왜 이렇게 동작하게 만든 걸까?

 

수학과 자바스크립트에서 전체 양화사 (for all(모든 값에 대한)" 양화사(quantifier))

MDN 페이지에서 every() 함수가 빈 배열을 true로 반환하는 이유에 대한 답변을 볼 수 있다.

every acts like the "for all" quantifier in mathematics. In particular, for an empty array, it returns true. (It is vacuously true that all elements of the empty set satisfy any given condition.)

번역하면 아래와 같다.

every는 수학의 "모든 ~에 대해(for all)" 양화사처럼 작동합니다. 특별히 빈 배열은 true를 반환합니다. (빈 집합의 모든 요소가 주어진 조건을 만족하는 것은 공허 참입니다.)

"공허한 참(Vacuous truth)"은 주어진 조건이 충족될 수 없는 경우(즉, 주어진 조건이 참이 아닌 경우) 무언가가 참임을 의미하는 수학적 개념이다. 전제 자체가 거짓이라 거기에서 도출되는 것이 아무것도 없으므로 해당 참은 무의미하다는 의미이다. 이것을 자바스크립트로 표현하면 every() 함수에서 빈 배열에 대해 콜백을 호출할 수 없기 때문에, 즉 조건을 충족할 수 없기 때문에 true를 반환한다.

for all 양화사는 데이터 집합에 대해 추론할 수 있게 해주는 "공허한 참"라고 불리는 수학의 더 큰 주제의 일부다. 특히 형식화된 배열의 경우 수학적 계산을 수행하는 데 자바스크립트 배열의 중요성을 고려하면 이러한 작업을 기본적으로 지원하는 것이 합리적이다. every()가 이것의 유일한 예는 아니다.

 

수학과 자바스크립트에서의 존재 양화사

자바스크립트의 some() 함수는 존재 양화사를 구현한 것이다. 존재 양화사는 어떠한 빈 집합에 대해서도 결과가 거짓임을 나타낸다. 따라서 some() 함수는 빈 배열에 대해 false를 반환하고 콜백도 실행하지 않는다. 아래는 이에 대한 예제다.

 

다른 언어에서의 양화사

Collections이나 iterables에 대해 양화사를 구현한 프로그래밍 언어는 자바스크립트뿐만이 아니다.

  • Python : all() 함수는 전체 양화사. any() 함수는 존재 양화사
  • Rust : Iterator:all() 함수는 전체 양화사. any() 함수는 존재 양화사

 

"전체" every()의 의미

every()의 동작이 직관적이지 않다고 여기는 여부는 논쟁의 여지가 있다. 하지만 이런 생각과 관계없이 에러를 방지하기 위해서는 every()의 "전체" 특성을 염두에 두어야 한다. 즉, 만약 every() 또는 비어있을 수 있는 배열을 사용하려면, 미리 명시적인 검사를 추가해야 한다. 예를 들어, 만약 숫자 배열에 의존하는 연산이 있고 빈 배열에 대해서 연산이 실패해야 한다면, every()를 사용하기 전에 배열이 비었는지를 확인해야 한다.

다시 말하면, 이는 비어있을 때 연산에 사용하면 안 되는 배열이 있을 때만 중요하다. 그렇지 않은 경우라면 추가적인 확인이 없어도 된다.

 

결론

처음에는 빈 배열에 대한 every()의 동작에 놀랐지만, 연산의 더 넓은 맥락을 이해하고 이러한 기능이 다른 언어에도 확장된 것을 확인하면서 납득할 수 있었다. 만약 이런 동작 때문에 혼란스럽다면, every()를 마주할 때 생각하는 방법을 바꿔보는 걸 추천한다. every()를 "배열의 모든 아이템이 조건을 만족하는가?"로 읽는 대신, "배열에 조건을 만족하지 않는 아이템이 있는가?"로 생각해 봐라. 이러한 사고의 전환은 앞으로 자바스크립트 코드의 오류를 방지하는 데 도움이 될 것이다.

 

참고

반응형