728x90

 

위와 같은 UI는 사용자 경험에 도움이 되기 때문에

많은 상용앱에서 볼수있다.

 

이는 아이폰에서도 x계열에서 특별한 처리가 필요한데

하단에 여백이 발생하기 때문이다.

 

어떻게 처리할수있는지 살펴보자.

개인적으로는 꽤나 시행착오를 걸쳤다.

 

키워드는 3가지이다.

1. KeyboardAvoidingView

2. SafeAreaView

3. react-native-iphone-x-helper

 

3번 라이브러리는 설치를 해주도록하자.

npm i react-native-iphone-x-helper --save

 

이제 구현을 할건데

KeyboardAvoidingView는 항상

제일 껍데기에 둔다고 생각하면 편하다.

 

Docs에서 보면

안드로이드와 ios에 동작 방식을 다루게 주기도 하는데

여기서는 동일하게 처리하도록 한다.

 

import { KeyboardAvoidingView, SafeAreaView, View, StatusBar, Platform } from 'react-native';
import { isIhponeX } from 'react-native-iphone-x-helper';


const statusBarHeight = StatusBar.currentHeight;
const isIphone = isIphoneX() && Platform.OS === 'ios';
const paddingBottom = isIphone ? 34 : 0;
const offset = isIphone ? -34 : statusBarHeight;

...

return (
<KeyboardAvoidingView
  behavior='height'
  style={{ flex: 1, backgroundColor: '#00cebe', paddingBottom }}
  keyboardVerticalOffset={}
>
  <SafeAreaView style={{ backgroundColor: '#000000' }}>
    <View style={{ flex: 1 }}>
      ...
      <CustomInput 
        keyboardType='number-pad'
        placeholder='인증 코드를 입력해주세요'
      />
    </View>
    <CustomButton>다음</CustomButton>
  </SafeAreaView>
</KeyboardAvoidingView>
)

 

input이나 button은 만들기 나름인데

버튼은 아래와 같이 만들었다.

const BottomButtonContainer = styled.TouchableWithoutFeedback`
  width: 100%;
  height: 56px;
`;

const BottomButtonWrapper = styled.View`
  width: 100%;
  height: 56px;

  align-items: center;
  justify-content: center;
  background-color: #00cebe;
`;

const BottomButtonText = styled.Text`
  color: white;
  text-align: center;
  font-size: 16px;
  line-height: 30px;
  font-weight: bold;
  letter-spacing: -0.48px;
`;

export const BottomButton = ({ name, onPress }: { name: string; onPress: Function | any; }) => (
  <BottomButtonContainer onPress={onPress}>
    <BottomButtonWrapper>
      <BottomButtonText>{name}</BottomButtonText>
    </BottomButtonWrapper>
  </BottomButtonContainer>
);

 

 

 

**************************

**** 21. 11. 30 업데이트 ****

**************************

 

문제상황

behavior를 height로 잡았더니

TextInput의 focus나 스크롤 위치 등

상황이 변화함에따라 버튼이

키보드 아래로 숨어버리거나

반만 보이거나 하는 등의 문제상황이 발견되었다.

 

따라서,

아래와 같이 수정하였으며

UX적인 측면에서 훌륭하게 동작함을 확인하였다.

// app.ts

import { StatusBar } from 'react-native';

... 생략
return (
  <>
    <StatusBar 
      barStyle='light-content'
      backgroundColor: 'black'
    />
  </>
)
// auth.code.ts

import { KeyboardAvoidingView, SafeAreaView, View, StatusBar, Platform } from 'react-native';
import { isIhponeX, getStatusBarHeight } from 'react-native-iphone-x-helper';


...(생략)
const _inputValiation = ():boolean = () => {
  let valid = true;
  ...(생략)
 
  return valid;
}

const isIphonexStyle = isIhponeX() && Platform.OS === 'ios';
const statusBarHeight = isIphonexStyle ? getStatusBarHeight(true) +3 : StatusBar.currentHeight;

return (
<KeyboardAvoidingView
  behavior={Platform.OS === 'ios' ? 'padding' : undefined}
  style={{ 
    flex: 1, 
    backgroundColor: _inputValiation() ? 'red' : 'gray', 
    marginTop: isIphonexStyle ? statusBarHeight : 0
  }}
>
  <SafeAreaView style={isIphonexStyle ? { backgroundColor: 'transparent' } : {}}>
    <ScrollView style={{ flex: 1, backgroundColor: 'black' }} keyboardShouldPersistTaps='never'>
      ...(생략)
      
      <CustomInput 
        keyboardType='number-pad'
        placeholder='인증 코드를 입력해주세요'
      />
    </ScrollView>
    <CustomButton style={{ backgroundColor: _inputValiation() ? 'red' : 'gray' }}>다음</CustomButton>
  </SafeAreaView>
</KeyboardAvoidingView>
)

 

