Nested object
Nested object를 설명하기 위해 아래와 같은 클래스를 만들었다.
class Dto {
filter: Filter;
page: Page;
sort: Sort;
}
interface Filter {
[key: string]: {
operator: string;
value: string;
}
}
const dto = new Dto();
비즈니스 로직에서 dto.filter.search.value의 존재 유무에 따라 쿼리가 달라진다고 가정하자.
그럼 dto.filter.search.value의 존재 유무를 알 수 있는 방법은 뭘까?
if(dto.filter.search.value) {
// ... 생략
} else {
// ... 생략
}
이렇게 하면 처리가 될까? 안된다!
VM119:1 Uncaught TypeError: Cannot read properties of undefined (reading 'search')
at <anonymous>:1:12
dto.filter.search.value가 없더라도 로직을 계속 진행시키고 싶은데 런타임 에러가 터진다. 왜냐하면 중간에 이미 dto.filter가 가진 search라는 필드가 undefined인데 속성을 달라고 하니까 예외가 터지는 것이다.
이런식으로 객체 안에서 또 프로퍼티를 가진 객체가 나오는 구조를 Nested object라고 부른다.
그렇다면 어떻게 해결할까?!
프로퍼티 꺼내는 방법!
고전적인 방법은 깊어지는 속성들을 하나씩 검사하는 방법이다.
if(dto && dto.filter && dto.filter.search && dto.filter.search.value) {
// ... 생략
} else {
// ... 생략
}
checkNested() 함수를 만들어서 조금 더 간편하게 사용하는 방법도 있다. checkNested()는 액세스하려는 프로퍼티가 null 또는 undefined면 즉시 반환한다.
function checkNested(obj) {
var args = Array.prototype.slice.call(arguments, 1);
for (var i = 0; i < args.length; i++) {
if (!obj || !obj.hasOwnProperty(args[i])) {
return false;
}
obj = obj[args[i]];
}
return true;
}
var test = {level1:{level2:{level3:'level3'}} };
checkNested(test, 'level1', 'level2', 'level3');
checkNested(test, 'level1', 'level2', 'foo');
ES6 버전부터는 ...args 문법과 reduce를 사용해서 한 줄짜리 간단한 함수로 만들 수도 있다.
function getNested(obj, ...args) {
return args.reduce((obj, level) => obj && obj[level], obj)
}
const test = { level1:{ level2:{ level3:'level3'} } };
console.log(getNested(test, 'level1', 'level2', 'level3'))
그외에도 Lodash를 사용해서 _.get(object, 'filter[search].value');
와 같은 문법으로 사용하는 방법 등 여러가지가 있지만, 굳이 다루지 않겠다.
옵셔널 체이닝(Optional chaining)
ES2020에 추가된 옵셔널 체이닝(Optional chaining)은 이러한 문제를 현재로써 가장 나은 방법으로 해결한다.
const result = dto?.filter?.search?.value;
선택적 연결 연산자인 '?.'을 사용하여 프로퍼티에 액세스하면 해당 프로퍼티가 null 또는 undefined면 즉시 반환한다.
즉, 처음에 설명한 예제의 경우 별도의 함수 수행 없이 아래와 같이 코드를 작성할 수 있다.
if(dto?.filter?.search?.value) {
// ... 생략
else {
// ... 생략
}
만약 Typescript를 사용하고 있다면 위의 문장이 에러가 날 텐데 ES6의 객체 리터럴을 사용해서 그렇다.
interface Filter {
[key: string]: {
operator: string;
value: string;
}
}
그때는 Object property . 대신 []을 사용해서 dto?.filter?.["search"]?.value;
로 작성하면 된다.
Reference
'Web > HTML, CSS, JavaScript' 카테고리의 다른 글
Javascript - Ajax 여러개 결과 기다렸다가 특정 함수를 실행하는 방법 비교! (0) | 2022.05.05 |
---|---|
Javascript - DB 초성으로 검색하기! (+ Performance, 함수형, ...) (1) | 2022.03.26 |
Javascript - Null 병합 연산자 (null, undefined 검사 후 삼항 연산자 사용 개선) (0) | 2022.02.15 |
TypeScript - 유틸리티 타입을 사용하는 이유 (Partial, Pick, Omit) [DTO를 우아하게 분리] (0) | 2022.02.02 |
JavaScript - 객체(Object) (0) | 2020.10.28 |