라벨이 개발인 게시물 표시

멀티 스레딩 환경에서의 Singleton 사용 및 견고한 Singleton 구현 방법 (Enum Singleton)

이미지
Singleton? Singleton pattern은 특정 인스턴스에 대하여 단일 인스턴스만 만들어질 수 있도록 제한하는 패턴이다. 스레드 풀이나 캐시, 네트워크 모듈 등등에서 같은 객체를 여러개 만드는 것은 불필요한 자원을 할당하며 예기치 않은 오류를 불러올 수 있는데, 이런 상황에서 singleton을 사용하여 하나의 인스턴스만을 생성하여 사용하도록 관리를 해주면 불필요한 메모리 낭비를 줄일 수 있고 사용성 측면에서도 효율적이라 자주 쓰이는 패턴중에 하나이다. 자주 사용하던 방식은 이런 방식이다. 이렇게 되면 생성자가 private이라 무조건  getInstance() 를 통해서만 인스턴스에 접근할 수 있기 때문에  mInstance == null 인 최초 상황에서만 초기화가 되며 그 이후에는 생성된 기존의 인스턴스를 사용하게 된다. 그러나 과연 'Singleton'을 보장할 수 있을까? 일단 기본적으로 Android는 멀티 스레딩 환경이다. 만약 A thread와 B thread에서 동시에 이 인스턴스에 접근하려고할 때 마침 mInstance 가 null이였다면, A thread에서도  mInstance == null 를 타기 때문에  mInstance 를 새로 할당할거고 B thread에서도  mInstance == null 를 타며 새로 할당하게 될 것이다. 그렇게 되면 예상했던 것과는 달리 mInstance 가 2개 이상의 중복 생성이 되며 예상하지 못한 오류를 만들어낼 수도 있다. 일단 해결책으로, Main thread에서만 접근하도록 제어할 수도 있을 것이다. 다른 스레드에서 접근하면 Exception을 날린다던가 하는 식으로 예외처리를 할 수 있지만, 만약 이 singleton 객체가 꼭 멀티 스레딩 환경에서도 사용가능하게끔 해야 한다면?? 그래서 나온 해결책이  getInstance() 를  synchronized ...

ReactNative: 기존 네이티브 프로젝트에 이식하기 (+native 메소드 호출)

