Skip to content

Commit fdabedb

Browse files
committed
[옵셔널 체이닝] 충돌 해결 (본문)
1 parent 3c0d0d9 commit fdabedb

1 file changed

Lines changed: 36 additions & 149 deletions

File tree

  • 1-js/04-object-basics/07-optional-chaining

1-js/04-object-basics/07-optional-chaining/article.md

Lines changed: 36 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -3,156 +3,116 @@
33

44
[recent browser="new"]
55

6-
<<<<<<< HEAD
76
옵셔널 체이닝(optional chaining) `?.`을 사용하면 프로퍼티가 없는 중첩 객체를 에러 없이 안전하게 접근할 수 있습니다.
87

98
## 옵셔널 체이닝이 필요한 이유
10-
=======
11-
The optional chaining `?.` is a safe way to access nested object properties, even if an intermediate property doesn't exist.
12-
13-
## The "non-existing property" problem
14-
>>>>>>> upstream/master
159

1610
이제 막 자바스크립트를 배우기 시작했다면 옵셔널 체이닝이 등장하게 된 배경 상황을 직접 겪어보지 않았을 겁니다. 몇 가지 사례를 재현하면서 왜 옵셔널 체이닝이 등장했는지 알아봅시다.
1711

18-
<<<<<<< HEAD
1912
사용자가 여러 명 있는데 그중 몇 명은 주소 정보를 가지고 있지 않다고 가정해봅시다. 이럴 때 `user.address.street`를 사용해 주소 정보에 접근하면 에러가 발생할 수 있습니다.
2013

2114
```js run
2215
let user = {}; // 주소 정보가 없는 사용자
23-
=======
24-
As an example, let's say we have `user` objects that hold the information about our users.
25-
26-
Most of our users have addresses in `user.address` property, with the street `user.address.street`, but some did not provide them.
27-
28-
In such case, when we attempt to get `user.address.street`, and the user happens to be without an address, we get an error:
29-
30-
```js run
31-
let user = {}; // a user without "address" property
32-
>>>>>>> upstream/master
3316

3417
alert(user.address.street); // TypeError: Cannot read property 'street' of undefined
3518
```
3619

37-
<<<<<<< HEAD
20+
이는 예상된 결과입니다. 자바스크립트는 이런 식으로 동작합니다. `user.address``undefined`이므로 `user.address.street`를 가져오려는 시도는 에러를 발생시킵니다.
21+
22+
실제 개발 환경에서는 이런 상황에서 에러 대신 `undefined`를 반환받는 것이 더 나은 경우가 많습니다.
23+
3824
또 다른 사례론 브라우저에서 동작하는 코드를 개발할 때 발생할 수 있는 문제가 있습니다. 자바스크립트를 사용해 페이지에 존재하지 않는 요소에 접근해 요소의 정보를 가져오려 하면 문제가 발생하죠.
3925

4026
```js run
4127
// querySelector(...) 호출 결과가 null인 경우 에러 발생
4228
let html = document.querySelector('.my-element').innerHTML;
4329
```
4430

45-
명세서에 `?.`이 추가되기 전엔 이런 문제들을 해결하기 위해 `&&` 연산자를 사용하곤 했습니다.
46-
47-
예시:
48-
=======
49-
That's the expected result. JavaScript works like this. As `user.address` is `undefined`, an attempt to get `user.address.street` fails with an error.
31+
이 경우에도 요소가 존재하지 않으면 `null의` `.innerHTML` 프로퍼티에 접근하려 했기 때문에 에러가 발생합니다. 요소가 없는 것이 정상적인 상황일 때도 있는데, 이럴 때는 에러를 피하고 그냥 `html = null`을 결과로 받아들이고 싶을 것입니다.
5032

51-
In many practical cases we'd prefer to get `undefined` instead of an error here (meaning "no street").
33+
어떻게 하면 될까요?
5234

53-
...and another example. In Web development, we can get an object that corresponds to a web page element using a special method call, such as `document.querySelector('.elem')`, and it returns `null` when there's no such element.
35+
가장 직관적인 해결책은 프로퍼티에 접근하기 전에 `if`문이나 조건부 연산자 `?`를 사용해 값을 확인하는 것입니다. 다음과 같이 말이죠.
5436

55-
```js run
56-
// document.querySelector('.elem') is null if there's no element
57-
let html = document.querySelector('.elem').innerHTML; // error if it's null
5837
```
59-
60-
Once again, if the element doesn't exist, we'll get an error accessing `.innerHTML` property of `null`. And in some cases, when the absence of the element is normal, we'd like to avoid the error and just accept `html = null` as the result.
61-
62-
How can we do this?
63-
64-
The obvious solution would be to check the value using `if` or the conditional operator `?`, before accessing its property, like this:
65-
66-
```js
6738
let user = {};
6839
6940
alert(user.address ? user.address.street : undefined);
7041
```
7142

72-
It works, there's no error... But it's quite inelegant. As you can see, the `"user.address"` appears twice in the code.
43+
에러 없이 잘 동작하네요. 하지만 코드가 꽤 볼품없습니다. 보시다시피 "user.address"가 코드에 두 번이나 등장합니다.
7344

74-
Here's how the same would look for `document.querySelector`:
45+
`document.querySelector`를 사용한 예시는 다음과 같은 모습일 것입니다.
7546

76-
```js run
47+
```
7748
let html = document.querySelector('.elem') ? document.querySelector('.elem').innerHTML : null;
7849
```
7950