핵심은 behavior 부분의 변화이다.

안드로이드에서는 AndroidManifest.xml에

...

<activity 
  ...
  android:windowSoftInputMode="adjustResize" <-- 여기
>
...
</activity>

위의 한줄만 있으면 keyboardavoidingview에서

따로 처리를 안해도 적절하게 동작한다.

 

따라서 android에서는 undefined를.

ios에서는 권장사양이자 적절하게 동작하는

padding으로 설정을 해주었다.

 

나머지는 약간의 트릭을 통해

버튼이나 배경색 등을 컨트롤하는 부분이다.

728x90
반응형
728x90
npm i --save react-native-fast-image
cd ios && pod install && cd ..

Image 라이브러리를 설치한다.

 

ios는 그냥 해도 잘 렌더링되지만

android는 간단한 세팅을 해줘야한다.

/android/app/bulild.gradle 

...
  implementation 'com.facebook.fresco:fresco:2.0.0'
  implementation 'com.facebook.fresco:animated-gif:2.5.0'
...

 

앱 실행(재실행)

import FastImage from 'react-native-fast-image';

...
<FastImage
  style={{ width: 100, height: 100 }}
  source={{ uri: 'https://mygifimage.com/test.gif' }}
/>
...
728x90
반응형
728x90

앱 배포를 눈앞에 두고 디테일을 잡는중

앱 실행되고 처음 새 화면으로 이동할때

순간적으로 하얗게 화면이 깜빡이는것이 목격됐다.

 

react의 Painting작업중 이슈가 생긴듯하다.

어떻게 처리할까 고민하다가

약간의 꼼수로 하얗게 깜빡이는 이슈를

잠재우기로했다.

 

// app.js

<NavigationContainer theme={{ colors: { background: '#000000' } }}>
 ...
</NavigationContainer>

Navigation의 기본 배경색을 전환될 화면의 배경색과 통일해 줌으로써

하얗게 되는 이슈를 해결했다. (?)

728x90
반응형
728x90

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

https://honeystorage.tistory.com/306

 

 

@react-native-firebase/app

@react-native-firebase/messaging

 

두개 모듈을 이용해 푸시알림을 구현했다.

ios & android 모두 잘 동작하는 듯 했으나 아니었다

 

Docs를 보다보면 푸시알림 수신관련해

상태를 구분해둔 표가있다.

 

그렇다는건

우리도 상태별로 테스트를

진행해서 문제가 없는지 확인해야된다는 것이다.

 

나는 이 중

Android 디바이스의 앱이 Quit Stats 상태일때

정상적으로 알림을 띄우지 못했다.

 

1박2일간의 사투끝에 어이없는 실수가 있었음을 깨달았다.

문제 해결 과정이 핵심이다.

 

1. Google에 문제 상황 검색하기

- 검색어 예시 : react-native-firebase push notification not working on android in quit stats

- 검색어 예시 : - rnfb not working in quit stats

 

2. git에 예시코드 찾아보기

- 검색어 예시 : react-native-firebase v6

- 검색어 예시 : rn firebase

 

3. 잠시 멈추어, 정확히 어느상황에 안되는지 확인

- 생각을 정리해보니 JS단까지 알림이 전달되않음.

- Java에는 알림이 전달이 되는지 확인해볼 필요가 있음

 

4. 디바이스를 연결하고 Android Studio로 디버깅

- 디바이스 연결

- Android Studio 실행

- react-native run-android

- Android Studio 하단의 LogCat [info]를 모니터링

- 서버에서 알림 발송

- Java에는 요청이 정확히 오지만 에러가 났음을 발견

- 에러메시지 >>> SoLoader.init ....

- MainApplication.java에 해당 코드를 주석처리 해뒀던것이 기억남

- 주석해제

 

5. 푸시알림 전송 및 이슈해결 확인

 

 

결론

  @Override
  public void onCreate() {
    super.onCreate();
    SoLoader.init(this, /* native exopackage */ false); // <-- 주석해제했음
  }

 

 

>>> RNFirebase 왜 아이콘이 안나와?

>>> RNFirebase 왜 진동이 안울려?

 

 

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

https://honeystorage.tistory.com/306

728x90
반응형
728x90

IOS를 테스트할때

Firebase의 App Distribution을 이용하거나 TestFlight를 이용하듯이

 

Play Console에도 내부테스트 기능이 존재한다.

그 옛날 직접 apk파일을 공유할때와 비교하면

정말 편리해졌다.

 

방법도 간단했다.

 

1. 빌드한다.

cd android
./gradlew bundleRelease

2. play console에 앱을 등록한다.

3. 테스트 > 내부테스트로 이동한다.

4. 빌드해서 얻은 파일을 등록한다.

(빌드된 파일 위치: app/build/outputs/bundle/release/app-release.aab)

