728x90

휘발유와 경유에 부과되는 세금은

이들의 가격을 인상시켜

국민 생활비 및 기업의 생산비용에

영향을 끼칠 뿐만 아니라

유류를 사용하는 자동차들로부터

발생하는 대기오염이나 교통혼잡 등을

감소시키는 역할을 한다.

 

또한, 우리나라처럼 석유의

대외 의존도가 높은 국가의 경우

휘발유과 경유에 대한 높은 세금부과는

소비의 감소를 통해 국부의 유출을

억제하는 효과를 가져올 수도있다.

 

2007년기준,

국세의 약 9.2%

지방세의 약 7.2%가

유류세이다.

 

[출처]: 조명환 한국조세연구원 초청연구위원

- 유류세의 이해 및 국제비교: 휘발유와 경유를 중심으로

728x90
반응형
728x90

최신 자바스스크립트 개발환경은

3강체제이다

 

React, Vue, Angular

 

그중에서도 최강자 React를

운좋게도 처음시작부터 계속해서 쓰고있다.

 

근데 이게 너무나도 빨리

유행이 변해가다보니

따라가기가 정말 쉽지않다

 

자칫 쉬어가다간 바로 고인물이다.

 

유행을 따라가는게 좋은것만은 아니라고들 하지만

React의 변화는 단순한 유행이 아니다.

 

예를들어, 망치가 처음 만들어졌을떄

지금의 모습이 아니었을것이다

 

힘을 더 잘주기위해 손잡이가 길어졌을테고

너무 길어지니 과유불급이라 판단되어

적정 수준의 길이를 찾았을테고

쇠뭉탱이의 무게나 크기도

오랜 시간을 거쳐 발전했을것이다

 

React도 마찬가지인것같다

 

그중에서도  React의 핵심이라고 할수있는

state (상태)

에 대한 관리는 많은 이슈를 가지고있다

춘추전국시대가 끝이 보이질않는다

 

Context API

Redux

MobX

Context API의 진화

Recoil

...

...

 

각각이 가진 고유한 특성과

장단점들이 있다

 

내가 가진 얄팍한 지식으로 그것들을 설명하기보다는

써본 결과..

 

너무나도 불편하고 지저분하고

이렇게까지 해야되나? 

그냥 예전 바닐라 스크립트 시절이 더 날지도...

라는 생각과

 

사과하나 자르려고

사과 공장을 차리는 느낌이 자꾸만 들었다

 

그런와중에 내가 원하는

바로 그 라이브러리를 찾게되었다.

 

내가 원하는건 별게아니다

1. 글로벌 상태 관리

2. 지저분한 코드 x

3. 간편한 유지보수

 

이 3가지면 충분하다.

뭐 미들웨어로 어쩌니저쩌니

커스텀 훅으로 두들겨패니 뭐니

 

그런거는 기본기만 되어있으면

원하는 사람이 붙여쓰면 되는것 아닌가?

 

이런 나의 니즈를 충족시켜준

라이브러리는

react-query (+ axios)

 

react-query는 서버로 부터 받아온 정보를

(통칭 Server state)

정말 간단하게 글로벌 state로써 관리하고

재호출할수 있으며

로딩/실패/성공에 대한 진행상황(status)을

체크할 수 있다.

 

진짜, 간략한 샘플코드를 확인해본다.

// index.js
import React from 'react';
import App from './App';
import { QueryClientProvider, QueryClient } from 'react-query';
...

function main() {
  const queryClient = new QueryClient();
  
  return (
    <QueryClientProvider client={queryClient}>
      <App />
    </QueryClientProvider>
  )
}

...
// api.ts
import axios from 'axios';
import { useQuery } from 'react-query';

const getNameAPI = async () => {
  try {
    const { data } = axios.get('...');
    return data;
  } catch(error) {
    throw error;
  }
}

export const getName = () => {
  return useQuery('test/name', getNameAPI);
}
// App.tsx
import React, { useEffect } from 'react';
import { useQueryClient } from 'react-query';
import { getName } from './api';

function App() {
  const queryClient = useQueryClient();
  const { data, status, error, isLoading } = getName();
  console.log('fetching data 확인: ', data, status, error, isLoading);
  
  useEffect(() => {
    // 5초뒤에 이전에 호출했던 API에서 받아온 server state를 확인해본다.
    setTimeout(() => {
      const data = queryClient.getQueryData('test/name');
      console.log('data: ', data);
    }, 5000);
  }, []);
  
  return (
    <div>
      ...
    </div>
  )
}

 

어떤가?

아주 기본적인 방식으로 axios, fetch등을

이용해 데이터를 호출한뒤

useQuery에 key와 콜백함수만 등록해주면

 

queryClient를 이용해서

이전에 불러왔던 데이터를 확인할 수 있다.

 

redux를 쓸때의 코드양과 비교하면

이루 말할수없을정도로

행복한 코드라고 할수있다.

 

서버로 부터 불러온 데이터를 재가공할일이

많다던지 하는 경우에는

redux를 쓰라고 한다.

 

그건 그때가서 볼일이다.

웹/앱을 개발한지 얼마 안됐을때

그 프로젝트가 그 단계까지 갈지말지는

모르는법이다.

 

가볍게 시작해서

살을 더해가자

 

