Spatial Video (공간동영상) in Swift

Spatial Video는 아이폰 15프로, 15프로맥스에서 지원하는 3차원 동영상 비디오 입니다. 아이폰 15프로 또는 맥스를 가지고 있다면 Spatial Video를 만들 수 있습니다. 하지만 현재 이렇게 녹화된 동영상은 애플 비젼프로, 오큘러스에서만 플레이 할수 있습니다.

Spatial Video의 기본 원리는 양쪽눈의 시각차이를 이용한 방법 이므로 이미 사용중인 3D 안경을 이용하는 방법으로 일반 동영상 플레이어에서도 3D 동영상을 볼 수 있습니다.

Swift를 이용한 동영상 분리

아래 링크의 프로젝트에서 Swift를 이용하여 Spatial Video를 분리하여 좌측, 우측 동영상을 분리하여 UIImage로 변환하는 기능을 볼수 있습니다.

Spatial Video Separate - Swift/SwiftUI
https://github.com/bipark/SpatialVideoSeparate-Swift

앱 다운로드

아래 링크의 앱은 Spatial Video의 좌측, 우측으로 분리하여 3D 안경을 이용하여 볼 수 있는 애너글리프 3D, 카드보드 3D 동영상으로 변환하는 기능을 가진 아이폰용 앱입니다.

https://apps.apple.com/us/app/spatial-video-converter-pro/id6479389968

크랙 디텍터 – Crack Detector

크랙디텍터(Crack Detector) V3가 앱스토어에 릴리즈되었습니다. 크랙디텍터는 스마트폰 카메라에서 촬영한 콘크리트 벽면의 벌어진틈(크랙)을 찾아내고 크랙의 두께와 길이를 이미지 프로세싱 방법으로 측정하는 소프트웨어 서비스 입니다.

V3 – 머신러닝

크랙디텍터를 만들기 시작한건 거의 10년이 넘었습니다만 상용화 가능한 수준의 제품이 된건 이번 버전이 처음입니다.

V3에서 가장 큰 변화는 사진에서 크랙을 찾아내는 방법을 기존의 룰베이스 이미지 프로세싱에서 세그멘테이션 머신러닝을 도입한 것입니다. 기존의 이미지 프로세싱 방법은 현장의 위치, 조명, 시간등에 따른 이미지 품질 차이로 인해서 크랙을 못찾거나 그림자를 크랙으로 인식하는 등의 문제가 있었습니다만 머신러닝 도입후 크랙디텍션 성능이 매우 향상되었습니다.

마커

앱을 다운 받고 콘크리트 벽에 크랙을 촬영하면 이미지를 분석해서 크랙을 찾아냅니다만 크랙의 크기를 Pixel 단위로 표시합니다. 마커를 활용하면 Pixel 단위를 mm 단위로 정확하게 변환 할 수 있습니다.

마커는 정밀측정이 가능한 4개의 마커를 가진 하드웨어 타입과 1개의 마커를 사용하는 스티커 타입을 쓸 수 있습니다.

앱다운로드

다운로드 - 아이폰
https://apps.apple.com/kr/app/crack-detector/id1530337221

무료다운로드 이며 안드로이드용은 준비 중입니다. 앱사용에 관한 문의는 rtlink.park@gmail.com

개발배경

브릿지(BritGi – Smart CCTV) 릴리즈

사용하지 않는 스마트폰을 CCTV로 활용할 수 있는 앱

브릿지(BritGi) V1.0

브릿지(BritGi)는 오래되서 사용하지 않는 스마트폰을 CCTV로 활용할 수 있는 앱입니다. 집을 비울때 반려견, 반려묘가 무었을 하고 있는지 내 스마트폰으로 실시간 조회 할 수 있습니다.

다운로드

아래 링크에서 무료로 다운로드 할 수 있습니다. 인앱결재 없이 계속 무료로 사용할 수 있습니다. 아이패드, 안드로이드 패드 지원합니다.

사용방법

브릿지(BritGi)는 오래된 스마트폰을 카메라로 사용하고 현재 쓰고 있는 스마트폰을 조회용으로 사용합니다.

  1. 카메라로 사용할 오래된 스마트폰과 지금쓰고 있는 스마트폰에 앱을 다운로드 하여 설치 합니다.
  2. 두개의 스마트폰에서 동일한 아이디(구글로그인 또는 애플로그인)로 로그인 합니다. (주의) 반드시 동일한 아이디로 로그인 해야 합니다. 카메라폰과 뷰어폰은 다른 종류라도 됩니다. – 예를들면 카메라는 안드로이드 뷰어는 아이폰
  3. 카메라 폰에서 카메라버튼을 눌러 카메라를 실행하고 뷰어폰에서 새로고침을 실행하면 카메라 목록이 화면에 나타나고 목록을 누르면 원격 영상을 볼수 있습니다.
  4. 카메라에 상시전원을 연결하고 고정하여 원격에서 전면, 후면 카메라를 선택하며 360도 거의 모든 방향을 볼 수 있습니다.

WebRTC(Peer-to-Peer)

브릿지(BritGi)는 카메라와 뷰어를 직접 연결하는 방식(WebRTC – Peer to Peer)으로 서버를 거치지 않고 영상정보를 전송합니다. 그래서 서버에 영상이 저장되지 않고 실시간으로만 조회가 가능합니다.

HD / Wifi, LTE, 5G

  • Wifi 연결에서는 HD 화질을 30 frame / sec 로 연결됩니다.
  • 5G/LTE 환경에서는 SD화질로 연결됩니다.
  • 가급적 Wifi 환경에서 사용하세요.

SwiftUI 에서 URL Schemes 과 Deeplink

최근 SwiftUI를 공부하며 개인적으로 문제가 되고 있던것 중에 하나가 과거에 하던 방식으로 문제를 해결하려는 버릇인데 URLSchames 문제를 해결하려다 또 그런일이 있었다. 프레임워크가 변하면 생각을 바꿔야 하는데 그게 쉽지 않다.

URL Schemes