5. 기타 빌드 관련 사항을 기록하고 내부테스트 시작

6. 테스터를 지정한다.

7. 링크를 복사해 테스터에게 공유한다.

8. 테스터는 플레이스토어앱을 연다.

9. 프로필 > 설정 > 정보 > 스토어버전 연타!

(개발자 설정 완료 뜨면)

프로필 > 설정 > 일반 > 내부앱  공유 On

10. 이메일에 테스터 지정에 동의한다.

11. 공유받은 링크를 클릭해 PlayStore로 이동

12. 앱을 설치한다.

728x90
반응형
728x90

attribute중에 contenteditable을 사용하면

div를 input, textarea처럼 쓸수있다.

 

입력창을 커스텀해서쓸때 종종 쓰이곤한다.

방법은 간단하다.

 

html + css / html + scss 버전 두가지를 살펴보자

 

1. html + css

<div contenteditable placeholder='메시지를 입력해주세요'></div>
[placeholder]:empty:before {
  display: block;
  content: attr(placeholder);
  color: #a6a6a6;
}

 

 

2. html + scss

<div contenteditable placeholder='메시지를 입력해주세요'></div>
&[placeholder]:empty:before {
  display: block;
  content: attrs(placeholder);
  color: #a6a6a6;
}

 

 

728x90
반응형
728x90
SDK location not found. Define location with sdk.dir in the local.properties

위와 같은 에러가 발생한다면

간단한 설정으로 이슈 해결이 가능하다.

 

android/local.properties 파일 추가

sdk.dir=/Users/user/Library/Android/sdk

작성

 

(혹은, 윈도우를 사용하거나 개별 경로를 설정하여 sdk의 경로가 다르다면 맞춰서 잡아주자)

728x90
반응형
728x90

https://reactnative.dev/docs/0.63/linking

위의 Docs를 기반으로 작성하였습니다.

 

알림톡, 링크 등을 이용해 앱을 열고자 할때

필요한 기능이 바로

Deep linking입니다.

 

마케팅이나 사용자 편의성 측면에서

굉장히 유용한 기능입니다.

설정도 간편하니 알아보도록 하겠습니다.

 

1. IOS 설정

// IOS 설정, Application.m


// top
#import <React/RCTLinkingManager.h>


// above @end
- (BOOL)application:(UIApplication *)application
   openURL:(NSURL *)url
   options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
  return [RCTLinkingManager application:application openURL:url options:options];
}

 

그리고 xcod > targets > app > info > url types

+를 눌러

Identifier, URL Schemes를 고유명칭으로 잡아줍니다.

예를들어, hssteve

 

2. Android 설정

// AndroidMenifest.xml

<intent-filter>
  <action android:name="android.intent.action.MAIN" />
  <category android:name="android.intent.category.LAUNCHER" />
  <action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>
</intent-filter>
<intent-filter> <-- 이 블럭 추가됨
  <action android:name="android.intent.action.VIEW" />
  <category android:name="android.intent.category.DEFAULT" />
  <category android:name="android.intent.category.BROWSABLE" />
  <data android:scheme="hssteve" />
</intent-filter>

 

3. App.jsx (App.tsx) 설정

Linking.addEventListener('url', schemeListener)

const schemeListener = ({ url }) => {
  console.log('from: ', url);
}

 

테스트를 해볼까요?

// android 테스트
npx uri-scheme open hssteve://some/thing --android

// ios 테스트
npx uri-scheme open hssteve://some/thing --ios

 

console이 잘 찍히나요?

페이지 이동을 위해 쓴다면

수신하는 url 맞게

redirect해주면 될것같습니다.

 

어떻게 쓸지는 자유!

유용한 기능 입니다.

728x90
반응형
728x90

완전탐색에 해당하는 문제이다.

function solution(answers) {
    var answer = [];
    const score = [
        { name: 1, count : 0 },
        { name: 2, count : 0 },
        { name: 3, count : 0 },
    ];
    const user1 = [1, 2, 3, 4, 5];
    const user2 = [2, 1, 2, 3, 2, 4, 2, 5];
    const user3 = [3, 3, 1, 1, 2, 2, 4, 4, 5, 5];
    
    answers.forEach((answer, index) => {
        const value = index + 1;
        if (value < 5) {
            if (answer === user1[index]) score[0].count += 1;
        } else {
            if (answer === user1[index % 5]) score[0].count += 1;
        }
        
        if (value < 8) {
            if (answer === user2[index]) score[1].count += 1;
        } else {
            if (answer === user2[index % 8]) score[1].count += 1;
        }
        
        if (value < 10) {
            if (answer === user3[index]) score[2].count += 1;
        } else {
            if (answer === user3[index % 10]) score[2].count += 1;
        }
    });
    
    score.sort((a,b) => a.count > b.count ? -1 : 1)
    answer = score.filter((val) => val.count === score[0].count).map((val) => val.name);
    
    return answer;
}