728x90
반응형
728x90

대부분의 성행하는 앱들은

앱 실행시 진입 화면이 있다.

 

그것이 광고성 이미지가 될수도있고

앱을 간략히 소개하는 이미지가 될수도 있다.

 

우리도 앱개발을 하다보면

반드시 넣을수밖에 없는 기능이라고 봐야한다.

 

준비물

1. react-native-splash-screen

2. @bam.tech/react-native-make

 

// splash-screen
npm install react-native-splash-screen
or
yarn add react-native-splash-screen


// helper
npm install --save-dev @bam.tech/react-native-make
or
yarn add npm --dev @bam.tech/react-native-make

 

@bam.tech/react-native-make는 설치시

설정할것은 따로없다.

 

react-native-splash-screen은 문서에 따르면

무언가 많이 설정해줘야하지만

그것을 따르지말고

아래의 가이드라인만 따라서 해보자

 

// android/app/src/main/java/com/[projectName]/MainActivity.java

...
import org.devio.rn.splashscreen.SplashScreen;
...

public class MainActivity extends ReactActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    SplashScreen.show(this, R.style.SplashScreenTheme); // 여기 추가
    super.onCreate(savedInstanceState);
  }

  /**
   * Returns the name of the main component registered from JavaScript. This is used to schedule
   * rendering of the component.
   */
  @Override
  protected String getMainComponentName() {
    return "ProjectName";
  }
}

 

...
#import "RNSplashScreen.h"
...

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  ...
  
  [RNSplashScreen show];
  return YES;
}

 

헬퍼로 이미지 변환해서 splash file 설정하는 스크립트 추가

// 사용방법: react-native set-splash --path [path-to-image] --resize [contain|cover|center] --background ["background-color"]
// NOTE: 이렇게 스크립트를 만들어두면 잊어버릴일도 없고, 이미지나 배경색상을 바꿀때 이 스크립트만 수정해주면 된다
// package.json

"scripts": {
  ...
  "splash": "react-native set-splash --path [image path] --background ['color'] --resize contain"
}

스크립트를 실행시켜보자

npm run splash

 

마지막으로, 실행된 스크립트가

앱 실행후 자동으로 닫히도록

코드를 추가

// project root - index.js or app.js

import SplashScreen from 'react-native-splash-screen';

...
useEffect(() => {
  SplashScreen.hide();
}, []);
...


or 

...
useEffect(() => {
  setTimeout(() => {
    SplashScreen.hide();
  }, 1500);
}, []);
...

 

728x90
반응형
728x90

[프로젝트 생성하기]

https://honeystorage.tistory.com/252?category=784116 

 

[개발환경 세팅하기]

https://honeystorage.tistory.com/253?category=784116 

 

[절대경로, 스타일 모듈 및 에디터 설정]

https://honeystorage.tistory.com/254

 

[푸시 알림 보내기]

https://honeystorage.tistory.com/255

 

앱 작업환경을 셋업하고

푸시알림만 붙였을뿐인데

이렇게나 힘이들다

 

이제 본격적인 작업에 앞서

배포를 먼저 테스트 해보고 작업을 진행할 예정이다

 

백날 만들었다가 배포에서 막히느니

배포하는 방법을 미리알아두고

작업 스케줄링을 하는게

맞다고 생각하기 때문이다.

 

검수를 실제로 넣지는 않고

android, ios 별로 검수 넣기 직전까지

어떻게 해나가야 하는지를 살펴볼것이다.

 

이번장은 

안드로이드 앱 배포하기 이다.

https://reactnative.dev/docs/0.63/signed-apk-android

(위에 공식 가이드를 따르면 어렵지않게 가능하다.)

 

(mac)

// java home 경로로 이동
cd /Library/Java/JavaVirtualMachines/jdkX.X.X_XXX.jdk/Contents/Home

// key file 생성
sudo keytool -genkey -v -keystore my-upload-key.keystore -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000

* key file 생성시 질문 순서
name -> company -> team -> si,gun,gu -> local -> countryNumber -> y
(android)

// 폴더위치
C:\Program Files\Java\jdkx.x.x_x\bin

// key file 생성
keytool -genkeypair -v -keystore my-upload-key.keystore -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000

* 일반 터미널에서 잘 안될수도있다.
그떄는 관리자 권한으로 터미널을 실행해보거나
PowerShell에서 시도해보자.

 

이때, 비밀번호를 지정하게되는데
gradle.properties에 입력해줘야되니
잊지않도록 하자

 

또한, 앱을 여러개 운영하거나

할 계획이 있다면 Key File의 이름도

앱이름에 맞게 변경해주는것이 좋다

 

생성된 Key File을 project/android/app/

에 위치시켜주고

project/android/gradle.properties
(혹은 project/android/.gradle/gradle.properties)

의 내용을 수정해줄 것이다.

MYAPP_UPLOAD_STORE_FILE=my-upload-key.keystore
MYAPP_UPLOAD_KEY_ALIAS=my-key-alias
MYAPP_UPLOAD_STORE_PASSWORD=*****
MYAPP_UPLOAD_KEY_PASSWORD=*****

좀전에 만든 키파일의 정보를 세팅해주면 된다.

 

마지막으로 project/android/app/build.gradle을

