본문 바로가기

컴터생각

리액트 네이티브, 간단한 샘플 예제 소스 만들며 기본 개념 잡아 보기 - React Native

 

 

리액트 네이티브로 모바일 앱 개발해보자, 꼬꼬꼬~~

 

모바일앱 개발의 양대산맥, 리액트 네이티브와 플러터
리액트로 웹 개발을 해봤다면 리액트 네이티브로, 뉴비라면 플러터로

둘 다 해보고 싶다면 why not.

참고 : 플러터 VS 리액트 네이티브, 2023년의 승자는? - https://youtu.be/Z9cCjrbTW50

 

자바스크립트만 알면, 모든 개발이 가능합니다. 개발자 되기 차암~ 쉽죠잉~

React.js - 웹 개발

React Native - aos, ios 모바일 앱 개발

node.js - 백엔드 서버 개발

 

 

리디북스 개발팀 사례로 보는 리액트 네이티브, 그리고 플러터

네이티브 앱 개발보다 더 빠른 개발이 필요한 상황은 모든 개발팀이 직면하고 있고, 다들 비슷한 고민을 할 겁니다. 가난한 회사가 흔해 빠진 앱 만드는데나 편리한 것이 크로스 플랫폼 프레임

madchick.tistory.com

 

아래 내용은 리액트 네이티브의 핵심 개념만 정리한 것이고

리액트 네이티브 개발을 시작해 보고 싶다면 - 추천 무료 강의

 

왕초보를 위한 React Native 101 – 노마드 코더 Nomad Coders

React Native로 2개의 앱 만들기

nomadcoders.co

 

 

설치해  샘플 앱 실행시켜 보기

엑스포 vs 네이티브 - 당연히 엑스포 버리고 리액트 네이티브 선택, 뭔가 만들고 싶으면 결국엔 어느 정도 네이티브를 알아야 함. 간단한 앱은 엑스포 만으로 빠르게 만들 수 있다지만, 앱 만들다 보면 엑스포 만으로 만들 수 있는 앱이 거의 없음.

  • Expo CLI : 초반 앱 개발 단순화 (필요한게 모두 들어 있음), 엑스포가 지원하지 못하는 기능 구현을 못함 (추가 네이티브 모듈 사용 불가)
  • React Native CLI : 뭐든 가능, 지원되는 것이 없어서 필요한 기능 구현을 위해서 라이브러리들 설치 필요, 맥북 필수, 네이티브 기본 지식 필수

구글이나 애플 인앱결제를 붙이고 싶다, 구글 애드몹 광고를 붙이고 싶다 등등 - 결국 리액트 네이티브로 가야 함

엑스포의 장점은 맥이 없어도 아이폰 앱 개발이 가능하다는 점

 

하지만, 리액트 네이티브로 앱을 만들면 타입 스크립트로 생성이 되니, 일단은 엑스포로 만들어 자바 스크립트로 개발 하는 방법으로 먼저 진행.

 

자바 스크립트 아니, dart 새로 배우기 싫어서 리액트 네이티브 하는건데, 타입 스크립트 또 배워야 하면 뭐가 달라. 일단은 자바 스크립트 쓸 수 있는 엑스포로 먼저 배우가 타입 스크립트는 나중에 고민. 

 

엑스포로 만든 소스도 나중에 엑스포를 버리고, 리액트 네이티브로 전환 가능.

 

 

Setting up the development environment · React Native

This page will help you install and build your first React Native app.

reactnative.dev

설치해야 할 것이 졸라 많음 ㅠㅠ - 이걸 다 설치하고 정상적으로 세팅해야 앱 개발 가능. 건투를 빕니다.

개발자 되는데 좌절하기 쉬운 부분입니다. 준비를 하는데 이렇게 엄청난 노력이 필요.

- node.js

- JDK

- Android Studio

- Android SDK

- Android Simulator

- Ruby : rbenv 설치해서, 루비는 2.7.6을 설치해야 함 (어이 없게도 다른 버전 설치하면 정상 동작 안됨)

- Xcode

- iOS Simulator

- Cocoa Pods

 