80-
We can see that the element search `document.querySelector('.elem')` is actually called twice here. Not good.
51+
요소 검색을 위한 `document.querySelector('.elem')`이 실제로 두 번이나 호출된 것을 볼 수 있습니다. 좋지 않은 방법이죠.
8152

82-
For more deeply nested properties, it becomes even uglier, as more repetitions are required.
53+
프로퍼티가 더 깊이 중첩되어 있다면 코드는 반복이 늘어나 더 지저분해질 것입니다.
8354

84-
E.g. let's get `user.address.street.name` in a similar fashion.
55+
비슷한 방식으로 `user.address.street.name`을 가져와 보겠습니다.
8556

8657
```js
87-
let user = {}; // user has no address
58+
let user = {}; // 주소 정보가 없는 사용자
8859

8960
alert(user.address ? user.address.street ? user.address.street.name : null : null);
9061
```
9162

92-
That's just awful, one may even have problems understanding such code.
63+
정말 끔찍하네요. 이런 코드는 이해하기조차 어려울 수 있습니다.
9364

94-
There's a little better way to write it, using the `&&` operator:
95-
>>>>>>> upstream/master
65+
명세서에 `?.`이 추가되기 전엔 이런 문제들을 해결하기 위해 `&&` 연산자를 사용하곤 했습니다.
9666

67+
예시 :
9768
```js run
9869
let user = {}; // 주소 정보가 없는 사용자
9970

100-
<<<<<<< HEAD
10171
alert( user && user.address && user.address.street ); // undefined, 에러가 발생하지 않습니다.
10272
```
10373

10474
중첩 객체의 특정 프로퍼티에 접근하기 위해 거쳐야 할 구성요소들을 AND로 연결해 실제 해당 객체나 프로퍼티가 있는지 확인하는 방법을 사용했었죠. 그런데 이렇게 AND를 연결해서 사용하면 코드가 아주 길어진다는 단점이 있습니다.
105-
=======
106-
alert( user.address && user.address.street && user.address.street.name ); // undefined (no error)
107-
```
10875

109-
AND'ing the whole path to the property ensures that all components exist (if not, the evaluation stops), but also isn't ideal.
76+
보시다시피 프로퍼티 이름이 코드 내에서 여전히 중복되기 때문입니다. 예를 들어 위 코드에서는 `user.address`가 세 번이나 등장합니다.
11077

111-
As you can see, property names are still duplicated in the code. E.g. in the code above, `user.address` appears three times.
112-
113-
That's why the optional chaining `?.` was added to the language. To solve this problem once and for all!
114-
>>>>>>> upstream/master
78+
바로 이런 문제를 완전히 해결하기 위해 옵셔널 체이닝(optional chaining) `?.`이 자바스크립트에 추가되었습니다!
11579

11680
## 옵셔널 체이닝의 등장
11781

118-
<<<<<<< HEAD
11982
`?.``?.`'앞'의 평가 대상이 `undefined``null`이면 평가를 멈추고 `undefined`를 반환합니다.
120-
=======
121-
The optional chaining `?.` stops the evaluation if the value before `?.` is `undefined` or `null` and returns `undefined`.
122-
>>>>>>> upstream/master
12383

12484
**설명이 장황해지지 않도록 지금부턴 평가후 결과가 `null`이나 `undefined`가 아닌 경우엔 값이 '있다' 혹은 '존재한다'라고 표현하겠습니다.**
12585

126-
<<<<<<< HEAD
12786

12887
이제 옵셔널 체이닝을 사용해 `user.address.street`에 안전하게 접근해봅시다.
129-
=======
130-
In other words, `value?.prop`:
131-
- works as `value.prop`, if `value` exists,
132-
- otherwise (when `value` is `undefined/null`) it returns `undefined`.
13388

134-
Here's the safe way to access `user.address.street` using `?.`:
135-
>>>>>>> upstream/master
89+
즉, `value?.prop`은 다음과 같이 평가됩니다.
90+
91+
* `value`가 존재하면 `value.prop`처럼 동작합니다.
92+
93+
* 그렇지 않은 경우(`value``undefined``null`일 때)에는 `undefined`를 반환합니다.
94+
95+
이제 `?.`을 사용해 `user.address.street`에 안전하게 접근해 봅시다.
96+
13697

13798
```js run
13899
let user = {}; // 주소 정보가 없는 사용자
139100