수정해주면 배포 준비는 모두 끝이다.

...
android {
    ...
    defaultConfig { ... }
    signingConfigs {
        release {
            if (project.hasProperty('MYAPP_UPLOAD_STORE_FILE')) {
                storeFile file(MYAPP_UPLOAD_STORE_FILE)
                storePassword MYAPP_UPLOAD_STORE_PASSWORD
                keyAlias MYAPP_UPLOAD_KEY_ALIAS
                keyPassword MYAPP_UPLOAD_KEY_PASSWORD
            }
        }
    }
    buildTypes {
        release {
            ...
            signingConfig signingConfigs.release // 기본이 debug로 되어있음
        }
    }
}
...

 

 

이제 빌드해서 PlayStore에 배포만 하면 되는데

빌드를 위한 명령어와 빌드된 파일의

경로는 아래와 같다

// 빌드 명령어
cd android
./gradlew bundleRelease

 

 

이제 ios 검수(검수가 통과되면 AppStore Connect에서 배포 가능합니다.)를

위한 작업들을 진행해볼건데

그다지 어렵지는 않다.

안해봐서 복잡해 보일뿐

 

과정은 이렇다

1. 빌드를 한다

2. AppstoreConnect에 접속한다.

3. 새 앱을 추가한다.

4. 앱 정보를 기입하고 검수를 넣는다.

5. 배포한다.

 

빌드를 위한 인증서가 더 필요한데,

이전 게시물에서 우리가 만들지 않은것같으니

한번 해보도록하자.

 

먼저 Apple Developer > Certificate로 가보자

여기 보이는 iOS Distribution 인증서를 하나 만들어야한다.

이걸 가지고 ProfileVisioning 파일을 새로 만들것이다.

 

만들었다면

Apple Developer > profiles로 가보자

App Store에 배포할 목적의 인증서느

Dist > App Store 항목을 선택후 진행해서

파일을 다운받자.

 

파일에는 파일의 목적을 명확히 알수있는

이름을 지어주는게 좋다

예를들면, AppStoreAppname. ~~~

 

파일 다운로드까지 한뒤

xcode로 이동

 

거의 다 했다

xcode > product > Clean Build Folder

(요놈은 빌드전 한번씩은 꼭 해주자)

 

다음은

xcode > product > Archieve

여기서 Distribute App을 선택하면

ProfileVisioning 파일을 요구한다.

그때 좀전에 만든 App store용 파일을 import하면 된다.

 

이런식으로

어렵지 않게 업로드를 진행할 수 있다.

 

이제  AppStore Connect로 이동해보면

이렇게 업로드한 앱이 나타난다.

검수를 위해서는

이렇게 알맞는 이미지와

정보들을 입력해서 제출하면된다.

검수가 통과된 경우

앱스토어에 퍼블리싱이 가능해진다.

 

728x90
반응형
728x90

[프로젝트 생성하기]

https://honeystorage.tistory.com/252?category=784116 

 

[개발환경 세팅하기]

https://honeystorage.tistory.com/253?category=784116 

 

[절대경로, 스타일 모듈 및 에디터 설정]

https://honeystorage.tistory.com/254

 

[ 푸시알림 완벽구현 - 최종판 ]

https://honeystorage.tistory.com/306

 

푸시알림 보내기와 관련된 키워드는 크게 4가지이다

1. react-native-push-notification && @react-native-community/push-notification-ios

2. react-native-firebase/app && react-native-firebase/messaging

3. 푸시알림 관련 firebase 설정

4. 푸시알림 관련 apple developer 설정

 

 

솔직히 앱을 개발할때 처음에 한번만 하면 되지만

할게 많아 마치 진입장벽처럼 느껴진다.

 

심지어 자주 안하다보니 매번 할때마다 새로운느낌...

 

 

(1) react-native-push-notification

그리고 @react-native-community/push-notification-ios

관련 세팅

 

늘 그렇듯 필요한 모듈을 설치하고

환경설정을 먼저 해본다.

 

yarn add react-native-push-notification
yarn add @react-native-community/push-notification-ios

https://www.npmjs.com/package/react-native-push-notification

환경설정은 위에 가이드라인을 충실히 따르자

https://www.npmjs.com/package/@react-native-community/push-notification-ios

ios를 위해 추가적으로 설정을 해주자

 

 

 

(2) firebase를 설정

낯선용어라도 겁먹지말고 잘 따라가면 

완성되어있을테니 가이드라인을 따라가보자

 

https://firebase.google.com/?hl=ko

위 링크로 접속해서 프로젝트를 생성해보자

그리고 위 버튼을 통해 ios와 android를 만든다.

다음, 안드로이드를 클릭

google-service.json을 다운로드해서

your-project/android/app/

경로에 위치시킨다.

 

ios에서도

GoogleService-Info.plist를

내려받아보자.

마찬가지로 적절한 위치에 배치할건데

xcode를 이용할것이다.

위 버튼에서 열리는 창에서

GoogleService-Info.plist를 add

 

아주 기본적인 설정은 여기까지이다.

 

지금부터는 돈이 들어간다.  ios때문이다.

안드로이드만 운영할 사람은 이 파트는 생략해도좋다.

 

(3) apple developer 설정

https://developer.apple.com/kr/

