반응형

 

플러터에서는 PODO(Plain Old Dart Object)라고 부르는 다트 객체를 모델을 관리하기 위해 사용한다.

흔히 자바에서 DTO라고 부르는 객체와 동일한 역할을 하는 녀석이라고 보면 된다.

 

어쨌든 백엔드 서버와의 통신을 이러한 객체 단위로 주로 하게 될테고, 이 객체의 내용은 보통 DB 스키마에 맞추어 사용하게 된다. 그런데 몽고DB의 경우 _id라는 기본적인 필드가 존재한다. (물론 안쓸 수도 있지만 RDB의 Primary Key를 가진 필드와 비슷한 역할을 하는 중요한 필드이므로 안쓰는 경우는 드물다.)

 

여기서 문제는 하필 필드 이름이 _id 라는 것이다. id 라면 좋았을 것을...

다음과 같이 몽고에서 임시로 컬렉션을 만들고 도큐먼트를 생성해보았다.

 

 

조회해보면 _id 필드가 자동으로 생성된다. 이 필드는 인덱스가 기본적으로 붙어있어서 검색시에 빠른 성능을 제공한다.

이제 플러터에서 이에 대한 다트 객체를 만들어보겠다.

 

일반적으로 만드는 방식대로 다트 객체를 만들어보았다.

빨간줄이 보인다. 매우 거슬린다.

 

선택적 매개변수의 첫 글자에 _(언더바)가 들어갈 수 없다는 내용이다.

다트에서 지원하지 않으니 몽고DB의 모든 _id를 사용하지 않고 다른 것으로 바꾸고 인덱스를 씌워야 하나?

다행히 해결 방법이 존재한다.

JsonKey라는 어노테이션(Annotation)을 쓰면 된다.

(어노테이션이라는 것은 @기호를 앞에 사용하는 메타데이터라고 보면 된다.)

 

바로 적용해보도록 하겠다.

두 줄만 추가하면 된다.

json_annotation 를 import 시켜주고 JsonKey annotation을 적용해주기만 하면 된다.

 

@JsonKey(name: '_id')

이 코드가 핵심인데 실제 키 이름은 _id를 사용한다는 의미이다.

그리고나서 밑에서는 String id 로 선언하여 _id를 사용하지 않고도 _id를 사용하는 것처럼 쓸 수 있다.

 

보통 서버와 클라이언트의 주고 받는 필드는 일치시켜주는 것이 혼동을 줄이는 일인데,

특히 몽고DB의 경우는 거의 필수적으로 사용될 내용인 것 같다.

_id만 해당되는 것이 아니라 lowerCamelCase를 써야 하는 경우면 다 해당된다고 보면 된다.

 

