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
function solution(genres, plays) {
    var answer = [];
    let genreArr = [];
    
    genres.forEach((gen, idx) => {
        const currentCnt = plays[idx];
        const hasIdx = genreArr.findIndex(g => g.name === gen);
        if (hasIdx >= 0) {
            genreArr[hasIdx].total += currentCnt;
            genreArr[hasIdx].cntList.push({ order: idx, cnt: currentCnt });
        } else {
            genreArr.push({
                name: gen,
                total: currentCnt,
                cntList: [{ order: idx, cnt: currentCnt }]
            });  
        };
    });
        
    genreArr.sort((a,b) => a.total > b.total ? -1 : 1);
    genreArr.forEach((gen) => {
        gen.cntList.sort((a,b) => a.cnt > b.cnt ? -1 : a.cnt === b.cnt ? 1 : a.cnt < b.cnt ? 1 : -1);
        gen.cntList.slice(0, 2).forEach(({ order }) => answer.push(order));
    });
    
    return answer;
}

1. 장르를  순회하며 새 배열에 필요한 객체 형태로 push해준다.

2. 우리가 알아야 할 값은, 장르별 총합과 index별 플레이 횟수이다.

3. 따라서, name, total, list<{order, cnt}> 구조로 객체를 정의해주었다.

4. 장르 total에 따라서 정렬

5. 장르 내 재생횟수 높은순 -> 고유번호 낮은순으로 정렬

6. 장르별로 두개씩 모아서 앨범을 출시한다고 했으므로, slice(0, 2)하여 출력

728x90
반응형
728x90

경우의 수와 관련된

수학문제

 

function solution(clothes) {
  let answer = 1;
  let obj = {};
  
  for(var i=0; i < clothes.length; i++) { 
    if(obj[clothes[i][1]] >= 1) { 
      obj[clothes[i][1]] += 1;
    } else { 
      obj[clothes[i][1]] = 1;
    } 
  }
    
  for (let key in obj) { 
    answer *= (obj[key]+1);
  }

  return answer - 1;
}

1. 객체에 해당 옷의 종류가 없으면 1

2. 객체에 해당 옷의 종류가 있으면 기존값 + 1

3. 경우의 수 계산을 위해 종류별로 곱해나가는데 (곱의 법칙), 해당 종류의 옷을 안입는 경우의 수도 존재하니 +1하여 곱한다.

4. 3번에 옥의티로 저렇게하면 모든 옷을 안입은 경우의 수가 발생하게 되는데 문제에서 아무것도 안입는 경우는 없다고 했으므로 마지막에 -1을 해준다.

 

 

* 한걸음 더 나아가 생각해보기

만약에, 문제에 모든 종류의 옷을 반드시 한가지씩 입어야한다는 조건이 붙는다면?

function solution(clothes) {
  let answer = 1;
  let obj = {};
  
  for(var i=0; i < clothes.length; i++) { 
    if(obj[clothes[i][1]] >= 1) { 
      obj[clothes[i][1]] += 1;
    } else { 
      obj[clothes[i][1]] = 1;
    } 
  }
    
  for (let key in obj) { 
    answer *= obj[key];
  }

  return answer;
}

1. 해당 종류의 옷을 안입는 경우를 제외

2. 위의 코드에서 모든 종류의 옷을 안입는 경우는 발생하지 않으므로 마지막에 -1도 제외

728x90
반응형
728x90

* 떠올려본 방법

1. completion을 포문으로 돌린다 + participant에서 해당 index를 찾아 + splice(index, 1) [시간초과]

2. 2중포문으로 비교해나간다. [시간초과]

3. sort를 이용해 각 배열을 정렬 + 순서대로 비교해가며 다를때 리턴 [성공]

 

function solution(participant, completion) {
  participant.sort();
  completion.sort();
    
  for (let i=0; i<participant.length; i++) {
    if (participant[i] !== completion[i]) {
      return participant[i];
    }
  }
}

 

728x90
반응형
728x90

react-native에서의 image 다루기는

웹에서 css를 통해 다루는것보다

조금 더 까다로운것같다.

 

이유는 auto라는 사기적인 옵션이

RN에는 존재하지않기 때문인것같다.

 

* 목표: 가로는 화면비율, 세로는 이미지 크기에 맞게 떨어지게 해보자

// Web이었다면?
width: 100%;
height: auto;
object-fit: contain;

 

웹에서는 이렇게나 간단한걸

우리는 약간은 길고 고생스럽게 해야한다.

import React, { useState } from 'react';
import { Image, Dimension } from 'react-native';

function ImageTestModule({ imagePath }) {
  const [height, setHeight] = useState(0);
  const { width } = Dimension.get('window');
  Image.getSize(imagePath, (w, h) => {
    setHeight(h * (width / w));
  });
  
  return (
    <Image
      style={{ width: '100%' }}
      source={{ uri: imagePath, height }}
      resizeMode='cover'
    />
  );
}

 

다소, 복잡하고 길어보이지만

완벽하게 원하던 바를 달성할수있다.

728x90
반응형
728x90

제작중인 앱에서 파일을

다운로드/업로드 해야한다.

 

주요 라이브러리중

rn-fetch-blob를 선택하였는데,

써보기도전에 cycle warning이 발생했다.

 

구글링 결과

node_modules > rn-fetch-blob > polyfill > Blob.js, Fetch.js, XmlHttpRequest.js

3개 파일을 아래와같이 일부 수정해줌으로써

경고를 지울수있었다.

 

// import RNFetchBlob from '../index.js'
import { NativeModules } from 'react-native';

...

const RNFetchBlob = NativeModules.RNFetchBlob;
const log = new Log('XMLHttpRequest')

 

다만, 이렇게했을때는

재설치, 업데이트 등을 했을떄 다시 해당 문제가

발생할수 있어보인다.

 

필요하다면 라이브러리를 포크해

손봐서 써야될지도

728x90
반응형
728x90

웹 혹은 앱을 개발하다보면

Select (Picker)는 거의 꼭 들어간다

 

RN에서는

https://openbase.com/categories/js/best-react-native-select-libraries

위에서 보면 알수있듯이

@react-native-picker/picker가

절대적으로 많이쓰인다.

 

그런데 현재 기준 최신버전인 2.1.0버전 설치과정에서

오류가 발생하고 해결했기에

정리해둔다.

 

npm 설치는 정상적으로 완료된다.

npm i react-native-picker-select

다음 작업도 문제 없어 보이는듯 하다

cd ios && pod install

 

그런데

react-native run-ios

막상 실행해보면 엄청난 빌드 오류들이 나타난다.

이거다 싶은 힌트도 없다

 

 

과거의 경험들로 비추어 봤을때

이런 경우에는 모듈간, 버전간 충돌인 확률이 높다.

 

 

아래와 같이 처리했다.

(제거) rm -rf node_modules
(제거) cd ios && rm -rf Pods && cd ..
(설치) npm i
(업데이트/설치) cd ios && pod update && cd ..
(실행) react-native run-ios

 

위와 같은 방법으로

나는 깔끔하게 문제를 해결했다.

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
반응형