위 링크로 접속해서 계정(account)를 클릭

개발자 멤버십을 구매하자 (년 단위 갱신이 필요하다)

 

결제가 완료되면 아래와 같은

화면을 만날수 있다.

 

본격적인 시작에 앞서

키체인을 통해 CSR이라 불리는 파일을 하나 만들것이다.

mac에서 키체인 접근을 열어보자

키체인 접근 >>> 인증 기관에서 인증서 요청

위와 같이 옵션 체크해서 

저장하면 

이런 파일을 얻을수 있다.

해당 파일은 앞으로 작업에 계속 쓰일예정이다.

 

 

Apple developer 사이트를 열어보자

여기서 Apns key파일을 만들건데 여기서 만드는 키 파일은

최대 2개까지만 생성 가능하다.

관리에 주의하자

첫번째 항목을 선택해서

확실하게 알아볼수 있는 KeyName을 붙여준뒤

생성!

 

 

다음은

다시 apple developer 사이트로 돌아가

Certificates, IDs 탭으로 이동

 

바로 인증서를 만들어볼것이다

추가 버튼을 눌러보자

2가지 인증서를 만들어야한다.

한개는 개발용, 나머지 하나는 배포용이다.

 

우리는 10개정도의 인증서를 만들어낼 계획이니

인증서의 네이밍을 확실하게 하자.

 

여기서는

IOS Developer

Apple Distribution

두개의 파일을 추가로 얻었다

계속해서 진행해보자

 

다운받은 두개의 파일을

각각 더블클릭해서

키체인에 추가한다.

xcode에서 팀 인증을 할때 release, debug에 맞게 쓰면 된다.

 

 

AppID를 추가할 차례이다

다시 Apple developer 사이트로 돌아가보자

화면의 + 버튼을 눌러보자 >>> App Ids 상태로 Continue >>> App 상태로 Continue

 

** 앱 상세기능 목록에서 Push Notification을 추가할것이다.

중요한 포인트는 Push Notification 라인에

잘 보면 Configure 0개라고 써있을것이다

 

이것을 클릭해서 2개를 추가해줄건데

이때, 처음에 만들었던 CSR 파일을 이용하면 된다.

 

추가했다면 다음으로 넘어가

xcode를 참고해 Bundle ID를 입력,

Description은 어떤 앱을 위한 ID인지 분별할수 있게 작성

AppID 설정도 완료되었다

 

 

이제 60%쯤왔다.

정말 길고 힘들지만

피할수없다. 앱에 푸시를 안쓸거면

굳이 앱이 필요한가? 싶을정도로

푸시는 중요하기 때문이다.

 

 

테스트 Device를 등록하면

TestFlight이나 Firebase Distribution을 이용해

온디바이스 테스트를 진행해볼수도 있다.

 

근데 이 또한 지나칠수 없다.

Simulator에서는 푸시알림 테스트가

애플 정책상 불가하기 때문이다.

Apple Developer에서 테스트 디바이스를 추가할 수 있다.

맥에 아이폰을 연결하면

xcode에서 내 아이폰(혹은 다른 ios 디바이스)의

고유 아이디를 확인할수있다.

(xcode > Window > Devices and Simulators)

 

 

Apple Developer에서 해야할 마지막 작업이다.

바로 Privisioning Profiles...

어렵지 않다.

이전에 했던 작업들과 유사하다.

이미지의 선택된

두가지 항목에 대해서 작업을 진행할것이다.

위 작업은 계속해서 Continue하며 안내에 따라 진행하면된다.

이때 다운로드하는 파일의 이름을 dev, dist에 알맞게 지어주자

이렇게 앞에 수식어를 붙여줌으로써 구분해두면 좋다

이제 요 파일들을 가지고 xcode에서

signing 작업을 진행해줄것이다.

debug, release 탭에서 동일하게 위와같이 작업해주면된다

Auto는 꺼주고 다운로드 받은 Provisioning Profile로

인증을 진행해주는것이다.

 

마지막으로 

xcode에서 뺴먹지말고 해줄 셋팅이있다.

https://rnfirebase.io/messaging/usage/ios-setup

위 가이드라인에도 잘 나와있듯이

 

이렇게 Background Modes에 두가지 속성을 켜주고

PushNotification을 셋업해줘야한다.

 

 

자, 이제 지긋지긋한 셋업도

진짜 끝나간다.

 

아래 모듈을 설치하자

yarn add @react-native-firebase/app
yarn add @react-native-firebase/messaging

cd ios/ && pod install

(npm으로 하지말고 yarn으로 설치할것을 권장

안그러면 설치 오류를 만날수있음)

 

프로젝트에 firebase.json추가

// firebase.json

{
  "react-native": {
    "analytics_auto_collection_enabled": false,
    "messaging_auto_init_enabled": false,
    "messaging_android_headless_task_timeout": 30000,
    "messaging_android_notification_color": "@color/dark"
  }
}

 

드디어 코드를 만들어볼 차례이다.

 

utils/push.fcm.js

utils/push.noti.js

 

utils 폴더를 하나 만들고 그 아래 두개의 파일을 만들어보자

(babel.config.js와 tsconfig.json 세팅도 이전 게시물을 참고해서 설정해보기)

 

// push.noti.js

