[ReactJS] 학습해보자.

Create React App으로 테스트

노마더코딩 리액트 기초배우기
ReactJS를 보다가 여기까지 흘러들어왔다.

Create React App
흘러 들어온김에 좋은 기술하나 배워가보자. 쿨럭.
설치법은 간단하다

npx create-react-app my-app
cd my-app
npm start

이렇게 하면, 로컬페이지가 로드된다.

my-app내 App.js를 살펴보자

JSX는 리액트 컴포넌트를 만들때 사용되는 언어
아래와 같은 코드가 JSX라고 한다.

    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>

마치며

현재 위치: 리액트 기초 배우기 #5 ReactJS로 웹 서비스 만들기
내가 뭘하려다가 ReactJS까지 흘러왔는지 순간 까먹었다. 내가 하고 싶은건.. 아마존 NoSQL 사용이였는데 말이다... 쿨럭.
뭐 하는김에 제대로 하면 좋지. 취미생활코딩이니까...

오늘은 여기까지.

[Tips] Visual Studio Code Cmd로 실행하는 법

Visual Studio Code Cmd로 실행하는 법

  1. 비주얼 스튜디오 코드를 일단 실행 시킨다.
  2. Command Palette (⇧⌘P)를 실행한다.
  3. Shell command를 입력하면, Shell Command: Install ‘code’ command in PATH command가 노출된다.
  4. 노출 부분을 클릭한다.
    해당이미지
  5. 이제 커맨드창에서 code를 입력하면 비주얼스튜디오 코드가 실행된다.

[iOS] SceneDelegate에 대한 학습

SceneDelegate

AppDelegate & SceneDelegate

  • AppDelegate

iOS 13 미만

Process LifeCycle UI LifeCycle
App Launched Entered Foreground
App Terminate Became Active
... ...

iOS 13이상

AppDelegate SceneDelegate
Process LifeCycle UI LifeCycle
Scene LifeCycle Entered Foreground
Session Created Became Active
Session Discarded

iOS12까지는 하나의 앱은 하나의 window로 구성되어 있었다.
iOS13부터는 window의 개념이 scene으로 변경되었으며, 하나의 앱은 여러개의 scene을 가질수 있게 되었다.
다시 말해 하나의 앱에서 여러개의 scene을 보여줄수 있게 되었다.

SceneDelegate

AppDelegate의 역할 중 UI의 상태를 알 수 있는 UILifeCycle에 대한 부분을 SceneDelegate가 하게 되었다.

SceneDelegate의 생명주기

  1. [SceneDelegate] scene(_:, willConnectTo:, options:)

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
    // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
    // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
    guard let _ = (scene as? UIWindowScene) else { return }
    }

    scene이 앱에 추가될 때 호출. 단 여기서 ViewController와 같은 클래스 객체를 만들어 사용할 때, 아직 viewDidLoad()가 호출되지 않음

  2. sceneDidDisconnect(_ 🙂

    func sceneDidDisconnect(_ scene: UIScene) {
    // Called as the scene is being released by the system.
    // This occurs shortly after the scene enters the background, or when its session is discarded.
    // Release any resources associated with this scene that can be re-created the next time the scene connects.
    // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
    }

    scene의 연결이 해제될 때 호출

  3. sceneDidBecomeActive(_: )

    func sceneDidBecomeActive(_ scene: UIScene) {
    // Called when the scene has moved from an inactive state to an active state.
    // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
    }

    app switcher에서 선택되는 등 scene과의 상호작용이 시작될 때 호출

    • app switcher : 홈버튼을 두번누르거나 아이폰 하단에서 위로 스와이프 했을 때 현재 실행중인 앱들이 보이는 화면
  4. sceneWillResignActive(_:)

    func sceneWillResignActive(_ scene: UIScene) {
    // Called when the scene will move from an active state to an inactive state.
    // This may occur due to temporary interruptions (ex. an incoming phone call).
    print("[SceneDelegate] sceneWillResignActive(_ scene: UIScene)")
    }

    사용자가 scene과의 상호작용을 중지할 때 호출(다른 화면으로의 전환과 같은 경우)

  5. sceneWillEnterForeground(_:)

    func sceneWillEnterForeground(_ scene: UIScene) {
    // Called as the scene transitions from the background to the foreground.
    // Use this method to undo the changes made on entering the background.
    print("[SceneDelegate] sceneWillEnterForeground(_ scene: UIScene)")
    }

    scene이 foreground로 진입할 때 호출

  6. sceneDidEnterBackground(_:)

    func sceneDidEnterBackground(_ scene: UIScene) {
    // Called as the scene transitions from the foreground to the background.
    // Use this method to save data, release shared resources, and store enough scene-specific state information
    // to restore the scene back to its current state.
    print("[SceneDelegate] sceneDidEnterBackground(_ scene: UIScene)")
    }

    scene이 background로 진입할 때 호출

AppDelegate 생명주기

AppDelegate - Scene LifeCycle

AppDelegate에는 Session LifeCycle에 대한 역할이 추가되었다.
SceneSession은 앱에서 생성한 모든 scene의 정보를 관리한다.

// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
    // Called when a new scene session is being created.
    // Use this method to select a configuration to create the new scene with.
    return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}