URL Schemes와 그에따른 처리방법은 아이폰앱에서 데이터를 전달할 수 있는 규격을 등록하고 등록된 규격의 데이터를 수신 했을때의 처리를 위한 일련의 처리를 말하는데 거의 초기버전의 iOS부터 지원한 방법이다.

내가 알고 있던 이 문제를 해결하는 방법은 AppDelegate와 SceneDelegate 에 아래와 같은 함수를 정의하고 URLContext의 URL을 파싱해서 데이터의 내용에 따라 분기를 하고 데이터에 맞는 ViewController를 오픈하는 단계까지 가는 것이 문제의 해결이었다.

 func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
        if let url = URLContexts.first?.url {            
            print("url: \(url.absoluteURL)")
            print("scheme: \(url.scheme)")
            print("host: \(url.host)")
        }
    }

하지만 현재 XCode(V 12.5) 에서 기본적인 프로젝트 생성에서 Life Cycle을 SwiftUI App으로 만들면 AppDelegate, SceneDelegate를 만들어 주지 않는다. 이게 존재 하지 않으니 어디에서 url을 받아들이고 분기해야 할지 알 수가 없었다.

참고로 앱을 생성할때 아래의 Life Cycle을 UIKit App Delegate를 선택하면 이전에 쓰던 AppDelegate과 SceneDelegate 파일이 자동으로 생성된다.

삽질

과거의 방법으로 문제를 해결하려는 시도는 결국 며칠간의 삽질로 끝났다. AppDelegate와 SceneDelegate를 새로 만들었고 이런 해결방법도 가능하다는 결론에 도달했지만 검색중에 보게된 코드 때문에 그 코드들을 모두 버리고 말았다.

.onOpenURL()

문제의 해결 방법이 너무 간단해서 약간 허탈하긴 했지만 아래의 코드와 같이 View의 메소드를 호출하는 방법으로 URL Schemes에 등록된 url의 접근을 확인 할 수 있다.

@main
struct MyApplication: App {
  var body: some Scene {
    WindowGroup {
      ContentView()
        .onOpenURL { url in
          print(url)
        }
    }
  }
}

DeepLink

위의 코드에서 보는것과 같이 모든 View 컨트롤은 .onOpenURL() 메소드를 가지고 있다. 이것은 위에 보이는 메인뷰에서도 url 이벤트를 확인해서 실제로 보여주고자 하는 상세 페이지까지 이동 했을 경우 그 뷰에도 동일한 함수(.onOpenURL())를 호출하면 url값을 얻을 수도 있다.

.onOpenURL { url in
            if (url.scheme! == "where-board" && url.host! == "invite") {
                if let components = NSURLComponents(url: url, resolvingAgainstBaseURL: true) {
                    for query in components.queryItems! {
                        print(query.name)
                        print(query.value!)                        
                    }
                }
            }            
        }

Sample 프로젝트

아래와 같이 신규 프로젝트를 생성해서 상세 페이지에서 URL Schames를 통해 전달된 데이터를 출력해 볼수 있다.

프로젝트의 Life Cycle은 SwiftUI App으로 선택한다.

URL Schemes을 생성하기 위해서는 프로젝트 – info – URL Types 를 선택하고 + 버튼을 눌러서 URL Schemes 항목에 유니크한 이름을 입력한다.

메인 프로젝트 App – Scene – View 의 구조에서 ContentView()에 아래의 코드와 같이 url을 확인 할 수 있다.

import SwiftUI

@main
struct DeepLink_TestApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .onOpenURL(perform: { url in
                    print(url)
                })
        }
    }
}

실제 콘텐츠뷰의 내부에서도 url 을 전달 받을 수 있다

import SwiftUI

struct ContentView: View {
    var body: some View {
        Text("Hello, world!")
            .padding()
            .onOpenURL(perform: { url in
                print(url)
            })

    }
}

아이폰에 빌드하고 앱이 실핼된 상태에서 사파리 브라우저에서 위와 같이 주소를 입력하고 앱으로 전환되어 아래와 같이 url이 전달되는 것을 확인할 수 있었다. 두번 프린트 되었는데 첫번째는 위의 코드에서 두번째는 아래의 코드에서 전달된 데이터이다.

my-deeplink-test://share?id=1234
my-deeplink-test://share?id=1234

대략 아래와 같은 코드를 이용해서 url을 파싱할 수 있다.

        .onOpenURL { url in
            if (url.scheme! == "my-deeplink-test" && url.host! == "share") {
                if let components = NSURLComponents(url: url, resolvingAgainstBaseURL: true) {
                    for query in components.queryItems! {
                        print(query.name)
                        print(query.value!)                        
                    }
                }
            }            
        }

20210515 박병일

iOS/Swift에서 OpenCV 사용

수년전에 iOS에서 OpenCV를 이용해서 작업을 해본적이 있었는데 새로 같은 작은 작업을 하려다보니 다 잊어버려서 OpenCV 프레임워크를 설치하는 과정을 잊어버려서 잊지 않기 위해서, 그리고 새버전의 Swift/Xcode에서 OpenCV와의 연결에 문제가 없는지 확인하기 위해 다시 한번 해보고 아래와 같이 정리 했다.

OpenCV 프레임워크 다운로드

iOS용 OpenCV 프레임워크를 다운 받는다. OpenCV가 버전업 될때마다 친절하게도 iOS용 프레임워크를 빌드해서 다운받을 수 있도록 준비가 되어 있다. 아래 링크에서 프레임워크를 다운 받을 수 있다. 현재 버전은 V4,5,2 최근 버전이 굉장히 빠르게 올라가고 있다. 머신러닝과 관련한 기능들이 많이 추가된것이 원인인듯하다.

https://opencv.org/releases/

다운 받아 압축을 해제 하면 iOS에서 바로 사용할 수 있는 프레임워크 형식의 파일과 폴더로 구성되어 있다.

Xcode 신규 프로젝트 생성

프레임워크 추가

Xcode에서 OpenCV-Test 라는 이름의 신규 프로젝트를 하나 생성 했다. 내가 쓰고 있는 Xcode의 버전은 12.5이다. 위에서 압축 해제한 OpenCV 프레임워크를 폴더를 생성한 프로젝트 폴더로 이동하고 프로젝트를 선택하고 [Build Phase] 에서 Link Binary With Libraries 의 + 버튼을 눌러서 프로젝트에 포함시킨다.

