JavaScript 개발을 하다 보면 중첩된 객체 안에서 특정 값을 가진 키(key)가 무엇인지 찾아야 하는 경우가 종종 있습니다. 이 글에서는 임의의 깊이까지 중첩된 객체를 탐색하여, 지정한 값과 일치하는 모든 키를 배열로 반환하는 Object.prototype.keysOf() 메서드를 직접 구현하는 방법을 알아보겠습니다.
문제 정의
다음과 같은 객체가 있다고 가정해 봅시다.
const obj = {
"name": "Vivek Sharma",
"occupation": "Software Engineer",
"age": 23,
"contacts": [{
"name": "Mukul Sharma",
"occupation": "Software Engineer",
"age": 31,
}, {
"name": "Jay Sharma",
"occupation": "Software Engineer",
"age": 27,
}, {
"name": "Rajan Sharma",
"occupation": "Software Engineer",
"age": 32,
}]
};
위 예제는 한 단계까지만 중첩되어 있지만, 실무에서는 훨씬 더 깊은 구조의 객체를 다루는 경우도 많습니다. 우리의 목표는 이런 객체 어디에 있든 지정한 값을 가진 모든 키를 찾아내는 것입니다.
keysOf() 메서드 구현하기
구현의 핵심 아이디어는 다음과 같습니다.
Object.keys()로 현재 객체의 모든 키를 가져옵니다.- 각 키의 값이 찾고자 하는 값과 일치하면(
===엄격 비교) 결과 배열에 해당 키를 추가합니다. - 값이 객체이면서 배열이 아니라면, 재귀 호출을 통해 내부 프로퍼티까지 계속 탐색합니다.
전체 코드
const obj = {
"name": "Vivek Sharma",
"occupation": "Software Engineer",
"age": 23,
"contacts": [{
"name": "Mukul Sharma",
"occupation": "Software Engineer",
"age": 31,
}, {
"name": "Jay Sharma",
"occupation": "Software Engineer",
"age": 27,
}, {
"name": "Rajan Sharma",
"occupation": "Software Engineer",
"age": 32,
}]
};
const keysOf = function(val, obj = this, res = []){
const keys = Object.keys(obj);
for(let ind = 0; ind < keys.length; ind++){
if(obj[keys[ind]] === val){
res.push(keys[ind]);
} else if(typeof obj[keys[ind]] === 'object' &&
!Array.isArray(obj[keys[ind]])){
keysOf(val, obj[keys[ind]], res);
}
}
return res;
};
Object.prototype.keysOf = keysOf;
console.log(obj.keysOf(23));
함수 시그니처를 살펴보면, val은 찾고자 하는 값, obj는 탐색 대상 객체(기본값은 this, 즉 메서드를 호출한 객체), res는 결과를 누적할 배열입니다. 기본 매개변수(default parameter)를 활용하면 재귀 호출 시 별도의 초기화 없이 하나의 결과 배열을 계속 공유할 수 있습니다.
실행 결과
콘솔에는 다음과 같이 출력됩니다.
['age']
obj.keysOf(23)을 호출하면 값이 23인 키 'age'가 배열에 담겨 반환됩니다. 참고로 contacts는 배열이므로 Array.isArray() 체크에 의해 재귀 탐색 대상에서 제외됩니다. 만약 배열 요소 내부까지 탐색하고 싶다면 해당 조건을 제거하거나 수정하면 됩니다.