애플리케이션의 비동기 처리는 빈번하게 발생하므로 비동기 요청의 응답을 기다리는 동안 사용자에게 로딩을 표시해줘야한다. 재사용이 가능한 컴포넌트로 로딩 컴포넌트를 만들어보자.

✏️ 접근성 고려

접근성을 고려하였을 때, 스크린 리더가 로딩중이 시작할 때와 로딩이 종료되었을 때를 읽을 수 있도록 하기 위해서는 public 폴더에 index.html 파일에 다음과 같이 기재가 되어있어야한다.

1
2
3
4
5
6
// public/index.html
<body>
<!-- 로딩 스피너 접근성을 위한 DOM 요소를 추가하세요. -->
<div id="loading-start" aria-live="assertive"></div>
<div id="loading-end" aria-live="assertive"></div>
</body>
  • aria-live="assertive"속성을 주어 다른 것보다 우선적으로 스크린 리더가 읽도록 설정해준다.

예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// Spinner.js
const loadingElements = {
start: document.getElementById("loading-start"),
end: document.getElementById("loading-end"),
};

export class Spinner extends React.Component {
static defaultProps = {
type: "connect", // 'grow', 'learn', 'connect'*
message: "로딩 중...",
showMessage: true,
timeToDisappear: {
start: 1500,
end: 2500,
},
};

render() {
const { type, message, showMessage } = this.props;
const spinnerImagePath = getAsset(`spinner/spinner-${type}.gif`);

return (
<figure className={styles.container}>
<img className={styles.image} src={spinnerImagePath} alt="" />
{showMessage ? (
<figcaption>{message}</figcaption>
) : (
<A11yHidden as="figcaption">{message}</A11yHidden>
)}
</figure>
);
}

componentDidMount() {
const { start } = loadingElements;
start.setAttribute("role", "alert");
start.insertAdjacentHTML(
"beforeend",
`<span class="a11yHidden">${this.props.message}</span>`
);
}

componentWillUnmount() {
const { start, end } = loadingElements;
const { timeToDisappear } = this.props;

setTimeout(() => {
start.removeAttribute("role");
start.innerHTML = "";
end.insertAdjacentHTML(
"afterbegin",
`<span class="a11yHidden">로딩이 종료되었습니다.</span>`
);
}, timeToDisappear.start);
setTimeout(() => {
end.innerHTML = "";
}, timeToDisappear.end);
}
}
  1. loading 요소를 반복적으로 사용할 것이기 때문에 최상단에 객체의 프로퍼티로 등록시켜주었다.
  2. Spinner 컴포넌트의 기본 props값을 설정해주었다.
    • 이는 컴포넌트를 만든 사람만 알 수 있기때문에 문서화를 하거나 TypeScript를 사용하여 개발자 경험(DX)를 높일 수 있다.
  3. 로딩중이라는 메시지를 보여주는 경우와 그렇지 않는 경우를 나누었다. 보여주지 않는 경우에는 접근성 컴포넌트로 생성하여 스크린 리더에는 읽히도록 설정해주었다.
  4. 컴포넌트가 mounted 될 때, role="alert"속성을 주어 스크린 리더가 읽고 있는 것을 중지하고 로딩중을 읽도록 설정하였다.
  5. StrictMode 에서는 mounted - unmounted - mounted 되는 특징때문에 2번 작동할 수 있으므로 성능을 고려하여 clean Up을 해줘야한다.