래퍼클래스 생성

File 메뉴에서 New -> File..을 선택해서 아래 그림과 같은 순서로 CVWrapper를 생성한다.

이때 주의할점은 Language 에 Swift 대신에 Objecitive-C 언어로 선택을 해주어야만한다. Next를 선택하면 Xcode는 자동으로 브릿지 Header 파일을 생성하겠냐는 메세지를 보여준다. Create Bridging Header를 선택해서 브릿지헤더파일을 생성한다.

그리고 생성된 CVWrapper.m 파일의 확장자를 CVWrapper.mm 으로 변경해준다.

Prefix Header 파일 생성

그리고 메뉴에서 File – New – File… 을 선택해서 PCH file을 위 그림과 같이 선택해서 주어진 파일명으로 생성하고 생성된 .pch 파일에 아래와 같은 코드를 삽입한다.

//
//  PrefixHeader.pch
//  OpenCV-Test
//
//  Created by Park Billy on 2021/05/07.
//

#ifndef PrefixHeader_pch
#define PrefixHeader_pch

#ifdef __cplusplus
#include <opencv2/opencv.hpp>
#endif

#endif /* PrefixHeader_pch */

생성한 Prefix 파일을 컴파일러에 등록해 주어야 하는데 아래의 그림과 같이 Build Settings에서 $(SRCROOT)/프로젝트명/PrefixHeader.pch의 형식으로 등록한다.

OpenCV 버전확인 하기

아래의 코드를 추가해서 OpenCV의 버전을 가져오는 기능을 만들어 봤다. CVWrapper.h / CVWrapper.mm

//
//  CVWrapper.h
//  OpenCV-Test
//
//  Created by Park Billy on 2021/05/07.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface CVWrapper : NSObject

+ (NSString *)openCVVersion;

@end

NS_ASSUME_NONNULL_END
//
//  CVWrapper.mm
//  OpenCV-Test
//
//  Created by Park Billy on 2021/05/07.
//

#import "CVWrapper.h"
#import <opencv2/opencv.hpp>

@implementation CVWrapper

+ (NSString *)openCVVersion {
    return [NSString stringWithFormat:@"OpenCV Version %s",  CV_VERSION];
}

@end

ContentView.Swift에서 래퍼클래스의 함수를 호출하여 실제 아이폰 화면에 OpenCV 버전을 보여주는 코드를 아래와 같이 추가했다.

//
//  ContentView.swift
//  OpenCV-Test
//
//  Created by Park Billy on 2021/05/07.
//

import SwiftUI

struct ContentView: View {
    @State var version = ""
    
    var body: some View {
        Text(self.version)
            .padding()
            .onAppear(){
                self.version = CVWrapper.openCVVersion()
            }
    }
}

실행결과

실행 결과는 아래와 같다.

실제로 iOS에서 OpenCV 기능을 활용하려면 위의 CVWrapper.h 에 함수를 정의 하고 CVWrapper.mm에 기능을 구현해서 Swift 코드에서 함수를 호출하는 방식으로 사용하면 된다.

소스코드

아래 링크에서 소스 코드를 다운 받을 수 있다. 이 프로젝트를 테스트 하려면 소스에 OpenCV 프레임워크는 포함되어 있지 않으므로 다운 받아서 이글의 첫번째 부분과 같이 프레임워크를 포함시켜야 한다.

https://github.com/bipark/opencv_ios_starter

20210507 박병일

Obj-C, AutoLayout, ReactNative, Flutter, SwiftUI, 개발생산성 등등

최근에 다시 Swift/SwiftUI를 스터디하며 그동안 iOS 개발을 하며 느꼈던 UI 개발에 대한 개인적인 생각을 정리해봤습니다. 시작과 끝에 대략 10년정도의 시간차이가 있습니다.

Obj-C, InterfaceBuilder 재미있었음

Objective-C를 접한건 2009년 4월. 델파이(Pascal)에 익숙해져 있던 나에겐 NS…로 시작하는 긴 함수명을 가진 프레임워크가 의외로 익숙했고 그해에 여러개의 앱을 스토어에 업로드해서 통산 100만 다운로드는 넘기는 경험을 해보기도 했다. 무료앱이라 그다지 수익을 내지는 못했지만… ^^;

Interface Builder는 델파이의 화면디자이너와 유사했고 시간이 지나며 StoryBoard로 화면이 통합되는 과정에 사이즈 때문에 느려지는 문제들이 있었지만 최소한의 일관성은 유지 했다는 측면에서 나쁘지 않았다는 생각

Obj-C, AotoLayout 재미없었음

iOS앱 개발이 재미가 없어지기 시작한 시점은 아마도 Xcode에 AutoLayout이 적용되기 시작한 시점이었던것 같은데 기껏 화면을 디자인해 놓으면 수정사항이 발생할때마다 Layout이 깨지고 화면전체의 레이아웃을 다시 잡아야하는 불편함을 감수해야 하는게 너무 싫었음. 시간이 지나면서 언어가 Obj-C에서 Swift로 바꿔었지만 UI를 만드는 과정에 너무 번거로운 상황이 반복되면서 점점 흥미를 잃기 시작

ReactNative, CSS 흥미로움

독립개발자에서 취직을 하면서 iOS와 Android를 동시에 지원해야 하는 일이 생기면서 멀티 플랫폼에 대한 고민을 하게 되었는데 적은 리소스로 목적을 달성해야하는 스타트업 비즈니의 환경에 적합한 솔루션을 찾은것이 ReactNative. (이하 RN)

RN은 웹개발 기술을 기반으로 하고 있는데 HTML, CSS를 이용한 화면 구성이 위에 언급한 AutoLayout과 대비되면서 Xcode에도 이런 방법이 도입되어야 하지 않을까 하는 생각을 했었던 기억.

Flutter, Widget 괜찮긴한데…

