React가 포함된 전체 스택 NLP: Ionic vs Cordova vs React Native

게시 됨: 2022-03-11

Apple이 첫 번째 iPhone을 출시한 후 약 15년 ​​동안 소프트웨어 개발 환경은 크게 바뀌었습니다. 스마트폰이 널리 채택되고 고유한 기능이 계속해서 성장함에 따라 사용자는 데스크톱이나 랩톱보다 모바일 장치를 통해 소프트웨어 서비스에 액세스하는 것을 점점 더 선호합니다. 스마트폰은 지리적 위치, 생체 인식 인증, 모션 감지와 같은 기능을 제공하며 이 중 많은 데스크톱 플랫폼이 이제 막 복제하기 시작했습니다. 일부 인구 통계에서는 스마트폰 또는 이와 유사한 모바일 장치가 컴퓨터를 완전히 우회하는 소프트웨어 소비의 주요 수단입니다.

기업들은 이러한 변화를 인지하고 주요 방식으로 이를 강화했습니다. 모바일 앱은 더 이상 뒷전이 아닙니다. 금융 중개 회사인 Robinhood, 소셜 미디어 회사인 Instagram, 차량 공유 회사인 Uber에 이르기까지 다양한 애플리케이션이 모바일 우선 개발 전략을 채택하고 있습니다. 데스크톱 애플리케이션이 있는 경우 주요 초점이 아닌 모바일 앱을 보완하기 위해 제공되는 경우가 많습니다.

풀 스택 개발자에게는 이러한 변화하는 추세에 적응하는 것이 중요합니다. 다행히도 웹 개발자가 모바일 개발에 자신의 기술을 적용하는 데 도움이 되는 성숙하고 잘 지원되는 기술이 많이 있습니다. 오늘 우리는 Cordova, Ionic 및 React Native의 세 가지 기술에 대해 알아볼 것입니다. 프론트엔드 웹 개발을 위한 가장 인기 있는 프레임워크 중 하나인 React.js를 핵심 개발 기술로 사용할 것입니다. iPhone 애플리케이션 개발에 중점을 둘 것이지만 이는 플랫폼 간 기술이며 Android 플랫폼과 교차 컴파일할 수 있습니다.

오늘 구축할 내용

NLP(자연어 처리)를 사용하여 Twitter 피드를 처리하고 선별하는 애플리케이션을 구축합니다. 이 애플리케이션을 통해 사용자는 Twitter 핸들 세트를 선택하고 Twitter API를 사용하여 최신 업데이트를 가져오고 감정과 주제에 따라 트윗을 분류할 수 있습니다. 그러면 사용자는 감정이나 주제에 따라 트윗을 볼 수 있습니다.

백엔드

프론트 엔드를 구축하기 전에 백엔드를 구축하고 싶을 것입니다. 지금은 백엔드를 단순하게 유지하겠습니다. 데이터 세트별 문제를 처리하기 위해 약간의 데이터 정리와 함께 기본 기성품 감정 분석 및 품사 태깅을 사용할 것입니다. TextBlob이라는 오픈 소스 NLP 라이브러리를 사용하고 Flask를 통해 결과를 제공합니다.

감정 분석, 품사 태깅 및 NLP: 빠른 입문서

이전에 자연어 분석 응용 프로그램을 사용해 본 적이 없다면 이러한 용어가 매우 생소할 수 있습니다. NLP는 자연어 데이터를 분석하고 처리하는 기술에 대한 포괄적인 용어입니다. 이것은 광범위한 주제이지만 이 영역을 다루는 모든 기술에 공통적인 많은 문제가 있습니다. 예를 들어, 인간 언어는 프로그래밍 언어나 수치 데이터와 달리 인간 언어 문법의 허용적 특성으로 인해 느슨하게 구조화되는 경향이 있습니다. 또한, 인간의 언어는 극단적으로 상황에 맞는 경향이 있으며 한 컨텍스트에서 발화되거나 쓰여진 문구는 다른 컨텍스트로 번역되지 않을 수 있습니다. 마지막으로, 구조와 맥락은 제쳐두고 언어는 매우 복잡합니다. 단락의 더 아래에 있는 단어는 단락 시작 부분에 있는 문장의 의미를 변경할 수 있습니다. 어휘는 발명, 재정의 또는 변경될 수 있습니다. 이러한 모든 복잡성으로 인해 데이터 분석 기술을 교차 적용하기가 어렵습니다.

감정 분석은 자연어 구절의 감정을 이해하는 데 중점을 둔 NLP의 하위 분야입니다. 인간의 감정은 본질적으로 주관적이어서 기술적으로 정의하기 어렵지만 감정 분석은 상업적 가능성이 매우 큰 하위 분야입니다. 감정 분석의 일부 응용 프로그램에는 다양한 기능에 대한 긍정적 및 부정적 평가를 식별하기 위해 제품 리뷰를 분류하고, 이메일 또는 연설의 분위기를 감지하고, 분위기별로 노래 가사를 그룹화하는 것이 포함됩니다. 감성 분석에 대한 더 자세한 설명을 찾고 있다면 여기에서 감성 분석 기반 애플리케이션 구축에 대한 제 기사를 읽을 수 있습니다.

품사 태깅 또는 POS 태깅은 매우 다른 하위 필드입니다. POS 태깅의 목표는 문법 및 문맥 정보를 사용하여 문장에서 주어진 단어의 품사를 식별하는 것입니다. 이 관계를 식별하는 것은 처음 보는 것보다 훨씬 더 어려운 작업입니다. 단어는 문맥과 문장의 구조에 따라 매우 다른 품사를 가질 수 있으며 규칙은 인간에게도 항상 명확하지 않습니다. 다행히 오늘날 많은 기성 모델은 대부분의 주요 프로그래밍 언어와 통합된 강력하고 다양한 모델을 제공합니다. 더 자세히 알고 싶다면 여기에서 POS 태깅에 대한 내 기사를 읽을 수 있습니다.

플라스크, TextBlob 및 Tweepy

NLP 백엔드의 경우 Flask, TextBlob 및 Tweepy를 사용합니다. Flask를 사용하여 작고 가벼운 서버를 구축하고 TextBlob을 사용하여 자연어 처리를 실행하고 Tweepy를 사용하여 Twitter API에서 트윗을 가져옵니다. 코딩을 시작하기 전에 Twitter에서 개발자 키를 가져와서 트윗을 검색할 수도 있습니다.

훨씬 더 정교한 백엔드를 작성하고 더 복잡한 NLP 기술을 사용할 수 있지만 오늘의 목적을 위해 백엔드를 최대한 단순하게 유지합니다.

백엔드 코드

이제 코딩을 시작할 준비가 되었습니다. 좋아하는 Python 편집기와 터미널을 실행하고 크래킹을 시작합니다!

먼저 필요한 패키지를 설치하려고 합니다.

 pip install flask flask-cors textblob tweepy python -m textblob.download_corpora

이제 기능에 대한 코드를 작성해 보겠습니다.

새 Python 스크립트를 열고 server.py라고 하고 필수 라이브러리를 가져옵니다.

 import tweepy from textblob import TextBlob from collections import defaultdict

이제 몇 가지 도우미 함수를 작성해 보겠습니다.

 # simple, average a list of numbers with a guard clause to avoid division by zero def mean(lst): return sum(lst)/len(lst) if len(lst) > 0 else 0 # call the textblob sentiment analysis API and noun phrases API and return it as a dict def get_sentiment_and_np(sentence): blob = TextBlob(sentence) return{ 'sentiment': mean([s.sentiment.polarity for s in blob.sentences if s.sentiment.polarity != 0.0]), 'noun_phrases': list(blob.noun_phrases) } # use the tweepy API to get the last 50 posts from a user's timeline # We will want to get the full text if the text is truncated, and we will also remove retweets since they're not tweets by that particular account. def get_tweets(handle): auth = tweepy.OAuthHandler('YOUR_DEVELOPER_KEY') auth.set_access_token('YOUR_DEVELOPER_SECRET_KEY') api = tweepy.API(auth) tl = api.user_timeline(handle, count=50) tweets = [] for tl_item in tl: if 'retweeted_status' in tl_item._json: Continue # this is a retweet if tl_item._json['truncated']: status = api.get_status(tl_item._json['id'], tweet_mode='extended') # get full text tweets.append(status._json['full_text']) else: tweets.append(tl_item._json['text']) return tweets # http and https are sometimes recognized as noun phrases, so we filter it out. # We also try to skip noun phrases with very short words to avoid certain false positives # If this were a commercial app, we would want a more sophisticated filtering strategy. def good_noun_phrase(noun_phrase): noun_phrase_list = noun_phrase.split(' ') for np in noun_phrase_list: if np in {'http', 'https'} or len(np) < 3: return False return True

이제 도우미 함수가 작성되었으므로 몇 가지 간단한 함수로 모든 것을 결합할 수 있습니다.

 # reshapes the tagged tweets into dictionaries that can be easily consumed by the front-end app def group_tweets(processed_tweets): # Sort it by sentiment sentiment_sorted = sorted(processed_tweets, key=lambda x: x['data']['sentiment']) # collect tweets by noun phrases. One tweet can be present in the list of more than one noun phrase, obviously. tweets_by_np = defaultdict(list) for pt in processed_tweets: for np in pt['data']['noun_phrases']: tweets_by_np[np].append(pt) grouped_by_np = {np.title(): tweets for np, tweets in tweets_by_np.items() if len(tweets) > 1 and good_noun_phrase(np)} return sentiment_sorted, grouped_by_np # download, filter, and analyze the tweets def download_analyze_tweets(accounts): processed_tweets = [] for account in accounts: for tweet in get_tweets(account): processed_tweet = ' '.join([i for i in tweet.split(' ') if not i.startswith('@')]) res = get_sentiment_and_np(processed_tweet) processed_tweets.append({ 'account': account, 'tweet': tweet, 'data': res }) sentiment_sorted, grouped_by_np = group_tweets(processed_tweets) return processed_tweets, sentiment_sorted, grouped_by_np