140101
alert( user?.address?.street ); // undefined, 에러가 발생하지 않습니다.
141102
```
142103
143-
<<<<<<< HEAD
144104
`user?.address`로 주소를 읽으면 아래와 같이 `user` 객체가 존재하지 않더라도 에러가 발생하지 않습니다.
145-
=======
146-
The code is short and clean, there's no duplication at all.
147105
148-
Here's an example with `document.querySelector`:
106+
코드가 짧고 깔끔해졌으며, 중복도 전혀 없습니다.
149107
150-
```js run
151-
let html = document.querySelector('.elem')?.innerHTML; // will be undefined, if there's no element
108+
`document.querySelector`를 사용한 예시를 살펴봅시다.
109+
110+
```javascript
111+
let html = document.querySelector('.elem')?.innerHTML; // 요소가 존재하지 않으면 undefined가 됩니다.
152112
```
153113
154-
Reading the address with `user?.address` works even if `user` object doesn't exist:
155-
>>>>>>> upstream/master
114+
`user?.address`로 주소를 읽으면 아래와 같이 `user` 객체가 존재하지 않더라도 에러가 발생하지 않고 잘 동작합니다.
115+
156116
157117
```js run
158118
let user = null;
@@ -163,26 +123,16 @@ alert( user?.address.street ); // undefined
163123
164124
위 예시를 통해 우리는 `?.``?.` '앞' 평가 대상에만 동작되고, 확장은 되지 않는다는 사실을 알 수 있습니다.
165125
166-
<<<<<<< HEAD
167126
참고로 위 예시에서 사용된 `user?.``user``null`이나 `undefined`인 경우만 처리할 수 있습니다.
168127
169128
`user``null`이나 `undefined`가 아니고 실제 값이 존재하는 경우엔 반드시 `user.address` 프로퍼티는 있어야 합니다. 그렇지 않으면 `user?.address.street`의 두 번째 점 연산자에서 에러가 발생합니다.
170-
=======
171-
E.g. in `user?.address.street.name` the `?.` allows `user` to safely be `null/undefined` (and returns `undefined` in that case), but that's only for `user`. Further properties are accessed in a regular way. If we want some of them to be optional, then we'll need to replace more `.` with `?.`.
172-
>>>>>>> upstream/master
173129
174130
```warn header="옵셔널 체이닝을 남용하지 마세요."
175131
`?.`는 존재하지 않아도 괜찮은 대상에만 사용해야 합니다.
176132

177-
<<<<<<< HEAD
178133
사용자 주소를 다루는 위 예시에서 논리상 `user`는 반드시 있어야 하는데 `address`는 필수값이 아닙니다. 그러니 `user.address?.street`를 사용하는 것이 바람직합니다.
179134