func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
    // Called when the user discards a scene session.
    // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
    // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}

Scene?

UIKit은 UIWindowScene 객체를 사용하는 앱 UI의 각 인스턴스를 관리한다. Scene에는 UI의 하나의 인스턴스를 나타내는 windows와 view controllers가 들어있다. 또한, 각 scene에 해당하는 UIWindowSceneDelegate 객체를 가지고 있고, 이 객체는 UIKit과 앱 간의 상호작용을 조정하는데 사용된다.
Scene들은 같은 메모리와 앱 프로세스 공간을 공유하면서 서로 동시에 실행된다. 결과적으로 하나의 앱은 여러 Scene과 Scene delegate 객체를 동시에 활성화 할 수 있다.
(Scenes - Apple Development Document)

Scene Session?

UISceneSession 객체는 Scene의 고유의 런타임 인스턴스를 관리한다. 사용자가 앱에 새로운 Scene을 추가하거나 요청하면, 시스템은 그 Scene을 추적하는 session 객체를 생성한다. 그 Session에는 고유한 식별자와 scene의 구성 세부사항(configuration details)가 들어있다.
UIKit은 session 정보를 그 Scene 자체의 생명주기동안 유지하고 app switcher에서 사용자가 그 Scene을 클로징하는 것에 대응하여 그 session을 파괴한다.
session 객체는 직접 생성하지 않고 UIKit이 앱의 사용자 인터페이스에 대응하여 생성한다.
또한, 아래 함수를 통해서 UIKit에 새로운 Scene과 Session을 프로그래밍적 방식으로 생성할 수 있다.
(UISceneSession - Apple Development Document)

func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration 
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>)

iOS13부터 AppDelegate의 역할

  1. 앱의 데이터 구조를 초기화 하는 것
  2. 앱의 Scenes의 환경설정(Configuration)
  3. 앱 밖에서발생한 알림(베터리 부족, 다운로드 완료 등)에 대응
  4. 특정한 Scenes, views, view controllers에 한정되지 않고 앱 자체를 타겟하는 이벤트에 대응하는 것
  5. 애플 푸쉬 알림 서브스와 같이 실행시 요구되는 모든 서비스를 등록
    (UIApplicationDelegate - Apple Development Document)

Deployment Target이 iOS 13 미만인 상황에서는?

iOS12이하는 단일 window 환경임으로, 아래와 같이 Scene처리를 제거할 수 있다.

  1. SceneDelegate.swift 제거
  2. AppDelegate에서 UISceneSession 관련 두개의 메소드 제거
  3. SceneDelegate로 옮겨진 window 프로퍼티를 AppDelegate로 옮기기
    var window: UIWindow?
  4. info.plist에서 Scene과 관련된 Manifest인 Application Scene Manifest 삭제

GRDB를 사용하다가 생긴 문제점 공유

GRDB를 사용하다보니 JOIN시에 해당 구조체를 가져오는게 쉽지 않다는 문제점을 발견했다.
이를 해결하기 위해, 검색을 하다보니 GRDB를 더 깊게 이해해야 한다는 문제점에 도달했다.

Sqlite3으로 모든 작업을 돌리기로 결정.
이유는 Sqlite3은 내가 맘대로 만들고, 제어도 더 쉽다고 생각했다.
Join과 cascade등의 처리를 위해서는 Sqlite3을 직접 사용하는게 더 좋은것 같다.

일단, Sqltie3으로 개발을 완료하고 나서 추후 nosql을 사용해보겠다.

오랫만에 개인 프로젝트를 하나 해보고 있는데, 트랜드를 다시 익히려다 보니 온갖 삽질을 다 하고 있다..

GRDB를 사용해서 대부분의 것들도 사용이 가능한거 같긴한데, 사전 지식이 꽤 많이 필요한거 같다.

RxSwift 쉽지 않구나.

Swift를 지나고 Reactive를 지나서 가는 여정이 참으로 험난하다.
뭔가 깨달은것 같으면 다시 막힘에 들어서는 반복중이다.

간단한걸 RxSwift MVVM으로 만드는게 이렇게 고통스러울줄이야...
좀 자료를 정리해서 글을 남겨야 할것 같다.

어떻게 글을 정리하고 글을 써야 할지 고민을 좀 해봐야 겠다.
정리가 안된다.. ㅠ

