# 스와이퍼 제작

React Native Directory (opens new window)에는 다양한 RN 라이브러리들이 존재합니다.

스와이퍼 기능을 구현한 컴포넌트를 제작해둔 컴포넌트도 여럿 존재하며 이를 설치 후 설명에 맞게 가져다 사용하면 스와이퍼 기능을 사용할 수 있게됩니다. 이 중 react-native-web-swiper 사용법을 정리합니다.

  1. yarn add -D react-native-web-swiperSwiper 컴포넌트 임포트
  2. react-native-web-swiper를 감싸는 ScrollView Wrapper 컴포넌트 제작
import Swiper from 'react-native-web-swiper';
import styled from 'styled-components/native';

const Container = styled.ScrollView``;
const View = styled.View``;
const Lists = () => {
    return (
        <Container>
            <Swiper
                loop
                timeout={2}
                controlsEnabled={false}
                containerStyle={{ width: '100%', height: 300 }}
            >
                <View></View>
                <View></View>
                <View></View>
                <View></View>
            </Swiper>
        </Container>
    );
};

Swiper 컴포넌트에는 containerStyle이 있습니다. 스와이퍼 컴포넌트에 대한 전체 너비 및 높이를 지정할 수 있습니다.

스와이퍼 컴포넌트를 라이브러리로 설치하면 각종 프롭스 설정을 통해 여러 기능을 사용할 수 있다는 점입니다. 스와이퍼 자동 넘김 및 무한루프 기능도 제공하고 있습니다.

loop를 불리언으로 설정하면 무한루프가 적용되고 timeout을 사용하면 몇 초가 지난 뒤 자동 넘김이 이루어질 지 타이밍을 설정할 수 있습니다.

controlsEnabled프롭스는 react-native-web-swiper에서 기본제공하는 컨트롤러를 활성화 할지 말지 선택할 수 있습니다.

Image 컴포넌트

RN에서 이미지 컴포넌트에 이미지 삽입을 위해서는 프롭스로 source를 부여하고 객체를 전달해야합니다. 웹의 img 태그에서는 src 속성만 부여하면 이미지가 나왔었지만 RN에서는 source 프로퍼티 객체의 uri 프로퍼티에 URL을 삽입해야합니다.

uri는 웹 상에 있는 이미지를 불러올 때 사용하고 시스템 상의 상대경로에 위치한 이미지를 불러올 때에는 require("이미지 경로..")로 표기합니다.

# RefreshControl

ScrollViewFlatList 컴포넌트는 모두 refreshControl이라는 프롭스를 갖습니다. 해당 컴포넌트를 페이지의 루트 컴포넌트로 지정한 뒤 페이지를 위로 당길때 새로고침 효과를 갖게 됩니다.

refreshControl에는 리액트 네이티브에서 기본적으로 제공하는 RefreshControl컴포넌트를 전달합니다. LoadingIndicator와 동일하게 생긴 컴포넌트이며 페이지 최상단에서 페이지를 아래로 당길 때 나타나는 로딩 컴포넌트입니다.

RefreshControl 컴포넌트에는 refreshingonRefresh 프롭스를 전달해야 합니다.

  1. refreshing : 로딩 여부를 나타낼 불리언 값을 전달합니다. 데이터 fetch 여부에 따라 상태값으로 관리하는 변수를 전달합니다.
  2. onRefresh : 새로고침 될 때에 실행될 함수를 전달합니다. 데이터 fetch 로직을 이 함수에 구현하여 전달합니다.

refreshControl 초기 상태

RefreshControl 컴포넌트 프롭스의 refreshing값을 초기에 true로 주게 되면 페이지 상단에 로딩 인디케이터가 계속해서 돌고 있게 됩니다. 따라서 초기 상태로는 false를 전달하고, onRefresh에 따라 true로 바꿨다가 데이터 fetch가 모두 마무리 되면 그때 다시 false로 바꿔야 로딩 컴포넌트가 숨겨지게 됩니다.

<FlatList onRefresh={onRefresh} refreshing={refreshing} />
// or
<ScrollView refreshControl={<RefreshControl onRefresh={onRefresh} refreshing={refreshing} />} />

플랫리스트 컴포넌트는 RefreshControl 컴포넌트를 전달할 필요가 없습니다.

# FlatList

스크롤뷰 컴포넌트와 플랫리스트 컴포넌트는 기본적으로 여러 컴포넌트에 대해 스크롤 뷰를 제공한다는 점에서 동일합니다. 두 컴포넌트의 주요 차이점은 성능에서 발생합니다.

플랫리스트 컴포넌트는 lazy rendering이라는 기능을 기본적으로 갖습니다. 스크롤뷰 컴포넌트의 경우 데이터에 대한 정제를 따로 하지 않으면 배열의 map 메서드로 전달된 모든 데이터를 한 번에 렌더링 하게 된다는 문제점이 있습니다.

많은 양의 데이터를 부분적으로 보여주기 위해서는 스크롤뷰 컴포넌트가 아닌 플랫리스트 컴포넌트를 사용해야 합니다.

플랫리스트는 스크롤뷰와 거의 동일한 프롭스들을 제공합니다. (리프레시 등)

아래 코드는 플랫리스트 컴포넌트 예시입니다.

<FlatList
    horizontal
    showsHorizontalScrollIndicator={false}
    data={trending}
    keyExtractor={(item) => item + ''}
    renderItem={({ item }) => (
        <VMedia
            poster_path={item.poster_path}
            original_title={item.original_title}
            vote_average={item.vote_average}
        />
    )}
    ItemSeparatorComponent={() => <View style={{ width: 20 }} />}
    contentContainerStyle={{ paddingLeft: 30 }}
