2024. 9. 11. 16:55ㆍ프론트엔드 개발/React & RN
개발을 하다보면 상수 선언을 할 때가 많은데 React, 특히 React Native에 상수를 선언할 때 특히 주의해야 할 점이 있다.
컴포넌트 외부에 상수를 선언하면 그 상수는 컴포넌트의 갱신주기 및 라이프사이클을 완전히 벗어나게 된다. 일반적인 경우 큰 문제되지 않지만, 실제로 그것이 고정된 값(상수)가 아니라 어떤 조건에 따라 변화하는 값을 다룰땐 주의해야 한다. 특히 시간과 같이 실제로는 내부적으로 변화되어야 하는 값을 상수로 처리할때 주의해야 한다.
아래 예시를 보자.
참고로 moment는 현재 시간을 가져오기 위해 사용한 타임 라이브러리의 일종이다.
...
const NOW_TIME = moment() // 컴포넌트 외부에 선언된 상수
export default function ExampleComp () {
return (
<View>
<Text>{NOW_TIME.format('YYYY-MM-DD HH:mm:ss')}</Text>
</View>
)
}
여기서 NOW_TIME은 앱을 실행시키고 값을 한번 할당한 이후엔 앱을 재실행하지 않는 이상 절대 변하지 않는다.
...
export default function ExampleComp () {
const NOW_TIME = moment() // 컴포넌트 내부에 선언된 상수
return (
<View>
<Text>{NOW_TIME.format('YYYY-MM-DD HH:mm:ss')}</Text>
</View>
)
}
반면 이 NOW_TIME은 앱을 실행시키고 ExampleComp의 라이프사이클에 따르며 마운트 당시의 값을 지니게 된다. 즉, 앱 재실행 없이 해당 컴포넌트 마운트/언마운트에 따라 값이 변하게 된다.
이런 이유로 같은 상수 선언처럼 보여도 실제 결과는 완전 달라질 수 있다.
조금 더 구체적인 예시를 살펴보자.
가령 매일 어떤 시간 리스트를 받아서 18시 이후는 잘라내는 로직을 생각해보자.
아래와 같이 CUT_TIME이란걸 지정해서 18시 이후의 데이터는 컷하는 식으로 로직을 짤 수 있다.
...
const HOUR_18 = moment().set({hour: 18, minute: 0, second: 0, millisecond:0});
export default function ExampleComp ({timeList}: {timeList: ITimeObj[]}) {
const [list, setList] = useState([])
useEffect(() => {
const filteredList = timelist.filter((item)=>{
return item.isBefore(HOUR_18);
})
setList(filteredList);
}, [timeList])
return (
<View>
{list.map((i) => {
return (
<View>
<Text>{i.format('YYYY-MM-DD HH:mm:ss'}</Text>
</View>)
}}
</View>
)
}
이렇게 했을 경우 NOW_TIME이 앱 실행 날짜의 18시로 고정된다. 앱을 재실행하지 않는 이상 이 값은 절대 변하지 않는다.
만약 9월 11일에 앱을 실행시키고 백그라운드에 놓은채 9월 12일에 다시 그 앱을 포그라운드로 돌려놨다고 생각해보자.
서버는 9월 12일자 데이터를 내리고, 앱은 9월 12일자 18시 이전의 데이터를 그리길 원하겠지만, 실제로는 아무것도 그려지지 않는다.
왜냐면 18시 이전으로 필터링하기 위한 기준값이 HOUR_18이 앱 실행시점인 9월 11일자 18시로 고정되어 있기 때문에 9월 12일자 00시든, 10시든, 17시 55분이든 9월 11일 18시 이후이므로 아무것도 그리지 않는다.
결국 의도한 UI를 보고자 한다면 앱을 재실행하는 수밖에 없다. 이걸 해결하기 위해선 코드를 아래와 같이 바꾸면 된다.
...
export default function ExampleComp ({timeList}) {
const HOUR_18 = moment().set({hour: 18});
const [list, setList] = useState([])
useEffect(() => {
const filteredList = timelist.filter((item)=>{
return item.isBefore(HOUR_18);
})
setList(filteredList);
}, [timeList])
return (
<View>
{list.map((i) => {
return (
<View>
<Text>{i.format('YYYY-MM-DD HH:mm:ss'}</Text>
</View>)
}}
</View>
)
}
상수가 컴포넌트 안에 선언되어 해당 컴포넌트가 마운트 될 때 그 시간에 맞춰 재정의되니 원래 의도했던대로 작동한다.
웹은 일반적으로 오래 켜두는 일이 드물고 거의 매 실행이 재실행과 다름없지만,
React Native와 같이 앱의 경우 보통 앱을 백그라운드에 놔뒀다가 필요할때 포그라운드로 호출해서 쓰는 패턴이 일반적이다. 앱을 완전히 껏다가 재실행하는 경우가 거의 없으므로 컴포넌트 외부에 위치한 상수가 의도한대로 갱신되지 않을 수 있다.
내부적 로직에 따라 상수를 갱신해야 할때, 그 상수 선언은 이런 케이스처럼 조심해야 한다.
'프론트엔드 개발 > React & RN' 카테고리의 다른 글
JSX, TSX를 SVG로 변환하기 (3) | 2024.11.04 |
---|---|
Codepush Standalone 버전 출시 (코드푸쉬 대안) (0) | 2024.09.30 |
create-react-app 은 더이상 권장되지 않는 방식인가? (0) | 2023.07.17 |
React Native 안드로이드에서 absolute의 Touchable이 작동하지 않을 때 (0) | 2021.04.02 |
[번역] React Clean Code를 위한 팁 (8) | 2021.03.15 |