멀티플랫폼은 한번 발을 들이면 쉽게 빠져 나갈수 없는데, 아이폰과 안드로이드를 한번에 빌드할 수 있다는데 누가 관심을 갖지 않겠는가? 그런면에서 Flutter는 구글의 후광을 입고 가장 빠르게 성장하고 있는 모바일 개발 플랫폼. 성능도 꽤 괜찮다.

위젯기반의 UI 레이아웃은 좀 심하다 싶게 캐스케이드 되는것을 제외하면 화면 변경이 용이하고 코드와 레이아웃간의 연결성도 나쁘지 않은편. 이것은 위에 언급한 RN도 거의 동일한 개발 편의성을 가지고 있지만 Flutter가 Android Studio 지원등에서 장점이 있고, 패키지 관리등에서 RN보다 좋은점이 많이 있음.

1년이 넘게 Flutter를 쓰면서 문제점이 나타나기 시작한 부분은 UI는 아니고 패키지 관리 부분이었는데 과도하게 외부 패키지를 사용하면 각 버전들의 의존성이 달라서 업데이트에 애를먹는 상황이 발생하게 되는데 이부분 좀 심각함

Swift, SwiftUI 완전 괜찮은데?

애플에서 발표한지 2년이 다되어가는 기능을 두고 이제와서 좋다고 말하는데 어지간히 뒷북이긴 하지만 애플이 자존심을 버리고 CSS의 장점을 잘(?) 흡수해서 하나의 코드페이지에 통합한것은 늦었지만 훌륭한 작업이었다고 칭찬할만 하다고..

iOS만 지원하는 SwiftUI와 위의 멀티플랫폼 도구를 비교하는게 오류가 있을 수 있지만 UI 구성과 코드의 자연스러운 연결에 대한 부분만 본다면 SwiftUI가 가장 우수하지 않을까 하는게 최근 앱을 하나 만들어 보고 내린 결론. 물론 개인적인 생각!

SwiftUI 테스트 프로젝트

초심으로 돌아가 돈을 벌생각이 없는 무료앱을 하나 만들어봄. 대략 2주정도 걸렸고 익숙하지 않은 방법으로 화면을 만들어야 했지만 애플의 튜토리얼 프로젝트를 따라하다보니 위에 RN, Flutter에서 UI를 만들며 했던 경험들이 매우 도움이됨

https://developer.apple.com/tutorials/swiftui

위의 튜토리얼에서 iOS화면구성에 필요한 기본적인 컨트롤의 레이아웃을 구성하는 방법에 대한 정보를 얻을 수 있었고 여기에 더해 API통신(Alamofire), GoogleAnalytics등을 Pod으로 설치해서 프로젝트 생성

내연봉계산기

앱다운로드는 아래링크

https://apps.apple.com/kr/app/id1564866857

이 앱은 개인적으로 수년에 한번씩 만드는 테스트 프로젝트인데 새로운 개발 프레임워크를 테스트 할때마다 새로 만드는 앱. 해가 바꿜때마다 만들때마다 소득세를 포함한 각종 공제액의 비율이 달라져서 매년 업데이트를 해야만 해서 새로운 공부를 시작할때 유용하게 재사용하고 있음. 물론 완전히 새로 만드는거지만

결론

어느것이 더 좋다는 결론을 내리려고 하는것은 아니고 개인적으로 최근 수년간 Xcode를 그저 빌드 도구(RN, Flutter 역시 iOS 빌드는 Xcode로 한다)로만 사용하고 있었는데 멀티플랫폼에 대한 부담을 벗어버리니 Swift/SwiftUI가 개인적으로 다시 의미가 생겼다고나 할까?

SwuftUI가 UI빌딩과 메인터낸스에서 발군의 장점이 있는것과 동시에 그동안 깨닫지 못하고 있었던 빌드타임과 런타임성능이 RN, Flutter에 비해 현저하게 빠르다는것도 매우 큰 장점이다. 물론 멀티플랫폼을 지원하지 않는다는게 단점이라면… 그건 당연한거지만

20210501 박병일

SwiftUI Tabar/NavigationView (탭바/네비게이션뷰)

최근 Flutter가 좀 지루해져서 – 지루해진 이유는 다음에 쓰기로 하고 – iOS 네이티브 개발을 다시 해보고 싶다는 충동(?)에 SwiftUI 스터디를 시작했다. iOS 개발은 Obj-C 부터 거의 10여년을 하고 있지만 아직 SwiftUI를 써보지 못해서 아래 링크의 튜토리얼을 따라해보고 대략의 감을 잡을 수 있었다.

아래 링크를 따라가면 SwiftUI의 기본적인 Interface에 대한 이해를 얻을 수 있다.

https://developer.apple.com/tutorials/swiftui/creating-and-combining-views

각설하고… 아래 코드는 iOS의 대표적인 UI인 탭바네비게이션 셈플코드이다. Xib 파일도 없고 폼디자이너도 없다. 이렇게 심플하게 코드를 만들 수 있는데 그동안 왜 그렇게 했는지 이해 할 수 없지만… 매우 만족 스럽다. Group, VStack, HStack 등을 이용해서 뷰 상세를 꾸미다보면 CSS에서 본것 같기도 하고 Flutter 에서 본것 같기도한데 각종UI 프레임워크의 좋은 점을 잘 가져와서 만들었다는 생각이 들었다.

//
//  ContentView.swift
//  study1
//
//  Created by Park Billy on 2021/04/20.
//

import SwiftUI

struct View1: View {
    var body: some View {
        
        // 네이게이션뷰로 만들어 줘야 BarTitle을 만들며 상세뷰로 이동 할 수 있다.
        NavigationView {
            // DetailView로 이동
            NavigationLink(destination: DetailView()){
                Text("Go Detail View")
            }
            .navigationBarTitle("첫번째뷰")
        }
        .tabItem { // 탭바 타이틀과 아이콘
            Text("View1")
            Image(systemName: "tray.full")
        }
    }
}

struct View2: View {
    var body: some View {
        NavigationView {
            Text("Hello View2")
                .navigationBarTitle("두번째뷰")
            
        }
        .tabItem {
            Text("View2")
            Image(systemName: "tray")
        }
    }
}