import PushNotification from 'react-native-push-notification';
import PushNotificationIOS from '@react-native-community/push-notification-ios';
import {Platform} from 'react-native';

class LocalNotificationService {
  configure = onOpenNotification => {
    PushNotification.configure({
      onRegister: function (token) {
        console.log('[LocalNotificationService] onRegister : localtoken', token);
      },
      onNotification: function (notification) {
        console.log('[LocalNotificationService] onNotification ', notification);
        if (!notification?.data) {
          return;
        }
        notification.userInteraction = true;
        onOpenNotification(Platform.OS === 'ios' ? notification.data.item : notification.data);

        if (Platform.OS === 'ios') {
          notification.finish(PushNotificationIOS.FetchResult.NoData);
        }
      },
      permissions: {
        alert: true,
        badge: true,
        sound: true,
      },
      popInitialNotification: true,
      requestPermissions: true,
    });
  };

  unRegister = () => {
    PushNotification.unregister();
  };

  showNotification = (id, title, message, data = {}, options = {}) => {
    PushNotification.localNotification({
      // Android only Properties
      ...this.buildAndroidNotification(id, title, message, data, options),
      // IOS and Android properties
      ...this.buildIOSNotification(id, title, message, data, options),
      // IOS and Android properties
      title: title || '',
      message: message || '',
      playSound: options.playSound || false,
      soundName: options.soundName || 'default',
      userInteraction: false,
    });
  };

  buildAndroidNotification = (id, title, message, data = {}, options = {}) => {
    return {
      id: id,
      authCancel: true,
      largeIcon: options.largeIcon || 'ic_launcher',
      smallIcon: options.smallIcon || 'ic_notification',
      bigText: message || '',
      subText: title || '',
      vibrate: options.vibrate || true,
      vibration: options.vibration || 300,
      priority: options.priority || 'high',
      importance: options.importance || 'high',
      data: data,
    };
  };

  buildIOSNotification = (id, title, message, data = {}, options = {}) => {
    return {
      alertAction: options.alertAction || 'view',
      category: options.category || '',
      userInfo: {
        id: id,
        item: data,
      },
    };
  };

  cancelAllLocalNotifications = () => {
    if (Platform.OS === 'ios') {
      PushNotificationIOS.removeAllDeliveredNotifications();
    } else {
      PushNotification.cancelAllLocalNotifications();
    }
  };

  removeDeliveredNotificationByID = notification => {
    console.log('[LocalNotificationService] removeDeliveredNotificationByID:', notification);
    PushNotification.cancelLocalNotifications({id: `${notificationId}`});
  };
}

export const localNotificationService = new LocalNotificationService();

 

// push.fcm.js

import messaging from '@react-native-firebase/messaging';
import {Alert, Platform} from 'react-native';

class FCMService {
  register = (onRegister, onNotification, onOpenNotification) => {
    this.checkPermission(onRegister);
    this.createNotificationListeners(onRegister, onNotification, onOpenNotification);
  };

  registerAppWithFCM = async () => {
    if (Platform.OS === 'ios') {
      await messaging().setAutoInitEnabled(true);
    }
  };

  checkPermission = onRegister => {
    messaging()
    .hasPermission()
    .then(enabled => {
      if (enabled) {
        this.getToken(onRegister);
      } else {
        this.requestPermission(onRegister);
      }
    })
    .catch(error => {
      console.log('[FCMService] Permission rejected ', error);
    });
  };

  getToken = onRegister => {
    messaging()
    .getToken()
    .then(fcmToken => {
      if (fcmToken) {
        onRegister(fcmToken);
      } else {
        console.log('[FCMService] User does not have a device token');
      }
    })
    .catch(error => {
      console.log('[FCMService] getToken rejected', error);
    });
  };

  requestPermission = onRegister => {
    messaging()
    .requestPermission()
    .then(() => {
      this.getToken(onRegister);
    })
    .catch(error => {
      console.log('[FCMService] Request Permission rejected', error);
    });
  };

  deleteToken = () => {
    messaging()
    .deleteToken()
    .catch(error => {
      console.log('[FCMService] Delete token error', error);
    });
  };

  createNotificationListeners = (onRegister, onNotification, onOpenNotification) => {
    messaging().onNotificationOpenedApp(remoteMessage => {
      if (remoteMessage) {
        const notification = remoteMessage.notification;
        onOpenNotification(notification);
      }
            
      Alert.alert(remoteMessage.body);
    });

    messaging()
    .getInitialNotification()
    .then(remoteMessage => {
      if (remoteMessage) {
        const notification = remoteMessage.notification;
        onOpenNotification(notification);
      }
    })
    .catch(error => {
      console.log('quit state notification error : ', error);
    });

    this.messageListener = messaging().onMessage(async remoteMessage => {
      if (remoteMessage) {
        let notification = null;
        if (Platform.OS === 'ios') {
          notification = remoteMessage.data.notification;
        } else {
          notification = remoteMessage.notification;
        }
        onNotification(notification);
      }
    });

    messaging().onTokenRefresh(fcmToken => {
      onRegister(fcmToken);
    });
  };

  unRegister = () => {
    this.messageListener();
  };
}

export const fcmService = new FCMService();

 

// app.js - 단순 참고용이니 참고만하자

