내가 참여하는 프로젝트 중, 최근에 이미지 업로드 기능을 유지∙보수할 기회가 있었다.
유지∙보수 요청은 이미지 다중 업로드 기능 추가였다.
기존 이미지 업로드 컴포넌트는 파일 한 개씩만 업로드 가능한 구조였다.
이 구조에서 다중 업로드를 구현하는 방법은 아래 2가지였다.
신규 개발이었다면 첫 번째 방법을 선택했겠지만, 다른 사람의 코드를 유지∙보수하는 입장에서 첫 번째 방법은 구조 변경 해야하는 영역이 컸고, 그로 인한 Side Effect도 걱정이었다.
그래서 나는 두 번째 방법을 선택했고, 결과는 처참했다.
다중 업로드를 하면, 여러 이미지가 업로드 되는 것이 아닌, 여러 이미지 중 마지막 이미지 하나만 화면에 render 되었다.
왜 이런 결과가 나왔고, 어떻게 이 문제를 해결했을까?
아래에서 자세히 알아보자.
setState()는 컴포넌트 내에서 로컬로 사용하는 state 변수를 업데이트할 수 있는 함수이다.
setState()를 통해 state를 업데이트 하면,
state 변화를 감지해 화면이 re-render 된다.
const [imgCnt, setImgCnt] = useState(0)
const initImgCnt = () => {
setImgCnt(0)
}
setState() 함수는 비동기적으로 동작한다.
모든 컴포넌트의 re-render가 시작하기 전까지 setState를 기다렸다가, 한 번에 state를 업데이트한다.
setState() 함수가 비동기로 동작하는 이유는 매번 state가 변할 때마다 화면을 re-render 한다면 성능 이슈가 발생할 수 있기 때문이다.
동기적으로 매번 업데이트 하는 것이 아닌, 비동기적으로 한 번에 업데이트 함으로써 불필요한 re-render를 방지한다.
언제 반환될지 모르는 변수를 어떻게 사용할 수 있을까?
조금 더 구체적으로, 비동기적으로 작동하는 setState()를 어떻게 동기적으로 사용할까?
방법은 아주 쉽다.
setState()의 인자로 객체가 아닌 updater 함수를 넘겨주면 된다.
const [imgCnt, setImgCnt] = useState(0)
const updater = (state, props) => {
return state + 1
}
// 이미지 개수 증가
const increaseImgCnt = () => {
setImgCnt(updater)
}
updater() 함수는 두 개의 인자를 받는다.
첫 번째 인자는 이전 state 값이고, 두 번째 인자는 props 값이 다.
updater 내부에서 updater() 함수는 우리를 현재 state 값에 접근할 수 있게 해, 우리가 state를 동기적으로 처리할 수 있게 한다.
const [imgList, setImgList] = useState([])
// 이미지 추가
const addImages = (file) => {
const updater = (prev) => [
...prev,
file,
]
setImgList(updater)
}
위 방법을 통해서 나는 이미지 다중 업로드 시, 비동기로 이미지 업로드 되었던 문제를 해결했다.