플러터에서는 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를 써야 하는 경우면 다 해당된다고 보면 된다.
데이타베이스 목록을 확인해보니, 내가 만들어놓은 데이타베이스는 없어지고 웬 이상한 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일 이내에 나와 같은 현상을 발견할 수 있을 것 같다.
여기서 크로스플랫폼 개발이라 하면 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 개발 생산성에 대한 매력이 너무 크기 때문에, 그 외 자료 부족 등의 불편한 부분은 감안할 정도라고 본다.
플러터로 간단한 앱부터, 플랫폼 수준의 앱까지 개발해보았는데, 앞으로도 플러터를 애용할 것 같다.
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