import {fcmService} from '@utils/push.fcm';
import {localNotificationService} from '@utils/push.noti';

const App = () => {
  const [token, setToken] = useState('');
  
  useEffect(() => {
    fcmService.registerAppWithFCM();
    fcmService.register(onRegister, onNotification, onOpenNotification);
    localNotificationService.configure(onOpenNotification);
  }, []);

  const onRegister = (tk: string) => {
    console.log('[App] onRegister : token :', tk);
    if (tk) setToken(tk);
  }

  const onNotification = (notify: any) => {
    console.log('[App] onNotification : notify :', notify);
    const options = {
      soundName: 'default',
      playSound: true,
    };

    localNotificationService.showNotification(
      0,
      notify.title,
      notify.body,
      notify,
      options,
    );
  }

  const onOpenNotification = (notify: any) => {
    console.log('[App] onOpenNotification : notify :', notify);
    Alert.alert('Open Notification : notify.body :' + notify.body);
  }
  
  const onCopy = () => {
    Clipboard.setString(token);
  }
  
  ...
  
  return (
    ...
      <TouchableOpacity onPress={onCopy}>
        <Section title="My token">{token || 'unknown token'}</Section>
      </TouchableOpacity>
    ...
  )
}
// index.js - 마찬가지다 참고만하자

import messaging from '@react-native-firebase/messaging';

messaging().setBackgroundMessageHandler(async remoteMessage => {
    console.log('Background remote message: ', remoteMessage);
});

function HeadlessCheck({isHeadless}) {
    if (isHeadless) {
        return null;
    }

    return <App />;
}

AppRegistry.registerComponent(appName, () => HeadlessCheck);

 

길고 긴 세팅이 모두 끝났다.

알림을 발송해보자

 

안드로이드와 아이폰에서 각각 앱을 빌드해두고

(npm run android, npm run ios)

firebase 페이지에 접속한다

인증키를 아직 등록하지 않았다면

화살표부분에 인증키를 입력하는 버튼이 있을것이다.

아까 처음에 만들었던 Apns키를 여기서 등록해주면 된다.

Team ID는

Apple Developer -> Account에서 확인할 수 있다.

 

 

자 발송을 해보자!

위 버튼을 이용해서 알림을 발송해 볼수있는데

이때 토큰을 입력하라고한다.

토큰의 디바이스별로 발급되는 고유 토큰인데

 

위에서 작성한 코드를 통해 우리는

UI로 확인하고 터치하여 코드를 복사할 수 있다.

화면에서 코드를 복사해다가

발송을 시도해보자

 

잘 되길 바란다.

미래의 나에게...

728x90
반응형
728x90

[프로젝트 생성하기]

https://honeystorage.tistory.com/252?category=784116 

 

[개발환경 세팅하기]

https://honeystorage.tistory.com/253?category=784116 

 

에 이어서

실제 개발에는 필수적이라고 할수있는

절대경로, 스타일 모듈 및 에디터 설정을 진행할것이다.

 

키워드는

babel-plugin-module-resolver

styled-components

prettier

3가지 이다.

 

먼저, styled-components를 통해 개발하기 위한 설정을 해주고

아주 작은 컴포넌트를 만들어볼것이다.

 

1. styled-components

설치를 해보자.

npm i --save styled-components
npm i --save-dev babel-plugin-styled-components @types/styled-components @types/styled-components-react-native

이제, babel.config.js에 코드 한줄만 추가해보자.

module.exports = {
  presets: ['module:metro-react-native-babel-preset'],
  plugins: ['babel-plugin-styled-components'], // 이것 추가
};

 

자, 설정은 모두 끝났다.

이전에 react를 다뤄봤다면 어렵지않게 

styled-components로 컴포넌트를 하나 만들어본다.

 

// App.tsx

const CustomBox = styled.View`
  width: 200px;
  height: 200px;
  background-color: red;

  // display: flex; - react-native의 View는 기본이 flex이다.
  align-items: center;
  justify-content: center;
`;

const CustomText = styled.Text`
  font-size: 16px;
  color: white;
  text-align: center;
`;

const App = () => {
  ...
  
  return (
    ...
      <CustomBox>
        <CustomText>제가 직접 만들었어요</CustomText>
      </CustomBox>
    ...
  )
}

 

화면에서 확인해보자

 

 

이런 이미지가 잘 나타났는가?

그렇다면  styled-components 개발환경 세팅은 끝났다고 볼수있다.

 

 

그런데 우리는 App.tsx 에서만 개발을 진행할수는 없다.

작은 단위로 구조화해서 개발을 진행해야하는데

그러려면 절대경로 설정은 필수라고 할수있다.

"../../../../../" 와 같은 지옥을 맛보기 싫다면

 

(다만, 규모가 작은 프로젝트에서

절대경로 설정에 어려움을 겪는다면 

굳이 설정할 필요는 없다고 생각한다.

그 시간에 빨리 작업을 마치는것도

개발 센스라고 본다.)

 

2. 절대경로 세팅하기

babel-plugin-module-resolver 를 설치하자.

npm i --save-dev babel-plugin-module-resolver

 

https://www.npmjs.com/package/babel-plugin-module-resolver

여기 가이드라인이 제법 잘 나와있다

따라해 보자.