샘플 앱을 만들어 보자 - 졸라 오래 걸림, 꾹 참고 기다리면 만들어짐

  • Expo-CLI : npx create-expo-app my-app
  • Native-CLI : npx react-native@latest init my-app 

 

만든 샘플 앱을 실행 - 아 이건 뭐, 더 머리 아프네. 안되는거 투성 (안되는건 구글링으로 한땀, 한땀 해결)

  • Expo-CLI
    • npx expo start - 화면에 QR 코드 나타남, 각 폰에서 Expo Go 앱 설치 후 스캔하면 앱이 실행 됨
    • npm run android
    • npm run ios
    • npm run web
  • Native-CLI
    • npx react-native start 
    • npx react-native run-android
    • npx react-native run-ios

 

 

 

Core Components

 

Core Components and APIs · React Native

React Native provides a number of built-in Core Components ready for you to use in your app. You can find them all in the left sidebar (or menu above, if you are on a narrow screen). If you're not sure where to get started, take a look at the following cat

reactnative.dev

핵심적으로 익혀야 하는 자주 사용하게 되는 컴포넌트들. 사용하기 쉽고, 많지 않아 쉽게 익힐 수 있음.

- View

- Text

- Image

- ScrollView

- TextInput

- Button

- Switch

 

웹 개발하는 화면과 상당히 비슷하면서도 약간 다름

비슷한 부분이 상당히 많아서 웹 개발 경험이 있으면 받아 들이기 매우 편함

 

버튼, 스위치를 보면 리액트 네이티브와 플러터와 차이점이 확연히 들어나는데

  • 리액트 네이티브 : aos, ios의 컴포넌트를 사용함 - 해당 OS의 기본 UI를 사용하기 때문에 해당 OS의 UI와 동일한 느낌을 줄 수 있음. 대신 양쪽 모두 동일한 느낌의 UI를 만들려면 고달픔
  • 플러터 : 모든걸 플러터가 직접 그려줌, 양쪽이 동일한 UI를 만들 때는 편하지만, 해당 OS의 공유한 UI를 살릴 수 없음

 

뭐가 좋다고 말하기 애매한 장단점인데, 난 개인적으로는 플러터 방식을 선호.

 

App.js

import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View, Image, TextInput, ScrollView, Button, Switch } from 'react-native';

export default function App() {
  return (
    <View style={styles.container}>
      <Text style={styles.text}>Open up App.js to start working on your app!!</Text>
      <StatusBar style="auto" />
      
      <Image source={require("./assets/cat.jpg")} style={styles.local_image} />
      <Image source={{uri: "https://cdn.pixabay.com/photo/2017/02/20/18/03/cat-2083492__340.jpg"}} 
      style={styles.url_image} />
      
      <TextInput placeholder="이름을 입력해주세요" />
      
      <Button title="click" onPress={()=>{console.log("clicked");}}/>

      <Switch value={true} />
      
      <ScrollView>
        <Image source={{uri: "https://i.ytimg.com/vi/ByH9LuSILxU/maxresdefault.jpg"}} 
        style={styles.url_image} />
      </ScrollView>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
  text: {
    fontSize: 20,
    fontWeight: "bold",
  },
  local_image: {
    width: 100,
    height: 100,
  },
  url_image: {
    width: 200,
    height: 200,
  },
});

 

 

 

컴포넌트 만들기, 그리고 prop

컴포넌트 : 재사용 가능하도록 분리된 코드 블럭 - 여러 컴포넌트를 조합하여 화면 구성

컴포넌트에 포함된 데이터가 변경되면 화면 갱신은 프레임워크(리액트 네이티브)가 알아서 새로 고침을 해줌

 

화면설계서를 보고 어떻게 컴포넌트로 나눌까 고민해서 컴포넌트로 분리

 

컴포넌트에 정보 전달을 어떻게 하나? - prop

상위 컴포넌트가 하위 컴포넌트에게 데이터를 전달하는 방법

 

컴포넌트들이 많아지고 복잡해지면 계층구조가 복잡해 질 수 밖에 없음