180-
실수로 인해 `user`에 값을 할당하지 않았다면 바로 알아낼 수 있도록 해야 합니다. 그렇지 않으면 에러를 조기에 발견하지 못하고 디버깅이 어려워집니다.
181-
=======
182-
For example, if according to our code logic `user` object must exist, but `address` is optional, then we should write `user.address?.street`, but not `user?.address?.street`.
183-
184-
Then, if `user` happens to be undefined, we'll see a programming error about it and fix it. Otherwise, if we overuse `?.`, coding errors can be silenced where not appropriate, and become more difficult to debug.
185-
>>>>>>> upstream/master
135+
이렇게 하면 실수로 `user``undefined`가 되었을 때 이를 알려주는 에러가 발생하므로 바로 확인하고 수정할 수 있습니다. 반대로 `?.`을 남용하면, 적절치 않은 상황에서도 에러가 조용히 무시되어 버리기 때문에 디버깅이 더욱 어려워집니다.
186136
```
187137
188138
````warn header="`?.`앞의 변수는 꼭 선언되어 있어야 합니다."
@@ -192,43 +142,25 @@ Then, if `user` happens to be undefined, we'll see a programming error about it
192142
// ReferenceError: user is not defined
193143
user?.address;
194144
```
195-
<<<<<<< HEAD
196145
`user?.anything`을 사용하려면 `let`이나 `const`, `var`를 사용해 `user`를 정의해야 하죠. 이렇게 옵셔널 체이닝은 선언이 완료된 변수를 대상으로만 동작합니다.
197-
=======
198-
The variable must be declared (e.g. `let/const/var user` or as a function parameter). The optional chaining works only for declared variables.
199-
>>>>>>> upstream/master
200146
````
201147

202148
## 단락 평가
203149

204150
`?.`는 왼쪽 평가대상에 값이 없으면 즉시 평가를 멈춥니다. 참고로 이런 평가 방법을 단락 평가(short-circuit)라고 부릅니다.
205151

206-
<<<<<<< HEAD
207152
그렇기 때문에 함수 호출을 비롯한 `?.` 오른쪽에 있는 부가 동작은 `?.`의 평가가 멈췄을 때 더는 일어나지 않습니다.
208-
=======
209-
So, if there are any further function calls or operations to the right of `?.`, they won't be made.
210-
211-
For instance:
212-
>>>>>>> upstream/master
213153

214154
```js run
215155
let user = null;
216156
let x = 0;
217157
218-
<<<<<<< HEAD
219158
user?.sayHi(x++); // 아무 일도 일어나지 않습니다.
220-
=======
221-
user?.sayHi(x++); // no "user", so the execution doesn't reach sayHi call and x++
222-
>>>>>>> upstream/master
223159
224160
alert(x); // 0, x는 증가하지 않습니다.
225161
```
226162

227-
<<<<<<< HEAD
228163
## ?.()와 ?.[]
229-
=======
230-
## Other variants: ?.(), ?.[]
231-
>>>>>>> upstream/master
232164

233165
`?.`은 연산자가 아닙니다. `?.`은 함수나 대괄호와 함께 동작하는 특별한 문법 구조체(syntax construct)입니다.
234166

