본문 바로가기
개발/JavaScript

[JavaScript] 옵셔널 체이닝(Optional chaining) 사용법과 주의할점

by coking 2023. 2. 13.

자바스크립트에 Optional Chaining을 사용하면 함수나 객체의 속성을 호출할 때 호출하는 값이 정의되지 않았거나 falsy 한 값인 경우 오류를 발생시키는 대신 undefined를 반환하게 해 주어 예상치 못한 런타임 에러를 막아준다.

 

const adventurer = {
  name: 'Alice',
  cat: {
    name: 'Dinah'
  }
};


const dogName = adventurer.dog?.name;
console.log(dogName);
// Expected output: undefined

console.log(adventurer.someNonExistentMethod?.());
// Expected output: undefined

위 예시처럼 실제 존재하지 않는 객체의 dog를 호출하거나 함수를 호출해도 Optional Chaining 덕분에 에러가 발생하지 않고 undefined를 반환하고 있음을 확인할 수 있다. 만약 Optional Chaining을 사용하지 않았더라면 밑에 예시처럼 될 것이다.

 

const adventurer = {
  name: 'Alice',
  cat: {
    name: 'Dinah'
  }
};

const peopleName = adventurer.people.name;
console.log(peopleName);

// Error: Cannot read properties of undefined (reading 'name')

const dogName = adventurer.dog?.name;
console.log(dogName);

console.log(adventurer.someNonExistentMethod?.());

peopleName을 호출하자마자 밑에 코드들은 실행도 되기 전에

 

이렇듯 굉장히 유용하게 사용할 수 있는 Optional Chaining은 기존 코드를 리팩터링 할 때 주의할 점이 존재한다.

 

vscode 리팩터링 기능이나 여러 글들을 보면 a && a.b 이런 코드들을  a?.b로 바꾸는 경우를 많이 봤을 텐데 첫 번째로 그렇게 바꿨을 경우 값이 다른 경우들을 보여주겠다.

function test(value) {
    console.log(`${value && value.length}, ${value?.length}`);
}

test(undefined);       // undefined, undefined
test(null);            // null, undefined
test(true);            // undefined, undefined
test(false);           // false, undefined
test(1);               // undefined, undefined
test(0);               // 0, undefined
test({});              // undefined, undefined
test([]);              // 0, 0
test({ length: "a" }); // a, a
test('');              // , 0
test(NaN);             // NaN, undefined

주석의 값으로 보듯이 항상 같은 결과를 뱉고 있지 않다는 점에 유의하자 

 

두 번째로 빈 문자열은 falsy 하지만 nullish 하지 않는 값이므로 사용했을 시 문제가 발생한다.

function tt(s) {
	// without optional chaining
    if (s && s.length === 0) {
        // not called for the empty string
        console.log(1);
    }

    // with optional chaining
    if (s?.length === 0) {
        // called for the empty string
        console.log(2);
    }
}

tt('');
// Expected output: 2

빈 문자열을 사용하여 함수 호출 시 Optional Chaining코드 조건문은 통과하는 것에 유의하자

 

세 번째로 Optional Chaining코드는 null 대한 결과를 undefined로 바꾼다.  그러나 기존 && 코드 사용 시 null을 반환한다.

null && null.status;
// Expected output:null

null?.status
// undefined

const testObject = undefined;

// testObject가 존재 할 때만 status값을 확인하는 조건문을 쓰고자 할 시 아래와 같은 문제가 발생

// 의도한데로 testObject 값이 null이든 undefined이든 falsy한 값이면 조건문을 통과하지 못한다.
if (testObject && testObject.status !== "testStatus") { 
	console.log("테스트1");
}

// 옵셔널체이닝 사용시 값을 undefined로 바꿔버리기에 비교를 하면 undefined랑 비교를 하는 게 아닌 이상 무조건 다르다
if(testObject?.status !== "testStatus") {
	console.log("테스트2");
}


// Expected output: 테스트2

 

네 번째로 부작용이 있는 호출에 사용 시 문제가 발생할 수 있습니다. 혹시 부작용을 나쁜 작용으로 생각하시는 분들이 계시다면 부작용에 대해 검색해 한 번 뜻에 대해 이해하시는 걸 추천드립니다!!

f() && f().a  

↓

f()?.a


// f는 기존 두 번 호출되지만 옵셔널체이닝 사용 시 한 번으로 호출 횟수가 줄기에 만약에 f에 부작용이 존재하고
// 이를 감안해서 사용하고 있을 시 문제가 발생할 수 있습니다.

 

다섯 번째로 TypeScript에서는 void 형식의 Optional Chaining을 지원하지 않는다.

type Input = void | {
    property: string
};

function f(input: Input) {
    // this works:
    console.log(input && input.property);
    // this breaks because void is not undefined in TypeScript:
    console.log(input?.property);
}

 

마지막으로 Optional Chaining은 ES2020 기능이므로 모든 최신 브라우저 및 Node 14+에서 지원되지만 이전 브라우저 및 Node 버전의 경우 변환이 필요할 수 있다고 한다.

댓글