이미지
(글 작성한 시점의 최신 ReactNative ver: 0.41) 1. What I want to do 내가 원한건 이런 화면이다. 전체적으로 Android 네이티브 코드로 제작되어있는데, (Toolbar나 DraweLayout 등...)    ① 기본적인 메뉴들은 네이티브 화면이 보여지지만    ② 특정 메뉴는 ReactNative로 코딩된 화면 노출    ③ 어떤 화면에서는 아예 혼합되어있어야 하고     ④ 아예 ReactNative로만 된 activity도 2 dept로 호출할 수 있는 앱    + 그리고 각 ReactNative 화면에서도 자유롭게 네이티브의 메소드들을 호출할 수 있게끔. ▼ gif로 보자면 대충 이런식으로 흐른다 . (연두색은 네이티브 / 하늘색은 ReactNative)   2. What I should do 일단 일반적인 방법으로 AndroidStudio에서 새로운 프로젝트를 생성해준다. 네이티브 코드로 대충 뼈대를 잡아준다. DrawerLayout , SlidingTab 등등.. ReactNative 사용 가능 환경 설정을 해준다. ( Integration With Existing Apps )  package.json ,  index.android.js  만들고 gradle 설정 작업 등을 한다.  ReactNative를 보여줄 부분에  ReactRootView 를 사용하여 dynamically adding 해준다. 대충 이런식으로.. 이 부분은 위에 링크한 공식 문서를 참고해 충분히 응용 가능하다. ReactNative에서 네이티브 내의 메소드들을 호출하기 위해 모듈을 생성한다.  ReactContextBaseJavaModule 를 오버라이딩 받아야하며, getName(...

ReactNative: onHost 생명주기 메소드가 없을 때

이미지
(글 작성한 시점의 최신 ReactNative ver: 0.40) 기존 Android 네이티브 앱에 ReactNative를 이식하기 위해서 ReactNative 공식 doc에서 가이드해주는  Integration With Existing Apps 를 따라하고 있었다. 1. 이슈 내용 하라는대로 제대로 잘 따라한 것 같은데, 일단 첫번째 문제는 doc에서 사용하라는  onHost... 로 시작하는 생명주기 메소드들을 찾을 수가 없었다. (아래 스크린샷의 메소드) 일단 뭐 대충 보니 메소드 이름과 파라미터들은 달랐지만  onPause() ,  onResume() ,  onDestroy()  라는 메소드들이 있어서 '뭐 아직 문서가 업데이트 되지 않았나보다' 하고 대수롭지 않게 넘기고  onPause() ,  onResume() ,  onDestroy()  메소드들을 사용했다. 그러고나서 빌드하니 발생하는 RuntimeException: IllegalAccessError?? 2. 이슈 원인 일단 원인은 프로젝트 루트의  build.gradle  에서 maven  코드 블럭 추가시 잘못된 경로를 추가한 문제였다. doc에 나오는 정확히  $rootDir/../node_modules/react-native/android  이 부분이다. 일반적으로 이 doc 문서를 보면서 차근차근 따라했다면, 프로젝트 루트안에  node_modules  폴더가 만들어졌으므로 node_modules  폴더가 저 경로 안에 위치할리가 없다. 3. 해결방법 해당 url을  $rootDir/node_modules/react-native/android  로 수정 하면 앞에서 언급한  onHostPaus...

ReactNative: 컴파일시 발생하는 에러 해결

이미지
(글 작성한 시점의 최신 ReactNative ver: 0.40) 런타임시 발생하는 에러 해결 은 이쪽으로 ReactNative 프로젝트를 빌드하는 방법은 크게 2가지가 있는데, cmd 창에서  react-native run-android  명령어를 통해서 빌드하거나 AndroidStudio에서 일반적인 방법으로 빌드하는 것이다. (js 파일 업데이트가 아닌 네이티브 코드를 포함한 빌드를 말한다.) 개발을 하다보면 빌드 자체가 안되는 경우가 있는데, 사실 이 경우는 좀 무궁무진해서(...) 다 정리할 수는 없지만 일반적으로 나 혹은 동료 개발자들이 (특히 ReactNative 학습 초기에) 겪은 케이스를 정리중이다. 적어도 ReactNative가 언급한 개발 환경 은 모두 셋팅이 되어 있어야 한다. NodeJs 설치부터 환경변수 설정, SDK, 심지어 에뮬레이터까지 모두 설명이 되어있다. SDK 같은 경우에는 Marshmallow SDK 뿐만 아니라 그래픽 시스템 이미지 SDK 등까지 설치가 필요하므로 주의가 필요하다. Gradle plugin의 최소 요구 버전을 맞춰야 한다. 사실 이 최소 요구버전 코드가 대체 어디 명시가 되어있는지 찾질 못하겠다. ^^ 그러나 확실한건 버전이 특정 버전보다 낮은 경우에 '패키지명.패키지명.MainActivity.java'를 찾지 못하겠다며 (패키지명이 두번씩 경로에 언급된다) 컴파일이 안된다. AndroidStudio를 통해서 업데이트하면 쉽다. AndroidStudio 같은 경우 project를 clean하고 해결되는 경우도 있다.  주로 라이브러리 관련한 이슈가 발생할 때 해결 가능 방법인데, 어쨌든 '갑자기' 안된다 싶을 때는 거의 'clean'으로 해결이 된다. 특히 AndroidStudio 버전이 2.2 이상인가로 올라가면서 만들어진 'InstantRun'을 사용하는 경우 인스턴트런 특유의 버그 때문에 발생할 가능성...

ReactNative: 6.0 Marshmallow 이상 버전에서 퍼미션 에러로 죽는 이슈 해결

이미지
(글 작성한 시점의 최신 ReactNative ver: 0.40) 일단 ReactNative는 아직까지 Marshmallow의 Permission 관련 대응이 되지 않고 있다. 그래서 이 부분에 대한 개발자들의 예외처리가 필요한데, 일단 다른 기능을 사용하지 않아도 기본적으로 DevSettingMode에서부터 '다른 앱 위에 그리기 권한'이 필요하다. (DevSettingMode가 뭔지 모르거나 혹은 설정해준적 없는 경우, default가 '사용'이기 때문에 대부분 사용하고 있는 경우이다.) 일단 6.0 이상의 단말의 Permission 획득에 관한 내용은 Android Developer 문서 참고 Permission 예외처리 로직이 없는 경우 권한 획득에 실패하여 당연히 앱이 죽을 것이다. 특히나 이 '다른 앱 위에 그리기 권한' 같은 경우에는 Dangerous Permission이 아니기 때문에, 앱 설정에서 해줄 수 있는 부분이 아니여서 설정 화면을 직접 띄워줘야한다. (같은 맥락으로 onActivityResult() 에서의 Granted 체크가 어렵다.) 로직은 간단하다 : showReactView() 메소드에서 ReactNative 화면을 호출하든 ReactRootView를 통해 view를 이식하든 자유롭게 코딩하면 된다. 이렇게 하면 6.0 이상의 버전에서는 해당 요청시 '다른 앱 위에 그리기 권한'을 허용하는 화면이 뜬다.

ReactNative: 런타임시 발생하는 에러 해결

이미지
(글 작성한 시점의 최신 ReactNative ver: 0.40) 컴파일시 발생하는 에러 해결 은 이쪽으로 처음 ReactNative를 공부하다보면 단순히 앱이 예기치 않은 오류로 죽는 것 뿐만 아니라, 런타임 시점에 빨간 에러 화면을 무진장 볼 수 있는데: 바로 이런 경우에 대한 해결방법이다. (물론 스크립트 코딩을 잘못해서 발생하는 디버그 에러는 제외다.) 'Could not connect to development server' 이슈를 포함해 처음부터 안되거나 알 수 없는 이유인경우 아래 해결방법들로 해결 될 수도 있다. 아래 해결 방법들은 ReactNative 학습 초기 단계의 해결방법들이 대다수이다. 앱이 예기치 않은 오류로 실행 중 죽어버리는 경우 UnsatisfiedLinkError(AsyncTask #1) 이슈인 경우 : 블로그 본문 링크 참고 DevSettingMode를 사용중이며 단말의 버전이 6.0 이상인 경우 단말이 필수로 overlay 퍼미션을 허용해줘야한다.  : 블로그 본문 링크 참고 프로젝트 루트의 build.gradle에 maven 경로 추가시 올바르게 추가해줘야 한다. onHost... 로 시작하는 생명주기 메소드를 찾을 수 없는 경우 :  블로그 본문 링크 참고 혹은 build.gradle 파일에  repositories  가 2개여서 잘못 추가할 수도 있는데,  allprojects  의  repositories  에 추가해줘야 한다. 상단에 첨부한 빨간 에러 화면을 보이는 경우 Android sdk의 환경변수가 필히 'ANDROID_HOME'으로 잡혀야 한다.  사실 이건 굉장히 이상하며 불친절한 부분이다.  ReactNative Getting Started 가이드 에서 설명하는 AndroidStudio나 Android SDK 등이 이미 설치 ...

ReactNative: 8081 port 서버 없이 빌드하기 (react-native bundle)

이미지
(글 작성한 시점의 최신 ReactNative ver: 0.40) 일반적으로 ReactNative를 사용하며 개발 시에는  react-native run-android  명령어로 빌드하며 8081 포트를 띄우거나  react-native start  명령어로 바로 포트를 띄워서, script 파일을 인터넷 연결을 통해 가져오게 되어있다. 5.0 버전 이상에서는  adb reverse tcp:8081 tcp:8081  명령어로 붙고 5.0 미만 버전에서는 pc와 동일한 wifi에 붙은 뒤 ip 설정을 해주는 방식으로 서버에 붙는다. - 해당 내용에 대한 ReactNative doc의 설명 On Android 5.0+ devices connected via USB, you can use the adb command line tool to setup port forwarding from the device to your computer: adb reverse tcp:8081 tcp:8081 Alternatively, select "Dev Settings" from the Developer Menu, then update the "Debug server host for device" setting to match the IP address of your computer. 그러나 릴리즈 할 때는 이 방법을 사용할 수 없기 때문에 script 파일을 ①외부 server에 업로드하여 접근하거나 ②프로젝트 내부에 ReactNative bundle을 포함시켜야 한다. 이 포스팅에서는 ②프로젝트 내부에 ReactNative bundle을 포함시키는 방법으로 빌드할 것이다. 일단 curl 명령어를 사용해야 하기 때문에 cURL이 설치가 되어있어야 한다. ( 다운로드 페이지 ) 일반적으로는 다운로드 페이지에서  curl executable > OS 선택 > Gen...

ReactNative: 'UnsatisfiedLinkError: could find DSO to load' 해결하기

이미지
(글 작성한 시점의 최신 ReactNative ver: 0.40) 기존에 존재하는 Android 프로젝트에 ReactNative를 화면 단위나 view 단위로 이식할 수 있는지를 테스트하기 위해서 ReactNative doc 을 참고하며 진행중이었다. 일단 기존 프로젝트에 잘못 이식하다가 프로젝트 자체가 풍비박산 될 수 있기 때문에, 그냥 AndroidStudio에서 'New Project'하여 버튼 하나를 넣어서 그 버튼을 누르면 ReactNative 화면을 띄우도록 제작하려고 했다. 코드는 대충 이렇다. 그 외 코드는 그냥 doc에 언급된대로 생명주기 메소드별 호출 및 back key 등의 이벤트 처리만 했다. 그런데..! 해당 Activity를 띄우려하니 RuntimeException이 발생했다. 자세히 보니 해당 로그 아래에 Caused by로 UnsatisfiedLinkError가 같이 찍혀있었다. (UnsatisfiedLinkError 로그는 캡쳐를 안해놔서 같은 현상의 다른 개발자 로그를 대신 첨부하였다.) ???? DSO를 찾을 수 없다고??? 열심히 검색검색하니 대충 다른 개발자들은 써드파티 라이브러리를 사용하는 기존 프로젝트에 ReactNative를 이식하는 과정에서 해당 에러가 발생하는 것 같았다. 근데 난 그냥 Hello World에 버튼 하나 있는 프로젝트인데?? 외부 라이브러리 상속받은거 전혀 없는데????? gradle을 자세히보니 안드로이드가 기본적으로 넣어주는 Appcompat 서포트 라이브러리가 있었다. 물론 혹시 몰라 그 부분도 지워봤지만 에러는 똑같았다.. ^^.. ㅠㅠ 1. 이슈 원인 일단 결론을 먼저 말하자면, ReactNative가 32비트 라이브러리만 지원하고 64비트 라이브러리는 지원하지 않기 때문에 64비트인 ARM64 장치에서는 이런 이슈가 발생 한다. 이 이슈가 특정 단말에서 재현되는 현상도 그런 이유에서이다. 해당 이슈는 ReactNativ...

RxAndroid: 비동기 및 이벤트 기반 프로그래밍을 위한 라이브러리

이미지
RxAndroid: 비동기 및 이벤트 기반 프로그래밍을 위한 라이브러리 from Stella Kim 2017년 1월 24일, 사내에서 진행한 RxAndroid 주제의 세미나 발표 자료이다. ReactiveX를 알게 되고 RxJava를 공부하며, 다음으로 착수하게 될 프로젝트에 RxJava를 도입하기로 결심을 했다. 같이 프로젝트 개발을 진행할 개발자들과 PL, PM에게는 간략한 기술검토 내용을 설명하여 설득을 했지만, 단순히 특정 프로젝트에 그치는 것이 아니라 향후에도 Rx 개념을 지속적으로 사용하고 싶었다. 또한 내가 투입되지 않는 프로젝트에서도 Rx를 사용하기를 원했다. 그렇게 되기 위해서는 최소 사내 개발자들은 Rx의 개념을 알고 사용법을 알아야하는데, 사실 누군가가(심지어 말단사원이) 제안했다고해서 가뜩이나 바쁜 연구원들이 바로 Rx를 공부하며 실무에 적용해볼리가 만무했다. 사내에는 2-3명 정도가 Rx에 대해 관심을 가지고 공부했을 뿐, 그 외에는 모두 Rx에 대해 잠시 들어만 봤거나 아예 모르는 연구원들이 대다수였다. 어떻게 해야 타 연구원들이 Rx에 대해 관심을 가지고 공부를 할 수 있을까 생각하다가, 최소한 'Rx가 뭔지 들어는 개발자'로 만들기 위해 아예 전사 세미나를 주최하기로 했다. (PL이 먼저 세미나 주최를 제안했던 것이 가장 큰 계기가 되었다.) 그리고 나름 성공적으로 마쳤다. '생산성이 좋습니다.' 한마디를 이야기하기 위해서는, 왜 좋은지, 어떻게 좋은지, 기존의 방법보다 어떤 것이 나으며 어떤 것이 부족한지에 대한 설명과 근거 자료들이 준비가 되어있어야 했고, 예상치 못한 질문에도 최대한 사실에 근거하며 잘 대답할 수 있게끔 전반적인 이해가 완전히 되어야 했기 때문에 공부와 세미나 자료 준비를 거의 동시에 진행했다. 공부도 공부 나름인게 Rx를 설명하기 위해서는 멀티코어 프로세서가 등장한 환경부터 동시성 프로그래밍, 반응성 프로그래밍 등...

Android: App 프로파일링과 최적화

이미지
안드로이드는 방대하며 하나의 이슈에 대해서도 수많은 솔루션들이 있다. Stackoverflow 에서는 많은 추천을 받은 답변이 Best solution으로 인정되는 경향이 있는데, 특히 다른 개발자들의 긍정적인 코멘트들이 달린 답변의 경우에는 더 그렇다. 그러나 사실 백그라운드에서 비동기로 실행되는 것과 관련되어 보고된 이슈들을 보면, 많은 추천을 받은 답변들을 좋은 답변이라고 하기에는 어렵다. 그 중엔 종종 앱의 성능과 안정성에 미치는 영향을 완전히 무시한 솔루션들이 있는데, 경우에 따라서 이 솔루션들은 단지 약간의 성능저하 정도만 발생시킬 수 있겠지만, 어떤 경우에는 앱이 충돌할 가능성도 있다. Splash 화면의 예 참고: 다음은 단지 최적화되지 않은 코드의 예시일 뿐이며, splash 화면을 구현하는데에 있어서 추천하는 방법이 절대 아니다. splash 화면 구현을 위한 적절한 솔루션은 Ian Lake의 Pro-tip 게시물을 참고하길 바란다. Splash 화면을 만든다고 가정해보자. 어떻게 만들지에 대한 아이디어는 있지만 그것이 과연 최적의 방법인지는 확신할 수 없다. 일단 최선의 방법을 찾기 위해 구글링을 해볼것이고, Stackoverflow나 블로그들에서 여러 솔루션들을 얻게 될 것이다. 예를 들자면 이런 답변 말이다: 이러한 코드는 구글에서 바로 검색되는 일반적인 솔루션이다. 이 코드는 Handler를 인스턴스화하고 Runnable로 postDelayed를 호출하여 splash 화면을 구현한 Activity다. SPLASH_DISPLAY_LENGTH만큼의 딜레이가 지연되고 나면 Runnable이 실행될 것이고, main Activity가 실행되며 splash가 종료될 것이다. 나는 이 코드를 복사하여 내 코드에 붙여넣은 뒤 원하는대로 잘 작동하는 것을 확인하며 만족해할 것이다. 여기까지는 아주 좋다. 코드 분석 이 코드에는 몇가지 결함이 있다: 1. Splash 화면이 보여지는 동안 ...