루트 경로 아래에 src 폴더를 하나만든 뒤

// babel.config.js 파일을 만들고 아래와 같이 설정

{
  "plugins": [
    [
      "module-resolver",
      {
        "root": ["."],
        "alias": {
          "@components": "./src/components",
        },
        "extensions": [".ios.ts",".android.ts", ".ts", ".ios.tsx", ".android.tsx", ".tsx", ".jsx", ".js", ".json"]
      }
    ]
  ]
}
// tsconfig.json
{
  "compilerOptions": {
    ...
    "baseUrl": "./src",
    "paths": {
      "@components": [
        "./components/*"
      ],
    }
    ...
  }
}

 

빨간줄이 나온다거나 컴파일 에러가 보여질수 있습니다.

열려있는 스크립트 파일은 모두 닫고,

실행중인 런타임도 종료한뒤

npm start --reset-cache

위 명령어로 다시 런타임을 실행하면

정상적으로 절대경로 사용이 가능해진것을

확인해볼 수 있습니다.

 

그렇다면 이전에 작성한 코드를 옮겨봅니다.

./src/components/sample.tsx 파일을 만들고

해당 파일로 이전에 작성한 CustomText, CustomBox 코드를 옮깁니다.

// ./src/components/sample.tsx

import styled from 'styled-components/native';

const CustomBox = styled.View`
  width: 200px;
  height: 200px;
  background-color: red;

  // display: flex; - react-native의 View는 기본이 flex이다.
  align-items: center;
  justify-content: center;
`;

const CustomText = styled.Text`
  font-size: 16px;
   color: white;
   text-align: center;
`;

export { CustomBox, CustomText };
// App.tsx

import { CustomBox, CustomText } from '@components/sample';

const App = () => {
  ...
  
  return (
    ...
      <CustomBox>
        <CustomText>제가 직접 만들었어요</CustomText>
      </CustomBox>
    ...
  )
}

 

정상적으로 실행된다면

개발을 위한 환경을 얼추 다 갖추었다.

 

 

이제 팀원간 코드를 어떻게 작성할지

어떤 스타일 가이드에 따라 작성할지를

세팅하는  ./prettierrc 를 작성할 차례이다.

 

기본파일로 ./prettierrc.js가 생성되어있는데

원하는대로 커스텀해보자

 

// 예시
module.exports = {
  bracketSpacing: false,
  jsxBracketSameLine: true,
  singleQuote: true,
  trailingComma: 'all',
  arrowParens: 'avoid',
  semi: true,
  useTabs: false,
  tabWidth: 4,
  printWidth: 120,
};
728x90
반응형
728x90

지난편에서는

프로젝트를 생성해보았다

 

[ React-Native 가이드 - (1) 프로젝트 생성하기 ]

https://honeystorage.tistory.com/252?category=784116 

 

아쉽게도

생성된 프로젝트는 바로 실행되지는 않는다

android나 ios에 맞는 설정들을 진행해주어야한다.

 

0. 준비물

1) JDK (sdk) - https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html

2) git - https://git-scm.com/book/ko/v2/%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-Git-%EC%84%A4%EC%B9%98

3) Android Studio - https://developer.android.com/studio?gclid=Cj0KCQjwxdSHBhCdARIsAG6zhlUyhNLs6BwGLaUaNyCMdvN1e1ZiIt8RQCTBJLKMdxWDSi4nJZawgQsaArAREALw_wcB&gclsrc=aw.ds

4) Xcode (ios 개발용 - mac os에서만 가능)

5) npm, node

위 다섯가지 항목중 한개라도 설치되지 않은것이 있다면 먼저 설치를 진행해주어야한다.

 

1. Android 프로젝트 세팅

먼저, project/android/ 위치에

local.properties 라는 파일을 만들어줄것이다.

해당 파일에는

(mac os)
sdk.dir = /Users/USER_NAME/Library/Android/sdk

(window)
sdk.dir = C:\\Users\\USER_NAME\\AppData\\Local\\Android\\sdk

둘중에 자신의 os 맞는 코드를 한줄 적어주면 된다.

 

또한, android sdk 와 관련하여 간단한 설정을 해주어야한다.

(mac os 기준)

// .zshrc가 이미 있을 경우
open ~/.zshrc

// .zshrc가 없을경우
touch ~/.zshrc

// 맽 밑줄에 아래와 같은 내용 추가
export ANDROID_SDK=/Users/<USER_NAME>/Library/Android/sdk
export PATH=/Users/<USER_NAME>/Library/Android/sdk/platform-tools:$PATH

(저장 및 닫기)

// 스크립트 반영을 위해 터미널에서 아래의 명령어 실행
source ~/.zshrc

 

그러면 이제 실행을 위한 준비는 모두 되었다

 

 

안드로이드 시뮬레이터를 실행하거나  

pc에 안드로이드 디바이스를 연결한 뒤

npm run android

** 안드로이드 시뮬레이터(에뮬레이터) **

android-studio에서 설치가능하다.

프로젝트를 생성하거나 그런 작업은 할필요없다.

알맞는 android 버전을 가진 시뮬레이터를 생성만하면 된다.

 

** 안드로이드 디바이스 세팅 **

(simulator가 아닌 on-device 테스트 하기 위해서는 아래의 세팅이 필수)