위와 같이 문제를 해결해보았다.

1. score를 쉽게 정렬하기위해 배열을 이용했다.

2. 수포자들이 찍기 방식이 일정한 패턴을 가지고 있음을 활용한다. (사실, 랜덤이라면 답을 구하는것 자체가 불가능하지않을까?)

3. 각자의 패턴의 길이만큼은 단순비교를

4. 패턴의 길이 이상에서는 나머지 값을 이용해서 답안과 비교한다.

5. forEach를 이용해 답안의 수 만큼 반복한다.

6. 마지막으로 배열을 정렬하고, 가장 큰 수와 같은 수만큼 답안을 획득한 수포자들만 필터한다.

7. map으로 수포자들의 이름(1,2,3)만 뽑아낸다.

 

간단하지만,

패턴을 이용한다. 나머지를 이용한다. 와 같은 발상을 못한다면

맞출수없는 문제같다.

 

마지막으로, 완전탐색의 개념을 살짝 짚어본다.

완전 탐색이란? 가능한 모든 경우의 수를 탐색하고, 확인하는 방법입니다.

728x90
반응형
728x90

정부 정책에 따라 대출제도가

하루가 다르게 변해가고있다.

 

최근 발표로 인해 6억원 이하 아파트 매매시

보금자리론 이용은

대출규제에서 제외되었다.

 

실거주자들을 위한

아주 올바른 선택이라고 생각한다.

 

근데 이미 집값이 다 상승한 상태에서

이 정책을 펼쳐버리니

상대적으로 저렴했던 아파트에도 수요가 몰려

모든 매물이 가격이 올라가버리는

부작용이 발생하고있다.

 

==============================

 

각설하고

진짜 진짜 흙수저 서민이

신혼집을 마련하기 방법을 알아보자.

 

우리가 구매할 아파트 "실구매가"는 "3억원"이다

신혼에 해당하는 흙수저 서민은 이 이상의

아파트는 쳐다보기 힘든것으로 생각된다. (내 얘기)

 

전략은 이렇다

총 액: 3.1억 (집값 3억 + 중개비 및 등기비용 (0.1억))

보금자리론(주택담보대출) : 2.1억 (40년 만기, 원리금균등, 실수요자 기준 70% 대출가능)

부부합산 현금 : 5천

1,2금융권 신용대출/퇴직금 땡겨쓰기/부모,지인 찬스: 4천

 

알뜰하고 착실하게 모아왔다면 현금이 더 있을테지만

흙수저 인생에 착실하게 돈모으기는

솔직히 어렵다(어려웠다..ㅠㅠ)

 

집만 살게아니라 가전에 결혼식 비용까지 나가야 하기 때문에

집에 모든돈을 쏟아 부을수는 없는게 또 신혼부부의 현실.

 

2금융권 외의 고금리 대출은 부디 제발 하지말기를.

그럴거면 적정 수준의전월세나 정부에서 운영하는 임대주택에 거주하며

돈을 더 모으는게 상책으로 보인다.

 

위와 같은 전략으로 대출을 했을때

월에 집에 지출되는 금액은 얼마일까?

https://www.hf.go.kr/hf/sub01/sub02_02_01.do

 

위에서 계산해본결과

보금자리론 2.1억원리금 : 약 80만원 (원금 21만 / 이자 59만)

신용대출 4천 원리금 : 약 53만원 (6%대 대출, 10년 납기, 원금 33만 / 이자 20만)

 

따라서, 3억원 아파트를 마련하고나면

우리는 원리금으로 월에 133만원을 고정적으로 지출하는 셈이다.

세부적으로 보면 원금에 해당하는 54만원은 저축으로 볼수있다.

버려지는 지출이 월에 79만원이다. (대략, 80만원)

 

그렇다면 우리가 매매하는 아파트가 월에 80만원, 혹은 연간 960만원 정도의

상승여력이 있는 아파트라면 구매할 가치가 있다고 볼수있겠다.

 

그런데, 우리는 내집마련이 하고싶으니

좀더 행복회로를 돌려보자.

월에 80만원이 버려지는 지출이라고 했다.

 

그런데 신혼부부 둘이 월세집에 살려면

월세가 적어도 5-60은 될것이다.

 

그렇다면 우리는 월세대비

2-30만원의 추가지출을 하고

훨씬 쾌적한 아파트에 사는셈이라고 볼수있다.

 

어떤가?

이렇게 보면 제법 괜찮지 아니한가?

(나는 맘에든다)

 

투자는 개인의 몫

 

아파트 가격 변동 시세를 확인하고

정부 정책이나 부동산 방향성에 대해서 

생각을 해보고 투자 혹은 내집마련을 하면 될것같다.

728x90
반응형