요즘 몇몇 웹사이트 들어가보면 브라우저 상단에 ProgressBar가 동작하는 것을 확인 할 수 있습니다.
유투브, 구글애드센스 등 브라우저에서 요청을 하면 ProgressBar를 표시해서 사용자에게 나름(?) 사용성을 향상 시켜줍니다.
해당 글에서는 React 프로젝트에서 상단 ProgressBar를 구현하는 방법을 정리합니다.
아래의 주요 라이브러리를 사용했습니다.
- CRA(Create React App)
- Axios(비동기 요청 라이브러리)
- Material-UI(React UI 라이브러리)
01. 관련 라이브러리 설치
1
2
|
npm install --save axios
npm install --save @material-ui/core
|
cs |
material-ui의 progressbar 컴포넌트를 사용할 예정입니다. (https://material-ui.com/components/progress/#progress)
02. axios.create으로 httpClient 커스터마이징
axios의 인터셉터 기능을 이용해야 합니다.
요청/응답 시, 특정 코드를 실행하여합니다.
먼저 아래와 같이 껍데기를 만들어 봅시다.
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
|
import axios from 'axios';
const baseURL = (() => {
if (process.env.NODE_ENV === 'development') {
return 'http://localhost:3001/'
} else {
return '/'
}
})();
const defaultClient = axios.create({
baseURL,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json; charset=utf-8',
}
})
defaultClient.defaults.timeout = 3000;
defaultClient.interceptors.request.use(function (config) {
// 요청 인터셉터
return config
}, function (error) {
return Promise.reject(error);
});
defaultClient.interceptors.response.use(function (response) {
// 응답 인터셉터
return response;
}, function (error) {
return Promise.reject(error);
});
export default defaultClient;
|
cs |
axios.create로 axios wrapper를 만들고 baseURL, headers를 설정해줬습니다.
그리고 request와 response에 대한 interceptors 설정하였습니다.
이제 defaultClient로 비동기 요청을 하게 되면 인터셉터가 동작하게 됩니다.
위에서 선언한 defaultClient 모듈로 아래와 같이 Http 비동기 요청 코드를 작성 할 수 있습니다.
1
2
3
4
5
6
|
import defaultClient from "../../lib/defaultClient";
function getList(){
const { data } = defaultClient.get('/api/contents', {});
// 비지니스로직
}
|
cs |
03. setProgress, timer 구현
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
|
import axios from 'axios';
const baseURL = (() => {
...(중략)
})();
let progress = 0; // 0 ~ 100, 요청 진행률
let timerId = null; // timer id
function setProgress(value) {
progress = value;
}
function timer() {
if (progress < 98) {
const diff = 100 - progress;
const inc = diff / (10 + progress * (1 + progress / 100)); // 증가값
setProgress(progress + inc);
}
timerId = setTimeout(timer, 50); // 50 ms 단위로 timer 재귀호출
}
...(중략)
defaultClient.interceptors.request.use(function (config) {
setProgress(0);
timer(); // HTTP 요청시 timer 실행
return config
}, function (error) {
return Promise.reject(error);
});
defaultClient.interceptors.response.use(function (response) {
if (timerId) {
clearTimeout(timerId); // HTTP 응답시 timer 해제
timerId = null;
}
setProgress(100); // 진행도: 100 = 요청/응답 완료
return response;
}, function (error) {
return Promise.reject(error);
});
export default defaultClient;
|
cs |
위와 같이 작성해줍니다.
요청 시작 시 progress = 0, timer 시작
응답 완료 시 progress = 100 timer 중지
04. Progress 컴포넌트 작성
./component/MyProgress.js
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
|
import React from 'react'
import LinearProgress from '@material-ui/core/LinearProgress';
import { makeStyles, createStyles } from '@material-ui/styles';
import { Fade, createMuiTheme } from '@material-ui/core';
const useStyles = makeStyles(() =>
createStyles({
bar: {
position: 'fixed',
top: 0,
left:0,
width: '100%',
zIndex: 9999
},
}),
);
const Progress = (props) => {
const classes = useStyles({});
return (
<Fade in={props.active}>
<div className={classes.bar}>
<LinearProgress
color="primary"
variant="determinate"
value={props.progress}
/>
</div>
</Fade>
)
}
export default Progress
|
cs |
props는 아래 두개를 인자로 받을 것입니다.
- active : boolean, 화면표시 여부
- progress : number, 진행도(0 ~ 100)
그리고 material-ui에서 제공하는 css-in-js 모듈, createStyles로 css를 작성했습니다.
05. Progress 컨테이너 작성
./container/MyProgressContainer.js
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
|
import React, { Component } from 'react'
import MyProgress from '../components/MyProgress';
class ProgressContainer extends Component {
constructor(props: Props){
super(props);
this.state ={
progress: 0,
active: false
}
}
componentDidMount(){
window.progressbar = this // 중요, window 전역에 ProgressContainer 할당
}
onChange = (value) => {
if(value === 100){
// 응답완료 시, initProgress 콜백 호출
this.setState({
progress: value,
active:true
}, this.initProgress)
} else{
// progress 가 변할때마다 state 갱신
this.setState({
progress: value,
active:true
})
}
}
initProgress = () => {
setTimeout(() => {
this.setState({
active:false
progress: 0
})
}, 1000)
}
render() {
return (
<MyProgress
active={this.state.active}
progress={this.state.progress}
/>
)
}
}
export default ProgressContainer
|
cs |
06. setProgress 수정
1
2
3
4
5
|
function setProgress(value: number) {
progress = value;
window.progressbar.onChange(progress); // ProgressContainer의 함수 호출
}
|
cs |
앞서 작성한 defaultClient 모듈에 한줄 추가해줍니다.
그러고 나서 defaultClient로 HTTP 요청을 보내보세요.
상단에 Progressbar가 표시되고
완료되면 사라지는걸 확인 하실수 있습니다.
'개발 이야기 > React' 카테고리의 다른 글
React 이미지파일 미리보기 (2) | 2019.12.24 |
---|---|
React 프로젝트 Font 적용하기 (0) | 2019.11.16 |
Redux-Saga 로 비동기 요청 예제 (0) | 2019.08.18 |
React 이미지 파일 업로드 하기 (0) | 2019.06.24 |
React와 Express 서버 연동 시키기 (0) | 2019.04.28 |