이제 팔로우하려는 핸들 목록에서 download_analyze_tweets 함수를 실행할 수 있으며 결과가 표시되어야 합니다.

다음 코드를 실행했습니다.

 if __name__ == '__main__': accounts = ['@spacex', '@nasa'] processed_tweets, sentiment_sorted, grouped_by_np = download_analyze_tweets(accounts) print(processed_tweets) print(sentiment_sorted) print(grouped_by_np)

이를 실행하면 다음과 같은 결과가 나타납니다. 결과는 분명히 시간에 따라 달라지므로 비슷한 내용이 표시되면 올바른 방향으로 가고 있는 것입니다.

 [{'account': '@spacex', 'tweet': 'Falcon 9… [{'account': '@nasa', 'tweet': 'Our Mars rove… {'Falcon': [{'account': '@spacex', 'tweet': 'Falc….

이제 매우 간단한 Flask 서버를 구축할 수 있습니다. server.py라는 빈 파일을 만들고 다음 코드를 작성합니다.

 from flask import Flask, request, jsonify from twitter import download_analyze_tweets from flask_cors import CORS app = Flask(__name__) CORS(app) @app.route('/get_tweets', methods=['POST']) def get_tweets(): accounts = request.json['accounts'] processed_tweets, sentiment_sorted, grouped_by_np = download_analyze_tweets(accounts) return jsonify({ 'processedTweets': processed_tweets, 'sentimentSorted': sentiment_sorted, 'groupedByNp': grouped_by_np }) if __name__ == '__main__': app.run(debug=True)

서버를 실행하면 이제 선택한 HTTP 클라이언트를 사용하여 서버에 게시 요청을 보낼 수 있습니다. {accounts: [“@NASA”, “@SpaceX”]}를 json 인수로 전달하면 API가 Twitter 분석 코드에서 반환된 것과 유사한 것을 반환해야 합니다.

이제 서버가 있으므로 프런트 엔드를 코딩할 준비가 되었습니다. 전화 에뮬레이터를 통한 네트워킹의 미묘한 차이 때문에 API를 어딘가에 배포하는 것이 좋습니다. 그렇지 않으면 앱이 에뮬레이터에서 실행 중인지 감지하고 에뮬레이터에 있을 때 localhost:5000 <Your Computer IP>:5000 으로 요청을 보내고 싶을 것입니다. 코드를 배포하는 경우 해당 URL에 대한 요청을 간단히 실행할 수 있습니다.

서버를 배포하기 위한 많은 옵션이 있습니다. 최소한의 설정이 필요한 무료 간단한 디버그 서버의 경우 상자에서 꺼내자마자 이 서버를 실행할 수 있는 PythonAnywhere와 같은 것을 권장합니다.

백엔드 서버를 코딩했으므로 이제 프론트엔드를 살펴보겠습니다. 웹 개발자에게 가장 편리한 옵션 중 하나인 Cordova부터 시작하겠습니다.

아파치 코르도바 구현

코르도바 프라이머

Apache Cordova는 웹 개발자가 모바일 플랫폼을 대상으로 하는 데 도움이 되는 소프트웨어 기술입니다. Cordova는 스마트폰 플랫폼에 구현된 웹 브라우저 기능을 활용하여 웹 애플리케이션 코드를 기본 애플리케이션 컨테이너로 래핑하여 애플리케이션을 생성합니다. 그러나 Cordova는 단순히 멋진 웹 브라우저가 아닙니다. Cordova API를 통해 웹 개발자는 오프라인 지원, 위치 서비스 및 온디바이스 카메라와 같은 다양한 스마트폰 관련 기능에 액세스할 수 있습니다.

우리 애플리케이션의 경우 React.js를 JS 프레임워크로 사용하고 React-Bootstrap을 CSS 프레임워크로 사용하여 애플리케이션을 작성합니다. 부트스트랩은 반응형 CSS 프레임워크이기 때문에 이미 더 작은 화면에서 실행을 지원합니다. 애플리케이션이 작성되면 Cordova를 사용하여 웹 애플리케이션으로 컴파일합니다.

앱 구성

Cordova React 앱을 설정하기 위해 고유한 작업을 시작합니다. Medium 기사에서 개발자 Shubham Patil은 우리가 하는 일에 대해 설명합니다. 기본적으로 우리는 React CLI를 사용하여 React 개발 환경을 설정한 다음 Cordova CLI를 사용하여 Cordova 개발 환경을 설정한 다음 최종적으로 둘을 병합합니다.

시작하려면 코드 폴더에서 다음 두 명령을 실행합니다.

 cordova create TwitterCurationCordova create-react-app twittercurationreact

설정이 완료되면 React 앱의 public 및 src 폴더 내용을 Cordova 앱으로 이동하려고 합니다. 그런 다음 package.json에서 스크립트, 브라우저 목록 및 React 프로젝트의 종속성을 복사합니다. 또한 "homepage": "./" 를 package.json의 루트에 추가하여 Cordova와의 호환성을 활성화합니다.

package.json이 병합되면 Cordova와 함께 작동하도록 public/index.html 파일을 변경하려고 합니다. 파일을 열고 www/index.html에서 메타 태그를 복사하고 Cordova.js가 로드될 때 본문 태그 끝에 있는 스크립트를 복사합니다.

다음으로 src/index.js 파일을 변경하여 Cordova에서 실행 중인지 감지합니다. Cordova에서 실행 중인 경우 deviceready 이벤트 핸들러 내에서 렌더 코드를 실행하려고 합니다. 일반 브라우저에서 실행 중인 경우 바로 렌더링하세요.

 const renderReactDom = () => { ReactDOM.render( <React.StrictMode> <App /> </React.StrictMode>, document.getElementById('root') ); } if (window.cordova) { document.addEventListener('deviceready', () => { renderReactDom(); }, false); } else { renderReactDom(); }

마지막으로 배포 파이프라인을 설정해야 합니다. config.xml 파일에 아래 정의를 추가합니다.

<hook type="before_prepare" src="hooks/prebuild.js" />

그리고 다음 스크립트를 prebuild.js에 넣습니다.

 const path = require('path'); const { exec } = require('child_process'); const fs = require('fs'); const rimraf = require('rimraf'); function renameOutputFolder(buildFolderPath, outputFolderPath) { return new Promise((resolve, reject) => { fs.rename(buildFolderPath, outputFolderPath, (err) => { if (err) { reject(err); } else { resolve('Successfully built!'); } }); }); } function execPostReactBuild(buildFolderPath, outputFolderPath) { return new Promise((resolve, reject) => { if (fs.existsSync(buildFolderPath)) { if (fs.existsSync(outputFolderPath)) { rimraf(outputFolderPath, (err) => { if (err) { reject(err); return; } renameOutputFolder(buildFolderPath, outputFolderPath) .then(val => resolve(val)) .catch(e => reject(e)); }); } else { renameOutputFolder(buildFolderPath, outputFolderPath) .then(val => resolve(val)) .catch(e => reject(e)); } } else { reject(new Error('build folder does not exist')); } }); } module.exports = () => { const projectPath = path.resolve(process.cwd(), './node_modules/.bin/react-scripts'); return new Promise((resolve, reject) => { exec(`${projectPath} build`, (error) => { if (error) { console.error(error); reject(error); return; } execPostReactBuild(path.resolve(__dirname, '../build/'), path.join(__dirname, '../www/')) .then((s) => { console.log(s); resolve(s); }) .catch((e) => { console.error(e); reject(e); }); }); }); };

이것은 React 빌드를 실행하고 Cordova 빌드가 시작되기 전에 빌드 폴더를 적절한 위치에 배치하여 배포 프로세스를 자동화합니다.

이제 앱을 실행해 볼 수 있습니다. 명령줄에서 다음을 실행합니다.

 npm install rimraf npm install npm run start

브라우저에서 React 앱이 설정되고 실행되는 것을 볼 수 있을 것입니다. 지금 Cordova를 추가하십시오.

 cordova platform add iOS cordova run iOS

그리고 에뮬레이터에서 실행 중인 React 앱이 표시되어야 합니다.

라우터 및 패키지 설정

앱을 빌드하는 데 필요한 일부 인프라를 설정하려면 필수 패키지를 설치하는 것으로 시작하겠습니다.

 npm install react-bootstrap react-router react-router-dom

이제 라우팅을 설정하고 그렇게 하는 동안 모든 구성 요소에서 공유할 간단한 전역 상태 개체도 설정합니다. 프로덕션 애플리케이션에서 Redux 또는 MobX와 같은 상태 관리 시스템을 사용하고 싶지만 지금은 간단하게 유지하겠습니다. App.js로 이동하여 다음과 같이 경로를 구성합니다.

 import { BrowserRouter as Router, Redirect, Route, } from "react-router-dom"; function App() { const [curatedTweets, setCuratedTweets] = useState(); return <Router> <Route path="/" exact render={() => <Input setCuratedTweets={setCuratedTweets} />} /> <Route path="/display" render={() => <Display curatedTweets={curatedTweets} />} /> <Route path="*" exact render={() => <Redirect to="/" />} /> </Router> }

이 경로 정의를 사용하여 구현해야 하는 두 가지 경로인 입력 및 표시를 도입했습니다. curatedTweets 변수가 Display로 전달되고 setCuratedTweets 변수가 Input으로 전달되고 있음을 주목하십시오. 이는 입력 구성 요소가 curatedTweets 변수를 설정하는 함수를 호출할 수 있고 Display가 표시할 변수를 가져옴을 의미합니다.

구성 요소 코딩을 시작하려면 /src/components라는 폴더를 /src 아래에 생성하겠습니다. /src/components 아래에 /src/components/input이라는 또 다른 폴더를 만들고 그 아래에 두 개의 파일(input.js 및 input.css)을 만듭니다. Display 구성 요소에 대해서도 동일한 작업을 수행합니다. /src/components/display 및 그 아래에 display.js 및 display.css를 만듭니다.

그 아래에 다음과 같이 스텁 구성 요소를 생성해 보겠습니다.

 import React from 'react'; import 'input.css' const Input = () => <div>Input</div>; export default Input

디스플레이도 마찬가지입니다.

 import React from 'react'; import display.css' const Display = () => <div>Display</div>; export default Display

이것으로 와이어프레이밍이 완료되고 앱이 실행되어야 합니다. 이제 입력 페이지를 코딩해 보겠습니다.

입력 페이지

큰 그림 계획

코드를 작성하기 전에 입력 페이지에서 수행할 작업에 대해 생각해 보겠습니다. 분명히 우리는 사용자가 가져오려는 Twitter 핸들을 입력하고 편집할 수 있는 방법을 원할 것입니다. 또한 사용자가 완료되었음을 나타낼 수 있기를 바랍니다. 사용자가 작업이 완료되었다고 표시하면 Python 큐레이션 API에서 큐레이트된 트윗을 가져와서 마지막으로 Display 구성 요소로 이동하려고 합니다.

이제 구성 요소가 무엇을 하기를 원하는지 알았으므로 코딩할 준비가 되었습니다.

파일 설정

다음과 같이 필요한 React Bootstrap 구성 요소인 탐색 기능에 액세스하기 위해 React Router 라이브러리의 withRouter 를 가져와 시작하겠습니다.

 import React, {useState} from 'react'; import {withRouter} from 'react-router-dom'; import {ListGroup, Button, Form, Container, Row, Col} from 'react-bootstrap'; import './input.css';

이제 Input에 대한 스텁 함수를 정의해 보겠습니다. Input이 setCuratedTweets 함수를 얻는다는 것을 알고 있으며 Python API에서 선별된 트윗을 설정한 후 디스플레이 경로로 이동할 수 있는 기능도 제공하고자 합니다. 따라서 setCuratedTweets 및 history(탐색용) 소품에서 가져오고 싶습니다.

 const Input = ({setCuratedTweets, history}) => { return <div>Input</div> }

히스토리 API 액세스 권한을 부여하기 위해 파일 끝에 있는 내보내기 문에서 withRouter 로 래핑합니다.

 export default withRouter(Input);

데이터 컨테이너

React Hooks를 사용하여 데이터 컨테이너를 설정해 봅시다. 이미 useState 후크를 가져왔으므로 다음 코드를 입력 구성 요소의 본문에 추가할 수 있습니다.

 const [handles, setHandles] = useState([]); const [handleText, setHandleText] = useState('');

이렇게 하면 사용자가 가져오려는 핸들 목록을 보유할 핸들에 대한 컨테이너 및 수정자와 사용자가 핸들을 입력하는 데 사용하는 텍스트 상자의 내용을 보유할 handleText 가 생성됩니다.

이제 UI 구성 요소를 코딩해 보겠습니다.

UI 구성 요소

UI 구성 요소는 상당히 간단합니다. 두 개의 버튼과 함께 입력 텍스트 상자가 포함된 부트스트랩 행 하나가 있습니다. 하나는 현재 입력 상자 내용을 핸들 목록에 추가하고 다른 하나는 API에서 가져옵니다. 사용자가 부트스트랩 목록 그룹을 사용하여 가져오려는 핸들 목록을 표시하는 또 다른 부트스트랩 행이 있습니다. 코드에서는 다음과 같이 보입니다.

 return ( <Container className="tl-container"> <Row> <Col> <Form.Control type="text" value={handleText} onChange={changeHandler} placeholder="Enter handle to pull" /> </Col> </Row> <Row className='input-row'> <Col> <Button variant="primary" onClick={getPull}>Pull</Button> {' '} <Button variant="success" onClick={onAddClicked}>Add</Button> </Col> </Row> <Row> <Col> <ListGroup className="handles-lg"> {handles.map((x, i) => <ListGroup.Item key={i}> {x} <span onClick={groupItemClickedBuilder(i)} className="delete-btn-span"> <Button variant="danger" size="sm"> delete </Button> </span> </ListGroup.Item>)} </ListGroup> </Col> </Row> </Container> );

UI 구성 요소 외에도 데이터 변경을 처리하는 세 가지 UI 이벤트 핸들러를 구현하려고 합니다. API를 호출하는 getPull 이벤트 핸들러는 다음 섹션에서 구현됩니다.

 // set the handleText to current event value const textChangeHandler = (e) => { e.preventDefault(); setHandleText(e.target.value); } // Add handleText to handles, and then empty the handleText const onAddClicked = (e) => { e.preventDefault(); const newHandles = [...handles, handleText]; setHandles(newHandles); setHandleText(''); } // Remove the clicked handle from the list const groupItemClickedBuilder = (idx) => (e) => { e.preventDefault(); const newHandles = [...handles]; newHandles.splice(idx, 1); setHandles(newHandles); }

이제 API 호출을 구현할 준비가 되었습니다.

API 호출

API 호출의 경우, 우리가 가져오고 싶은 핸들을 가져와서 POST 요청으로 Python API로 보내고, 결과 JSON 결과를 curatedTweets 변수에 넣으려고 합니다. 그런 다음 모든 것이 잘되면 프로그래밍 방식으로 /display 경로로 이동하려고 합니다. 그렇지 않으면 더 쉽게 디버그할 수 있도록 콘솔에 오류를 기록합니다.

코드 모드에서는 다음과 같습니다.

 const pullAPI = (e) => { e.preventDefault(); fetch('http://prismatic.pythonanywhere.com/get_tweets', { method: 'POST', mode: 'cors', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }, body: JSON.stringify({ accounts: handles }) }).then(r=>r.json()).then(j => { setCuratedTweets(j); history.push('/display'); }) .catch(e => { console.log(e); }) }

그리고 그것으로 우리는 잘해야합니다. 자유롭게 앱을 실행하고 몇 가지 핸들을 추가하고 API에 요청을 보내십시오.

이제 감상 페이지를 코딩할 준비가 되었습니다.

감정 정렬 모드

Python API는 이미 트윗을 감정별로 정렬하기 때문에 Python API에서 결과를 얻은 후에는 감정 페이지가 실제로 그렇게 어렵지 않습니다.

큰 그림 계획

트윗을 표시할 목록 인터페이스가 필요합니다. 또한 몇 가지 탐색 구성 요소가 주제 그룹화 모드로 전환하고 입력 페이지로 돌아가기를 원할 것입니다.

시작하려면 display.js 파일에 SentimentDisplay 모드 하위 구성요소를 정의해 보겠습니다.

SentimentDisplay 컴포넌트

SentimentDisplay는 curatedTweets 개체를 가져와 목록에 감정 정렬 트윗을 표시합니다. React-Bootstrap의 도움으로 구성 요소는 매우 간단합니다.

 const SentimentDisplay = ({curatedTweets}) => { return <ListGroup> {curatedTweets.sentimentSorted.map((x, i) => <ListGroup.Item key={i}> <div className="account-div">{x.account}:</div> <span className="tweet-span">{x.tweet}</span> <span className="sentiments-span">({x.data.sentiment.toPrecision(2)})</span> </ListGroup.Item> )} </ListGroup> }

이 작업을 수행하는 동안 스타일을 추가해 보겠습니다. display.css에 다음을 입력하고 가져옵니다.

 .account-div { font-size: 12px; font-weight: 600; } .tweet-span { font-size: 11px; font-weight: 500; } .sentiments-span { font-size: 10px; } .tl-container { margin-top: 10px; } .disp-row { margin-top: 5px; }

이제 SentimentDisplay 구성 요소를 표시할 수 있습니다. Display 기능을 다음과 같이 변경합니다.

 const Display = ({curatedTweets}) => { return <SentimentDisplay curatedTweets={curatedTweets} /> };

또한 이 기회에 탐색 구성 요소를 코딩해 보겠습니다. "편집으로 돌아가기" 버튼과 주제 그룹 모드라는 두 개의 버튼이 필요합니다.

다음과 같이 SentimentDisplay 구성 요소 바로 위에 별도의 부트스트랩 행에서 이러한 버튼을 구현할 수 있습니다.

 Return <Container className="tl-container"> <Row> <Col> <Link to="/"><Button variant='primary'>Back</Button></Link> {' '} <Button variant='success'>View by Topic</Button> </Col> </Row> <Row className="disp-row"> <Col> <SentimentDisplay curatedTweets={curatedTweets} /> </Col> </Row> </Container>

앱을 실행하고 몇 개의 핸들에서 트윗을 가져옵니다. 꽤 멋져 보인다!

주제 그룹화 모드

이제 주제 그룹화 모드를 구현하려고 합니다. SentimentDisplay보다 조금 더 복잡하지만 매우 편리한 부트스트랩 구성 요소가 우리를 엄청나게 도와줍니다.

큰 그림 계획

모든 명사구를 가져와 아코디언 목록으로 표시합니다. 그런 다음 아코디언 목록이 확장되면 명사구가 포함된 트윗을 렌더링합니다.

주제 그룹화 모드로 전환 구현

먼저 감성 모드에서 주제 그룹화 모드로 전환하는 로직을 구현해 보겠습니다. 먼저 스텁 구성 요소를 만드는 것으로 시작하겠습니다.

 const TopicDisplay = () => { return <div>Topic Display</div> }

그리고 그것을 표시하는 모드를 생성하기 위해 몇 가지 로직을 설정하십시오. 기본 디스플레이 구성 요소에서 다음 줄을 추가하여 구성 요소가 표시되는 논리를 만듭니다.

 // controls the display mode. Remember to import {useState} from 'react' const [displayType, setDisplayType] = useState('Sentiment'); // Switch the Display Mode const toggleDisplayType = () => { setDisplayType(displayType === 'Sentiment' ? 'Topic': 'Sentiment'); } // determines the text on the mode switch button const switchStr = displayType === 'Sentiment'? 'View by Topic': 'View by Sentiment'

그리고 JSX를 다음으로 변경하여 로직을 추가합니다.

 Return <Container className="tl-container"> <Row> <Col> <Link to="/"><Button variant='primary'>Back</Button></Link> {' '} <Button variant='success' onClick={toggleDisplayType}>{switchStr}</Button> </Col> </Row> <Row className="disp-row"> <Col> { displayType === 'Sentiment'? <SentimentDisplay curatedTweets={curatedTweets} />: <TopicDisplay curatedTweets={curatedTweets} /> } </Col> </Row> </Container>

이제 토글할 때 주제 그룹 표시 스텁이 표시되어야 합니다.

토픽디스플레이 컴포넌트

이제 TopicDisplay 구성 요소를 코딩할 준비가 되었습니다. 이전에 논의한 바와 같이 부트스트랩 아코디언 목록을 활용합니다. 구현은 실제로 매우 간단합니다.

 const TopicDisplay = ({curatedTweets}) => { return <Accordion> {Object.keys(curatedTweets.groupedByNp).map((x, i) => <Card key={i}> <Card.Header> <Accordion.Toggle as={Button} variant="link" eventKey={i}> {x} ({curatedTweets.groupedByNp[x].length}) </Accordion.Toggle> </Card.Header> <Accordion.Collapse eventKey={i}> <Card.Body> <ListGroup> {curatedTweets.groupedByNp[x].map((y, i2) => <ListGroup.Item key={i2}> <div className="account-div">{y.account}:</div> <span className="tweet-span">{y.tweet}</span> <span className="sentiments-span">({y.data.sentiment.toPrecision(2)})</span> </ListGroup.Item> )} </ListGroup> </Card.Body> </Accordion.Collapse> </Card> )} </Accordion> }

앱을 실행하면 Topic Display가 표시됩니다.

이제 앱이 완료되었으며 에뮬레이터용 앱을 빌드할 준비가 되었습니다.

에뮬레이터에서 앱 실행

Cordova를 사용하면 에뮬레이터에서 앱을 매우 쉽게 실행할 수 있습니다. 다음을 실행하기만 하면 됩니다.

 cordova platform add ios # if you haven't done so already cordova run ios

에뮬레이터에 앱이 표시되어야 합니다. Bootstrap은 반응형 웹 앱이기 때문에 웹 앱은 iPhone의 너비에 맞게 조정되며 모든 것이 아주 멋지게 보입니다.

Cordova 앱이 완료되면 이제 Ionic 구현을 살펴보겠습니다.

이온 반응 구현

이온 프라이머

Ionic은 하이브리드 애플리케이션을 더 쉽게 구축할 수 있게 해주는 웹 구성 요소 라이브러리 및 CLI 툴킷입니다. 원래 Ionic은 AngularJS 및 Cordova 위에 구축되었지만 이후 React.js에서 구성 요소를 출시하고 Cordova와 유사한 플랫폼인 Capacitor를 지원하기 시작했습니다. Ionic을 차별화하는 점은 웹 구성 요소를 사용하더라도 구성 요소가 기본 모바일 인터페이스와 매우 유사하게 느껴진다는 것입니다. 또한 Ionic 구성 요소의 모양과 느낌은 실행되는 운영 체제에 자동으로 적응하므로 응용 프로그램의 모양과 느낌이 더욱 자연스럽고 자연스럽게 느껴지도록 도와줍니다. 마지막으로, 이것은 우리 기사의 범위를 벗어나지만, Ionic은 또한 애플리케이션을 좀 더 쉽게 배포할 수 있도록 하는 몇 가지 빌드 도구를 제공합니다.

우리 애플리케이션의 경우 Ionic의 React 구성 요소를 사용하여 Cordova 섹션에서 구축한 일부 JavaScript 로직을 활용하면서 UI를 구축할 것입니다.

앱 구성

먼저 Ionic 도구를 설치하려고 합니다. 따라서 다음을 실행해 보겠습니다.

 npm install -g @Ionic/cli native-run cordova-res

그리고 설치가 끝나면 프로젝트 폴더로 이동합니다. 이제 Ionic CLI를 사용하여 새 프로젝트 폴더를 만듭니다.

 ionic start twitter-curation-Ionic blank --type=react --capacitor

마술이 일어나는 것을 지켜보고 이제 다음을 사용하여 폴더로 이동하십시오.

 cd twitter-curation-Ionic

다음을 사용하여 빈 앱을 실행합니다.

 ionic serve

따라서 앱이 설정되었으며 사용할 준비가 되었습니다. 몇 가지 경로를 정의해 보겠습니다.

계속 진행하기 전에 Ionic이 TypeScript를 사용하여 프로젝트를 시작했음을 알 수 있습니다. TypeScript를 사용하는 데 방해가 되지는 않지만 몇 가지 매우 좋은 기능이 있으며 이 구현에 사용할 것입니다.

라우터 설정

이 구현을 위해 우리는 세 가지 sentimentDisplay 를 사용할 topicDisplay 입니다. 우리는 Ionic에서 제공하는 전환 및 탐색 기능을 활용하고 Ionic 구성 요소를 사용하고 있고 아코디언 목록은 Ionic과 함께 미리 패키지로 제공되지 않기 때문에 이 작업을 수행합니다. 물론 자체적으로 구현할 수도 있지만 이 자습서에서는 제공된 Ionic 구성 요소를 그대로 사용합니다.

App.tsx로 이동하면 이미 정의된 기본 경로가 표시되어야 합니다.

입력 페이지

큰 그림 계획

몇 가지 주요 차이점을 제외하고 부트스트랩 구현과 유사한 논리 및 코드를 많이 사용할 것입니다. 먼저 TypeScript를 사용합니다. 즉, 다음 섹션에서 볼 수 있는 코드에 대한 유형 주석이 있습니다. 둘째, Bootstrap과 스타일이 매우 유사하지만 스타일이 OS에 민감한 Ionic 구성 요소를 사용할 것입니다. 마지막으로 Bootstrap 버전과 같이 히스토리 API를 사용하여 동적으로 탐색하지만 Ionic 라우터 구현으로 인해 히스토리에 약간 다르게 액세스합니다.

설정

스텁 구성 요소로 입력 구성 요소를 설정하는 것으로 시작하겠습니다. 입력이라는 페이지 아래에 폴더를 만들고 그 아래에 Input.tsx라는 파일을 만듭니다. 해당 파일 안에 다음 코드를 넣어 React 컴포넌트를 생성합니다. TypeScript를 사용하기 때문에 약간 다릅니다.

 import React, {useState} from 'react'; const Input : React.FC = () => { return <div>Input</div>; } export default Input;

그리고 App.tsx의 구성 요소를 다음과 같이 변경합니다.

 const App: React.FC = () => ( <IonApp> <IonReactRouter> <IonRouterOutlet> <Route path="/input" component={Input} exact={true} /> <Route exact path="/" render={() => <Redirect to="/input" />} /> </IonRouterOutlet> </IonReactRouter> </IonApp> );

이제 앱을 새로 고치면 입력 스텁 구성 요소가 표시되어야 합니다.

데이터 컨테이너

이제 데이터 컨테이너를 생성해 보겠습니다. 우리는 입력된 Twitter 핸들에 대한 컨테이너와 입력 상자의 현재 내용을 원합니다. TypeScript를 사용하기 때문에 구성 요소 함수의 useState 호출에 유형 주석을 추가해야 합니다.

 const Input : React.FC = () => { const [text, setText] = useState<string>(''); const [accounts, setAccounts] = useState<Array<string>>([]); return <div>Input</div>; }

또한 API의 반환 값을 보관할 데이터 컨테이너가 필요합니다. 그 내용을 다른 경로와 공유해야 하므로 App.tsx 수준에서 정의합니다. App.tsx 파일의 React에서 useState 를 가져오고 앱 컨테이너 기능을 아래와 같이 변경합니다.

 const App: React.FC = () => { const [curatedTweets, setCuratedTweets] = useState<CuratedTweets>({} as CuratedTweets); return ( <IonApp> <IonReactRouter> <IonRouterOutlet> <Route path="/input" component={Input} exact={true} /> <Route exact path="/" render={() => <Redirect to="/input" />} /> </IonRouterOutlet> </IonReactRouter> </IonApp> ); }

이 시점에서 Visual Studio Code와 같은 구문 강조 기능이 있는 편집기를 사용하는 경우 CuratedTweets가 켜진 것을 볼 수 있습니다. 이는 파일이 CuratedTweets 인터페이스가 어떻게 생겼는지 모르기 때문입니다. 이제 그것을 정의합시다. src 아래에 인터페이스라는 폴더를 만들고 그 안에 CuratedTweets.tsx라는 파일을 만듭니다. 파일에서 CuratedTweets 인터페이스를 다음과 같이 정의합니다.

 interface TweetRecordData { noun_phrases: Array<string>, sentiment: number } export interface TweetRecord { account: string, data: TweetRecordData, tweet: string } export default interface CuratedTweets { groupedByNp: Record<string, Array<TweetRecord>>, processedTweets: Array<TweetRecord>, sentimentSorted: Array<TweetRecord> }

이제 앱은 API 반환 데이터의 구조에 대해 알고 있습니다. App.tsx에서 CuratedTweets 인터페이스를 가져옵니다. 이제 문제 없이 App.tsx가 컴파일되는 것을 볼 수 있습니다.

여기서 몇 가지 작업을 더 수행해야 합니다. setCuratedTweets 함수를 입력 구성 요소에 전달하고 입력 구성 요소가 이 기능을 인식하도록 해야 합니다.

App.tsx에서 다음과 같이 입력 경로를 수정합니다.

 <Route path="/input" render={() => <Input setCuratedTweets={setCuratedTweets}/>} exact={true} />

이제 다른 것 아래에 있는 편집기 플래그가 표시되어야 합니다. 입력은 전달되는 새 소품에 대해 알지 못하므로 Input.tsx에서 정의하려고 합니다.

먼저 CuratedTweets 인터페이스를 가져온 다음 다음과 같이 ContainerProps 인터페이스를 정의합니다.

 interface ContainerProps { setCuratedTweets: React.Dispatch<React.SetStateAction<CuratedTweets>> }

And finally, change the Input component definition like so:

 const Input : React.FC<ContainerProps> = ({setCuratedTweets}) => { const [text, setText] = useState<string>(''); const [accounts, setAccounts] = useState<Array<string>>([]); return <div>Input</div>; }

We are done defining the data containers, and now, onto building the UI components.

UI 구성 요소

For the UI component, we will want to build an input component and a list display component. Ionic provides some simple containers for these.

Let's start by importing the library components we'll be using:

 import { IonInput, IonItem, IonList, IonButton, IonGrid, IonRow, IonCol } from '@Ionic/react';

Now, we can replace the stub component with the IonInput , wrapped in an IonGrid:

 return <IonGrid> <IonRow> <IonCol> <IonInput value={text} placeholder="Enter accounts to pull from" onIonChange={e => setText(e.detail.value!)} /> </IonCol> </IonRow> </IonGrid>

Notice that the event listener is onIonChange instead of onChange . Otherwise, it should look very familiar.

When you open the app in your browser, it may not look like the Bootstrap app. However, if you set your browser to emulator mode, the UI will make more sense. It will look even better once you deploy it on mobile, so look forward to it.

Now, let's add some buttons. We will want an “Add to list” button and a “Pull API” button. For that, we can use IonButton. Change the size of the input's IonCol to 8 and add the following two buttons with columns:

 <IonCol size="8"> <IonInput value={text} placeholder="Enter accounts to pull from" onIonChange={e => setText(e.detail.value!)} /> </IonCol> <IonCol size="2"> <IonButton style={{float: 'right'}} color="primary" size="small" onClick={onAddClicked}>Add</IonButton> </IonCol> <IonCol size="2"> <IonButton style={{float: 'right'}} color="success" size="small" onClick={onPullClicked}>Pull</IonButton> </IonCol>

Since we're writing the buttons, let's write the event handlers as well.

The handler to add a Twitter handle to the list is simple:

 const onAddClicked = () => { if (text === undefined || text.length === 0) { return; } const newAccounts: Array<string> = [...accounts, text]; setAccounts(newAccounts); setText(''); }

We will implement the API call in the next section, so let's just put a stub function for onPullClicked :

 const onPullClicked = () => {}

Now, we need to write the component for displaying the list of handles that has been inputted by the user. For that, we will use IonList, put into a new IonRow:

 <IonRow> <IonCol> <IonList> {accounts.map((x:string, i:number) => <IonItem key={i}> <IonGrid> <IonRow> <IonCol size="8" style={{paddingTop: '12px'}}>{x}</IonCol> <IonCol><IonButton style={{float: 'right'}} color="danger" size="small" onClick={deleteClickedBuilder(i)}>Delete</IonButton></IonCol> </IonRow> </IonGrid> </IonItem>)} </IonList> </IonCol> </IonRow>

Each list item is displaying the handle and a delete button in its very own IonGrid. For this code to compile, we will want to implement the deleteClickedHandler as well. It should be very familiar from the previous section but with TypeScript annotations.

 const deleteClickedBuilder = (idx: number) => () => { const newAccounts: Array<string> = [...accounts]; newAccounts.splice(idx, 1); setAccounts(newAccounts); }

Save this, and you should see the Input page with all the UI components implemented. We can add handles, delete handles, and click the button to invoke the API.

As a final exercise, let's move the in-line styles to CSS. Create a file in the input folder called input.css and import it in the Input.tsx file. Then, add the following styles:

 .input-button { float: right; } .handle-display { padding-top: 12px; }

Now, add className="input-button” on all of the IonButtons and className=”handle-display” on the handle list item IonCol that is displaying the intended Twitter handle. Save the file, and you should see everything looking quite good.

API 호출

The code to pull the API is very familiar from the previous section, with one exception - we have to get access to the history component to be able to dynamically change routes. We will do this using the withHistory hook.

We first import the hook:

 import { useHistory } from 'react-router';

And then implement the handler in the input component:

 const history = useHistory(); const switchToDisplay = () => { history.push('/display'); } const onPullClicked = () => { fetch('http://prismatic.pythonanywhere.com/get_tweets', { method: 'POST', mode: 'cors', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }, body: JSON.stringify({ accounts }) }).then(r=>r.json()).then(j => { setCuratedTweets(j); switchToDisplay(); }) .catch(e => { console.log(e); }) }

헤더 추가

Our Input page looks quite nice, but it looks a little bare due to Ionic's mobile-centric styling. To make the UI look more natural, Ionic provides a header feature that lets us provide a more natural user experience. When running on mobile, the header will also simulate the native OS's mobile platform, which makes the user experience even more natural.

Change your component import to:

 import { IonPage, IonHeader, IonToolbar, IonTitle, IonContent, IonInput, IonItem, IonList, IonButton, IonGrid, IonRow, IonCol } from '@Ionic/react';

And now wrap the UI in an Ionic page with a header, like so:

return <IonPage> <IonHeader> <IonToolbar> <IonTitle>Twitter Curation App</IonTitle> </IonToolbar> </IonHeader> <IonContent> <IonHeader collapse="condense"> <IonToolbar> <IonTitle size="large">Twitter Curation App</IonTitle> </IonToolbar> </IonHeader> <IonGrid> <IonRow> <IonCol size="8"> <IonInput value={text} placeholder="Enter accounts to pull from" onIonChange={e => setText(e.detail.value!)} /> </IonCol> <IonCol size="2"> <IonButton className="input-button" color="primary" size="small" onClick={onAddClicked}>Add</IonButton> </IonCol> <IonCol size="2"> <IonButton className="input-button" color="success" size="small" onClick={onPullClicked}>Pull</IonButton> </IonCol> </IonRow> <IonRow> <IonCol> <IonList> {accounts.map((x:string, i:number) => <IonItem key={i}> <IonGrid> <IonRow> <IonCol size="8" className="handle-display">{x}</IonCol> <IonCol><IonButton className="input-button" color="danger" size="small" onClick={deleteClickedBuilder(i)}>Delete</IonButton></IonCol> </IonRow> </IonGrid> </IonItem>)} </IonList> </IonCol> </IonRow> </IonGrid> </IonContent> </IonPage>

이제 멋지게 보입니다!

감정 정렬 페이지

큰 그림 계획

감정 정렬 페이지는 Bootstrap 페이지의 감정 정렬 페이지와 대체로 유사하지만 TypeScript 및 Ionic을 사용합니다. 또한 모바일에서 실행되는 동안 Ionic의 탐색 기능을 활용하기 위해 Topic Display를 별도의 경로로 구현할 것이므로 이 페이지에도 Topic Display 경로로 이동할 수 있는 기능을 제공해야 합니다.

경로 설정

먼저 SentimentSorted라는 새 폴더와 그 아래에 SentimentSorted.tsx라는 파일을 만들어 보겠습니다. 다음과 같이 스텁 구성 요소를 내보냅니다.

 import React from 'react'; const SentimentSorted: React.FC = () => { return <div>Sentiment Sorted</div> } export default SentimentSorted;

그리고 App.tsx에서 구성 요소를 가져옵니다.

 import SentimentSorted from './pages/sentimentsorted/SentimentSorted';

그리고 경로를 추가합니다:

 <Route path="/display" render={() => <SentimentSorted curatedTweets={curatedTweets} />} />

SentimentSorted 가 curatedTweets 소품을 기대하지 않는다는 TypeScript 오류가 발생하므로 다음 섹션에서 처리해 보겠습니다.

UI 구성 요소

컨테이너의 소품을 정의하는 것으로 시작하겠습니다. 입력 구성 요소와 매우 유사합니다.

 import CuratedTweets from '../../interfaces/CuratedTweets'; interface ContainerProps { curatedTweets: CuratedTweets }

이제 스텁 표시를 변경하십시오.

 const SentimentSorted: React.FC<ContainerProps> = ({curatedTweets}) => { return <div>Sentiment Sorted</div> }

그리고 모든 것이 컴파일되어야 합니다.

디스플레이는 매우 간단하며 디스플레이 구성 요소가 있는 IonList일 뿐입니다.

 return <IonGrid> <IonRow> <IonCol> <IonList> {(curatedTweets.sentimentSorted).map((x, i) => <IonItem key={i}> <IonLabel className="ion-text-wrap"> <h2>{x.account}:</h2> <h3>{x.tweet}</h3> <p>({x.data.sentiment.toPrecision(2)})</p> </IonLabel> </IonItem>)} </IonList> </IonCol> </IonRow> </IonGrid>

입력 구성 요소를 사용하여 일부 트윗을 저장하고 가져오면 목록에 표시된 트윗이 표시되어야 합니다.

이제 탐색 버튼을 추가해 보겠습니다. IonGrid에 추가:

 <IonRow> <IonCol> <IonButton color='primary' onClick={switchToInput}>Back</IonButton> <IonButton color="success" onClick={toggleDisplayType}>{switchStr}</IonButton> </IonCol> </IonRow>

switchToInput 은 history API로 구현하기가 매우 쉽습니다.

 const switchToInput = () => { history.goBack(); }

그리고 ToggleDisplayType 도 친숙해야 합니다.

 const toggleDisplayType = () => { setDisplayType(displayType === 'Sentiment' ? 'Topic': 'Sentiment'); } const switchStr = displayType === 'Sentiment'? 'View by Topic': 'View by Sentiment'

이제 SentimentDisplay 구성 요소가 구현되었습니다. 이제 주제 표시 페이지를 구현하기 전에 모든 주제를 표시하는 구성 요소를 구현해야 합니다. 다음 섹션에서 그렇게 할 것입니다.

주제 그룹 구성 요소

주제 목록 표시 옵션을 추가하여 조건부로 표시해 보겠습니다. 그렇게 하려면 감정 표시 목록을 분리해야 합니다. SentimentDisplay의 이름을 Display로 바꾸고 감정 표시 목록을 분류해 보겠습니다.

 interface SentimentDisplayProps { sentimentSorted: Array<TweetRecord> } const SentimentDisplay: React.FC<SentimentDisplayProps> = ({sentimentSorted}) => { return <IonList> {(sentimentSorted || []).map((x, i) => <IonItem key={i}> <IonLabel className="ion-text-wrap"> <h2>{x.account}:</h2> <h3>{x.tweet}</h3> <p>({x.data.sentiment.toPrecision(2)})</p> </IonLabel> </IonItem>)} </IonList> }

CuratedTweets 인터페이스에서 클래스 정의 중 하나를 사용하는 방법에 주목하십시오. 이러한 구성 요소에는 전체 CuratedTweets 개체가 필요하지 않고 하위 집합만 필요하기 때문입니다. 주제 목록은 매우 유사합니다.

 interface TopicDisplayProps { groupedByNP: Record<string, Array<TweetRecord>> } const TopicDisplay: React.FC<TopicDisplayProps> = ({groupedByNP}) => { return <IonList> {Object.keys(groupedByNP || {}).map((x, i) => <IonItem key={i} routerLink={`/topicDisplay/${encodeURIComponent(x)}`}> <IonLabel className="ion-text-wrap"> <h2>{x} ({groupedByNP[x].length})</h2> </IonLabel> </IonItem>)} </IonList> }

이제 Display Component에서 조건부 표시를 쉽게 설정할 수 있습니다.

 return ( <IonGrid> <IonRow> <IonCol> <IonButton color='primary' onClick={switchToInput}>Back</IonButton> <IonButton color="success" onClick={toggleDisplayType}>{switchStr}</IonButton> </IonCol> </IonRow> { displayType === 'Sentiment'? <SentimentDisplay sentimentSorted={curatedTweets.sentimentSorted} /> : <TopicDisplay groupedByNP={curatedTweets.groupedByNp} /> } </IonGrid> );

기본 내보내기를 변경했는지 확인하고 이제 주제 표시 페이지를 구현할 준비가 되었습니다.

주제 표시 페이지

큰 그림 계획

주제 표시 페이지는 감정 표시와 유사한 목록 표시이지만 경로 매개변수에서 문제의 주제를 찾습니다.

경로 설정

여기까지 왔다면 무엇을 해야 하는지 이미 알고 있어야 합니다. topicdisplay 및 TopicDisplay.tsx라는 페이지 폴더를 만들고 스텁 구성 요소를 작성한 다음 App.tsx 페이지로 가져옵니다. 이제 경로를 설정해 보겠습니다.

 <Route path="/topicDisplay/:topic" render={() => <TopicDisplay curatedTweets={curatedTweets} /> } />

이제 UI 구성 요소를 구현할 준비가 되었습니다.

UI 구성 요소

먼저 ContainerProps 정의를 생성해 보겠습니다.

 interface ContainerProps { curatedTweets: CuratedTweets } const TopicDisplay: React.FC<ContainerProps> = ({curatedTweets}) => { Return <div>Topic Display</div> }

이제 URL 경로 이름에서 주제를 검색해야 합니다. 이를 위해 우리는 history API를 사용할 것입니다. 이제 useHistory 를 가져오고, 히스토리 API를 인스턴스화하고, 경로 이름에서 주제를 가져옵니다. 그 동안 스위치 백 기능도 구현해 보겠습니다.

 const TopicDisplay: React.FC<ContainerProps> = ({curatedTweets}) => { const history = useHistory(); const switchToDisplay = () => { history.goBack(); } const topic = history.location.pathname.split('/')[2]; const tweets = (curatedTweets.groupedByNp || {})[topic];

이제 특정 주제에 대한 트윗이 있으므로 표시하는 것은 실제로 매우 간단합니다.

 return ( <IonGrid> <IonRow> <IonCol> <IonButton color='primary' onClick={switchToDisplay}>Back</IonButton> </IonCol> </IonRow> <IonRow> <IonCol> <IonList> {(tweets || []).map((x, i) => <IonItem key={i}> <IonLabel className="ion-text-wrap"> <h2>{x.account}:</h2> <h3>{x.tweet}</h3> <p>({x.data.sentiment.toPrecision(2)})</p> </IonLabel> </IonItem>)} </IonList> </IonCol> </IonRow> </IonGrid> );

저장하고 실행하면 상황이 좋아 보일 것입니다.

에뮬레이터에서 앱 실행

에뮬레이터에서 앱을 실행하려면 Cordova로 설정하는 방법과 유사하게 몇 가지 Ionic 명령을 실행하여 모바일 플랫폼을 추가하고 코드를 복사하면 됩니다.

 ionic build # builds the app ionic cap add ios # adds iOS as one of the platforms, only have to run once ionic cap copy # copy the build over ionic cap sync # only need to run this if you added native plugins ionic cap open ios # run the iOS emulator

그리고 앱이 표시되는 것을 확인해야 합니다.

React 네이티브 구현

리액트 네이티브 프라이머

React Native는 이전 섹션의 웹 기반 접근 방식과 매우 다른 접근 방식을 취합니다. React Native는 React 코드를 기본 구성 요소로 렌더링합니다. 여기에는 몇 가지 장점이 있습니다. 첫째, 기본 운영 체제와의 통합이 훨씬 심화되어 개발자가 Cordova/Capacitor를 통해 사용할 수 없는 새로운 스마트폰 기능과 OS별 기능을 활용할 수 있습니다. 둘째, 중간에 웹 기반 렌더링 엔진이 없기 때문에 React Native 앱은 일반적으로 Cordova를 사용하여 작성된 앱보다 빠릅니다. 마지막으로 React Native는 기본 구성 요소의 통합을 허용하므로 개발자는 응용 프로그램을 훨씬 더 세밀하게 제어할 수 있습니다.

애플리케이션의 경우 이전 섹션의 논리를 사용하고 NativeBase라는 React Native 구성 요소 라이브러리를 사용하여 UI를 코딩합니다.

앱 구성

먼저 여기의 지침에 따라 React Native의 모든 필수 구성 요소를 설치하려고 합니다.

React Native가 설치되면 프로젝트를 시작하겠습니다.

 react-native init TwitterCurationRN

설치 스크립트를 실행하면 결국 폴더가 생성됩니다. Cd를 폴더에 넣고 react-native run-ios를 실행하면 예제 앱과 함께 에뮬레이터 팝업이 표시되어야 합니다.

구성 요소 라이브러리이기 때문에 NativeBase도 설치하려고 합니다. 이를 위해 다음을 실행합니다.

 npm install --save native-base react-native link

또한 React Native 스택 내비게이터를 설치하려고 합니다. 뛰자:

 npm install --save @react-navigation/stack @react-navigation/native

그리고

 react-native link cd ios pod-install cd

기본 플러그인의 연결 및 설치를 완료합니다.

라우터 설정

라우팅을 위해 위 단계에서 설치한 스택 내비게이터를 사용합니다.

라우터 구성 요소 가져오기:

 import { NavigationContainer } from '@react-navigation/native'; import {createStackNavigator} from '@react-navigation/stack';

이제 스택 탐색기를 만듭니다.

 const Stack = createStackNavigator();

스택 탐색기를 사용하도록 앱 구성 요소의 콘텐츠를 변경합니다.

 const App = () => { return ( <> <StatusBar bar /> <NavigationContainer> <Stack.Navigator initialRouteName="Entry"> <Stack.Screen name="Entry" component={Entry} /> </Stack.Navigator> </NavigationContainer> </> ); };

이 시점에서 Entry가 아직 정의되지 않았기 때문에 오류가 발생합니다. 행복하게 만들기 위해 스텁 요소를 정의합시다.

프로젝트에 구성 요소 폴더를 만들고 Entry.jsx라는 파일을 만들고 다음과 같이 스텁 구성 요소를 추가합니다.

 import React, {useState} from 'react'; import { Text } from 'native-base'; export default Entry = ({navigation}) => { return <Text>Entry</Text>; // All text must be wrapped in a <Text /> component or <TextView /> if you're not using NativeBase. }

앱에서 항목 구성 요소를 가져오면 빌드해야 합니다.

이제 입력 페이지를 코딩할 준비가 되었습니다.

입력 페이지

큰 그림 계획

위에서 구현한 것과 매우 유사하지만 NativeBase 구성 요소를 사용하는 페이지를 구현합니다. 후크 및 가져오기와 같이 우리가 사용한 대부분의 JavaScript 및 React API는 모두 여전히 사용할 수 있습니다.

한 가지 차이점은 나중에 보게 될 탐색 API로 작업하는 방식입니다.

UI 구성 요소

우리가 사용할 추가 NativeBase 구성 요소는 Container, Content, Input, List, ListItem 및 Button입니다. 이것들은 모두 Ionic 및 Bootstrap React에 유사하며, NativeBase의 빌더는 이러한 라이브러리에 익숙한 사람들에게 매우 직관적으로 만들었습니다. 다음과 같이 가져오기만 하면 됩니다.

 import { Container, Content, Input, Item, Button, List, ListItem, Text } from 'native-base';

구성 요소는 다음과 같습니다.

 return <Container> <Content> <Item regular> <Input placeholder='Input Handles Here' onChangeText={inputChange} value={input} /> <Button primary onPress={onAddClicked}><Text> Add </Text></Button> <Text> </Text> <Button success onPress={onPullClicked}><Text> Pull </Text></Button> </Item> <Item> <List style={{width: '100%'}}> {handles.map((item) => <ListItem key={item.key}> <Text>{item.key}</Text> </ListItem>)} </List> </Item> </Content> </Container>

이제 상태 및 이벤트 핸들러를 구현해 보겠습니다.

 const [input, changeInput] = useState(''); const [handles, changeHandles] = useState([]); const inputChange = (text) => { changeInput(text); } const onAddClicked = () => { const newHandles = [...handles, {key: input}]; changeHandles(newHandles); changeInput(''); }

마지막으로 API 호출:

 const onPullClicked = () => { fetch('http://prismatic.pythonanywhere.com/get_tweets', { method: 'POST', mode: 'cors', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }, body: JSON.stringify({ accounts: handles.map(x => x.key) }) }).then(r=>r.json()).then(data => { navigation.navigate('SentimentDisplay', { data }); }) .catch(e => { console.log(e); }) }

이 구현은 다른 방식으로 탐색한다는 점을 제외하고 NativeBase 구현과 동일합니다. 스택 내비게이터는 "내비게이션"이라는 소품을 구성 요소에 전달하며 .navigate 를 사용하여 경로 사이를 탐색하는 데 사용할 수 있습니다. 단순히 탐색하는 것 외에도 대상 구성 요소에 데이터를 전달할 수도 있습니다. 우리는 이 메커니즘을 사용하여 데이터를 전달할 것입니다.

앱을 컴파일하려면 여전히 Entry 이 탐색 구성 요소를 인식하도록 해야 합니다. 그렇게 하려면 구성 요소 함수 선언을 변경해야 합니다.

 export default Entry = ({navigation}) => {

이제 저장하면 페이지가 표시됩니다.

감정 정렬 페이지

큰 그림 계획

이전 섹션과 매우 유사하게 감상 페이지를 구현하지만 페이지 스타일을 약간 다르게 지정하고 탐색 라이브러리를 다르게 사용하여 API 호출 반환 값을 얻습니다.

React Native에는 CSS가 없기 때문에 StyleSheet 개체를 정의하거나 단순히 스타일을 인라인으로 코딩해야 합니다. 구성 요소 간에 일부 스타일을 공유할 것이기 때문에 전역 스타일시트를 만들어 보겠습니다. 경로 설정 후에 하도록 하겠습니다.

또한 StackNavigator 에는 뒤로 탐색 버튼이 내장되어 있으므로 자체 뒤로 버튼을 구현할 필요가 없습니다.

경로 설정

StackNavigator 의 경로 정의는 매우 간단합니다. 우리는 단순히 Stack Screen이라는 이름의 새 것을 만들고 React 라우터와 매우 유사한 구성 요소를 제공합니다.

 <NavigationContainer> <Stack.Navigator initialRouteName="Entry"> <Stack.Screen name="Entry" component={Entry} /> <Stack.Screen name="SentimentDisplay" component={SentimentDisplay} /> </Stack.Navigator> </NavigationContainer>

이 작업을 수행하려면 물론 components/SentimentDisplay.js에 스텁 구성 요소를 만들어야 합니다.

 import React from 'react'; import {Text} from 'native-base'; const SentimentDisplay = () => { return <Text>Sentiment Display</Text>; } export default SentimentDisplay;

가져오기:

 import SentimentDisplay from './components/SentimentDisplay';

이제 전역 스타일시트를 만들 준비가 되었습니다.

전역 스타일시트

먼저 globalStyles.js라는 파일을 만듭니다. 그런 다음 React Native에서 StyleSheet 구성 요소를 가져오고 스타일을 정의합니다.

 import {StyleSheet} from 'react-native'; export default StyleSheet.create({ tweet: {paddingTop: 5}, accountName: {fontWeight: '600'}, })

그리고 UI를 코딩할 준비가 되었습니다.

UI 구성 요소

UI 구성 요소는 경로 작업 방식을 제외하고는 매우 친숙합니다. StackNavigator 의 특별한 props 탐색 및 경로를 사용하여 현재 애플리케이션 상태를 가져오고 사용자가 해당 페이지를 보기를 원하는 경우 주제 표시로 이동하려고 합니다.

탐색 소품에 액세스하도록 구성 요소 정의를 변경합니다.

 const SentimentDisplay = ({route, navigation}) => {

이제 애플리케이션 상태 읽기 및 탐색 기능을 구현합니다.

 const {params: {data}} = route; const viewByTopicClicked = () => { navigation.navigate('TopicDisplay', { data }); }

전역 스타일 가져오기:

 import globalStyles from './globalStyles';

그리고 구성 요소:

 import { View } from 'react-native'; import {List, Item, Content, ListItem, Container, Text, Button} from 'native-base';

마지막으로 구성 요소:

 return <Container> <Content> <Item> <Button primary onPress={viewByTopicClicked}><Text>View By Topic</Text></Button> </Item> <Item> <List style={{width: '100%'}}> {data.sentimentSorted.map((item, i) => <ListItem key={i}> <View style={globalStyles.listItem}> <Text style={globalStyles.accountName}>{item.account}:</Text> <Text style={globalStyles.tweet}>{item.tweet} ({item.data.sentiment.toPrecision(2)})</Text> </View> </ListItem>)} </List> </Item> </Content> </Container>;

일부 트윗을 저장하고 가져오면 감정 표시가 표시되어야 합니다. 이제 주제 그룹화 페이지로 이동합니다.

주제 그룹화 페이지

큰 그림 계획

주제 표시는 다시 매우 유사합니다. 핸들러 빌더를 사용하여 특정 주제 항목에 대한 표시 페이지로 이동하는 탐색 기능을 빌드하고 이 페이지에 특정한 스타일시트도 정의할 것입니다.

우리가 할 새로운 작업은 버튼과 매우 유사한 기능을 하는 React Native 특정 구성 요소인 TouchableOpacity를 구현하는 것입니다.

경로 설정

경로 정의는 이전과 동일합니다.

 <Stack.Navigator initialRouteName="Entry"> <Stack.Screen name="Entry" component={Entry} /> <Stack.Screen name="SentimentDisplay" component={SentimentDisplay} /> <Stack.Screen name="TopicDisplay" component={TopicDisplay} /> </Stack.Navigator>

스텁 구성 요소 구성 요소/TopicDisplay.js:

 import React from 'react'; import {Text} from 'native-base'; const TopicDisplay = () => { return <Text>Topic Display</Text>; } export default TopicDisplay;

가져오기:

 import TopicDisplay from './components/TopicDisplay;

UI 구성 요소

이 중 많은 부분이 매우 친숙하게 보일 것입니다. 라이브러리 함수 가져오기:

 import { View, TouchableOpacity, StyleSheet } from 'react-native'; import {List, Item, Content, ListItem, Container, Text} from 'native-base';

전역 스타일 가져오기:

 import globalStyles from './globalStyles';

사용자 정의 스타일 정의:

 const styles = StyleSheet.create({ topicName: {fontWeight: '600'}, })

탐색 소품을 정의합니다.

 export default TopicDisplay = ({route, navigation}) => {

데이터 및 작업 핸들러를 정의합니다. 함수를 반환하는 함수인 핸들러 빌더를 사용하고 있습니다.

 const {params: {data}} = route; const specificItemPressedHandlerBuilder = (topic) => () => { navigation.navigate('TopicDisplayItem', { data, topic }); }

그리고 이제 구성품입니다. onPress 핸들러를 가질 수 있는 TouchableOpacity를 사용하고 있음을 주목하세요. TouchableTransparency도 사용할 수 있었지만 TouchableOpacity의 클릭 앤 홀드 애니메이션이 우리 애플리케이션에 더 적합했습니다.

 return <Container> <Content> <Item> <List style={{width: '100%'}}> {Object.keys(data.groupedByNp).map((topic, i) => <ListItem key={i}> <View style={globalStyles.listItem}> <TouchableOpacity onPress={specificItemPressedHandlerBuilder(topic)}> <Text style={styles.topicName}>{topic}</Text> </TouchableOpacity> </View> </ListItem>)} </List> </Item> </Content> </Container>;

그리고 이것은 그것을해야합니다. 저장하고 응용 프로그램을 사용해보십시오!

이제 주제 표시 항목 페이지로 이동합니다.

주제 표시 항목 페이지

큰 그림 계획

Topic Display Item 페이지는 매우 유사하며 모든 특이 사항은 다른 섹션에서 처리되므로 여기에서 원활하게 항해해야 합니다.

경로 설정

경로 정의를 추가합니다.

 <Stack.Screen name="TopicDisplayItem" component={TopicDisplayItem} />

가져오기 추가:

 import TopicDisplayItem from './components/TopicDisplayItem';

그리고 스텁 컴포넌트를 생성합니다. 단순한 구성 요소 대신 사용할 NativeBase 구성 요소를 가져와서 route props를 정의해 보겠습니다.

 import React from 'react'; import {View} from 'react-native'; import {List, Item, Content, ListItem, Container, Text} from 'native-base'; import globalStyles from './globalStyles'; const TopicDisplayItem = ({route}) => { const {params: {data, topic}} = route; return <Text>Topic Display Item</Text>; } export default TopicDisplayItem;

UI 구성 요소

UI 구성 요소는 매우 간단합니다. 우리는 전에 그것을 보았고 실제로 사용자 정의 논리를 구현하지 않습니다. 그래서, 그냥 가자! 심호흡을…

 return <Container> <Content> <Item> <List style={{width: '100%'}}> {data.groupedByNp[topic].map((item, i) => <ListItem key={i}> <View style={globalStyles.listItem}> <Text style={globalStyles.accountName}>{item.account}:</Text> <Text style={globalStyles.tweet}>{item.tweet} ({item.data.sentiment.toPrecision(2)})</Text> </View> </ListItem>)} </List> </Item> </Content> </Container>;

저장하고 출발해야 ​​합니다! 이제 에뮬레이터에서 앱을 실행할 준비가 되었습니다... 잠깐, 이미 하지 않았습니까?

앱 실행

글쎄, 당신은 React Native로 작업하고 있기 때문에 이미 에뮬레이터에서 앱을 실행하고 있으므로 이 부분은 이미 처리되었습니다. 이것은 React Native 개발 환경의 가장 큰 장점 중 하나입니다.

아휴! 이것으로 이 기사의 코딩 부분을 마쳤습니다. 기술에 대해 배운 내용을 살펴보겠습니다.

기술 비교

코르도바: 찬반 양론

Cordova의 가장 좋은 점은 숙련된 웹 개발자가 기능적이고 합리적으로 표시할 수 있는 것을 코딩할 수 있는 엄청난 속도입니다. 웹 개발 기술과 경험은 결국 웹 앱을 코딩하기 때문에 쉽게 이전됩니다. 개발 프로세스가 빠르고 간단하며 Cordova API에 액세스하는 것도 간단하고 직관적입니다.

Cordova를 직접 사용할 때의 단점은 대부분 웹 구성 요소에 대한 과도한 의존에서 비롯됩니다. 사용자는 모바일 앱을 사용할 때 특정 사용자 경험과 인터페이스 디자인을 기대하게 되었으며, 애플리케이션이 모바일 사이트처럼 느껴질 때 경험은 약간 거북할 수 있습니다. 또한 전환 애니메이션 및 탐색 유틸리티와 같이 앱에 내장된 대부분의 기능은 수동으로 구현해야 합니다.

Ionic: 장단점

Ionic의 가장 좋은 점은 "무료"로 제공되는 모바일 중심 기능이 얼마나 많다는 것입니다. 웹 애플리케이션을 코딩하는 것처럼 코딩함으로써 단순히 Cordova 및 React-Bootstrap을 사용하는 것보다 훨씬 모바일 친화적으로 보이는 앱을 빌드할 수 있었습니다. 탐색 애니메이션, 기본 스타일의 버튼, 사용자 경험을 매우 매끄럽게 만드는 많은 사용자 인터페이스 옵션이 있었습니다.

Ionic 사용의 단점은 부분적으로 장점 때문이었습니다. 첫째, 때때로 앱이 다양한 환경에서 어떻게 작동할지 상상하기 어려웠습니다. 앱이 한 방향으로 보인다고 해서 동일한 UI 배치가 다른 환경에서도 동일하게 보인다는 의미는 아닙니다. 둘째, Ionic은 많은 기본 기술 위에 위치하며 일부 구성 요소에 액세스하는 것이 어려운 것으로 판명되었습니다. 마지막으로 이것은 Ionic-React에만 해당되지만 Ionic은 Angular용으로 처음 구축되었기 때문에 많은 Ionic-React 기능이 문서화 및 지원이 적은 것처럼 보였습니다. 그러나 Ionic 팀은 React 개발자의 요구 사항에 매우 세심한주의를 기울이고 새로운 기능을 신속하게 제공합니다.

네이티브 반응: 장단점

React Native는 모바일에서 개발하는 매우 부드러운 사용자 경험을 가지고 있습니다. 에뮬레이터에 직접 연결하면 응용 프로그램이 어떻게 보이는지 알 수 있습니다. 웹 기반 디버거 인터페이스는 웹 애플리케이션 세계의 디버깅 기술을 교차 적용하는 데 매우 유용했으며 생태계는 매우 강력합니다.

React Native의 단점은 기본 인터페이스에 가깝다는 것입니다. 많은 DOM 기반 라이브러리를 사용할 수 없었기 때문에 새로운 라이브러리와 모범 사례를 배워야 했습니다. CSS의 이점이 없으면 응용 프로그램의 스타일 지정이 다소 덜 직관적입니다. 마지막으로, 학습할 새로운 구성 요소가 많이 있으므로(예: div 대신 View, 모든 것을 래핑하는 Text 구성 요소, Buttons vs. TouchableOpacity vs. TouchableTransparency 등) 누군가가 시작하는 경우 처음에는 약간의 학습 곡선이 있습니다. 역학에 대한 사전 지식이 거의 없는 네이티브 세계에 반응하십시오.

각 기술을 사용하는 경우

Cordova, Ionic 및 React Native는 모두 매우 강력한 장단점을 가지고 있기 때문에 각 기술에는 최고의 생산성과 성능을 누릴 수 있는 컨텍스트가 있습니다.

UI 디자인과 일반적인 모양과 느낌을 둘러싼 강력한 브랜드 아이덴티티가 있는 웹 우선 애플리케이션이 이미 있는 경우 가장 좋은 옵션은 스마트폰의 기본 기능에 액세스할 수 있는 Cordova입니다. 웹 구성 요소를 만들고 그 과정에서 브랜드 아이덴티티를 보존합니다. 반응형 프레임워크를 사용하는 비교적 간단한 애플리케이션의 경우 필요한 변경 사항이 거의 없이 모바일 앱을 빌드할 수 있습니다. 그러나 귀하의 애플리케이션은 앱보다는 웹 페이지처럼 보일 것이며 사람들이 모바일 앱에서 기대하는 일부 구성 요소는 별도로 코딩됩니다. 따라서 애플리케이션을 모바일로 이식하는 웹 우선 프로젝트에 있는 경우 Cordova를 권장합니다.

앱 우선 철학으로 새 애플리케이션을 코딩하기 시작하지만 팀의 기술이 주로 웹 개발에 있다면 Ionic을 추천합니다. Ionic의 라이브러리를 사용하면 기본 구성 요소에 가까운 모양과 느낌을 주는 코드를 빠르게 작성하면서 웹 개발자로서의 기술과 본능을 적용할 수 있습니다. 웹 개발 베스트 프랙티스는 Ionic을 사용한 개발과 쉽게 교차 적용되고 CSS를 사용한 스타일링은 원활하게 작동한다는 것을 알았습니다. 또한 사이트의 모바일 버전은 반응형 CSS 프레임워크를 사용하여 코딩된 웹사이트보다 훨씬 더 기본적으로 보입니다. 그러나 그 과정에서 일부 단계에서 React-Ionic-Native API 통합에 수동 조정이 필요하다는 사실을 알게 되었으며, 이는 시간이 많이 소요될 수 있습니다. 따라서 응용 프로그램이 처음부터 개발되고 모바일 지원 웹 응용 프로그램과 모바일 응용 프로그램 간에 상당한 양의 코드를 공유하려는 경우 Ionic을 권장합니다.

일부 네이티브 코드베이스가 구현된 새 애플리케이션을 코딩하는 경우 React Native를 사용해 볼 수 있습니다. 네이티브 코드를 사용하지 않더라도 이미 React Native에 익숙하거나 주요 관심사가 하이브리드 애플리케이션이 아닌 모바일 애플리케이션인 경우에 가장 좋은 옵션이 될 수도 있습니다. 프론트엔드 개발 노력의 대부분을 웹 개발에 집중한 결과, 구성 요소 구성과 코딩 규칙의 차이로 인해 React Native를 시작하는 것이 Ionic이나 Cordova보다 학습 곡선이 더 많다는 것을 처음 발견했습니다. 그러나 이러한 뉘앙스를 배우면 특히 NativeBase와 같은 구성 요소 라이브러리의 도움으로 코딩 경험이 매우 원활해집니다. 개발 환경의 품질과 애플리케이션에 대한 제어를 고려할 때 프로젝트의 프론트 엔드가 주로 모바일 애플리케이션인 경우 선택한 도구로 React Native를 권장합니다.

향후 주제

탐색할 시간이 없었던 주제 중 하나는 카메라, 지리적 위치 또는 생체 인증과 같은 기본 API에 쉽게 액세스할 수 있다는 것이었습니다. 모바일 개발의 큰 이점 중 하나는 일반적으로 브라우저에서 액세스할 수 없는 풍부한 API 에코시스템의 액세스 가능성입니다.

향후 기사에서는 다양한 플랫폼 간 기술을 사용하여 이러한 기본 API 지원 애플리케이션을 쉽게 개발할 수 있는지 살펴보고자 합니다.

결론

오늘 우리는 세 가지 다른 플랫폼 간 모바일 개발 기술을 사용하여 Twitter 큐레이션 앱을 구현했습니다. 이 글이 각 기술이 어떤 것인지 잘 이해하고 자신만의 React 기반 애플리케이션을 개발하는 데 영감을 주었길 바랍니다.

읽어 주셔서 감사합니다!