React Native FlatList Example using XML Request

XML 데이터를 웹에서 요청하여 해당 데이터를 FlatList로 표현하는 예제를 수행해보자. 본 글은 다음 링크에 있는 포스트를 참고했다.

프로젝트 생성

react-native 명령을 이용해 프로젝트를 생성하고 필요한 패키지를 설치한다.

$ react-native init FlatListDemo
$ cd FlatListDemo/
$ npm install x2js

기본 실행 확인

$ npx react-native run-ios

프로젝트 생성 후 바로 실행 했을 때 위와 같은 화면이 뜨면 정상적으로 수행 된 것이다.

데이터 fetch function 만들기

이제 FlatList를 이용해 데이터를 뿌리기 위해 먼저 데이터를 fetch하는 함수를 만든다. 데이터는 https://media.daum.net/syndication/today_sisa.rss 사이트에서 가져올 예정이다.

<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0">
  <channel>
    <title>다음뉴스 - 시사 주요뉴스 RSS</title>
    <link>http://media.daum.net/society/</link>
    <description />
    <language>ko</language>
    <copyright>Copyright (c) Kakao Corp. All rights reserved.</copyright>
    <pubDate>Fri, 27 Dec 2019 01:42:10 GMT</pubDate>
    <dc:creator>다음뉴스</dc:creator>
    <dc:date>2019-12-27T01:42:10Z</dc:date>
    <dc:language>ko</dc:language>
    <dc:rights>Copyright (c) Kakao Corp. All rights reserved.</dc:rights>
    <item>
      <title>"바른미래, 보수 흔적 묻은 '당명' 바꾸자"..손학규 "검토"</title>
      <link>https://v.daum.net/v/20191227103752096</link>
      <description>[서울=뉴시스] 유자비 최서진 기자 = 바른미래당이 바른정당계의 내년 1월 '새로운보수당' 창당 예고로 분당을 눈앞에 둔 가운데 27일 당 지도부에서 "우리도 이대로 남아선 안 된다"며 재창당 수준의 변화를 위해 당명을 바꿔야 한다는 주장이 나왔다. 바른미래당 당권파에 속하는 주승용 최고위원은 이날 오전 국회에서 열린 바른미래당 최고위원회의에서 "새보수</description>
      <enclosure url="https://t1.daumcdn.net/news/201912/27/newsis/20191227103752530ftjr.jpg" type="image/jpeg" />
      <pubDate>Fri, 27 Dec 2019 01:37:52 GMT</pubDate>
      <guid isPermaLink="false">media:news:20191227103752096</guid>
      <dc:creator>뉴시스</dc:creator>
      <dc:date>2019-12-27T01:37:52Z</dc:date>
    </item>
    <item>
      <title>北매체 "올해 남북관계 진전 못한 건 南외세의존 정책 때문"</title>
      <link>https://v.daum.net/v/20191227103616034</link>
      <description>[서울=뉴시스] 김성진 기자 = 북한 선전매체가 27일 올해 남북관계가 한 걸음도 진전하지 못한 이유에 대해 외세 눈치만 살피는 남한 정부의 그릇된 '외세의존정책' 때문이라고 비난했다. 북한은 최근 선전매체를 통해 한미 정부에 대해 비판의 목소리를 이어오고 있다. 북한 대외선전매체 우리민족끼리는 이날 '송년의 언덕에서 되새겨보는 진리'라는 제목의 글에서</description>
      <enclosure url="https://t1.daumcdn.net/news/201912/27/newsis/20191227103616931qbht.jpg" type="image/jpeg" />
      <pubDate>Fri, 27 Dec 2019 01:36:16 GMT</pubDate>
      <guid isPermaLink="false">media:news:20191227103616034</guid>
      <dc:creator>뉴시스</dc:creator>
      <dc:date>2019-12-27T01:36:16Z</dc:date>
    </item>

데이터를 보면 rss 밑에 channel이 있고 channel 아래에 item들이 있는 구조다.

소스코드 구현

FetchRSS.js

import X2JS from 'x2js'

const daum_url = 'https://media.daum.net/syndication/today_sisa.rss'


export const fetchHotEntry = () => {
    return fetchRss(daum_url)
}


const fetchRss = async (url) => {
    const response = await fetch(url)
    const rssText = await response.text()
    const x2js = new X2JS()
    const rss = x2js.xml2js(rssText)
    return rss.rss.channel
}

FetchRSS.js는 url로 부터 데이터를 가져오는 function을 구현한 파일이다. fetchRSS 함수를 보면 return 데이터가 XML의 channel 값인 걸 알 수 있다.

NewsListItem.js

import React from 'react'
import { StyleSheet, Text, View } from 'react-native'

export default class NewsListItem extends React.Component {
    render () {
        const item = this.props.item
        return (
            <View style={styles.item}>
                <Text
                    style={styles.itemTitle}
                    numberOfLines={3}
                    ellipsizeMode={'tail'}
                >
                    { item.title }
                </Text>
                <Text
                    style={styles.itemCreator}
                    numberOfLines={2}
                    ellipsizeMode={'tail'}
                >
                    { item.creator.toString() }
                </Text>
                <Text style={styles.itemDate}>{ item.pubDate.toString() }</Text>
            </View>
        )
    }
}

const styles = StyleSheet.create({
    item: {
        padding: 15,
        borderBottomColor: '#eee',
        borderBottomWidth: 1
    },
    itemTitle: {
        fontSize: 14,
        lineHeight: 14 * 1.6,
        fontWeight: 'bold'
    },
    itemCreator: {
        marginTop: 8,
        fontSize: 13,
        lineHeight: 13 * 1.6,
        color: '#666'
    },
    itemDate: {
        marginTop: 8,
        fontSize: 13,
        color: '#c00'
    }
})

NewsListItem.js는 리스트에서 한 행에 노출될 데이터를 정의하는 파일이다. 하나의 행에는 뉴스 제목, 뉴스 제작사, 뉴스 발행일을 표현할 수 있도록 했다.

NewsList.js

import React from 'react'
import { Text, View, FlatList } from 'react-native'
import NewsListItem from './NewsListItem'

export default class NewsList extends React.Component {
    state = {
        items: []
    }

    async componentDidMount () {
        const feed = await this.props.fetchEntry()
        const items = feed.item
        console.log(items)
        this.setState({ items })
    }

    _keyExtractor = (item, index) => item.link

    _renderItem = ({ item }) => (
        <NewsListItem item={item} />
    )

    render () {
        return (
            <FlatList
                data={this.state.items}
                extraData={this.state.items}
                keyExtractor={this._keyExtractor}
                renderItem={this._renderItem}
            />
        )
    }
}

NewsList.js는 앞서 만든 NewsListItem을 리스트로 배치하기 위해 작성한 소스코드다. 코드를 보면 fetchEntry()을 이용해 RSS 데이터를 받고 받은 데이터에서 XML의 <item> 값들을 items 변수에 넣는 곳을 알 수 있다.

App.js

import React from 'react'
import NewsList from './NewsList'
import { View } from 'react-native'
import { fetchHotEntry } from './FetchRSS'

export default class App extends React.Component {
  render() {
    return (
        <View style={{marginTop: 20}}>
          <NewsList fetchEntry={fetchHotEntry} />
        </View>
    )
  }
}

이제 앞서 만든 컴포넌트들을 불러와 메인에 띄워보자. App.js 파일을 위와 같이 설정하면 다음과 같은 실행 결과를 확인 할 수 있다.

위에서 실행한 모든 소스코드는 github를 참고하시기 바랍니다.

Leave a Reply