인사이트

인사이트리포트

디지털프렌스포메이션 최신 정보 및 트렌드를 제공합니다.

SW 테크놀로지 클라우드 플랫폼

JavaScript Optional Chaining 소개와 사용 방법

2023.05.25원지혜 프로
다운로드

들어가며

ES2020부터 추가된 Optional chaining 기능[1]은 현대 JavaScript 프로그래밍에서 자주 사용하는 기능 중 하나다. Optional chaining을 사용하면 중첩 객체의 속성을 에러 없이 접근할 수 있기 때문에 이미 다른 프로그래밍 언어(C#[2], Swift[3], CoffeeScript[4], Kotlin[5] 등)에서도 유사한 기능을 지원하고 있다. 본 아티클에서는 optional chaining에 대한 소개와 함께, 안전하게 사용할 수 있는 방법을 사례와 함께 알아보도록 하겠다.

 

Optional Chaining 소개

Optional chaining은 객체 체인 마다 속성 값이 유효한지 명시적으로 검증하지 않고, 연결된 객체 체인 내에 깊숙이 위치한 속성에 접근할 수 있다. Optional chaining 연산자 `?.`를 객체 속성에 접근하거나 함수 호출에서 사용할 때, 객체 속성 값이 nullish(`null` 또는 `undefined`, 이하 널)이거나 함수가 없다면, 연산자 `?.` 왼쪽 표현식은 `undefined`로 단락(short-circuiting)되어 에러가 발생하지 않는다.

예를 들어 아래와 같이 사용자 API 응답(response) 객체가 있다고 가정해보면, 응답 객체에서 사용자의 도시(city)를 가져와야 한다고 할 때, optional chaining 없이 중첩된 하위 속성을 찾으려면, 다음과 같이 속성 값이 널인지 연속적으로 확인해야 한다.

10행에서 객체 하위 속성들을 확인할 때마다 `&&` 연산자로 연결하다 보니 코드가 길어져 코드 가독성이 떨어지게 된다. 만약 개발 중 객체 구조가 변경된다면, 코드 수정 비용이 늘어날 수도 있게 된다. 하지만 이러한 경우, Optional chaining을 사용하면 아래와 같이 짧고 간단하게 표현할 수 있어 코드 가독성뿐만 아니라 생산성에도 도움을 줄 수 있다.

마찬가지로 ES2020에 추가된 널 병합 연산자(nullish coalescing operator) `??` [6] 와 함께 사용하면, 객체의 속성에 안전하게 접근하면서 속성 값이 없는 경우 기본 값을 사용할 수 있다. 예를 들어 아래와 같이 사용자의 별명(nickname) 값을 가져올 수 있는데, 아래 보기와 같이 ‘profile’ 속성이 없지만 에러를 발생시키지 않고 좌측 표현식이 `undefined`로 평가되어, 기본 값 ‘Unknown’이 변수 userNickname에 할당된다.

 

Optional Chaining 문법

Optional chaining 연산자 `?.` 는 객체, 배열, 함수와 함께 사용할 수 있으며 다음과 같이 4가지 위치에 올 수 있다.

 

Optional Chaining 사용 방법

이미 ES2020 공개된 지 2년이 넘었기 때문에, 모든 최신 브라우저에서 Optional chaining 문법을 지원하고 있다. [1] Node.js는 14버전부터 지원하며, 그 이전 버전에서는 Babel과 함께 사용할 수 있고, TypeScript 3.7 버전부터 지원한다. Optional chaining이 유용한 만큼 GitHub에 있는 JavaScript, TypeScript, React, Vue 프로젝트에서도 많이 사용하고 있다. 하지만 주의 깊게 사용하지 않으면 에러를 발생시키는 사례들을 딥스캔(DeepScan)[8]을 통해 발견하였다. 딥스캔은 에스코어(S-Core)가 개발·운영 중인 JavaScript와 TypeScript 에 특화된 SaaS형 소스코드 검사 서비스로 정적 분석 기술을 활용해 GitHub 저장소에 커밋된 코드를 자동으로 분석하여 결함을 탐지하고 리포팅하는 역할을 하고 있다. 에스코어 딥스캔이 탐지한 Optional chaining과 관련된 결함 대부분은 널 포인터와 불충분한 널 체크와 연관이 있는 것으로 파악되었고, 각각의 경우에 대해 자세히 살펴보도록 하겠다.

Optional chaining 사용한 속성은 널 값을 가질 수 있다는 의미를 포함한다. 따라서 해당 속성이 널인 경우 TypeError가 발생하므로 딥스캔에서는 널 포인터(NULL_POINTER) 결함으로 탐지하고 있다. 특히, 표현식에 Optional chaining을 여러 번 사용하는 경우, TypeError 발생시 문제 원인이 되는 속성을 쉽게 파악하기 어렵다. 위 [그림 1]을 보면 변수 `isLoading`, `isError` 값이 falsy이고 `data.licenses` 배열이 널인 경우 `data.licenses[0]` 접근하면 에러가 발생한다. 따라서, 조건식 `!data?.licenses?.length`에서 `data.licenses`에 Optaional chaining을 사용하지 않고 `!data?.licenses.length`로 수정해야 한다고 판단할 수 있다. 그렇기 때문에 반드시 값이 있어야 하는 속성에는 Optional chaining을 사용하지 않도록 하고, 하나의 표현식에 여러 번 사용해야 한다면 주의 깊게 살펴봐야 한다.

Optional chaining은 사용자 장치, 브라우저에서 지원하지 않는 기능 때문에 사용할 수 없는 API 존재 여부를 확인할 때도 유용하게 사용할 수 있다. 하지만, 함수 반환 값이 필요한 상황에서 optional chaining 사용은 좀 더 주의가 필요하다. 위 [그림 2]처럼 `window.defaultUserLanguage` 메소드가 없으면 `language` 변수는 `undefined` 이므로 50행에서 `indexOf` 메소드 호출시 TypeError가 발생한다. 이러한 경우에는 `language` 변수에 기본 값을 지정하거나 널인 경우에 대비해야 할 것이다.

또한 함수 호출할 때 optional chaining 위치에 따라 의미가 완전히 달라지므로 구별해서 사용해야 한다. 즉, 함수 이름 뒤에 `()?.` 사용하면 함수 반환 값 존재를 확인하는 뜻이고, `?.()` 사용하면 함수 존재를 확인한다는 뜻이다. 특히 메소드 체이닝을 사용할 때 의도치 않은 동작을 발생하지 않으려면 `?.` 위치를 혼동하지 않고 사용해야 한다.

불충분한 널 체크(INSUFFICIENT_NULL_CHECK)는 optional chaining을 일관적으로 사용하지 않는 경우에 발생하는 결함이다. 위 [그림 3] 148행에 `variantStyle` 객체는 널 값일 수도 있다는 의미를 포함한다. 따라서 널인 경우 [그림 4] 151행 코드 수행할 때 에러가 발생할 수도 있다. 그렇다면 널 체크가 불충분 하므로, 151행에서 optional chaining을 사용해야 한다. 하지만 반대로 151행에서는 널 체크 없이 `variantStyle` 속성에 접근하고 있고 이는 항상 값을 갖고 있다는 의미이므로, `variantStyle` 객체에 optional chaining 사용은 불필요하다. Optional chaining은 널 체크를 쉽게 만들었지만, 일관적으로 사용하지 않는다면 에러를 발생할 수도 있고 개발 의도를 파악하기 어렵게 만들고는 하니 주의가 필요하다.

 

마치며

지금까지 Optional chaining에 대해 알아보고 안전하게 사용하는 방법에 대해 살펴보았다. Optional chaining은 웹 프로그래밍에서 많은 상황에서 유용하게 사용할 수 있는 기능이다. API 응답 객체 속성 접근 외에도 React 또는 Vue 컴포넌트에서 props 객체 속성 접근, 이벤트 리스너 존재 유무 검사에도 사용할 수 있다. 하지만, 새로운 기능을 개발하거나 레거시 코드를 개선할 때 Optional chaining을 많이 사용한다면 앞서 설명한 사례와 같이 예상치 못한 에러나 동작이 발생할 수 있다. 그렇기 때문에 이러한 잠재적인 결함을 미리 예상하고 사전에 수정한다면 코드의 신뢰성과 품질을 향상시킬 수 있을 것이다. 또한, 정적 분석 도구(인 에스코어 딥스캔과)와 Optional chaining을 함께 사용한다면 개발자가 놓칠 수 있는 결함을 빠르고 일관적으로 찾아 주기 때문에 좋은 선택지가 될 수 있으니 참고했으면 한다.

# References

[1] https://github.com/tc39/proposal-optional-chaining
[2] https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/member-access-operators?redirectedfrom=MSDN#null-conditional-operators--and-
[3] https://docs.swift.org/swift-book/documentation/the-swift-programming-language/optionalchaining/
[4] http://coffeescript.org/#existential-operator
[5] https://kotlinlang.org/docs/reference/null-safety.html#safe-calls
[6] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining
[7] https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#prod-OptionalExpression
[8] https://deepscan.io/docs/rules/#error


원지혜 프로

원지혜 프로

소프트웨어어사업부 개발플랫폼그룹

프로그래밍 언어를 전공하고 개발 플랫폼 분야에서 경력을 쌓아왔습니다. 현재 DeepScan 서비스를 개발하고 있습니다.

연관 아티클

  • Webpack5 Module Federation 소개
    SW 테크놀로지2024.02.02

    Webpack5 Module Federation 소개

    자세히 보기
  • MariaDB 서버 모니터링 및 성능 최적화: InnoDB Buffer Pool 2부
    데이터 관리2023.11.23

    MariaDB 서버 모니터링 및 성능 최적화: InnoDB Buffer Pool 2부

    자세히 보기
  • MariaDB 서버 모니터링 및 성능 최적화: InnoDB Buffer Pool 1부
    데이터 관리2023.11.10

    MariaDB 서버 모니터링 및 성능 최적화: InnoDB Buffer Pool 1부

    자세히 보기