RxTableView

RxSwift를 학습은 했는데, 쓸 때마다 기억이 안난다.
아직 익숙해지지 않아서일까.. 그래서 정리해본다.

어떻게 정리해봐야 할까 고민을 해보아야 할것 같다.
일단 방식만 나열하는 걸로...

총 네가지 방식의 사용법이 존재한다.

1. tableView.rx.items 사용하기

    func bindingTableViewItems01() {
        let cities = ["01", "L", "K"]
        let citiesOb: Observable<[String]> = Observable.of(cities)
        citiesOb.bind(to: tableView.rx.items) { (tableView: UITableView, index: Int, element: String) -> UITableViewCell in
            guard let cell = tableView.dequeueReusableCell(withIdentifier: TitleCell.identifier) as? TitleCell else {
                return UITableViewCell()
            }
            cell.title?.text = element
            
            // CellType 변경
            // element 타입을 기준으로 셀을 리턴 가능

            return cell
        }
        .disposed(by: disposeBag)
    }

2. tableView.rx.items(cellIdentifier:String) 사용하기


    func bindingTableViewItems02() {
        let cities = ["02", "L", "K"]
        let citiesOb: Observable<[String]> = Observable.of(cities)
        citiesOb.bind(to: tableView.rx.items(cellIdentifier: TitleCell.identifier))
        { (index: Int, element: String, cell: UITableViewCell) in
            if let cell = cell as? TitleCell  {
                cell.title.text = element
            }
        }
        .disposed(by: disposeBag)
    }
   

3. tableView.rx.items(cellIdendifier:String,cellType:Cell.Type) 사용하기


    func bindingTableViewItems03() {
        let cities = ["03", "L", "K"]
        let citiesOb: Observable<[String]> = Observable.of(cities)
        citiesOb.bind(to: tableView.rx.items(cellIdentifier: TitleCell.identifier, cellType: TitleCell.self))
        { (index: Int, element: String, cell: TitleCell) in
            cell.title.text = element
            cell.textLabel?.text = element + "     " + element
        }
        .disposed(by: disposeBag)
    }

4. tableView.rx.items(dataSource:protocol<RxTableViewDataSourceType, UITabelViewDataSource>) 사용하기


    // tableView를 어떻게 표현할지 미리 지정한 datasource를 사용한 방법
    // pod으로 RxDataSource를 설치
    // TODO: 참고 자료 https://github.com/RxSwiftCommunity/RxDataSources
    func bindingTableViewItems04() {
        // RxDataSource에서는 SectionModelType을 따르는 SectionModel을 이미 구현해 놓았는데, 이것을 사용하면 된다.
        typealias CitySectionModel = SectionModel<String, String>
        typealias CityDataSource = RxTableViewSectionedReloadDataSource<CitySectionModel>
        
        let cities = ["03", "L", "K", "L", "K", "L", "K", "L", "K", "L", "K"]
        let sections = [
            CitySectionModel(model: "first section", items: cities),
            CitySectionModel(model: "second section", items: cities)
        ]
        
        let configureCell: (TableViewSectionedDataSource<CitySectionModel>, UITableView, IndexPath, String) -> UITableViewCell = {
            (datasource, tableView, indexPath, element) in
            guard let cell = tableView.dequeueReusableCell(withIdentifier: TitleCell.identifier, for: indexPath) as? TitleCell else {
                return UITableViewCell()
            }
            cell.title.text = element
            return cell
        }
        let datasource = CityDataSource.init(configureCell: configureCell)

        datasource.titleForHeaderInSection = { datasource, index in
            return datasource.sectionModels[index].model
        }
                
        Observable.just(sections)
            .bind(to: tableView.rx.items(dataSource: datasource))
            .disposed(by: disposeBag)
    }

무료 폰트를 찾아서..

앱을 만들려다 보니 저작권들이 거슬린다.
경제적 자유! 그것을 위한 우리의 여행은 끝이 나지 않는다..

아래 링크들은 유투버를 통해서 알게 된 링크이다.
라이센스를 잘 읽어보고, 글꼴을 사용하길 바란다.
경우에 따라 유료인것도 있어서...

1.검은고딕 https://github.com/zesstype/Black-Han...

2.에스코어드림 http://www.s-core.co.kr/who-we-are/font/

3.몬소리체 https://brunch.co.kr/@creative/32

4.Noto Sans https://fonts.google.com/specimen/Not...

5.잉크립퀴드체 http://www.thefaceshop.com/event/lipq...

6.tvn 즐거운 이야기체 http://tvn10festival.tving.com/playgr...

7.스웨거체 http://www.swagger.kr/font.html

8.주아체 http://font.woowahan.com/

눈누 https://noonnu.cc/