이러면 여러 컴포넌트들이 정보를 받아서 하위로 전달하는 것이 매우 귀찮아짐

이런 문제를 해결하기 위해서 전역 상태 관리가 필요해지는데, 리액트 웹 개발할 때와 동일하게 redux 를 많이 사용함

 

App.js

import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View } from 'react-native';

const Header = (props) => {
  console.log("Header : ", props);
  return (
    <Text>{props.title}</Text>
  );
}

export default function App() {
  return (
    <View style={styles.container}>
      <Header title="헤더정보" />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

 

 

 

React Hooks - useState, useEffect

Hooks - 변수를 업데이트 하기 위한 체계, 콜백 함수로 구현

리액트 초기 버전은 클래스형으로 개발 되었으나, 상속구조가 복잡해지면서 성능이 저하되는 문제 발생

성능 개선을 위해 함수형으로 변경됨

함수형이다 보니 모든게 함수로 처리, 클래스형에서 클래스의 멤버로 이벤트 핸들러가 담당하던 역할을 함수로 대체

이게 바로 리액트 훅.

 

관리되어야 하는 정보가 있다면 - useState 사용

변수 이름과 함수 이름을 한 쌍으로 관리

 

특정시점에 뭘 해야 한다면 - useEffect 사용

 

함수형으로 사용하면 코드도 훨씬 더 심플해지는 부가적인 장점도 있습니다

 

 

useState 사용해 봅시다

aos와 ios 화면 보이는 모습이 완전 달라서 참 거슬립니다.

이걸 동일하게 맞추는건 상당히 스트레스가 될 것도 같지만, 뭐 그래도 그동안 리액트 네이티브로 만들어진 앱이 몇개고 동일한 고민을 하면서 노력한 사람들이 얼마일건데 다 방법이 있겠죠.

계속 공부해 나가다 보면 알게 될 겁니다.

 

이번엔 컴포넌트를 별도 파일로 분리하고, App.js에서 임포트 하고 화면에 컴포넌트를 출력합니다.

 

App.js

import { StyleSheet, View } from "react-native";
import StateWithFuctionalComponent from "./StateWithFuctionalComponent";

export default function App() {
  return (
    <View style={styles.container}>
      {/* <StateWithClassComponent /> */}
      <StateWithFuctionalComponent />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#fff",
    alignItems: "center",
    justifyContent: "center",
  },
});

 

컴포넌트에서 useState를 사용합니다.

사용을 위한 준비는 매우 간단. 변수명/함수이름 정의하고 초기값을 설정해 주면 됩니다.

어디에 선언해야 하는지 눈여겨 봐두고

사용은 값을 읽고 싶으면 변수명을, 값을 변경하고 싶으면 함수명을 사용하면 됩니다.

간단하죠.

 

StateWithFuctionalComponent.js

import React, { useState } from "react";
import { View, Text, Button, Switch, TextInput } from "react-native";

const Component = () => {
  const [count, setCount] = useState(0); // number
  const [isOn, setIsOn] = useState(false); // boolean
  const [name, setName] = useState(""); // string

  return (
    <View>
      <Text>You clicked {count} times</Text>
      <Button title="Click me" onPress={() => setCount(count + 1)} />

      <Text>-------</Text>
      <Switch
        value={isOn}
        onValueChange={(v) => {
          console.log("v", v);
          setIsOn(v);
        }}
      />

      <Text>-------</Text>
      <TextInput
        value={name}
        onChangeText={(v) => {
          console.log("v", v);
          setName(v);
        }}
      />
    </View>
  );
};

export default Component;

 

 

useEffect 사용해 봅시다

앱이 실행 될 때 한번만 실행되게 하고 싶으면 배열을 빈 상태로 useEffect 사용

 

특정 변수(스테이트)가 변경되었을 때 실행되게 하고 싶으면 배열에 변수명 기입

 

App.js

import { StyleSheet, View, Button } from "react-native";
import UseEffectWithFunctionalComponent from "./UseEffectWithFunctionalComponent";

export default function App() {
  return (
    <View style={styles.container}>
      <UseEffectWithFunctionalComponent />
      <Button title="toggle" onPress={() => setIsTrue(!isTrue)} />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#fff",
    alignItems: "center",
    justifyContent: "center",
  },
});

 

