JavaScript로 개발하다 보면 깊게 중첩된 객체 안에 특정 키가 실제로 존재하는지 확인해야 하는 경우가 자주 있습니다. 단순히 obj.a.b.c처럼 접근하면 중간 경로에 값이 없을 때 TypeError가 발생할 수 있기 때문에, 안전하게 존재 여부를 검사하는 함수를 만들어 두면 매우 유용합니다.
문제 상황
예를 들어 다음과 같이 빈 객체에 대한 참조가 있다고 가정해 보겠습니다.
let test = {};이 객체는 나중에 다음과 같이 여러 단계로 중첩된 객체를 가질 수 있습니다.
test = { level1: { level2: { level3: "level3" } } };여기서 우리가 작성해야 할 함수는 첫 번째 인자로 검사 대상 객체를 받고, 그 뒤로 임의 개수의 키 문자열을 인자로 받습니다. 그리고 전달된 키 문자열들이 나타내는 중첩 경로가 객체 안에 실제로 존재하는지 판단하여 true 또는 false를 반환해야 합니다.
구현 예제
이를 구현한 코드는 다음과 같습니다.
const checkNested = function(obj = {}){
const args = Array.prototype.slice.call(arguments, 1);
for (let i = 0; i < args.length; i++) {
if (!obj || !obj.hasOwnProperty(args[i])) {
return false;
}
obj = obj[args[i]];
 };
return true;
}
let test = {
level1:{
level2:{
level3:'level3'
}
}
};
console.log(checkNested(test, 'level1', 'level2', 'level3'));
console.log(checkNested(test, 'level1', 'level2', 'foo'));실행 결과
콘솔에 출력되는 결과는 다음과 같습니다.
true false
코드 동작 원리
이 함수의 핵심 로직은 다음과 같습니다.
1. 인자 분리: Array.prototype.slice.call(arguments, 1)을 사용해 첫 번째 인자(객체)를 제외한 나머지 키 문자열들을 배열로 만듭니다.
2. 순차적 탐색: 반복문을 돌면서 각 단계마다 현재 객체에 해당 키가 있는지 hasOwnProperty()로 확인합니다. 키가 없거나 객체가 null/undefined라면 즉시 false를 반환합니다.
3. 참조 갱신: 키가 존재하면 obj를 하위 객체로 갱신하며 한 단계씩 깊이 들어갑니다.
4. 최종 반환: 모든 키를 통과하면 해당 중첩 경로가 존재한다는 의미이므로 true를 반환합니다.
참고: 옵셔널 체이닝을 활용한 현대적인 방법
ES2020부터 도입된 옵셔널 체이닝(?.)을 사용하면 더 간결하게 표현할 수도 있습니다.
const checkNestedModern = (obj, ...keys) => keys.reduce((acc, key) => acc?.[key], obj) !== undefined; console.log(checkNestedModern(test, 'level1', 'level2', 'level3')); // true console.log(checkNestedModern(test, 'level1', 'level2', 'foo')); // false
다만 이 방식은 값으로 undefined가 저장된 경우와 키 자체가 없는 경우를 구분하지 못한다는 점에 유의해야 합니다. 정확한 존재 여부 검사가 필요하다면 앞서 소개한 hasOwnProperty() 기반 방식이 더 안전합니다.