구글폰트 https://fonts.google.com/

  • 참고 유튜버 영상 링크
  • https://www.youtube.com/watch?v=7gOiGK83X-o

@escaping

다음과 같은 블럭함수를 사용할 경우에, 에러가 발생한다.
"Escaping closure captures non-escaping parameter 'completion'"

    func testFunc(_ completion: (String?) -> Void ) {
        DispatchQueue.global().async {
            print("global something...")
            
            DispatchQueue.main.async {
                completion("End")
            }
        }
    }

원인은 testFunc이 종료되고 나서 completion이 호출되는데, 호출이 불가하기 때문이다.
이러한 경우에 @escaping 을 선언해주면 정상 동작을 한다.

    func testFunc(_ completion: @escaping (String?) -> Void ) {
        DispatchQueue.global().async {
            print("global something...")
            
            DispatchQueue.main.async {
                completion("End")
            }
        }
    }

여기까지는 모두 알고 있는 항목이였을 것이다.
굳이 뭐 이런 글을이라며....
하지만, 오늘 배운게 하나 있는데, completion이 옵셔널로 선언된다면, default값으로 @escaping이 설정된다는 것이였다.


다음과 같이 코드 작성이 가능하다는 말이다.

    func testFunc(_ completion: ((String?) -> Void)? ) {
        DispatchQueue.global().async {
            print("global something...")
            
            DispatchQueue.main.async {
                completion?("End")
            }
        }
    }

난 분명 까먹을것이기 때문에, 기록을 남긴다.

protocol Equatable

글 작성중입니다.
글 하나 작성하는데 상당히 오랜 시간이 필요하네요..
곧 마무리 해서 올리겠습니다.

iOS 개발을 하다보면 꽤 많은 프로토콜들이 등장한다.
대부분의 개발자들은 별 생각없이 넘어갈것이라고 생각한다.
그러다가 어느순간에 이건 도대체 뭐지? 라는 의문이 들것이고 이에 대해 알고 싶어질것이라 생각한다.
지금 내가 그렇고, 매번 궁금한게 생길때마다 다시 찾아보는 수고를 덜기 위해 정리해보려고 한다.

equatable| ikwéitəbl | 형용사 | 동일시할 수 있는.

아래 애플링크에 있는 글들을 정리를 좀 해볼께요.
좀 보기 편하게 제 스타일대로 의역을 좀 해놨습니다.
틀린부분이 있다면 알려주세요.

Equatable

Type의 같음을 비교할 수 있다.

개요

Equatable 프로토콜을 따르는 Type들은 equal-to operator (==) 또는 불균형인지를 not-equal-to operator (!=)를 사용하여 비교 할수 있다.

배열와 컬렉션은 Equatable을 만족할때, 쉽게 비교를 수행할수 있다.
예를 들면 다음과 같다.
배열의 요소들이 Equatable을 만족할 때, 제공되는 closure를 사용하여 비교하지 않고, 특별한 값을 포함하고 있는지를 contains(_:) 메소드를 사용하여 체크할수 있다.

let students = ["Kofi", "Abena", "Efua", "Kweku", "Akosua"]

let nameToCheck = "Kofi"
if students.contains(nameToCheck) {
    print("\(nameToCheck) is signed up!")
} else {
    print("No record of \(nameToCheck).")
}
// Prints "Kofi is signed up!"

Equatable Protocol을 따르기

Equatable을 당신이 만든 Type에 추가한다는 것은, Collection에서 특별한 인스턴스를 찾기 위해 convenient APIs를 사용한다는 뜻이다.
Equatable은 base protocol이며, Hashable과 Comparable protocols에도 사용된다.
그리고, Collection의 요소들을 sets과 sorting하여 구성하는 것처럼, custom type을 사용할수 있게 해준다.

Equtable을 만족시며 type를 생성하기 위해서는 다음 두가지를 충족시켜야 한다.

  • Struct일 경우, 모든 properties가 Equatable을 만족 시켜야 한다.
  • Enum일 경우, 모든 value들이 Equatable을 만족 시켜야 한다.
    (value없는 enum은 선언없이 만족된다. 맞는 해석인지 모르겠다. 테스트 필요.)

작성중...

  • 참고 링크
  • https://developer.apple.com/documentation/swift/equatable

Pod install시 에러 발생 [ParserError – 767]

ParserError - 767: unexpected token at ' ' #10088

Pod 설치시 에러가 나면 곤란한 경우가 많다.
처음보는 에러가 하나 발생했다.
pod repos와 캐시를 클린해주면 해결이 된다.

해결방법은 다음과 같다.

rm -rf ~/.cocoapods/repos
pod cache clean --all
pod install
  • 참고 링크
  • https://github.com/CocoaPods/CocoaPods/issues/10088