UseEffectWithFunctionalComponent.js

import React, { useEffect, useState } from "react";
import {
  View,
  Text,
  Button,
  TextInput,
  Switch,
  ActivityIndicator,
} from "react-native";

const Component = () => {
  const [count, setCount] = useState(0);
  const [isOn, setIsOn] = useState(true);
  const [input, setInput] = useState("");
  const [isRefresh, setIsRefresh] = useState(false);

  useEffect(() => {
    console.log("didMount");
  }, []);

  useEffect(() => {
    console.log("didUpdate - count", count);
  }, [count]);

  useEffect(() => {
    console.log("didUpdate - isOn", isOn);
  }, [isOn]);

  useEffect(() => {
    console.log("didUpdate - input", input);
  }, [input]);

  useEffect(() => {
    if (isRefresh) {
      setTimeout(() => {
        setIsRefresh(false);
      }, 2000);
    }
  }, [isRefresh]);

  return (
    <View style={{ alignItems: "center" }}>
      <Text>You clicked {count} times</Text>
      <Button title="Click me" onPress={() => setCount(count + 1)} />

      <Text style={{ marginVertical: 15 }}>
        -------------------------------------------------
      </Text>
      <Switch value={isOn} onValueChange={setIsOn} />

      <Text style={{ marginVertical: 15 }}>
        -------------------------------------------------
      </Text>

      <Text>input: {input}</Text>
      <TextInput
        value={input}
        onChangeText={setInput}
        style={{ borderBottomWidth: 1, borderColor: "grey" }}
      />

      <Text style={{ marginVertical: 15 }}>
        -------------------------------------------------
      </Text>
      <Button
        title="새로고침!"
        onPress={() => {
          setIsRefresh(true);
        }}
      />
      {isRefresh && <ActivityIndicator />}
    </View>
  );
};

export default Component;

 

 

 

제공되는 것 말고 내가 맘대로 하고 싶어 - custom hook

useState 는 1개의 변수(스테이트)와 1개의 함수만 설정 가능

함수를 여러개로 확장하고, 내가 지은 이름으로 만들고 싶을 때 커스텀 훅 사용

이름은 항상 use 로 시작해야 함

 

App.js

import { StyleSheet, View } from "react-native";
import CustomHook from "./CustomHook";

export default function App() {
  return (
    <View style={styles.container}>
      <CustomHook />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#fff",
    alignItems: "center",
    justifyContent: "center",
  },
});

 

CustomHook.js

import React, { useState } from "react";
import { Button, TextInput, View } from "react-native";

const InputBox = (props) => {
  return (
    <View style={{ flexDirection: "row" }}>
      <TextInput
        value={props.value}
        onChangeText={props.onChangeText}
        style={{ borderBottomWidth: 1, width: 200 }}
        placeholder={props.placeholder}
      />
      <Button title="초기화" onPress={props.onReset} />
    </View>
  );
};

const useInput = (initialValue) => {
  const [value, setValue] = useState(initialValue); 

  const resetValue = () => setValue(initialValue); 

  return {
    value,
    setValue,
    resetValue,
  };
};

const CustomHook = () => {
  const {
    value: name,
    setValue: setName,
    resetValue: resetName,
  } = useInput("");
  const { value: age, setValue: setAge, resetValue: resetAge } = useInput("");
  const {
    value: city,
    setValue: setCity,
    resetValue: resetCity,
  } = useInput("");

  return (
    <View>
      <InputBox
        value={name}
        onChangeText={setName}
        placeholder="이름을 입력해 주세요"
        onReset={resetName}
      />
      <InputBox
        value={age}
        onChangeText={setAge}
        placeholder="나이를 입력해 주세요"
        onReset={resetAge}
      />
      <InputBox
        value={city}
        onChangeText={setCity}
        placeholder="사는 곳을 입력해 주세요"
        onReset={resetCity}
      />
    </View>
  );
};

