[javascript] 중첩된 객체에서 원하는 필드를 뽑아오기

개요

본 글에서는 중첩된 객체에서 어떤 값을 가져온다는 간단한 유틸리티 함수를 만듭니다. 하지만 이미 너무나도 유명한 유틸리티 라이브러리인 Lodash 등을 이용해보는 것이, 라이브러리 자체를 익히는 데 시간과 노력이 많이 들 수 있지만, 추후 생산성 측면에서 훨씬 좋아질 수도 있습니다.

동기

중첩된 객체에서 정보들을 한번에 손쉽게 가져오는 방법을 계속 재사용해야 했습니다. 예를 들어 obj 가 뭐가 올 지도 모르고 필요한 필드도 뭐가 될지는 모르는데, 거기다가 객체가 중첩되어 있을지도 모르는데 객체(중첩되어 있을 수 있음)와 필드 이름를 제공하면 거기에 대한 값을 가져온다는 로직을 간편하게 재사용하고 싶어서 함수를 만들어보고자 합니다. 예시 아래를 바로 참조해주세요. (코드도 바로 나와있습니다.) 이를 이용하면 mongoose 에서 search 필드를 간단하게 만들어 간단한 검색 기능 등을 구현해볼 수 있습니다.

코드와 실행 예제

오브젝트가 다음과 같이 주어졌을 때,

const obj = {
  a: 1,
  b: "hi",
  c: {
    hello: "hi",
    d: [{ name: "김씨" }, { name: "박씨" }, { name: "최씨", age: 24 }]
  }
};

다음과 같이 getValueOfField 함수를 수행한다면,

console.log(getValueOfField(obj, 'c.hello'));

다음과 같이 결과가 나오기를 기대합니다. 이는 obj.c.hello 의 값을 그대로 가져온 것입니다.

hi

이를 코드로 더 많은 예제와 함께 구현해본다면 다음과 같습니다.

원리

우선 field 를 해석해야 합니다. 가장 간단한 경우부터 봅시다. 필드에 .이 없을 경우에는 그냥 해당 값을 접근하여 리턴하면 끝입니다. .이 있는지 없는지의 판단은 String.prototype.indexOf 함수를 이용하면 됩니다. 이 함수는 어떤 substring 이 해당 string 에 존재한다면 해당 위치를 가져오고, 존재하지 않는다면 -1를 리턴합니다. 해당 string 이 복수로 존재한다면 가장 먼저 등장하는 요소의 위치를 가져옵니다.

  // .이 존재하는지를 체크하기 위해 pos 를 구함.
  const pos = field.indexOf(".");

  // '.'이 없다면 바로 리턴.
  if (pos === -1) {
    let value = obj[field];
  }

하지만 만약에 "c.hello"와 같이 .이 들어가게 된다면 어떻게 해야 할까요? 그렇다면 비슷한 로직을 두 번 수행해야 합니다. 첫번째로 c에 먼저 접근한 다음 hello 필드에 접근해야 합니다. 이는 재귀적으로 구현할 수 있습니다! 이를 간단한 그림으로 표현하면 다음과 같습니다.

첫번째 실행과 두번째 실행

여기서 이야기하는 첫번째 실행과 두번째 실행은 같은 단계에서 두 번 실행되는 것이 아니라, 재귀적으로 두 번 중첩된다는 뜻입니다. 이를 바로 코드로 하면 다음과 같습니다.

  // .이 있다면 다음 요소 탐색
  const nextObj = obj[field.slice(0, pos)];
  const nextField = field.slice(pos + 1);

  // 배열이 아닐 경우에는 그냥 하위 필드를 재귀하여 리턴
  return getValueOfField(nextObj, nextField);

pos 는 앞서 구해놨었던 .의 위치이고, 이를 기준으로 오른쪽 부분(field.slice(0, pos))으로 다음번 탐색할 객체를 지정해주고, 나머지 오른쪽 부분으로 다음 필드 디름을 지정해줍니다. 이렇게 되면 'c.hello'의 값을 구할 수 있게 되었습니다!

배열도 접근할 수 있도록 하기

배열 내 요소에 대해서도 접근할 수 있으면 좋을 것 같습니다. 위 예제에서 'c.d.name'을 하게 되면 ['김씨', '박씨', '최씨']가 나오도록 말이죠. 그럼 배열이 나왔을 때 다르게 처리해주면 되겠습니다! 다음 코드로 각 요소에 대해서 getValueOfField를 적용하여 리턴해 내보낼 수 있습니다.

  // nextObj가 배열일 경우 하나하나에 대한 하위 필드를 모아서 리턴하도록 함.
  if (Array.isArray(nextObj)) {
    return nextObj.map((item) => getValueOfField(item, nextField));
  }

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

Scroll to top