struct DetailView: View {
    var body: some View {
        Text("Hello Detail View")
            .navigationBarTitle("상세뷰")
    }
}

struct ContentView: View {
    var body: some View {
        // 탭바를 만든다 ... 이렇게 간단히?
        TabView {
            View1()
            View2()
        }
    }
}

아래는 실행 결과 – 첫번째뷰에서 [Go Detail View]를 누르면 상세뷰로 이동한다. 상세뷰는 상단에 네비게이션 타이틀이 뷰 스크롤되면 자동으로 상단으로 붙어올라가는 기능이 있다. 그리고 빌드하지 않아도 Xcode에서 프리뷰를 볼 수도 있다.

오랜만에 네이티브로 스토어에 앱을 하나 올려야겠다고 생각했다.

코로나 집콕 프로젝트 #2

이글은 앞선글(http://practical.kr/?p=183)에 연결된다. 작업을 진행 할때마다 페이스북에 올린 짧은글들과 이미지들을 묶어서 정리했다

2021년 1월 15일

https://www.facebook.com/rtlink.park/posts/4243516198997718

ESP32의 문제는 디스플레이가 없어서(당연한거지만… ) 디버깅이 힘들다. USB로 컴퓨터에 물려놓고 로그를 찍어가며 실행을 확인해 볼 수 밖에 없다. 근데 이게 그러라고 만든 기계가 아니니 전원만 넣으면 혼자 돌아가야 한다.며칠전에 데이터가 잘 들어오다가 새벽부터 데이터가 안들오고 다운이 되었는데 원인을 알수가 없었다 – 물론 원인은 내 발코딩이다 당연하지! – 그래도 뭘 잘못했는지는 알아야 해서 MQTT로 로그를 서버에 전송하는 기능을 넣고 앱에서 조회 할 수 있도록 만들었다. 정확한 에러의 원인은 알 수 없지만 최소한 로그를 많이 남기면 그전에 뭘 하고 있었는지는 알 수 있다. 그 다음이 문제였겠지… 결국 그 버그는 잡았지만 버그라는게 그것 뿐일리가 없다.그리고 적당한 타이밍에 알리에서 Pan & Tilt Servo 셋트를 보내 주셨다. 카메라를 원격에서 조정하면 좋겠다고 생각하고 주문했는데 일단 조립만 하고 OTA 다 하면 붙여봐야지!

2021년 1월 16일

https://www.facebook.com/rtlink.park/posts/4246916305324374

간밤에 ESP32의 OTA를 다 만들었다. 이제는 SW 업그레이드를 위해 설치한 장비를 떼러 가지 않아도 된다. 그래서 좀 더 형태를 갖춘 온도 측정기를 만들었다.ESP32-BME680 기반으로 온도, 습도, 압력, VOC를 측정기를 하나 더 만듬. 빵판에 올려 놓으니 그래도 조금은 깔끔해짐. 이건 식당에 설치할 예정.앱 인터페이스가 좀 더 예뻐져야 하는데… 공돌이에겐 진짜 어려운일..^^;

2021년 1월 24일

https://www.facebook.com/rtlink.park/posts/4270100223005982

기존의 일체형 토양수분 측정기의 문제는 화분에 물을 준다던가 실외에 둘때 비가 온다거나 하면 통신모듈(esp32)이 침수된다. 그래서 독립형 센서를 연결했다. 데이터는 대충 맞췄고, 방수박스까지 사놓긴 했는데…왠일인지 동백화분은 시들시들하다. 모든 식물을 다 죽이고 마는 똥손이 이런걸 만들고 있다니… 아이러니 라고..

2021년 1월 28일

https://www.facebook.com/rtlink.park/posts/4282926348390036

ESP32 보드 수집이 취미가 될것 같음. 오늘 도착한 보드는 10개 묶어서 35달러짜리 미니 보드와 아두이노 타입의 조금 큰 보드지난번에 사놓은 케이스에 토양수분 센서까지 붙여서 넣어봤는데 보드만 있는것보다는 뭔가 있어보임. 그동안 여러가지 센서를 테스트 삼아 붙여 봤는데 토양수분, 온습도만 측정하는 전용박스로 프로젝트를 마무리 하는게… 누가 뭐라하는 사람이 없으니 끝이 안나서.. ^^;근데 토양수분센서는 캘리브레이션이 제일 문제… 제로/스팬 환경을 만들기가 너무 어려움

2021년 1월 31일

https://www.facebook.com/rtlink.park/posts/4290248040991200

ESP32에 토양습도 센서를 붙여서 데이터의 거동을 보다가 – 화분에 물을 주면 처음엔 100% 수준에서 쭉 떨어지다가 어느 시점이 되면 약간 증가하는 현상을 보이고 또 떨어지다가 어느 시점에서 약간 증가한 후에 다시 떨어지는 모양의 그래프가 나타난다.처음 데이터가 올라갈땐 이럴리가 없을텐데.. 캘리브레이션 과정에서 제로/스팬이 잘못된게 아닌가 하고 여러 방법으로 코드를 바꿔 봤지만 동일한 결과가 나타났고 좀 더 지켜보니 그런 패턴이 반복된다. 자료를 좀 찾아보니 토질에 따라 다르고 그 결과도 흡수와 배출을 반복하는게 아닌가(? 확실치 않다..) 하는 생각에 이르게 되었다. 뭘 알겠는가… 코딩만 하는 공돌이가.. ^^;엇쨋든 결론은 앱에서 그래프를 축소 확대 할 수 있는 기능이 필요하다는데 이르렀다. 결론이 이상하네.. ^^;

2021년 2월 2일

https://www.facebook.com/rtlink.park/posts/4295899163759421

세상에는 이해할 수 없는 일이 많기도 하지만 가끔은 그런일이 내 거실에서 일어 나기도 한다. 이전 포스팅에서 화분에 꽂아둔 수분센서가 측정한 토양 수분 데이터가 오르고 내리기를 반복하면서 점점 내려간다고 올렸는데 중간에 위치를 바꾸느라 전원이 끊어져 리부트된 이후에 계속해서 올라만 가고 있다.내가 안보는 사이에 누군가 물을 조금씩 붇고 있나? 알수없는 일이다.결국 처음부터 다시 할 수 밖에… 이러면 센서의 정확성을 의심하지 않을 수 없다. 이러니 중국산 센서를 어떻게 믿나… 까지 가고 만다.

2021년 2월 4일

https://www.facebook.com/rtlink.park/posts/4301045793244758

며칠전 올린 포스트에서 중국산 토양수분센서의 데이터가 시간에 따라 상승하는 결과가 나타나서 믿을 수 없다는 생각을 하고 또 다른 중국산(?) 센서를 동시에 같은 화분에 설치해서 약 24시간 측정했다.두 센서의 캘리브레이션이 달라서 절대 수치는 다르지만 대략 데이터의 움직임은 비슷하게 나타났다. 이것들이 같은 중국산이라고.. 서로 짯나? ㅋ 엇쨋든 보이지 않는곳에서 일어나는 변화는 나의 예측과 맞지 않는다. 일단 하루 더 지켜보는 걸로.. 하여간 IOT 측정 네트웍을 만들려던 프로젝트는 센서의 정확도를 확인하는 프로젝트로 변해가고 있는데… 나도 어디로 갈지 잘모르겠다.

2021년 2월 6일

https://www.facebook.com/rtlink.park/posts/4307357982613539

2틀이나 들여서 토양수분, 온도, 습도 챠트를 통합했지만 토양수분 데이터의 변화폭이 너무 적어서 상호관계를 한눈에 알기가 쉽지않다.청색이 온도 챠트인데 환기를 할때마다 고꾸라지는 특성이 있다. ㅋㅋ

2021년 2월 7일

https://www.facebook.com/rtlink.park/posts/4310002775682393

화분1, 2는 동일한 화분에 다른 토양수분센서가 설치되어 있고 화분1은 센서의 성능이 의심되고 있던 센서이다. 일주일만에 이놈이 불량스러운 아웃풋을 내고 있다는 사실을 확인했다. 처음엔 비슷하게 떨어지다가 며칠지나서 토양수분이 다시 서서히 올라감… 아직 원인 파악은 못함.. 그냥 센서 자체가 문제인듯… 여러개 구입했는데 다 똑같음하여간 축적된 데이터는 중요하다 이말씀.. 이힛!

2021년 2월 17일

https://www.facebook.com/rtlink.park/posts/4337140246301979

3주간의 데이터 측정결과 – 이전 포스팅에서 중국산 센서의 정확성을 믿을 수가 없어서 믿을만한(?) 다른 중국산 토양 수분센서를 구해서 측정을 시작한지 대략 3주가 지났다. 센서는 문외한이지만 데이터를 가지고 판단해 본다면 화분1의 센서가 문제가 있다고 판단해도 되지 않을까 싶은데… 이런건 돈받고 팔고 있다니.. 나쁜 사람들 같으니라고… 그동안 투자한 내 시간은 어디서 보장받나 싶지만… 난 한가한 사람이니 참기로 한다. 앱의 UI를 바꿨는데 좀 예뻐진건지… 그게 그거 같기도 하고..

코로나 집콕 프로젝트 #1

의도

2020년은 코로나의 해였다. 년초에 시작한 코로나는 년말을 지나도록 잡히지 않았고 나는 하반기 내내 외부 프로젝트를 줄이고 집에 박혀서 더 많은 삽질을 했다. 이유없는 삽질에 결론을 만들기는 어렵긴 하지만 무언가 결과가 있어야겠다고 생각한게 12월이니… 오래도 결렸다.

하반기에 주로 많이한 일들이 IoT 디바이스를 가지고 놀아본거였고 거기에 Flutter를 이용해서 앱을 두어개 만들어 스토어에 업로드 해본 경험으로 IoT Network을 만들 수 있지 않을까? 하는 생각을 했고 순수하게 우리집안에 온습도 정도를 측정해보자는 생각으로 12월초에 프로젝트를 시도 했다.

12월 16일

페이스북에 뭔가 하고 있다고 공유를 시작했는데… 대략 아래와 같은 기술 스택으로 프로토 타입을 만들어 봤다. 그림에 보이는 보드는 5~6달러 정도의 ESP32 호환 보드로 여기에 MicroPython을 올려서 DHT11 온습도 센서를 연결하고 받은 데이터를 Wifi – MQTT 프로토콜을 이용해서 AWS EC2 서버에 Python & FASTApi 기반으로 개발된 서버에 전송한다.

서버에서 MQTT로 수신한 데이터는 MariaDB에 저장하고 FASTApi는 모바일앱에서 데이터를 요청하면 REST 기반으로 데이터를 보내준다.

앱은 Flutter & Dart 로 개발되었고 iOS & Android를 지원한다. 서버에 측정 데이터를 요청하고 받은 측정 데이터를 보여준다. 대략의 기술 스택은 아래와 같다.

ESP32, Raspberry PI + DHT11 + MicroPython + MQTT + AWS EC2 + MariaDB + Flutter

https://www.facebook.com/rtlink.park/posts/4162860857063253

위에 보이는 라즈베리파이 Zero에도 ESP32와 똑같은 기능의 Python 코드를 별도로 만들어 서버로 전송하고 앱에서 데이터를 조회 할 수 있다. 그렇게 3개의 온습도 측정기를 거실과 베란다에 설치하고 측정을 시작했다.

저렴한 ESP32보드(5달러짜리임..)를 사용한 탓인지 중국산 건전지 탓인지 아니면 둘다의 문제인지 3일이 못가서 건전지가 다 소모 되었고 결국 5V 아답터로 교체했다. ESP32는 하루 한번 정도 다운이 되었는데 코드 버그인지 보드 문제인지 알수가 없었다 – 나중에 알게 되었지만 코드에도 문제가 있었다. ^^;

12월 21일

https://www.facebook.com/rtlink.park/posts/4176739459008726

원래 온습도를 측정하기 시작한 이유는 코로나 시즌이니 가족들의 건강을 위하여 실내환경을 좀더 쾌적하게 유지해 보려는 의도였는데 데이터를 앱으로만 보고 있으니 접근성이 좋지 않았다. 그리고 앱을 계속 디버깅하고 있으니 가족들에게 앱을 설치해주는 일도 쉽지 않았다.

그래서 온습도 전용 디스플레이를 만들어야겠다고 생각하고 아래의 모니터링 장비를 만들었다. 이 모니터는 라즈베이파이4와 전용 디스플레이로 구성되어 있으며 라즈비안OS가 설치되어 있는데 개발은 Vue.js + Nuxt.js로 개발 하였고 웹브라우저를 Full Screen으로 오픈하는 방식으로 화면을 채우고 주기적으로 서버에 REST를 통해 데이터를 요청하고 디스플레이한다.

앱에는 설정된 온습도 범위에 따라 데이터 컬러가 변하는 정도의 알람(?)과 그래프가 추가 되어서 시간별 데이터를 조회 할 수 있게 되었다.

12월 28일

크리스마스에도 달렸다. 코딩말고 뭘 더할게 있겠는가? ㅋ

https://www.facebook.com/rtlink.park/posts/4194455677237104

알리익스프레스에서 거의 1달만에 도착한 토양수분측정기를 동백나무 화분에 설치했다. 이것 역시 ESP32기반의 모듈이라 온습도 측정을 위해 만든 모듈에 토양수분 측정 기능만 추가 했는데 토양 수분 모듈은 기기별로 데이터의 캘리브레이션이 필요한데 일단 이건 나중에 하기로 하고 일반적인 수식을 적용 했더니 데이터가 정확히 맞지는 않는듯 했지만 시간이 지날수록 수분이 줄어들고 있다는것을 확인 할 수 있었다.

앱은 한눈에 모든 기기의 현재 데이터를 확인 할 수 있도록 바꾸었고 불루투스를 이용해서 디바이스의 초기 데이터(디바이스 코드, Wifi SSID)를 셋팅 하기 위하여 Flutter에서 블루투스 전송 작업을 시작했고, 디바이스를 더 붙여 보려는 욕심에 인벤토리를 뒤져서 먼지측정 센서를 찾아냈다. 하지만…

12월 31일

올해 마지막날이라고 해서 들뜬 분위기로 하루를 보낼수는 없다!

알리에서 주문한 새 ESP32 디바이스가 6개 그리고 와이파이 안테나가 한뭉치 왔다. 안테나를 붙이니 보드에 붙어있던 안테나에 비해 도달거리가 늘어났고 그래서 세탁실에도 온습도 측정기를 하나 더 달았다. 그리고 작업중이던 미세먼지 측정기도 연결하기 위해 배선 작업을 했지만 측정기에 문제가 있었는지 아니면 배선을 잘못했는지 아니면 코딩을 잘못했는지… (원인이 너무 많아 디버깅불가) 하여간 데이터가 들어 오지 않았다.

미세먼지 측정기는 가진게 하나밖에 없어서 두번째 시도를 못하고 그냥 두고 있다.

2021년 1월 3일

https://www.facebook.com/rtlink.park/posts/4210535365629135

홈 IoT의 꽃은 리모트 컨트롤이다. 앱에 버튼을 눌러 전등을 끄고 켜거나.. 끄고 켜거나.. 끄고 켜거나… 그거말고 뭘하지?… 하여간 기왕에 하는거 이걸 안하면 안될것 같아서 리모트 컨트롤 기능을 추가했다.

서버에서로 MQTT를 이벤트 방식으로 수신하기 때문에 당연히 같은 방법으로 ESP32에서도 수신 할 수 있을거라고 생각했지만 ESP32에서는 이벤트 방식으로 되지 않았다. 쓰레드 루프를 만들고 계속 확인을 하면서 리모트 컨트롤 데이터를 수신해야 했다.

앱에서는 리모트 컨트롤 인터페이스를 기존의 측정기 화면과 동일한 화면을 쓰게 만들긴 했는데… 나중에 별도의 화면으로 만들어야 될것 같다.

2021년 1월 8일

https://www.facebook.com/rtlink.park/posts/4224526074230064

하다보면 욕심이 생긴다. 데이터만 볼게 아니고 화면도 볼 수 있을까? 인벤토리를 뒤져보니 ESP32-Cam이 나왔다. 이건 언제 삿지? 하여간 붙여야지…

그래서 온습도를 측정중인 동백꽃을 동영상으로 볼 수 있도록 기능을 추가 했는데 MicroPython으로 ESP32-CAM을 연동하는 셈플이 많지않아서 C코드 기반의 MJPEG 스트리밍을 송신하고 앱에서는 웹뷰로 스트리밍을 수신하도록 구성했다. 로컬 Wifi IP로 라우팅되고 있어서 외부에 나가서는 볼 수가 없어서 일단은 공유기에서 포트포워딩을 하도록 만들었다.

아래 링크를 보면 ngrok를 쓸수 있다는 내용이 있는데… 나중에 해봐야겠다.

삽질은 2021년에도 계속 된다…

내가 개발에 사용한 플러터 패키지

플러터 (Flutter)를 이용해서 앱을 개발하는데 패키지는 필수불가결한 요소입니다. 물론 온전히 기본제공하는 위젯(Widget)과 기능만으로 목적하는 기능을 구현할 수도 있겠지만 기본으로 지원하지 않거나 특별한 디자인과 기능을 원하는 요구사항을 만족시키기는 기본 패키지만으로는 매우 어렵습니다. 하지만 공개된 패키지를 잘 사용하면 쉽고 빠르게 목표물에 도달 할 수도 있습니다.

이 글에서는 내가 스캐니(Scanny – http://practical.kr/?p=71) 를 개발하면서 사용한 패키지들을 용도 위주로 정리했습니다. 여러 패키지들을 테스트해서 검토하고 실제 사용해서 릴리즈까지 해봤으니 개발자들에 도움이 되길 바랍니다.

패키지 검색 & 설치

플러터 패키지 검색은 https://pub.dev 에서 키워드를 입력하면 됩니다. 검색 결과에서 Popularity 숫자가 높은것을 선택하면 비교적 안심하고 쓸 수 있습니다. 하지만 자신에게 맞는것을 확인 하려면 결국 설치 해보고 테스트 해보는것이 최선입니다. 일반적으로 github 리파지터리에 셈플 프로젝트가 있으므로 다운 받아서 실행해보고 자신의 프로젝트에 적용하는 것이 좋습니다.

설치는 검색 결과에서 Installing 탭에 있는 dependencies아래에 있는 패키지명과 버전을 복사해서 자신의 프로젝xm 안에 pubspec.yaml 파일에 붙여넣고 터미널에서 아래와 같이 입력하면 설치됩니다.

> flutter pub get

그리고 실제 사용할 코드에서 아래와 같이 Import 해서 사용합니다.

import 'package:url_launcher/url_launcher.dart';

pub.dev 에는 패키지가 굉장히 많습니다. 기존에 iOS/Android를 지원하던 많은 오픈소스 패키지를 Flutter에 맞도록 포팅했기 때문에 거의 필요한건 웬만하면 다 있다고 생각하면 될것 같습니다.

개발에 사용한 패키지 목록

아래는 최근 릴리즈한 스캐니(Scanny)를 개발하며 사용 피키지 목록과 개략적인 설명

Provider - https://pub.dev/packages/provider

상태관리에 거의 필수적으로 쓰는 패키지
image_picker - https://pub.dev/packages/image_picker

사진촬영 과 사용자 앨범에서 이미지를 가지고 올때 쓴다. 파라메카 옵션을 카메라와 앨범을 선택 할 수 있고 해상도 조정도 가능합니다
http - https://pub.dev/packages/http

http 통신을 위해 사용합니다. Rest Api- GET, POST들을 이것으로 했습니다. 또한가지 많이 사용하는 패키지가 Dio(https://pub.dev/packages/dio)가 있는데 장단점이 있고 내가 보기엔 Dio가 조금 더 좋아보이지만 http를 먼저 사용하기 시작해서 그냥 이걸로 쓰고있습니다. 다음엔 Dio를 고려해볼 생각입니다.
photo_view - https://pub.dev/packages/photo_view

이미지 조회용 - 여러장의 이미지를 좌우 스와이프로 조회 할 수 있습니다. 이미지 앨범을 만들때 유용합니다.
modal_progress_hud - https://pub.dev/packages/modal_progress_hud

통신중이거나 처리중에 모달로 대기화면을 만들때 사용했습니다. 이런류의 프로그래스 컨트롤은 워낙 많지만 단순한게 제일 좋다고 생각합니다.
event_bus - https://pub.dev/packages/event_bus

컨트롤러 사이에 데이터를 주고 받을때 쉽게 사용할 수 있는 데이터버스. 사용상에 주의할 점은 싱글톤으로 개발해야 한다는 점입니다. 이벤트버스 클래스를 별도로 만들고 한번만 생성되도록 만들어야 데이터를 주고 받을 수 있습니다. 
toast - https://pub.dev/packages/toast

제목 그대로 토스트 - 간단한 메세지를 뿌립니다. 역시 심플한게 최고죠
path_provider - https://pub.dev/packages/path_provider

폴더명을 얻어올때 사용했습니다. 파일 읽기, 저장할때. pub.dev에 보면 Favorite아이콘이 붙어 있는 패키지가 있는데 이것도 그렇습니다. 좋은거라는 말이겠죠? ^^;
image_crop - https://pub.dev/packages/image_crop

말그대로 이미지 잘라내는 용도
flutter_cache_manager - https://pub.dev/packages/flutter_cache_manager

캐시된 파일을 읽어서 앨범에 저장하거나 공유하는 용도로 사용했습니다.
공유(Share)는 과거 버전에서는 소셜별로 기능별로 구현해야 했지만 최근 프레임워크가 통합관리되어 개발은 편해졌지만 커스터마이즈가 어려워진건 불만요소. 하지만 개발은 편합니다. 사용자 인터페이스도 일관성이 있고.. 아래 두가지 패키지가 미묘하게 기능이 달라서 두개를 한꺼번에 쓰게 되었습니다. 여기도 중국 개발자들이 많네요 중국어 코맨트... 영어로 좀 쓰지 말입니다. 번역기 좋은데 ㅋ

share_extend - https://pub.dev/packages/share_extend
esys_flutter_share - https://pub.dev/packages/esys_flutter_share
keyboard_attachable - https://pub.dev/packages/keyboard_attachable

UI 만들면서 좀 어려웠던것이 키보드와 뷰의 연동이었는데 앱의 키보드가 올라오면 하단의 버튼바가 같이 올라와야 하는데 기존에 디자인과 충돌이 있어서 이걸 사용했는데 구현방법에 차이가 있어서 디자인에 일관성이 나오지 않아 좀 애를 먹었지만 해결은 했습니다.  
easy_localization - https://pub.dev/packages/easy_localization

다국어 지원 - 다국어용 패키지가 많은데 이게 비교적 사용하기 쉽습니다. 언어별로 json 파일을 만들면 됩니다. 이말은 다른건 그보다 복잡하다는 말입니다. 스캐니는 일단 영어, 한글만 지원했습니다. 
url_launcher - https://pub.dev/packages/url_launcher

주어진 URL Schemes에 따라 브라우저를 띄우거나 이메일 클라이언트를 자동으로 호출해 줍니다. 매우 편해요.
package_info - https://pub.dev/packages/package_info

프로젝트 정보 - 버전, 패키지 이름, 빌드번호 등을 자져올 수 있습니다.

플러터 패키지는 잘 쓰면 매우 유용합니다. 하지만 너무 많고 옥석을 골라내기가 힘들기 때문에 꽤 많은 시간을 투자해서 나만의 패키지들을 구성하는게 중요한것 같습니다. 위에 사용한 패키지들의 실제 동작은 아래 링크의 앱 – 스캐니(Scanny) – 에서 확인 할 수 있습니다.

https://apps.apple.com/kr/app/id1529856835

박병일