1. 설정 > 휴대전화 정보 > 소프트웨어 정보 (이동)
2. 빌드번호를 5회 ~ 7회 연타
3. 개발자 옵션 활성화
4. 설정 > 개발자 옵션 (이동)
5. 화면켜짐 상태유지 On
6. USB 디버깅 On

 

 

2. Ios 세팅

다음은 애플의 ios 환경세팅이다.

시뮬레이터가 이미 설치되어있다면 다소 작업이 간단하다.

시뮬레이터가 없다면 xcode에서 simulator를 먼저 설치하자

 

설치되어있다면 시뮬레이터를 실행

(ios는 안드로이드와 달리 on-device 개발을 진행하는것이 어렵다.

시뮬레이터로 개발하고 배포전 testFlight을 통해 on-device 테스트를 해야한다.)

 

npm run ios

 

ios는 비교적 간단하게 실행해볼수있다.

 

 

 

다음 편에서는

개발을 진행하기위한 필수 세팅으로 볼수있는

 

에디터 환경 설정 + 절대경로 잡기 + 스타일모듈 붙이기 (styled-components)

 

위 3가지를 진행할것이다.

728x90
반응형
728x90

https://reactnative.dev/docs/typescript

 

Using TypeScript · React Native

TypeScript is a language which extends JavaScript by adding type definitions, much like Flow. While React Native is built in Flow, it supports both TypeScript and Flow by default.

reactnative.dev

 

(위 링크에 상황별 가이드가 자세히 나와있다)

 

 

우리는 react-native + typescript로 새로 프로젝트를 열때를 기준으로 한다.

npx react-native init MyAppName --template react-native-template-typescript

 

위 명령어 만으로 프로젝트 생성은 완료이다.

 

다음편은, 생성된 프로젝트에

플랫폼별로 실행 환경 세팅을 진행할것이다.

728x90
반응형
728x90

회사생활을 하다보니

회사 상황에 따라 개발을 하게되곤 하는대요

 

최근에는 1-2년정도는

react + typescript 환경에서 웹개발을 했었습니다.

 

이번에 다시 react-native를 통해

개발을 할일이 생겨서

과거에 했던것들을 전부 까먹은

지금같은 상황은 반복하지 않기위해

 

가장 많이 쓰이는 환경 세팅 및 기능을

일목요연하게 정리해볼 예정입니다.

 

1. 프로젝트 생성하기

2. 개발환경 세팅하기

3. 개발을 위한 절대경로, 스타일 모듈 및 에디터 설정하기

4. firebase를 통해 push 알림 발송하기

5. Android 배포하기

6. IOS 배포하기

728x90
반응형
728x90

최근 웹 사이트 트렌드를 살펴보면

뭐 별것도아닌 사이트 만드는데도

react, vue, angular 등의 라이브러리 혹은

프레임워크를 사용하는 경향이 있습니다.

 

왜 그런걸까요...

아마도 의존성이 생긴것이 아닌가 싶습니다.

너무 길들여진 나머지

pure javascript와 html, css로는

웹 사이트를 만들지 못하게된

개발자가 많아진것은 아닐까요?

 

각설하고

정말 가벼운 웹사이트를 퍼블리상 할 일이 생겨

만들고 난 뒤에 js, css 파일등의 버전 관리를

어떻게 할까 고민해 보았습니다

 

1. 웹팩을 쓴다?

2. 몇번이나 업데이트한다고.. 직접 수정한다.

3. 스크립트를 통해 관리한다!

 

3번 방식을 채택하였습니다.

프로젝트 규모에 비해 너무 과하지도 않고

직접 하는것에 비해 과거 버전과 충돌할 가능성은

0%로 줄이는 효과가 있기 때문입니다.

 

코드는 간략하게 작성했는데요

프로젝트 구조가

index.html
/css (직접 작성한 코드)
  style.css
  pallete.css
/js (직접 작성한 코드)
  something.css
/common (swipe.js나 jquery같은 손대지 않을 소스코드)
  /css
  /js

이렇게 되어있다고 했을때의

코드로 보시면 되겠습니다

const fs = require("fs");
const html = fs.readFileSync("index.html").toString();

fs.readdir("./css", async (err, filenames) => {
  if (err) {
    console.log("err: ", err);
  } else {
    let newHtml = html;

    const renames = filenames.map((filename) => {
      const newFileName = getNewFilename(filename);
      fs.renameSync("./css/" + filename, "./css/" + newFileName);
      newHtml = newHtml.replace(strReplace(filename), newFileName);
      return newFileName;
    });
    await Promise.all(renames);

    fs.writeFileSync("./index.html", newHtml, "utf8");
  }
});

function strReplace(str) {
  return new RegExp(str, "gi");
}

function getNewFilename(currentName) {
  const dotIndex = currentName.indexOf(".");
  return `${currentName.slice(0, dotIndex)}.${new Date().valueOf()}.css`;
}

/css 폴더 경로의 파일들을 버전관리 하는

node script 파일입니다.

 

/js 폴더의 스크립트 버전도 관리하려면

css파트를 복붙해서 js로만 변경해주면 되겟죠?

 

해당 소스코드는  github에도 올려두었습니다

https://github.com/jaekwangLee/css-versioning

728x90
반응형