그리고 코드 상단에 json_annotation을 import하였는데, 패키지를 찾을 수 없는 경우 json_annotation 패키지를 설치하면 된다. (https://pub.dev/packages/json_annotation)

나는 별도로 설치하지는 않았는데 Dart 버전에 따라 내장되어있는 것 같기도 하다.

반응형
반응형

MongoDB를 사용할 때 기본이라고 할 수 있는 데이타베이스와 컬렉션에 대해서 알아보고 만들어볼 것이다.

Database라는 것은 MongoDB에서 사용하는 데이타베이스의 가장 큰 단위이다.

Collection은 Database의 하위에 속하는 개념이다.

Field는 모여서 하나의 컬렉션을 구성하게 된다.

Document는 위의 항목으로 구성된 데이타베이스의 실제 데이타이다.

 

예를 들어서 설명해보겠다.

 

내가 쇼핑몰을 만들기 위해 DB를 구축해야 하는 상황이고, 쇼핑몰 이름은 신사 쇼핑몰이다.

신사 쇼핑몰 내에서 관리되어야 할 DB에는 유저 정보, 상품 정보, 결제 정보, 문의 내용 등 여러가지가 있다.

여기서 신사 쇼핑몰 이 자체가 데이타베이스가 되고, 유저정보, 상품 정보 등의 내용은 컬렉션이 된다.

그리고 유저 정보 내의 이름, 생년월일, 전화번호 등의 정보는 필드가 된다.

유저정보에 이름, 생년월일, 전화번호 등의 모든 필드를 채운 완성된 하나의 정보가 들어가면 도큐먼트가 된다.

 

한가지 더 예를 들어서 학원을 운영하는 프로그램을 개발하는데 여기에 구축해야 될 DB를 만든다고 하면,

학원은 데이타베이스가 되고, 학원 내에 필요한 정보들인 선생님 정보, 학생 정보, 과목 정보 등이 컬렉션이 된다.

여기서도 선생님 정보를 예로 들면 선생님 이름, 가르치는 과목 등의 정보가 필드가 된다.

이 필드들이 모두 채워진 하나 이상의 데이타를 도큐먼트라 한다.

 

위의 예로 어느정도 개념은 잡혔으리라 생각한다.

 

MySQL과 같은 RDB를 사용해본 개발자라면 ,

MySQL의 Database = MongoDB의 Database

MySQL의 Table = MongoDB의 Collection,

MySQL의 Column = MongoDB의 Field,

MySQL의 Row = MongoDB의 Document로 보면 된다.

 

이제 각 항목들을 만들어보고 지워보는 등의 작업을 해볼 것이다.

 

1. Database 생성

> use shinsa

  shinsa 라는 이름으로 DB를 생성한다.

 

2. Collection 생성 (대소문자 구분에 주의한다.)

> db.createCollection("users");

  users 라는 이름으로 컬렉션을 생성한다. 

  위에서 shinsa라는 DB 생성 과정을 거쳤으므로 shinsa DB 내에 users 컬렉션이 생성되는 것이다.

 

3. Collection 생성 확인

> show collections;

 

4. Database 생성 확인

> show databases;

 

 

위와 같이 users 컬렉션과 shinsa 데이타베이스가 생성된 것을 확인할 수 있다.

 

5. users 컬렉션에 유저 도큐먼트를 생성한다.

> db.users.insert({"name": "홍길동", "age": 20, "gender": "man"});

  name 필드, age 필드, gender 필드로 구성된 users 컬렉션에 해당 도큐먼트를 생성하는 명령이다.

 

6. users 컬렉션의 생성한 데이타를 확인한다.

> db.users.find();

위와 같이 도큐먼트가 추가된 것을 확인할 수 있다.

여기까지가 데이타베이스를 구성하고 조회하는 가장 기본적인 내용이다.

실제로 MongoDB를 실무에서 사용하게 될 때 이런 식으로 DB를 구성하게 될 것이다.

(물론 이 외에 다양한 고급 기능들이 존재하지만 그것은 나중에 다루도록 한다.)

 

7. 만들어진 컬렉션을 삭제한다.

> db.users.drop(); 

  show collections; 명령으로 users 컬렉션이 삭제된 것을 확인할 수 있다.

 

8. 만들어진 데이타베이스를 삭제한다.

> use shinsa;

> db.dropDatabase();

  db.dropDatabase() 명령만 사용하여 바로 제거해도 되지만, 실수로 다른 DB를 선택해놓고 삭제하는 것을 방지 하기 위해 use 데이타베이스 명령으로 확실히 데이타베이스를 설정해주고 삭제하도록 하는 것이 좋다.

  이제 show databases; 명령으로 shinsa 데이타베이스가 삭제된 것을 확인할 수 있다.

 

반응형
반응형

얼마 전 황당한 일을 겪었다.

로컬 PC에 몽고 데몬을 올려놓고 해당 DB를 연동하여 테스트 서버로 쓰고 있었는데,

갑자기 잘 되던 테스트 페이지에 로그인이 안되며 계정이 없다는 오류를 띄우는 것이었다.

바로 로컬 몽고DB에 접속하여 DB를 확인해보았다.

 

데이타베이스 목록을 확인해보니, 내가 만들어놓은 데이타베이스는 없어지고 웬 이상한 DB가 들어있었다.

느낌이 불길했다.

 

해당 DB를 들어가서 컬렉션 목록을 확인했다.

읽어보라고 떡하니 'README'라는 컬렉션이 만들어져 있었고 바로 내용을 확인해보았다.

 

"All your data is a backed up. You must pay 0.04 BTC to 1FYqD4YtPpcnHyyMiFFigG53s51dcb6xx1 48 hours for recover it. After 48 hours expiration we will leaked and exposed all your data. In case of refusal to pay. we will contact the General Data Protection Regulation, GDPR and notify them that you store user data in an open form and is not safe. Under the rules of the law, you face a heavy fine or arrest and your base dump will be dropped from our server! You can bitcoin here, does not take much time to buy https://localbitcoins.comwith this guide https://localbitcoins.com/guides/how-to-buy-bitcoins After paying write to me in the mail with your DB IP: recoverbase@cock.li and you will receive a link to download your database dump."

아뿔싸... 내가 그 말로만 듣던 랜섬웨어를 당했다니... 분하기도 하고 치욕스럽기까지 했다.

내용은 대충 이렇다. 0.04 비트코인을 지불하면 잃어버린 데이타를 복구해주겠다는 것이고, 

이것을 무시하면 GDPR에 신고하여 법적인 책임을 물게 하겠다는 것이다.

 

개인정보보호정책에 의해 개인의 정보는 안전하게 관리되어야 하므로 이를 허술하게 관리한 대가로 책임을 묻게 될 수 있는 것이다.

 

일단 날아간 데이타는 없으면 귀찮아지기는 하겠지만, 크리티컬한 수준은 아니었으므로 버리기로 결심했다.

저런 놈들에게 10원이라도 내어주는건 용납할 수도 없기 때문이다.

(참고로 중요한 데이타라고 하더라도 비용 지불한 후에 돌려받는다는 보장은 없다.)

 

원인은 매우 간단하다. (그렇기에 더 어이가 없었던 것)

계정 인증이 되어있지 않았기 때문인데 mysql 이나 다른 DB는 설치시 기본적으로 계정 설정에 대한 정보를 물었던 것으로 기억한다.

 

몽고DB의 경우 설치시 디폴트로 계정 설정이 되지 않고 계정 없이 몽고DB에 어드민 권한으로 접근할 수 있게 된다.

여기에 네트웍 망이 public이라면 '내 데이타를 가져가주세요' 광고하는 꼴이다.

 

사실 그동안은 네트워크가 내부망으로 구성이 되어있기 때문에 외부에서 DB를 접속할 수 없었고,

계정 설정이 안되어 있어도 크게 문제는 없었을 것이다.

 

나는 외부 미팅으로 로컬 DB를 잠깐 접근할 필요가 있어서 공유기 포트포워딩 세팅으로 외부망 접근을 열어놓았다.

이 때 몽고DB 내부 접근 계정이라도 만들어 놓았어야 했는데 그걸 하지 않았기에 이 사단이 난 듯 하다.

 

보안은 아무리 로컬에 구성한다해도 이처럼 어떤 경우가 발생할지 모르므로 계정 인증 설정은 항시 해놓는 것이 문제를 일으키지 않을 것 같다. 설정할 때 비밀번호를 허술하게 해놓는다면 설정하지 않는 것과 비슷할 수 있다.

 

브루트 포스 공격(Brute force attack)이라는 흔한 암호 해독 공격 방식이 있는데 이는 문자열의 경우의 수를 다 대입하여 적용해보는 것이다. 그러니 비밀번호가 너무 단순하다면 인증 설정이 되어있어도 해킹에 노출될 수 있다. 꼭 복잡한 암호를 설정하길 바란다.

 

아마 해커들은 현재도 전세계 피씨를 가리지 않고 아이피를 스캔하여 계정 인증 없이 노출된 서버들을 찾아다니고 있을 것이다. 궁금하다면 몽고DB에 그럴싸한 데이타를 올려놓고 계정 인증 없이 외부망으로 노출시키면 3일 이내에 나와 같은 현상을 발견할 수 있을 것 같다.

 

여러분도 항상 보안에 신경써서 이런 자질구레한 일들이 벌어지지 않길 바란다.

 

반응형
반응형

오랜만에 안드로이드 스튜디오 프로젝트를 열고 Gradle Sync 도중 위와 같은 오류가 발생하였다.

무언가 해당 Gradle 버전에 종속적인 문제가 발생한 것으로 예측된다.

 

이 때 간단히 해결할 수 있는 방법으로는 정상적으로 빌드될 수 있는 안정된 Gradle 버전을 찾는 것이다.

 

우선 내가 사용하고 있는 버전을 확인해보자.

안드로이드 스튜디오에서 File -> Project Structure -> Project 탭을 보면 아래와 같은 창을 확인할 수 있다.

내가 사용하는 플러그인 버전은 3.2.1이고, Gradle 버전은 5.1.1이다.

이 버전을 변경하기 위해서는 플러그인 버전에 따라 필요한 Gradle 버전이 무엇인지를 알아야 한다.

플러그인과 Gradle 버전 관계표

나는 기존 플러그인 버전이 3.4.1이었기 때문에 Gradle 버전을 5.1.1 버전을 사용했었다.

여기서 플러그인 버전을 3.2.1로 설정하고, Gradle 버전을 4.6으로 변경하여 사용할 것이다.

 

프로젝트 최상단 폴더의 build.gradle 을 열면 플러그인 버전을 변경할 수 있다.

위와 같이 기존 플러그인 버전 대신 3.2.1로 변경하고, 그 다음에는 Gradle 버전을 변경해주어야 한다.

프로젝트 폴더\gradle\wrapper\gradle-wrapper.properties 파일을 열면 버전을 수정할 수 있다.

위와 같이 기존 버전 대신 4.6 버전을 넣어주었다.

 

이제 위의 Try Again 버튼을 클릭하여 문제가 해결되었는지 확인하면 된다.

정상적으로 빌드까지 마친 것을 확인하였다.

혹시나 위의 버전으로도 오류가 난다면 위 '플러그인과 Gradle 버전 관계표'를 보고 적절한 버전을 찾아가야 할 수도 있다.

반응형
반응형

 

리액트를 빌드하던 중 위와 같이 멈추고 더 이상 진행되지 않는 경우가 있다.

메모리가 부족하게 되면 위 현상이 나타나게 된다.

특히 AWS 프리티어를 쓰는 경우 t2.micro 사양을 사용하게 되는데 램이 1기가밖에 되지 않는다.

조금만 빌드 규모가 커져도 위와 같은 증상을 발견하게 된다.

 

해결 방법은 여러가지가 있다. 

 

1. 빌드시 GENERATE_SOURCEMAP 옵션을 꺼준다.

위와 같이 package.json 파일을 열면 build 부분에 GENERATE_SOURCEMAP를 false로 설정해준다.

어짜피 프로덕션시에는 결국 이 옵션은 꺼주는게 맞기는 하다.

노출되지 않아야 하는 코드들을 보호해주는 역할도 해주기 때문이다.

그리고 해당 속성이 켜져있을 경우 디버깅 정보가 포함되어 빌드되기 때문에 빌드 용량이 커지게 되며,

이는 곧 메모리 부족으로 이어지므로 꺼주게 되면 어느정도 메모리 부족 현상도 해결해주게 된다.

 

2. 메모리 스왑을 통한 메모리 부족 현상 처리

위의 GENERATE_SOURCEMAP 옵션을 끄더라도 결국 빌드 규모가 커질 경우 메모리를 많이 차지할 수 밖에 없다.

즉, 1번 방법으로도 해결되지 않을 경우 이 메모리 스왑 방법을 써볼 수 있다.

메모리 스왑은 간단히 설명하면 메모리의 부족한 부분을 디스크의 일부를 사용하여 대신 사용할 수 있는 기능이다.

메모리에 비해 디스크가 속도가 훨씬 느리기 때문에 스왑 사용시 속도가 느려진다는 단점은 있다.

AWS에서 프리티어는 써야하고 메모리 부족 현상은 해결해야 할 때 써볼만 할 것이다.

 

위 스크린샷은 SSH를 통해 AWS의 메모리 현황을 표시해본 것이다.

보면 기본적으로 AWS 인스턴스는 스왑 메모리가 지정되어있지 않아서 0으로 표시된다.

 

(1) sudo dd if=/dev/zero of=/mnt/swapfile bs=1M count=2048

(2) sudo mkswap /mnt/swapfile

(3) sudo swapon /mnt/swapfile

위 세 명령어를 입력하여 스왑 메모리를 생성해준다.

 

 

위와 같이 메모리를 확인해보면 스왑 메모리가 2기가가 잡혀있는 것을 확인할 수 있다.

그리고 다시 빌드하면 메모리 부족 현상으로 인한 빌드 멈춤 문제는 해결될 것이다.

 

하지만 이처럼 가상 메모리를 쓰게 되면 퍼포먼스의 문제가 발생하므로 임시 방편으로 쓰고 사양을 올릴 것을 추천한다.

 

※ 스왑메모리 해제 방법

sudo swapoff -v /mnt/swapfile
sudo rm /mnt/swapfile

위 두 명령어로 스왑메모리를 해제할 수 있다.

 

이상 리액트 빌드시 멈춤 현상을 해결하는 방법에 대해 설명하였다.

이 외에도 다른 방법들도 있기는 하다. 빌드 규모를 줄이기 위한 코드 스플리팅이나,

위 메모리 스왑 이전에 캐시 메모리가 많이 존재한다면 캐시 메모리를 초기화해주는 방법 등이 있다.

필자 또한 임시적으로 문제를 해결해야 했기 때문에 위에 언급한 2가지 문제로 충분히 해결이 가능했다.

반응형
반응형

 

많은 사람들이 앱 서비스를 개발하여 성공하기를 갈망한다.

아무래도 휴대폰이 필수 기기가 된 이 시대에 사용자에게 쉽게 다가가기 위한 서비스가 앱이기 때문일 것이다.

웹 또한 인터넷을 통해 쉽게 접근이 가능하지만, 외부에 있다던지 밤 늦게 침대에서 자유롭게 사용하기에는 휴대폰만한게 없기 때문에 앱이 접근성 면에서는 더욱 유리하다.

 

앱은 사용자가 접근하기에는 매우 편리한 서비스지만, 웹에 비해 개발에 신경써야할 것이 더 많다고 생각한다.

다양한 이유가 있지만 간단히 말하면, 웹은 인터넷 하나로 통하지만, 앱은 Android, IOS 두가지 플랫폼을 신경써주어야 하기 때문이다.

 

앱 개발에 관심을 갖는 사람들은 많지만 정작 개발하려면 어떻게 시작해야할지 막막하고,

자신이 직접 개발을 하지 않고 외주 등을 통해 진행하려한다해도 최소한의 개발이 어떻게 진행되는지는 알아야 할 것이다.

 

이에 따라, 나는 앱 서비스 개발에 대해 전혀 감을 잡지 못하는 분들에게 최소한의 개발 과정을 이해시키고

사업을 진행하는데 있어서 도움이 되길 바라는 마음에서 이 글을 쓴다.

(참고로 앱에 대해 한정해서 얘기하지만 전반적으로 웹이든, 다른 분야든 어느 정도 통용은 된다.)

 

먼저 서비스 개발의 모든 것은 아이디어를 떠올리는 것부터 시작된다.

'이 앱을 만들면 성공할 것 같다.', '이 앱은 기존에 없는 앱이다', '이건 무언가 불편한데...', '이렇게 하면 재밌겠다.' 등등 내가 겪은 무언가를 통하거나 여러 이유에서 새로운 서비스를 만들어야겠다는 필요성을 느끼게 된다.

 

이제 여기서 서비스 개발을 위해 다음 단계를 진행하게 되는데,

이 단계에서 많은 사람들이 착각하는 것이 이 상태에서 바로 개발을 진행하려한다는 것이다.

개발자를 만나서 '지금 내가 이런 아이템을 생각하고 있습니다. 이렇게 만들어주세요.' 이렇게 말하면,

제대로 된 개발자라면 거절하거나, 정리된 기획 문서를 요구한다.

물론, 돈이 궁하다던지, 경험이 별로 없는 개발자 혹은, 추후 리스크를 감수할 수 있는 개발자라면 그냥 받아서 진행하는 경우도 있다. 이 경우 순탄하게 개발이 진행되는 경우는 흔치 않다.

갑자기 잠수를 탄다던지, 중간에 포기를 선언한다던지 하는 경우가 꽤 드물지 않게 존재한다.

그 이유는 개발자가 생각하는 개발의 규모와 외뢰자가 생각하는 개발의 규모가 다르기 때문이다.

애초에 정확한 설계도가 없는 상태에서 각자가 생각하는 것이 동일할 수가 없기 때문에 어쩌면 당연한 것이다.

 

아이디어를 떠올렸다면 '기획'을 진행해야 한다. 기획이란 아이디어를 구체화시키는 것이라 보면 된다.

기획에는 많은 것들이 내포되어있다. 시장성 조사, 사업계획서 작성, 스토리보드 작성, 추후 추진 계획 등...

여기서는 개발 진행에 필요한 최소한의 것만 설명할 것이다.

 

개발자가 원활하게 개발을 진행하려면 스토리보드 & 와이어프레임이 필요하다.

(아주 간단한 앱이라면 구두로만 해도 진행은 가능하다.)

위를 보면 앱 화면들이 나열되어있다.

내가 만들고 싶은 아이디어를 구체화 시키는 작업이 필요한데, 개발자를 쉽게 이해시키고 개발을 요청하기 위해

위와 같이 내가 만들 앱의 모든 화면을 구현해놓은 것을 스토리보드라 한다.

스토리보드에서 어떤 버튼을 터치하면 어디로 연결이 되는지 등의 정보를 와이어로 연결해준 것을 와이어프레임이라고 한다. 와이어프레임이 있어야 앱 화면간의 유기적인 동작 구조를 이해할 수 있기 때문이다.

뭐 스토리보드만 있어도 개발이 불가능한건 아니니 스토리보드만 요청하는 경우도 있기는 하다.

 

어쨌든 저러한 스토리보드 작성을 본인이 직접 하던가, 앱 기획자를 외주로 구하던지 해서 만들어내야 한다.

애초에 앱에 대한 지식이 별로 없다면 본인이 작성하여도 이해시키기 어려울 가능성이 높으므로 앱 기획자를 고용하는게 순탄하게 진행할 수 있는 방법일 것이다.

스토리보드 작성을 위한 툴은 여러가지가 있는데, 대표적으로 Adobe XD, Sketch, Axure 등이 있다.

기획자마다 작업하는 툴이 다를 수 있고, 되도록 내가 원하는 툴로 작업해줄 것을 요청하는게 좋다.

Sketch는 인기가 많은 툴이지만 맥OS에서만 구동이 가능하고 나의 경우 주로 윈도우 환경에서 작업하기 때문에,

기획 문서를 요청시 Adobe XD로 작업을 요구한다. 규모에 따라 다르지만 보통 기획 작업은 3~6주 정도 걸린다.

 

자, 그럼 스토리보드를 진행 완료하고 그 다음 작업은 무엇일까?

바로 앱 디자인이다. 앱 기획자는 디자이너가 아니기 때문에, UX/UI 디자인을 고려하기 어려운 경우가 많다.

그렇기에 깔끔하고 유저들이 사용하기 쉬운 디자인을 구현하기 위하여 앱 디자인을 진행하게 된다.

앱 디자인은 저렇게 만들어진 스토리보드를 앱 디자이너에게 넘겨주기만 하면 된다.

그럼 앱 디자이너는 저 스토리보드에 새로운 디자인을 씌워서 넘겨줄 것이다.

(참고로 기획자가 작성한 스토리보드 수준의 디자인만으로 충분하다면 굳이 앱 디자인을 진행하지 않고 맡겨도 된다.)

앱 디자인 기간은 보통 화면 개수에 따라 달라지게 되는데, 보통 3~4주 정도 걸리게 된다.

단가의 경우 디자이너마다 다르지만, 앱 화면 하나당(중복화면 제외) 단가 2~6만원으로 잡고 진행하는 듯 하다.

 

여기까지 완료되었으면 이제는 개발자에게 요청할 수 있는 단계가 된다.

개발자는 스토리보드를 넘겨받으면 자신이 이 스토리보드를 보고 어느정도 일정 내에 소화 가능한지, 어느정도 비용이 들 것인지를 산정하게 된다.

여기서가 중요한데, 개발을 어떤 방식으로 진행할지에 대한 부분을 결정해야 한다.

앱 개발하는 방법은 여러가지가 있는데, 크게 웹앱, 하이브리드앱, 크로스플랫폼 앱, 네이티브 앱 4개 단계로 나눠진다.

 

[웹앱]

웹앱은 모바일 웹이라고 보면 된다. 개발 단가는 가장 싼 편에 속하지만, 브라우저로 접근해야된다는 단점이 있다.

웹이기 때문에 구현하고나면 Android/IOS 상관없이 접속이 가능하다.

 

[하이브리드 앱]

모바일 웹을 네이티브로 감싼 앱이라고 보면 된다. 즉, 기기에서 아이콘을 눌러서 앱을 실행하지만 그 내부는

웹뷰(브라우저)로 되어있는 것이다. 웹앱보다 편리한 접근을 원하거나, 푸시 알림 등의 네이티브 기능이 필요할 때

하이브리드 앱으로 구현을 한다. 이전에는 네이티브로 Android/IOS 각각 브라우저로 감싸서 별도의 결과물을 만들어냈지만, 최근에는 크로스플랫폼 개발툴을 이용하여 Android/IOS 결과물을 동시에 만들어내기도 한다.

어쨌든 결국엔 웹앱을 감싼 것이기 때문에 퀄리티면에서는 조금 부족할 수밖에 없다.

 

[크로스플랫폼 앱]

Android/IOS 개발을 동시에 진행하지만 최종적으로는 각각의 결과물을 만들어준다.

네이티브에 비해 생산성이 월등하다. 하지만, 네이티브 종속적인 기능을 구현할 때는 개발에 어려움이 생길 여지가 있다.

최근 스타트업들이 대부분 이 크로스플랫폼 앱으로 개발을 진행한다. 아무래도 네이티브로 진행할 경우 안드로이드, IOS 개발자를 각각 구하여 각각 개발해야 하기 때문에 매우 비효율적이기 때문이다.

성능이나 퀄리티면에서도 하이브리드 앱과는 다르게 네이티브에 가깝게 구현할 수 있다.

 

[네이티브 앱]

휴대폰의 능력을 100% 활용하여 앱을 개발할 수 있다. 단, 안드로이드, IOS 각각 개발을 따로 진행해야한다는 점이 단점이다. 네이티브 종속적인 기능을 활용해야 하는 경우가 아니라면 크로스플랫폼 앱 개발로 진행하는 것을 추천한다.

개발 시간이 크로스플랫폼 앱 개발에 비해 거의 2배 이상 들게 되며, 비용 또한 상당히 높아지게 된다.

 

위 개발 방법 중 자신이 개발할 앱이 어느쪽에 적합한지 확인한 후에 개발자에게 요청하게 된다.

앱의 개발 기간은 프로젝트 내용에 따라 천차만별이다. 또 위의 개발 방식에 따라서도 달라지게 된다.

아주 간단한 프로젝트는 1개월만에도 만들어지기도 하지만, 복잡한 프로젝트는 최소 6개월~1년이 걸리기도 한다.

즉, 자신이 생각하는 프로젝트 규모에 따라 적절한 개발 인력을 산정하는 것도 중요하다.

그리고 개발이 시작된다면, 적어도 1주일에 한번 정도라도 주기적인 미팅으로 개발 진행 단계를 파악할 것을 권장한다.

그냥 개발자만 믿고 놔두다가는 마지막에 가서 어떤 오류에 부딪혀서 계속 고민중이라느니,

다른 사정이 있어서 못했다는 등의 이유로 거의 개발이 되어있지 않는 경우도 종종 존재한다.

실제로 이러한 경우로 프로젝트가 무산되는 경우도 꽤 있다.

 

개발이 완료되면 최종적으로 스토어에 올리는 작업을 진행하게 된다.

(보통은 테스트 단계에서 테스트 버전으로 미리 등록된다.)

이 경우 스토어에 앱에 대한 디테일한 정보를 개발자에게 알려주고 세팅을 요청할 수도 있다.

그리고 추후 유지보수를 위해 개발자에게 소스코드 또한 반드시 넘겨 받는다.

 

이제 앱이 심사를 받고 심사가 끝나게 되면 실제로 스토어에서 개발한 앱을 검색할 수 있게 된다.

당연히 앱의 활성화를 위해 마케팅은 개발 단계에서든지, 개발 이후에서든 진행을 해야할 것이다.

나름 간단하게 쓴다고 썼는데, 별 내용이 없는 듯한데도 너무 길어진 듯 하다.

추가적으로 궁금한 사항은 댓글 등을 통해 메일을 남겨주시거나 하면 답변드리겠습니다.

반응형
반응형

 

SVG 이미지는 픽셀 단위로 이루어진 비트맵 방식의 PNG와 다르게 벡터 이미지 방식을 사용하고 있다.

장점은 크기를 자유롭게 조정하여도 이미지 깨짐이 발생하지 않는다는 것이다.

 

휴대폰은 기종별로 다양한 해상도를 갖고 있으며, 개발자들은 이에 대응할 수 있어야 한다.

PNG 파일 하나를 이용해서 해상도별로 크기를 조정해가면서 사용할 경우 이미지 깨짐이 발생할 수 있고,

그렇다고 PNG 파일을 해상도별로 나누어 사용하기에는 소모 비용이 너무 크다.

 

이 때 사용할 수 있는 것이 바로 SVG 이미지이다.

플러터에서는 SVG를 쉽게 사용할 수 있도록 제공하는 플러그인이 존재한다.

 

flutter_svg 라는 플러그인인데, 아래의 순서대로 따라해보도록 하자.

 

위와 같이 pubspec.yaml 파일을 열고 flutter_svg: 0.17.4 를 추가해준다.

 

실제로 사용할 이미지를 추가해준다.

경로는 원하는 곳에 넣어도 상관없다.

 

위에서 추가한 파일을 pubspec.yaml에도 적어준다.

flutter pub get 명령으로 해당 내용을 반영해준다.

 

이제 flutter_svg 플러그인을 사용하기 위해 위와 같이 import 해준다.

 

위와 같이 SvgPicture 객체를 사용하여 SVG 이미지를 뿌려줄 수 있다.

width, height 속성을 이용하여 크기를 자유롭게 변경할 수 있으며,

SVG 벡터 이미지이므로 이미지 깨짐이 없다.

 

아주 깔끔하게 출력되었다.

 

참고로 SvgPicture.network() 함수를 통해 외부 네트워크에 있는 이미지를 표시할 수도 있다.

 

앱 내에서 아이콘이 차지하는 비중이 상당히 높기 때문에 SVG 파일을 이용하면,

이미지 용량도 절약할 수 있고 사이즈에 구애 받지 않기 때문에 더 효율적인 작업이 가능하다.

 

단, 아이콘이 아닌 복잡한 형태의 이미지는 PNG를 사용하는 것이 낫다.

단순하지 않은 이미지는 오히려 PNG 이미지보다 용량이 더 커질 수 있고, 속도에도 영향을 미칠 수 있기 때문이다.

반응형
반응형

 

플러터는 크로스플랫폼 개발 툴이다.

여기서 크로스플랫폼 개발이라 하면 Android/IOS 모바일 개발을 동시에 할 수 있는 것을 말한다.

 

나는 안드로이드 개발만 하다가 IOS 개발까지 해야함에 따라 크로스플랫폼 개발을 고려하게 되었다.

처음 크로스플랫폼을 접할 때 크로스플랫폼이 가질 수 있는 한계에 대해 매우 고심했고,

다양한 크로스플랫폼을 최대한 비교하여 가장 효율적이고 개발에 가장 유리할 수 있는 크로스플랫폼을 찾고자 했다.

 

크로스플랫폼을 쓰고자 하는 목적은 보통 생산성에 맞추어져 있다.

구현하고자 하는 기술에 문제가 되지 않는다면 크로스플랫폼 개발은 매우 효율적이고 올바른 선택이 될 것이다. 네이티브로 개발한다면 안드로이드와 IOS 플랫폼을 각각 개발하여 두배의 시간 혹은 두배의 인력이 드는데 크로스플랫폼을 쓴다면 비용적으로나 시간적으로나 매우 유리해지기 때문이다.

 

나는 크로스플랫폼 개발 툴 중 자마린, 리액트 네이티브, 플러터 셋중에 무엇을 사용할지 고민을 하였다.

자마린의 경우 나는 C#도 주로 사용했던 언어였기 때문에 그 접근성 때문에 고민했었고,

리액트 네이티브는 자바스크립트 기반인 부분에 이끌렸다. 자마린과 양대 산맥이기도 하고...

 

플러터의 경우는 자마린, 리액트네이티브 사이에서 혜성처럼 갑자기 떠오른 느낌이었고,

다트라는 조금 낯선 언어(그래봤자 자바와 비슷한 느낌)를 사용하지만 구글에서 밀고 있기도 하고, 여러 자료를 찾아본 결과 UI 구현에 대해 상당한 강점이 있으리라 판단했다. 


서론이 길었는데 내가 내린 판단은 플러터를 사용했을 때 가장 생산성 높은 개발을 이룰 수 있다라는 것이다.

우선, UI 구현 자체가 매우 수월하다. 초보자들도 복잡한 UI를 플러그인만 잘 갖다사용하여도 어렵지 않게 구현해낼 수 있다. UI가 별거 아닌 작업처럼 보이지만, 다이나믹한 UI를 구현하기 위해서는 상당한 시간을 할애해야 한다.

 

다른 개발툴을 플러터만큼 파보지 않아서 판단하기에 오류가 있을 수 있지만, 

UI 생산성은 자마린, 리액트 네이티브도 따라오기 어려울 것 같다는 판단이 든다.

 

사실 이 UI 구현에 대한 장점만으로도 플러터를 사용할 이유는 충분했다.

네이티브 개발시 레이아웃 구성이나, 다이나믹한 UI를 구현할 때마다 꽤 시간을 들여야 했던 것을 떠올려보면,

플러터는 그 시간을 날로 먹는(?) 수준으로 줄여준다. 거의 로직에만 신경 쓸 수 있도록 말이다.

 

한가지 더 기대하고 있는 것은 기획 디자인 툴인 Adobe XD에서 'XD To Flutter' 기능을 출시할 예정에 있다.

(얼마 전 출시는 된 상태인 것 같은데 어느 정도 수준인지 확인해보지는 못했다.)

현재 제플린에서 Flutter Extension을 제공하고 있지만, 경험자에게 듣기로는 수동으로 작업하는게 더 빠를 정도로 코드 변환이 매우 지저분하다고 한다. 그래서 XD To Flutter 기능이 완벽하게 출시된다면 UI 작업은 개발자가 거의 신경을 쓰지 않아도 될 수준이 되지 않을까 싶다. 디자이너가 넘겨준 XD파일을 그대로 변환시키면 될 것이므로..

 

하지만, 역시나 단점도 존재한다. 내가 꼽은 단점은 아직 플러터가 리액트네이티브나 다른 크로스플랫폼에 비해 활성도가 떨어진다는 점이다. 자료가 타 개발 도구에 비해 부족하다는 것. 예를 들어, 원하는 기능을 구현할 때 해당 기능에 대한 플러그인이 존재하면 가져다쓰면 금방 개발 가능하지만, 없다면 그에 해당하는 플러그인을 직접 만들어야 한다. 

혹은 존재하더라도 베타 버전의 플러그인이라 다른 플러그인과 충돌하여 컴파일에 오류가 발생하거나 하는 등의 문제도 존재한다. 그래도 이 부분은 점점 개선이 될 것으로 생각하고 있다. 내가 플러터를 접한지 1년이 안되었는데 그 사이에도 문제가 있었던(혹은 베타였던) 플러그인이 많이 개선되었다.

 

개발에 대한 안정성만 놓고 보면 아직까지는 리액트 네이티브가 한 수위라고 생각되지만,

UI 개발 생산성에 대한 매력이 너무 크기 때문에, 그 외 자료 부족 등의 불편한 부분은 감안할 정도라고 본다.

플러터로 간단한 앱부터, 플랫폼 수준의 앱까지 개발해보았는데, 앞으로도 플러터를 애용할 것 같다.

 

반응형
반응형

 

firebase_admob: ^0.9.0+9

image_picker : ^0.6.2+1

 

위 두개의 패키지를 사용하고, 빌드하는데 다음과 같은 오류가 발생하였다.

 

WARNING: Conflict with dependency 'androidx.core:core' in project ':app'. Resolved versions for runtime
classpath (1.0.2) and compile classpath (1.0.0) differ. This can lead to runtime crashes. To resolve this issue follow
advice at https://developer.android.com/studio/build/gradle-tips#configure-project-wide-properties. Alternatively, you
can try to fix the problem by adding this snippet to
C:\temp\android\app\build.gradle:
[ +4 ms] dependencies {
[ +3 ms] implementation("androidx.core:core:1.0.2")
[ +1 ms] }
[ +2 ms] WARNING: Conflict with dependency 'androidx.annotation:annotation' in project ':app'. Resolved versions for
runtime classpath (1.0.2) and compile classpath (1.0.0) differ. This can lead to runtime crashes. To resolve this issue
follow advice at https://developer.android.com/studio/build/gradle-tips#configure-project-wide-properties.
Alternatively, you can try to fix the problem by adding this snippet to
C:\temp\android\app\build.gradle:
[ +1 ms] dependencies {
[ +2 ms] implementation("androidx.annotation:annotation:1.0.2")


[ +5 ms] *******************************************************************************************
[ +3 ms] The Gradle failure may have been because of AndroidX incompatibilities in this Flutter app.
[ +2 ms] See https://goo.gl/CP92wY for more information on the problem and how to fix it.
[ +1 ms] *******************************************************************************************
[ +5 ms] "flutter appbundle" took 40,539ms.
Gradle task bundleRelease failed with exit code 1

#0 throwToolExit (package:flutter_tools/src/base/common.dart:28:3)
#1 _buildGradleProjectV2 (package:flutter_tools/src/android/gradle.dart:751:5)
#2 _asyncThenWrapperHelper. (dart:async-patch/async_patch.dart:71:64)
#3 _rootRunUnary (dart:async/zone.dart:1132:38)
#4 _CustomZone.runUnary (dart:async/zone.dart:1029:19)
#5 _FutureListener.handleValue (dart:async/future_impl.dart:137:18)
#6 Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:678:45)
#7 Future._propagateToListeners (dart:async/future_impl.dart:707:32)
#8 Future._completeWithValue (dart:async/future_impl.dart:522:5)
#9 _AsyncAwaitCompleter.complete (dart:async-patch/async_patch.dart:30:15)
#10 _completeOnAsyncReturn (dart:async-patch/async_patch.dart:288:13)
#11 runCommandAndStreamOutput (package:flutter_tools/src/base/process.dart)
#12 _asyncThenWrapperHelper. (dart:async-patch/async_patch.dart:71:64)
#13 _rootRunUnary (dart:async/zone.dart:1132:38)
#14 _CustomZone.runUnary (dart:async/zone.dart:1029:19)
#15 _FutureListener.handleValue (dart:async/future_impl.dart:137:18)
#16 Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:678:45)
#17 Future._propagateToListeners (dart:async/future_impl.dart:707:32)
#18 Future._completeWithValue (dart:async/future_impl.dart:522:5)
#19 Future._asyncComplete. (dart:async/future_impl.dart:552:7)
#20 _rootRun (dart:async/zone.dart:1124:13)
#21 _CustomZone.run (dart:async/zone.dart:1021:19)
#22 _CustomZone.runGuarded (dart:async/zone.dart:923:7)
#23 _CustomZone.bindCallbackGuarded. (dart:async/zone.dart:963:23)
#24 _microtaskLoop (dart:async/schedule_microtask.dart:41:21)
#25 _startMicrotaskLoop (dart:async/schedule_microtask.dart:50:5)
#26 _runPendingImmediateCallback (dart:isolate-patch/isolate_patch.dart:116:13)
#27 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:173:5)

 

위 문제를 해결하기 위해서 프로젝트 폴더의 android/build.gradle 파일을 열고 

아래 추가 부분에 해당하는 코드를 추가해주고 다시 빌드해본다.

 

buildscript {
  ext.kotlin_version = '1.3.0'
  repositories {
    google()
    jcenter()
  }

  dependencies {
    classpath 'com.android.tools.build:gradle:3.3.2'
    classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
  }

  //--------------추가 부분-----------
  subprojects {
    project.configurations.all {
      resolutionStrategy.eachDependency { details ->
        if (details.requested.group == 'com.android.support'
            && !details.requested.name.contains('multidex') ) {
          details.useVersion "27.1.1"
        }

        if (details.requested.group == 'androidx.core'
            && !details.requested.name.contains('androidx') ) {
          details.useVersion "1.0.1"
        }
      }
    }
  }
  //----------------여기까지------------
}

반응형
반응형

위와 같은 오류 발생시 대처 방법

 

[1] 아래와 같이 android/app/build.gradle 에 아래 코드를 추가한다.

packagingOptions {
    exclude 'META-INF/proguard/androidx-annotations.pro'
}

위와 같이 buildTypes{} 아래에 새로 추가해준다.

 

[2] android/build.gradle 에서 gradle 버전을 3.3.0 으로(혹은 그 이후 버전) 변경해준다.

classpath 'com.android.tools.build:gradle:3.3.0'

위 코드처럼 수정해주면 되며, 기존에 버전이 3.2.1 이하 버전이었다면, gradle 버전 문제일 확률이 높다.

즉, [1]번 해결책보다 [2]번 해결책이 더욱 적합하다.

 

 

반응형

+ Recent posts