export default CustomHook;

 

 

 

[번역] 2023년 버전 리액트 프로젝트를 시작하는 방법

이 글에서는 2023년의 시점에서 새로운 리액트 프로젝트를 시작할 때 고려할만한 몇 가지 스타터 키트를 소개하고 있습니다. 각각의 장단점과, 적정한 기술 수준을 제시하고 있으니 새 리액트 프

velog.io

 

GitHub - noddy1996/react-native-redux-starter: A starter boilerplate for a mobile app using React Native and Redux.

A starter boilerplate for a mobile app using React Native and Redux. - GitHub - noddy1996/react-native-redux-starter: A starter boilerplate for a mobile app using React Native and Redux.

github.com

 

GitHub - eramudeep/react-native-ecommerce: React native starter kit for an e-commerce application developed by Amusoftech.

React native starter kit for an e-commerce application developed by Amusoftech. - GitHub - eramudeep/react-native-ecommerce: React native starter kit for an e-commerce application developed by Amus...

github.com

 

 

 

바이킹스워프 잠실점 롯데월드몰 - 랍스터와 해산물이 마음껏 무제한

샌프란시스코의 Fisherman's Wharf(피셔맨스 워프)를 모티브로 해산물들이 눈 앞에 펼쳐져 마치 해안가에 와 있는 듯한 경험을 선사합니다. 캐나다와 미국으로부터 직접 공수한 랍스터를 무제한으로

madchick.tistory.com

 

경동시장 안동집 손칼국시, 국물 맛이 끝내주는데 저렴해요 가성비 갑

외근 나갔다가 복귀 길에 점심시간이 되어 경동시장에 들러 손칼국시로 점심을 해결했어요. 서울에서 나고 자랐지만, 경동시장은 처음입니다. 어릴적 부모님 따라 가봤지만, 기억이 안 날 수도

madchick.tistory.com

 

추워지기 전에 얼른 가봐야할 북한산 뷰가 예술인 평창동 까페 더 피아노, 전망 좋은 힐링 까페

아직 늦지 않았습니다. 더 추워지기 전에 가면 좋습니다. 저는 작년에 다녀왔고, 작년에 찍은 사진이라 지금은 약간 다를 수 있습니다. 영화나 드라마, 화보, CF 촬영 장소로 많이 사용되었다고

madchick.tistory.com

 

대부도 유리섬박물관, 경기도 안산 가볼만한 나들이 명소, 당일치기 여행 코스

한국의 무라노 대부도 유리섬, 안산 및 대부도에서 갈만한 곳의 대명사인 유리섬박물관의 캐치프레이즈 입니다. 무라노는 수공예를 고집하는 유리공예가 유명한 이탈리아의 작은 섬 이라고 합

madchick.tistory.com

 

뚝섬역, 저녁에 혼자 가면 반주 주는 성수동 일본 가정식 맛집 메시

사무실 근처에 있어서 야근할 때 저녁 먹으러 자주 가는 곳 입니다. 뚝섬역에서 매우 가깝습니다. 서울숲에서는 조금 멀어요. 블루보틀 가깝습니다. 홈페이지 가보면 혼밥, 혼술 하기 좋은 일본

madchick.tistory.com

 

안산 소고기 맛집 아리화 - 맛과 분위기 모두 잡는 최고 선택

안산 고잔동의 소고기 맛집으로 추천하는 아리화. 멋진 인터리어 덕분에 분위기도 아주 좋습니다. 함 가보심 후회 없이 만족하실 겁니다. 매장 인테리어는 어떻게 이렇게 하게 되었는지, 나중에

madchick.tistory.com

 

기장 웨이브온 (WAVEON COFFEE), 부산 기장 핫플 명성이 괜히 생긴게 아닌 뷰맛집 까페

예전에 한국수력원자력 인재개발원 출장 다니다가 알게된 까페입니다. 한수원 오가며 이 앞을 지나갈 수 밖에 없는데, 이 앞을 지날 때면 차량 정체가 되는 경우가 많은 겁니다. 도대체, 왜? 하

madchick.tistory.com

728x90