@@ -246,7 +178,6 @@ let userAdmin = {
246178
let userGuest = {};
247179
248180
*!*
249-
<<<<<<< HEAD
250181
user1.admin?.(); // 관리자 계정입니다.
251182
user2.admin?.();
252183
*/!*
@@ -255,19 +186,6 @@ user2.admin?.();
255186
두 상황 모두에서 user 객체는 존재하기 때문에 `admin` 프로퍼티는 `.`만 사용해 접근했습니다.
256187

257188
그리고 난 후 `?.()`를 사용해 `admin`의 존재 여부를 확인했습니다. `user1``admin`이 정의되어 있기 때문에 메서드가 제대로 호출되었습니다. 반면 `user2``admin`이 정의되어 있지 않았음에도 불구하고 메서드를 호출하면 에러 없이 그냥 평가가 멈추는 것을 확인할 수 있습니다.
258-
=======
259-
userAdmin.admin?.(); // I am admin
260-
*/!*
261-
262-
*!*
263-
userGuest.admin?.(); // nothing happens (no such method)
264-
*/!*
265-
```
266-
267-
Here, in both lines we first use the dot (`userAdmin.admin`) to get `admin` property, because we assume that the `user` object exists, so it's safe read from it.
268-
269-
Then `?.()` checks the left part: if the `admin` function exists, then it runs (that's so for `userAdmin`). Otherwise (for `userGuest`) the evaluation stops without errors.
270-
>>>>>>> upstream/master
271189

272190
`.`대신 대괄호 `[]`를 사용해 객체 프로퍼티에 접근하는 경우엔 `?.[]`를 사용할 수도 있습니다. 위 예시와 마찬가지로 `?.[]`를 사용하면 객체 존재 여부가 확실치 않은 경우에도 안전하게 프로퍼티를 읽을 수 있습니다.
273191

@@ -278,13 +196,9 @@ let user1 = {
278196
firstName: "Violet"
279197
};
280198
281-
<<<<<<< HEAD
282199
let user2 = null; // user2는 권한이 없는 사용자라고 가정해봅시다.
283200
284201
let key = "firstName";
285-
=======
286-
let user2 = null;
287-
>>>>>>> upstream/master
288202
289203
alert( user1?.[key] ); // Violet
290204
alert( user2?.[key] ); // undefined
@@ -296,17 +210,11 @@ alert( user2?.[key] ); // undefined
296210
delete user?.name; // user가 존재하면 user.name을 삭제합니다.
297211
```
298212

299-
<<<<<<< HEAD
300213
```warn header="`?.`은 읽기나 삭제하기에는 사용할 수 있지만 쓰기에는 사용할 수 없습니다."
301214
`?.`은 할당 연산자 왼쪽에서 사용할 수 없습니다.
302-
=======
303-
````warn header="We can use `?.` for safe reading and deleting, but not writing"
304-
The optional chaining `?.` has no use on the left side of an assignment.
305-
>>>>>>> upstream/master
306215
307-
For example:
216+
예시:
308217
```js run
309-
<<<<<<< HEAD
310218
// user가 존재할 경우 user.name에 값을 쓰려는 의도로 아래와 같이 코드를 작성해 보았습니다.
311219

312220
user?.name = "Violet"; // SyntaxError: Invalid left-hand side in assignment
@@ -320,32 +228,11 @@ user?.name = "Violet"; // SyntaxError: Invalid left-hand side in assignment
320228
1. `obj?.prop` -- `obj`가 존재하면 `obj.prop`을 반환하고, 그렇지 않으면 `undefined`를 반환함
321229
2. `obj?.[prop]` -- `obj`가 존재하면 `obj[prop]`을 반환하고, 그렇지 않으면 `undefined`를 반환함
322230
3. `obj?.method()` -- `obj`가 존재하면 `obj.method()`를 호출하고, 그렇지 않으면 `undefined`를 반환함
323-
=======
324-
let user = null;
325-
326-
user?.name = "John"; // Error, doesn't work
327-
// because it evaluates to: undefined = "John"
328-
```
329-
330-
````
331-
332-
## Summary
333-
334-
The optional chaining `?.` syntax has three forms:
335-
336-
1. `obj?.prop` -- returns `obj.prop` if `obj` exists, otherwise `undefined`.
337-
2. `obj?.[prop]` -- returns `obj[prop]` if `obj` exists, otherwise `undefined`.
338-
3. `obj.method?.()` -- calls `obj.method()` if `obj.method` exists, otherwise returns `undefined`.
339-
>>>>>>> upstream/master
340231
341232
여러 예시를 통해 살펴보았듯이 옵셔널 체이닝 문법은 꽤 직관적이고 사용하기도 쉽습니다. `?.` 왼쪽 평가 대상이 `null`이나 `undefined`인지 확인하고 `null`이나 `undefined`가 아니라면 평가를 계속 진행합니다.
342233
343234
`?.`를 계속 연결해서 체인을 만들면 중첩 프로퍼티들에 안전하게 접근할 수 있습니다.
344235
345-
<<<<<<< HEAD
346236
`?.``?.`왼쪽 평가대상이 없어도 괜찮은 경우에만 선택적으로 사용해야 합니다.
347237
348238
꼭 있어야 하는 값인데 없는 경우에 `?.`을 사용하면 프로그래밍 에러를 쉽게 찾을 수 없으므로 이런 상황을 만들지 말도록 합시다.
349-
=======
350-
Still, we should apply `?.` carefully, only where it's acceptable, according to our code logic, that the left part doesn't exist. So that it won't hide programming errors from us, if they occur.
351-
>>>>>>> upstream/master

0 commit comments

Comments
 (0)