/>
  1. horizontal : 스크롤을 수평으로 사용합니다.
  2. showsHorizontalScrollIndicator : 스크롤을 보여줍니다. false설정 시 스크롤을 숨깁니다.
  3. data : 렌더링에 필요한 데이터를 지정합니다. 스크롤뷰에서의 map 메서드에 사용되는 배열데이터와 동일하게 활용되는 데이터입니다. (required)
  4. renderItem : data각 요소를 어떻게 표현할 지에 대해 함수 컴포넌트로 작성합니다. renderItem 프롭스는 객체를 파라미터로 받고 그 중 item 프로퍼티가 있는데, 해당 값이 data 프롭스에 전달되었던 배열 데이터 각 원소를 의미합니다.
  5. ItemSeparatorComponent : Seperator가 아닌 Separator입니다. renderItem으로 표현되는 각각의 컴포넌트 사이사이에 컴포넌트를 하나 집어넣습니다. 해당 프롭스는 함수를 받으며 리턴값으로 컴포넌트를 전달하면 됩니다.
  6. keyExtractor : 이 프롭스는 함수를 받습니다. renderItem과 같이 순회하는 아이템 각각의 컴포넌트에 대해 아이디값을 지정해줍니다.

ScrollView 안의 FlatList

플랫리스트 컴포넌트를 스크롤뷰 컴포넌트 안에 집어넣을때 동일하게 수직 방향을 바라보도록 하면 에러가 발생합니다.

VirtualizedLists should never be nested inside plain ScrollViews with the same orientation because it can break windowing and other functionality - use another VirtualizedList-backed container instead.

또한 플랫리스트 컴포넌트는 자식 컴포넌트를 가질 수 없으므로 최상단 스크롤뷰 컴포넌트를 플랫리스트로 대체할 수도 없습니다.

이를 위한 플랫리스트 프롭스로 ListHeaderComponent가 있습니다. 동일한 역할로 ListFooterComponent도 존재합니다.

화면 중앙에 데이터들을 표현할 플랫리스트 컴포넌트를 두고, 플랫리스트 기준 화면 위쪽에 다른 컴포넌트들을 모두 모으기 위한 것으로 ListHeaderComponent 프롭스가 사용되는 것이고 반대의 개념으로 ListFooterComponent가 사용됩니다.

<FlatList
    onRefresh={onRefresh}
    refreshing={refreshing}
    ListHeaderComponent={
        <>
            <HeaderComponents></HeaderComponents>
        </>
    }
    // ....
/>

# React Query로 리액트 네이티브 앱 새로고침 구현하기

다음의 문서 (opens new window)를 보면 useQuery 훅의 세 번째 파라미터 대한 데이터들을 확인할 수 있습니다.

새로고침의 주요 로직은 데이터들을 refetch 해온다는 것입니다. 따라서 useQuery 훅의 반환 객체 프로퍼티에 refetch를 설정하면 해당 API로의 refetch함수를 받을 수 있습니다.

또한 현재 refetch중인지 여부를 불리언 값으로 받을 수 있는 isRefetching 프로퍼티도 존재합니다.

const { isLoading, data, refetch, isRefetching } = useQuery(['key'], getData);

queryKey 활용하기

useQuery 훅의 쿼리 키는 배열 또는 문자열 데이터를 사용할 수 있습니다. 이때 쿼리키를 배열 데이터로 선언하게 되면 내부 데이터에 대한 형식은 상관이 없습니다.

쿼리키는 특정 동작을 갖는 것이 아니라 단순히 캐싱 데이터 접근을 위한 열쇠 역할만 하기 때문입니다.

 // An individual todo
 useQuery(['todo', 5], ...)
 // queryKey === ['todo', 5]

 // An individual todo in a "preview" format
 useQuery(['todo', 5, { preview: true }], ...)
 // queryKey === ['todo', 5, { preview: true }]

 // A list of todos that are "done"
 useQuery(['todos', { type: 'done' }], ...)
 // queryKey === ['todos', { type: 'done' }]

위와 같이 배열 내에 객체를 선언하더라도 이는 쿼리한 데이터 접근을 위한 열쇠 생김새롤 표현하는 것입니다.

쿼리키는 카테고리화 할 수가 있습니다. 배열 데이터 선언 후 위의 코드에서처럼 첫 번째 인덱스에 동일한 원소 데이터를 삽입해두는 것입니다.

부분적으로 쿼리 키와 일치하는 모든 API fetcher 함수로 요청을 보낼 수 있는 함수가 바로 queryClient.refetchQueries(["키값"]) 입니다.

앱의 첫 시작에서 QueryClientProvider의 프롭스로 provider를 전달하였습니다. 이때 전달된 queryClient 객체를 하위 컴포넌트에서 불러와 활용할 수 있게 되는겁니다.

queryClient 객체의 함수로 refetchQueries가 있고, 이 함수의 파라미터로 전달된 키값을 부분적으로 갖는 모든 API에 요청을 보내는 것입니다.

queryClient를 프로바이더로부터 불러오기 위해서는 useQueryClient 훅을 사용하면 됩니다.

function App() {
    const queryClient = useQueryClient();
    const onRefetch = async () => {
        queryClient.refetchQueries(['movies']);
    };
    return (
        //....
    )
}