├── CS
├── ETC
│ ├── WWDC2022.md
│ ├── Framework&Library.md
│ ├── 아키택처패턴과_디자인패턴이란?.md
│ ├── clean_code_00.md
│ ├── 깨끗한코드란.md
│ ├── clean_code_01.md
│ └── fastlane.md
├── 네트워크
│ ├── OSI_7_Layer.md
│ ├── TCP&UDP.md
│ ├── README.md
│ ├── HTTP와Socket.md
│ ├── web_http.md
│ ├── 캡슐화&역캡슐화.md
│ ├── HTTPMethod.md
│ ├── session_cookie.md
│ ├── HTTP와HTTPS.md
│ └── 3wayHandshake_4wayHandshake.md
├── 운영체제
│ ├── README.md
│ ├── Memory.md
│ ├── MultiProcessing과MultiThreading.md
│ ├── Stack&Heap.md
│ ├── ThreadSafe.md
│ └── Thread&Process.md
├── 자료구조
│ ├── README.md
│ ├── Trie.md
│ ├── persistent.md
│ ├── dynamicArrayInC.md
│ ├── dynamicMemoryInC.md
│ ├── algebraic_data_type.md
│ └── hashtable.md
├── 알고리즘
│ ├── 이진탐색트리.md
│ ├── Greedy_Ex.swift
│ ├── README.md
│ ├── 기준에_따라_데이터를_정렬.md
│ ├── 정렬.md
│ ├── 탐욕법(Greedy).md
│ ├── 트리.md
│ ├── 선택정렬.md
│ ├── DFS&BFS.swift
│ ├── 완전탐색.md
│ ├── 이진탐색.md
│ └── BinarySearch_Ex.swift
├── 데이터베이스
│ ├── deadlock.md
│ ├── commit_&_rollback.md
│ ├── db_view.md
│ ├── denormalization.md
│ ├── db가필요한이유.md
│ └── db_normal_form.md
├── 아키택처패턴
│ ├── README.md
│ ├── MVC.md
│ └── MVVM.md
└── 패러다임
│ ├── OOP_vs_FP.md
│ ├── first_class_citizen.md
│ ├── FP.md
│ └── pure_function.md
├── Python
├── README.md
├── 1-1_알고리즘이란.md
└── 1-2_반복하는알고리즘.md
├── Swift
├── Optional.md
├── Enumeration.md
├── Collection_Type.md
├── final.md
├── Override&Overload.md
├── protocol지향언어.md
├── array_subscript.md
├── ==_vs_===.md
├── unknown_default.md
├── dynamicDispatch.md
├── alamofire_request_url_print.md
├── map_compactMap_flatMap.md
├── typeCasting.md
├── compare_countZero_and_isEmpty.md
├── 기존방법과_async_await의차이.md
├── mutating.md
├── EscapingClosure.md
├── URLCache.md
├── regEx.md
├── array_set_dictionary.md
├── iterator.md
├── docs_translate_types.md
├── about_string.md
├── big_o_in_swift.md
└── hashable_equatable_comparable.md
├── iOS
├── Sync_Async.md
├── Storage_Modifier.md
├── priority.md
├── SceneDelegate.md
├── README.md
├── view_draw_layout_cycle.md
├── viewLifeCycle.md
├── layoutSubviews.md
├── LifeCycle.md
├── appLifeCycle.md
├── accessibilityIdentifier.md
├── rootViewController.md
├── data_prefetching.md
├── presentedVC_presentingVC.md
├── imageCachingExtension.md
├── loadView_vs_viewDidLoad.md
├── custom_shadow.md
├── ARC_vs_GC.md
├── deeplink_vs_universalLink.md
├── GCD&Operation.md
├── keyboardIssue_UIView_Extension.md
├── WebView_method.md
├── GCD_EX.md
├── dispatchGroup.md
├── universalLink.md
├── urlSession.md
└── present_dismiss_transition_smooth.md
├── links
└── README.md
├── LICENSE
└── .gitignore
/CS/ETC/WWDC2022.md:
--------------------------------------------------------------------------------
1 | # WWDC 2022 요약
2 |
3 |
--------------------------------------------------------------------------------
/CS/네트워크/OSI_7_Layer.md:
--------------------------------------------------------------------------------
1 | # OSI 7 Layer
2 |
3 |
--------------------------------------------------------------------------------
/CS/운영체제/README.md:
--------------------------------------------------------------------------------
1 | # 운영체제
2 |
3 | ## INDEX
4 |
--------------------------------------------------------------------------------
/CS/자료구조/README.md:
--------------------------------------------------------------------------------
1 | # 자료구조
2 |
3 | ## INDEX
4 |
--------------------------------------------------------------------------------
/CS/알고리즘/이진탐색트리.md:
--------------------------------------------------------------------------------
1 | # 이진탐색트리 (Binary Search Tree)
2 |
3 |
--------------------------------------------------------------------------------
/CS/자료구조/Trie.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keenkim1202/KEENs_TIL/HEAD/CS/자료구조/Trie.md
--------------------------------------------------------------------------------
/CS/네트워크/TCP&UDP.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keenkim1202/KEENs_TIL/HEAD/CS/네트워크/TCP&UDP.md
--------------------------------------------------------------------------------
/Python/README.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keenkim1202/KEENs_TIL/HEAD/Python/README.md
--------------------------------------------------------------------------------
/Swift/Optional.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keenkim1202/KEENs_TIL/HEAD/Swift/Optional.md
--------------------------------------------------------------------------------
/iOS/Sync_Async.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keenkim1202/KEENs_TIL/HEAD/iOS/Sync_Async.md
--------------------------------------------------------------------------------
/Python/1-1_알고리즘이란.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keenkim1202/KEENs_TIL/HEAD/Python/1-1_알고리즘이란.md
--------------------------------------------------------------------------------
/Swift/Enumeration.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keenkim1202/KEENs_TIL/HEAD/Swift/Enumeration.md
--------------------------------------------------------------------------------
/CS/알고리즘/Greedy_Ex.swift:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keenkim1202/KEENs_TIL/HEAD/CS/알고리즘/Greedy_Ex.swift
--------------------------------------------------------------------------------
/Python/1-2_반복하는알고리즘.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keenkim1202/KEENs_TIL/HEAD/Python/1-2_반복하는알고리즘.md
--------------------------------------------------------------------------------
/iOS/Storage_Modifier.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keenkim1202/KEENs_TIL/HEAD/iOS/Storage_Modifier.md
--------------------------------------------------------------------------------
/Swift/Collection_Type.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/keenkim1202/KEENs_TIL/HEAD/Swift/Collection_Type.md
--------------------------------------------------------------------------------
/Swift/final.md:
--------------------------------------------------------------------------------
1 | # final 키워드
2 |
3 | - 상속방지
4 | - final 키워드가 붙은 클래스를 상속할 수 없다.
5 | - 혹은 상속받은 클래스 안의 일부 함수를 override 하지 못하게 하고 싶을 때 사용한다.
6 |
--------------------------------------------------------------------------------
/CS/자료구조/persistent.md:
--------------------------------------------------------------------------------
1 | # Persistent Data Structure
2 |
3 | - 내용 정리 준비중
4 |
5 |
6 | ## 참고 자료
7 | - [Persistent data structure - Wiki](https://en.wikipedia.org/wiki/Persistent_data_structure)
8 | - [자료구조 B-Tree](https://ssocoit.tistory.com/217)
9 |
--------------------------------------------------------------------------------
/CS/알고리즘/README.md:
--------------------------------------------------------------------------------
1 | # 알고리즘
2 |
3 | ## INDEX
4 | - [탐욕법](탐욕법(Greedy).md)
5 | - [예제풀이](Greedy_Ex.swift)
6 |
7 | - [완전탐색](완전탐색.md)
8 | - 순열
9 | - 재귀
10 | - BFS
11 | - DFS
12 | - 비트마스크
13 |
14 | - DP
15 | - [BFS & DFS](DFS&BFS.swift)
16 |
--------------------------------------------------------------------------------
/CS/데이터베이스/deadlock.md:
--------------------------------------------------------------------------------
1 | # 교착상태 (Deadlock)
2 |
3 | > 교착상태란?
4 |
5 | 2개 이상의 트랜잭션이 특정 자원(테이블 또는 행)의 잠금(Lock)을 획득한 채 다른 트랜잭션이 소유하고 있는 잠금을 요구하면 아무리 기다려도 상황이 바뀌지 않는 상태가 되는데 이를 교착상태 라고 한다.
6 |
7 | > 교착상태를 방지하기 위한 방법
8 |
9 | - 트랜잭션을 자주 커밋한다.
10 | - 정해진 순서로 테이블에 접근한다.
11 | - `SELECT` ~ `FOR UPDATE` 의 사용을 피한다.
--------------------------------------------------------------------------------
/CS/데이터베이스/commit_&_rollback.md:
--------------------------------------------------------------------------------
1 | # commit과 rollback 이란?
2 |
3 | > 커밋(Commit)
4 |
5 | : 모든 부분작업이 정상적으로 완료하면 이 변경사항을 한꺼번에 DB에 반영
6 | 작성한 쿼리문에서 Update, Delete, Insert를 수행했을 때, 그 쿼리문 수행결과에 대해 확정을 짓겠다는 뜻이다.
7 |
8 | > 롤백(Rollback)
9 |
10 | : 부분 작업이 실패하면 트랜잭션 실행 전으로 되돌려 쿼리문 수행결과에 대해 번복을 함.
11 | 즉, 쿼리문 수행 이전으로 원상복귀 하겠다는 뜻이다.
12 | (Commit 하기 전에 사용됨)
--------------------------------------------------------------------------------
/Swift/Override&Overload.md:
--------------------------------------------------------------------------------
1 | # Override 와 Overload
2 |
3 | - `override` : 재정의
4 | - `overload` : 인자갯수가 다를 경우 처리.
5 |
6 | # Overriding & Overloading
7 | - `overriding`는 상속 관계에서 같은 기능을 사용하지만 함수 내용이 다른 경우 자식 클래스에서 함수를 재 정의 하는 것.
8 | - `overloading`은 리턴타입, 인자개수, 인자타입이 다를 경우 함수를 재정의 하는 것,
9 |
10 | → overriding이 가능하려면 함수이름, 리턴타입, 매개변수가 모두 동일해야 한다.
11 |
--------------------------------------------------------------------------------
/CS/데이터베이스/db_view.md:
--------------------------------------------------------------------------------
1 | # DB에서의 View
2 |
3 | > 데이터베이스 뷰란?
4 |
5 | : 허용된 데이터를 제한적으로 보여주기 위해 하나 이상의 테이블에서 유도된 가상 테이블입니다.
6 |
7 | > 장점
8 |
9 | - 뷰의 데이터가 저장되는 물리적 위치가 없으므로 리소스를 낭비하지 않고 출력을 생성합니다.
10 | - 삽입, 업데이트 및 삭제와 같은 명령을 허용하지 않으므로 데이터 액세스가 제한됩니다.
11 |
12 | > 단점
13 |
14 | - 해당 뷰와 관련된 테이블을 삭제하면 뷰가 관련이 없습니다.
15 | - 큰 테이블에 대해 뷰를 만들 때 더 많은 메모리가 사용됩니다.
--------------------------------------------------------------------------------
/iOS/priority.md:
--------------------------------------------------------------------------------
1 | # ContentHuggingPriority
2 |
3 | ```swift
4 | someLabel.setContentHuggingPriority(.required, for: .vertical)
5 | ```
6 |
7 | - required
8 | - defaultHigh
9 | - defaultLow
10 |
11 | # ContentCompressionResistancePriority
12 | ```swift
13 | someLabel.setContentCompressionResistancePriority(.required, forAxis: .Horizontal)
14 | ```
15 |
16 |
--------------------------------------------------------------------------------
/CS/네트워크/README.md:
--------------------------------------------------------------------------------
1 | # 네트워크
2 |
3 | ## INDEX
4 | - [TCP & UDP](TCP&UDP.md)
5 | - [HTTP/HTTPS](HTTP와HTTPS.md)
6 | - [HTTP와 Socket](HTTP와Socket.md)
7 | - [HTTP Method](HTTPMethod.md)
8 | - [OSI 7 Layer](OSI_7_Layer.md)
9 | - [캡슐화와 역캡슐화](캡슐화&역캡슐화.md)
10 | - [세션과 쿠키란 무엇엇인가](session_cookie.md)
11 |
12 | ## 참고자료
13 | - [네트워크 질문리스트](https://juicyjerry.tistory.com/196)
14 |
--------------------------------------------------------------------------------
/links/README.md:
--------------------------------------------------------------------------------
1 | # Link 모음
2 |
3 | ## Swift
4 | - [Swift: Why You Should Avoid Using Default Implementations in Protocols](https://betterprogramming.pub/swift-why-you-should-avoid-using-default-implementations-in-protocols-eeffddbed46d)
5 |
6 | ## iOS
7 |
8 |
9 | ## WWDC
10 | - [Getting Started with Instruments - WWDC19](https://developer.apple.com/videos/play/wwdc2019/411/)
11 |
12 | ## CS
13 |
14 |
15 | ## ETC
16 |
--------------------------------------------------------------------------------
/Swift/protocol지향언어.md:
--------------------------------------------------------------------------------
1 | # protocol 지향 언어로써의 Swift의 특징
2 |
3 | - 객체지향 프로그램 패러다임에 기반을 둔 언어들은 클래스의 상속을 통하여 공통 기능을 모듈화한다.
4 | - 스위프트는 표준 라이브러리의 대부분이 `Struct`로 구성되어있다.
5 | - 상속을 지양한다.
6 | - 상속의 경우 규모가 커질 경우 관리가 어렵다.
7 |
8 |
9 |
10 | - 어떻게 상속이 되지 않는 구조체 기반에서 공통, 다양한 기능을 구현할까??
11 | - `프로토콜`, `익스텐션` 을 사용한다!
12 |
13 |
14 |
15 | - 의존성 주입, 의존성 분리(프로토콜)을 구성하기 유리
16 | - 오직 프로토콜이 요구하는 변수와 함수 즉 각각은 독립적, 이를 통해 공통의 프로토콜을 따름
17 |
--------------------------------------------------------------------------------
/iOS/SceneDelegate.md:
--------------------------------------------------------------------------------
1 | # SceneDelegate 에 대하여
2 |
3 | - iOS13 부터 생겨난 것으로 이전에는 AppDelegate만 존재했다.
4 | - AppDelgate의 역할을 process 생명주기(시작과 종료)와 UI 생명주기(UI 상태)를 관리할 수 있다.
5 | → 이전까지는 앱은 오직 하나의 프로세스와 그에 따른 하나의 UI만 가졌기 떄문이다.
6 |
7 |
8 |
9 | - iOS13부터 window의 개념이 scene으로 대체되었는데, 아이패드에서 한 화면에 두개의 창을 띄우듯이 여러개의 scene을 가질 수 잇게되었다.
10 | - AppDelegate에서 Session 생명주기에 대한 역할이 추가되어 scene session의 생성 삭제를 알리는 메서드가 추가되었고, 앱에서 생성한 모든 scene의 정보를 관리한다.
11 |
--------------------------------------------------------------------------------
/Swift/array_subscript.md:
--------------------------------------------------------------------------------
1 | # Array 에서 subscript를 사용하여 Index Out of Range 방지하기
2 |
3 | ```swift
4 | extension Array {
5 | subscript(safe index: Int) -> Element? {
6 | guard index >= 0, index < endIndex else {
7 | return nil
8 | }
9 |
10 | return self[index]
11 | }
12 | }
13 |
14 | var someArr: [Int] = [1,2,3,4,5,6]
15 |
16 | someArr[safe: -1] // nil
17 | someArr[safe: 10] // nil
18 | someArr[safe: 4] // 5
19 | ```
20 |
21 | - subscript를 사용하면 nil 이 반환되므로 `Index Out of Range` 가 발생하지 않는다.
22 |
--------------------------------------------------------------------------------
/iOS/README.md:
--------------------------------------------------------------------------------
1 | # iOS
2 |
3 | ## INDEX
4 |
5 | - [View Life-Cycle](iOS/viewLifeCycle.md)
6 | - [loadView()와 viewDidLoad() 의 차이는?](iOS/loadView_vs_viewDidLoad.md)
7 | - [App Life-Cycle](iOS/appLifeCycle.md)
8 | - [ARC & GC](iOS/ARC_vs_GC.md)
9 | - [ARC & Retain Cycle 예제](https://nareunhagae.tistory.com/61) `Blog`
10 | - [Storage Modifier (Strong, Weak, Unowned)](iOS/Storage_Modifier.md)
11 | - [SceneDelegate란?](iOS/SceneDelegate.md)
12 | - [rootViewController란?](iOS/rootViewController.md)
13 | - [URLSession](iOS/urlSession.md) `🚧`
14 |
--------------------------------------------------------------------------------
/CS/데이터베이스/denormalization.md:
--------------------------------------------------------------------------------
1 | # 반정규화(De-Normalization)
2 |
3 | > 반정규화란
4 |
5 | 시스템의 성능 향상, 개발 및 운영의 편의성 등을 위해 정규화된 데이터 모델을 통합, 중복, 분리하는 과정으로, 의도적으로 정규화 원칙을 위배하는 행위이다.
6 |
7 | - 데이터 무결성이 깨질 수 있는 위험을 무릅쓰고 데이터를 중복하여 반정규화를 적용하는 이유는 데이터를 조회할 때
8 | - 디스크 I/O량이 많아서 성능이 저하되거나 경로가 너무 멀어 조인으로 인한 성능저하가 예상되거나 칼럼을 계산하여 읽을 때
9 | 성능이 저하될 것이 예상되는 경우 반정규화를 수행하게 된다.
10 |
11 | > 반정규화 방법에는
12 |
13 | 테이블 통합, 테이블 분할, 중복 테이블 추가, 중복 속성 추가 등이 있음
14 |
15 | > 반정규화를 수행하는 주된 경우
16 |
17 | - 다량의 범위를 자주 처리해야하는 경우
18 | - 특정 범위의 데이터만 자주 처리하는 경우
19 | - 요약/집계 정보가 자주 요구되는 경우
--------------------------------------------------------------------------------
/iOS/view_draw_layout_cycle.md:
--------------------------------------------------------------------------------
1 | # View Layout Cycle
2 |
3 | ### 화면에 뷰가 뜨기까지의 생명주기 상세 순서
4 |
5 |
6 |
7 |
8 | > `requiresConstraintBasedLayout` > `loadView` > `viewDidLoad` > `viewWillAppear` >
9 |
10 | > `updateConstraints` > `instrinsicContentSize` > `updateViewConstraints` >
11 |
12 | > `viewWillLayoutSubviews` > `layoutSubviews` > `viewDidLayoutSubviews` >
13 |
14 | > `drawRect` >
15 |
16 | > `viewDidAppear`
17 |
--------------------------------------------------------------------------------
/CS/네트워크/HTTP와Socket.md:
--------------------------------------------------------------------------------
1 | # HTTP통신과 Socket통신의 차이
2 |
3 | ```
4 | 📌 HTTP 통신
5 | ```
6 | - HTTP는 HTML 파일을 전송하는 프로토콜로, 웹 브라우저 통신에서 일어나며 JSON, image 파일도 전송하곤한다.
7 | - 클라이언트가 서버로 요청 → 서버가 응답 하는 방식의 통신으로 단방향 통신이다.
8 | - 응답 후 connection이 끊어지는게 기본이지만, 성능상 필요하다면 keep alive 옵션을 주어 일정 시간동안 connection을 유지할 수 있다.
9 |
10 | ```
11 | 📌 Socket 통신
12 | ```
13 | - 투 프로그램이 서로 데이터를 주고 받을 수 있는 양쪽에 모두 생성되는 통신 단자이다.
14 | - 서버와 클라이언트 양방향 연결이 이루어지는 통신으로, 서로 요청을 보낼 수 있다.
15 | - 계속 connection을 들고 있기 떄문에 HTTP 통신에 비해 많은 리소스가 소모된다.
16 | → 스트리밍, 실시간 채팅 등 실시간으로 데이터를 주고받는 경우 connection을 자주 맺고 끊는HTTP보다 소켓이 적합하다.
17 |
--------------------------------------------------------------------------------
/CS/네트워크/web_http.md:
--------------------------------------------------------------------------------
1 | # 웹과 네트워크에 대하여
2 |
3 | ## 웹 브라우저 주소 입력란에 URL을 입력했을 때 어떻게 해서 웹 페이지가 보여질까?
4 |
5 | - 브라우저 주소 입력란에 `URL`을 입력하여 어딘가에 송신한다.
6 | - 어딘가에서 응답이 돌아오면 웹 페이지가 표시된다.
7 | - 웹 브라우저는 주소 입력란에 지정된 `URL`에 의지하여 웹 서버로부터 리소스 파일 등의 정보를 얻는다.
8 | - 이때, 서버에 의뢰하는 웹 브라우저 등을 클라이언트(`Client`) 라고 부른다.
9 |
10 |
11 |
12 |
13 | ### HTTP(HyperText Transfer Protocol)
14 | - 이렇게 클라이언트에서 서버까지 일련의 흐름을 결정하고 있는 것은 웹에서 사용되는 `HTTP` 프로토콜 이다.
15 | - 웹은 `HTTP` 라는 프로토콜을 준수하여, 이 약속을 기반으로 통신을 한다.
16 |
--------------------------------------------------------------------------------
/CS/ETC/Framework&Library.md:
--------------------------------------------------------------------------------
1 | # Framework & Library
2 |
3 | - Framework : 특정 문제를 해결하기 위해 상호 협력하는 클래스와 인터페이스의 집합
4 | 완성된 어플리케이션이 아닌 프로그래머가 완성시키는 작업을 해야 한다.
5 | - 특정 개념들의 추상화를 제공하는 클래스/컴포넌트로 구성
6 | - 추상적인 개념들이 문제를 해결하기 위해 같이 작업하는 방법을 정의
7 | - 컴포넌트의 재사용이 가능
8 | - Library : 단순 활용 가능한 도구들의 집합
9 | 개발자가 만든 클래스에서 호출하여 사용, 클래스들의 나열로 필요한 클래스를 불러서 사용하는 방식
10 | - 차이점
11 | - 프레임워크 : 전체적인 흐름을 스스로 쥐고있다. 개발자가 그 안에 필요한 코드를 짜 넣는다.
12 | - 라이브러리 : 개발자가 전체적인 흐름을 만들고, 라이브러리를 가져다 쓴다.
13 |
14 | → 프레임워크는 가져다 사용한다기보다 거기 안에 들어가서 사용한다는 느낌으로 볼 수 있다.
15 | → 라이브러리는 앱의 흐름을 직접 제어하고, 동작중 필요한 기능이 있을 때 능동적으로 사용할뿐.
16 |
--------------------------------------------------------------------------------
/CS/운영체제/Memory.md:
--------------------------------------------------------------------------------
1 | # Memory
2 | - Stack, Heap, Data, Code 로 구성되어 있다.
3 | - 프로세스가 만들어질 때 PCB도 같이 만들어진다.
4 |
5 |
6 |
7 | ## 요약
8 | > PCB
9 | - 레지스터: CPU에서 명령, 주소들을 들고 있다.
10 | - 카운터: 프로그램 안에서 현재 어느 위치를 실행시키고 있는지 주소를 가지고 있다.
11 |
12 | > 데이터
13 | - 정적 변수, 전역변수가 저장되는 곳
14 |
15 | > 스택
16 | - 지역변수, 매개변수가 저장되는 곳
17 | - 컴파일 타임에 크기가 결정된다.
18 |
19 | > 힙
20 | - 메모리의 위쪽에 할당되어있다.
21 | - 동적으로 할당 가능한 메모리다. 런타임에 크기가 결정된다.
22 | - 메모리 사이즈에 제한이 없다.
23 |
24 | > 코드
25 | - 우리가 실행하는 코드가 있는 부분이다.
26 |
27 |
28 |
--------------------------------------------------------------------------------
/CS/네트워크/캡슐화&역캡슐화.md:
--------------------------------------------------------------------------------
1 | # 캡슐화 & 역캡슐화
2 |
3 | > : Encapsulation & Decapsulation
4 |
5 | 네트워크를 통해 데이터를 보낼 때 `캡슐화`와 `역캡슐화` 과정이 이루어진다.
6 |
7 |
8 |
9 | ### 캡슐화/역캡슐화를 하는 이유?
10 | - 데이터를 보내기 위해서는 데이터의 헤더에 필요한 정보를 붙여서 다음 계층으로 보내야 하기 때문이다.
11 |
12 |
13 |
14 | ### 간략 정의
15 | - 캡슐화
16 | - 위의 이유로 헤더를 덧붙여 나가는 것을 `캡슐화` 라고 한다.
17 | - 송신하는 측에서 캡슐화를 통해 데이터를 전송한다.
18 | - 역캡슐화
19 | - 수신하는 측에서 역캡슐화를 통해 헤더를 벗겨내 최초로 보낸 데이터 형태를 구하는 과정을 `역캡슐화` 라고 한다.
20 |
21 |
22 |
23 |
24 | ### 참고 링크
25 | - [캡슐화와 역캡슐화 TIL](https://velog.io/@qmasem/TIL-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%BA%A1%EC%8A%90%ED%99%94-%EC%97%AD%EC%BA%A1%EC%8A%90%ED%99%94-encapsulation-decapsulation)
--------------------------------------------------------------------------------
/CS/아키택처패턴/README.md:
--------------------------------------------------------------------------------
1 | # 디자인패턴이란?
2 | - 객체 지향 프로그래밍은 40여년 전부터 현재까지도 사용되어지고 있으며, 선조 프로그래머들의 노력 덕에 유지보수와 협업에 용이한 코드들의 구조에 대한 노하우가 쌓이게 되었다.
3 | - 이 데이터들을 패턴화 한 것이 `디자인 패턴` 이다.
4 | - 수 많은 디자인 패턴이 존재하며,
5 | 그중에서도 웹, 앱 같은 유저 인터페이스를 통한 `이벤트 기반 프로그래밍`을 잘 할 수 있도록 고안된 것이 `MVC, MVVM, MVP, VIPER` 등 이다.
6 | - [링크](https://eeyatho.tistory.com/77)를 통해 발전 과정을 간략하게 살펴보자.
7 |
8 | ## 디자인패턴이 필요한 이유?
9 | - 코드를 짜다보면 비슷한 패턴이 반복된다. 그것을 패턴화하여 하나의 템플릿으로 만들어 둔 것이 디자인 패턴이다.
10 | - 사용하는 이유는
11 | - 문제를 접근하는 방법을 새로 만들어내는 것보다, 기존에 비슷한 상황에서 사용했던 템플릿을 활용하여 문제를 해결하면 능률적으로 일을 처리할 수 있다.
12 | - 협업 시, 언어에 구애 받지 않고 의사소통이 가능하며, 일관성 있는 코드를 작성할 수 있다.
13 |
14 | ## INDEX
15 | - MVC
16 | - MVP
17 | - MVVM
18 | - MVVM-C
19 | - VIPER
20 |
--------------------------------------------------------------------------------
/CS/알고리즘/기준에_따라_데이터를_정렬.md:
--------------------------------------------------------------------------------
1 |
2 | # 기준에 따라 데이터를 정렬
3 |
4 | ## 정렬(Sorting)이란?
5 | > : 데이터를 특정한 기준에 따라순서대로 나열하는 것
6 |
7 | - 프로그램에서 데이터를 가공할 때 오름차순/내림차순 등 어떤 식으로든 정렬해서 사용하는 경우가 많기 때문에 정렬 알고리즘은 프로그램을 작성할 때 가장 많이 사용되는 알고리즘 중 하나다.
8 |
9 |
10 |
11 | ### 장점
12 | - 정렬된 테이터는 이진 탐색이 가능해진다. (정렬은 이진 탐색의 전처리 과정이다.)
13 | - 정렬을 공부하다보면 자연스럽게 알고리즘 효율의 중요성을 깨닫게 된다.
14 |
15 | ### 예제
16 | ```
17 | 아래와 같이 숫자 카드가 10장이 있다. (0-9)
18 | [7, 5, 9, 0, 3, 1, 6, 2, 4, 8]
19 | 다음 카드를 오름차순으로 정렬해보자.
20 |
21 | 내림차순의 경우는 오름차순으로 정렬된 리스트를 뒤집으면 된다.
22 | 리스트를 뒤집는 연산은 O(N) 의 복잡도로 간단한 수행이므로 오름차순만 알아본다.
23 | ```
24 |
25 |
26 |
27 | ### 정렬 방법별 풀이
28 | - [선택 정렬](/CS/알고리즘/선택정렬.md)
29 | - 삽입 정렬
30 | - 퀵 정렬
31 | - 계수 정렬
32 |
--------------------------------------------------------------------------------
/CS/운영체제/MultiProcessing과MultiThreading.md:
--------------------------------------------------------------------------------
1 | # 멀티 프로세싱 vs 멀티 스레딩
2 | : CPU의 최대 활용을 위해 여러개의 프로그램을 동시에 실행하는 것
3 |
4 | ## 멀티 테스킹(Multitasking)
5 |
6 | 초기 아이폰을 보면 한 번에 하나의 앱만 실행할 수 있었다.
7 |
8 | 지금은 한번에 여러개의 앱을 실행이 가능해졌다.
9 |
10 | ## 컨텍스트 전환(Context Switching)
11 |
12 | - CPU를 한 프로세스/ 스레드에서 다른 프로세스 / 스레드로 전환하는 것
13 | - 프로세스 제어 블록(PCB)에서 CPU의 컨텍스트 상태를 복원/저장해서 나중에 같은 시점에서 프로세스 실행을 재게할 수 있게 하는 절차이다.
14 | - 이 기술을 통해 여러 프로세스가 단일 CPU를 공유할 수 있고, 멀티 테스킹 운영체제의 주요 기능이다.
15 |
16 |
17 | ## 멀티 프로세싱(Multiprocessing)
18 |
19 | A,B를 동시에 실행하고 싶다면
20 |
21 | A가 실행되고 B가 준비상태에 있고, B가 준비가 되면 A가 CPU에 적재된다.
22 |
23 | 이렇게 실행 준비 상태를 번갈아실행되는 컨텍스트 스위칭이 발생한다.
24 |
25 | ## 멀티 스레딩(Multithreading)
26 |
27 | 공유 자원이 있기 때문에 캐시 적중률이 올라간다.
28 |
--------------------------------------------------------------------------------
/CS/네트워크/HTTPMethod.md:
--------------------------------------------------------------------------------
1 | # HTTP Method
2 |
3 | ## 📌 GET / POST
4 |
5 | - `GET`
6 | - http request message의 헤더 부분의 URL에 담겨서 전송되며, 바디는 비어있는 상태의 URL 뒤에 데이터를 가져오기 위한 조건이 포함
7 | → URL에 조건이 포함되어있기 떄문에 데이터의 제한이 있고, 보안에 위험하다.
8 | - `POST`
9 | - http request message의 바디 부분에 클라이언트의 요청을 처리하기 위한 데이터가 존재.
10 | → URL에 노출되지 않기 떄문에 보안 위험은 없고, 보내는 데이터의 제한이 없다.
11 | ```
12 | GET 요청은 캐싱이 된다. 웹서버에 요청이 전달되지 않고, 캐시에서 데이터를 전달해준다.
13 | ```
14 |
15 |
16 | ## 📌 PUT/ PATCH
17 |
18 | - `PUT`
19 | - 요청된 자원을 수정할 때 사용하는 메서드로, 필드 전체를 수정할 때 사용한다.
20 | - 만약 일부만 전달할 경우, 그 외의 필드는 NULL 혹은 초기값으로 처리된다.
21 | - `PATCH`
22 | - 요청된 자원을 수정할 때, 자원내 필드를 일부 수정할 때 사용한다.
23 |
24 |
25 | ## 📌 DEL
26 |
27 | - 제정한 리소스를 삭제한다.
28 |
--------------------------------------------------------------------------------
/Swift/==_vs_===.md:
--------------------------------------------------------------------------------
1 | # == vs ===
2 |
3 | - `==` : 값을 비교하는 연산자
4 |
5 | ```swift
6 | let a: Int = 3
7 | let b: Int = 3
8 | let c = a
9 |
10 | a == b // true : 상수/변수 안에 들어있는 값이 같은지 비교하기 때문
11 | a == c // true
12 | ```
13 |
14 |
15 |
16 | - `===` : 참조 값을 비교하는 연산자
17 |
18 | ```swift
19 | class Person {
20 | let name: String
21 | let age: Int
22 |
23 | init(name: String, age: Int) {
24 | self.name = name
25 | self.age = age
26 | }
27 | }
28 |
29 | let 철수 = Person(name: "철수", age: 22)
30 | let 철수쌍둥이동생 = Person(name: "철수", age: 22)
31 | let 철수복제인간 = 철수
32 |
33 | 철수 === 철수쌍둥이동생 // false : 각각의 객체를 생성하였기에 참조하고 있는 메모리 주소가 다르기 때문
34 | 철수 === 철수복제인간 // true : 철수와 철수복제인간은 같은 메모리 주소를 참조 하고 있기 때문
35 | ```
--------------------------------------------------------------------------------
/Swift/unknown_default.md:
--------------------------------------------------------------------------------
1 | # @unknown default
2 |
3 | - swift 5에서 소개된 개념이다.
4 | - enum(열거형) 에서 사용된다.
5 |
6 |
7 | - `switch`문을 통해 알려진 경우들을 커버하지만 `CLAuthorizationStatus`와 같이 미래에 추가될 수도 있는 알려지지 않은 값들을 가질 수도 있다.
8 | - 그런 경우를 대비할 수 있는 코드이다.
9 |
10 | ## 미리 준비해야하는 이유?
11 | - API 확장 프로세스 때문
12 | - 새로운 케이스를 추가해야 하는 경우에 대비되지 않은 코드로 인해 문제가 생기는 것 = `a source breaking change`
13 |
14 | ## enum에는 2가지가 있다
15 | > frozen enum
16 |
17 | - 더 이상 변화가 일어나지 않은 고정된 enum
18 |
19 | > non forzen enum
20 |
21 | - 추후에 변화가 일어날 수 있는 enum
22 |
23 | ```
24 | 후자의 경우는 @unknown 키워드가 필요하다.
25 | ```
26 |
27 | ## 그냥 default와의 차이점
28 | - `default`와 일반적으로는 동일하게 작용한다.
29 | - 큰 차이점: 알려진 모든 요소들에 대한 케이스가 존재하지 않는 경우 아래와 같이 컴파일러가 경고할 것
30 | ```
31 | ⚠️ Switch must be exhaustive
32 | ```
33 |
--------------------------------------------------------------------------------
/iOS/viewLifeCycle.md:
--------------------------------------------------------------------------------
1 | # View Life-Cycle
2 |
3 | 앱은 하나 이상의 뷰로 구성되어있으며, 각각의 뷰는 생명주기를 갖는다.
4 |
5 | 순환적으로 발생하기 때문에 화면 전환에 따라 로직을 적절한 곳에서 실행시켜야 한다.
6 |
7 | 총 6가지가 있는데, 이 6가지 전에 가장먼저 실행되는
8 |
9 | - `loadView` : 뷰를 만들고메모리에 올린다. 이후 viewDidLoad가 호출된다.
10 | - `viewDidLoad` : VC가 메모리에 로드된 후 호출된다. 딱 1번만 실행되기에 리소스 초기화 작업을 주로 한다.
11 | - `viewWillAppear` : 뷰가 생성되기 직전에 실행된다. 뷰가 나타나기 전에 실행해야하는 작업을 한다.
12 | - `viewDidAppear` : 뷰가 생성된 후 실행한다. 데이터를 받아 보여주거나 애니메이션 작업 등을 할 수 있다. (viewWillAppear에 넣어줬다가 반영안되는 경우도 있음)
13 | - `viewWillDisappear` : 뷰가 사라지기 직전에 실행
14 | - `viewDidDisappaear` : 뷰가 사라진 후 실행
15 | - `viewDidUnload` : 메모리에서 해제될 때 실행
16 |
17 |
18 | ## 참고
19 |
20 | [View 레이아웃 그리는 주기](https://github.com/keenkim1202/KEENs_TIL/blob/main/iOS/view_draw_layout_cycle.md)
21 |
--------------------------------------------------------------------------------
/CS/데이터베이스/db가필요한이유.md:
--------------------------------------------------------------------------------
1 |
2 | # DB는 왜 필요할까
3 |
4 | 데이터베이스가 존재하기 전에는 파일 시스템을 사용해서 데이터를 관리헀었다.
5 |
6 | 그러면 데이터를 각 파일 단위로 저장하고 업무 처리를 위해 어플리케이션과 상호 연동을 시켜야하는데,
7 |
8 | → 종속성과 데이터 무결성의 문제가 발생한다.
9 |
10 | 파일 시스템마다 따로 관리해야하는데 어떤건 데이터 간의 관련이 있어서 상호 연동을 해야하거나, 유니크해야 한다던가... 파일시스템으로 관리하기에 한계가 있다.
11 |
12 |
13 | ## 특징
14 |
15 | **독립성**
16 |
17 | - 데이터 베이스의 크기를 늘리거나, 데이터 파일을 늘리고 새롭게 추가해도 어플리케이션을 수정할 필요가 없다.
18 |
19 | **무결성**
20 |
21 | - 다양한 경로로 데이터가 들어오면 잘못된 데이터가 들어올 수 있다.
22 | - 이런 경우의 수를 방지하는 데이터 유효성 검사를 통해 무결성을 구현할 수 있다.
23 |
24 | **보안성**
25 |
26 | - 계정 관리 혹은 접근 권한 설정을 통해 보안성을 가질 수 있따.
27 |
28 | **일관성**
29 |
30 | - 연관된 정보를 논리적 구조로 관리하여, 하나의 데이터만 변경했을 경우 발생할 수 있는 불일치성을 배제할 수 있다.
31 |
32 | **중복 최소화**
33 |
34 | - 데이터를 통합 관리함으로써, 파일 시스템의 단점인 자료 중복의 문제를 최소화할 수 있다.
--------------------------------------------------------------------------------
/CS/운영체제/Stack&Heap.md:
--------------------------------------------------------------------------------
1 | # Stack
2 | - 함수가 수행 중인 경우에만 함수 내 데이터에 접근할 수 있는 임시 메모리 할당 체계이다.
3 | - 함수 실행을 끝 마치자마자 메모리를 자동으로 할당/해제 한다.
4 | - stack에 비해 상당히 빠르다. (상대적)
5 | - heap 메모리 영역에 비해 저장 공간이 작다.
6 | - 프로그래머가 신경쓸 필요 없다. (알아서 할당/해제 되므로)
7 |
8 | # Heap
9 | - 자동으로 할당 해제되지 않는다.
10 | - 처리 시간(엑세스 시간)은 stack에 비해 상당히 느리다. (상대적)
11 | - 객체를 선언하면 항상 heap 메모리 영역에 생성된다.
12 | - 객체에 대한 참조 정보는 stack 메모리 영역에 저장된다.
13 | - heap 메모리 영역에 저장된 데이터는 수행중인 모든 스레드에서 볼 수 있기 때문에 stack 메모리 할당만큼 안전하지 않음.
14 | - stack 메모리 영역에 비해 상당히 크다.
15 | - 프로그래머가 잘 관리하지 못하면 메모리 누수가 발생할 수 있다.
16 |
17 | # Stack vs Heap
18 | | |Stack|Heap|
19 | |:---:|---|---|
20 | |위치 |연속적 메모리 공간|불연속한 메모리 공간에 위치|
21 | | |컴파일 타임에 정해진 대로 수향|프로그래머가 직접 작성|
22 | |비용 |작다|크다|
23 | |구현 |쉽다|어렵다|
24 | |처리속도|빠르다|늘디|
25 | |이슈 |메모리 shortage|메모리 fragmentation|
26 | |크기 |정해짐|유연함|
27 | | |선형적|계층적|
28 |
--------------------------------------------------------------------------------
/CS/패러다임/OOP_vs_FP.md:
--------------------------------------------------------------------------------
1 | ## 함수형 프로그래밍
2 | - 함수형 프로그램밍은 순수 함수를 기반으로 하는 프로그래밍 패러다임
3 | - 순수 함수는 어떤 입력에 대해 항상 같은 출력을 만드는 함수를 의미하며 즉, 외부에 영향을 주거나 받는 사이드 이펙트가 없다.
4 | - 스위프트는 함수형 프로그래밍 언어이면서 동시에 객체 지향 프로그램밍 언어의 특징인 상속, 은닉, 캡슈화, 추상화 등을 제공하는 멀티 패터다임 언어입니다.
5 |
6 |
7 | ## 객체지향 프로그래밍
8 | - 컴퓨터 프로그램을 명령어의 목록으로 보는 기존의 명령형 프로그래밍 패러다임의 시각에서 벗어나 여러 개의 독립된 단위의 객체의 모임으로 파악하고자 하는 시작
9 | - 각각의 객체는 서로 메시지를 주고 받고, 데이터를 처리할 수 있다.
10 | - 개발과 유지보수를 간편하게, 직관적으로 코드 분석
11 | - 객체지향 프로그래밍 패러다임을 차용한 언어는 필수로 명령형 프로그래밍 패러다임을 사용
12 | - 프로퍼티, 변수 등에 해당하는 메모리값의 변화 (상태변화)가 있기 때문
13 |
14 |
15 |
16 | ## Swift의 1급 객체
17 | - 1급 객체는 `함수의 인자로 전달되거나 반환 값으로 사용할 수 있는` 객체를 의미합니다.
18 | - `변수나 상수에 할당` 할 수 있는 객체입니다.
19 | - 스위프트는 기본 타입들과 함수나 클로저까지 모두 1급 객체에 해당합니다.
20 |
21 |
22 | ### 참고 링크
23 | - [OOP vs FP](https://www.emizentech.com/blog/oop-vs-functional-programming.html)
24 |
--------------------------------------------------------------------------------
/CS/데이터베이스/db_normal_form.md:
--------------------------------------------------------------------------------
1 | # 정규형 (Normal Form)
2 |
3 | 릴레이션이 정규화된 정도는 정규형(Normal Form)으로 표현한다.
4 |
5 |
6 |
7 | 각 정규형 마다 만족시켜야하는 제약조건이 존재하고,
8 | 릴레이션이 특정 정규형의 제약조건을 만족하면 릴레이션이 해당 정규형에 속한다고 한다.
9 | 차수가 높아질수록 제약조건이 많아지고, 엄격해진다.
10 |
11 |
12 |
13 |
14 | > 제 1정규형 : 각 속성이 원자값으로만 구성.
15 |
16 | - 릴레이션에 속한 모든 속성의 도메인(값)이 원자값으로만 구성되어 있으면 제1정규형에 속한다.
17 | - 즉, 하나의 튜플의 하나의 속성에 여러개의 값이 들어있는 다중값이 허용되지 않는다.
18 |
19 | > 제 2정규형 : 완전 함수 종속 (부분 함수 종속 제거)
20 |
21 | - 릴레이션이 제 1정규형에 속하고, 기본키가 아닌 모든 속성이 기본키에 완전 함수 종속되면 제2정규형에 속한다.
22 |
23 | ```
24 | * 무손실 분해
25 | : 자연 조인을 하면 원래의 릴레이션으로 다시 복원할 수 있도록, 정보의 손실 없이 릴레이션을 분해하는 것을 무손실 분해라고 한다.
26 | ```
27 |
28 | > 제 3정규형 : 이행적 함수 종속 제거
29 |
30 | - 릴레이션이 제2정규형에 속하고, 기본키가 아닌 모든 속성이 기본키에 이행적 함수 종속되지 않으면 제3정규형에 속한다.
31 |
32 | - 이행적 함수 종속이란 릴레이션을 구성하는 3개의 속성 집합 X,Y,Z에 대해 함수 종속 관계 X→Y, Y→Z가 존재하면 논리적으로 X→Z가 성립한다. 이때 Z가 X에 이행적으로 함수적 종속되었다고 한다.
33 |
34 | - 이럴때에는 X와 Y 속성 집합 릴레이션과 Y와 Z 속성 집합의 릴레이션으로 분해한다.
--------------------------------------------------------------------------------
/Swift/dynamicDispatch.md:
--------------------------------------------------------------------------------
1 | # Dynamic Dispatch
2 |
3 | ## Static Dispatch (Direct Call)
4 | - 컴파일 타임에 호출될 함수를 결정하여, 런타임에 그대로 실행
5 |
6 | → 컴파일 타임에 결정나기때문에 성능상 이점을 가질 수 있다.
7 |
8 | ## Dynamic Dispatch (Indirect Call)
9 | - 런타임에 호출될 함수를 결정한다.
10 | - 클래스마다 함수 포인터들의 배열인 vTable(Virtual DIspatch Table)을 유지한다.
11 | - 하위 클래스가 메서드를 호출할 때, vTable을 참조하여 실제 호출할 함수를 결정한다.
12 |
13 | → 이 과정이 런타임에 일어나기 때문에 성능상 손해를 보게 된다.
14 |
15 | ```
16 | 📌 vTable
17 | : 메서드 오버라이딩에 따라 실행 시점에 어떤 메서드를 실행할지 결정하는 동적 디스패치를 지원하기 위해 프로그래밍 언어에서 사용하는 메커니즘.
18 | ```
19 |
20 | ### ReferenceType에서의 Dispatch
21 | - class에는 상속 가능성이 있어서, 서브 클래스에서 함수를 호출할 수 있기 때문에 Dynamic Dispatch를 사용하게 된다.
22 | - 컴파일러는 하위 클래스에서 오버라이딩이 될 경우를 대비하여, 상위 클래스의 메서드를 참조해야할지, 하위 클래스의 메서드를 참조해야할지 확인하는 작업을 런타임에 해야한다.
23 | - 각 클래스마다 가지고 있는 vTable 안에 함수 포인터로 두고, 실제 런타임 시점이 vTable을 사용해서 어떤 메서드가 불리는지 결정해버리는 것.
24 | - 메서드가 오버라이딩 되지 않을 수도 있지만, 클래스이기에 그럴 가능성이 있어서, 무조건 vTable을 확인해서 참조한다.
--------------------------------------------------------------------------------
/CS/아키택처패턴/MVC.md:
--------------------------------------------------------------------------------
1 | # MVC
2 |
3 |
4 |
5 | : Model + View + Controller
6 | - `View`와 `Model` 사이에서 `Controller`가 양쪽을 모두 업데이트 한다.
7 | - `View`는 그저 화면에 요소를 나타내기만 한다.
8 | - `Model`은 앱에서 사용되는 데이터를 읽고 쓰는 역할만 한다.
9 | ```
10 | 예를 들어,
11 | - 서버에서 받아온 json 데이터를 파싱하고, 원하는 형태로 변형하여 View에 띄우는 과정
12 | - 사용자와의 interaction을 통해 들어온 정보 혹은 변경된 정보를 model에 반영하는 과정
13 | 등 화면에 보이는 것과 데이터 이외의 것은 모두 ViewController에서 담당하다보니,
14 |
15 | 우스겟 소리로 MVC를 Massive View Controller 라고 부르기도 한다.
16 | ```
17 | - `controller`가 비대해진 다는 것은 테스트 하기에도 어렵다는 단점이 있다.
18 |
19 | ## 요소
20 | - `Model` : Business logic, Data를 담당하는 부분
21 | - `View` : 사용자의 입장에서 화면에 보여지는 부분 (presentation)
22 | - `Controller` : `Model`과 `View`의 중간 다리.
23 | - `Model`의 변화를 `View`에 갱신
24 | - `View`의 입력을 받아 `Model`에 반영
25 |
26 |
27 |
28 |
29 |
30 | 참고 링크
31 |
32 | - [iOS에서의 MVC와 MVVM](https://velog.io/@nnnyeong/iOS-MVC-MVVM-Architecture-Pattern)
33 |
34 |
35 |
--------------------------------------------------------------------------------
/CS/ETC/아키택처패턴과_디자인패턴이란?.md:
--------------------------------------------------------------------------------
1 | # 아키택처패턴과 디자인패턴이란?
2 |
3 | ## 생겨난 이유는 무엇일까
4 | > 프로젝트를 진행할 때 혼자서 진행한다면?
5 | >
6 | 다른 사람과 의사소통을 할 필요가 없기에 자신이 편한대로 코드를 작성하면 된다.
7 |
8 | > 하지만, 다수가 함께 진행하게 된다면?
9 | >
10 | 다양한 사람들과 함께 프로젝트 개발 언어, 구조, 패턴, 역할 분담 등을 정하게 될 것이다. 그리고 대부분의 경우 혼자 작업할 때보다
11 | 코드이 질도 훨씬 높을 것이다.
12 | 이때 일관성있는 코드를 작성하려면, 서로 간의 의사소통이 잘 되려면 공통된 틀과 규칙이 필요할 것이다.
13 |
14 |
15 | ## 아키텍처 패턴과 디자인 패턴 차이는?
16 | - 아키텍처 : 프로그램 내에서 큰 구조를 구성하여 다른 구성 요소를 관리하는 역할
17 | - ex) MVC, MVVM, MVP, VIPER
18 | - 디자인 패턴 : 자주 발생하는 특정 유형의 문제를 해결하는 방법으로 아키텍처보다 좁은 개념
19 | - ex) Delegate, Singleton, Observer
20 |
21 | ## 아키택처 패턴의 장점은?
22 | - 패턴들을 사용하면서 앱의 성능, 효율 등을 업그레이드 시킬 수 있다.
23 | - 하나의 프로젝트에 수많은 개발자들이 함께 협업을 하게 된다. 이런 사황애서 아래와 같은 장점이 있다.
24 | - 인수인계 등 능률증가
25 | - 구조 단순화
26 | - 검증된 해결 방안을 쉽게 도출
27 | - 효율적이고 체계적인 코드 작성
28 |
29 | ## 좋은 아키택처 패턴이란?
30 | - Distribution
31 | - 엄격한 규칙을 가지고 객체 사이에 균형있게 책임이 분배되어있어야 한다.
32 | - Testability
33 | - 각각의 요소를 독립적으로 테스트할 수 있어야 한다.
34 | - Easy To Use
35 | - 사용하기 편해야 한다.
36 | - 편리함과 코드의 길이는 비례한다.
37 | - 사용하기 불편하다면(코드가 길다면) 기존의 아키텍처를 변경하거나 혹은 구현할 때 유지보수 비용이 많이들게 될 것이다.
38 |
39 |
--------------------------------------------------------------------------------
/Swift/alamofire_request_url_print.md:
--------------------------------------------------------------------------------
1 | # Alamofire 에서 response data 와 request url 출력하는 방법
2 |
3 | ```swift
4 | import Alamofire
5 | let url: Stirng = "" // 요청할 base url 작성
6 | let networkHeader: [String: String] = [:] // 필요로 하는 헤더 작성
7 | let params: [String: String] = [:] // 필요로 하는 파라미터 작성
8 |
9 | Alamofire.request(url, method: .get, parameters: params, encoding: URLEncoding.httpBody, headers: networkHeader).responseObject { (response: DataResponse) in
10 | // request URL 출력하기
11 | print("* REQUEST URL: \(String(describing: response.request))")
12 |
13 | // reponse data 출력하기
14 | if
15 | let data = response.data,
16 | let utf8Text = String(data: data, encoding: .utf8) {
17 | print("* Reponse Data: \(utf8Text)") // encode data to UTF8
18 | }
19 |
20 | switch response.result {
21 | case .success:
22 | guard let message: CampaignDefaultVO = response.result.value else { return }
23 | // some code ...
24 |
25 | case .failure(let err):
26 | self.showAlert(msg: err.localizedDescription)
27 | }
28 | }
29 | ```
30 |
--------------------------------------------------------------------------------
/iOS/layoutSubviews.md:
--------------------------------------------------------------------------------
1 | # Layout Subviews Methods
2 |
3 | 레이아웃이 결정되기 전, 후에 레이아웃과 관련된 부가적인 작업들을 수행할 수 있도록 하는 메서드가 존재한다.
4 |
5 | ViewController에서 레이아웃이 결정되는 과정
6 | - `viewWillLayoutSubviews()` 메서드 호출
7 | - ViewController의 contentView가 `layoutSubviews()` 메서드 호출
8 | - 레아아웃 정보의 변경사항을 뷰에 반영
9 | - `viewDidLayoutSubviews()` 메서드 호출
10 |
11 | ## viewWillLayoutSubviews()
12 | - 뷰의 bounds가 변하는 뷰는 하위 뷰들의 위치를 조정한다.
13 | - 레이아웃이 결정되기 전에 다음과 같은 작업을 수행하고자 할 때 이 메서드를 override 하여 사용한다.
14 | - 뷰를 추가하거나 제거 하는 작업
15 | - 뷰의 크기나 위치를 업데이트하는 작업
16 | - 제약조건을 업데이트하는 작업
17 | - 뷰와 관련된 기타 프로퍼티 업데이트 작업
18 |
19 | ## layoutSubviews()
20 | - 현재 레이아웃 정보들을 바탕으로 새로운 레이아웃을 계산하고 반영한다.
21 | - 이후 뷰 계층구조를 순회하면서 하위 모든 뷰들이 이 메서드를 호출한다.
22 | - 뷰의 크기가 변경될 떄마다 이에 대응하여 하위 뷰들의 크기와 위치가 변경되어야 한다.
23 | - autoLayout을 통해 autoresizongMask 프로퍼티를 설정하여 상위 뷰의 크기가 변경되었을 떄 어떻게 대응할지 규칙을 정할 수 있다.
24 | - 뷰의 크기에 변동이 생기면 하위 뷰들의 autoresizing을 적용하는데, 변경사항들을 반영하기 위해서 `layoutSubviews()` 메서드를 호출한다.
25 |
26 | ## viewDidLayoutSubviews()
27 | - 레이아웃이 결정되고 나서 아래와 같은 일을 수행하고자할 때 이 메서드를 override 하여 사용한다.
28 | - 다른 뷰의 content 업데이트 작업
29 | - 뷰의 크기나 위치 최종 조정 작업
30 | - 테이블뷰의 데이터 reload 작업
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 KEEN
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/iOS/LifeCycle.md:
--------------------------------------------------------------------------------
1 | # App Life-Cycle
2 |
3 | 앱의 실행부터 종료까지의 주기를 말한다.
4 |
5 | 사용자가 발생시키는 이벤트에 따라 처리되며, 5가지의 상태가 있다.
6 |
7 | - `Not Running` : 아무것도 실행하지 않은 상태. or 실행중이지만 시스템에 의해 종료된 상태
8 | - `InActive` : 앱이 forgroud 상태로 돌아가지만, 이벤트를 받지 안는 상태
9 | - `Active` : 일반적으로 앱이 돌아가는 상태. 이벤트를 받는 단계
10 | - `Background` : suspend로 진입하기 전에 거치는 단계. 통화 같이 계속 앱이 실행되는 경우에는 backgorund에서 동작하고, 보통의 앱은 background를 스처 suspend 된다.
11 | - `Suspended` : 앱이 background 상태이며, 아무코도 실행하지 않는상태. 시스템이 임의로 backgorund에서 susepnd로 만들고 리소스가 해제된다.
12 |
13 | ```
14 | background에서 inactive로 가게하면 앱의 데이터가저장되어있으면 해당 화면을 보여준다.
15 | ```
16 |
17 | # View Life-Cycle
18 |
19 | 앱은 하나 이상의 뷰로 구성되어있으며, 각각의 뷰는 생명주기를 갖는다.
20 |
21 | 순환적으로 발생하기 떄문에 화면 전환에 따라 로직을 적절한 곳에서 실행시켜야 한다.
22 |
23 | - `loadView` : 뷰를 만들고메모리에 올린다. 이후 viewDidLoad가 호출된다.
24 | - `viewDidLoad` : VC가 메모리에 로드된 후 호출된다. 딱 1번만 실행되기에 리소스 초기화 작업을 주로 한다.
25 | - `viewWillAppear` : 뷰가 생성되기 직전에 실행된다. 뷰가 나타나기 전에 실행해야하는 작업을 한다.
26 | - `viewDidAppear` : 뷰가 생서된 후 실행한다. 데이터를 받아 보여주거나 애니메이션 작업 등을 할 수 있다. (viewWIllAppear에 넣어줬다가 반영안되는 경우도 있음)
27 | - `viewWillDisappear` : 뷰가 사라지기 직전에 실행
28 | - `viewDidDisappaear` : 뷰가 사라진 후 실행
29 | - `viewDidUnload` : 메모리에서 해제될 떄 실행
30 |
--------------------------------------------------------------------------------
/CS/ETC/clean_code_00.md:
--------------------------------------------------------------------------------
1 | # clean code 0장
2 | : 들어가며
3 |
4 | ## 코드 품질을 측정하는 유일한 척도 = 분 당 내지르는 WTF 횟수
5 |
6 | - 난관에 부딫일 때 옳은 문 뒤에 있으려면 `장인 정신`을 익혀야 한다.
7 |
8 | - 장인 정신을 익히는 과정은 이론과 실전이다.
9 | - 장인에게 필요한 원칙, 패턴, 기법, 경험이라는 지식을 습득해야 한다.
10 | - 열심히 일하고 연습해 지식을 몸과 마음으로 체득해야 한다.
11 | ```
12 | ex.
13 | 누군가 공식을 이용해 자전거를 타기가 가능하다는 사실을 증명하고,
14 | 자전거를 타기 위해 필요한 지식 전부를 알려줄 수 있지만,
15 | 그럼에도 자전거를 처음 타는 사람이라면 100% 넘어진다.
16 | ```
17 |
18 | ## 구현도 마찬가지!
19 |
20 | - 깨끗한 코드를 만드는 그럴듯한 원칙을 모두 적어준 후 알아서 하려고 내버려둔다면 배울 수 없기에 이 책은 원칙만 가르치지 않는다.
21 |
22 | ## 깨끗한 코드를 작성하는 것은 어렵다.
23 |
24 | - 단순히 원칙과 패턴을 안다고 깨끗한 코드가 나오지 않는다.
25 | - 고생을 하고 스스로 연습하고 실패도 맛봐야 하고, 남이 시도하다 실패하는 모습도 봐야 한다.
26 | - 결정을 내리느라 고민하는 모습, 잘못된 결정으로 대가를 치르는 모습도 봐야 한다.
27 |
28 | 이 책은 3 부분으로 나뉘어진다.
29 | - `PART 1` : 깨끗한 코드를 작성하는 원칙, 패턴, 실기를 설명한다.
30 | - `PART 2` : 여러 사례 연구를 소개하며 문제 있는 코드를 문제가 적은 코드로 바꾸는 연습을 한다.
31 | - `PART 3` : 사례 연구를 만들면서 수집한 냄새와 휴리스틱을 열거한다. 사례 연구에서 코드를 정리하면서 내린 각 결정과 휴리스틱 사이읭 관계가 중요하다.
32 |
33 | > `휴리스틱(heuristic)` 이란?
34 | 스스로 경험에 기반하여 문제를 해결하거나 학습하거나 발견해 내는 방법을 말한다.
35 | 의역하면 어림짐작하기, 주먹구구식 등으로 볼 수 있다.
36 |
37 | - 이 책의 저자는 그저 기분 좋은 책으로 머물지 않고, 손으로 몸으로 마음으로 익혀 이 책을 철저히 자신의 일부처럼 활용하기를 바란다.
--------------------------------------------------------------------------------
/Swift/map_compactMap_flatMap.md:
--------------------------------------------------------------------------------
1 | # map, compactMap, flatMap
2 |
3 | ## map
4 | - 각 요소에 대한 값을 컨테이너 내부에서 원하는 형태로 변경 후 배열의 형태로 반환한다.
5 |
6 | ```swift
7 | func map(_ transform: (String) throws -> T) rethrows -> [T]
8 | ```
9 |
10 | ```swift
11 | let numbers: [Int] = [1, 2, 3, 4, 5]
12 | numbers.map{ String($0) } // ["1", "2", "3", "4", "5"]
13 | ```
14 |
15 | ## compactMap
16 | - 1 차원 배열에서 nil을 제거하고 옵셔널바인딩을 해주고 싶을 때 사용한다.
17 | ```swift
18 | func compactMap(_ transform: (Int?) throws -> ElementOfResult?) rethrows -> [ElementOfResult]
19 | ```
20 |
21 | ```swift
22 | let optionalNumbers: [Int?] = [1, nil, 2, nil, 3, nil, 4, nil, 5]
23 | optionalNumbers.compactMap { $0 } // [1, 2, 3, 4, 5]
24 | optionalNumbers.flatMap { $0 } // flatMap을 사용해도 동일한 기능을 하나, deprecated 되고 대신 compactMap이 생겼다.
25 | ```
26 |
27 | ## flatMap
28 | - 2차원 배열을 1차원으로 평평하게 만들어준다.
29 | ```swift
30 | func flatMap(_ transform: ([Int]) throws -> SegmentOfResult) rethrows -> [SegmentOfResult.Element] where SegmentOfResult : Sequence
31 | ```
32 |
33 | ```swift
34 | let twoDimentionNumbers: [[Int?]] = [[1, nil, 2, nil], [3, nil, 4, nil, 5]]
35 | twoDimentionNumbers.flatMap { $0 } // [Optional(1), nil, Optional(2), nil, Optional(3), nil, Optional(4), nil, Optional(5)]
36 | ```
37 |
--------------------------------------------------------------------------------
/Swift/typeCasting.md:
--------------------------------------------------------------------------------
1 | ## 타입 캐스팅(Type Casting) 이란?
2 | > 인스턴스의 타입을 확인하거나 클래스 계층의 다른 부모 클래스/자식 클래스로 취급하는 방법이다.
3 |
4 | ## 업 캐스팅
5 | > 자식 클래스 -> 부모 클래스로 형변환 / 부모 클래스의 프로퍼티와 메서드를 사용하기 위한 방법이다.
6 | - `as` : 타입 변환이 확실하게 가능한 경우에만 사용한다. (그 외에는 컴파일 에러 발생)
7 |
8 | ```swift
9 | class Food { // 부모 클래스
10 | let calories: Double
11 |
12 | init(calories: Double) {
13 | self.calories = calories
14 | }
15 | }
16 |
17 | class Hamburger: Food { // 자식 클래스
18 | let name: String
19 |
20 | init(calories: Double, name: String) {
21 | self.name = name
22 | super.init(calories: calories)
23 | }
24 | }
25 |
26 | let cheeseBurger = Hamburger(calories: 446, name: "cheeseburger")
27 | let upcastedBurger = cheeseBurger as Food
28 |
29 | print(upcastedBurger.calories)
30 | print(upcastedBurger.name) // 컴파일 에러 (업캐스팅 되었으므로 name 프로퍼티에는 접근 불가능하다.)
31 | ```
32 |
33 | ## 다운 캐스팅
34 | > 부모 클래스 -> 자식 클래스로 형변환 / 자식 클래스의 프로퍼티와 메서드를 사용하기 위한 방법이다.
35 | - `as?` (옵셔널 캐스팅) : 변환이 성공하면 옵셔널 값을 가지며, 실패시에는 nil을 반환한다.
36 | - `as!` (강제 캐스팅) : 변환이 성공하면 언래핑된 값을 가지며, 실패시 런타임 에러가 발생한다.
37 |
38 |
39 | ```swift
40 | let downcastedBurger = cheeseBurger as? Food // as? 를 통해 다운캐스팅
41 | print(downcastedBurger) // nil
42 |
43 | let forcedDowncastedBurger = cheeseBurger as! Food // as! 를 통해 다운캐스팅
44 | print(forcedDowncastedBurger) // 런타임 에러
45 | ```
--------------------------------------------------------------------------------
/iOS/appLifeCycle.md:
--------------------------------------------------------------------------------
1 | # 체크할 부분
2 | - 13버전 기준으로 `sceneDelegate`, `appDelegate`가 나누어진 부분
3 | - 메모릭 부족해지면 백그라운드에서 서스팬드 → 종료
4 |
5 | ### App State
6 |
7 | 1. `Not Running`
8 | - 실행되지 않았거나, 시스템에 의해 종료된 상태
9 | 2. `Inactive`
10 | - 실행 중이지만 이벤트를 받고있지 않은 상태.
11 | - 예를들어, 앱 실행 중 미리알림 또는 일정 얼럿이 화면에 덮여서 앱이 실질적으로 이벤트는 받지 못하는 상태등을 뜻합니다.
12 | 3. `Active`
13 | - 어플리케이션이 실질적으로 활동하고 있는 상태.
14 | 4. `Background`
15 | - 백그라운드 상태에서 실질적인 동작을 하고 있는 상태.
16 | - 예를 들어 백그라운드에서 음악을 실행하거나, 걸어온 길을 트래킹 하는 등의 동 뜻합니다.
17 | 6. `suspended`
18 | - 백그라운드 상태에서 활동을 멈춘 상태.
19 | - 빠른 재실행을 위하여 메모리에 적재된 상태지만 실질적으로 동작하고 있지는 않습니다.
20 | - 메모리가 부족할때 비로소 시스템이 강제종료하게 됩니다.
21 |
22 | 대부분의 상태 전환은 AppDelegate객체의 메소드 호출을 거칩니다.
23 | AppDelegate 객체는 `UIResponder`, `UIApplicationDelegate`를 상속 및 델리게이트 참조하고 있습니다.
24 | `UIApplicationDelegate`은 `UIApplication` 객체의 작업에 개발자가 접근할 수 있도록 하는 메소드들을 담고 있습니다.
25 |
26 | ### App Delegate
27 |
28 | - `application: willFinishLaunchingWithOptions` : 어플리케이션이 최초 실행될 때 호출되는 메소드
29 | - `application: didFinishLaunchingWithOptions` : 어플리케이션이 실행된 직후 사용자의 화면에 보여지기 직전에 호출
30 | - `applicationDidBecomeActive` : 어플리케이션이 Active 상태로 전환된 직후 호출
31 | - `applicationWillResignActive` : 어플리케이션이 Inactive 상태로 전환되기 직전에 호출
32 | - `applicationDidEnterBackground` : 어플리케이션이 백그라운드 상태로 전환된 직후 호출
33 | - `applicationWillEnterForeground` : 어플리케이션이 Active 상태가 되기 직전에, 화면에 보여지기 직전의 시점에 호출.
34 | - `applicationWillTerminate` : 어플리케이션이 종료되기 직전에 호출
35 |
--------------------------------------------------------------------------------
/CS/알고리즘/정렬.md:
--------------------------------------------------------------------------------
1 | # 정렬
2 |
3 | ## 1. 선택정렬(Selection Sort)
4 | - 선택된 값과 나머지 데이터중에 비교하여 알맞은 자리를 찾는 알고리즘
5 |
6 | ### 시간복잡도
7 | - 최선, 평균, 최악 모두 `O(N^2)`
8 |
9 | 
10 |
11 | ## 2. 삽입정렬 (Insertion Sort)
12 | - 데이터 집합을 순회하면서 정렬이 필요한 요소롤 뽑아내어 이를 다시 적당한곳으로 삽입하는 알고리즘
13 | - 성능은 버블정렬보다 좋다
14 |
15 | ### 시간복잡도
16 | - 최악, 평균 : `O(N^2)`
17 | - 최선 : `O(N)`
18 | - 이미 정렬되어 있는 경우
19 |
20 | 
21 |
22 |
23 | ## 3. 버블정렬(Bubble Sort)
24 | - 거품이 수면으로 올라오는 듯 하여 붙여진 버블정렬
25 | - 인접한 두 수를 비교하여 오름차순 or 내림차순으로 정렬
26 |
27 | ### 시간복잡도
28 | - 최선, 평균, 최악 모두 `O(N^2)`
29 |
30 | 
31 |
32 | ## 6. 퀵 정렬(Quick Sort)(분할정복)
33 | - 데이터 집합내에 임의의 기준값(=pivot)을 정하고 해당 피봇으로 집합을 기준으로 두개의 부분 집합으로 나눈다.
34 | - 한쪽 부분에는 피봇값보다 작은 값들만, 다른 한쪽은 큰값들만 넣는다.
35 | - 더 이상 쪼갤 부분 집합이 없을 때까지 각각의 부분 집합에 대해 피봇/쪼개기 재귀적으로 적용.
36 | - 분할 정복법 사용(Divide-And-Conquer)
37 | - 범위, 기준, 비교, 스왑으로 순서
38 |
39 | ## 시간복잡도
40 | - 최악 : `O(N^2)`
41 | - 평균, 최선 : `O(NlogN)`
42 |
43 | 
44 |
45 |
46 | [참고자료](https://hyo-ue4study.tistory.com/68)
47 |
--------------------------------------------------------------------------------
/CS/자료구조/dynamicArrayInC.md:
--------------------------------------------------------------------------------
1 | # 동적 배열 (C언어)
2 |
3 | ## 1차원 배열
4 | ```c
5 | #include
6 | #include
7 | #include
8 |
9 | #define N 5
10 |
11 | int main() {
12 | srand((unsigned int) time(NULL));
13 |
14 | int *X = (int*)malloc(N * sizeof(int));
15 |
16 | if (X == NULL) {
17 | fprintf(stderr, "Out of memory");
18 | exit(0);
19 | }
20 |
21 | for (int c = 0; c < N; c++) {
22 | *(X + N + c) = rand() % 10; // 난수를 생성하여 값으로 할당
23 | }
24 |
25 | for (int c = 0; c < N; c++) {
26 | printf("%d ", (X + N)[c]);
27 | }
28 | printf("\n");
29 |
30 | free(X); // deallocate memory
31 | return 0;
32 | }
33 | ```
34 |
35 | ## 2차원 배열
36 | ```c
37 | #include
38 | #include
39 | #include
40 |
41 | #define M 4
42 | #define N 5
43 |
44 | int main() {
45 | srand((unsigned int) time(NULL));
46 | int *X = (int *)malloc(M * N * sizeof(int));
47 |
48 | if (X == NULL) {
49 | fprintf(stderr, "Out of memory");
50 | exit(0);
51 | }
52 |
53 | for (int r = 0; r < M; r++) {
54 | for (int c = 0; c < N; c++) {
55 | *(X + r * N + c) = rand() % 100; // 난수를 생성하여 값으로 할당
56 | }
57 | }
58 |
59 | for (int r = 0; r < M; r++) {
60 | for (int c = 0; c < N; c++) {
61 | printf("%d ", (X + r * N)[c]);
62 | }
63 |
64 | printf("\n");
65 | }
66 |
67 | free(X); // deallocate memory
68 | return 0;
69 | }
70 | ```
71 |
--------------------------------------------------------------------------------
/CS/알고리즘/탐욕법(Greedy).md:
--------------------------------------------------------------------------------
1 | # 탐욕법 (Greedy)
2 | - 현재 상황에서 가장 좋은 것 (최선의 선택)을 고르는 알고리즘
3 | → 현재 최적인 답을 선택하는 과정을 반복하여 결과를 도출한다.
4 |
5 | - 동적 프로그래밍을 간단한 문제 해결에 사용하면 지나치게 많은 일을 한다는 것에서 착안하여 고안되었다.
6 |
7 | ```
8 | 각 단계에서 선택한 최적해가 전체적으로도 최선이길 "바라는" 알고리즘
9 | (바라는거지 그게 최적해라는 것은 아님)
10 | ```
11 |
12 | ## BUT
13 | 가장 좋은 결과는 최적해를 보장하지는 않는다. → 그래서 근삿값을 구하는 용도로 사용되곤 한다.
14 |
15 | 매 순간 최적의 해로 보이는 해를 선택하며 현재 선택이 나중에 어떤 영향을 미칠지 고려하지 않는다.
16 |
17 | # 충족 조건
18 | - 앞의 선택이 이후의 선택에 영향을 주지 않을 때 (= Greedy Choice Property)
19 | - 문제의 최적해가 부분 문제에 대해서도 최적해일 때 (= Optimal Substructure)
20 |
21 | → 두 조건을 모두 만족한다면 최적해를 구할 수 있다. 그렇지 않으면 `근삿값` 정도 구할 수 있다.
22 |
23 | ### 근삿값을 구해서 어디에 쓰는가??
24 | - 항상 최적의 결과를 도출하지는 않지만, 어느정도 최적에 근사한 값을 빠르게 도출할 수 있다는 장점!
25 | - 탐욕 알고리즘은 근사 알고리즘으로 사용할 수 있다.
26 |
27 | > 근사 알고리즘(Approximation Algorithm) 이란
28 | - 어떤 최적화 문제에 대한 해의 근사값을 구하는 알고리즘
29 | - 비교적 빠른 시간에 계산이 가능하며, 어느정도 보장된 근사해를 구할 수 있다.
30 |
31 |
32 | # 어떻게 풀까
33 | - 문제의 답을 만드는 과정을 여러 부분 문제로 나눈다.
34 | - 각 부분 문제마다 어떠한 우선순위(조건)으로 탐욕적으로 선택할지 결정
35 | - ‘탐욕적 선택 속성', ‘최적 부분 구조'를 만족하는지 체크
36 |
37 | ### 탐욕적 선택 속성 (Greedy Choice Property)
38 |
39 | - 답의 모든 부분을 고려하지 않고 탐욕적으로만 선택하더라도 최적해를 구할 수 있다는 속성
40 | - ‘항상 모든 단계에서 탐욕적으로 선택한 답이 최적해에 포함되어있다’ 라는 뜻
41 |
42 | ### 최적 부분 구조 (Optimal Substructure)
43 |
44 | - 부분 문제의 최적해로 전체 문제의 최적해를 만들 수 있음을 말한다.
45 | → 항상 최적의 선택만 내렸을 때 전체 문제의 최적해를 구할 수 있는지 보아야 한다.
46 | (이 속성은 보통 증명할 필요가 없이 저명한 경우가 많다.)
47 |
48 |
49 | # TIP
50 | ex. `최단 경로`, `교환 가능한 동전의 최소 갯수`, `가능한 많은 수의 회의시간`
51 |
52 | ```
53 | 예제를 풀어보는 것을 추천한다.
54 | 풀 때 손으로 & 수학문제 풀듯이 먼저 풀어보면 도움이 될 것이다.
55 | ```
56 |
--------------------------------------------------------------------------------
/iOS/accessibilityIdentifier.md:
--------------------------------------------------------------------------------
1 | # Accessibility Identifier 설정하기
2 |
3 | > TIP: Xcode 의 `accessibility inspector`를 사용하면 원하는 요소에 `accessibility identifier`가 정상적으로 부여/인식 되는지 확인할 수 있다.
4 |
5 | ## 간단한 뷰
6 | - 간단한 뷰에서는 `.accessibilityIdentifier()` 메서드를 사용하여 쉽게 id를 부여할 수 있다.
7 | ```swift
8 | // SwiftUI에서 사용 예시
9 | Button().accessibilityIdentifier("keenButton")
10 | ```
11 |
12 | ## 복잡한 뷰
13 | - 복잡한 뷰에 대해 `accessibility identifier`를 부여하고자 할때, 그냥 부여하면 정상적으로 인식이 되지 않는 경우가 있다.
14 | - `@ViewBuilder`를 사용하여 복잡한 뷰에 대해 `accessibility identifier`를 부여할 수 있다.
15 | - 처음에는 `View`에 바로 `extension을` 작성하였으나, `accessibility inspector`를 통해 확인해보았을 때 정상인식되지 않아 `ViewModifier`를 만들어 주는 방식을 사용했다.
16 |
17 | ```swift
18 | import SwiftUI
19 |
20 | struct AccessibilityIdentifierModifier: ViewModifier {
21 | let accessibilityIdentifier: String?
22 |
23 | @ViewBuilder
24 | func body(content: Content) -> some View {
25 | if let identifier = accessibilityIdentifier {
26 | content.accessibility(identifier: identifier)
27 | } else {
28 | content
29 | }
30 | }
31 | }
32 |
33 | extension View {
34 | func setAccessibilityIdentifier(_ accessibilityIdentifier: String?) -> some View {
35 | return self.modifier(AccessibilityIdentifierModifier(accessibilityIdentifier: accessibilityIdentifier))
36 | }
37 | }
38 |
39 | // SwiftUI에서 시용 예시
40 |
41 | VStack {
42 | Text("hello")
43 | Text("my")
44 | Text("name")
45 | Text("is")
46 | Text("keen")
47 | }
48 | .setAccessibilityIdentifier("helloSentence")
49 |
50 | ```
51 |
--------------------------------------------------------------------------------
/CS/네트워크/session_cookie.md:
--------------------------------------------------------------------------------
1 | # 세션과 쿠키
2 |
3 | ## HTTP 쿠키
4 | - 서버가 웹 브라우저에 전송하는 작은 데이터 조각
5 | - 웹 브라우저가 이 조각을 저장했다가 동일한 서버에 다시 요청할 때 쿠키도 같이 전송해준다.
6 | - 서버에서 어떤 요청이 브라우저에 들어왔는지를 판단할 때 사용하기도 한다.
7 |
8 | ### 쿠키 사용 목적
9 | - 세션 관리
10 | - 개인화
11 | - 트래킹
12 |
13 | ### 쿠키 동작 방식
14 | 1. 클라이언트가 서버에 로그인 요청
15 | 2. 서버는 요청을 처리해 응답 헤더에 `set-cookie`를 추가해서 응답
16 | 3. 클라이언트는 그 다음부터 서버에 요청할 때, 쿠키를 `헤더`에 추가해서 요청
17 | 4. 서버는 쿠키를 보고 클라이언트에 응답
18 |
19 | - 서버에서 클라이언트에 쿠키를 저장하라고 전달하면,
20 | - 클라이언트는 쿠키 `헤더`를 사용하여 서버로 이전에 저장했던 모든 쿠키들을 회신할 수 있다.
21 |
22 | ## 세션
23 | - 인증을 여러 번 하지 않는 이유 중 하나가 `보안에 취약하다는 점`
24 | - 클라인언트가 서버로 인증을 위해 쿠키를 요청을 여러번 하면 개인 정보를 보내야 하는데
25 | 이 과정에서 탈취가 일어날 수도 있기 때문이다.
26 | ```
27 | -> 그래서 세션 사용
28 | ```
29 |
30 |
31 |
32 | - 세션은 일정 시간 동안, 같은 사용자 상태를 이정하게 유지시키는 기술
33 | - 웹 서버가 세션 아이디 파일을 만들어 서비스가 돌아가고 있는 서버에 저장하는 것
34 | - 서버에 저장되기 떄문에 사용자 정보를 노출하지 않을 수 있다.
35 |
36 | 1. 클라이언트가 서버로 `HTTP` 요청을 한다
37 | 2, 서버는 접근한 클라이언트의 쿠키를 확인해서, 클라이언트가 해당 `SessionID`를 보내왔는지 확인한다
38 | 3. 만일 보내지 않았다면, 서버는 새롭게 SessionID를 생성, 클라이언트에 `set-cookie`값으로 `SessionID`를 보낸다.
39 | 4. 그 다음 요청부터, 전달 받은 `SessionID` 쿠키를 자동으로 헤더에 추가해서 요청한다.
40 | 5. 요청 헤더의 `SessionID`값을 저장된 세션 저장소에서 찾아보고 유효한지 확인 후 요청을 처리하고 응답해준다.
41 |
42 | ## 차이점
43 | > 쿠키
44 |
45 | |특징|내용|
46 | |:---:|---|
47 | |저장위치|로컬 |
48 | |보안 |탈취, 변조 가능 |
49 | |생명주기|브라우저 종료해도 남음 |
50 | |속도 |파일에서 읽기 때문에 빠르다|
51 |
52 |
53 |
54 | > 세션
55 |
56 | |특징|내용|
57 | |:---:|---|
58 | |저장위치|서버 |
59 | |보안 |서버에 저장되어 상대적으로 안전 |
60 | |생명주기|브라우저 종료 시 세션 삭제 |
61 | |속도 |요청마다 서버에서 처리 해야하기 때문에 느림|
62 |
63 |
64 |
--------------------------------------------------------------------------------
/CS/운영체제/ThreadSafe.md:
--------------------------------------------------------------------------------
1 | # Thread-Safe
2 |
3 | ## Thread-safe 란?
4 | 멀티 스레드 프로그래밍에서 일반적으로
5 | 어떤 함수나 변수, 객체가 여러 스레드로부터 동시에 접근이 이루어져도 프로그램의 실행에 문제가 없음을 말한다.
6 |
7 |
8 | 보다 엄밀하게는
9 | ```
10 | 하나의 함수가 한 스레드로부터 호출되어 실행중일 때,
11 | 다른 스레드가 그 함수를 호출하여 동시에 함께 실행되더라도 각 스레드에서의 함수의 수행 결과가 올바로 나오는 것을 말한다.
12 | ```
13 |
14 | ## Thread-safe 를 지키는 방법은?
15 |
16 | - `Re-entrancy`
17 | - 어떤 함수가 한 스레드에 의해 호출되어 실행 중일 때, 다른 스레드가 그 함수를 호출하더라도 그 결과가 각각에게 올바르게 주어져야 한다.
18 |
19 | - `Thread-local storage`
20 | - 공유 자원의 사용을 최대한 줄여 각각의 스레드에서만 접근 가능한 저자소들을 사용함으로써 동시 접근을 막는다.
21 | - 이 방식은 동기화 방법과 관련되어 있고, 또한 공유 상태를 피할 수 없을 때 사용하는 방식이다.
22 |
23 | - `Mutual exclusion`
24 | - 공유 자원을 꼭 사용해야 할 경우 해당 자원의 접근을 세마포어 등의 락으로 통제한다.
25 |
26 | - 상호 배제란? : 한 thread가 자원에 접근할 떄 다른 thread의 접근을 막는 것을 의미한다.
27 |
28 | - `Atomic operatoins`
29 | - 공유 자원에 접근할 떄 원자 연산을 이용하거나 '원자적'으로 정의 된 접근 방법을 사용함으로써 상호 배제(Mutual Exclusion)를 구현할 수 있다.
30 | - 통상적으로 Criticial Section 처리가 자주 사용되는 편이다.
31 |
32 | ## Atomic? Non-Atomic?
33 | > atomic
34 | > : 프로그래밍에서 데이터의 변경이 동시에 일어난 것처럼 보이게 하는 것을 의미한다.
35 |
36 | - 데이터의 값을 변경하는 것은 항상 그 시간이 필요하다.
37 | - `atomic`한 데이터의 변경이 이루어지는 시간에는 `lock` 을 걸어 데이터를 변경하는 시간동안에는 접근이 이루어지지 않게 한다.
38 |
39 | ```
40 | 그래서 프로퍼티가 `atomic 하다`는 것은 멀티 스레드 환경에서 데이터가 반드시 변경전과 후의 상황에서만 접근하는 것을 보장함.
41 | 즉, 데이터의 변경이 이루어지고 있는 순간에는 접근이 불가능!
42 |
43 | atomic property 는 항상 serial 하게 접근됨.
44 | ```
45 | > non-atomic
46 | > : 반환된 값에 대해 보장하지 않는다. 그렇기에 정확한 값일 수도, 쓰레기 값일 수도 있다.
47 |
48 | ### Swift의 경우
49 | - `Thread-safe`를 고려한 언어가 아니기 때문에 모든 프로퍼티는 `non-atomic` 이다.
50 | - 따라서 별도 `atomic`을 지정하는 것이 불가능 -> 그렇기 떄문에 GCD(Grand-Central-Dispatch) 를 통해 다뤄야 한다.
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/CS/알고리즘/트리.md:
--------------------------------------------------------------------------------
1 | # 트리
2 |
3 | ## 관련 용어
4 | 
5 |
6 | - `루트 노드(root node)` : 부모가 없는 노드, 트리는 하나의 루트 노드만을 가진다.
7 | - `단말 노드(leaf node)` : 자식이 없는 노드, ‘말단 노드’ 또는 ‘잎 노드’라고도 부른다.
8 | - `내부 노드(internal)` : 단말 노드가 아닌 노드
9 | - `간선(edge)` : 노드를 연결하는 선 (link, branch 라고도 부름)
10 | - `형제(sibling)` : 같은 부모를 가지는 노드
11 | - `노드의 크기(size)` : 자신을 포함한 모든 자손 노드의 개수
12 | - `노드의 깊이(depth) : 루트에서 어떤 노드에 도달하기 위해 거쳐야 하는 간선의 수
13 | - `노드의 레벨(level)` : 트리의 특정 깊이를 가지는 노드의 집합
14 | - `노드의 차수(degree)` : 하위 트리 개수 / 간선 수 (degree) = 각 노드가 지닌 가지의 수
15 | - `트리의 차수(degree of tree)` : 트리의 최대 차수
16 | - `트리의 높이(height)` : 루트 노드에서 가장 깊숙히 있는 노드의 깊이
17 |
18 |
19 | ## 특징
20 | - 그래프의 한 종류이다. ‘최소 연결 트리’ 라고도 불린다.
21 | - 트리는 계층 모델 이다.
22 | - 트리는 DAG(Directed Acyclic Graphs, 방향성이 있는 비순환 그래프)의 한 종류이다.
23 | - loop나 circuit이 없다. 당연히 self-loop도 없다.
24 | - 즉, 사이클이 없다.
25 | - 노드가 N개인 트리는 항상 N-1개의 간선(edge)을 가진다.
26 | - 즉, 간선은 항상 (정점의 개수 - 1) 만큼을 가진다.
27 | - 루트에서 어떤 노드로 가는 경로는 유일하다.
28 | - 임의의 두 노드 간의 경로도 유일하다. 즉, 두 개의 정점 사이에 반드시 1개의 경로만을 가진다.
29 | - 한 개의 루트 노드만이 존재하며 모든 자식 노드는 한 개의 부모 노드만을 가진다.
30 | - 부모-자식 관계이므로 흐름은 top-bottom 아니면 bottom-top으로 이루어진다.
31 | - 순회는 Pre-order, In-order 아니면 Post-order로 이루어진다. 이 3가지 모두 DFS/BFS 안에 있다.
32 | - 트리는 이진 트리, 이진 탐색 트리, 균형 트리(AVL 트리, red-black 트리), 이진 힙(최대힙, 최소힙) 등이 있다.
33 |
34 | ## 자른 자료구조와 비교
35 | ### 트리 vs 이진트리
36 | - 각 노드가 최대 두 개의 자식을 갖는 트리
37 | - 모든 트리가 이진 트리는 아니다.
38 |
39 | ## 트리 vs 그래프
40 |
41 |
--------------------------------------------------------------------------------
/CS/알고리즘/선택정렬.md:
--------------------------------------------------------------------------------
1 | # 선택 정렬 (Selection Sort)
2 |
3 | - 무작위 데이터가 여러 개 있을 때, 이 중에서 가장 작은 데이터를 선택해 맨 앞에 있는 데이터와 바꾸고,
4 | - 그 다음 작은 데이터를 선택해 앞에서 두 번째 데이터와 바꾸는 작업을 반복.
5 | - 가장 원시적인 방법으로 매번 '가장 작은 것을 선택' 한다는 의미에서 `선택정렬` 이라고 한다.
6 |
7 |
8 |
9 | ### 예제
10 | ```
11 | 아래와 같이 숫자 카드가 10장이 있다. (0-9)
12 | [7, 5, 9, 0, 3, 1, 6, 2, 4, 8]
13 | 다음 카드를 오름차순으로 정렬해보자.
14 |
15 | 내림차순의 경우는 오름차순으로 정렬된 리스트를 뒤집으면 된다.
16 | 리스트를 뒤집는 연산은 O(N) 의 복잡도로 간단한 수행이므로 오름차순만 알아본다.
17 | ```
18 |
19 |
20 |
21 | ### 코드
22 | ```swift
23 | var array: [Int] = [7, 5, 9, 0, 3, 1, 6, 2, 4, 8]
24 |
25 | for i in 0.. array[j] { // 만약 현재 j인덱스의 값이 minIndex의 값보다 작다면
30 | minIndex = j // minIndex를 j로 바꾼다
31 | }
32 | }
33 |
34 | array.swapAt(i, minIndex) // i와 minIndex 번째 값을 스왑해준다.
35 | }
36 |
37 | print(array)
38 | ```
39 | - Swift언어의 리스트관련 `swapAt()` 메서드를 활용하여 쉽게 구현이 가능하다.
40 |
41 |
42 |
43 |
44 | ## 시간복잡도
45 | - `N - 1` 번 동안 가장 작은 수를 찾아 맨 앞으로 보내줘야 한다.
46 | - 매번 가장 작은 수를 찾기 위한 비교 연산이 필요하다.
47 |
48 |
49 |
50 | 연산횟수는...
51 | - N + (N - 1) + (N - 2) + (N - 3) + ... + 2
52 | - N * (N + 1) / 2
53 | - (N^2 + N) / 2
54 | - N^2
55 |
56 | 로 볼 수 있다.
57 |
58 |
59 |
60 | 코드상 직관적으로 말하면 for문이 2번 중첩되어있으므로 `O(N^2)` 으로 볼 수 있다.
61 |
62 |
63 |
64 | - 선택 정렬은 데이터의 개수가 10,000개 이상이면 정렬 속도가 급격히 느려지는 것을 확인할 수 있다.
65 | - 선택 정렬은 다른 정렬 알고리즘보다 매우 비효율적이다.
66 | - 하지만 특정한 리스트에서 가장 작은 데이터를 찾을 때 사용하기 좋다.
67 |
68 | ```
69 | 직접적으로 선택정렬로 값을 도출하기 보다는
70 | 선택정렬을 통해 가장 작은 데이터를 찾아 활용하는 경우가 잦으므로 기억해두자.
71 | ```
--------------------------------------------------------------------------------
/CS/네트워크/HTTP와HTTPS.md:
--------------------------------------------------------------------------------
1 | # HTTP / HTTPS
2 |
3 | ## 개요
4 | - 클라이언트-서버 구조에서 사용는 프로토콜로, 인터넷에서 가장 많이 사용되는 프로토콜은 `HTTP`이다.
5 | - 하지만 `“보안이 적용되지 않은 데이터를 전송한다”`는 위험성을 가지고 있다.
6 | - 언제든지 데이터가 도청될 수 있기 떄문이다. 따라서 보안에 상당히 취약한 프로토콜이라고 할 수 있다.
7 |
8 | - `HTTPS`는 `HTTP`에 `TLS` 계층을 더한 것이다.
9 | - `TLS`는 `SSL`의 개선된 버전으로, 서버와 브라우저 사이에 안전하게 암호화된 데이터를 전송할 수 있게 해준다.
10 | - (HTTP: 80포트, HTTPS: 443포트)
11 |
12 |
13 |
14 | 암호화 매커니즘을 통해 기밀성, 무결성 , 인증의 보안 기능성을 얻게 된다.
15 |
16 | ```
17 | - 기밀성 : 교환되는 데이터를 도청해도 의미가 없도록 암호화
18 | - 무결성 : 정보가 중간에 변질되지 않는 것을 보장. 전자서명을 통해 통신을 맺어 신뢰성 확보
19 | - 인증 : 인증서 기관을 통해 해당 서버가 안전하다는 것을 확인
20 | ```
21 |
22 | ## 개념
23 | ```
24 | HTTP (Hypertext Transfer Protocol)
25 | ```
26 | - Hypertext인 HTML을 전송하기 위한 통신 규악이다.
27 | (Hypertext: 문서와 문서가 링크로 연결되어있는 형태의 문서 체계)
28 | ```
29 | HTTPS ( Hypertext Transfer Protocol Over Secure Socket Layer)
30 | ```
31 | - HTTP의 보안이 강화된 것이다.
32 |
33 | ## 비교
34 | - 공통점: 통신 규약이다.
35 | - 차이점:
36 | - `HTTP`는 `SSL` 인증서를 사용하지 않고, `HTTPS`는 사용한다.
37 | - `HTTPS`는 `HTTP`의 보안을 강화한 것이다.
38 | → `HTTP`는 암호화되지 않은 방법으로 데이터를 전송하기 때문에 서버/클라이언트가 주고 받는 메시지를 감청하기 쉽다.
39 | - ex) 로그인 페이지: 아이디/비밀번호 같은 중요정보가 노출될 수 있기에 `HTTPS`로 통신한다.
40 |
41 | ## 원리
42 | - 공개 키 방식은 두 개의 키를 갖게 되며, A키로 암호화하면 B키로 복호화가 가능하여 반대도 가능하다.
43 | - 여기서 두 개의 키 중 하나는 공개키이고 다른 하나는 비공개키이다.
44 | → 비공개 키는 private한 사용자가 가지고 있게 되며, 공개 키는 다인에게 공개되는 키이다.
45 | - 유저가 공개키를 이용하여 데이터를 암호화한 뒤, 비공개 키의 소유자에게 전달하면 비공개 키의 소유자는 비공개 키로 복호화하여 그 데이터를 얻을 수 있다.
46 |
47 |
48 | - HTTP 장점 : 검색 엔진 최적화의 혜택을 받을 수 있다.
49 | (구글이 우리 사이트가 안전하다고 판단하여 검색 순위를 앞에 배치해주는 것)
50 |
51 |
52 | ## SSL
53 | : 전자상거래에서 데이터 보안을 위해 개발한 통신 레이어
54 |
55 | - 표현 계층의 프로토콜 (응용 계층 아래)
56 | - 응용 계층의 아래에 있기 때문에 어떠한 데이터도 암호화해서 보낼 수 있다.
57 | - `TLS`는 `SSL`의 상위 버전
58 |
--------------------------------------------------------------------------------
/CS/패러다임/first_class_citizen.md:
--------------------------------------------------------------------------------
1 | # 1급 객체 (First Class Citizen)
2 |
3 | ## 일급객체란?
4 | > : First-class Citizen
5 |
6 | - 함수의 인자로 넘길 수 있고, 변수에 저장이 가능하며, 리턴값으로 반환이 가능한 객체를 의미한다.
7 | - 더 자세히 말하면 아래의 조건을 충족하면 1급 객체라고 본다.
8 | - 런타임에도 생성이 가능하다.
9 | - 인자 값으로 전달할 수 있어야 한다.
10 | - 반환값으로 사용할 수 있어야 한다.
11 | - 데이터 구조 안에 저장할 수 있어야 한다.
12 | - 일급객체의 대표적인 예는 클로저(closure) 이다.
13 |
14 | ```swift
15 | // ex. 상수/변수에 함수 대입
16 | func lotto(range: Int) -> Int {
17 | return Int.random(in: 1...range)
18 | }
19 |
20 | let firstNumber: Int = lotto(range: 10) // 함수 호출 결과값을 상수에 할당
21 | let randomNumber = lotto(range: ) // 함수 자체를 할당
22 | let anotherRandomNumber: (Int) -> Int = lotto // 함수의 타입을 지정하여 할당
23 | ```
24 |
25 | ```swift
26 | // ex. 함수를 리턴값으로 사용 가능
27 | func plus(lhs: Int, rhs: Int) -> Int {
28 | print(#function)
29 | return lhs + rhs
30 | }
31 |
32 | func plusAgain(a: Int, b: Int) -> (Int, Int) -> Int {
33 | print(#function)
34 | return plus
35 | }
36 |
37 | let doPlus = plusAgain(a: 1, b: 2) // (Int, Int) -> Int
38 |
39 | doPlus(7, 8) // 15
40 | // doPlus에는 plusAgain()의 리턴값인 plus()가 들어있다.
41 | // 그러므로 doPlus를 선언할 때 a, b에 넣어준 값과 상관없이 doPlus를 호출할 때 인자로 넣어준 7, 8을 연산한 15가 결과값으로 나온다.
42 |
43 | plusAgain(a: 1, b: 2) // (Int, Int) -> Int
44 | plusAgain(a: 1, b: 2)(7, 8) // 15
45 | // 마찬가지로 doPlus라는 상수에 담는 과정을 제외하고 바로 plusAgain을 대입하여 작성해도 결과는 같다.
46 | ```
47 |
48 | ```swift
49 | // ex. 인자로 함수를 전달 가능
50 | func increament(a: Int) -> Int {
51 | print(1)
52 | return a + 1 // 4
53 | }
54 |
55 | func sqaure(value: Int, fn: (Int) -> Int) -> Int {
56 | print(2)
57 | return fn(value) * fn(value) // 16
58 | }
59 |
60 | print(3)
61 |
62 | sqaure(value: 3, fn: increament(a:)) // 16
63 | print(4)
64 |
65 | // 출력 : 3 2 1 1 4
66 | ```
--------------------------------------------------------------------------------
/Swift/compare_countZero_and_isEmpty.md:
--------------------------------------------------------------------------------
1 | # count == 0 vs isEmpty 무엇을 사용하는게 더 효율적일까?
2 |
3 | 문자열이 비어있는지 확인하는 방법은 두 가지가 있다.
4 | ```swift
5 | let string: String = "hello"
6 |
7 | string.count == 0 // 1
8 | string.isEmpty // 2
9 | ```
10 |
11 | - `count`의 시간복잡도는 O(n)
12 | - `isEmpty`의 시간복잡도는 O(1)
13 |
14 |
15 |
16 | swift에서 문자열을 indexing할 때
17 | 일반적으로 `String.Index`로 접근 시 시간복잡도는 O(1) 이 아닌 O(n) 이 된다.
18 | - 자세한 내용은 [String.Index 에 대하여](https://github.com/keenkim1202/KEENs_TIL/blob/main/Swift/about_string.md) 를 참고하세요.
19 |
20 | 문자열을 index를 통해 접근하고 싶다면 아래와 같은 `subscript(i:)`를 만들어줄 수 있다.
21 | ```swift
22 | extension Stirng {
23 | subscript(i: Int) -> Character {
24 | return self[index(startIndex, offsetBy: i)]
25 | }
26 | }
27 |
28 | string[1] // e
29 | ```
30 |
31 |
32 |
33 | 위에서 언급한 것처럼 문자열을 indexing할 때는 선형의 시간을 갖게 된다.
34 | 문자열의 갯수가 n개라면 n의 시간이 소요된다.
35 | 만약 특정 index의 문자를 출력하고자 한다면 `print(string[n])` 을 사용할 것이다.
36 | 이것은 이미 문자열을 순회하면서 필요로하는 index위치의 문자를 출력하므로 O(n) 의 시간복잡도를 갖는다.
37 | 그리고 `for`문을 돌면서 또 O(n) 의 시간복잡도를 갖으므로 총 O(n^2) 의 시간복잡도를 갖게 된다.
38 | ```swift
39 | for index in stride(from: 0, to: stirng.count, by: 2) { // O(n)
40 | print(string[index]) // O(n)
41 | }
42 |
43 | // 총 O(n^2)
44 | ```
45 |
46 |
47 |
48 | ## 1번
49 | count의 경우 시간복잡도는 O(n) 이다.
50 | 문자열의 문자 하나하에 대해 `String.Index`에 접근하면서 count해 나가기 때문이다.
51 | (참고: swift에서 문자는 눈으로 보기에 1글자여도 메모리상 각기 다른 크기를 차지하기 때문에 일정한 메모리 크기에 따라 index를 나누는 C언어와 달리 index를 통해서 특정 위치의 문자열에 대한 정보를 알 수 없다.)
52 | - 자세한 내용은 [String.Index 에 대하여](https://github.com/keenkim1202/KEENs_TIL/blob/main/Swift/about_string.md) 를 참고하세요.
53 |
54 | ## 2번
55 | `isEmpty`는 문자열의 첫번째 index와 마지막 index가 같은지 비교하여 한번의 체크로 문자열이 비어있는지 판단하고 true, false로 리턴해준다.
56 | 한번의 연산으로 끝나므로 시간복잡도는 O(1) 이다.
57 |
58 | ## 결론
59 | 1번과 2번의 시간복잡도를 비교해보았을 때,
60 | 문자열의 길이가 얼마나 길지는 알 수 없겠지만 무조건 2번의 경우가 시간복잡도가 작거나 같다.
61 | 작은 차이일 수도 있지만, 효율을 고려하면 O(1)인 2번 즉, `isEmpty` 를 사용하는 것이 좋다.
--------------------------------------------------------------------------------
/iOS/rootViewController.md:
--------------------------------------------------------------------------------
1 | # rootViewController 란?
2 |
3 | [공식문서](https://developer.apple.com/documentation/uikit/uiwindow/1621581-rootviewcontroller)
4 |
5 |
6 |
7 | > `Instance Proeprty`
8 | > The root view controller for the window.
9 |
10 |
11 | ```
12 | 윈도우를 위한 root view controller 이다. (이름 그대로임)
13 | ```
14 |
15 | - `rootViewController`는 윈도우의 `content view`를 제공한다.
16 | - `viewController`를 이 프로퍼티에 할당함으로써, `viewController`의 `view`를 윈도우의 `content view`로 설정한다.
17 |
18 | ## window 란?
19 | > `Class`
20 | > The backdrop for your app's user interface and the object that despatches events to your views.
21 |
22 | ```
23 | UIWindow는 나의 뷰에서 일어나는 이벤트를 처리하고 UI와 객체의 뒷바탕을 제공한다.
24 | ```
25 |
26 | 쉽게 말하면, 뷰에서 발생한 이벤트들을 분기(이벤트가 일어나야할 객체로 전달)하고 앱의 컨텐츠를 보여주는 메인 window를 제공합니다.
27 |
28 | - window는 눈에는 보이지 않지만, 앱의 view를 그리는데 매우 중요한 역할을 한다.
29 | - 화면에 표시되는 모든 view들은 window로 묶여 있고, 각각의 window는 다른 view들과 독립적이다.
30 | - 앱에서 이벤트를 받으면 처음에는 해당 view객체로 전달되고, 이벤트가 해당 view로 전달된다.
31 | - window는 vc를 이용해 방향 변경을 구현한다.
32 |
33 |
34 | ## 스토리보드 사용 시
35 | - 스토리 보드를 사용하면 window객체가 자동 생성되기 때문에 직접 구현할 일은 없다.
36 |
37 | ```swift
38 | // SceneDelegate.swift
39 |
40 | class SceneDelegate: UIResponder, UIWindowSceneDelegate {
41 |
42 | var window: UIWindow?
43 |
44 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
45 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
46 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
47 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
48 | guard let _ = (scene as? UIWindowScene) else { return }
49 | }
50 |
51 | ...
52 |
53 | }
54 | ```
55 |
56 | → sceneDelegate파일을 살펴보면 UIWIndow를 상속받은 window가 있는 것을 볼 수 있다.
57 |
58 |
--------------------------------------------------------------------------------
/CS/알고리즘/DFS&BFS.swift:
--------------------------------------------------------------------------------
1 | /*
2 | BFS / DFS 문제 구분
3 | // 차이
4 | BFS -> 너비 우선 순회 (queue)
5 | - 현재 나의 위치에서 가장 가까운 노드를 먼저 방문하는 것
6 | - 방문하면 현재 위치는 pop해주고 방문한 곳은 체크
7 | - 방문할 곳은 queue에 넣는 과정
8 | -> 미로탐색은 최단거리만 가지고 탈출하는 것이기에 BFS가 적합
9 | DFS -> 깊이 우선 순회 (stack, recursive)
10 | - 이동할 때마다 가중치가 붙어서 이동하거나, 이동에 제약이 있는 경우 DFS로 구현하는 것이 좋다.
11 | - 탐색 시간은 더 걸리겠지만, 가중치에 대한 변수를 지속해 관리할 수 있다는 장점이 있어서 코드 구현이 편리하다.
12 | */
13 |
14 | import Foundation
15 |
16 | // queue
17 | public struct Queue {
18 | fileprivate var array = [T]()
19 |
20 | public var isEmpty: Bool {
21 | return array.isEmpty
22 | }
23 |
24 | public var count: Int {
25 | return array.count
26 | }
27 |
28 | public mutating func enquque(_ element: T) {
29 | array.append(element)
30 | }
31 |
32 | public mutating func dequeue() -> T? {
33 | if isEmpty {
34 | return nil
35 | } else {
36 | return array.removeFirst()
37 | }
38 | }
39 |
40 | public var front: T? {
41 | return array.first
42 | }
43 | }
44 |
45 | let graph = [
46 | [], // 0
47 | [2,3], // 1
48 | [1,4,5], // 2
49 | [1,6,7], // 3
50 | [2], // 4
51 | [2], // 5
52 | [3], // 6
53 | [3,8], // 7
54 | [7] // 8
55 | ]
56 |
57 | var visited = Array.init(repeating: false, count: graph.count)
58 |
59 | // dfs
60 | func dfs(start: Int) {
61 | visited[start] = true // 시작점
62 |
63 | print(start, terminator: " ")
64 |
65 | for i in graph[start] { // 왼쪽부터 순회
66 | if !visited[i] {
67 | // print(visited)
68 | dfs(start: i)
69 | }
70 | }
71 | }
72 |
73 | // dfs(start: 1)
74 |
75 | var queue = Queue()
76 | // bfs
77 | func bfs(start: Int) {
78 | queue.enquque(start) // 시작점 큐에 넣기
79 | visited[start] = true // 시작점 방문으로 체크
80 |
81 | while !queue.isEmpty {
82 | guard let elem = queue.dequeue() else { return }
83 | print(elem, terminator: " ")
84 |
85 | for i in graph[elem] {
86 | if !visited[i] {
87 | queue.enquque(i)
88 | visited[i] = true
89 | }
90 | }
91 | }
92 | }
93 |
94 | // bfs(start: 1)
95 |
--------------------------------------------------------------------------------
/CS/알고리즘/완전탐색.md:
--------------------------------------------------------------------------------
1 | # 완전탐색 (Brute Force)
2 |
3 |
4 | 가능한 모든 경우의 수를 다 체크해서 정답을 찾는 방법. → 무식하게 다 순회하면 됨
5 |
6 | CS에서 문제 해결 알고리즘을 사용할 때 기본적인 두가지 규칙.
7 | ```
8 | 1) 사용된 알고리즘이 적절한가 (문제를 해결할 수 있는가)
9 | 2) 효율적으로 동작하는가
10 | ```
11 |
12 | # 완전탐색 기법을 사용하는 경우
13 |
14 | **고려할 사항은 다음과 같다**
15 | ```
16 | 1) 가능한 경우의 수를 대략적으로 계산
17 | 2) 가능한 모든 방법을 다 고려
18 | 3) 실제 답을 구할 수 있는지 적용
19 | ```
20 |
21 | **2에서의 모든 방법**
22 | ```
23 | - 완전탐색 → 반복/조건문을 통해 모든 경우의 수를 테스트하는 방법
24 | - 순열(Permutation) → n개의 원소 중 r개의 원소를 중복 허용없이 나열하는 방법
25 | - 재귀 호출 (Recursive)
26 | - BFS, DFS
27 | - 비트마스크 - 2진수 표현 기법을 활용하는 방법
28 | ```
29 |
30 | ## 순열
31 |
32 | 순열은 임의의 수열이 있을 때, 그것을 다른 순서로 연산하는 방법이다.
33 | 즉, 순서가 중요하다! → [1, 2, 3] ≠ [3, 2, 1]
34 | 같은 데이터가 입력된 수열이지만, 그 순서에 따라 의미가 있고 이 순서를 통해 연결되는 이전/다음 수열을 찾아낼 수 있는 경우에 계산할 수 있다.
35 |
36 | ## 재귀
37 |
38 | 자기 자신을 호출하는 것을 의미한다.
39 | 예를 들어 “숫자 n개 중 m개를 고르는 경우”를 보자.
40 | 이중 반복문으로 문제를 해결하게 되면, n과 m이 매우 큰 숫자라면..?
41 | 재귀 함수를 활용하여 자기 자신을 호출함으로써 다음 숫자를 선택할 수 있도록 이동시켜 전체 코드를 짧게 줄일 수 있다.
42 |
43 | **포인트**
44 |
45 | 1) 재귀를 탈출하기 위한 탈출 조건이 필요하다
46 |
47 | - 이것이 없으면 n개를 모두 골랐음에도 더 숫자를 선택하고자 하여
48 | - 선택된 숫자를 저장하는 배열에 범위 초과 오류가 나거나
49 | - 다른 자료구조를 쓴 경우 잘못된 출력이 나올 수 도 있고
50 | - 무한 루프가 발생할 수 있다.
51 |
52 | 2) 현재 함수의 상태를 저장하는 parameter가 필요하다
53 |
54 | - current, count를 통해 어떤 숫자까지 선택했는지, 몇 개를 선택했는지 전달해야 한다.
55 | - 이것이 없다면 현재 함수의 상태를 저장할 수 없어서, 재귀 탈출 조건을 만들 수 없게 되거나 잘못된 결과를 출력하게 된다.
56 |
57 | 3) return 문을 신경써야 한다
58 |
59 | - 재귀를 통해 이후의 연산 결과를 반환 후 이전 결과에 추가 연산을 수행하는 경우도 있을 수 잇다.
60 | - 즉, 문제 해결을 위한 정확한 정의를 수행해야 한다.
61 |
62 | **DP와 매우 흡사**
63 |
64 | top-down을 사용하면 재귀를 통해 수행하는데, 기저 사례를 통해 탈출 조건을 만들고, 현재 함수의 상태를 전달하는 parameter를 전달하고, return을 통해 필요한 값을 반환하여 ㄷ바을 구하는 연산에 사용한다.
65 |
66 | **완전 탐색의 재귀와 DP의 차이점**
67 |
68 | DP는 작은 문제가 큰 문제와 동일한 구조를 가져 큰 문제의 답을 구할 시에 작은 문제의 결과를 기억한 뒤 그대로 사용하여 수행 속도를 빠르게 한다.
69 |
70 | 완전탐색은 크고 작은 문제의 구조가 다를 수 있고, 이전 결과를 반드시 기억하는 것이 아니라 해결 가능한 방법을 모두 탐색한다는 차이가 잇다.
71 |
72 | ## BFS, DFS
73 |
74 | - 그래프 자료 구조에서 모든 정점을 탐색하기 위한 방법이다.
75 |
76 | BFS (너비 우선 탐색)
77 |
78 | - 현재 정점과 인접한 정점을 우선으로 탐색한다.
79 |
80 | DFS (깊이 우선 탐색)
81 |
82 | - 현재 인접한 정접을 탐색 후 그 다음 인접한 정점을 탐색하여 끝까지 탐색한다.
83 |
--------------------------------------------------------------------------------
/iOS/data_prefetching.md:
--------------------------------------------------------------------------------
1 | # API call Pagination 구현 시 Prefetching 작업이 Smooth 하지 않을 때
2 | : collectionView에서 unsplash API를 통해 이미지를 불러오는 작업을 할 때 발생한 이슈 (tableView도 마찬가지)
3 |
4 | ## Touble
5 | - 처음엔 `PrefetchDataSource`를 사용하여 구현하였다. 그런데 빠르게 스크롤하여 `collectionView`의 최하단에 도달하는 경우 prefetch 가 되지 않았다.
6 | - 다시 약간 위로 스크롤 후 아래로 스크롤 해야 prefetch 작업이 이루어졌다.
7 | - 위의 코드는 다음과 같다.
8 | ```swift
9 | override viewDidLoad() {
10 | super.viewDidLoad()
11 |
12 | collectionView.prefetchDataSource = self
13 | }
14 |
15 | ...
16 |
17 | extension ViewController: UICollectionViewDataSourcePrefetching {
18 | func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) {
19 | for indexPath in indexPaths {
20 | if unsplashList.count - 1 == indexPath.item {
21 | page += 1
22 | fetchImages(page: page)
23 | }
24 | }
25 | }
26 | }
27 | ```
28 |
29 | ## Shooting
30 | - 그래서 현재 스크롤 위치를 기준으로 데이터를 Prefetch 해오도록 코드를 수정하였다.
31 | - `scrollViewDidScroll(_ scrollView:)` 함수에서
32 | `collectionView의 cntentView의 origin y의 offset` 이
33 | `contentView의 높이 - bound 즉, 자신만의 상대적 좌표계를 기준으로 계산한 collectionView에서의 높이` 보다 크거나 같은지를 비교하여
34 | 현재 스크롤하고 있는 위치가 컬렉션뷰의 최하단에 도착하면 api 호출을 하도록 구현하였다.
35 |
36 | (*헷갈린다면 view에서의 [frame / bounds](https://nareunhagae.tistory.com/3) 그리고 origin과 offset의 개념을 체크해보세요.)
37 |
38 | - 위의 코드는 다음과 같다.
39 | ```swift
40 | func scrollViewDidScroll(_ scrollView: UIScrollView) {
41 | if collectionView.contentOffset.y >= (collectionView.contentSize.height - collectionView.bounds.size.height) {
42 | page += 1
43 | fetchImages(page: page)
44 | }
45 | }
46 | ```
47 |
48 | ## Conclusion
49 | - `prefetchDataSource`를 `tableView`에서 사용할 때는 마지막 index에 도달하기 전에 prefetch 작업을 해주면 되었었는데, `collectionView`는 cell을 어떻게 구성하느냐에 따라 화면 최하단에 보여질 index의 갯수가 달라진다.
50 | - `prefetchDataSource` 보다 `scrollViewDidScroll`를 사용하는 것이 현재 프로젝트에서 좀 더 smooth 하게 스크롤, data fetching 되는 것 같다고 느껴졌다.
51 | - `prefetchDataSource`에서도 조금 수정하면 이 부분을 보완할 수 있을 것 같지만, 지금은 간단하게 이미지 다운로드 시 비동기 처리 부분을 연습하기 위해 만든 프로젝트 이므로 추후에 해결방법을 추가작성 해보려 한다.
52 |
--------------------------------------------------------------------------------
/iOS/presentedVC_presentingVC.md:
--------------------------------------------------------------------------------
1 | # PresentedViewController
2 | > `Instance Property` presentedViewController
3 | > The view controller that is presented by this view controller, or one of its ancestors in the view controller hierarchy.
4 |
5 | ```swift
6 | var presentedViewController: UIViewController? { get }
7 | ```
8 |
9 | - 지금 ViewController가 띄우고 있는 VieController
10 | - `present(_: animated: completion:)` 메서드를 통해 vc(⬛️)를 모달형식으로 보여주고자 할 때,
11 | - 이 메서드를 부른 vc(⬛️)가 보여주고 있는 vc(🟩)에 대해 이 프로퍼티를 가지고 있다. (즉, 현재 vc가 띄울 vc)
12 | - 만약 현재 vc(⬛️)가 다른 vc(🟩 이 없는 경우)를 모달형식으로 띄워주고 있지 않다면 이 프로퍼티는 `nil` 이다.
13 |
14 | # PresentingViewController
15 | > `Instance Property` presentingViewController
16 | > The view controller that presented this view controller.
17 |
18 | ```swift
19 | var presentingViewController: UIViewController? { get }
20 | ```
21 |
22 | - 지금 ViewController를 띄우는 VieController
23 | - `present(_: animated: completion:)` 메서드를 통해 vc(⬛️)를 모달형식으로 보여주고자 할 때,
24 | - 이 메서드에 의해 불려진 vc(🟧)에 대해 이 프로퍼티를 가지고 있다. (즉, 현재 vc를 띄운 vc)
25 | - 만약 현재 vc(⬛️)를 띄운 부모들(ancestors) 중 그 누구도 모달형식으로 띄워지지 않았다면 이 프로퍼티는 `nil` 이다.
26 |
27 | # parent
28 | > `Instance Property` parent
29 | > The parent view controlelr of the recipient.
30 |
31 | - 만약 수용자의 `container view controller`의 자식(child)이라면, 이 프로퍼티는 자신이 포함되어 있는 `view controller`를 들고 있다.
32 | - 만약 수용자가 부모가 없다면, 이 프로퍼티는 `nil` 이다.
33 | - (여기서 수용자(recipient)는 이 프로퍼티를 부르고 있는 뷰컨을 말하는 것 같다.)
34 |
35 | - `iOS 5.0` 이후로, 만약 부모 vc이 없거나 다른 뷰에 의해 띄워진 뷰의 경우, `presenting view controller` 가 리턴된다.
36 | - 따라서, `iOS 5.0` 이후 부터는 parent를 쓸 필요없이 `presentingViewController` 프로퍼티를 사용하면 된다.
37 |
38 |
39 |
40 | 
41 |
42 |
43 |
44 | # 요약
45 | 각각의 사각형을 뷰컨이라고 하자.
46 | 🟧 가 ⬛️ 를 present 하고 있고, ⬛️ 가 🟩 를 present 하고 있다.
47 |
48 |
49 |
50 | 그때,
51 | - ⬛️ 를 띄우고 있는 그 하위 뷰컨 : 🟧 (presnetingViewController)
52 | - ⬛️ 가 띄우고 있는 그 상위 뷰컨 : 🟩 (presentedViewController)
53 |
54 |
55 |
56 | ⬛️ 의 parent는 🟧 이다.
57 | - 그리고 그 parent 는 iOS 5.0 이후 부터 부모가 없거나 다른 뷰에 의해 띄워진 경우 presenting view controlelr 를 리턴해주므로,
58 | - presentingViewController 프로퍼티를 사용하면 동일한 효과를 낼 수 있다.
59 |
--------------------------------------------------------------------------------
/CS/패러다임/FP.md:
--------------------------------------------------------------------------------
1 | # Funtional Programming (함수형 프로그래밍)
2 |
3 | > Swift는
4 | > FP이면서 동시에 OOP의 상속, 은닉, 캡슐화, 추상화 등을 제공하며 POP 적인 특징도 가지고 있는 멀티 패러다임 언어이다.
5 | > 그러므로 FP에 대해서도 알고 넘어가자.
6 |
7 |
8 | ## 함수형 프로그래밍이란?
9 | - `순수 함수`를 기반으로 하는 프로그래밍 패러다임이다.
10 | - 함수형 프로그래밍은 자료 처리를 수학적 함수의 계산을 취급하고, 상태와 가변데이터를 멀리하는 프로그래밍 패러다임이다.
11 | - 일반적인 명령형 프로그래밍은 상태 값을 변경할 수 있어 예측하지 못한 에러(side-effect)를 발생시킬 가능성이 있다.
12 | - 함수형 프로그래밍에서는 함수가 `일급 객체`가 될 수 있다.
13 |
14 | ```
15 | 그러므로 함수형 프로그래밍을 이해하려면 `순수 함수`와 `일급객체`가 무엇인지에 대한 이해가 선행되어야 한다!
16 | ```
17 |
18 | ## 순수 함수란?
19 | > : Pure Fuction
20 |
21 | - 어떠한 입력에 대해 항상 같은 출력을 만드는 함수를 의미한다.
22 | - 즉, 외부의 영향을 받거나 주지 않는 것, `side-effect`(부수 효과)가 없는 것을 말한다.
23 | - 이하 순수 함수에 대한 자세한 내용은 [순수함수에 대하여](pure_function.md)를 참고하자.
24 |
25 | ## side-effect 란?
26 | - 함수를 통해 함수 외부 값의 상태(`state`)가 변하는 것을 의미한다.
27 |
28 | ## 일급객체란?
29 | > : First-class Citizen
30 |
31 | - 함수의 인자로 넘길 수 있고, 변수에 저장이 가능하며, 리턴값으로 반환이 가능한 객체를 의미한다.
32 | - 더 자세히 말하면 아래의 조건을 충족하면 1급 객체라고 본다.
33 | - 런타임에도 생성이 가능하다.
34 | - 인자 값으로 전달할 수 있어야 한다.
35 | - 반환값으로 사용할 수 있어야 한다.
36 | - 데이터 구조 안에 저장할 수 있어야 한다.
37 | - 일급객체의 대표적인 예는 클로저(closure) 이다.
38 | - 자세한 내용은 [1급객체란](first_class_citizen.md)을 참고하자.
39 |
40 | ## 함수형으로 디자인 하기 위한 조건
41 | - 모듈 방식
42 | - 각각의 프로그램을 반복하여 작은 단위로 쪼개야 한다.
43 | - 모든 기능 조각을 조림하여 완성된 프로그램을 정의할 수 있어야 한다.
44 | - 거대한 프로그램을 작은 조각으로 분해했을 때, 각각의 조각들이 상태를 공유하는 것을 피해야 한다.
45 | - 상태 오염
46 | - 가변 상태를 피하도록 값을 통해 프로그래밍한다.
47 | - side-effect가 발생하지 않도록 데이터의 의존성이 생기지 않도록 한다.
48 | - 타입
49 | - 다입의 사용을 신중하게 한다.
50 | - 데이터 타입의 신중한 선택은 코드를 견고, 안전, 강력하게 작성하도록 도와준다.
51 |
52 |
53 | ## 참고하면 좋은 링크
54 | - [Swift와 함수형 프로그래밍의 역사 - realm](https://academy.realm.io/kr/posts/tryswift-rob-napier-swift-legacy-functional-programming/)
55 | - [Functional Swift](https://www.objc.io/books/functional-swift/)
56 | - [Beginenrs guide to functional Swift](https://theswiftdev.com/beginners-guide-to-functional-swift/)
57 | - [Swift Functional Programming - PacktPublishing Github](https://github.com/PacktPublishing/Swift-Functional-Programming)
58 | - [An Introduction to Functional Programming in Swift - Raywenderich](https://www.raywenderlich.com/9222-an-introduction-to-functional-programming-in-swift)
59 |
60 | ## 추가할 내용
61 | - 명령형 -> 함수형으로 바꾸어보는 코드 예시
62 |
--------------------------------------------------------------------------------
/CS/자료구조/dynamicMemoryInC.md:
--------------------------------------------------------------------------------
1 | # 동적 메모리 할당 (C언어)
2 | - 동적 메모리가 할당되는 공간은 heap 이라고 한다.
3 | - heap은 운영체제가 사용되지 않는 메모리 공간을 모아 놓은 곳이다.
4 | - 필요한 만큼만 할당 받고, 필요할 때 사용하고 반납하기 때문에 메모리를 매우 효율적으로 사용할 수 있다.
5 |
6 |
7 | ## 동적메모리 할당 예제
8 | ```c
9 | // 전형적인 동적 메모리 할당 코드
10 |
11 | int *p;
12 | p = (int *)malloc(sizeof(int)); // 동적 메모리 할당
13 | *p = 1000; // 동적 메모리 사용
14 | free(p); // 동적 메모리 반납
15 | ```
16 |
17 | - `malloc()` 함수는 `size` 바이트 만큼의 메모리 블록을 할당한다.
18 | - `sizeof` 키워드는 변수나 타입의 크기를 숫자로 반환한다. 크기의 단위는 바이트가 된다.
19 | - `malloc()`은 동적 메모리 블록의 시작 주소를 반환한다.
20 | - 반환되는 주소의 타입은 `void *`이므로 이를 적절한 포인터로 형변환시켜야 한다.
21 | - 메모리 확보가 불가능하면 `NULL`을 함수의 반환값으로 반환한다.
22 | - 동적 메모리는 포인터로만 사용할 수 있다.
23 | - `free()` 함수는 할당된 메모리 블록을 운영체제에게 반환한다.
24 | - 여기서 주의할 점은 `malloc()` 함수가 반환했던 포인터 값을 잊어버리면 안된다는 것. 포인터값을 잊어버리면 동적 메모리를 반환할 수 없다.
25 | - `malloc()`은 시스템의 메모리가 부족해서 요구된 메모리를 할당할 수 없으면 `NULL`을 반환한다.
26 | - 따라서 `malloc()`의 반환값은 항상 `NULL`인지 검사해야 한다.
27 |
28 |
29 | ```c
30 | // 동적 메모리 할당 예제 1
31 | #include
32 | #include
33 | #include
34 |
35 | #define SIZE 10
36 |
37 | int main(void) {
38 | int *p;
39 |
40 | p = (int *)malloc(SIZE * sizeof(int));
41 |
42 | if(p == NULL) {
43 | fprintf(stderr, "메모리 부족. 할당 불가\n");
44 | exit(1);
45 | }
46 |
47 | for (int i = 0; i < SIZE; i++) {
48 | p[i] = i;
49 | }
50 |
51 | for (int i = 0; i < SIZE; i++) {
52 | printf("%d", p[i]);
53 | }
54 |
55 | free(p);
56 | return 0;
57 | }
58 |
59 | ```
60 |
61 | ```c
62 | // 동적 메모리 할당 예제 2
63 | #include
64 | #include
65 | #include
66 |
67 | typedef struct studentTag {
68 | char name[10];
69 | int age;
70 | double gpa;
71 | } student;
72 |
73 | int main(void) {
74 | student *s;
75 |
76 | s = (student *)malloc(sizeof(student)); // student 구조체를 나타내는 포인터 s 선언
77 |
78 | if (s == NULL) {
79 | fprintf(stderr, "메모리 부족. 할당 불가\n");
80 | exit(1);
81 | }
82 |
83 | strcpy(s -> name, "Kim");
84 | s -> age = 20;
85 |
86 | free(s);
87 | return 0;
88 | }
89 | ```
90 | - `malloc()` 함수를 이용하여 `Student` 구조체를 동적으로 생성
91 | - `(*s).name` 이라고 표기할 수 있지만 `s -> name` 이 좀 더 편리하여 사용
92 |
--------------------------------------------------------------------------------
/iOS/imageCachingExtension.md:
--------------------------------------------------------------------------------
1 | ## Image 를 캐싱하는 Extension 코드
2 |
3 | - 이미지를 캐싱할 때 Kingfisher 라이브러리를 사용하는 방법이 가장 간단하지만,
4 | - 라이브러리에 대한 의존도를 낮추고 스위프트 기본 제공 라이브러리를 사용해서 캐싱을 구현하고자 한다면
5 | - 아래와 같은 `URLSession`과 `NSCache`를 활용하여 구현한 `Extension`을 작성하여 사용하면 용이하다.
6 |
7 | > ImageLoader.swift
8 |
9 | ```swift
10 | import UIKit
11 |
12 | enum ImageLoaderError: Error {
13 | case unknown
14 | case invalidURL
15 | }
16 |
17 | struct ImageLoader {
18 | let url: String
19 |
20 | func load(completion: @escaping (Result) -> Void) {
21 | if let url = URL(string: self.url) {
22 | URLSession.shared.dataTask(with: url) { data, response, error in
23 | guard (response as? HTTPURLResponse)?.statusCode == 200,
24 | error == nil,
25 | let data = data,
26 | let image = UIImage(data: data) else {
27 | completion(.failure(.unknown))
28 | return
29 | }
30 |
31 | completion(.success(image))
32 | }.resume()
33 | } else {
34 | completion(.failure(.invalidURL))
35 | }
36 | }
37 | }
38 |
39 | ```
40 |
41 | > UIImageView++Extension.swift
42 |
43 | ```swift
44 | import UIKit
45 |
46 | let imageCache = NSCache()
47 |
48 | extension UIImageView {
49 | func loadThumbnail(urlSting: String?) {
50 | guard let urlSting = urlSting else { return }
51 | image = nil
52 |
53 | if let imageFromCache = imageCache.object(forKey: urlSting as AnyObject) {
54 | image = imageFromCache as? UIImage
55 | return
56 | }
57 |
58 | ImageLoader(url: urlSting).load() { [weak self] result in
59 | guard let self = self else { return }
60 | switch result {
61 | case .success(let image):
62 | imageCache.setObject(image, forKey: urlSting as AnyObject)
63 | DispatchQueue.main.async {
64 | self.image = image
65 | }
66 |
67 | case .failure(_):
68 | self.image = UIImage(named: "indicator")
69 | }
70 | }
71 | }
72 | }
73 | ```
74 |
--------------------------------------------------------------------------------
/CS/네트워크/3wayHandshake_4wayHandshake.md:
--------------------------------------------------------------------------------
1 |
2 | ## 3 way handshake
3 |
4 | - TCP는 장치들 사이에서 논리적인 접속을 성립(establish) 하기 위하여 이 기법을 사용한다.
5 | - TCP/IP 프로토콜을 이용해서 통신을 하는 응용프로그램이 데이터를 전송하기 전에 먼저 정확한 전송을 보장하기 위해 상대방 컴퓨터와 사전에 세션을 수립하는 과정을 의미한다.
6 |
7 | ```
8 | TCP 접속을 성공적으로 성립하기 위해서는 반드시 필요한 절차이다.
9 | ```
10 |
11 | > 역할
12 |
13 | - 양쪽 모두 데이터를 전송할 준비가 되어있다는 것을 보장하고, 실제로 데이터 전달이 시작하기 전에 한쪽이 다른쪽이 준비되었다는 것을 알 수 있도록 한다.
14 | 이 신호가 `ACK` 이다.
15 |
16 |
17 |
18 | > 과정
19 |
20 |
21 |
22 |
23 |
24 | ```
25 | 클라이언트 > 서버 : TCP SYN
26 | 서버 > 클라이언트 : TCP SYN ACK
27 | 클라이언트 > 서버 : TCP ACK
28 |
29 | * SYN = Synchronize numbers
30 | * ACK = Acknowledgement
31 | ```
32 | (1)
33 | 클라이언트는 서버에 접속을 요청하는 `SYN` 패킷을 보낸다.
34 | 클라이언트가 `SYN`을 보내고 `SYN`/`ACK` 응답을 기다리는 `SYN_SENT`상태가 된다.
35 |
36 |
37 |
38 | (2)
39 | 서버는 `SYN` 요청을 받고 클라이언트에게 요청을 수락한다는 `ACK`와 `SYN` 플래그가 설정된 패킷을 발송하고
40 | 클라이언트가 다시 `ACK`로 응답하기를 기다린다.
41 | 이때 서버는 `SYN_RECEIVED` 상태가 된다.
42 |
43 |
44 |
45 | (3)
46 | 클라이언트는 서버에게 ACK를 보내고 이후로부터는 연결이 이루어지고 데이터가 오가게 되는 것이다,
47 | 이때 서버의 상태는 `ESTABLISHED` 이다.
48 |
49 | ```
50 | 위와 같은 방식으로 통신하는 것이 신뢰성 있는 연결을 맺어준다는 TCP의 3-Way-Handshake 방식이다.
51 | ```
52 |
53 |
54 |
55 |
56 | ## 4 way handshake
57 |
58 | `3-Way-Handshake`는 TCP의 연결을 초기화할 떄 사용한다면,
59 | `4-Way-Handshake`는 세션을 종료하기 위해 수행되는 절차이다.
60 |
61 | > 과정
62 |
63 |
64 |
65 |
66 |
67 | (1)
68 | 클라이언트가 연결을 종료하곘다는 `FIN` 플래그를 전송한다.
69 |
70 |
71 |
72 | (2)
73 | 서버는 일단 확인메세지를 보내고, 자신의 통신이 끝날 때까지 기다리는 데 이 상태가 `TIME_WAIT` 이다.
74 |
75 |
76 |
77 | (3)
78 | 서버가 통신이 끝났으면 연결이 종료되었다고 클라이언트에게 `FIN` 플래그를 전송한다.
79 |
80 |
81 |
82 | (4)
83 | 클라이언트는 확인했다는 메세지를 보낸다.
84 |
85 |
86 |
87 | ### Q.
88 | ```
89 | 만약 서버에서 FIN을 전송하기 전에 전송한 패킷이 라우팅 지연이나 패킷 유실로 인한 재전송 등으로 인해 FIN 패킷보다 늦게 도착하는 상황이 발생한다면??
90 | ```
91 |
92 | ### A.
93 | ```
94 | 클라이언트에서 세션을 종료시킨 후 뒤늦게 도착하는 패킷이 있다면 이 패킷은 Drop 되고 데이터는 유실된다.
95 | 이러한 현상을 대비하여 클라이언트는 서버로부터 FIN을 수신하더라도 일정시간(default 240초) 동안 세션을 남겨놓고 잉여 패킷을 기다리는 과정을 거친다.
96 | 이 과정을 "TIME_WAIT" 이라고 한다.
97 | ```
98 |
--------------------------------------------------------------------------------
/iOS/loadView_vs_viewDidLoad.md:
--------------------------------------------------------------------------------
1 | # loadView() 와 viewDidLoad() 의 차이
2 | > loadView()
3 |
4 | 뷰 컨트롤러가 자신의 메인 뷰 (`self.view`)를 로드할 때 호출되는 메서드이다.
5 |
6 | 즉, 그 메인 뷰를 생성하려고 호출하는 메서드 인것. 그래서 이 메서드 안에서 새로운 뷰를 만들어서 반환해줘도 된다.
7 | ```
8 | 스토리보드를 사용하는 경우, 스토리보드에 있는 뷰를 가져와 사용할 테니 굳이 사용할 필요가 없다.)
9 | ```
10 |
11 |
12 |
13 | > viewDidLoad()
14 |
15 | 위의 뷰가 모두 생성되고 메모리에 올라간 후 호출되는 메서드 이다.
16 |
17 | 즉 뷰컨트롤러의 메인 뷰가 생성된 이후 하고 싶은 작업에 대해서 작성하면 되는 메서드이다.
18 |
19 | ```
20 | 간단하게 말하면,
21 | loadView()는 뷰가 로드되기 시작할 때 불려지고, viewDidLoad()는 뷰 로드가 완료된 후 불려진다.
22 | ```
23 |
24 |
25 |
26 | ## 결론
27 | > `loadView 관련 공식 문서` :
28 | > Your custom implementation of this method should not call super.
29 | > If you use Interface Builder to create your views and initialize the view controller, you must not override this method.
30 |
31 | - loadView()는
32 | - `viewController`에서 `root view`를 그리는 메서드이고, `viewDidLoad()` 이전에 실행된다.
33 | - 뷰를 직접 초기화 해주어야 한다.
34 | - 코드로 직접 뷰 컨트롤러를 그리는 경우에만 사용해야 한다. (custom view 대입 등)
35 | ```swift
36 | override loadView() {
37 | self.view = customView
38 | }
39 | ```
40 |
41 | ## 주의할 점!
42 | - 루트뷰(view)에 커스텀뷰를 대입하여 사용할 때는 `super.loadView()`를 쓰면 안된다.
43 | - IB(Inetface Builder)를 사용하여 구현하는 경우는 super.loadView()를 호출해야 하자만, 커스텀뷰를 사용하는 경우는 IB로 뷰를 생성할 필요가 없기 때문이다.
44 | - IB로 뷰를 생성한 다음에 다시 내가 만든 커스텀뷰를 대입한다? -> 낭비
45 |
46 | - 정리하면...
47 | - `loadView()`는 뷰 컨트롤러의 루트뷰를 만들고 로드하는 메서드이다.
48 | - IB를 통해 루트 뷰를 만들었을 땐 loadView()를 애초에 override 하지 않는게...
49 | - 코드로 작성한 커스텀 뷰를 루트 뷰로 지정해주고 싶을 때만, override해서 구현하면 된다.
50 | - loadView()의 역할이 궁금하다면 아래의 'loadView()란' 링크를 살펴보길 바란다.
51 |
52 | ## 용도에 따른 사용
53 | - `loadView`는 뷰 컨트롤러의 기본 `view`를 custom view로 사용하고자 할 때 유용하다.
54 | - `loadView`에서는 새로운 view를 생성해서 `return`해주는 것을 코드를 까보면 알 수 있다.
55 | - 반면에 기본 `UIView`를 ciewController의 기본 `view`로 사용하고, 그 위에 무언가 얹어서 쓰거나 뷰가 생성된 이후에 어떤 세팅을 해서 사용하고 싶다면 `viewDidLoad`에서 하면 된다.
56 |
57 | > 공식문서
58 |
59 | [loadView()란?](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621454-loadview)
60 |
61 | [viewDidLoad()란?](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621495-viewdidload)
62 |
63 | > 참고 링크
64 |
65 | [loadView에서 super.loadView()를 부르면 안되는 이유](https://stackoverflow.com/questions/9105450/should-super-loadview-be-called-from-loadview-or-not)
66 |
--------------------------------------------------------------------------------
/Swift/기존방법과_async_await의차이.md:
--------------------------------------------------------------------------------
1 | 기존의 비동기작업 시, 비동기처리, 분기처리 등의 클로저가 중첩된 형태로 작성하게 된다.
2 |
3 | 그렇게 되면...
4 |
5 | - 가독성이 떨어지고
6 |
7 | ```swift
8 | // deeply-nested closures
9 | func processImageData1(completionBlock: (_ result: Image) -> Void) {
10 | loadWebResource("dataprofile.txt") {
11 | dataResource in loadWebResource("imagedata.dat") {
12 | imageResource in decodeImage(dataResource, imageResource) {
13 | imageTmp in dewarpAndCleanupImage(imageTmp) {
14 | imageResult in completionBlock(imageResult)
15 | }
16 | }
17 | }
18 | }
19 | }
20 |
21 | processImageData1 { image in
22 | display(image)
23 | }
24 | ```
25 |
26 | - 콜백은 오류처리를 어렵고 장황하게 만든다.
27 |
28 | ```swift
29 | // Using a `switch` statement for each callback:
30 | func processImageData2c(completionBlock: (Result) -> Void) {
31 | loadWebResource("dataprofile.txt") {
32 | dataResourceResult in
33 | switch dataResourceResult {
34 | case .success(let dataResource):
35 | loadWebResource("imagedata.dat") { imageResourceResult in
36 | switch imageResourceResult {
37 | case .success(let imageResource):
38 | decodeImage(dataResource, imageResource) { imageTmpResult in
39 | switch imageTmpResult {
40 | case .success(let imageTmp):
41 | dewarpAndCleanupImage(imageTmp) { imageResult in
42 | completionBlock(imageResult)
43 | }
44 | case .failure(let error):
45 | completionBlock(.failure(error))
46 | }
47 | }
48 | case .failure(let error):
49 | completionBlock(.failure(error))
50 | }
51 | }
52 | case .failure(let error):
53 | completionBlock(.failure(error))
54 | }
55 | }
56 | }
57 |
58 | processImageData2c { result in
59 | switch result {
60 | case .success(let image):
61 | display(image)
62 | case .failure(let error):
63 | display("No image today", error)
64 | }
65 | }
66 | ```
67 |
68 |
69 | 위의 문제를 해결하기 위해 `async-await proposal`은 swift의 `coroutine` 모델을 도입했다.
70 |
71 | 비동기 함수의 semetics를 정의하였으나 동시성을 제공하지는 않는다.
72 | 비동기 함수에서 `await`으로 흐름을 제어함으로써 동기적인 코드가 작성 가능하다.
73 |
74 | ```
75 | - 비동기 코드가 마치 동기 코드인 것 처럼 작성할 수 있다.
76 | - 클로저를 활용한 코드보다 상대적으로 가독성이 좋다.
77 | ```
78 |
--------------------------------------------------------------------------------
/iOS/custom_shadow.md:
--------------------------------------------------------------------------------
1 | # 뷰에 원하는 방향에만 그림자 넣기
2 |
3 | - 뷰를 그리다가 사방이 아니라 일부 방향에만 그림자를 넣어주고 싶었다.
4 | - 그림자를 넣어줄 방향에 대한 `enum`을 하나 만들고, `case`에 따라 `switch`문을 통해 분기처리를 하여 `offset`값을 조정하여 원하는 방향에만 그림자 효과를 주었다.
5 |
6 | ```swift
7 | import UIKit
8 |
9 |
10 | extension UIView {
11 | /// 그림자 방향
12 | enum ShadowDirection {
13 | case bottom
14 | case top
15 | case left
16 | case right
17 | }
18 |
19 | /// 사방에 그림자를 넣는 함수
20 | func addShadow(offset: CGSize, color: UIColor = .black, opacity: Float = 0.2, radius: CGFloat = 0) {
21 | self.layer.masksToBounds = false
22 | self.layer.shadowColor = color.cgColor
23 | self.layer.shadowOffset = offset
24 | self.layer.shadowOpacity = opacity
25 | self.layer.shadowRadius = radius
26 | }
27 |
28 | /// 원하는 한 방향에만 그림자를 넣는 함수
29 | func addShadow(direction: ShadowDirection, size: Int, color: UIColor = .black, opacity: Float = 0.2, radius: CGFloat = 0) {
30 | var offSetValue = CGSize()
31 |
32 | switch direction {
33 | case .bottom:
34 | offSetValue = CGSize(width: 0, height: size)
35 | case .top:
36 | offSetValue = CGSize(width: 0, height: -size)
37 | case .left:
38 | offSetValue = CGSize(width: -size, height: 0)
39 | case .right:
40 | offSetValue = CGSize(width: size, height: 0)
41 | }
42 |
43 | addShadow(offset: offSetValue, color: color, opacity: opacity, radius: radius)
44 | }
45 |
46 | /// 원하는 방향들에 그림자를 넣는 함수
47 | func addShadow(directions: [ShadowDirection], size: Int, color: UIColor = .black, opacity: Float = 0.2, radius: CGFloat = 0) {
48 | var offSetValue = CGSize()
49 |
50 | for direction in directions {
51 | switch direction {
52 | case .bottom:
53 | offSetValue = CGSize(width: 0, height: size)
54 | case .top:
55 | offSetValue = CGSize(width: 0, height: -size)
56 | case .left:
57 | offSetValue = CGSize(width: -size, height: 0)
58 | case .right:
59 | offSetValue = CGSize(width: size, height: 0)
60 | }
61 |
62 | addShadow(offset: offSetValue, color: color, opacity: opacity, radius: radius)
63 | }
64 | }
65 | }
66 | ```
67 |
--------------------------------------------------------------------------------
/CS/ETC/깨끗한코드란.md:
--------------------------------------------------------------------------------
1 | # 깨끗한 코드란??
2 |
3 | : 깨끗한 코드에 대한 정의는 프로그래머 수만큼이나 다양할 것이다.
4 | 우리 분야에서 유명하고 노련한 프로그래머들의 의견을 들어보자.
5 |
6 | ## 비야네 스트롭스트룹
7 | > Bjarne Stroupstrup ; C++ 창시자
8 |
9 | - 나는 우아하고 효율적인 코드를 좋아한다.
10 | - 논리가 간단해야 버그가 숨어들지 못한다.
11 | - 의존성을 최대한 줄여야 유지보수가 쉬워진다.
12 | - 오류는 명백한 전력에 의거해 철저히 처리한다.
13 | - 깨끗한 코드는 한 가지를 제대로 한다.
14 | ```
15 | 나쁜 코드는 너무 많은 일을 하려 애쓰다가 의도가 뒤섞이고 목적이 흐려진다.
16 | 깨끗한 코드는 한 가지에 집중하여, 주변 상황에 현혹되거나 오염되지 않은 채 한길만 걷는다.
17 | ```
18 |
19 | ## 그래디 부치
20 | > Grady Booch ; Object Oriented Analysis and Design with Application 저자
21 |
22 | - 깨끗한 코드는 단순하고 직접적이다.
23 | - 깨끗한 코드는 잘 쓴 문장처럼 읽힌다.
24 | - 깨끗한 코드는 결코 설계자의 의도를 숨기지 않는다. 오히려 명쾌한 추상화와 단순한 제어문으로 가득하다.
25 | ```
26 | "가독성"을 강조한다. 코드는 추측이 아니라 사실에 기반해야 하며, 반드시 필요한 내용만 담아야 한다.
27 | ```
28 |
29 | ## 데이브 토마스
30 | > Dave Thomas ; OTI 창집자이자 이클립스 전략의 대부
31 |
32 | - 깨끗한 코드는 작성자가 아닌 사람도 읽고 고치기 쉽다.
33 | - 단위 테스트 케이스와 인수 테스트 케이스가 존재한다.
34 | - 의미 있는 이름이 붙는다.
35 | - 특정 목적을 달성하는 방법은 하나만 제공한다.
36 | - 각 의존서을 명확히 정의한다.
37 | - API는 명확하며 최소로 줄였다.
38 | - 언어에 따라 필요한 모든 정보를 코드만으로 명확하게 표현할 수 없기에, 코드는 문학적으로 표현해야 마땅하다.
39 | ```
40 | 마찬가지로 "가독성"을 강조하지만, 깨끗한 코드란 "다른 사람이 고치기 쉽다"고 단언한다.
41 | 테스트가 없는 코드는 깨끗한 코드가 아니다.
42 | 또한 "문학적"이여아 한다는 말은 인간이 읽기 좋은 코드를 작성하라는 말이다.
43 | ```
44 |
45 | ## 마이클 페더스
46 | > Michael Feathers ; Working Effectively with Legacy Code 저자
47 |
48 | - 깨끗한 코드는 언제나 누군가 주의 깊게 짰다는 느낌을 준다.
49 | - 고치려고 살펴봐도 딱히 손 댈 곳이 없다.
50 | - 작성자가 이미 모든 사항을 고려헸기 때문에 고칠 궁리를 하다보면 언제나 제자리로 돌아온다.
51 | ```
52 | "주의" 깊게 작성한 코드.
53 | 깨끗한 코드란, 누군가 시간을 들여 깔끔하고 단정하고 꼼꼼하게 주의를 기울인 코드이다.
54 | ```
55 |
56 | ## 론 제프리스
57 | > Ron Jeffries ; Extreme Programming Installed 저자
58 |
59 | - 켄트 백이 제안한 단순한 코드 규칙으로 구현을 시작한다. 중요한 순으로 나열하자면 간단한 코드는...
60 | - 모든 테스트를 통과한다.
61 | - 중복이 없다.
62 | - 시스템 내 모든 설계 아이디어를 표현한다.
63 | - 클래스, 메서드, 함수 등을 최대한 줄인다.
64 | - 나는 주로 `중복`에 집중한다.
65 | - 같은 작업을 여러 차례 반복한다면 코드가 아이디어를 제대로 표현하지 못한다는 증거이다.
66 | - 내게 있어 `표현력`은 의미있는 이름을 포함한다.
67 | - 표현력은 이름에만 국한되지 않는다.
68 | - 메서드가 어려 기능을 수행한다면 `메서드 추출 리팩터링 기법`을 적용해 기능을 명확히 기술하는 메서드 하나와 기능을 실제로 수행하는 메서드 여러 개로 나눈다.
69 | - 집합에서 항목을 특정 항목을 찾아내야 할 때
70 | - 나는 추상 메서드나 추상 클래스를 만들어 실제 구현을 감싼다.
71 | ```
72 | 중복을 피해라. 한 기능만 수행해라. 제대로 표현해라. 작게 추상화해라.
73 | ```
74 |
75 | # 워드 커닝햄
76 | > Ward Cunningham ; Wiki 창시자, Fit 창시자, Extreme Programming 공동 창시자, 디자인 패턴을 뒤에서 움직이는 전문가, 스몰토크와 객체 지향의 정신적 지도자
77 |
78 | - 코드를 읽으면서 짐작했ㄷ너 기능을 각 루틴이 그대로 수행한다면 깨끗한 코드라 불러도 되겠다.
79 | - 코드가 그 문제를 풀기 위한 언어처럼 보인다면 아름다운 코드라 불러도 되겠다.
80 | ```
81 | 코드는 독해하느라 멀리르 쥐어짤 필요가 없어야 한다. 읽으면서 짐작한 대로 돌아가는 코드가 깨끗한 코드다.
82 | ```
--------------------------------------------------------------------------------
/CS/알고리즘/이진탐색.md:
--------------------------------------------------------------------------------
1 | # 이진탐색
2 |
3 | 데이터가 정렬되어있는 배열에서 특정한 값을 찾아내는 알고리즘.
4 |
5 |
6 |
7 | 배열의 중간에 있는 임의의 값을 선택하여 찾고자 하는 값 X와 비교한다.
8 | - X가 중간 값보다 작으면 중간 값을 기준으로 좌측의 데이터들을 대상으로
9 | - X가 중간값보다 크면 배열의 우측을 대상으로
10 |
11 | 다시 탐색한다.
12 |
13 | 동일한 방법으로 다시 중간의 값을 임의로 선택하고 비교한다. (해당 값을 찾을 때까지 반복)
14 |
15 | ## 조건
16 | - 오름차순으로 정렬된 sequence가 주어져야 한다.
17 | - 그렇지 않은 경우에는 정렬 관련 작업을 선행해야 한다.
18 |
19 | ## 시간 복잡도
20 | - 필요 검색 횟수 (평균) : O(`logN`)
21 | - 매번 탐색할 때마다 검색 범위가 반으로 줄어들기 때문.
22 | - 검색 실패 (최악) : log(N + 1) = O(`logN`)
23 | - 검색 성공 : logN - 1 = O(`logN`)
24 | - 최선 : O(`1`)
25 |
26 | ## Ex
27 | - array : 타겟이 들어있다고 추정하는 배열
28 | - target : 찾고자하는 수
29 | - start, end : array의 시작, 끝 인덱스
30 | - 리턴값 : 타겟의 인덱스 값 (존재 하지 않는 경우 nil)
31 |
32 | ### 재귀함수로 구현
33 | ```swift
34 | func recurBinarySearch(array: [Int], target: Int, start: Int, end: Int) -> Int? {
35 | if start > end {
36 | return nil
37 | }
38 |
39 | let mid = (start + end) / 2
40 |
41 | if array[mid] == target {
42 | return mid
43 | } else if array[mid] > target {
44 | return binarySearch(array: array, target: target, start: start, end: mid - 1)
45 | } else {
46 | return binarySearch(array: array, target: target, start: mid + 1, end: end)
47 | }
48 | }
49 |
50 | let testArr: [Int] = [3, 4, 6, 8, 10, 33, 55, 200]
51 |
52 | binarySearch(array: testArr, target: 55, start: 0, end: testArr.count - 1) // 6
53 |
54 | ```
55 |
56 | ### 반복문으로 구현 1
57 | - key값을 array에서 이진탐색으로 찾는다.
58 | - 존재하는 경우, array에서 key가 몇 번째 인덱스에 있는지 인덱스 값을 리턴한다.
59 |
60 | ```swift
61 | func loopBinarySearch(array: [Int], target: Int, start: Int, end: Int) -> Int? {
62 | var start = start
63 | var end = end
64 |
65 | while start <= end {
66 | let mid = (start + end) / 2
67 |
68 | if array[mid] == target {
69 | return mid
70 | } else if array[mid] > target {
71 | end = mid - 1
72 | } else {
73 | start = mid + 1
74 | }
75 | }
76 |
77 | return nil
78 | }
79 | ```
80 |
81 | ### 반복문으로 구현 2
82 | ```swift
83 | func loopBinarySearch(array: [Int], target: Int) -> Int? {
84 | var start = start
85 | var end = array.count - 1
86 |
87 | while start <= end {
88 | let mid = (start + end) / 2
89 |
90 | if array[mid] == target {
91 | return mid
92 | } else if array[mid] > target {
93 | end = mid - 1
94 | } else {
95 | start = mid + 1
96 | }
97 | }
98 |
99 | return nil
100 | }
101 | ```
102 |
--------------------------------------------------------------------------------
/Swift/mutating.md:
--------------------------------------------------------------------------------
1 | # mutating 키워드
2 |
3 | - Swift의 구조체는 값 타입(`value type`)의 불변 객체 (`immutable object`) 이다.
4 | - 값 타입 프로퍼티들을 인스턴스 메서드에 의해 수정될 수 없다.
5 | - `mutating` 키워드를 메서드 앞에 붙이면 구조체나 열거형 인스턴스에서 프로퍼티를 수정할 수 있게 된다.
6 | - struct 안에 포함된 어느것이든 상태를 바꾸고자 할 때 사용하는 키워드이다.
7 |
8 |
9 |
10 | - `mutating` 키워드가 붙은 메서드를 실행하면 스위프트는 새로운 구조체를 생성해 변경된 프로퍼티의 값을 할당하고 반환해 현재 구조체를 대체한다.
11 | - 즉, 실질적으로 새로운 `struct` 객체를 만들어 원래 있던 자리에 그대로 넣어준다. (바꿔치기)
12 | - (함수의 파라미터에 `inout` 키워드를 사용하는 것과 비슷하다.)
13 | - 구조체의 불변성을 지키기 위해 이런 방법을 사용한다.
14 |
15 |
16 |
17 | - `mutating` 키워드는 해당 메서드를 호출하는 `caller`에게 값이 바뀔 것이다 라는 것을 알도록 한다.
18 | - 이 개념을 이해하기는 쉬운 예로 숫자 연산을 들 수 있다.
19 | - 예를 들어 `3 + 2` 이라는 연산을 수행하면 `3`이 `5`가 되지 않는다.
20 | - 연산을 수행한 후 `5` 라는 새로운 값을 갖게 되는 것이다.
21 | - mutating 함수들은 모두 같은 규칙 하에 작동한다.
22 | - 상수(`constant`)에 대해 mutating 함수를 호출할 수 없다.
23 |
24 | ```swift
25 | struct SomeStruct {
26 | var value: Int = 0
27 |
28 | mutating func increaseValue(increament: Int) { // increament 만큼 value를 증가시키는 함수
29 | self = SomeStruct(value: value + increament)
30 | }
31 | }
32 | ```
33 |
34 |
35 |
36 | ## 구조체 인스턴스를 상수(constant)로 선언하면 어떻게 될까?
37 |
38 | ```swift
39 | // 상수로 선언한 객체
40 | let constantStruct = SomeStruct()
41 | variableStruct.value // 0
42 | // constantStruct.increaseValue(increament: 10) // error : change 'let' to 'var' to make it mutable
43 | ```
44 |
45 | - 이유: 그 이유는 상수로 선언된 객체에 새로운 값을 할당하는 것이 불가능하기 때문이다.
46 | - 그렇기 때문에 mutating 함수는 오직 변수(`variable`) 에서 사용할 수 있다.
47 |
48 |
49 |
50 | ## 변수(variable)로 바꾸어 선언하면 어떻게 될까?
51 | ```swift
52 | // 변수로 선언한 객체
53 | var variableStruct = SomeStruct()
54 | variableStruct.value // 0
55 |
56 | variableStruct.increaseValue(increament: 20)
57 | variableStruct.value // 20
58 | ```
59 | - `increaseValue(increament:)` 함수를 실행한 후 결과물로 나온 `value`가 20인 인스턴스를 `variableStruct`에 대체가 가능하므로 잘 작동한다.
60 |
61 |
62 |
63 | ## 전체 코드
64 |
65 | ```swift
66 | struct SomeStruct {
67 | var value: Int = 0
68 |
69 | mutating func increaseValue(increament: Int) { // increament 만큼 value를 증가시키는 함수
70 | self = SomeStruct(value: value + increament)
71 | }
72 | }
73 |
74 | // 상수로 선언한 객체
75 | let constantStruct = SomeStruct()
76 | variableStruct.value // 0
77 | // constantStruct.increaseValue(increament: 10) // error : change 'let' to 'var' to make it mutable
78 |
79 | // 변수로 선언한 객체
80 | var variableStruct = SomeStruct()
81 | variableStruct.value // 0
82 |
83 | variableStruct.increaseValue(increament: 20)
84 | variableStruct.value // 20
85 | ```
--------------------------------------------------------------------------------
/iOS/ARC_vs_GC.md:
--------------------------------------------------------------------------------
1 | # ARC & GC
2 |
3 | ## ARC 란?
4 | - `ARC = Automatic Refernce Counting` 이라는 메모리 관리 기법을 말한다.
5 | - Swift는 `ARC`를 사용하여 메모리를 관리한다.
6 | - 개발자가 기존에 수동으로 관리하던 `reference counting`을 자동으로 관리해주는 기술을 말한다.
7 | - `ARC`는 런타임이 아닌, 컴파일 시점에 컴파일러가 개발자를 대신하여 메모리 관리 코드를 적절한 위치에 자동으로 삽입한다.
8 |
9 | > Swift 코드에서 개발자가 retain/release 코드를 작접 작성하지 않아도 실제 바이너리 코드에는 메모리 해제 코드가 들어가도록 하는 것이다.
10 |
11 | ## GC란?
12 | - `GC = Garbage Collection` 이라는 메모리 관리 기법을 말한다.
13 | - `GC`방식은 메모리 관리를 `Garbage Collector`라는 것이 프로그램 실행 중에 동적으로 감시하고 있다가, 더 이상 사용할 필요가 없다고 여겨지는 것, 즉 `garbage`를 메모리에서 삭제하는 것이다.
14 | ```
15 | garbage는 어떤 변수도 가리키지 않게 된 메모리 영역을 의미한다.
16 | ```
17 | - `GC`는 런타임에 메모리를 관리한다.
18 |
19 | ## 공통의 목적
20 | - 개발자의 일거리를 덜어주는 목적
21 | - `GC`, `ARC`를 통해 더 이상 참조 카운트를 추적하지 않아도 된다.
22 | - 객체를 수동으로 메모리에서 해제하는 것에 대해 걱정할 필요가 없어진 것.
23 |
24 | ## 비교
25 | |비교|ARC|GC|
26 | |---|---|---|
27 | |시점|컴파일 타임|런타임|
28 | |원리|컴파일 시점에 언제 참조/해제 되는지 결정|주기적으로 참조를 추적하여 사용하지 않는 인스턴스를 해제|
29 | |오버헤드|런타임 시점에 추가적인 오버헤드가 발생하지 않는다|런타임 시점에 객체를 추적하는 과정에서 오버헤드가 발생하여 성능저하가 생길 수 있다|
30 | |단점|순환 참조가 발생 시 메모리 누수의 위험이 있다||
31 | |기타|-|별도로 동적 객체를 관리할 필요가 없다|
32 | |기타|-|인스턴스가 해제될 확률이 ARC에 비해 높다|
33 |
34 |
35 | ## 장단점
36 | ||ARC|
37 | |---|---|
38 | |장점| - 객체가 사용되지 않을 때, 실시간으로 메모리에서 release 한다. - 컴파일러가 메모리 반환 코드를 넣어주는 것이기에 오버헤드에서 자유롭다. - 백그라운드 처리가 없으므로, 모바일과 같은 저전력 시스템에서 더 효과적이다. (즉, 메모리와 CPU가 데스크탑에 비해 제한적인 모바일 기기에서는 더 중요한 문제이고 그만큼 성능 측면에서 좋다.) |
39 | |단점| - 다만 개발자의 실수에 의해 순환 참조가 발생될 수 있기에 이를 예방하는 작업이 필요하다. |
40 |
41 | ||GC|
42 | |---|---|
43 | |장점| - Retain Cycle이 발생하지 않는다. |
44 | |단점| - 항상 메모리를 차지하고 감시해야하기 때문에 메모리 사용량이 늘어날 수 밖에 없고, 지속적인 감시를 위해 CPU를 항상 사용한다. - 정기적으로 GC가 동작하여 더 이상 사용되지 않는 메모리를 반환하는 방식이므로, 정확한 시간에 releaser가 호출되지 않는다.|
45 |
46 | > ARC는 Retain Cycle을 해결 못하는 이유?
47 | ```
48 | * 순환 참조란?
49 | - 순환 참조는 두개(또는 그 이상)의 객체가 서로를 참조할 때 발생한다.
50 | - 객체에 대한 외부 참조가 해제되어도 서로를 참조하고 있어 alive상태를 유지하는 현상을 말한다.
51 | ```
52 |
53 | - `GC`는 reachable 객체를 살펴보며 작동한다. 외부 참조가 존재하지 않는 것을 감지하면, 서로를 참조하는 객체 그래프 전체를 버리기에 순환 참조 문제가 발생하지 않는다.
54 | - `ARC`는 더 낮은 수준에서 작동하고, 참조 수를 기반으로 생명주기를 관리하기 때문에 Retain Cycle을 자동으로 처리할 수 없으며, 그로 인해 메모리 누수 문제를 일으킨다.
55 | - `ARC`는 Retain Cycle을 피하는 방법을 제공하지만, 개발자의 명시적인 설계가 필요하다. -> 이를 위해 `strong, weak, unowned` 와 같은 `Storage Modifier` 가 도입되었다.
56 |
57 | ## 요약
58 | > GC(Garbage Collection)
59 | - 정기적으로 GC가 동작하여 더 이상 사용되지 않는 메모리를 반환하는 방식, 정확한 시간에 release가 호출되지 않음
60 | - GC는 runtime에 background에서 메모리를 검사한다.
61 | - background에서 동작하므로, 앱 성능 저하를 유발한다.
62 |
63 | > ARC(Automatic Reference Counting)
64 | - RC(Reference Counting)를 통해 메모리를 관리하는 방식
65 | - compile time에 retain/release 코드를 주입
66 | - background에서 동작하지 않음, 저전력 시스템에서 효과적
67 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## DS_Store
6 | .DS_Store
7 |
8 | ## User settings
9 | xcuserdata/
10 |
11 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
12 | *.xcscmblueprint
13 | *.xccheckout
14 |
15 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
16 | build/
17 | DerivedData/
18 | *.moved-aside
19 | *.pbxuser
20 | !default.pbxuser
21 | *.mode1v3
22 | !default.mode1v3
23 | *.mode2v3
24 | !default.mode2v3
25 | *.perspectivev3
26 | !default.perspectivev3
27 |
28 | ## Obj-C/Swift specific
29 | *.hmap
30 |
31 | ## App packaging
32 | *.ipa
33 | *.dSYM.zip
34 | *.dSYM
35 |
36 | ## Playgrounds
37 | timeline.xctimeline
38 | playground.xcworkspace
39 |
40 | # Swift Package Manager
41 | #
42 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
43 | # Packages/
44 | # Package.pins
45 | # Package.resolved
46 | # *.xcodeproj
47 | #
48 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
49 | # hence it is not needed unless you have added a package configuration file to your project
50 | # .swiftpm
51 |
52 | .build/
53 |
54 | # CocoaPods
55 | #
56 | # We recommend against adding the Pods directory to your .gitignore. However
57 | # you should judge for yourself, the pros and cons are mentioned at:
58 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
59 | #
60 | # Pods/
61 | #
62 | # Add this line if you want to avoid checking in source code from the Xcode workspace
63 | # *.xcworkspace
64 |
65 | # Carthage
66 | #
67 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
68 | # Carthage/Checkouts
69 |
70 | Carthage/Build/
71 |
72 | # Accio dependency management
73 | Dependencies/
74 | .accio/
75 |
76 | # fastlane
77 | #
78 | # It is recommended to not store the screenshots in the git repo.
79 | # Instead, use fastlane to re-generate the screenshots whenever they are needed.
80 | # For more information about the recommended setup visit:
81 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
82 |
83 | fastlane/report.xml
84 | fastlane/Preview.html
85 | fastlane/screenshots/**/*.png
86 | fastlane/test_output
87 |
88 | # Code Injection
89 | #
90 | # After new code Injection tools there's a generated folder /iOSInjectionProject
91 | # https://github.com/johnno1962/injectionforxcode
92 |
93 | iOSInjectionProject/
94 |
--------------------------------------------------------------------------------
/Swift/EscapingClosure.md:
--------------------------------------------------------------------------------
1 | # Escaping Closure
2 |
3 | ### Escaping Closure 란?
4 | - 클로저가 함수의 인자로 전달되었을 때, 함수의 실행이 종료된 후 실행되는 클로저이다.
5 | ```swift
6 | class ViewModel {
7 | var completionHandler: (() -> Void)? = nil
8 |
9 | func fetchData(completion: @escaping () -> Void) {
10 | completionHandler = completion
11 | }
12 | }
13 | ```
14 | > 실행순서
15 | 1. 클로저가 fetchData() 함수의 completion 인자로 전달된다.
16 | 2. 클로저 completion이 conpletionHandler 변수에 저장된다.
17 | 3. fetchData() 함수가 값을 반환하고 종료된다.
18 | 4. 클로저 completion은 아직 실행되지 않았다.
19 | (completion은 함수의 실행이 종료되기 전에 실행되지 않기 때문에
20 | 함수 밖에서 실행되는 클로저이다.)
21 |
22 |
23 | ### Non-Escaping Closure 란?
24 | - 반대로 함수의 실행이 종료되기 전에 실행되는 클로저이다.
25 |
26 | ```swift
27 | func runClosure(closure: () -> Void) {
28 | closure()
29 | }
30 | ```
31 | > 실행순서
32 | 1. 클로저가 `runClosure()` 함수의 `closure` 인자로 전달된다.
33 | 2. 함수 안의 `closure()` 가 실행된다.
34 | 3. `runClosure()` 함수가 값을 반환하고 종료한다.
35 |
36 |
37 |
38 | ## 예시
39 | escaping closure의 예로는 비동기로 실행되는 `HTTP 요청 CompletionHandler` 가 있다.
40 | ``` swift
41 | func makeRequest(_ completion: @escaping (Result<(Data, URLResponse), Error>) -> Void) {
42 | URLSession.shared.dataTask(with: URL(string: "http://somesite.com")!) { data, response, error in
43 | if let error = error {
44 | completion(.failure(error))
45 | } else if let data = data, let response = response {
46 | completion(.success((data, response)))
47 | }
48 | }
49 | }
50 | ```
51 | - `makeRequest()` 함수에서 실행되는 `completion` 클로저는 함수 실행 중에 즉시 실행되지 않고, URL 요청이 끝난 후 비동기로 실행이 된다.
52 | - 클로저로된 인자인 `completion`의 타입에 `@escaping` 을 붙여서 `escaping closure` 라는 것을 명시해주어야 한다.
53 |
54 |
55 |
56 | ## Q. `@escaping` 키워드가 붙으면 무조건 escaping으로만 사용해야 할까?
57 | ### NO!
58 | - `@escaping` 이 붙어있어도 `non-escaping closure`를 인자로 넣을 수 있다.
59 | ```swift
60 | func runClosure(closure: @escaping () -> Void) {
61 | closure() // closure는 non-escaping 이지만 @escaping 사용이 가능
62 | }
63 | ```
64 |
65 |
66 |
67 | - 하지만 반대로 `escaping closure` 를 `@escaping` 키워드 없이 사용은 불가
68 | ``` swift
69 | class ViewModel {
70 | var completionhandler: (() -> Void)? = nil
71 |
72 | func fetchData(completion: () -> Void) { // @escaping 누락으로 컴파일 에러 발생
73 | completionhandler = completion
74 | }
75 | }
76 | ```
77 |
78 | ## Q. `@escaping`를 사용하면 escaping과 non-escaping을 모두 사용할 수 있다면 왜 나누어 사용하는가?
79 | - 컴파일러의 퍼포먼스와 최적화 때문이다.
80 | - non-escaping은
81 | - 컴파일러가 클로저의 실행이 언제 종료되는지 알고 있다.
82 | ㄷ- 따라서 클로저에서 사용하는 특정 객체에 대한 `retain, release` 등의 처리를 생략해 객체의 `life-cycle`을 효율적으로 관리 가능하다.
83 | - escaping은
84 | - 함수 밖에서 실행되기 문에 클로저가 함수 밖에서도 적절히 실행되는 것을 보장하기 위해,
85 | 클로저에서 사용하는 객체에 대한 추가적인 `reference cycle`을 관리해줘야 한다.
86 | - 이러한 이유로 Swift에서는 필요할 때만 `escaping closure`를 사용하도록 구분해두었다.
87 |
--------------------------------------------------------------------------------
/iOS/deeplink_vs_universalLink.md:
--------------------------------------------------------------------------------
1 | # 유니버설 링크와 딥링크 차이점
2 |
3 | ## deep link
4 | > 스토어나 웹사이트 대신 직접적으로 사용자를 앱으로 보내주는 타입의 링크를 말한다.
5 |
6 | ### 장점
7 | - 긍정적인 사용자 경험을 제공하는 동안 스마트한 방법으로 컨버전(conversion)을 유도할 수 있지만, 만약 사용자에게 깔려있지 않은 앱으로 연결을 시도한다면? `deferred` 된다.
8 | - `deferred` 상태에서 우리는 앱스토어로 이동하도록 할 수 있다.
9 | - 딥링크의 천재적인 점은 유저가 앱을 깔고 열어도 여전히 우리가 앱 내에서 사용자를 이동시키고 싶은 위치로 보내준다는 것이다.
10 |
11 | ```
12 | 즉,
13 | 앱이 설치되어 있지 않으면 앱스토어를 통해 깔도록 유도
14 | 앱을 설치한 후(설치되어 있는 경우)에 같은 링크를 열면 처음에 의도했던 앱내 화면으로 이동
15 | ```
16 |
17 | > 컨버전(conversion)
18 | > : 마케팅에서 사용되는 용어. 광고, 제안, 노티 등을 통해 사용자를 유입시키는 행위.
19 | > 모바일에서 컨버전을 충족하는 액션에는 download, install, sign-up, purchase 가 있다.
20 |
21 | - 사용자를 바로 앱 안의 특정 위치로 곧장 이동시켜준다.
22 | - 한번의 클릭으로 사용자를 원하는 곳으로 옮겨줄 수 있다.
23 | - 사용자가 특정 위치를 찾고 이동하기 까지의 시간과 에너지를 아껴줌으로써 UX를 향상 시킬 수 있다.
24 | - 사용자에게 잠재적 보상 혹은 제안이 될 수 있는 캠페인 제안을 그들에게 다시 보냄으로써 새로운 경험을 시도하도록 설득하기 쉽다.
25 |
26 |
27 |
28 | ### 작동방식
29 | - 커스텀 `URL scheme`(iOS의 경우) 또는 `intent URL`(AOS의 경우) 을 특정함으로써 만약 이미 앱이 깔려있다면 앱을 열어준다.
30 | - 또한 딥링크를 통해 유저가 실행하고자 하는 캠페인이 묶여 있을 수도 있는 특정 이벤트나 특정 페이지로 곧장 이동하도록 할 수도 있다.
31 |
32 |
33 | ----
34 |
35 | ## universal link
36 | - 애플이 웹사이트를 거쳐 iOS앱을 열 수 있게 하는 방식 (웹뷰)
37 | - 웹사이트 내의 콘텐츠로 바로 연결되기 때문에 iOS사용자들은 통합된 모바일 경험을 제공받을 수 있다.
38 | - 대체로 딥링크와 유사하다.
39 | - 차이점은 universal link는 애플기기 전용 방식. 앱을 통해 웹페이지를 여는 기능을 제공한다.
40 |
41 |
42 | ### deep link 와 차이점
43 | - 유니버셜은 커스텀 URL 스킴을 정의하지 않고 일련의 웹페이지를 앱 내 위치와 연결한다는 점이 다르다.
44 | - 따라서 사용자가 이러한 웹 페이지를 열면 iOS는 사용자를 앱으로 리다이렉션(redirection) 한다.
45 |
46 | - iOS9 이상에서만 제공되므로, 사용자 기기에 섪치되어있지 않더라도 작동한다.
47 | - 앱이 설치되어있지 않은 경우 웹사이트로 링클르 클릭하게 되는 경우, 사파리를 통해 웹사이ㄷ트에 연결된다.
48 | ```
49 | 하나의 URL로 웹과 앱을 연결할 수 있다.
50 | 웹 사이트 링크를 클릭했을 때 사용자를 앱으로 바로 유도할 수 있기에 앱 engagement 높이고 UX를 간편하게 할 수 있다.
51 | ```
52 |
53 |
54 |
55 | ### 만들어진 이유
56 | - 애플 고유의 딥링크 경험을 제공하기 위해서
57 | - iOS를 위한 보다 안정적인 딥링킹 생태계를 형성하기 위해서
58 |
59 |
60 |
61 | ### 사용시 발생 가능 이유
62 | > 오른쪽 상단 모서리 오류(the right corner evil)
63 |
64 | - 아이폰이나 아이패드 오른쪽 상단의 ‘브레드크럼(breadcrumb)’ 관련 이슈
65 | - 사용자는 앱에서 웹뷰로 혹은 그 반대로 이동하고자할 때 클릭함
66 | - 사용자가 링크를 클릭하면 iOS는 사용자의 최근 행동을 바탕으로 앱을 실행할 것인지, 웹페이지를 열것인지 결정
67 | - 앱으로 연결되는 링크를 클릭한 다음 브레드크럼을 클릭래 사파리에서 웹을 열었다면 iOS는 이 과정을 기억하여 사용자가 다시 앱으로 돌아가고 싶어 하더라도 계속해서 이 사이트를 사파리에서 연다.
68 |
69 | ```
70 | 사용자는 웹뷰에 갇히게 된다.
71 | iOS는 사용자가 웹 페이지의 스마트 앱 배너를 클릭할 때까지 앱을 다시 실행하지 않으며, 이 과정은 개발자가 수정 불가능하다.
72 | 오로지 사용자가 배너를 이용하거나 '링크를 길게 누르기 > 옵션 > 앱으로 열기'를 통해 앱으로 돌아갈 수 있다.
73 |
74 | -> 앱의 오류가 아닌 잘못된 딥링킹 과정이 앱을 사용하지 못하도록 하는 문제가 생길 수 있다.
75 | ```
76 |
77 |
78 |
79 | > 링크 깨짐
80 | - 간혹 앱 업데이트를 하다가 몇몇 버전에서는 딥링크가 다르게 작동하는 경우가 있다. (가끔 네이버가 나온다거나..)
81 | - 어떤 앱 버전이 설치되어 있느냐에 따라 링크 작동 여부가 달라진다.
82 |
83 | ```
84 | OS 혹은 앱의 버전차이에 의한 문제사항이다. (설정의 문제가 아님)
85 |
86 | -> 사용자는 OS가 아니라 앱의 오류가 생겼다고 생각하며 UX에 대해 부정적으로 생각할 수 있다.
87 | ```
88 |
--------------------------------------------------------------------------------
/Swift/URLCache.md:
--------------------------------------------------------------------------------
1 |
2 | # URLCache
3 | " 캐시된 응답 객체를 URL request와 매핑하는 객체
4 |
5 | ```swift
6 | class URLCache : NSObject
7 | ```
8 |
9 | - `URLCache` 클래스는 NSURLRequest 객체를 `CachedURLResponse` 객체에 매핑하여 `URL` 로드 요청에 대한 응답 캐싱을 구현한다.
10 | - `compositive in-memory`와 `on-disk cache`를 제공하며, 둘의 비율과 크긱를 조작활 수 있다.
11 | - 캐시 데이터가 지속적으로 저장되는 경로를 제어할 수도 있다.
12 |
13 | ## 캐시가 제거될 수 있는가?
14 | - iOS에서 `on-disk cache`는 시스템의 디스크 공간이 부족할 때 제거될 수 있지만, 앱이 실행되고 있지 않을 때만 제거된다.
15 |
16 | ## Thread Safety
17 | - `iOS 8`, `macOS 10.10` 이후 버전부터는 thread safe 하다.
18 | - 동시에 다수의 context에서 실행되어도 안전하지만, [cachedResponse(for:)](https://developer.apple.com/documentation/foundation/urlcache/1411817-cachedresponse), [storeCachedResponse(_:for:)](https://developer.apple.com/documentation/foundation/urlcache/1410340-storecachedresponse)
19 | 와 같은 메서드는 조심하는게 좋다.
20 | - 같은 request에 대한 response를 read/write 시도할 경우 race condition을 피할 수 없다.
21 | - `URLCahce의` 하위 클래스는 이런 thread-safe한 방식으로 재정의된 메서드를 구현해야 한다.
22 |
23 | # Subclassing
24 | - URLCache는 있는 그대로 사용하는게 가장 좋지만, 특정한 요구사항을 위해 subclass를 할 수 있다.
25 | - ex. 캐시된 응답을 선별, 보안 등의 이유로 저장 메커니즘을 다시 구현해야할 때
26 | - 이 클래스를 제정의(override)할 때는 `task` 파라미터를 가지고 있는 메서드를 시스템에서는 선호한다.
27 | - 따라서 subclassing을 할 때 다음과 같이 `task 기반 메서드`를 재정의 해야 한다.
28 | - "`response를 cache에 저장할 때`"는 [2번](https://developer.apple.com/documentation/foundation/urlcache/1410340-storecachedresponse) 대신에 [1번](https://developer.apple.com/documentation/foundation/urlcache/1414434-storecachedresponse)의 메서드를 재정의 하는 것이 좋다. (혹은 2번에 추가적으로 1번까지 재정의해라.)
29 | ```swift
30 | // 1
31 | func storeCachedResponse(
32 | _ cachedResponse: CachedURLResponse,
33 | for dataTask: URLSessionDataTask
34 | )
35 |
36 | // 2
37 | func storeCachedResponse(
38 | _ cachedResponse: CachedURLResponse,
39 | for request: URLRequest
40 | )
41 | ```
42 | - "`cache로 부터 response를 가져올 때`"는 [2번](https://developer.apple.com/documentation/foundation/urlcache/1411817-cachedresponse) 대신에 [1번](https://developer.apple.com/documentation/foundation/urlcache/1409184-getcachedresponse)의 메서드를 재정의 하는 것이 좋다. (혹은 2번에 추가적으로 1번까지 재정의해라.)
43 | ```swift
44 | // 1
45 | func getCachedResponse(
46 | for dataTask: URLSessionDataTask,
47 | completionHandler: @escaping @Sendable (CachedURLResponse?) -> Void
48 | )
49 |
50 | // 2
51 | func cachedResponse(for request: URLRequest) -> CachedURLResponse?
52 | ```
53 | - "`cache된 response를 제거할 때`"는 [2번](https://developer.apple.com/documentation/foundation/urlcache/1415377-removecachedresponse) 대신에 [1번](https://developer.apple.com/documentation/foundation/urlcache/1412258-removecachedresponse)의 메서드를 재정의 하는 것이 좋다. (혹은 2번에 추가적으로 1번까지 재정의해라.)
54 | ```swift
55 | // 1
56 | func removeCachedResponse(for dataTask: URLSessionDataTask)
57 |
58 | // 2
59 | func removeCachedResponse(for request: URLRequest)
60 | ```
61 |
--------------------------------------------------------------------------------
/iOS/GCD&Operation.md:
--------------------------------------------------------------------------------
1 | # GCD & Operation 비교
2 |
3 | ## 📌 GCD : Grand Central Dispatch
4 |
5 | - 멀티 코어 시스템에서 동시성 실행을제공하는 프로그래밍 언어 요소, 라이브러리를 말한다.
6 | - 이 CGD의 개념으로 동시성 프로그래밍을 하는것이 Dispatch Queue 이다.
7 | - DispatchQueue에 작업을 정의해서 넣어주면, 운영체제가 작업들을 적절한 스레드에 할당해준다.
8 | - `큐의 종류, qos 우선순위, sync, async`를 설정해서 지정한 작업이 현재 스레드 혹은 다른 스레드에서 실행 될 수 있도록 한다.
9 |
10 | ## DispatchQueue
11 | > `Class`
12 | > : 앱의 main thread 혹은 background thread 에서 serially 혹은 concurrently 하게 작업을 실행을 관리하는 객체이다.
13 |
14 | DispatchQueue를 크게 두가지로 나눌 수 있다.
15 | - Serial
16 | - 큐에 등록된 작업을 한 번에 하나씩 처리하는 것이다.
17 | - 한 작업이 끝날 때까지 큐에 있는 다른 작업은 건드리지 않기 떄문에 실행 순서가 언제나 같다.
18 |
19 |
20 |
21 | - Concurrent
22 | - 동시에 여러 작업을 수행할 수 있다.
23 | - 운영체제는 Dispatch Queue에서 꺼내온 현재 작업이 끝나지 않아도 다음작업을 다른 스레드에 할당해 여러 작업이 실행되게 한다.
24 |
25 |
26 |
27 | ### 3종류의 큐를 선택해서 사용할 수 있다.
28 | - Main
29 | - 메인스레드에서 작업을 보관하고 수행하는 큐이다.
30 | - 메인 스레드에서 동작하기 때문에 단 하나만 존재할 수 있고, serial한 특성을 가지고 있다.
31 | - (큐에 쌓이는 순서대로 실행)
32 | - 메인에서 가장 중요한 업무는 UI 업데이트를 하는 것이다.
33 | - Global
34 | - 메인이 아닌 다른 스레드에서 작업을 처리한다. 그리고 concurrent 특성을 가지 기 떄문에 여러 스레드로 분산 처리가 가능하다.
35 | - 작업이 완수되는 순서를 보장하지 않는다.
36 | - Custom
37 | - 사용자가 어떤 특성의 큐로 생성할지 결정할 수 있게 해줍니다.
38 | - 기본적으로는 serial 하지만, attributes인자로 concurrent로 바꿔줄 수 있다.
39 |
40 | ## 📌 Qos : Quality of Service
41 |
42 | gobal과 custom에 적용가능 옵션이다.
43 |
44 | 작업 분산 처리시 작업의 중요도를 설정하여 실행의 우선순위를 부여하여, 우선순위가 높은 큐를 더 많은 스레드에 작업분산을 시킨다.
45 | (종류: userInteractice > userInitiated > default > utility > background)
46 |
47 |
48 | ## 주의 사항
49 | ### 📌 main.sync 금지!
50 |
51 | - Main에서 sync를 호출하면 메인스레드는 해당 작업이 끝날 때까지 block 됩니다.
52 | - 그러면 메인스레드는 아무것도 할 수 없는 상태가 되어 작업을 수행할 수 없습니다.
53 | - 큐에 등록된 작업은 시작되지 못하고, 끝나지도 않는 데드락이 발생합니다.
54 |
55 |
56 | ### 📌 같은 큐에서 sync 금지!
57 |
58 | - 같은 큐에 작업을 등록하고, 해당 작업을 동일 큐에 sync하게 등록하면 같은 스레드에 작업을 또 할당할 수도있다.
59 | - 이것을 운영체제에서 스레드를 할당하는 과정에서 데드락이 발생할 수 있다.
60 |
61 |
62 | ## 📌 DispatchGroup
63 |
64 | - 작업을 Dispatch Queue에 보내면 GCD가 스레드를 적절히 생성하여 분배해준다.
65 | - 여러 스레드로 분배된 작업이 끝나는 시점을 각각 파악하는 것이 아니라 하나의 그룹을 지어 한번에 파악하고 싶을 때 사용한다.
66 | → 그룹으로 묶인 작업의 마지막 시점을 파악
67 |
68 | ```
69 | - 그룹 생성
70 | - 작업을 그룹에 추가 (작업을 다른 큐로 보내도 같은 그룹으로 지정 가능)
71 | - 작업이 끝나면 실행할 코드를 담은 notify 작성
72 | ```
73 |
74 | wait() 을 통해 모든 작업이 완료될때까지 현재 스레드를 block할수 있음
75 | → 함수를 실행하는 곳인 메인큐에 wait하면 안됨. task들이 다른 스레드에서 실행되는만큼 앱이 멈춘다.
76 | → 그룹 내의 작업은 wait이 실행되고 이는 현재 스레드로 할당해선 안된다. wait상태에서 할당되면 데드락이발생.
77 |
78 |
79 | ## 📌 Operation
80 |
81 | - 단일 작업에 관란 데이터와 코드를 나타내는 추상 클래스
82 | - Operation 객체들을 priority에 의해 실행시키는 큐이다.
83 | - 한번 operation queue에 넣으면 작업이 끝날 때까지 queue가 존재
84 | - 해당 queue에서 Operation을 삭제, 명령시킬 수 있다는 장점이 있다.
85 | - 서브 클래싱이 가능한데, 이게 가능하다는 것은 캡슐화를 했다는 의미이므로 기능에 관한 모듈성이 향상
86 | - 작업의 실행 상태를할수 있고, 취소 및 순서 지정을 해줄 수 있다
87 | → 진행 상황과 종속성을 체크하면서 여러 클래스에 대한 책임을 분리할 수 있다.
88 |
89 | - 상태의 종류
90 | - pending, ready, executing, finished, cancelled
91 |
92 |
93 | ## 📌 Operation vs GCD
94 |
95 | - Operation Queue는 내부적으로 GCD를 사용한다.
96 | - GCD는 동시에 실행될 작업 단위를 나타내는 방버이고 종속성 설정이 어렵다.
97 | - Operation은 GCD에 비해 복잡하지만, 작업 간 종속성, 재사용, 취소, 중지 등이 가능하다
98 |
--------------------------------------------------------------------------------
/CS/알고리즘/BinarySearch_Ex.swift:
--------------------------------------------------------------------------------
1 |
2 | // 이진탐색 예제
3 | // 출처: 이코테 - 나동빈 저
4 |
5 | import Foundation
6 |
7 | // 1번
8 | /*
9 | 전자매장에는 부품이 N개 있다.
10 | 각 부품은 정수 형태의 고유한 번호가 있다. 어느날 손님이 M개 종류의 부품을 대량으로 구매하겠다며 당일 날 견적서를 요청했다.
11 | 나는 떄를 놓치지 않고 손님이 문의한 부품 M개 종류를 모두 확인해서 견적서를 작성해야 한다.
12 | 이때 가게 안에 부품이 모두 있는지 확인하는 프로그램을 작성하라.
13 |
14 | 가게가 현재 보유한 부품이 총 5개일 때 부품의 번호는 다음와 같다.
15 | N = 5
16 | [8, 3, 7, 9, 2]
17 |
18 | 손님은 총 3개의 부품이 있는지 확인 요청했는데 부품 번호는 다음과 같다.
19 | M = 3
20 | [5, 7, 9]
21 |
22 | 손님이 요청한 부품 번호의 순서대로 부품을 확인해 부품이 있으면 yes, 없으면 no를 출력하라.
23 | */
24 |
25 | func binarySearch(array: [Int], target: Int) -> Int? {
26 | var start = 0
27 | var end = array.count - 1
28 |
29 | while start <= end {
30 | let mid = (start + end) / 2
31 |
32 | if array[mid] == target {
33 | return mid
34 | } else if array[mid] > target {
35 | end = mid - 1
36 | } else {
37 | start = mid + 1
38 | }
39 | }
40 |
41 | return nil
42 | }
43 |
44 | // 가게가 보유한 부품배열 : array
45 | // 손님이 요청한 부품 배열 : targets
46 | func sol1(array: [Int], targets: [Int]) -> [String] {
47 | let array = array.sorted()
48 | var result: [String] = []
49 |
50 | for target in targets {
51 | let index = binarySearch(array: array, target: target)
52 |
53 | if index != nil {
54 | result.append("yes")
55 | } else {
56 | result.append("no")
57 | }
58 | }
59 |
60 | return result
61 | }
62 |
63 | sol1(array: [8, 3, 7, 9, 1], targets: [5, 7, 9])
64 |
65 |
66 | // 2번
67 | /*
68 | 떡볶이의 떡을 만드는데, 떡의 길이가 일정하지 않다.
69 | 대신에 한 봉지 안에 들어가는 떡의 총 길이는 절단기로 잘라서 맞춘다.
70 | 절단기에 높이 H를 지정하면 줄지어진 떡을 한번에 절단한다.
71 | 높이가 H보다 긴 떡은 H 윗부분이 잘릴 것이고, 낮은 떡은 잘리지 않는다.
72 |
73 | 예를 들어, 높이가 19, 14, 10, 17인 떡이 나란히 있고,
74 | 절단기 높이를 15로 하면 잘린 떡의 길이는 4, 0, 0, 2 일 것이다.
75 | 손님이 왔을 때 요청한 총 길이가 M일 때,
76 | 적어도 M 만큼의 떡을 얻기 위해 설정할 수 있는 절단기의 높이의 최댓값을 구하라.
77 | */
78 |
79 | // 떡볶이들의 높이 배열 : array
80 | // 손님이 요청한 떡 길이 : target
81 | func sol2(array: [Int], target: Int) -> Int {
82 | let array = array.sorted()
83 | var result: Int = 0
84 |
85 | var start = 0
86 | // var end = array.count - 1
87 | var end = array.max()!
88 |
89 | while start <= end {
90 | let mid = (start + end) / 2
91 | var total: Int = 0 // 손님이 가져갈 떡 길이의 합
92 |
93 | for elem in array {
94 | if elem > mid {
95 | total += (elem - mid) // 잘랐을 때 남은 떡의 길이 연산
96 | }
97 | }
98 |
99 | if total < target { // 떡의 양이 부족한 경우 더 많이 자르기 위해 왼쪽을 탐색
100 | end = mid - 1
101 | } else { // 떡의 양이 충분한 경우 덜 자르기 위해 오른쪽을 탐색
102 | result = mid // 최대한 덜 자른 경우를 찾기 위해 result에 현재 mid를 저장
103 | start = mid + 1
104 | }
105 | }
106 |
107 | return result
108 | }
109 |
110 | sol2(array: [19, 14, 10, 17], target: 6) // 15
111 |
--------------------------------------------------------------------------------
/Swift/regEx.md:
--------------------------------------------------------------------------------
1 | # 정규 표현식
2 |
3 | > 정규표현식 (Regular Expression)
4 | : 정규표현식 또는 정규식은 특정한 규칙을 가진 문자열의 집합을 표현하는 데 사용하는 언어이다.
5 |
6 | > `Class` [NSRegularExpression](https://developer.apple.com/documentation/foundation/nsregularexpression)
7 | : An immutable representation of a compiled regular expression that you apply to Unicode Strings.
8 | (유니코드 문자열들에 내가 적용한 컴파일된 정규표현식의 불변 표현이다.)
9 |
10 | ## Swfit에서의 regEx 특징
11 | - swift의 NSReguularExpression은 ICU (International Components for Unicode) 를 준수하고 있다.
12 | - 특수기호를 사용하고자 할 때 앞에 `\` 를 붙인다.
13 | - regex에서 `\` 가 사용되는 기호는 `\\` 이렇게 백슬래시를 2개 사용해주어야 한다.
14 | - ex1 ) `.` 을 정규표현식에 넣고 싶다면 `\.`
15 | - ex2 ) `\.`을 정규표현식에 넣고 싶다면 `\\.`
16 |
17 | ## 사용 예시
18 | - 보통 회원가입 혹은 로그인 시에 사용자가 올바른 형식으로 입력값을 주었는지 체크할 때 사용된다.
19 | - 이메일, 닉네임, 비밀번호 등
20 |
21 |
22 |
23 |
24 |
25 | -----
26 |
27 | ## 기호 설명
28 | - ^ : 시작
29 | - $ : 끝
30 | - {0,} : 0자 이상
31 | - {0,10} : 0자 이상 10자 이하
32 | - [] : 대괄호 안에 있는 문자중 임의의 문자
33 |
34 |
35 |
36 | - A-Z : 영어 대문자
37 | - a-z : 영어 소문자
38 | - 0-9 : 숫자
39 | - ~!@#$%^&* : 특수문자 (넣을 것만 골라 작성해도 됨)
40 | - 가-힣 : 한글
41 |
42 |
43 |
44 | - () : 문자를 묶음 처리
45 | - (.) : 임의의 문자
46 | - \1 : 앞의 부분식 (슬래시가 들어간 기호의 경우 \를 하나 더 붙여 표기해야 함.)
47 |
48 | ```
49 | ex. (.)\\1{3,} : 앞과 동일한 문자 3개 이상 필요
50 | ```
51 |
52 |
53 | - *: {0,} 0이상
54 | - +: {1.} 1이상
55 | - ?: {0,1} 0이상 1이하
56 |
57 |
58 |
59 | ## 예제 코드
60 |
61 | ```
62 | ex. [a-z]+ : 영어 소문자 1개 이상
63 | ```
64 |
65 | ```swift
66 | // 전화번호
67 | // 010 으로 시작하며 가운데 4자리 끝 4자리
68 | let phoneRegEx = "^010-?([0-9]{4})-?([0-9]{4})"
69 |
70 | // 이메일
71 | // 영어 대소문자, 숫자, 특수문자(._%+-) 다음에 @ 가 오고,
72 | // 그 다음 영어 대소문자, 숫자, 특수문자(._)가 온 후 . 잉 오고,
73 | // 영어 대문자가 오는 형식으로 구성되어있으며, 최소 2자이다.
74 | let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"
75 |
76 | // 아이디
77 | // 영어 대소문자, 숫자로 구성되어있으며, 최소 5자 - 최대 13자
78 | let idRegEx = "[A-Za-z0-9]{5,13}"
79 |
80 | // 닉네임
81 | // 한글, 영어 대소문자, 숫자로 구성되어있으며, 최소 2자 - 최대 7자
82 | let nickRegEx = "[가-힣A-Za-z0-9]{2,7}"
83 |
84 | // 비밀번호
85 | // 영어 대소문자, 숫자, 특수문자(!_@$%^&+=) 필수로 구성되어있으며, 최소 8자 - 최대 20자
86 | let pwRegEx = "[A-Za-z0-9!_@$%^&+=]{8,20}"
87 |
88 | // 영어 대소문자, 숫자는 필수로 구성되어있으며, 특수문자는 있어도 없어도 상관 없음, 최소 8자 - 최대 20자
89 | let pwRegEx = "[A-Za-z0-9(@$#!%*?&)?]{8,20}"
90 |
91 | // extension을 활용한 메서드
92 | extension String {
93 | func isPhoneValid() -> Bool {
94 | let phoneNumberTest = NSPredicate(format: "SELF MATCHES %@", "^010-?([0-9]{4})-?([0-9]{4})")
95 | return phoneNumberTest.evaluate(with: self)
96 | }
97 | }
98 | ```
99 |
100 |
101 | ## 참고할만한 메서드
102 | - [numberOfMatches(in:options:range:)](https://developer.apple.com/documentation/foundation/nsregularexpression/1414308-numberofmatches)
103 | - 주어진 범위의 문자열에서 정규표현식과 일치하는 건의 갯수를 세는 메서드도 제공한다.
104 |
105 |
106 |
107 |
108 | - [WWDC22 : Meet Swift RegEx](https://developer.apple.com/wwdc22/110357)
109 | - [공식문서: NSRegularExpression](https://developer.apple.com/documentation/foundation/nsregularexpression)
110 |
--------------------------------------------------------------------------------
/CS/아키택처패턴/MVVM.md:
--------------------------------------------------------------------------------
1 | # MVVM 패턴
2 | mvvm은 `view`를 `비즈니스로직`이나 `model`로 부터 분리시켜 `view`가 어느 특정 모델에 종속되지 않도록 해주는 패턴이다.
3 |
4 |
5 |
6 |
7 | : Model + View + ViewModel
8 |
9 | ## 구성요소
10 | - Model
11 | - 데이터, 네트워크 로직, 비즈니스 로직 등을 담고, 데이터를 캡슐화하는 역할
12 | - `view`, `viewModel` 은 신경쓰지 않는다.
13 | - 데이터를 어떻게 가지고 있을지에 대해서만 신경쓰고, 어떻게 보여질지는 신경쓰지 않는다.
14 | - MVC 와 유사.
15 | - View
16 | - 사용자가 화면에서 보이는 것들에 대한 구조, 배치 등을 다룬다.
17 | - `model` 을 직접 알고 있으면 안 된다.
18 | - `viewModel` 로부터 가져온 데이터로 표현한다.
19 | - 사용자로부터 이벤트를 수신하고, 그에 대한 처리를 `viewModel`에 넘긴다.
20 | - ViewModel
21 | - `view`로 부터 전달받은 요청을 처리할 로직을 담고 있다.
22 | - `model` 에 변화가 생기면 `view`에 notification을 보낸다. (데이터변화를 `view`가 알 수 있게)
23 | - `view`와 `model` 사이의 중계자 역할. presentation logic을 처리한다.
24 | - MVC의 `controller`와 유사.
25 | - ViewController
26 | - `View Life-Cycle`을 처리하고, 데이터를 UI 구성요소에 `bind`할 때만 `viewModel`, `view`와 통신한다.
27 | ## MVC와 차이점
28 | > 구성요소간의 의존성
29 | - MVC
30 | - 기본적으로 `view`와 `model`은 다른 구성요소들을 알지 못하며, `controller`는 `view`와 `model`을 모두 알고있는 형태로 구현되어야 한다.
31 |
32 | - MVVM
33 | - `view`는 `viewModel`을 알고 있고, 소유한다.
34 | - `viewModel`은 `model`만 알고 있도록 구현한다.
35 | - `model`은 다른 구성요소를 알지 못하도록 마들어야 한다. (= MVC와 같음)
36 |
37 | > view가 데이터를 보여주는 방식
38 | - MVC
39 | - `model`에 `view`를 Observer로 둥록하여 데이터를 설정하는 방식 (`model`이 `view`를 알고 있는 형태)
40 | - `viewController`가 `model`의 변경을 알아차리고 직접 `view`에 데이터를 설정해주는 방식(aapple의 MVC)
41 | - MVVM
42 | - `view`와 `viewModel`의 `binding`을 통해 스스로 데이터를 보여준다.
43 | - `view`에 데이터를 보여주기 위한 설정 책임을 `view` 스스로가 가진다)
44 |
45 | > user interaction
46 | - MVC
47 | - 사용자 상호작용에 대해 `view`가 `controller`에 처리를 부탁하기 위해 `delegate pattern, target-action` 등을 사용
48 | - MVVM
49 | - `view`와 `viewModel`을 알고 있으므로 필요할 때 `viewModel`의 메서드를 호출하는 방식으로 구현 가능 (필수는 아님)
50 |
51 |
52 | ## Apple의 MVC
53 | - `ViewController` 가 `view`에 대한 설정뿐만 아니라 `View Life-Cycle` 관리 등등 `view`와 밀접한 관계이다.
54 | - MVVM에서의 `viewModel`은 `view`를 알지 못하며 `view``를 설정하는 코드가 전혀 안들어간다.
55 | (데이터 변경에 대한 `view` 업데이트는 온전히 `view`의 책임인 것)
56 | - `UIKit - MVC` : event driven
57 | - 이벤트에 따라 특정 로직이 실행되고, 그에 따라 view가 바뀐다.
58 | - `SwiftUI - MVVM` : data driven
59 | - 데이터의 변경에 따라 로직이 실행도고, 그에 따라 view가 바뀐다.
60 |
61 | ## MVVM 장점
62 | - Distribution (책임 분배)
63 | - `view`는 바인딩을 통해 `viewModel`로 부터 보여줄 데이터를 가져온 뒤 직접 업데이트한다.
64 | - MVC에 비해 `controller`의 역할이 줄어든 것
65 | - Testability (테스트 용이성)
66 | - `viewModel`은 `view`에 대해 모른다.
67 | - 그렇기에 `viewModel`을 쉽게 테스트할 수 있도록 해준다.
68 | - easy og use (사용 편리성)
69 | - `view`를 수동으로 업데이트하는 MVP보다 바인딩을 사용하는 경우 MVVM이 훨씬 간단하다.
70 | (100% 언제나 그렇지는 않음. 간단한 프로젝트를 하는 경우는 굳이 MVVM을 사용하면 더 복잡해보일 수 있다.)
71 |
72 | ## MVVM 한계
73 | - 간단한 프로젝트에 사용하기에는 과하다.
74 | - 바인딩에 관한 툴이 없으면 boilerplate 코드 가 발생할 수 있다.
75 | - 큰 프로그램의 경우, 데이터 바인딩을 많이 쓰면 메모리를 많이 쓰게 된다.
76 | - `presentaion logic`이 늘어나고 SRP가 지켜지지 않으면 `viewModel`도 비대해질 수 있다.
77 | - 정해진 답이 없이 사람마다 구현 방식이 다르다.
78 | (본인만의 스타일을 만들거나 혹은 회사 스타일에 맞춰면 됨)
79 |
80 |
81 |
82 |
83 | 참고 링크
84 |
85 | - [iOS에서의 MVC와 MVVM](https://velog.io/@nnnyeong/iOS-MVC-MVVM-Architecture-Pattern)
86 | - [iOS에서의 MVVM](https://velog.io/@ictechgy/MVVM-%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4)
87 |
88 |
89 |
--------------------------------------------------------------------------------
/Swift/array_set_dictionary.md:
--------------------------------------------------------------------------------
1 | # CollectionType(Array, Set, Dictionary) 비교
2 |
3 | ## 간단 설명
4 | - Array
5 | - 리스트 컬렉션
6 | - random access가 가능. index를 통해 요소에 접근할 수 있다.
7 |
8 | ```swift
9 | let array: [Int] [1, 1, 2, 3]
10 | array[1] // 1
11 | ```
12 |
13 | - Set
14 | - 중복되지 않은 값들의 컬렉션
15 | - 순서가 보장하지 않는다.
16 | - hashable 프로토콜을 채택하는 값들을 저장한다.
17 | - 교집합, 차집합 등 집합 연산 메서드를 제공한다.
18 |
19 | ```swift
20 | let set: [Int] = [1, 1, 2, 3] // [1, 2, 3]
21 |
22 | // 중복을 허용하지 않으므로, 중복된 값을 넣어도 제거된다.
23 | ```
24 |
25 | - Dictionary
26 | - key - value 형태의 데이터를 관리하는 컬렉션
27 | - key 타입은 hashable 프로토콜을 채택한다.
28 | - 중복된 key는 허용하지 않는다.
29 | - 순서를 보장하지 않는다.
30 |
31 | ```swift
32 | var dictionary: [Int: String] = [001: "홍길동", 002: "이철수", 003: "김영희"]
33 | dictionary[001] // "홍길동"
34 | dictionary[001] = "아무개"
35 | dictionary[001] // "아무개"
36 |
37 | // key 값은 유일하므로 001에 "홍길동"이라는 값이 있는 상태에서 "아무개"를 할당하면, "홍길동"이 "아무개"로 대치된다.
38 | ```
39 |
40 | ## 시간복잡도
41 | mutating method들은 COW(Copy On Write)를 고려해야 한다.
42 |
43 | > Array
44 | - [공식 문서 참고](https://github.com/apple/swift/blob/main/stdlib/public/core/Array.swift)
45 | - `append(_ newElement: Element)`
46 | - 평균: O(1)
47 | - 최악: O(n) -> 최악의 상황은 메모리를 재할당 해야할 때이다.
48 | - `append(contentsOf:)`
49 | - 평균: O(m) -> m = Elements의 갯수
50 | - `insert(_ newElement: Element, at i: Int)`
51 | - 평균: O(n)
52 | - i가 마지막 index일 경우 append와 시간 복잡도가 같다.
53 | - `subscript(_:)`
54 | - read는 항상 O(1)
55 | - write는 평균 O(1)
56 | - NSArray와 bridged된 경우, 다른 array와 storage를 공유하고 있을 경우: O(n) -> COW 때문
57 |
58 | |시간복잡도|메서드|추가설명|
59 | |:--:|:--:|:--:|
60 | |O(1)|`count`, `randomElement()`, `last`, `isEmpty`, `popLast()`, `removeLast()`, `reseversed()`, `swapAt(_:_:)`||
61 | |O(n)|`reserveCapacity(_:)`, `remove(at:)`, `removeFirst()`, `removeAll(keepingCapacity:)`, `contains(_:)`, `contains(where:)`, `first(where:)`, `firstIndex(where:)`, `last(where:)`, `lastIndex(where:)`, `firstIndex(of:)`, `lastIndex(of:)`, `min()`, `max()`, `enumerated()`, `reverse()`, `shuffle()`, `shuffled()`, `split()`||
62 | |O(NlogN)|`sort()`, `sorted()`|merge sort와 insertion sort 기반으로 [tim sort](https://youtu.be/2pjUsuHTqHc)를 사용한다.|
63 | |O(m)|`elementsEqual(_:)`, `==`||
64 |
65 | > Set
66 | - [공식 문서 참고](https://github.com/apple/swift/blob/main/stdlib/public/core/Set.swift)
67 | |시간복잡도|메서드|추가설명|
68 | |:--:|:--:|:--:|
69 | |O(1)|`subscript(_:)`, `count`, `contains(_:)`, `removeFirst()`, `firstIndex(of:)`||
70 | |O(n)|`contains(where:)`||
71 |
72 | > Dictionary
73 | - [공식 문서 참고](https://github.com/apple/swift/blob/main/stdlib/public/core/Dictionary.swift)
74 | |시간복잡도|메서드|추가설명|
75 | |:--:|:--:|:--:|
76 | |O(1)|`subscript(_:)`, `count`, `index(forKey:)`, `popFirst()`|index(forKey:)의 경우, NSDictionary로 wrap된 경우 O(n). 공식 문서 참조)|
77 | |O(n)|`contains(where:)`, `mapValues(_:)`, `remove(at:)`, `removeValue(forKey:)`, `removeAll(keepingCapacity:)`, `rereversed()`|contains(_:) method는 없다. (key로 바로 참조하면 알 수 있기 때문)|
78 | |O(m+n)|`compactMapValues(_:)`|n = 기존 Dictionary의 크기, m: 결과 Dicrionary의 크기|
79 |
80 | ## 기타 참고
81 | 문자열의 경우
82 | - `count`: O(n)
83 | - `contains(_:)`, `~=`: O(1)
84 |
85 | 고차함수의 경우
86 | - `map`, `flatMap`, `compactMap`, `filter`, `reduce`: O(n)
87 | - 결국 for문으로 한번 순회하는 것과 같다.
88 |
89 | 그래서 문자열이 비어있는지 비교할 때는
90 | count보다 isEmpty를 사용하는 것이 좋다.
91 | - 자세한 내용은 [count == 0 vs isEmpty 무엇을 사용하는게 더 효율적일까?](https://github.com/keenkim1202/KEENs_TIL/blob/main/Swift/compare_countZero_and_isEmpty.md) 를 참고하세요.
--------------------------------------------------------------------------------
/CS/운영체제/Thread&Process.md:
--------------------------------------------------------------------------------
1 | ## 프로그램 (program)
2 | > 작업을 실행할 수 있는 파읻ㄹ
3 |
4 | - end user가 사용할 수 있는 프로그램을 어플리케이션이라고 한다.
5 | - 프로그램은 바이너리 파일로 이루어져있는데, 이것을 실행시킨 것을 프로세스 라고 한다.
6 |
7 | ## 프로세스 (process)
8 |
9 | > 단순히 실행 중인 프로그램을 말한다.
10 |
11 | - 즉, 사용자가 작성한 프로그램이 운영체제에 의해 메모리 공간을 할당받아 실행중인 것을 말한다.
12 | - 프로세스는 프로그램에 사용되는 데이터와 메모리 등의 자원, 스레드로 구성된다.
13 | - 하나의 프로세스를 구성하는 스레드들은 프로세스에 할당된 메모리, 자원 등을 공유한다.
14 | - 각각의 스레드는 독립적인 작업을 수행해야 하기 때문에 각자 스택과 PC 레지스터 값을 가지고 있다.
15 |
16 | ### 스택을 스레드마다 독립적으로 할당하는 이유
17 |
18 | > 스택은 `함수 호출 시 전달되는 인자`, `되돌아갈 주소값`, `함수 내에서 선언하는 변수` 등을 저장하기 위해 사용되는 메모리 공간이다.
19 |
20 | - 스택 메모리 공간이 독립적이라는 것은 독립적인 함수 호출이 가능하다는 것이고, 이는 독립적인 실행 흐름이 가능하게 한다.
21 | → 따라서 독립적인 실행 흐름을 위한 최소 조건으로 독립적인 스택을 할당한다.
22 |
23 | ### PC 레지스터를 스레드마다 독립적으로 할당하는 이유
24 |
25 | > PC 값은 스레드가 명령어의 어디까지 수행하였는지를 나타낸다.
26 |
27 | - 스레드는 CPU를 할당받았다가 스케쥴러에 의해 다시 섬정당한다.
28 | - 그렇기 때문에 명령어가 연속적으로 수행되지 못하고, 어느 부분까지 수행했는지 기억할 필요가 있다.
29 | → 따라서 PC 레지스터를 독립적으로 할당한다.
30 |
31 |
32 | ## 스레드 (thread)
33 |
34 | > 프로세스 내에서 실제로 작업을 수행하는 주체를 의미한다. (= 프로세스 실행의 단위)
35 |
36 | - 경량화된 프로세스
37 | - 하나의 프로세스 안에 있는 스레드들은 code, data, heap 영역을 공유하고, stack만 각자 갖는다.
38 | - 모든 프로세스에는 한 개 이상의 스레드가 존재하여 작업을 수행한다.
39 | - 또한, 두 개 이상의 스레드를 가지는 프로세스를 `멀티스레드 프로세스` 라고 한다.
40 |
41 | 
42 |
43 | ### 스레드의 장점
44 | - 프로세스보다 생성 및 종료 시간, 스레드간 전환 시간이 짧다.
45 | - 프로세스의 메모리, 자원 등을 공유하므로 커널의 도움없이 상호간에 통신이 가능하다.
46 |
47 | ### 스레드 동기화 방법의 종류
48 | > Mutex / Semaphore / Monitor 가 있다.
49 | 셋의 공통점은 모두 운영체제의 동기화 기법이라는 것이다.
50 |
51 | ### 1) Mutual Exclusion (뮤텍스)
52 |
53 | > ‘상호배제’ 라고도 한다. 임계영역을 가진 스레드들의 러닝타임이 겹치지 않게 각각 단독으로 실행되기 하는 기술이다.
54 | > 다중 프로세스들의 공유 리소스 접근을 조율하기 위해 locking, unlocking을 사용한다.
55 | >
56 | → 뮤텍스 객체를 두 스레드가 동시에 사용할 수 없다는 의미이다.
57 |
58 | - 스레드의 동시 접근을 서용하지 않겠다는 의미이다.
59 | - 임계 영역에 들어가기 위해서는 뮤텍스를 가지고 있어야 들어갈 수 있다.
60 |
61 | → 임계영역에 들어간 스레드가 뮤텍스를 이용해 임계영역에서 본인이 나올 때까지 다른 스레드가 들어오지 못하게 내부에서 자물쇠로 잠근다.
62 |
63 | ** 임계영역(Critial Section)이란?
64 |
65 | - 다중 프로그래밍 운영체제에서 여러 프로세스가 데이터를 공유하면서 수행될 때
66 | 각 프로세스에서 공유 데이터를 접근하는 프로그램 코드 부분을 가리키는 말이다.
67 |
68 | → 같은 부분에 여러 프로세스가 동시에 접근하면 시간 차이 등으로 인해 문제가 발생할 수 있다.
69 | 그러므로 공유 데이터에 접근할 때,
70 | 다른 프로세스들은 절대로 그 데이터에 접근하지 못하도록 해야한다.
71 |
72 |
73 | ### 2) Semaphore (세마포어)
74 | > 멀티 프로그래밍 환경에서 공유된 자원에 대한 접근을 제한하는 방법이다.
75 | > 사용하고 있는 스레드/프로세스의 수를 공통으로 관리하는 하나의 값을 이용해 상호배제를 달성한다.
76 | > 공유 자원에 접근할 수 있는 프로세스의 최대 허용치만큼 동시에 사용자가 접근할 수 있다.
77 | >
78 |
79 | - 리소스의 상태를 나타내는 간단한 카운터라고 생각할 수 있다.
80 | - 세마포어는 운영체제/커널의 한 지정된 저장장치 내의 값으로, 각 프로세스는 이를 확인하고 변경할 수 있다.
81 | - 확인되는 세마포어의 값에 따라, 그 프로세스가 즉시 자원을 사용할 수 있거나, 또는 이미 다른 프로세스에 의해 사용중이라는 사실을 알게 되면 재시도를 하기 전 일정 시간을 기다려야 한다.
82 | - 세마포어는 이진수를 사용하거나 추가적인 값을 가질 수도 있다.
83 |
84 | → 세마포어를 사용하는 프로세스는 그 값을 확인하고, 자원을 사용하는 동안에는 그 값을 변경함으로써 다른 세마포어 사용자들이 기다리도록 해야한다.
85 |
86 | - 뮤텍스와 비슷한 역할 BUT, 동시 접근 동기화가 아닌, 접근 순서 동기화에 더 가깝다.
87 | - 일반적으로 비교적 긴 시간을 확보하는 리소스에 대해 이용하게 되며, 운영체제 리소스를 경적으로 사용하는 다중 프로세스에서 행동을 조정하거나 동기화 시키는 기술이다.
88 |
89 | ### Mutex / Semaphore 둘의 차이는?
90 |
91 | - 가장 큰 차이점은 관리하는 동기화 대상의 갯수이다.
92 |
93 | - 뮤텍스: 하나의 프로세스/스레드가 접근하는 것을 막아준다 → 동기화 대상이 하나
94 | - 세마포어: 여러 프로세스/스레드가 접근하는 것을 막아준다 → 동기화 대상이 하나 이상
95 |
96 | - 세마포어는 뮤텍스가 될 수 있지만, 뮤텍스는 세마포어가 될 수 없다.
97 | → 왜냐면 뮤텍스는 상태가 0, 1 두개 뿐인 binary semaphore 이기때문이다.
98 | - 세마포어는 소유할 수 없고, 뮤텍스는 소유 가능하다.
99 |
100 | - 뮤텍스는 소유주가 이에 대한 책임을 진다. (상태가 두개 뿐이므로 lock을 가질 수 있음)
101 |
102 | - 뮤텍스: 뮤텍스를 소유하고 있는 스레드가 이 뮤텍스를 해제할 수 있다.
103 | → 세마포어: 세마포어를 소유하지 않는 스레드가 세마포어를 해제할 수 있다.
104 | - 세마포어: 시스템 범위에 걸쳐있고 파일 시스템 상의 파일 형태로 존재한다.
105 | → 뮤텍스: 프로세스 범위를 가지며 프로세스가 종료될 때 자동으로 clean up 된다.
106 |
--------------------------------------------------------------------------------
/Swift/iterator.md:
--------------------------------------------------------------------------------
1 | # Iterator
2 | - 컬렉션(Collection)에 저장되어 있는 요소(element)들을 순회하는 인터페이스이다.
3 | - `Sequence` 프로토콜을 준수하는 collection들은 iterator로 만들 수 있다.
4 |
5 |
6 |
7 | Iterator는 for 를 사용할 수 있게 해준다.
8 | - filter, map, reduce, sorted와 같은 메서드들을 제한없이 사용할 수 있다.
9 |
10 | 프로그래밍을 하다보면 데이터들을 looping(iterating)할 일이 많다.
11 | - ex) array 안의 원소들을 하나하나 가져올 때, 문자열 안의 개별적인 문자를 가져올 때, 특정 범위의 숫자를 가져올 때…
12 |
13 |
14 |
15 | swift에서 iteration이 어떻게 작동하는지 알아보자.
16 |
17 |
18 |
19 | ## IteratorProtocol
20 | > A type that supplies the values of a sequence one at a time.
21 |
22 | ```swift
23 | public protocol IteratorProtocol {
24 | /// The type of element traversed by the iterator.
25 | associatedtype Element
26 | mutating func next() -> Element?
27 | }
28 | ```
29 |
30 | - 매번 for 루프를 사용하 때, 당신은 이미 iterator를 사용하고 있다.
31 | - 예를 들어 배열에 `for-in`문을 통해 loop를 돈다.
32 | - `for-in`은 [syntatic sugar](https://en.wikipedia.org/wiki/Syntactic_sugar)이다. (사용자가 사용하기 쉽게 한단계 포장한 것을 의미)
33 | - 그 아레에서는 `makeIterator()`를 통해 iterator가 만들어지는 일이 벌어지고 있다.
34 | - Swift는 while문을 통해 원소들을 훑는다. 마찬가지다.
35 |
36 | - `makeIterator()` 메서드는 `IteratorProtocol` 과 매우 연관되어있는 `Sequence` 프로토콜 안에 정의 되어있다.
37 |
38 | - Iterator는 `IteratorProtocol`을 적용한다. (스위프트에서 이것은 작지만 강력한 요소이다.)
39 | - `IteratorProtocol`은 `Element` 라는 타입과 `next()` 메서드와 연관이 있다.
40 | - `next()` 메서드는 `optional Element` 타입을 리턴한다.
41 | - Iterator들은 다수의 element들을 loop할 수 있는 값을 생성한다.
42 |
43 |
44 |
45 | ## makeIterator()
46 | > makeiterator() `Instance Method`
47 | > : Retuens an iterator over the elements of the collection.
48 |
49 | ```swift
50 | func makeIterator() -> IndexingIterator
51 | ```
52 |
53 | - collection을 이 메서드를 통해 iterator로 만들 수 있다.
54 |
55 | ```swift
56 | let str: String = "hello"
57 | var iterator = str.makeIterator()
58 | ```
59 |
60 | ## next()
61 | > next() `Instance Method`
62 | > : Advances to the next element and returns it, or nil if no next element exists.
63 |
64 |
65 | - next()를 부를 때마다 iterator값은 다음 element를 가리키게 된다.
66 | - 값이 바뀌므로 mutate될 수 있도록 변수로 선언해주어야 한다. (let으로 선언 불가)
67 | - next()를 불렀는데 다음 element가 존재하지 않는다면(마지막 element라면) `nil` 을 리턴한다.
68 | - 계속 next()를 부르면 계속 nil이 나온다.
69 | ```swift
70 | iterator.next() // "h"
71 | iterator.next() // "e"
72 | iterator.next() // "l"
73 | iterator.next() // "l"
74 | iterator.next() // "o"
75 | iterator.next() // nil
76 | iterator.next() // nil
77 | iterator.next() // nil
78 | ```
79 |
80 |
81 | ## 문자열 자르기 문제에 활용
82 | - 문자열을 원하는 길이로 자르는 함수를 iterator를 활용하여 구현해보기
83 |
84 |
85 | ```swift
86 | // str: 주어진 문자열, size: 자를 크기
87 | func splitString(str: String, size: Int) -> [String] {
88 | var results: [String] = [] // 자른 문자열들을 담을 배열
89 | var count: Int = 0 // size 길이가 될 때까지 temp에 문자를 더할 때마다 증가시킬 count (최대 size)
90 | var temp: String = "" // size 길이의 문자열이 완성될 때까지 임시로 담아둘 문자열 변수
91 |
92 | for char in str {
93 | temp.append(char)
94 | count += 1
95 |
96 | if count == size {
97 | results.append(temp)
98 | count = 0
99 | temp = ""
100 | }
101 | }
102 |
103 | if !temp.isEmpty {
104 | results.append(temp)
105 | }
106 |
107 | return results
108 | }
109 | ```
110 |
111 | - 생각해보니 count 변수가 굳이 필요없을 것 같아서 없앴다.
112 | ```swift
113 | func splitString(str: String, size: Int) -> [String] {
114 | var results: [String] = []
115 | var temp: String = ""
116 |
117 | for char in str {
118 | temp.append(char)
119 |
120 | if temp.count == size {
121 | results.append(temp)
122 | temp = ""
123 | }
124 | }
125 |
126 | if !temp.isEmpty {
127 | results.append(temp)
128 | }
129 |
130 | return results
131 | }
132 | ```
133 |
--------------------------------------------------------------------------------
/iOS/keyboardIssue_UIView_Extension.md:
--------------------------------------------------------------------------------
1 | ## 키보드의 움직임에 따라 버튼을 움직이도록 하는 extension
2 |
3 | ```swift
4 | /// 키보드가 올라옴에 따라 버튼을 움직이기
5 | extension UIView {
6 | /// 키보드 위로 올라올 버튼의 높이
7 | static var buttonUpperKeyboardHeight: CGFloat = 0
8 |
9 | /// 기존 버튼의 높이
10 | public var defaultButtonHeight: CGFloat {
11 | get {
12 | return UIButton.buttonUpperKeyboardHeight
13 | }
14 | set {
15 | UIButton.buttonUpperKeyboardHeight = newValue
16 | onKeyboard()
17 | }
18 | }
19 |
20 | /// sageArea의 bottom값을 리턴하는 함수
21 | private var safeAreaBottom: CGFloat {
22 | if let window = UIApplication.shared.windows.first {
23 | return window.frame.height - window.safeAreaLayoutGuide.layoutFrame.height - window.safeAreaLayoutGuide.layoutFrame.minY
24 | }
25 |
26 | return 0
27 | }
28 |
29 | /// 키보드가 올라오면 버튼을 위로 올려주는 함수
30 | public func onKeyboard(scrollView: UIScrollView? = nil, keyboardHeight: CGFloat = 0) {
31 | guard
32 | let bottomConstraint = self.findConstraint(layoutAttribute: .bottom),
33 | let heightConstraint = findHeightConstraint()
34 | else { return }
35 |
36 | var isOnSuperView: Bool = self.superview?.isEqual(bottomConstraint.firstItem) == true
37 | var currentButtonHeight: CGFloat = self.frame.height
38 |
39 | if UIButton.buttonUpperKeyboardHeight > 0 {
40 | currentButtonHeight = UIButton.buttonUpperKeyboardHeight
41 | } else {
42 | currentButtonHeight = self.frame.height
43 | isOnSuperView = false
44 | }
45 |
46 | UIView.animate(withDuration: 0.21, animations: { [weak self] in
47 | guard let `self` = self else { return }
48 |
49 | if keyboardHeight > 0 {
50 | bottomConstraint.constant = keyboardHeight - (isOnSuperView ? 0 : self.safeAreaBottom)
51 | heightConstraint.constant = currentButtonHeight
52 |
53 | scrollView?.contentInset.bottom = keyboardHeight + UIButton.buttonUpperKeyboardHeight
54 | scrollView?.scrollIndicatorInsets.bottom = keyboardHeight + UIButton.buttonUpperKeyboardHeight
55 | } else {
56 | bottomConstraint.constant = 0
57 | heightConstraint.constant = currentButtonHeight + (isOnSuperView ? self.safeAreaBottom : 0)
58 |
59 | scrollView?.contentInset.bottom = 0
60 | scrollView?.scrollIndicatorInsets.bottom = 0
61 | }
62 |
63 | self.superview?.layoutIfNeeded()
64 | }, completion: nil)
65 | }
66 |
67 | /// 뷰의 제약조건을 찾는 함수
68 | private func findConstraint(layoutAttribute: NSLayoutConstraint.Attribute) -> NSLayoutConstraint? {
69 | for constraint in (superview?.constraints ?? []) where isMatchItem(constraint: constraint, layoutAttribute: layoutAttribute) {
70 | return constraint
71 | }
72 |
73 | return nil
74 | }
75 |
76 | /// 주어진 조건과 맞는지 체크하는 함수
77 | private func isMatchItem(constraint: NSLayoutConstraint, layoutAttribute: NSLayoutConstraint.Attribute) -> Bool {
78 | let isFirstMatch = constraint.firstItem as? UIView == self && constraint.firstAttribute == layoutAttribute
79 | let isSecondMatch = constraint.secondItem as? UIView == self && constraint.secondAttribute == layoutAttribute
80 | return isFirstMatch || isSecondMatch
81 | }
82 |
83 | /// 뷰의 height 제약조건을 찾는 함수
84 | private func findHeightConstraint() -> NSLayoutConstraint? {
85 | return constraints.last(where: {
86 | $0.firstItem as? UIView == self && $0.firstAttribute == .height && $0.relation == .equal
87 | })
88 | }
89 | }
90 | ```
91 |
--------------------------------------------------------------------------------
/CS/패러다임/pure_function.md:
--------------------------------------------------------------------------------
1 | # 순수함수 (Pure Function) 에 대하여
2 |
3 | ## 순수 함수란?
4 | - 어떠한 입력에 대해 항상 같은 출력을 만드는 함수를 의미한다.
5 | - 즉, 외부의 영향을 받거나 주지 않는 것, `side-effect`(부수 효과)가 없는 것을 말한다.
6 |
7 | ## 순수 함수의 특징
8 | - 동일한 입력에 대해 동일한 출력을 가진다.
9 | - `side-effect` 가 없다.
10 | - 두 개의 순수한 표현식 사이에 데이터 의존성이 없는 경우,
11 | - 순서를 반대로 하거나 병렬로 수행이 가능하다.
12 | - 서로 간섭을 할 수 없다.
13 | - 그렇기 때문에 `thread-safe` 하다.
14 |
15 | ## 순수함수가 아닌 예시
16 | ### ex1) 전역(혹은 지역) 프로퍼티에 의존하는 경우
17 | ```swift
18 | // side-effect (O)
19 | var counter: Int = 0
20 |
21 | func increament(num: Int) -> Int {
22 | counter += 1
23 | return counter
24 | }
25 | ```
26 | - `increament(num:)` 함수는 함수 바깥에 있는 전역 프로퍼티 `counter` 값을 변경 시킨다.
27 | - 다른 함수가 `counter` 프로퍼티에 접근할 지도 모른다.
28 | - 그렇기에 이러한 로직은 프로퍼티의 값이 바뀜으로써(`mutating the property`) 예상치 못한 부수효과(`side-effect`)를 발생시킬 여지가 있으므로 순수함수가 아니다.
29 |
30 | ### ex2) 랜덤 요소에 의존하는 경우
31 | ```swift
32 | // side-effect (O)
33 | func getRamdomNumber(maxNum: Int) -> Int {
34 | return Int.random(in: 0...maxNum)
35 | }
36 | ```
37 | - 함수에 랜덤적 요소가 포함되어있다면 테스트하기 힘들다.
38 | - 순수함수는 나의 코드를 테스트하기 쉽게 한다.
39 | - `getRandomNumber(maxNum:)` 함수는 `0부터 `maxNum` 사이의 숫자를 랜덤하게 리턴해준다.
40 | - 위와 같이 랜덤 요소를 리턴하는 함수로 테스트(= 충분한 unit test)를 할 수 있을까?
41 | - 여러 `input`을 통해 함수의 `output`을 테스트해볼 수는 있을 것이다.
42 | - `output`은 `getRandomNumber(maxNum:)` 에 주어진 `input`에 의존하게 된다.
43 | - 그 말은, 여러 `input` 경우의 수에 따른 테스트를 실행함으로써 테스트가 가능하고, 그 당시 다른 테스트들을 방해하는 테스트는 없을 것이다.
44 | - 그래서 순수함수라고 착각할 수 있다.
45 | - 이 함수는 함수를 실행할 때마다 같은 결과를 낸다는 보장이 없다.
46 | - `XCTest`를 통해 테스트를 작성할 수 없듯 이 함수는 테스트가 불가능하다는 뜻이다. (= `untestable`)
47 | - XCTest 관련 참고 링크 : [How to Test Asynchronous Functions Using Expectation in Swift
48 | ](https://betterprogramming.pub/how-to-test-asynchronous-functions-using-expectation-2c9183fd99c9)
49 |
50 | ## 순수함수의 예시
51 | ```
52 | [ 순수함수의 조건 ]
53 | 1. 항상 같은 결과를 리턴한다.
54 | 2. 전역으로 선언된 것의 상태를 변경 시키지 않고 그대로 둔다.
55 | ```
56 |
57 | ```swift
58 | // side-effect (X)
59 | func hello(name: String) -> String {
60 | return "안녕! \(name)아!"
61 | }
62 | ```
63 | - `hello(name:)` 함수는 위의 두 조건을 만족하므로 순수함수라고 볼 수 있다.
64 | - 1번 조건 만족: 항상 같은 `input`에 같은 `output`을 리턴한다.
65 | - 2번 조건 만족: 전역 변수 즉, 전역적으로 선언된 상태값(`global state`)을 변경시키지 않는다.
66 |
67 | ## 순수함수의 장점
68 | - 읽기 쉽고, `side-effect`가 없다.
69 | - 그러므로 문제가 발생시 그 이유에 대해 쉽게 추론이 가능하다.
70 | - 명확히 정의된 함수의 파라미터를 가지고 있다. 그러므로 도출된 `output`은 온전히 `input` 파라미터에 의해 결정된다.
71 |
72 | - 함수는 오직 `input` 파라미터에 의해 결정되므로, 해당 코드는 이식이 가능하다. (= `portable`)
73 | - 이 말은, 함수는 앱 전체 어느 곳에서든 사용히 가능하다는 뜻이다.
74 |
75 | - 테스트 코드 작성에 용이하다.
76 | - 주어진 다양한 `input`에 대한 예측가능한 결과를 코드를 통해 유추할 수 있다면, 우리는 테스트할 수 있다.
77 | - 여기저기 공유되고 있는 가변상태(`mutable state`)는 모든 경우의 수를 빼놓지 않고 테스트 하기 어렵다.
78 | - 이것은 `독립적`이고 `반복 가능한` 테스트에 반하는 아이디어이다.
79 |
80 | - 정의상으로 순수함수는 참조 투명성(referential transparency)을 갖는다.
81 | - 이는 프로그램의 결과를 변경하지 않고 표현식을 값으로 대체할 수 있음을 의미한다.
82 | ```
83 | (1) : f(x) = x + 1
84 | (2) : y = f(x) + 1
85 | (3) : y = x + 1 + 1
86 |
87 | 위의 3 가지 수식이 있다고 할 때, (2)와 (3)은 다른가? 둘은 같다.
88 | 식이 원래 가지는 의미가 변하지 않고 대체가 가능하다.
89 | ```
90 |
91 | - 메모리의 관점에서는
92 | - 한번 값이 할당된 메모리 위치에는 새로운 값을 다시 할당하지 않는다는 것이 참조 투명성의 필수 조건이다.
93 | - 이 조건이 만족된다면, 어떤 함수를 많이 호출(callback) 하더라도, 항상 동일한 결과를 얻게 된다.
94 | - (프로그램이 사용하는 컴퓨터 메모리의 값은 프로그램이 실행되는 동안에는 절대 변하지 않는다.)
95 |
96 | - 구조체, 열거형과 같은 값 타입(value type)은 우리가 상태를 변경하고자할 때 명확히 명시한다.
97 | - 즉, 함수가 상태를 변경하고자 할 때는 `mutating` 키워드를 꼭 필요로 한다는 뜻이다.
98 | - 이 키워드를 통해 참조 투명성을 갖을 수 있다.
99 | - 순수함수들은 작성하기 좋고, 테스트가 가능한 코드에 중요하다는 것을 나타내기 때문에 유용하다.
100 |
101 | ```
102 | [ 참조 투명성(referential transparency) ]
103 | - 일반적으로 응용프로그램의 동작에 영향을 미치지 않고 항상 함수를 반환값으로 대체할 수 있음을 의미한다.
104 | - 코드의 재사용성을 보장합니다.
105 | - 데이터가 가변상태인 것을 거부한다. (데이터의 상태 변경 불가)
106 | - 가변이 가능하다면, 동일한 함수의 두 호출은 잠재적으로 두 개의 서로 다른 결과를 만들어낼 가능성이 있고, 텍스트와 유지보수가 어렵다.
107 |
108 | - 표현식을 결과값으로 대체했을 때 아무 문제가 발생하지 않는다면, 해당 표현식은 "참조 투명성"을 갖는다고 한다.
109 | ```
--------------------------------------------------------------------------------
/iOS/WebView_method.md:
--------------------------------------------------------------------------------
1 | # WebView 관련 메서드
2 |
3 | 웹뷰에서 네이티브(iOS, AOS)로 메세지를 전달할 수 있다.
4 | 그때 웹에서 보내는 메세지의 이름과 메세지가 전달될 때 처리할 대상(self)를 등록해야 한다.
5 | ```swift
6 | let contentController = webView.configuration.userContentController
7 | contentController.add(self, name: "test")
8 | ```
9 |
10 | ## WKNavigationDelegate
11 | ```swift
12 | func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { }
13 | ```
14 | - 웹 페이지 탐색 여부를 결정하는 함수 (특정한 액션 정보를 바탕으로 새로운 컨텐츠로 navigate 하는 것에 delegate에게 허락을 구하는 함수)
15 | - handler는 1번만 호출되어야 하므로 통과할 때 decisionHandler(.allow) 처리.
16 | - (ex. 이외의 특정 조건일 때 .cancel 후 return)
17 |
18 |
19 |
20 | ```swift
21 | func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) { }
22 | ```
23 | - 웹 뷰가 컨텐츠 탐색을 시작할 때 호출되는 함수 (main frame에서 navigation이 시작되었다는 것을 delegate에게 알리는 함수)
24 |
25 |
26 |
27 | ```swift
28 | func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { }
29 | ```
30 | - 웹 뷰가 콘텐츠 받기를 완료 혹은 실패했을 때 호출되는 함수 (navigation이 완료되었다고 delegate에게 알리는 함수)
31 |
32 |
33 |
34 | ## WKScriptMessageHandler, WKUIDelegate
35 |
36 | ```swift
37 | extension SomeViewController: WKScriptMessageHandler, WKUIDelegate {
38 | func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
39 | print(#function, message.name)
40 | switch message.name {
41 | case "outLink":
42 |
43 | let body = message.body
44 | print(body, type(of: body))
45 | guard let outLinkDict = body as? [String: String] else { return }
46 | print("dict: \(outLinkDict)")
47 |
48 | let baseUrl: String = "http://somebaseurl.com"
49 |
50 | guard
51 | let path = outLinkDict["path"],
52 | let title = outLinkDict["title"]
53 | else { return }
54 |
55 | let url = baseUrl + path
56 | let naviTitle = title
57 | // do something ...
58 |
59 | default:
60 | break
61 | }
62 | }
63 | ```
64 | - 웹 뷰에서 브릿지로 객체를 보내줬을 때 파싱해서 사용하는 방법
65 |
66 |
67 |
68 |
69 | ```swift
70 | extension WebAlarmTalkViewController: WKScriptMessageHandler, WKUIDelegate {
71 | func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
72 | print(#function, message.name)
73 | switch message.name {
74 | case "outLink":
75 | guard let bodyString = message.body as? String,
76 | let bodyData = bodyString.data(using: .utf8) else { return }
77 |
78 | guard let outlinkInfo = try? JSONDecoder().decode(Outlink.self, from: bodyData) else { return }
79 |
80 | let baseUrl: String = "http://somebaseurl.com"
81 |
82 | let url = baseUrl + outlinkInfo.path
83 | let naviTitle = outlinkInfo.title
84 | // do something ...
85 |
86 | default:
87 | break
88 | }
89 | }
90 | }
91 | ```
92 | - 웹 뷰에서 브릿지로 문자열 타입의 JSON 형태를 보내줬을 때 파싱해서 사용하는 방법
93 |
94 |
95 |
96 | ```swift
97 | /// 웹 상에서 뒤로 갈 수 있다면 이전 화면으로, 가장 첫 진입 화면이라면 메뉴로 pop 하는 함수 (leftBaButtonItem)
98 | @objc func onNaviBack(_ sender: UIBarButtonItem) {
99 | if let webView = webView, webView.canGoBack {
100 | webView.goBack()
101 |
102 | // TIP : 이전 페이지 스택에 남기지 않고 뒤로가기 시, 최초 진입 페이지를 보여주고 싶을 때 주석 사용
103 | // let firstNavigationItem = webView.backForwardList.backList.first!
104 | // webView.go(to: firstNavigationItem)
105 | // load(url: firstNavigationItem.url)
106 | naviTitle = "이것은 타이틀"
107 | } else {
108 | self.navigationController?.popViewController(animated: true)
109 | }
110 | }
111 | ```
112 | - 웹뷰 내에서 쌓인 페이지들의 스택이 비었을 때(즉, 최초 진입 페이지에 도달했을 때 뷰컨을 pop 해주는 방법
113 |
114 |
115 |
116 | ```swift
117 | ```
118 |
119 |
120 |
121 | ```swift
122 | ```
123 |
124 |
125 |
126 | ```swift
127 | ```
128 |
129 |
130 |
131 | ```swift
132 | ```
133 |
134 |
135 |
--------------------------------------------------------------------------------
/CS/ETC/clean_code_01.md:
--------------------------------------------------------------------------------
1 | # Clean Code 1장
2 | : 깨끗한 코드
3 |
4 | - 이 책은 코드를 최대한 다양한 각도에서 살펴보고자 한다.
5 | - 이 책을 읽고나면
6 | - 좋은 코드와 나쁜 코드를 구분하는 능력이 생긴다.
7 | - 좋은 코드를 작성하는 방법도 익힌다.
8 | - 나쁜 코드를 좋은 코드로 바꾸는 실력도 쌓인다.
9 |
10 |
11 |
12 |
13 | ## 코드가 존재하리라
14 | - 코드를 자동으로 생성하는 시대가 다가오면 프로그래머가 필요가 없을까?
15 | - 그럴 일은 전혀 없다. 코드는 요구사항을 상세히 표현하는 수단이기 때문이다.
16 | - 기계가 실행할 정도로 상세하게 요구사항을 명시하는 작업이 프로그래밍이다.
17 | - 이렇게 명시한 결과가 코드 이다.
18 |
19 |
20 |
21 | - 요구사항을 모호하게 줘도 우리 의도를 정확히 꿰뚫어 프로그램을 완벽하게 실행하는 기계는 절대로 불가능하다.
22 | - 창의력과 직관을 보유한 우리 인간 조차도 고객의 막연한 감정만 갖고는 성공적인 시스템을 구현하지 못한다.
23 | - 제대로 명시한 요구사항은 코드만큼 정형적이며 테스트 케이스로 사용하도 좋다.
24 |
25 |
26 |
27 | - 궁극적으로 코드는 요구사항을 표현하는 언어라는 사실을 명심한다.
28 | - 요구사항에 더욱 가까운언어를 만들 수도 있고, 요구사항에 정형 구조를 뽑아내는 도구를 만들 수도 있다.
29 | - 하지만 어느 순간에는 정밀한 표현이 필요하다. 그 필요성을 없앨 방법은 없다. 그러므로 코드도 항상 존재할 것이다.
30 |
31 |
32 |
33 |
34 | ## 나쁜 코드
35 | - 우리는 오랫동안 나쁜 코드에 시달려왔기에 좋은 코드가 중요하다는 사실을 안다.
36 |
37 | > 80년대 후반 킬러앱을 구현한 회사
38 |
39 | - 제품이 큰 인기를 끈 프로그램이 있었는데
40 | - 출시 시기가 점차 늘어지고, 이전 버그가 다음 버전에도 그대로 남아있었다.
41 | - 프로그램 시동 시간이 길어지고, 프로그램이 죽는 횟수도 늘어났다.
42 | - 그 회사는 얼마 못가 망했다.
43 |
44 | > 왜 망했을까
45 |
46 | - 출시에 바빠 코드를 마구 짰던 것이다.
47 | - 기능을 추가할수록 코드는 엉망이 되어갔고, 결구 감당이 불가능한 수준에 이루렀다.
48 | ```
49 | 회사가 망한 원인은 바로 나쁜 코드 탓이었다.
50 | ```
51 |
52 | > 프로그래머라면 누구나 당연히 나쁜 코드로 고생한 경험이 있다.
53 | - 그렇다면 왜 나쁜 코드를 짰는가?
54 | - 우리는 모두 자신이 짠 쓰레기 코드를 쳐다보며 나중에 손보겠다고 생각한 경험이 있다.
55 | - 그때 그 당시 우리는 `르블랑의 법칙`을 몰랐다.
56 | ```
57 | * 르블랑의 법칙:
58 | 나중을 결코 오지 않는다. (롤 르블랑 아님)
59 | ```
60 |
61 |
62 |
63 | ## 나쁜 코드로 치르는 대가
64 | > 개발 속도와 팀 생산성이 떨어진다.
65 |
66 | - 프로젝트 초반에는 번개처럼 나가다가 1-2년 만에 굼뱅이처럼 기어가는 팀도 많다.
67 | - 매번 얽히고 설킨 코드를 해독해서 얽히고 설킨 코드를 더한다.
68 | ```
69 | 시간이 지나면 쓰레기 더미는 점점 높고 깊어지고 커지고, 불가항력이 된다.
70 | ```
71 |
72 | > 팀 생산성이 떨어진다.
73 |
74 | - 나쁜 코드가 쌓일수록 팀 생산성을 떨어지고 0에 근접한다.
75 | - 관리층은 이를 복구하기 위해 인력을 추가로 투입한다.
76 | - 하지만 새 인력은 시스템 설계에 대한 조예가 깊지 않아, 설계 의도에 맞는 변경과 반하는 변경을 구분하지 못한다.
77 | ```
78 | 결국 나쁜 코드를 더 많이 양성하고, 생산성을 더더욱 떨어져 0에 수렴한다.
79 | ```
80 |
81 | > 원대한 재설계의 꿈
82 |
83 | - 마침내 팀에서는 혐오스러운 코드로는 일을 하지 못하겠다며 관리층에게 재설계를 요구하게 된다.
84 | - 관리층은 자원을 쏟기 싫지만 생산성이 바닥이라는 것을 인정하기에 재설계를 허락한다.
85 | - 처음부터 시작해 진정으로 아룸다운 작품을 창조할 새로운 팀과 현재 시스템을 유지보수할 팀이 나뉘게 된다.
86 | - 새로운 팀은 기존 기능을 따라잡아야 하고, 기존 시스템에 변경사항이 생기면 그것 또한 반영해야 한다.
87 | ```
88 | 새 시스템이 기존 시스템을 따라잡을 즘이면
89 | 기존 팀원들은 모두 나갔고 새로운 팀원들이 새 시스템을 설계하고자 나선다.
90 | 왜? 현재 시스템이 너무 엉망이어서
91 | ```
92 |
93 | > 태도
94 | - 코드가 너무 엉망이라 몇 시간을 예상한 업무가 몇 주로 늘어난 경험이 있는가?
95 | - 한 줄만 고치면 되리라 예상했다가 모듈 수백 개를 건들인 경험이 있는가?
96 |
97 |
98 |
99 | 코드가 왜 그렇게 되었을까?
100 | - 일정에 쫒기더라도 대다수 관리자는 좋은 코드를 원한다.
101 | - 그들이 일정과 요구사항을 강력하게 밀어붙이는 이유는 그것이 그들의 책임이기 때문이다.
102 | - 좋은 코드를 사수하는 일은 바로 우리 프로그래머들의 책임이다.
103 | ```
104 | 나쁜 코드의 위험을 이해하지 못하는 관리자 말을 그대로 따르는 행동은 전문가 답지 못하다.
105 | ```
106 |
107 | > 원초적 난제
108 |
109 | - 근본적인 가치에서 난제에 봉착한다.
110 | - 첫째, 나쁜 코드가 업무 속도를 늦춘다.
111 | - 둘째, 기한을 맞추려면 나쁜 코드를 양산할 수 밖에 없다.
112 |
113 | - 진짜 전문가는 둘째 부분이 틀렸다는 사실을 잘 안다.
114 | - 나쁜 코드를 양산하면 기한을 맞추지 못한다.
115 | - 오히려 엉망진창인 상태로 인해 속도가 늦어지고 결국 기한을 놓친다.
116 | ```
117 | 기한을 맞추는 유일한 방법, 빨리가는 유일한 방법은 언젠 코드를 "최대한 깨끗하게 유지하는 습관"이다.
118 | ```
119 |
120 | > 깨끗한 코드라는 예술
121 | > : "깨끗한 코드를 어떻게 작성할까?"
122 |
123 | - 깨끗한 코드와 나쁜 코드를 구분할 줄 안다고 깨끗한 코드를 작성할 줄 안다는 뜻은 아니다.
124 | - `청결` 이라는 힘겹게 습득한 감각을 활용해 자잘한 기법들을 적용하는 절제와 규율이 필요하다.
125 | - 열쇠는 `코드 감각`이다.
126 | - 코드 감각이 있어야 좋은 코드와 나쁜 코드를 구분하고,
127 | - 절제와 규율을 적용해 나쁜 코드를 좋은 코드로 바꾸는 전략도 파악한다.
128 |
129 | - `코드 감각이 없는 프로그래머`는 때로는 나쁜 모듈을 알아보지만, 거기서 끝이다.
130 | - `코드 감각이 있는 프로그래머`는 나쁜 모듈을 보면 좋은 모듈로 개선할 방안을 떠올리고, 최고 방안을 선택한 후 그 방안까지 이동하는 경로를 계획한다.
131 |
132 |
133 |
134 | [깨끗한 코드란?](깨끗한코드란.md)
135 |
136 |
137 |
138 | ## 우리는 저자다
139 | > `코드를 읽는 시간 : 코드를 짜는 시간` = `10 : 1`을 훌쩍 넘는다.
140 |
141 | - 새 코드를 짜면서 우리는 끊임없이 기존 코드를 읽는다.
142 | - 따라서 읽기 쉬운 코드가 중요하다.
143 | - 읽기 시운 코드를 짜면, 기존 코드를 읽는 시간이 줄어들고, 그러면 새 코드를 짜기도 쉬워진다.
144 |
145 |
146 |
147 |
148 | ## 보이스카우드 규칙
149 | : 캠프장은 처음 왔을 때보다 더 깨끗하게 해놓고 떠나라.
150 |
151 | - 체크아웃할 때보다 좀 더 깨끗한 코드를 체크인한다면 코드는 절대 나빠지지 않는다.
152 | - 한꺼번에 많은 시간과 노력을 투자해 코드를 정리할 필요가 없다.
153 | ```
154 | - 변수 이름 하나를 개선, 조금 긴 함수 하나를 분할, 약간의 중복을 제거, 복잡한 if문 하나를 정리하면 충분하다.
155 | ```
156 |
157 |
158 |
159 | ## 결론
160 | - 이 책을 읽는다고 뛰어난 프로그래머가 된다는 보장은 없다.
161 | - 코드 감각을 확실히 얻는다는 보장도 없다.
162 | ```
163 | 단지 뛰어난 프로그래머가 생각하는 방식과 그들이 사용하는 기술/기교/도구를 소개할 뿐이다.
164 | ```
165 |
166 |
167 |
168 | - 좋은 코드도 소개하고, 나쁜 코드도 소개한다.
169 | - 나쁜 코드를 좋은 코드로 바꾸는 방법도 소개한다.
170 | - 다양한 경험적 교훈, 체계, 절차, 기법도 열거한다.
171 | - 무수한 예제도 보여준다.
172 | ```
173 | 나머지는 여러분에게 달렸다.
174 | ```
--------------------------------------------------------------------------------
/iOS/GCD_EX.md:
--------------------------------------------------------------------------------
1 | # GCD 관련 요약 설명 및 예제 코드
2 | # GCD란?
3 | - GCD는 특정 코드 블록으로 된 Task들을 `FIFO Queue`들에 넘겨주면 이들을 관리하고 제공하는 녀석이다.
4 | - Task 를 만들어 GCD에 넘기면, 시스템에서 알아서 Thread를 할당하여 실행시켜 준다.
5 | ```
6 | 간단하게 말하면, Queue 를 이용해서 실행해야할 작업들을 알아서 관리해주는 녀석이다.
7 | 💡 자세한 내용은 'GCD & Operation' 을 참고하자!
8 | ```
9 |
10 | ## Queue란?
11 | - 자료구조에 나오는 그 Queue가 맞음.
12 | - FIFO (First In First Out)
13 | - 즉, 들어온 순서대로 Task를 처리하고, 제일 처음 들어온 Task가 제일 먼저 나가는 구조를 띄는 자료구조를 말한다.
14 |
15 | ## 요약 설명
16 | - GCD에서 사용하는 Queue는 DispatchQueue의 타입으로 선언 되어있고 아래의 3가지가 있다.
17 |
18 | ### Main Queue
19 | - Main Thread에서 작동하는 Queue
20 | - 주로 UI 관련 작업을 한다.
21 |
22 | ### Global Queue
23 | - 시스템에 의해 관리되는 Concurrent Queue
24 | - Queue에 들어가는 Task 우선순위를 Qos를 통해 표현할 수 있다.
25 | - Qos = Quality Of Service : global에서 수행될 Task들의 우선순위에 대한 표현
26 |
27 | Qos 높은 순위부터 순서대로 보면
28 | - `userInteractive`
29 | : main thread와 같이 바로 수행해야 될 작업에 사용
30 | - `userInitiated`
31 | : 사용자가 결과를 기다리는 작업 (중요도가 높을 때)
32 | - `default`
33 | : 덜 중요한 작업
34 | - `utility`
35 | : 오래 걸리는 혹은 무거운 작업 (ex. 네트워킅 처리, 파일 불러오기)
36 | - `background`
37 | : 사용자에게 당장은 필요없는 작업 (ex. 위치 업데이트, 영상 다운로드)
38 |
39 | ### Custom Queue
40 | - 개발자가 직접 생성해서 사용하는 queue
41 | - `ConcurrentQueue`, `SerialQueue`로 생성 가능
42 |
43 |
44 | ## 예제 코드
45 | - Network 작업을 통해 데이터를 받아와 화면에 보여주고자 할 때
46 | ```swift
47 | DispatchQueue.global().async {
48 | // 1) some networking code...
49 |
50 | DispatchQueue.main.async {
51 | // 2) some UI update code..
52 | }
53 | }
54 | ```
55 | 1) 데이터 통신을 통해 데이터를 받아오는 작업을 global queue 에서 해줍니다.
56 | `e.g.` 데이터로부터 이미지를 받아오거나 API 를 통해 특정 쿼리에 대한 정보를 받아오는 작업
57 |
58 | 2) 받아온 데이터를 사용자가 보는 화면(UI)에 보여줍니다.
59 | `e.g.` imageView에 다운 받은 이미지를 대입, tableView를 갱신하여 가져온 데이터를 바탕으로 UI에 반영
60 |
61 | ### global().sync
62 | ```swift
63 | DispatchQueue.global().sync {
64 | for i in 1..<6 {
65 | print(i)
66 | }
67 | print("---global sync done---")
68 | }
69 | for i in 10..<16 {
70 | print(i)
71 | }
72 | ```
73 |
74 | > 실행 결과
75 |
76 | ```
77 | 1
78 | 2
79 | 3
80 | 4
81 | 5
82 | ---global sync done---
83 | 10
84 | 11
85 | 12
86 | 13
87 | 14
88 | 15
89 | ```
90 |
91 | > 설명
92 |
93 | - global queue(main 이외의 다른 큐)에 sync(동기)로 작업을 할당하도록 생성하였다.
94 | - 그러므로 `global().sync` 블록 안에 있는 작업이 다 끝나야 다른 작업을 실행한다.
95 |
96 | ### Custom Serial Queue
97 | ```swift
98 | let customQueue = DispatchQueue(label: "custom") // serial한 customQueue 생성
99 | customQueue.sync {
100 | for i in 1..<6 {
101 | print(i)
102 | }
103 | print("---custom serial done---")
104 | }
105 | for i in 10..<16 {
106 | print(i)
107 | }
108 | ```
109 | > 실행결과
110 | - global sync 와 동일
111 |
112 | > 설명
113 | - `Custom Queue`의 default는 `serial` 이다.
114 |
115 | ### global().async
116 | ```swift
117 | DispatchQueue.global().async { // A
118 | for i in 1..<6 {
119 | print(i)
120 | }
121 | print("---global async 1 done---")
122 | }
123 |
124 | DispatchQueue.global().async { // B
125 | for i in 10..<16 {
126 | print(i)
127 | }
128 | print("---global async 2 done---")
129 | }
130 |
131 | for i in 100..<106 { // C
132 | print(i)
133 | }
134 | ```
135 |
136 | > 실행결과
137 |
138 | ```
139 | 1️⃣ 1
140 | 3️⃣ 100
141 | 2️⃣ 10
142 | 2️⃣ 11
143 | 3️⃣ 101
144 | 1️⃣ 2
145 | 3️⃣ 102
146 | 1️⃣ 3
147 | 3️⃣ 103
148 | 1️⃣ 4
149 | 3️⃣ 104
150 | 1️⃣ 5
151 | 3️⃣ 105
152 | 2️⃣ 12
153 | 2️⃣ 13
154 | ---global async 1 done---
155 | 2️⃣ 14
156 | 2️⃣ 15
157 | ---global async 2 done---
158 | ```
159 |
160 | > 설명
161 |
162 | - 실행할 때 마다 결과는 다르게 나온다.
163 | - global에서 async(비동기)로 작업을 할당하도록 생성하였다.
164 | - C가 실행중일 때, 두개의 global에 async하게 할당된 작업은 concurrent(동시에)하게 작업이 실행되므로 A, B, C가 랜덤하게 출력된다.
165 |
166 | ### Serial Async
167 | ```swift
168 | let customQueue = DispatchQueue(label: "custom") // serial한 customQueue 생성
169 |
170 | customQueue.async { // A
171 | for i in 1..<6 {
172 | print("1️⃣", i)
173 | }
174 | print("---global async 1 done---")
175 | }
176 |
177 | customQueue.async { // B
178 | for i in 10..<16 {
179 | print("2️⃣", i)
180 | }
181 | print("---global async 2 done---")
182 | }
183 |
184 | for i in 100..<106 { // C
185 | print("3️⃣", i)
186 | }
187 | ```
188 |
189 | > 실행결과
190 | ```
191 | 1️⃣ 1
192 | 3️⃣ 100
193 | 3️⃣ 101
194 | 3️⃣ 102
195 | 3️⃣ 103
196 | 3️⃣ 104
197 | 3️⃣ 105
198 | 1️⃣ 2
199 | 1️⃣ 3
200 | 1️⃣ 4
201 | 1️⃣ 5
202 | ---global async 1 done---
203 | 2️⃣ 10
204 | 2️⃣ 11
205 | 2️⃣ 12
206 | 2️⃣ 13
207 | 2️⃣ 14
208 | 2️⃣ 15
209 | ---global async 2 done---
210 | ```
211 |
212 | > 설명
213 | - async(비동기)로 처리하되 serial(직렬)하게 A, B를 실행한다.
214 | - 따라서 A가 끝나야 B가 실행이 가능하다.
215 | - 따라서 A, C 혹은 B, C는 섞여 나올 수 있어도 A, B는 섞여 나오지 않는다.
--------------------------------------------------------------------------------
/Swift/docs_translate_types.md:
--------------------------------------------------------------------------------
1 | # Types
2 |
3 | > Swift 공식 문서 번역:
4 | > The Swift Prgramming Language Swift 5.7 [원문보기](https://docs.swift.org/swift-book/ReferenceManual/Types.html)
5 |
6 | ---
7 |
8 | ## Index
9 | - Type Annotation
10 | - Type Identifier
11 |
12 | ---
13 |
14 | Swift에는 두 종류의 타입이 있다.
15 | `named type`은 정의할 때 특정 이름을 지어줄 수 있는 타입이다.
16 | `named type`은 `classes, structures, enumerations, protocols`를 포함한다.
17 |
18 |
19 |
20 | 예를 들어,
21 | 사용자 정의 클래스 (`user-defined class`) `MyClass` 의 인스턴스는 `MyClass` 타입을 가진다.
22 |
23 | 게다가 `user-defined named type`은 Swift standard libarary는 흔하게 사용하는 named type들을 정의한다.
24 | 예를 들면 `arrays, dictionaries, optional values` 를 나타내는 것을 포함한다.
25 |
26 |
27 |
28 | Data type들은 보통 다른 언어들에서 기본(basic, primitive)으로 여겨진다 - 숫자, 문자, 문자열들은 나타내는 타입들과 같은 - 이것들은 사실 `named type` 으로, Swift standard library 에 구조체(structure)로 정의 되어있고 적용되어 있다.
29 | 그들은 `named type`이기 때문에, `extension`을 정의하고 활용하여 당신의 프로그램에서 필요에 맞추어 그들의 행동을 확장 할 수도 있다.
30 |
31 | `compound type`은 Swift 언어 그 자체로 정의된 이름이 없는 타입이다.
32 | `compound type`은 두가지가 있다. 함수타입(function type)과 튜플타입(tuple type).
33 | `compound type`은 `named type`과 다른 `compound type`을 포함할 수 있다.
34 |
35 |
36 |
37 | 예를 들어,
38 | tuple type `(Int, (Int, Int))` 는 두 요소를 포함하고 있다.
39 | 처음은 `Int` 라는 named type, 두번째는 다른 compound type인 `(Int, Int)` 이다.
40 |
41 |
42 |
43 | 당신은 named type이나 compound type 주위를 괄호로 묶어줄 수 있다.
44 | 하지만, 타입 주변에 괄호를 추가한다고 어떠한 효과를 가지는 것은 아니다.
45 | 예를 들어 `(Int)` 는 `Int` 와 동일하다.
46 |
47 | 이 챕터에서는 Swift 언어 자기 자신에서 타입이 어떻게 정의되어있는지, Swift의 타입추론(type inference behavior)이 어떻게 묘사되고 있는지 알아볼 것이다.
48 |
49 |
50 | ---
51 |
52 | ## Type Annotation
53 |
54 | type annotation은 명확하게 변수(`variable`) 혹은 표현식(`expression`)을 지정한다.
55 | type annotation은 콜론 (`:`) 으로 시작하며 type으로 끝난다.
56 | 예시는 아래와 같다.
57 |
58 | ```swift
59 | let someTuple: (Double, Double) = (3.14159, 2.71828)
60 | func someFunction(a: Int) { /* ... */ }
61 | ```
62 |
63 | 첫번째 에시에서 보면, `someTuple` 이라는 표현식은 `(Double, Double)` 튜플 타입임을 가짐을 지정한다.
64 | 두번째 예시를 보면, `someFunction` 이라는 함수의 파라미터는 `Int` 타입을 가짐을 지정한다.
65 |
66 |
67 |
68 | type annotation은 타입 앞에 타입의 속성(type attributes)에 대한 리스트도 옵션으로 포함할 수 있다.
69 | ```
70 | ex)
71 | @escaping, @autoclosure
72 | ```
73 | ---
74 |
75 | ## Type Identifier
76 | `type identifier`는 named type 또는 이름이 지어진 `type alias` 또는 `compound type`을 의미한다.
77 |
78 |
79 |
80 | 대부분의 경우 type identifier는 identifier로써 같은 이름을 가진 `named type`을 의미한다.
81 | 예를 들어 `Int`는 `Int` 라는 named type을 직접적으로 나타내는 type identifier 이고, `Dictionary`는 `Dictionary` 라는 named type을 나타낸다.
82 |
83 |
84 |
85 | type identifer가 나타내는 타입과 이름이 같지 않은 경우는 두 가지가 있다.
86 | 첫번째 경우는, type identifer가 named 혹은 compound type의 type alias를 나타내는 경우이다.
87 | 그 대신에 아래의 예시 처럼 type annotation에 사용된 `Point` 는 `(Int, Int)` 튜플 타입을 나타낸다.
88 |
89 | ```swift
90 | typealias Point = (Int, Int)
91 | let origin: Point = (0, 0)
92 | ```
93 |
94 |
95 |
96 | 두번째 경우는, type identifier가 named type들을 다른 모듈 혹은 중첩된 다른 타입을 나타내기 위해 dot(`.`) syntax 을 사용하는 경우이다.
97 | 예를 들어, 아래 코드의 type identifier는 `ExampleModule`이라는 모듈 안에 정의된 `MyType`이라는 named type을 의미한다.
98 | ```swift
99 | var someValue: ExampleModule.MyType
100 | ```
101 |
102 | ---
103 |
104 | ## Tuple Type
105 | `Tuple`타입 는 쉼표로 구분된 형식 목록으로 괄호로 묶어 표기한다.
106 | 함수의 반환 타입으로써의 `Tuple`타입을 사용하여 다수의 값을 하나의 tuple에 담아 반환할 수 있다.
107 | 또한 tuple의 각각의 원소들을 나타내는 이름을 부여할 수 있다.
108 | 각가의 원소의 이름은 식별자 다음에 코론(`:`) 뒤에 오도록 구성한다.
109 | 이 기능들에 대한 예제를 확인하고 싶다면 [다수의 반환값을 갖는 함수들](https://docs.swift.org/swift-book/LanguageGuide/Functions.html#ID164) 를 확인하라.
110 |
111 | `Tuple` 타입의 원소들이 이름을 가진 경우, 이름은 타입의 부분이다.
112 | ```swift
113 | var someTuple = (top: 10, bottom: 12) // someTuple is of type (top: Int, bottom: Int)
114 | someTuple = (top: 4, bottom: 42) // OK: names match
115 | someTuple = (9, 99) // OK: names are inferred
116 | someTuple = (left: 5, right: 5) // Error: names don't match
117 | ```
118 |
119 | 모든 `Tuple`타입은 `Void`를 제외한 두개 혹은 그 이상의 타입을 포함할 수 있다.
120 | (`Void`는 비어있는 `Tuple`타입의 type alias 이다. `()`)
121 |
122 | ---
123 |
124 | ## Function Type
125 |
126 |
127 | ---
128 |
129 | ## Array Type
130 |
131 |
132 | ---
133 |
134 | ## Dictionary Type
135 |
136 |
137 | ---
138 |
139 | ## Optional Type
140 |
141 |
142 | ---
143 |
144 | ## Implicitly Unwrapped Optional Type
145 |
146 |
147 | ---
148 |
149 | ## Protocol Composition Type
150 |
151 |
152 | ---
153 |
154 | ## Opaque Type
155 |
156 |
157 | ---
158 |
159 | ## Metatype Type
160 |
161 |
162 | ---
163 |
164 | ## Any Type
165 |
166 |
167 | ---
168 |
169 | ## Self Type
170 |
171 |
172 | ---
173 |
174 | ## Type Inheritance Clause
175 |
176 |
177 | ---
178 |
179 | ## Type Inference
180 |
--------------------------------------------------------------------------------
/iOS/dispatchGroup.md:
--------------------------------------------------------------------------------
1 | # DispatchGroup
2 |
3 | > DispatchGroup `Class`
4 | > : A group of tasks that you monitor as a single unit.
5 |
6 | ```swift
7 | class DispatchGroup: DispatchObject
8 | ```
9 |
10 | - 여러 테스크들(tasks)을 그룹으로 묶어서 동시에 수행하고자할 때 사용한다.
11 | - 하나의 `task`를 `{ }` 를 이용해 block으로 정의하고, 그 block을 그룹에 참가(enter)시킨다.
12 | - 테스크들의 세트/묶음을 `group` 이라고 표현한다.
13 |
14 | - 여러개의 `work item`들을 하나의 그룹으로 묶고, 같은 큐(queue) 혹은 다른 큐들에 비동기적(asyncronous)으로 수행하도록 스케쥴할 수 있다.
15 | - `class` DispatchWorkItem : 이 클래스는 task의 캡슐화를 하는데 이용합니다. 즉, task를 세분화하여 만들어두고 DispatchQueue로 실행을 시키게 된다.
16 | - 이 클래스의 인스턴스를 work item이라고 부른다.
17 |
18 | - 만약 그룹안의 모든 work item들이 수행을 끝마치면 그룹은 completion handler를 수행한다.
19 | - 혹은 그룹안에 있는 모든 task들이 수행을 끝마칠때까지 동기적으로 기다려줄 수도 있다.
20 |
21 | ----
22 |
23 | ## 대표적인 메서드
24 |
25 | ### [enter()](https://developer.apple.com/documentation/dispatch/dispatchgroup/1452803-enter)
26 |
27 | > 해당 그룹에 block이 들어가졌음을(entered) 명시적으로 나타낸다.
28 |
29 | - 파라미터로 group을 받는데, dispatch group을 업데이트하기 위해 사용되며, `NULL`이 될 수 없다.
30 |
31 | ```
32 | Dispatch Group 에 task를 추가하여(enter) +1 시킨다.
33 | ```
34 |
35 |
36 |
37 | ### [leave](https://developer.apple.com/documentation/dispatch/dispatchgroup/1452872-leave)
38 |
39 | > 그룹 안에서 수행이 끝난 블록을 명시적으로 나타낸다. (수행이 끝났으므로 해당 그룹을 떠남을 의미)
40 |
41 | - `enter`와 마찬가지로, `NULL`이 될 수 없다.
42 |
43 | ```
44 | Dispatch Group 에 task를 빼내어(leave) -1 시킨다.
45 | ```
46 |
47 |
48 |
49 | ### [wait()](https://developer.apple.com/documentation/dispatch/dispatchgroup/2016090-wait)
50 |
51 | > 이전에 제출된 work가 끝나기를 동기적으로 기다린다.
52 |
53 |
54 |
55 | ### [notify(qos: flags: queue: excute:)](https://developer.apple.com/documentation/dispatch/dispatchgroup/2016066-notify)
56 |
57 | > 현재 그룹이 모든 테스크 수행을 끝내면 execute 인자의 clossure에 작성되어 있는 툭정 행동을 수행할 것을 알린다.
58 |
59 | ```
60 | Dispatch Group 에 들어있는 task가 0이 되었을 때 실행되는 함수이다.
61 | ```
62 |
63 | - qos : 해당 work가 수행될 서비스의 퀄리티를 정한다.(quality of service)
64 | - flags : work가 어떻게 수행될 것인지에 대한 옵션을 지정해줄 수 있다.
65 | - 옵션종류 : assignCurrentContext, barrier, detached, enforceQoS, inheritQoS, noQoS
66 | - 옵션별 상세 설명은 이 [링크](https://developer.apple.com/documentation/dispatch/dispatchworkitemflags)를 참고해보자.
67 | - queue : 그룹의 수행이 모두 완료되었을 때에 알림받을 큐
68 | - work : 그룹의 수행이 완료되었을 때 dispatch queue에서 수행될 work
69 |
70 | ----
71 |
72 | ## 사용방법
73 | ### 설명
74 | - main은 UI를 그리는 작업을 해야하기 때문에 `main.sync`를 제외한 다른 비동기적인 혹은 백그라운드에서 의 작업 `dispatchQueue` 블록에 작성한다.
75 | - `someGroup.enter()`를 하고 바로 아래의 함수를 행 그룹에 추가한다. (3까지 반복)
76 | - 그렇게 `DispatchQueue.main.async { }` 블록의 끝에 도달한 후 group에 들어온 task를 순서대로 실행한다. (task1, task2, task3 순서)
77 | - task1 수행이 완료되고 `compeltion()`을 통해 넘어온 문자열을 `result`라는 이름으로 받아 해당 블록을 실행한다.
78 | - 그룹 안의 모든 task의 실행이 끝난 후 group은 `notify()`를 통해 main에 모든작업이 끝났음을 알리며, 모든작업이 끝난 후 해주고 싶은 추가적인 작업을 해준다. (아래 코드이 경우는 print문 출력)
79 |
80 |
81 | ### 예제 코드
82 | ```swift
83 |
84 | func someWork(number: Int, completion: @escaping (String) -> Void) {
85 | completion("현재 \(number) 작업을 수행합니다.")
86 | }
87 |
88 | func someGroupTasks() {
89 | DispatchQueue.main.async { // background에서 비동기적으로 작업을 수행하기 위해 async로 수행
90 | let someGroup = DispatchGroup() // group 생성
91 |
92 | print("---- task1 enter ----")
93 | someGroup.enter() // group에 task + 1
94 |
95 | // 현재 그룹에 들어와 있는 상태로 실행할 작업을 작성
96 | someWork(number: 1) { result in
97 | print(result)
98 | someGroup.leave() // group에 task - 1
99 | print("---- task1 leave ----")
100 | }
101 |
102 | print("---- task2 enter ----")
103 | someGroup.enter()
104 | someWork(number: 2) { result in
105 | print(result)
106 | someGroup.leave()
107 | print("---- task2 leave ----")
108 | }
109 |
110 | print("---- task3 enter ----")
111 | someGroup.enter()
112 | someWork(number: 3) { result in
113 | print(result)
114 | someGroup.leave()
115 | print("---- task3 leave ----")
116 | }
117 |
118 | someGroup.notify(queue: .main) {
119 | print("Group의 모든 작업이 끝났습니다.")
120 | }
121 | }
122 | }
123 |
124 | someGroupTasks()
125 |
126 | ```
127 |
128 | ### 실행결과
129 | ```
130 | ---- task1 enter ----
131 | 현재 1 작업을 수행합니다.
132 | ---- task1 leave ----
133 | ---- task2 enter ----
134 | 현재 2 작업을 수행합니다.
135 | ---- task2 leave ----
136 | ---- task3 enter ----
137 | 현재 3 작업을 수행합니다.
138 | ---- task3 leave ----
139 | Group의 모든 작업이 끝났습니다.
140 | ```
141 |
142 |
143 |
144 | ### 실제 사용 예시를 보고 싶다면? 아래의 블로그 글을 참고하세요!
145 | - [다수의 반복적인 네트워크 통신 처리를 완료한 후 화면을 갱신해주고 싶을 때](https://nareunhagae.tistory.com/65)
146 |
147 |
148 |
149 | ----
150 |
151 | 참고자료
152 | - [Apple Developer - DispatchGroup](https://developer.apple.com/documentation/dispatch/dispatchgroup)
153 |
--------------------------------------------------------------------------------
/iOS/universalLink.md:
--------------------------------------------------------------------------------
1 | # universal link
2 | > Seamlessly link to content in your app or on your website.
3 | > With universal links, you can always give users the most integrated mobile experience,
4 | > even when your app isn’t installed on their device.
5 |
6 |
7 |

8 |
9 |
10 | - iOS에서만 작동하는 딥링크의 한 종류로 웹에서 앱을 호출하는 기능이 필요할 때 사용한다.
11 | - 사용자가 유니버설 링크를 사용하면 링크 접근 시,
12 | - 앱이 설치되어있는 경우 앱으로 이동
13 | - 설치되어 있지 않으면 앱을 설치하도록 앱스토어로 이동
14 |
15 | (*deep link : 어플리케이션 내 특정 페이지에 도달할 수 있도록 하는 링크)
16 |
17 |
18 | ## 특징
19 | - url 형태로 동작(IP 및 HTTP에선 동작하지 않음)
20 | - iOS9 이상에서만 지원
21 |
22 | ## 사전작업
23 | - 웹 서버 작업
24 | - 웹서버 : `AASA(Apple-App-Site-Association)` 파일 추가
25 | - 앱 : `Associated Domains` 추가
26 |
27 | ## 사용방법
28 | - 소유한 도메인이 있어야 한다.
29 | - 도메인 정보가 포함된 JSON 파일인 `AASA` 파일을 웹서버에 업로드 한다.
30 | - 사용자가 앱 설치 시, 이 파일에 등록된 도메인으로 `AASA` 파일에 대한 요청을 보낸다.
31 | - 사용자가 이미 앱을 설치했다면, 앱을 바로 실행시켜주는 링크의 도메인에서 호스팅이 된다.
32 |
33 | ### 주의사항
34 | - redirection 이 없어야 함
35 | - HTTPS를 지원해야 함
36 | - 128KB 보다 작아야 함
37 |
38 | ----
39 |
40 | ## 웹서버 설정
41 | ### AASA 파일
42 | - 파일 이름: `apple-app-site-association` (확장자 없음)
43 | - 위치 : `well-known` 또는 `root` 에 추가
44 |
45 | > iOS 13 이후
46 | ```swift
47 | {
48 | applinks: {
49 | details: [
50 | {
51 | appIDs: ["."],
52 | components: [
53 | {
54 | /: “*”
55 | }
56 | ]
57 | }
58 | ]
59 | }
60 | }
61 | ```
62 |
63 | > iOS 12 이전
64 | ```swift
65 | {
66 | "applinks": {
67 | "apps": [],
68 | "details": [{
69 | "appID": ".",
70 | "paths": ["*"]
71 | },
72 | {
73 | "appID": ".",
74 | "paths": ["/files"]
75 | }]
76 | }
77 | }
78 | ```
79 |
80 | ### 구성 요소
81 | - `apps` : 유니버설 링크에서는 사용하지 않지만 빈 배열이라도 꼭 명시되어 있어야 한다.
82 | - `details` : 웹사이트에서 핸들링되는 앱 목록 (한 웹사이트에서 유니버셜 링크를 사용하는 여러 앱 연동 가능)
83 | - `appId` : 앱 식별값. 팀 아이디와 번들 아이디 사용
84 | (형식 : `.`)
85 |
86 | ### 팀아이디 확인 방법
87 | ```
88 | Apple Developer 사이트(https://developer.apple.com/) > Account > Certificates, IDs & Profiles > Identifiers
89 | ```
90 |
91 | ### 번들 아이디 확인 방법
92 | ```
93 | 연결할 앱 Xcode Project > Target > General > Identity
94 | ```
95 |
96 |
97 |

98 |
99 |
100 |
101 | - paths : 앱에서 지원하는 웹 사이트 경로
102 |
103 | ----
104 |
105 | ## 앱 설정
106 |
107 | - 앱에서 유니버셜 링크를 허용할 도메인을 추가해야 한다.
108 |
109 | - 아래의 위치에 추가
110 | ```
111 | Project > Target > Signing&Capabilities> Associated Domains에 Domains 추가
112 | ```
113 |
114 |
115 |
116 | - ex) applinks 형식
117 | ```
118 | //applinks:your_dynamic_links_domain
119 | applinks:example.com
120 | applinks:*.example.com
121 | ```
122 |
123 | ## 사용자가 앱 설치 시
124 | 등록된 도메인의 해당 URL의 `.well-known/apple-app-site-association` 경로에서 AASA 포맷을 확인하라고 파일에 대한 요청을 보냄
125 |
126 | (`entitlements`로 등록이 잘 되었는지 확인 가능)
127 |
128 | - 여기까지 설정하면 앱과 웹이 연결되어 서로의 정보를 알고 있는 상태이다.
129 | - 메모앱에서 설정한 도메인주소를 입력 후 클릭 시 연결한 앱이 실행되는지를 통해 테스트해볼 수 있다.
130 |
131 | ---
132 |
133 | ## 동적링크 URL handle 하기
134 | 유니버셜 링크로 들어오는 요청은
135 | SceneDelegate의 `scene(scene: continue userActivity:)` 메서드에서 전달받은 `NSUserActivity` 객체를 통해 처리할 수 있다.
136 |
137 | > iOS 13 이상
138 | ```swift
139 | func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
140 | guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
141 | let incomingURL = userActivity.webpageURL,
142 | let components = NSURLComponents(url: incomingURL, resolvingAgainstBaseURL: true),
143 | let path = components.path else {
144 | return
145 | }
146 |
147 | //도메인 주소의 쿼리값을 받음
148 | let params = components.queryItems ?? [URLQueryItem]()
149 | print("path = \(incomingURL)")
150 | print("params = \(params)")
151 | }
152 |
153 | func scene(_ scene: UIScene, openURLContexts URLContexts: Set) {
154 | for urlContext in URLContexts {
155 | let urlToOpen = urlContext.url
156 | // ...
157 | }
158 | }
159 | ```
160 |
161 | > iOS 9 이하
162 | ```swift
163 | - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *))restorationHandler {
164 | NSURL *url = userActivity.webpageURL;
165 | // ...
166 | }
167 |
168 | - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options {
169 | // ...
170 | }
171 | ```
172 |
173 |
174 |
175 |
176 | 참고 자료
177 | - [Apple Developer - Universal Link](https://developer.apple.com/ios/universal-links/)
178 | - [유니버설링크와 딥링크 차이점](https://www.adjust.com/ko/blog/universal-links-vs-deep-links/)
179 |
--------------------------------------------------------------------------------
/Swift/about_string.md:
--------------------------------------------------------------------------------
1 | # 문자열(String), 문자(Character), String.Index 타입에 대하여
2 |
3 | ## 문자열이란
4 | - 문자(Character) 값들의 컬렉션이다.
5 | - Unicode-compliant 하고 빠르다.
6 | - `+` 연산자를 통해 두 문자열을 결합할 수 있다.
7 | - Foundation의 `NSStirng` 클래스와 연결된 타입이기에 Foundation을 import 하면 사용이 가능하다.
8 | - (필요시 다음 링크를 참고해보자 : [Bridging Between String and NSString](https://developer.apple.com/documentation/swift/string#2919514))
9 |
10 | ```
11 | * 리터럴이란?
12 | : 소스코드의 고정된 값을 의미하는 용어이다. 데이터(값) 자체를 뜻한다.
13 | ```
14 |
15 | - string literal은 쌍따옴표(double quotation)으로 둘러 쌓인 `Character`들의 `sequence` 이다.
16 |
17 |
18 |
19 | ## 문자열은 값타입이다. (구조체나 열거형과 마찬가지로)
20 | - 새로운 문자열을 만들면 해당 문자열 값은 함수나 메서드에 전달될 때 복사되어 전달된다.
21 | - 값이 복사되어 전달되기 때문에 해당 문자열이 어디에 전달되거나 원본값을 직접적으로 변경하지 않는 이상 본래의 값을 유지할 수 있다.
22 | - 각각의 `Character`에 `for - in` 문을 사용하여 접근할 수 있다.
23 |
24 | - 스위프트의 Character 타입은 하나의 `Extended Grapheme Cluster`를 나타낸다.
25 | - `Extended Grapheme Cluster`는 하나 혹은 다수의 `Unicode scalar`들의 `sequence`가 결합되어 사람이 읽을 수 있는 형태의 하나의 `Character` 로 제공한다.
26 |
27 | ```
28 | * Grapheme Cluster
29 | : 화면에 보이는 문자의 단위.
30 |
31 | * Extended Grapheme Cluster
32 | : 사람이 읽을 수 있는 하나의 글자 단위.
33 | 언어마다 하나의 글자가 여러 개의 unicode scalar를 가질 수 있고, 각 글자마자 메모리에 적재되는 크기가 다를 수 있다.
34 | ```
35 |
36 |
37 |
38 | ## 문자열은 왜 index값을 통해 바로 접근이 불가능할까?
39 |
40 | ```swift
41 | // ex)
42 | let someString: String = "hello"
43 | someString[2] // error
44 | ```
45 |
46 | - C, C++ 과 같은 언어의 경우, 문자열 타입은 정수의 index값을 통해 문자에 접근이 가능하며, 각각의 index마다 동일한 크기의 데이터 값을 가지고 있다.
47 | - 반면 Swift의 `String`은 유니코드 표준을 준수하고 있다.
48 | - `Character` 타입의 데이터는 위에서 언급한대로 `extended grapheme clusters` 로 표현되어있다.
49 |
50 |
51 |
52 | ### 공식문서의 예시를 참고하면
53 |
54 | ```swift
55 | /* 예시 1 */
56 | let eAcute: Character = "\u{E9}" // é
57 | let combinedEAcute: Character = "\u{65}\u{301}" // e followed by
58 | // eAcute is é, combinedEAcute is é
59 | ```
60 | - combinedEAcute는 두개의 Unicode Scalar "\u{65}\u{301}" 가 Unicode-aware text-rendering system 에 의해 e에 ́를 더하여 é 라는 하나의 Character가 된다.
61 |
62 | ```swift
63 | /* 예시 2 */
64 | let precomposed: Character = "\u{D55C}" // 한
65 | let decomposed: Character = "\u{1112}\u{1161}\u{11AB}" // ᄒ, ᅡ, ᆫ
66 | // precomposed is 한, decomposed is 한
67 | ```
68 |
69 |
70 | - 한글도 마찬가지다.
71 | - 유니코드 상으로 '한'을 나타내는 "\u{D55C}" 와 ㅎ, ㅏ, ㄴ 을 합친 "\u{1112}\u{1161}\u{11AB}" 는 모두 "한"이라는 하나의 Character를 나타낸다.
72 | ```
73 | "한글최고" 라는 문자열에서 "한" 이라는 Character에 대해 인덱스로 접근하고자 한다면 Swift 입장에서는 굉장히 모호할 것이다.
74 | ```
75 |
76 | - 위의 예시1, 예시2와 같이 사람이 읽을 수 있는 하나의 글자를 표현하는데 여러개의 `unicode scalar` 값이 필요로 한다.
77 | - 하지만 `unicode scalar`외 갯수와 상관 없이 하나의 `Character`를 나타낸다.
78 |
79 | 위의 이유로 `someeString[2]` 와 같은 식으로 접근을 하려고 한다면, Swift는 정확히 어떤 `Character`를 원하고 어떻게 접근해야하는지 모른다.
80 | (정수의 index값을 통해 메모리를 배열처럼 보고 index 번째를 찾는 것이기 때문이다.)
81 |
82 |
83 |
84 | ## 만약 index를 통해 Character에 접근하고자 한다면?
85 | ```swift
86 | // ex) someString의 4번째 Character 'o' 에 접근하고자 할 때
87 |
88 | let someString: String = "hello, world!"
89 | let index: String.Index = someString.index(someString.startIndex, offsetBy: 4)
90 | someString[index] // 'o'
91 | ```
92 | - Swift에서는 index를 정수(Int) 타입으로 받지 않고, `String.Index` 타입으로 받는다.
93 | - 관련 프로퍼티/메서드들을 간단히 소개하면 아래와 같다.
94 | - `startIndex` : the index of the first character. (처음 문자의 인덱스)
95 | - `endIndex` : the index 'after' the last character. (마지막 문자 다음 인덱스)
96 | - `index(after: String.Index)` : index of the character directly 'after' the given index. (주어진 인덱스 바로 전 문자)
97 | - `index(after: String.Index)` : index of the character directly 'before' the given index. (주어진 인덱스 바로 다음 문자)
98 | - `index(String.Index, offsetBy: String.IndexDistance)`
99 | - offsetBy value can be positive or negative and starts from the given index.
100 | - (offsetBy 인자는 양수, 음수 둘다 가능하며 주어진 인덱스로부터 시작하여 얼마나 떨어진 곳의 문자인지를 나타낸다.)
101 |
102 | ```swift
103 | let someString: String = "hello, world!"
104 | let index: String.Index = someString.index(someString.startIndex, offsetBy: 4)
105 | someString[index] // 'o'
106 |
107 | someString.startIndex // 문자열의 처음 문자의 index
108 | someString.endIndex // 문자열의 마지막 문자의 index가 아닌 문자열의 끝을 가리키기에 그대로 사용 시 fatal error 발생 (Fatal error: String index is out of bounds)
109 | someString.index(before: someString.endIndex) // 문자열의 마지막에 접근하고 싶을 때는 아래와 같이 접근 (혹은 offsetBy를 음수로 지정)
110 |
111 | someString[someString.index(before: someString.endIndex)]
112 | someString.index(someString.startIndex, offsetBy: n) // 문자열의 n번째 문자
113 |
114 | ```
115 |
116 |
117 | ## 정리
118 | ```
119 | * String.Index 란?
120 | : 문자열 안의 문자 혹은 코드 유닛의 위치.
121 | Swift 에서 문자열의 index를 표현하기 위해 사용되는 특수한 타입이다.
122 | ```
123 |
124 | ### String을 Int타입의 index로 접근할 수 없는 이유, 대신 String.Index 타입이 필요한 이유는
125 | - 정수(Int)값을 통해 `String`의 `Charater`에 접근하면 편하겠지만,
126 | - 위에서 언급했듯, Swift의 Charater 타입은 `Extended Grapheme Cluster` 로 표현되어있다.
127 | - Swift는 한 글자가 여러 개의 Unicode Scalar값을 가질 수 있기 때문에, 각각의 Character는 메모리를 점유하는 크기가 다르다.
128 | - 그 말은 특정 Charater의 index를 계산하기 모호하다는 뜻이다.
129 | - 때문에 C, C++ 과 같이 각각의 Character들이 같은 크기의 메모리를 점유하는 것과 달리 index값을 통해 String의 index번째 Character에 접근할 수 없다.
130 | - 따라서 애플이 만든 특수한 타입 `String.Index`를 통해 String의 Character에 접근하거나 `for - in` 문을 통해 접근할 수 있다.
131 |
--------------------------------------------------------------------------------
/iOS/urlSession.md:
--------------------------------------------------------------------------------
1 | # URLSession
2 |
3 | # 개요
4 |
5 | 앱을 만들다보면 웹서버와 통신이 필요한 경우가 많다.
6 |
7 | 웹 서버 통신은 크게 2가지로 나눌 수 있는데,
8 |
9 | - HTTP 통신: URL 기반으로 클라이언트에게 요청을 보내고, 서버로부터 응답을 받는 형태
10 | - 웹소켓 통신: 클라이언트와 서버가 특정 port를 통해 연결되어 있는 양방향 형태. 실시간 통신에 주로 사용.
11 |
12 | → 이 중에서 HTTP/HTTPS 통신 방법을 URLSession으로 알아볼 것이다.
13 |
14 | # Declaration
15 |
16 | [공식문서](https://developer.apple.com/documentation/foundation/urlsession)
17 |
18 | > `Class`
19 | > An object that coordinates a group of related, network data transfer tasks.
20 |
21 |
22 | - URLSession은 네트워크 데이터를 전송하는 일을 한다.
23 | - URLSession은 URL의 endpoint로부터 `데이터를 다운로드/업로드하는 API`를 제공하는 '클래스'다.
24 | - 앱이 `not running`, `suspend` 상태에서 background download를 수행할 수 있도록 하기 위해 이 API를 사용할 수도 있다.
25 |
26 | [URLSessionDelagate](https://developer.apple.com/documentation/foundation/urlsessiondelegate)와 [URLSessionTaskDelegate](https://developer.apple.com/documentation/foundation/urlsessiontaskdelegate)와 관련된 것을 사용하여 `authentication`을 지원하거나 `redirection`이나 `task completion` 같은 이벤트를 받을 수 있다.
27 |
28 | # Thread Safety
29 |
30 | [Thread-Safe란?](https://github.com/keenkim1202/KEENs_TIL/blob/main/CS/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C/ThreadSafe.md)
31 |
32 | - URL session API 는 `thread-safe` 하다.
33 | - session과 task들을 어느 thread context에서든 자유롭게 생성할 수 있다.
34 |
35 | → 자체적으로 비동기적으로 작동하게 구현되어있으므로, 따로 비동기 처리를 할 필요가 없다.
36 |
37 | 대신 `completionHandler`를 작성할 때, UI관련 작업을 수행한다면 반드시 `Main thread`에서 작업해주어야 한다.
38 |
39 | ```
40 | ex.
41 | 하나의 탭 또는 ㅊ아마다 하나의 세션을 만들어 볼 수 있고,
42 | 한 세션은 상호작용하는 데 사용, 다른 하나는 백그라운드에서 다운로드하는데 사용할 수 있다.
43 | 다운로드가 완료된 후, UI를 업데이트 하고 싶다면 main thread에서 작업한다.
44 | ```
45 |
46 | # URLSession Configuratoin
47 |
48 | > URLSession은 configuration이라는 객체를 가지고 있다.
49 |
50 | (업로드를 할 지, 다운로드를 할 지 등의 행동과 규칙을 정의하는 객체.)
51 |
52 | - URLSession 객체를 초기화 하기 전에 가장 먼저 작업해야할 첫 단계이며,
53 | 타임아웃값, 캐싱 정책, HTTP header와 같은 값들로 구성된다.
54 |
55 | ### 종류
56 |
57 | - `.default`
58 | - `.epemeral`
59 | - `.background`
60 |
61 | configuration의 복사본으로 session을 세팅한다.
62 |
63 | 따라서 session이 생성되고 난 이후에 configuration 객체가 변동되어도 session은 변하지 않는다.
64 |
65 | → configuration을 수정하고 싶으면, 새로운 configuration으로 새로운 session을 만들어야 함!
66 |
67 | # URLSession 종류
68 |
69 | URLSession의 종류는 configuration 객체에 의해 결저된다.
70 |
71 | ### 공유 세션(singleton)
72 |
73 | ```swift
74 | let sharedSession = URLSesssion.shared()
75 | ```
76 |
77 | - 기본 요청을 위한 세션
78 | - configuration 객체 없음
79 | - 사용자 정의 불가
80 |
81 | ### 기본 세션
82 |
83 | ```swift
84 | let defaultSession = URLSession(configuration: .default)
85 | ```
86 |
87 | - disk에 기록함( 캐시, 쿠키, authentication)
88 | - delegate 지정 가능 (순차적 데이터 처리)
89 |
90 | ### 임의 세션
91 |
92 | ```swift
93 | let ephemeralSession = URLSession(configuration: .ephemeral)
94 | ```
95 |
96 | - disk에 데이터를 쓰지 않음
97 | - 메모리에 올려서 세션을 연결
98 | - 세션 만료 시 데이터가 사라진다. → 비공개 세션
99 |
100 | ### 백그라운드 세션
101 |
102 | ```swift
103 | let brackgroundSession = URLSession(configuration: .background)
104 | ```
105 |
106 | - 백그라운드에서 업로드, 다운로드 가능
107 | - 별도의 프로세스가 모든 데이터 전송을 처리 → suspend, not running 상태에서도 수행
108 | - [참고 링크 - raywenderlich urlsession tutorial]((https://www.raywenderlich.com/3244963-urlsession-tutorial-getting-started))
109 |
110 |
111 | # URLSession Task
112 |
113 | 각 세션 내에는 task를 추가할 수 있다.
114 |
115 | 각 task는 특정 URL에 대한 요청을 의미하며, HTTP redirection이 될 수 있다.
116 |
117 | URL 주소만으로 요청할 때는 URL 객체를 이용하고,
118 |
119 | 주소와 HTTP 메소드, Body 까지 설정해야 할 때는 URLRequest 객체를 이용하면 된다.
120 |
121 | ```swift
122 | // URL 객체
123 | let url = URL(string: "https://api.address.com")
124 |
125 | // URRequest 객체
126 | let request: URLRequest = URLReuest(url: url)
127 | request.httpMethod = "GET"
128 | request.addValue("application/json", forHTTPHeaderField: "Accept")
129 | ```
130 |
131 | → URLRequest는 캐싱 정책, HTTP method, HTTP body 등을 설정할 수 있다.
132 |
133 | ## 종류
134 |
135 |
136 |
137 | `URLSessionDataTask (HTTP GET)`
138 |
139 | - reponse data를 받아서 Data 형태의 객체를 받아오는 작업
140 | - JSON, XML, HTML
141 |
142 | `URLSessionUploadTask (HTTP POST/PUT)`
143 |
144 | - Data 객체 또는 파일 형태의 데이터를 서버로 업로드 하는 작업 (백그라운드 ok)
145 |
146 | `URLSessionDownloadTask`
147 |
148 | - 파일 형태의 데이터를 다운로드 하는 작업 (백그라운드 Ok)
149 | - 일시정지, 재개, 취소 가능
150 |
151 | ```
152 | + 웹소켓 작업은 URLSession이 아니라 WebSocket 프로토콜을 사용
153 | ```
154 |
155 | # Task 추가
156 | ```swift
157 | session.dataTasks(with:)
158 | ```
159 |
160 | ## 사용 예시
161 | ```swift
162 | extension URLSession {
163 | func request(_ request: URLRequest, completion: @escaping(T?, APIError?) -> Void) {
164 | URLSession.shared.dataTask(with: request) { data, response, error in
165 | DispatchQueue.main.async {
166 | guard error == nil else {
167 | completion(nil, .failed)
168 | return
169 | }
170 |
171 | guard let data = data else {
172 | completion(nil, .noData)
173 | return
174 | }
175 |
176 | guard let response = response as? HTTPURLResponse else {
177 | completion(nil, .invalidResponse)
178 | return
179 | }
180 |
181 | guard response.statusCode == 200 else {
182 | completion(nil, .failed)
183 | return
184 | }
185 |
186 | do {
187 | let decoder = JSONDecoder()
188 | let userData = try decoder.decode(T.self, from: data)
189 | completion(userData, nil)
190 | } catch {
191 | completion(nil, .invalideData)
192 | }
193 | }
194 |
195 | }.resume()
196 | }
197 | }
198 | ```
199 | → [코드 전문 보러기기](https://github.com/keenkim1202/SproutFARM/blob/main/SproutFARM/Sources/Extension/URLSession%2B%2BExtension.swift)
200 |
--------------------------------------------------------------------------------
/iOS/present_dismiss_transition_smooth.md:
--------------------------------------------------------------------------------
1 | # present/dismiss 를 push/pop 처럼을 좀 더 스무스 하게 하고 싶을 때
2 |
3 | 아래와 같이 `UIViewControllerAnimatedTransitioning`를 상속 받는 두개의 클래스를 생성하여 사용한다.
4 |
5 | ## PresentTransition
6 | ```swift
7 | import UIKit
8 |
9 | class PresentTransition: NSObject, UIViewControllerAnimatedTransitioning {
10 | var animator: UIViewImplicitlyAnimating?
11 |
12 | func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
13 | return 0.3
14 | }
15 |
16 | func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
17 | let animator = self.interruptibleAnimator(using: transitionContext)
18 | animator.startAnimation()
19 | }
20 |
21 | func interruptibleAnimator(using transitionContext: UIViewControllerContextTransitioning) -> UIViewImplicitlyAnimating {
22 | if self.animator != nil {
23 | return self.animator!
24 | }
25 |
26 | let container = transitionContext.containerView
27 | let fromVC = transitionContext.viewController(forKey: .from)!
28 | let fromViewInitFrame = transitionContext.initialFrame(for: fromVC)
29 | var fromViewFinalFrame = fromViewInitFrame
30 | fromViewFinalFrame.origin.x = -fromViewFinalFrame.width
31 |
32 | let fromView = fromVC.view!
33 | let toView = transitionContext.view(forKey: .to)!
34 |
35 | var toViewInitialFrame = fromViewInitFrame
36 | toViewInitialFrame.origin.x = toView.frame.size.width
37 |
38 | toView.frame = toViewInitialFrame
39 | container.addSubview(toView)
40 |
41 | let animator = UIViewPropertyAnimator(duration: self.transitionDuration(using: transitionContext), curve: .easeInOut) {
42 | toView.frame = fromViewInitFrame
43 | fromView.frame = fromViewFinalFrame
44 | }
45 |
46 | animator.addCompletion { _ in
47 | transitionContext.completeTransition(true)
48 | }
49 |
50 | self.animator = animator
51 | return animator
52 | }
53 |
54 | func animationEnded(_ transitionCompleted: Bool) {
55 | self.animator = nil
56 | }
57 | }
58 | ```
59 |
60 | ## DismissTransition
61 | ```swift
62 | import UIKit
63 |
64 | class DismissTransition: NSObject, UIViewControllerAnimatedTransitioning {
65 | var animator: UIViewImplicitlyAnimating?
66 |
67 | func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
68 | return 0.3
69 | }
70 |
71 | func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
72 | let animator = self.interruptibleAnimator(using: transitionContext)
73 | animator.startAnimation()
74 | }
75 |
76 | func interruptibleAnimator(using transitionContext: UIViewControllerContextTransitioning) -> UIViewImplicitlyAnimating {
77 | if self.animator != nil {
78 | return self.animator!
79 | }
80 |
81 | let fromVC = transitionContext.viewController(forKey: .from)!
82 | var fromViewInitFrame = transitionContext.initialFrame(for: fromVC)
83 | fromViewInitFrame.origin.x = 0
84 |
85 | var fromViewFinalFrame = fromViewInitFrame
86 | fromViewFinalFrame.origin.x = fromViewFinalFrame.width
87 |
88 | let fromView = fromVC.view!
89 | let toView = transitionContext.viewController(forKey: .to)!.view!
90 |
91 | var toViewInitialFrame = fromViewInitFrame
92 | toViewInitialFrame.origin.x = -toView.frame.size.width
93 | toView.frame = toViewInitialFrame
94 |
95 | let animator = UIViewPropertyAnimator(duration: self.transitionDuration(using: transitionContext), curve: .easeInOut) {
96 | toView.frame = fromViewInitFrame
97 | fromView.frame = fromViewFinalFrame
98 | }
99 |
100 | animator.addCompletion { _ in
101 | transitionContext.completeTransition(true)
102 | }
103 |
104 | self.animator = animator
105 | return animator
106 | }
107 |
108 | func animationEnded(_ transitionCompleted: Bool) {
109 | self.animator = nil
110 | }
111 | }
112 | ```
113 |
114 | ## 사용법
115 | ## FirstViewController
116 | - present 할 때
117 | ```swift
118 | import UIKit
119 |
120 | class ViewController: UIViewController {
121 |
122 | override func viewDidLoad() {
123 | super.viewDidLoad()
124 |
125 | }
126 |
127 | @IBAction func onPresent(_ sender: UIButton) {
128 | let secondVC = self.storyboard?.instantiateViewController(withIdentifier: "SecondVC") as! SecondViewController
129 | secondVC.modalPresentationStyle = .custom
130 | secondVC.transitioningDelegate = self
131 |
132 | self.present(secondVC, animated: true, completion: nil)
133 | }
134 | }
135 |
136 | extension ViewController: UIViewControllerTransitioningDelegate {
137 | func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
138 | return PresentTransition()
139 | }
140 |
141 | func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
142 | return DismissTransition()
143 | }
144 | }
145 | ```
146 |
147 | ## SecondViewController
148 | - dismiss 할 떄
149 |
150 | ```swift
151 | import UIKit
152 |
153 | class SecondViewController: UIViewController {
154 |
155 | override func viewDidLoad() {
156 | super.viewDidLoad()
157 |
158 | }
159 |
160 | @IBAction func onDismiss(_ sender: UIButton) {
161 | self.dismiss(animated: true, completion: nil)
162 | }
163 | }
164 |
165 | ```
166 |
--------------------------------------------------------------------------------
/CS/자료구조/algebraic_data_type.md:
--------------------------------------------------------------------------------
1 | # Algebraic Data Types (ADT)
2 | > 대수적 자료형
3 |
4 | Functional Programming(함수형 프로그래밍)과 type theory(타입 이론)에서 `Algebraic data type`은 다른 타입의 결합으로 만들어지는 합성 타입(`Composite type`)을 가리킨다.
5 |
6 | ```
7 | type thoery
8 | : type의 개념을 사용하여 합법적으로 사용 가능한 논리식에 제한을 두는 논리 체계들의 총칭.
9 | 논리식이란 어떤 논리 체계의 언어 속 기호들로 구성된 유한 문자열 가운데, 합법적인 명제로 여길 수 있는 것들을 말한다.
10 | ```
11 |
12 | 대수적 자료형의 대표적으로 `Sum type`(합 타입), `Product type`(곱 타입) 이 있다.
13 | - Sum : `Union`(공용체)
14 | - Product : `Record`(레코드), `Tuple`(튜플)
15 |
16 | `Algebraic data type`은 다른 자료형의 대체로 다른 자료형을 생성자로 감싸고 있다.
17 | 어떤 값도 대수적 자료형의 생성자의 인지가 될 수 있다.
18 |
19 | 반면에 다른 자료형은 생성자를 실행하 수 없으며 `Pattern matching`(패턴 매칭) 과정을 통해 생성자를 얻을 수 있다.
20 |
21 | 가장 일반적인 `Algebraic data type`은 두 개의 생성자를 가진 `list`(목록형) 이다.
22 | `list`는 비어있는 `list`를 위해 `nil` 또는 `[]` 를 지원한다.
23 |
24 | ----
25 |
26 | ## Primitive and Composite types
27 | ### Primitive Types (기본 자로형)
28 | 모든 프로그래밍 언어는 `primitive type`(원시 타입)을 가지고 있다.
29 | 그들은 주로 간단하고 `atomic` 하다.
30 | (더 이상 쪼갤 수 없는 가장 작은 단위 라는 의미에서 `atomic` 하다고 표현한다.)
31 | - primitive type : Bool, Int, Float, Double, Character, ...
32 |
33 | ### Composite types (복합 자료형)
34 | `composite type`은 다수의 primative type 혹은 composite type 으로 구성된 것을 말한다.
35 | `Int`들의 배열, `age`와 `name`필드를 갖고 있는 `Person` 타입과 같은 것들을 말이다.
36 | - composite type : String(Charater들의 배열), [Int], [String: Int], Person(age: name:) , ...
37 |
38 |
39 | ----
40 |
41 | ## Product and Snum types
42 | ### Product Types (곱 타입)
43 | `product type`은 대부분의 프로그래밍 언어에 존재한다.
44 | `product type`은 `primitive type`들을 `AND 결합`(AND conjunction)으로 결합시킨 것이다.
45 | 예를 들어 person이 `age` 와(AND) `married` 두 가지 속성을 모두 가지고 있을 수도 있다.
46 | - Swift에서 `product type`은 `Tuple`, `Struct`, `Class`를 나타낸다.
47 | ```swift
48 | // Example:
49 | // Tuple
50 | (20, true) // (UInt8, Bool)
51 |
52 | // Struct
53 | struct Person {
54 | let age: UInt8
55 | let isMarried: Bool
56 | }
57 |
58 | Person(age: 20, isMarried: true)
59 |
60 | // Class
61 | class Person {
62 | let age: UInt8
63 | let isMarried: Bool
64 |
65 | init(age: UInt8, isMarried: Bool) {
66 | self.age = age
67 | self.isMarried = isMarried
68 | }
69 | }
70 |
71 | Person(age: 20, isMarried: true)
72 | ```
73 |
74 | Person 타입이 가질 있는 가능한 모든 값들을 계산해보면, `isMarried 필드의 갯수` * `age 필드의 갯수`를 알아야 한다.
75 | - 우리는 그들의 `product`(곱)이 필요한 것이다.
76 | - `isMarried`에 들어갈 수 있는 값: true, false -> 2개
77 | - `age`에 들어갈 수 있는 값: 0, 1, ... 255 -> 256개
78 |
79 | ```
80 | 위의 경우 사람이 최대 255살까지 살 수 있다는 가정하에
81 | 우리는 최대 `2 * 256 = 512` 가지의 경우의 수를 갖을 수 있다.
82 | ```
83 |
84 | ### Sum Types (합 타입))
85 | `sum type`은 `primitive type`들을 `OR 결합`(OR conjunction)으로 결합시킨 것이다.
86 | 그들은 choice(선택)을 나타낸다.
87 | 이 에시에서 우리는 Documents의 열거를 가지고 있다.
88 |
89 | `Document`는 birth certificate(출생 증명서) OR(혹은) driving license(운전면허증) 중 하나가 될 수 있다.
90 | - 두 가지가 동시에 될 수는 없다. 그들은 상호 배타적이다.
91 |
92 | Swift는 `associated value`를 통해 `enum`을 지원한다.
93 | 이 예제에서 birth certificate는 age와 함께, driving license는 isExpired 라는 Bool 값과 함꼐 들어온다.
94 |
95 | ```swift
96 | enum Document {
97 | case birthCertificate(age: UInt8)
98 | case drivingLicense(isExpired: Bool)
99 | }
100 |
101 | let birthCertificate = Document.birthCertificate(age: 20)
102 | let drivingLicense = Document.drivingLicense(isExpired: false)
103 | ```
104 |
105 | Document 타입에서 가능한 모든 값들을 계산해보면, 우리는 각각의 케이스의 모든 값들을 더하면 된다.
106 | - driving license는 만료되거나 아니거나 두 가지 경우의 수가 있다.
107 | - birth certificate는 `age`를 0 ~ 255 의 경우의 수가 있다.
108 |
109 | ```
110 | 위의 경우 사람이 최대 255살까지 살 수 있다는 가정하에
111 | 우리는 최대 `2 + 256 = 258` 가지의 경우의 수를 갖을 수 있다.
112 | ```
113 |
114 | ## 실전 에제
115 | 물론 Sum 과 Product type을 결합하여 사용할 수도 있다.
116 | `Struct`는 안에 `Enum` 값을 가질 수 있고, `Enum` 안에 `Struct` 값을 가질 수도 있다.
117 | 그렇기 때문에 application 상태를 모델링할 때 굉장한 유연성을 제공한다.
118 |
119 | 예를 들어, 3가지 상호 배타적인 `ScreenState`가 있다.
120 | 만약 screen이 `loaded` 상태일 때, `Journey`의 배열에 접근할 수 있다.
121 | - `Location`타입인 `start`와 `end`의 product에 해당한다.
122 |
123 | 만약 screen이 `loading` 상태일 때, journeys는 존재하지 않는다.
124 | - type system에서 불가능 상태를 표현할 수 없다.
125 | - data가 아직 로딩중일 때는 절대 실수로 journeys를 랜더링해서는 안된다. (값이 존재하지 않으니까)
126 | ```swift
127 | // Sum type
128 | enum ScreenState {
129 | case loading
130 | case loaded(userJourneys: [UserJourney])
131 | case failed(error: Error)
132 | }
133 |
134 | // Product Type
135 | struct Journey {
136 | let start: Location
137 | let end: Location
138 | }
139 | ```
140 |
141 | [Point-Free](https://www.pointfree.co/)의 영상중 하나인 [algebraic data types](https://www.pointfree.co/episodes/ep4-algebraic-data-types) 에 또 다른 좋은 예시가 있다.
142 | Algebraic Data Types를 사용하지 않는 Apple의 API 의 문제점에 대한 이야기이다.
143 | 이 API는 network request를 생성하고 completion handler에서 다수의 illegal 상태를 허용한다.
144 | 이것은 ADT를 지원하지 않는 Objective-C로 구현된 것이기에 어쩔 수 없다.
145 |
146 | ```swift
147 | // URLSession dataTask(with: completionHandler:)
148 | func dataTask(with url: URL, compeltionHandler: (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask
149 | ```
150 |
151 | `completionHandler`에 집중해보자.
152 | Data, URLResponse, Error 모두 optional 타입을 받는다.
153 | 모두 8개의 경우의 수를 가지고 있고, 대부분이 illegal 하다.
154 | 경우의 수를 모두 작성해보면...
155 | ```swift
156 | (Data?, URLResponse?, Error?)
157 | -----------------------------
158 | (Data?, URLResponse?, Error?)
159 | ```
160 |
161 | |Data|URLResponse|Error|is illegal|
162 | |:--:|:--:|:--:|:--:|
163 | |data|response|nil | |
164 | |nil |nil |error| |
165 | |nil |response|error| |
166 | |nil |response|nil |O|
167 | |data|response|error|O|
168 | |data|nil |error|O|
169 | |data|nil |nil |O|
170 | |nil |nil |nil |O|
171 |
172 | illegal한 상태를 피하기 위해서 [Ole Begemann의 블로그](https://oleb.net/blog/2018/03/making-illegal-states-unrepresentable/)에 소개된 두 개의 새로운 product type를 사용하면 고칠 수 있다.
173 | response의 `Success`와 `Failure` 값을 들고 있는 것이다.
174 | ```swift
175 | extension URLSessionDataTask {
176 | struct Success {
177 | let data: Data
178 | let response: URLSResponse
179 | }
180 |
181 | struct Failure: Error {
182 | let error: Error
183 | let response: URLResponse?
184 | }
185 | }
186 | ```
187 |
188 | 우리는 이 타입의 use case와 맞아 떨어지는 Swift에 내장된 Sum type인 `Result`타입과 엮어 사용할 수 있다.
189 | ```swift
190 | Result
191 | ```
192 |
193 | 위의 illegal한 경우의 수를 포함하는 경우와 비교해보자.
194 | ```swift
195 | .success(Success(data: data, response: response)) // is illegal? : NO
196 | .failure(Failure(error: error, response: nil)) // is illegal? : NO
197 | .failure(Failure(error: error, response: response)) // is illegal? : NO
198 | ```
199 |
200 | 이 새로운 API를 사용하면 모든 케이스를 legal하게 사용할 수 있다.
--------------------------------------------------------------------------------
/CS/자료구조/hashtable.md:
--------------------------------------------------------------------------------
1 | # Hash / HashTable
2 |
3 | ## 해시(hash)란,
4 | > index에 해시값을 사용하는 자료구조로, 정렬을 하지 않고도 빠른 검색/삽입이 가능하다.
5 |
6 | `다양한 길이를 가진 데이터`를 `고정된 길이를 가진 데이터`로 매핑(mapping)한 값이다.
7 | 이를 이용해 특정 배열의 인덱스, 위치, 위치를 입력하고자 하는 데이터의 값을 이용해 저장하거나 찾을 수 있다.
8 | 기존에 사용했던 자료 구조들은 탐색이나 삽입에 선형 시간이 걸리기도 했던 것에 비해,
9 | 해시를 이용하면 즉시 저장하거나 찾고자 하는 위치를 참조할 수 있으므로 더욱 빠른 속도로 처리할 수 있다.
10 | 해시값이라고도 한다.
11 |
12 | ## 특징
13 | - 무결성
14 | - 특정한 데이터를 이를 상징하는 더 짧은 길이의 데이터로 변환하는 행위를 의미.
15 | - 상징 데이터는 원본 데이터가 조금만 달라져도 크게 달라지는 특성이 있어서 무결성을 지키는데 도움이 된다.
16 | - 보안성
17 | - 해시는 기본적으로 복호화가 불가능하다.
18 | - 당연히 입력 데이터 집합이 출력 데이터 집합을 포함하고 있으므로 특정한 출력 데이터를 토대로 입력 데이터를 찾을 수 없기 때문이다.
19 | - 동일한 출력 값을 만들어낼 수 있는 입력 값의 가짓수는 수학적으로 무한개라 볼 수 있다.
20 | - 해시는 애초에 복호화를 수행할 수 없게 설계되어있다.
21 | - 비둘기집 원리
22 | - 대부분의 해시 알고리즘은 항상 고정된 길이의 결과 문자열을 반환한다는 특징을 가지고 있다.
23 | - 비둘기가 5마리일 때 상자가 4개밖에 존재하지 않는다면 아무리 비둘기를 균등하게 분배해도 최소 한 상자에는 2마리의 비둘기가 들어가게 된다는 원리.
24 | - 이 원리에 따라 해시에서는 '서로 다른 입력값의 해시 결과 값이 동일한 문제' 즉, `해시 충돌`이 발생할 여지가 있다.
25 |
26 | ## 구현방식
27 | > 개별 체이닝(Separate Chaining) 과 오픈 어드레싱(Open Addressing)
28 |
29 | 해시 충돌이 발생하여 같은 index가 만들어질 수도 있다.
30 | 이 경우 보통 사용하는 방법이 두 가지 정도이다.
31 |
32 | 하나는 리스트 각각의 index를 연결리스트(Linked List)로 만들어 새로 입력이 될 때마다 같은 해시를 가진다 하더라도 색인이 연결리스트로 구현되어 있기 때문에
33 | ```
34 | - 원하는 데이터의 접근이 가능한 개별 체이닝
35 | - 다음에 위치한 색인들 중 비어있는 곳에 넣는 방식인 오픈 어드레싱
36 | ```
37 | 이 있다.
38 |
39 | ### 개별 체이닝
40 | 해시 테이블의 기본방식이기도 하다.
41 | 충돌 발생 시 연결리스트로 link하는 방식이다. 출돌이 발생한 bannana과 bus를 서로 연결리스트로 연결한다.
42 | 이처럼 기본적인 자료구조와 임의로 정한 간단한 알고리즘만 있으면 되므로, 이 방식은 인기가 높다.
43 | 원래 해시 테이블 구조의 원형이기도 하며 가장 전통적이 방식이다.
44 |
45 |
46 | ### 오픈 어드레싱
47 | 충돌 발생 시 탐사(Probing)를 통해 빈 공간을 찾아나서는 방식이다.
48 | 사실상 무한정 저장할 수 있는 체이닝 방식과 달리, 저전체 슬롯의 개수 이상은 저장할 수 없다.
49 | 충돌이 일어나면 데이블 공간 내에서 탐사를 통해 빈 공간을 찾아 해결하낟.
50 | 이 때문에 모든 원소가 반드시 자신의 해시값과 일치하는 주소에 저장된다는 보장은 없다.
51 | 가장 간단한 방식은 선형 탐사(Lenear Probing) 방식이며 충돌이 발생할 경우 해당 위치부터 순차적으로 탐사를 진행한다.
52 | 특정 위치가 선점되어 있으면 그 바로 다음 위치를 확인하는 식이다.
53 | 탐사를 짆애하다 비어있는 공간을 발견하면 삽입하게 된다. (가장 가까운 다음 빈 위치에 새 키를 삽입한다)
54 | 선형 탐사 방식은 구현 방법이 간단하면서도 의외로 전체적인 성능이 좋은 편이기도 하다.
55 |
56 | ## Swift에서의 해시 테이블
57 | 딕셔너리를 사용하면 된다.
58 |
59 | 딕셔너리는 key - value 로 값을 저장하고 가져온다.
60 | 딕셔너리는 해시 테이블로 구현되어있다.
61 | (참고: 해시테이블은 또 내부적으로 배열로 구현되어있다.)
62 | 그러면 이 해시테이블 내부 배열에서 key값을 통해 value를 가져오려면 테이블의 index 정보를 알아야 한다.
63 |
64 | 이 해당 index를 찾도록 도와주는 것이 hash function 이다.
65 | hash function은 key값으로 연산을 하여 index값 즉, 해시의 주소값을 만들어준다.
66 |
67 | ## 구현해보기
68 | ### 1) 배열 만들기
69 | ```swift
70 | var hashTable: [String?] = .init(repeating: nil, count: 3)
71 | // 값이 없으면 nil을 뱉도록 구성
72 | ```
73 |
74 | ### 2) 해시 함수 만듥
75 | 일반적으로는 SHA256같은 안전한 알고리즘을 사용한다.
76 | 직접 만들어보는 연습을 하기 위해 임의로 함수를 만든다.
77 | ```swift
78 | func hash(key: Int) -> Int {
79 | return key % 3
80 | }
81 | ```
82 |
83 | ### 3) 해시 테이블에 저장하는 함수 만들기
84 | key-value 쌍을 받아 이 값을 해시 테이블에 저장한다.
85 | ```swift
86 | func updateValue(_ value: String, forKey key: String) {
87 | guard let key = UnicodeScalar(key)?.value else { return } // String 타입의 key를 Unicode를 사용하여 Int타입으로 만들어줌 (key가 Int형이면 생략)
88 | let hashAddress = hash(key: Int(key))
89 | hashTable[hashAddress] = value
90 | }
91 | ```
92 | - 비어있으면 insert, 이미 존재한다면 update
93 |
94 | ### 4) 해시 테이블의 값을 얻는 함수 만들기
95 | ```swift
96 | func getValue(forkey key: String) -> String? {
97 | guard let key = UnicodeScalar(key)?.value else { return nil }
98 | let hashAddress = hash(key: Int(key))
99 | return hashTable[hashAddress]
100 | }
101 | ```
102 |
103 | ### 중간 결과 확인
104 | ```swift
105 | updateValue("apple", forKey: "a")
106 | updateValue("bus", forKey: "b")
107 | updateValue("car", forKey: "c")
108 |
109 | print(hashTable) // [Optional("car"), Optional("apple"), Optional("bus")]
110 | ```
111 |
112 | ## 해시가 충돌할 경우
113 | ### Saperate Chaining (Open Hashing)
114 | - 충돌이 일어날 경우 연결리스트를 이용하여 데이터를 추가로 뒤에 연결하여 저장하는 기법
115 |
116 | ### Linear Probing (Close Hashing)
117 | - 충돌이 일어날 경우 해당 해쉬 주소값을 순회하면 가장 처음 나오는 빈 공간에 저장하는 기법
118 |
119 |
120 | ## 시간복잡도
121 | ```
122 | O(1)
123 | ```
124 | - 최악의 경우(모두 충돌이 발생한 경우): `O(n)`
125 | - 평균: `O(1)`
126 |
127 | ## 장단점
128 | ```
129 | 시간복잡도 <-> 공간복잡도
130 | ```
131 | > 장점
132 | - 데이터 read/write 속도가 빠르다.
133 | - key에 대한 데이터 중복 확인이 쉽다.
134 |
135 | > 단점
136 | - 일반적으로 저장 공간이 많이 필요하다. (충돌 문제를 대비해 저장공간을 넓게 잡는다.)
137 | - 충돌 발생 시 해결을 위한 별도의 자료구조가 필요하다.
138 |
139 | > 언제 사용하면 좋은가?
140 | - 탐색이 잦은 경우
141 | - 캐시를 구현할 때
142 |
143 | ## Ex
144 | ### HashTable
145 | ```swift
146 | public struct HashTable {
147 | private typealias Element = (key: Key, value: Value)
148 | private typealias Bucket = [Element]
149 | private var buckets: [Bucket]
150 |
151 | private(set) public var count = 0
152 |
153 | public var isEmpty: Bool { return count == 0 }
154 |
155 | public init(capacity: Int) {
156 | assert(capacity > 0)
157 | buckets = Array(repeatElement([], count: capacity))
158 | }
159 | ```
160 |
161 | ### HashTable 객체 만들기
162 | ```swift
163 | var hashTable = HashTable(capacity: 5) // capacity에 원하는 크기를 지정하여 선언
164 | ```
165 |
166 | ### 주어진 key값을 통해 index 값을 구하는 메서드
167 | ```swift
168 | private func index(forKey key: Key) -> Int {
169 | return abs(key.hashValue % buckets.count)
170 | }
171 | ```
172 |
173 | ### 사용법 (syntex)
174 | ```swift
175 | hashTable["a"] = "apple" // insert
176 | let x = hashTable["a"] // lookup
177 | hashTable["a"] = "airplane" // update
178 | hashTable["a"] = nil // delete
179 | ```
180 |
181 | ### subscript 함수 만ㄷ르기
182 | ```swift
183 | public subscript(key: Key) -> Value? {
184 | get {
185 | return value(forKey: key)
186 | }
187 | set {
188 | if let value = newValue {
189 | updateValue(value, forKey: key)
190 | } else {
191 | removeValue(forKey: key)
192 | }
193 | }
194 | }
195 | ```
196 |
197 | ### key값에 따른 value를 구하는 함수
198 | ```swift
199 | public func value(forKey key: Key) -> Value? {
200 | let index = self.index(forKey: key)
201 | for element in buckets[index] {
202 | if element.key == key {
203 | return element.value
204 | }
205 | }
206 | return nil // key not in hash table
207 | }
208 | ```
209 |
210 | ### 값 수정
211 | ```swift
212 | public mutating func updateValue(_ value: Value, forKey key: Key) -> Value? {
213 | let index = self.index(forKey: key)
214 |
215 | // Do we already have this key in the bucket?
216 | for (i, element) in buckets[index].enumerated() {
217 | if element.key == key {
218 | let oldValue = element.value
219 | buckets[index][i].value = value
220 | return oldValue
221 | }
222 | }
223 |
224 | // This key isn't in the bucket yet; add it to the chain.
225 | buckets[index].append((key: key, value: value))
226 | count += 1
227 | return nil
228 | }
229 | ```
230 |
231 | ### 값 삭제
232 | ```swift
233 | public mutating func removeValue(forKey key: Key) -> Value? {
234 | let index = self.index(forKey: key)
235 |
236 | // Find the element in the bucket's chain and remove it.
237 | for (i, element) in buckets[index].enumerated() {
238 | if element.key == key {
239 | buckets[index].remove(at: i)
240 | count -= 1
241 | return element.value
242 | }
243 | }
244 | return nil // key not in hash table
245 | }
246 | ```
--------------------------------------------------------------------------------
/CS/ETC/fastlane.md:
--------------------------------------------------------------------------------
1 | # Fastlane 이란?
2 |
3 | ```
4 | The easiest way to build and release mobile apps.
5 |
6 | fastlane is an open source platform aimed at simplifying Android and iOS deployment.
7 | fastlane lets you automate every aspect of your development and release workflow.
8 | ```
9 |
10 | - 위에 적혀있다시피, 모바일 앱의 배포 과정 자동화를 시켜주는 툴이다.
11 | - 기존 iOS 배포 과정은 매우 오래걸린다. Archive 하는 도중에 Xcode가 튕기는 경우도 다수 있고, Archive한 후 Apple developer 사이트에 들어가 TestFlight를 배포하고, 심사를 작성하여 애플에 제출하고 심사를 통과 하면 앱스토어에 출시된다.
12 | - 이 과정을 fastlane은 단축시켜준다.
13 |
14 |
15 |
16 |
17 | ## Fastlane 설치
18 | 컴퓨터에 `HomeBrew`가 설치되어있지 않다면 아래의 명령어를 통해 먼저 설치하자.
19 | ```bash
20 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
21 | ```
22 |
23 |
24 |
25 | `HomeBrew`가 설치되어있다면 아래의 명령어를 실행하면 된다.
26 | ```bash
27 | brew install fastlane
28 | ```
29 | (명령어 실행시 권한이 없다는 에러가 뜬다면 앞에 `sudo` 키워드를 붙일 것)
30 |
31 |
32 |
33 | 컴퓨터에 `gem`이 설치되어있지 않다면 `gem`을 설치한 후,
34 | ```bash
35 | # rbenv 설치
36 | brew install rbenv ruby-build
37 |
38 | # rbenv를 bash에 추가
39 | echo 'if which rbenv > /dev/null; then eval "$(rbenv init -)"; fi' >> ~/.bash_profile
40 | source ~/.bash_profile
41 |
42 | # ruby 설치
43 | rbenv install 2.4.4
44 | rbenv global 2.4.4
45 | ```
46 |
47 |
48 |
49 | fastlane을 업데이트할 떄 사용되는 `bundler` 도 설치해주자.
50 | ```bash
51 | gem install bundler
52 | ```
53 |
54 |
55 |
56 | 이후 fastlane을 업데이트 하고자 할 때는 아래의 명령어를 입력하면 된다.
57 | ```bash
58 | bundle update
59 | ```
60 |
61 |
62 |
63 |
64 | ## 기본 설정
65 | Fastlane을 사용하고자하는 플젝트의 디렉토리로 이동하여 아래의 명령어를 실행하자.
66 | ```bash
67 | fastlane init
68 | ```
69 | - fastlane 초기화를 통해 이 디텍토리에서 fastlane을 설정할 수 있다. (pod init 처럼)
70 |
71 | init을 하면 다음과 같은 문구가 출력된다.
72 | ```
73 | What would you like to user fastlane for?
74 | 1. Automate screenshots
75 | 2. Automate beta distribution to TestFlight
76 | 3. Automate App Store distribution
77 | 4. Manual setup - manually setup your project to automate your tasks
78 | ```
79 | - fastlane을 통해 하고 싶은 작업의 번호들을 입력하면 된다. (나중에 추가 가능하다. 이때 선택한 것을 미리 세팅해줄 뿐임)
80 |
81 | 2,3번의 경우에는
82 | `Apple ID`와 `application specific password`에 대한 정보를 입력해 주어야 한다.
83 |
84 | > application specific password란? [appleid.apple.com](https://appleid.apple.com/account/manage) > 로그인 > 맨밑의 '앱 암호' > 암호 생성
85 | 시 발급되는 비밀번호이다.
86 |
87 |
88 | - 애플 서버에 매번 로그인하는 과정을 fastlane이 알아서 앱 암호를 통해 로그인해준다.
89 |
90 | 위의 과정을 완료하면 아래의 파일이 생겨있을 것이다.
91 | ```bash
92 | Gemfile
93 | Gemfile.lock
94 | fastlane/Appfile
95 | fastlane/Fastfile
96 | ```
97 | - Gemfile, Gemfile.lock
98 | - fastlane 의 버전 등을 관리하는 파일
99 | - fastlane/
100 | - 해당 프로젝트, 애플 계정, fastlane 설정사항 등이 들어있는 디렉토리
101 |
102 |
103 |
104 |
105 | ## AppStore Connect 인증 방법
106 | - 두가지 방법이 있다.
107 | - Cert, Sign
108 | - Match
109 |
110 | 나는 여기서 첫번째 방법을 사용할 것이다.
111 | 나의 애플 계정을 입력하고 인증서를 내 로컬 Keychain에 넣어두면 알아서 인증해주는 방식이다.
112 | (로컬에 인증서가 없어도 알아서 다운받아 진행해준다.)
113 |
114 |
115 |
116 |
117 | ## 환경변수 설정
118 | Appfile에 아래와 같은 내용이 들어있을 것
119 | ```ruby
120 | app_identifier("자신의 앱 identifier") # The bundle identifier of your app
121 | apple_id("자신의 애플 아이디") # Your Apple email address
122 | # For more information about the Appfile, see:
123 | # https://docs.fastlane.tools/advanced/#appfile
124 | ```
125 | - 혼자 사용할 때는 문제가 발생하지 않지만, 여러명에서 git을 통해 사용하다보면 `apple_id` 에서 conflict이 일어날 것이다.
126 | - 각자의 애플 아이디로 환경 변수를 설정하여 사용하는 것이 좋다.
127 | - 아래와 같이 환경변수를 설정해보자.
128 |
129 | ```ruby
130 | app_identifier("자신의 앱 identifier") # The bundle identifier of your app
131 | apple_id(ENV["APPLE_ID"]) # Your Apple email address -> Please Set Env Variable in /fastlane/.env!!
132 | # For more information about the Appfile, see:
133 | # https://docs.fastlane.tools/advanced/#appfile
134 | ```
135 | - 아래의 명령어를 통해 `fastlane/` 폴더 안에 `.env` 파일을 만들고 에디터를 열어서
136 | ```bash
137 | vi fastlane/.env
138 | ```
139 |
140 | 거기에 아래와 같은 형식으로 자신의 이메일을 작성하고 저장한다.
141 | ```bash
142 | APPLE_ID="자신의 애플 아이디"
143 | ```
144 | - vi를 닫고 저장하는 방법은 `esc`를 누른 후 `:wq` 를 입력하고 엔터 치면된다.
145 |
146 | 그리고 이 파일(`/fastlane/.env`)을 `.gitignore`에 추가하면 끝이다.
147 |
148 |
149 |
150 |
151 | ## TestFlight 설정
152 | fastlane/Fastfile 에 Ruby 언어로 작성한다.
153 | Fastlane 에서 제공하는 명령어들만 사용해도 괜찮다.
154 | (추가적인 세팅을 원하다면 알아서 찾아볼 것.)
155 | 예시 파일 내부 코드는 아래와 같다.
156 | ```ruby
157 | desc "build app and upload to testflight"
158 | lane :beta do
159 | get_certificates
160 | get_provisioning_profile
161 | increment_build_number(
162 | build_number: latest_testflight_build_number + 1
163 | )
164 | build_app(
165 | configuration: "Debug"
166 | )
167 | upload_to_testflight
168 | slack(
169 | message: "Testflight 배포 성공!",
170 | slack_url: "https://hooks.slack.com/자신의 채널 훅스 링크"
171 | )
172 | end
173 | ```
174 | - `lane`
175 | - lane 명을 설정할 수 있다.
176 | - 예시로 `beta`라고 설정하였다.
177 | - 앞으로 이 lane으로 사용할 것이라면 `fastlane beta` 라고 작성하면 된다.
178 | - `get_certificates`, `get_provisioning_profile`
179 | - Cert, Sigh 방식에서 인증할 떄 사용되는 함수이다.
180 | - 각각 인증서, 프로필을 가져온다.
181 | - 여기서 에러가 난다면, 애플 계정 혹은 인증서에 문제가 있을 수 있으니 체크해보자.
182 | - `increment_build_number`
183 | - 내 프로젝트 빌드엄버를 올려준다.
184 | - `build_app`
185 | - configuration을 설정해주는 부분으로 Debug와 Release 중에 Debug로 설정해주었다. (사용할 버전으로 설정하면 된다.)
186 | - `upload_to_testflight`
187 | - TestFlight에 업로드해준다.
188 | - AppStore Connect의 빌드 목록에 뜨는 것을 기다리는 과정을 생략하고 싶다면 `skip_waiting_for_build_processing: true` 를 추가하면 된다.
189 |
190 | - `slack`
191 | - slack과 연동하여 배포 성공여부에 대한 메세지를 보내도록 설정할 수 있다.
192 |
193 |
194 |
195 |
196 | 실패한 경우에 대한 error handler도 추가할 수 있다.
197 | ```ruby
198 | platform :ios do
199 |
200 | ...
201 |
202 | error do |lane, exception, options|
203 | slack(
204 | message: "에러 발생 : #{exception}",
205 | success: false,
206 | slack_url: "https://hooks.slack.com/자신의 채널 훅 링크",
207 | )
208 | end
209 |
210 | ...
211 |
212 | end
213 | ```
214 | - iOS의 어떤 lane에서든 에러가 발생하면 slack에 메세지를 보내도록 설정하는 코드이다.
215 |
216 |
217 |
218 |
219 | ## AppStore 심사 제출
220 | ```ruby
221 | desc "build app and release to App Store."
222 | lane :release do |options|
223 | if options[:v]
224 | get_certificates
225 | get_provisioning_profile
226 | increment_build_number(
227 | build_number: latest_testflight_build_number + 1
228 | )
229 | build_app(
230 | configuration: "Release"
231 | )
232 | upload_to_app_store(
233 | app_version: options[:v],
234 | submit_for_review: true,
235 | force: true,
236 | automatic_release: true,
237 | skip_screenshots: true,
238 | skip_metadata: false
239 | )
240 | slack(
241 | message: "AppStore 배포에 성공했습니다!",
242 | slack_url: "https://hooks.slack.com/자신의 채널 훅스 링크"
243 | )
244 | end
245 | end
246 | ```
247 | - `lane`
248 | - 버전 넘버작성 시 `v`를 적어주지 않으면 작동하지 않도록 설정해두었다.
249 | - `build_app`
250 | - Release로 배포되도록 설정하두었다.
251 | - 실수로 Debug로 배포되지 않도록 하자.
252 | - `upload_to_app_store`
253 | - 빌드를 업로드한 후 심사 제출하는 코드이다.
254 | - 옵션들은 원하는대로 bool값을 커스텀해주면 된다.
255 |
256 | - 추가
257 | - `fastlane/metadata/ko` 디렉토리 안의 `release_notes.txt` 파일을 열어 변경 사항 문구를 작성한 후 `release` lane에서 실행하면 `metadata` 업로드 시 해당 정보를 포함해서 올려준다.
258 |
259 | ## 슬랙용 웹 후크 url 만드는 방법
260 | - slack 채널에 사용되는 훅 링크는 [여기](https://feat-ooq9217.slack.com/apps/new/A0F7XDUAZ--)에서 찾을 수 있다.
261 | - 슬랙 워크스페이스에 로그인
262 | - 포스트할 채널 선택
263 | - '수신 웹 후크를 통한 앱 추가' 클릭
264 | - 생성된 url을 복사하여 위의 `slack_url` 에 넣기
265 |
266 |
267 | ## 확장가능 CI/CD
268 |
269 | > CI: Continuous Integration
270 | > 코드의 새로운 변경사항을 지속적으로 프로덕트에 통합
271 |
272 | > CD: Continuous Deployment
273 | > 짧은 주기로 사용자에게 서비스를 제공할 수 있돌고 지속적으로 프로덕트를 배포하는 방법
274 |
275 |
276 | TestFlight와 AppStore 배포에 편리성을 보고 fastlane을 접하였다.
277 |
278 | CI/CD 파이프라인을 구축하여 개발자가 배포의 과정보다는 본래의 업무에 더 집중할 수 있다.
279 |
280 | fastlane을 통해 빌드, 테스트, 배포를 모두 자동화할 수 있다.
281 | (ex. Jenkins와의 연계)
282 |
283 |
284 |
285 |
286 |
287 | ### 참고링크
288 | [fastlane 공식문서](https://docs.fastlane.tools/)
289 |
--------------------------------------------------------------------------------
/Swift/big_o_in_swift.md:
--------------------------------------------------------------------------------
1 | # Swift에서의 Big O에 대하여
2 |
3 | `Big O 표기법(Big O Notation)`에 대해 다들 들어본 적이 있을 것이다. 하지만 제대로 이해하고 무엇인지 아는 사람은 많지 않다.
4 |
5 | 이 글을 다 읽은 후에 `Big O 표기법`을 사용하여 알고리즘을 추론할 수 있기를 바란다.
6 | 혹은 적어도 `Big O 표기법`이 무엇인지, 무엇을 표현하는지, 어떻게 하는지는 이해할 수 있기를 바란다.
7 |
8 | ...
9 |
10 | ---
11 |
12 | ## Big O 가 무엇인지 이해하기
13 | `Big O 표기법`은 함수(function)이나 알고리즘(algorithm)의 성능을 설명하는데 사용되며 아래와 같이 표기한다.
14 | ```
15 | O(1)
16 | ```
17 |
18 | 데이터의 크기가 증가함에 따라 실행 시간이 어떻게 되는지를 통해 성능을 측정한다.
19 | (*데이터의 모음을 `collection`이라고 앞으로 지칭하겠다.)
20 |
21 | ---
22 |
23 | ## O(1)
24 | > constant time algorithm
25 |
26 |
27 |
28 | 가장 최고의 성능을 낼때 `O(1)`이라고 한다. collection의 사이즈와 관계없이 항상 실행시간이 같은 경우를 의미한다.
29 | (즉, 데이터의 갯수가 1개여도 1억개여도 항상 같은 실행시간이 걸리는 것을 말한다.)
30 | `O(1)`을 시각화하면 아래의 그래프와 같다.
31 |
32 | // 그래프 이미지 첨부 예정
33 |
34 | ### Swift에서 O(1) 에 해당하는 예시
35 | ```swift
36 | // 1-1
37 | let numberArray = [0, 1, 2, 3, 4, 5]
38 | numberArray[2]
39 | ```
40 |
41 | - 위와 같이 subscript를 사용하여 `Array`의 어느 한 원소에 접근할 때를 들 수 있다.
42 |
43 | 이 말은 즉, `Array`의 크기가 얼마나 크던간에 특정 위치(index)에 있는 값에 접근하여 읽어오는것은 항상 같은 성능을 갖음을 의미한다.
44 |
45 | ### 여기서 주의해야할 점은
46 | - 항상 빠를 것이다
47 | - 항상 성능이 좋을 것이다
48 |
49 | 라고 생각하는 것이다.
50 |
51 | `O(1)`은 때로 매우 느리고 성능이 형편없을 수 있다.
52 | ```
53 | O(1)이라 표기할 때 말하고자 하는 것은 해당 알고리즘의 성능이 적용되는 "데이터의 크기에 의존하지 않는다"는 점이다.
54 | ```
55 |
56 | `O(1)`의 시간복잡도를 갖는 알고리즘은 상수(constant)로 간주된다.
57 | 즉, 적용되는 데이터가 증가해도 성능이 저하되지 않는다.
58 |
59 | ---
60 |
61 | ## O(n)
62 | > linear time algorithm
63 |
64 |
65 | `O(n)`은 collection의 크기가 증가함에 따라 시간복잡도도 증가하는 것을 말한다.
66 | 그래프로 시각화해보면 선형적인 증가를 보여준다.
67 | 알고리즘 실행 시간 혹은 성장 정도가 collection의 크기와 비례하여 선형적으로 증가한다.
68 |
69 | // 그래프 이미지 첨부 예정
70 |
71 | ## Swift에서의 예시
72 | `map`을 예로 들 수 있다.
73 | `map`은 배열의 모든 아이템을 순회하기 떄문에 `O(n)`의 시간복잡도를 갖는 알고리즘이라 여길 수 있다.
74 |
75 | Swift에서 기본적으로 제공하는 함수들 중 다수가 유사한 성능을 갖는다.
76 | - `filter`, `compactMap`, `first(where:)`
77 |
78 | 만약 당신이 `first(where:)`가 친숙하다면 이것이 O(n)의 시간복잡도를 갖는다는 것에 의아할 것이다.
79 | (나는 O(1)인줄 알고 있었다.)
80 |
81 | 왜냐면 위에서 O(n)은 collection의 모든 항목을 단순히 순회하고 방문 것으로 설명했기 떄문이다.
82 | - `first(where:)`는 그렇지 않는데 말이지..
83 |
84 | 이 경우, `where`인자를 사용하여 일치하는 항목을 예측하고 찾는 순간 리턴할 수 있다.
85 | 아래의 예시 코드를 보자.
86 |
87 | ```swift
88 | // 1-2
89 | let wordArray = ["Hi", "My", "name", "is", "keen"]
90 | var numberOfChecked = 0
91 |
92 | let fourLetterWord = wordArray.first(where: { word in
93 | numberOfChecked += 1
94 | return word.count == 4
95 | })
96 |
97 | print(fourLetterWord) // name
98 | print(numberOfChecked) // 3
99 | ```
100 |
101 | 코드를 보면 내가 원하는 조건과 일치하는 단어를 찾는데 3번이면 된다.
102 | 위에서 말한 두루뭉실한 정의를 바탕으로 보면, 당신은 명확하게 모든 경우에 대해 `O(n)` 은 아니라고 주장할 수도 있다.
103 | - `map` 처럼 `Array`의 모든 원소를 순회하지 않았기 때문에
104 |
105 | ### 그 주장은 맞다!
106 | `Big O 표기법`은 어떤 특정한 `use case`를 고려하지 않는다.
107 | 이 알고리즘은 최악의 경우 일치하는 원소를 마지막까지 찾지 못하고 `nil`을 리턴할 수도 있다.
108 |
109 | `Big O 표기법`은 평균(most common)과 최악(worst case)의 경우를 다룬다.
110 | `first(where:)`의 경우, 최악의 경우를 가정하는게 가장 합리적이다.
111 | - 일치하는 원소를 찾을 거라는 보장이 없고,
112 | - 만약 보장한다고 해도, 가장 처음 혹은 마지막에 발견할지 모르기에 모든 경우가 동등하지 않기 때문이다.
113 |
114 | 처음에 Array의 데이터를 읽어오는 경우는 O(1) 이라고 말한 이유는
115 | Array가 얼마나 많은 아이템들을 가지고 있던 성능은 항상 같기 때문이다.
116 | Swift 공식문서를 보면 Array에 원소를 삽입(insert)하는 경우에 대해서 아래와 같이 기술했다.
117 | ```
118 | Complexity: Reading an element from an array is O(1).
119 | Writing is O(1) unless the array's storage is shared with another array or uses a bridged NSArray instance as its storage,
120 | in which case writing is O(n), where n is the length of the array.
121 | ```
122 | ```
123 | (해석)
124 | 복잡도: 배열에서 하나의 원소를 읽는 것은 O(1) 이다.
125 | 쓰는 것은 배열의 저장공간이 다른 배열과 공유되고 있거나 저장NSArray 인스턴스와 연결되어 사용하는 경우가 아니라면 O(1) 이다.
126 | 그 경우에는 배열의 길이가 n이라면 쓰는데 O(n)이 걸린다.
127 | ```
128 |
129 | ## Swift에서 Array가 메모리 공간을 할당하는 방식
130 | 배열은 그들 안에 아이템을 삽입할 때 특별한 일이 발생한다.
131 | 하나의 배열은 대게 자기자신을 위해 특정 크기의 메모리 공간을 아껴두고 있다.
132 | 그 공간이 가득차고 나면, 아껴둔 메모리 공간이 충분하지 않게 될 수 있고, 그러면 그 배열은 자기 자신을 위한 더 많은 메모리 공간을 필요로 하게 된다.
133 |
134 | 이 메모리 재할당은 Swift문서에 언급되지 않은 성능 문제(performance hit)를 야기한다.
135 | 이러한 문제가 발생하는 이유를 추측하보면 Swift 핵심 개발 팀이 `배열의 쓰기` 행위를 할 때 최악의 경우가 아닌 평균 성능을 사용하기로 결정한 것으로 보인다.
136 | ```
137 | 위의 이유로 새로운 항목을 삽입(insert)할 때 배열 크기가 조정되지 않을 가능성이 높다.
138 | ```
139 |
140 | ----
141 |
142 | ## O(n^2)
143 | > quadratic time algorithm
144 |
145 |
146 |
147 | O(n^2) 의 성능은 bubble sort와 같은 간단한 정렬 알고리즘에서 흔하게 볼 수 있다.
148 | 쉬운 예제를 들면 아래와 같다:
149 | ```swift
150 | // 1-3
151 | let intergers = (0..<5)
152 | let squareCoords = integers.flatMap { i in
153 | return intergers.map { j in
154 | return (i, j)
155 | }
156 | }
157 |
158 | print(squareCoords) // [(0,0), (0,1), (0,2) ... (4,2), (4,3), (4,4)]
159 | ```
160 |
161 | `squareCoords`를 생성하기 위해서 `flapMap`을 사용하여 integer들을 순회하였다.
162 | 그 `flapMap` 안에는, 다시 `map`을 사용하여 순회를 하였다.
163 | 이것은 `return (i, j)` 라인은 `5^2` 즉, `25`번 실행됨을 말한다.
164 | 각각의 원소들을 배열에 추가하고, 생성되는 `squareCoords`는 기하급수적으로 증가한다.
165 | 6x6 의 사각형은 36번, 7x7은 49, 8x8은 64번 순회할 것이다.
166 | 이를 보면 `O(n^2)` 은 최선의 성능은 갖는다고 볼 수 없음을 알 수 있다.
167 |
168 | ----
169 |
170 | ## O(logn)
171 | > logarithmic time algorithm
172 |
173 |
174 | 이름에서 알 수 있듯이, 로그 스케일로 증가하는 복잡도를 확인할 수 있다.
175 | `O(logn)` 복잡도를 갖는 알고리즘은 적은 양의 데이터에 사용할 때 성능이 안 좋다.
176 | 하지만 데이터가 증가하고 `n`이 무한에 가까워지면 알고리즘의 성능은 점점 좋아진다.
177 |
178 | 이 예시로 `이진탐색(binary search)` 를 들 수 있다.
179 | 만약 우리는 정렬된 배열을 가지고 있고, 그 안에서 특정 원소를 찾고자 한다고 가정해보자.
180 | binary search는 이 방법을 사용하면 굉장히 효율적일 것이다:
181 | ```swift
182 | // 1-4
183 | extension RandomAccessCollection where Element: Comparable, Index == Int {
184 | func binarySearch(for item: Element) -> Index? {
185 | guard self.count > 1 else {
186 | if let first = self.first, first == item {
187 | return self.startIndex
188 | } else {
189 | return nil
190 | }
191 | }
192 |
193 | let middleIndex = (startIndex + endIndex) / 2
194 | let middleItem = self[middleIndex]
195 |
196 | if middleItem < item {
197 | return self[index(after: middleIndex)...].binarySearch(for: item)
198 | } else if middleItem > item {
199 | return self[.. 1회
221 | 2개 -> 2회
222 | 10개 -> 3회
223 | 50 -> 6회
224 | 100개 -> 7회
225 | 1000개 ->10회
226 | ```
227 |
228 | 위의 예시를 보면, 데이터의 크기가 `10개 -> 50개`로 증가하는데 최대 검색 횟수는 `2배` 밖에 증가하지 않았다.
229 | 이것은 데이터 세트가 증가함에 따라 `O(log n)` 알고리즘의 성능 저하가 덜 중요해지는(신경쓰지 않아도 되는) 이유를 보여주는 좋은 예이다.
230 |
231 | ----
232 |
233 | ## 모든 알고리즘 비교 그래프
234 |
235 |
236 | 프로그래밍에서 BigO 표기법은 더 많다. 그 중 대표적인 예시를 보여준 것이다.
237 | (다른 표기법이 궁금한 사람은 [위키링크](https://en.wikipedia.org/wiki/Big_O_notation#Orders_of_common_functions) 를 참고)
238 | 몇몇 흔한 복잡도의 아이디어를 살펴보고 수학적으로 추론해보자.
239 |
240 | ----
241 |
242 | # 코드에서 BigO 를 결정하는 방법
243 | 이제 대략적으로 BigO가 무엇인지, 어떻게 결정되는지 알 것이다.
244 | 이제 당신의 프로젝트 안의 코드를 보고 복잡도를 알아내는 연습을 해보자.
245 | 충분한 연습을 거치면 감이 생길 것이다.
246 |
247 |
248 |
249 | 코드의 성능을 말하는 간단한 방법은 함수 안에 있는 `for` 문의 갯수를 살펴보는 것이다:
250 | ```swift
251 | // 2-1
252 | func printAll(from items: [T]) {
253 | for item in items {
254 | print(item)
255 | }
256 | }
257 | ```
258 |
259 | 이 코드는 `O(n)` 이다.
260 | 함수 안에 하나의 `for` 문을 가지고 잇고, 중간에 이탈하는 일(break) 없이 주어진 입력값에 해당하는 모든 원소를 순회한다.
261 | 이 함수는 선형적으로 성능이 증가함이 명확하다.
262 |
263 |
264 |
265 | `O(1)` 의 복잡도를 갖는 경우는 다음과 같다:
266 | ```swift
267 | // 2-2
268 | func printFirst(_ items: [T]) {
269 | print(items.first)
270 | }
271 | ```
272 |
273 | 반복문이 없고, 한개의 `print`문만 존재한다.
274 | `[T]` 안에 얼마나 많은 원소가 들어있든 상관없이 항상 같은 실행 시간을 가질 것이다.
275 |
276 |
277 |
278 |
279 | 조금 더 까다로운 예제를 보자:
280 | ```swift
281 | // 2-3
282 | func doubleLoop(over items: [T]) {
283 | for item in items {
284 | print("loop 1: \(item)")
285 | }
286 |
287 | for item in items {
288 | print("loop 2: \(item)")
289 | }
290 | }
291 | ```
292 |
293 | 여기에 두 개의 반복문이 있다.
294 | 그렇기에 위의 `1-3`예제를 바탕으로 당신은 `O(n^2)` 이라고 생각하 수 있다.
295 |
296 | 하지만 이 경우는 좀 다르다. `1-3`의 경우는 중첩된(nested) 반복문이다.
297 | 반복문 바깥에서 같은 데이터들에 대해 한번 더 반복문을 실행한다.
298 |
299 |
300 |
301 | `2-3`의 경우,
302 | 각각의 반복문이 따로 떨어져 있다.
303 | 이것은 배열안의 원소의 갯수의 두 배만큼 실행시간이 소요된다는 의미이다. 제곱이 아니라!
304 |
305 |
306 |
307 | 따라서 복잡도는 `O(2n)` 이다.
308 |
309 | 상수 배수를 갖는 복잡도는 종종 `O(n)`과 같이 축약해서 표현한다.
310 | 결국은 선형적으로 증가하는 성능을 갖기 때문이다.
311 | 성능을 나타낼 때 데이터가 몇번 반복되는 지는 상관없고 알 필요 없다.
312 |
313 |
314 |
315 | [Cracking the Coding Interview](https://www.crackingthecodinginterview.com/) 에 나온 반복문 예제를 한번 살펴보자:
316 | ```swift
317 | func printPairs(for integers: [Int]) {
318 | for (idx, i) in integers.enumerated() {
319 | for j in integers[idx...] {
320 | print((i, j))
321 | }
322 | }
323 | }
324 | ```
325 |
326 | 위의 코드는 중첩된 반복문을 포함하고 있다, 그래서 딱 봤을 때 O(n^2) 처럼 보인다.
327 | 하지만 자세히 봐보아라.
328 |
329 | 이 중첩된 반복문은 전체 데이터를 순회하지 않는다.
330 | 대신! 그 일부 원소들을 순회한다.
331 |
332 | 외부 반복문이 진행될수록 내부 반복문의 순회할 원소의 수는 감소한다.
333 | 입력값이 `[1, 2, 3]` 일때 외부 반복문이 한바퀴 돌 때마다 줄바꿈을 하여 표현해본다면:
334 | ```
335 | (1, 1) (1, 2) (1, 3)
336 | (2, 2) (2, 3)
337 | (3, 3)
338 | ```
339 |
340 |
341 |
342 | 원소를 하나 추가하여 `[1, 2, 3, 4]` 인 경우는:
343 | ```
344 | (1, 1) (1, 2) (1, 3) (1, 4)
345 | (2, 2) (2, 3) (2, 4)
346 | (3, 3) (3, 4)
347 | (4, 4)
348 |
349 | ```
350 |
351 |
352 |
353 | 이를 바탕으로 보면, 우리는 외부 반복문은 n번 실행됨을 알 수 있다.
354 | - 배열 안의 원소 수만큼 선형적으로 이루어지니까.
355 |
356 | 내부 반복문은 외부 반복문이 실행될 때마다 평균적으로 약 n의 절반을 실행된다.
357 | - 처음에는 n번, 다음은 n-1, 다음은 n-2 ...
358 |
359 | 이를 연산해보면 O(n * n / 2) 니까, O(n^2 / 2) 로 볼 수 있다.
360 | 이 또한 상수배를 생략하여 간단히 표현하보면 O(n^2) 이 된다.
361 |
362 |
363 |
364 | 중첩된 반복문을 보고 O(n^2) 이라고 즉각적으로 단정짓기 보다는, 왜 그런 결론이 나는지 추론하고 이해하는 과정이 중요하다.
365 |
366 | 이와 관련된 좋은 [WWDC 2018 영상](https://developer.apple.com/videos/play/wwdc2018/223/)이 있다. 관심 있다면 참고하라.
367 |
368 |
369 |
370 |
371 |
372 |
373 | ----
374 |
375 | > 참고링크
376 | - [시간복잡도 표기 예 - 정보통신기술용어해설](http://www.ktword.co.kr/test/view/view.php?m_temp1=6146)
377 | - [introduction to Big O](https://www.donnywals.com/an-introduction-to-big-o-in-swift/)
378 |
--------------------------------------------------------------------------------
/Swift/hashable_equatable_comparable.md:
--------------------------------------------------------------------------------
1 | # hashable, equatable, comparable
2 |
3 | ## 공식문서
4 | - [Hashable](https://developer.apple.com/documentation/swift/hashable)
5 | - [hash(into:)](https://developer.apple.com/documentation/swift/hashable/hash(into:))
6 | - [Equatable](https://developer.apple.com/documentation/swift/equatable)
7 | - [Comparable](https://developer.apple.com/documentation/swift/comparable)
8 |
9 |
10 | ## Hashable
11 | > `Protocol` Hashable
12 | > : A type that can be hashed into a Hasher to produce an integer hash value.
13 |
14 | ```swift
15 | protocol Hashable: Equatable
16 | ```
17 |
18 | - 간단히 말하면 정수 hash값을 제공하는 타입이다.
19 | - Set, Dictionary 의 key값으로 `Hashable`을 준수하는 모든 타입을 사용할 수 있다.
20 | - Swift의 `Dictionary` 의 형태로 사용된다.
21 | - 이때 반드시 KeyType은 `Hashable` 타입이어야 한다. (= 그 자체로 유일함을 나타낼 수 있어야 한다.)
22 |
23 | ### Hashable을 준수하는 Swift의 타입
24 | - Stirng, Integer, Float, Bool, Set
25 | - associated value 없는 enum
26 | - associated value를 갖는 경우 모두 Hashable을 준수하도록 정의해야 한다.
27 | - 구조체에서 정의한 모든 저장 프로퍼티들
28 |
29 | ### 예시
30 | 공식 문서속 예시를 보자.
31 |
32 | - grid 버튼들 안에서 특정한 한 위치를 묘사하는 GridPoint 라는 타입이 있다.
33 | - 아래와 같이 정의되어있다.
34 | ```swift
35 | // x-y 좌표 시스템 안의 한 점
36 | struct GridPoint {
37 | var x: Int
38 | var y: Int
39 | }
40 | ```
41 |
42 |
43 |
44 | 사용자가 이미 누른(tapped) 지점들의 Set을 나타내고자 한다.
45 | GridPoint 타입은 아직 hashable 하지 않다.
46 |
47 | 아직 Set에 사용할 수 없다.
48 | Set 안에 담아주려면 우선 Hashable을 준수해야하는데,
49 | 준수하도록 하려면 == 연산자 함수를 제공해야 하고 hash(into:) 메서드를 적용해야 한다.
50 | ```swift
51 | extension GridPoint: Hashable {
52 | static func == (lhs: GridPoint, rhs: GridPoint) -> Bool {
53 | return lhs.x == rhs.x && lhs.y == rhs.y
54 | }
55 |
56 | func hash(into hasher: inout Hasher) {
57 | hasher.combine(x)
58 | hasher.combine(y)
59 | }
60 | }
61 | ```
62 |
63 |
64 |
65 | hash(into:) 메서드는 gridPoint의 x,y 프로퍼티를 hasher로 제공하도록 한다.
66 | 이 프로퍼티들은 == 연산자 함수 안에서 동등함(equality)을 테스트하는 데 사용되는 것과 같다.
67 |
68 | 이제 GridPoint는 Hashable 프로토콜을 준수하고 있으므로, 이전에 사용자가 눌렀던 grid point들의 Set를 만들 수 있다.
69 | ```swift
70 | var tappedPoints: Set = [GridPoint(x: 2, y: 3), GridPoint(x: 4, y: 1)]
71 | let nextTap = GridPoint(x: 0, y: 1)
72 |
73 | if tappedPoints.contains(nextTap) {
74 | print("Already tapped at (\(nextTap.x), \(nextTap.y)).")
75 | } else {
76 | tappedPoints.insert(nextTap)
77 | print("New tap detected at (\(nextTap.x), \(nextTap.y)).")
78 | }
79 | // Prints "New tap detected at (0, 1).")
80 | ```
81 |
82 |
83 | ## Equatable
84 | > `Protocol` Equatable
85 | > : A type that can be compared for value equality.
86 |
87 | ```swift
88 | protocol Equatable
89 | ```
90 |
91 | `Equatable`을 준수하는 타입들은 동일여부를 `==` 연산자를 통해 비교할 수 있다.
92 | - `!=` 연산자를 통해 같지 않음을 비교할 수도 있다.
93 |
94 | `Swift standard library` 안에 있는 대부분의 기본 타입들은 `Equatable`을 준수한다.
95 |
96 | sequence나 collection 연산을 할 때 Equatale을 준수함으로써 원소들을 비교하기 쉬워진다.
97 |
98 | - 예를 들어, 배열이 특정 원소를 가지 고 있는지 체크하고자할 때,
99 | - 동등성을 결정하기 위해 클로저를 제공하는 대신 배열의 원소가 Equatable 을 만족한다면 contains(_:) 메섣를 통해 자기 자신의 값을 넘겨줄 수 있다.
100 |
101 | 아래의 예제를 보면 contains(_:) 메서드가 문자열 배열에 어떻게 쓰이는지 확인할 수 있다.
102 | ```swift
103 | let student: [String] = ["Kofi", "Abena", "Efua", "Kweku", "Akosua"]
104 | let nameToCheck = "Kofi"
105 |
106 | if student.contains(nameToCheck) {
107 | print("\(nameToCheck) is signed up!")
108 | } else {
109 | print("No record of \(nameToCheck).")
110 | }
111 |
112 | // Prints "Kofi is signed up!"
113 | ```
114 |
115 | ### Equatable을 준수하는 방법
116 | `Equatable`을 당신의 커스텀 타입에 준수시킴으로써 당신은 collection에서 특정 instance를 찾고자할 때 더 편리한 APIs를 사용할 수 있다.
117 | `Equatable`은 또한 `Hashable`과 `Comparable` 프로토콜을 기반으로 하고 있어서,
118 | - 커스텀 타입에 sets를 만들거나 collection의 원소들을 정렬하는 것과 같은 사용성을 준다.
119 |
120 | 타입의 기존 선언 안에 `Equatable` 을 만족하도록 선언하고 다음의 기준을 만족할 때 커스텀 타입에 대한 `Equatable` 프로토콜의 기능을 사용할 수 있다:
121 | - Struct의 경우,
122 | - 해당 struct안의 모든 `stored proeprty`(저장 프로퍼티)들은 `Equatable`을 준수해야만 한다.
123 | - Enum의 경우,
124 | - 모든 `associated value`(연관값)들은 `Equatable`을 준수해야 한다.
125 | - (`associated value`가 없는 enum은 선언 없이도 `Equatable`을 준수한다.)
126 |
127 | `Equatable` 만족하는 타입을 커스텀 하기 위해서는,
128 | - 위에 언급되지 않은 타입에 `Equatable`을 적용하거나
129 | - `Equatable`을 만족하는 기존에 존재하는 타입을 확장(extend)
130 | - `==`라는 동등함을 비교하는 static 메서드 만든다.
131 | 하는 방법이 있다.
132 |
133 | standard library는 `Equatable` 타입에 `==` 함수의 반대의 결과를 내뱉는 `!=` 연산자를 제공한다.
134 |
135 | 예제를 한번 보자.
136 | `StreetAddress` 클래스는 building number, street name, 그리고 옵셔널인 unit number 를 가지고 있다.
137 | 이 클래스 타입을 정의하는 초기화 선언부는 다음과 같다:
138 | ```swift
139 | class StreetAddress {
140 | let number: String
141 | let street: String
142 | let unit: String?
143 |
144 | init(_ number: String, _ street: String, unit: String? = nil) {
145 | self.number = number
146 | self.street = street
147 | self.unit = unit
148 | }
149 | }
150 | ```
151 |
152 | 이제 당신은 address의 배열을 가지고 있다는 가정하에, 당신은 특정 주소가 맞는기 확인하려고 한다.
153 | `containts(_:)` 메서드를 사용하기 위해서 closure를 사용하여 각각의 원소를 부르는 방법, `StreetAddress` 타입이 `Equatable`을 준수하도록 확장해주는 방법이 있다.
154 | ```swift
155 | extension StreetAddress: Equatable {
156 | static func ==(lhs: StreetAddress, rhs: StreetAddress) -> Bool {
157 | return lhs.number == rhs.number &&
158 | lhs.street == rhs.street &&
159 | lhs.unit == rhs.unit
160 | }
161 | }
162 | ```
163 |
164 | 이제 `StreetAddress` 타입은 `Equatable`을 준수한다.
165 | 당신은 `==` 을사용하여 두 인스턴스 사이의 동등성을 확인할 수 있고,
166 | 또는 `constains(_:)` 메서드를 사용하여 해당 인스턴스를 가지고 있는지 판별할 수 있다.
167 |
168 | ```swift
169 | let addresses = [
170 | StreetAddress("1490", "Grove Street"),
171 | StreetAddress("2119", "Maple Avenue"),
172 | StreetAddress("1400", "16th Street")]
173 | let home = StreetAddress("1400", "16th Street")
174 |
175 | print(addresses[0] == home) // false
176 | print(addresses.contains(home)) // true
177 | ```
178 |
179 | 동등함(`Equality`)은 대채가능함(`Substitutability`)을 암시한다.
180 | - 어느 동등함을 비교할 수 있는 두 인스턴스는 값에 따라 달라지는 모든 코드에서 서로 대체하여 사용이 가능하다.
181 |
182 | 대체가능함을 유지하기 위해 `==` 연산자는 `Equatable`의 모든 가시적 측면을 고려해야 한다.
183 | `Class`타입 이외의 `Equatable` 타입의 비가치적 측면을 노출하는 것은 권장되지 않으며, 노출되는 모든 것은 문서에서 명시적으로 지적해야 한다.
184 | ```
185 | Q. 여기서 비가치적 측면이란 무엇일까?
186 | ```
187 |
188 | Equatable 타이브이 인스턴스 사이의 동등성은 등가관계이므로, Equatable을 준수하는 어느 커스텀 타입도 아래의 3가지 조건을 만족해야 한다:
189 | - a, b, c 라는 어떤 값이 있다고 하자.
190 | - `a == a` 는 항상 참(true)이다. (= Reflexivity)
191 | - `a == b` 는 `b == a` 임을 암시한다 (= Symmetry)
192 | - `a == b && b == c` 는 `a == c` 임을 암시한다. (= Transitivity)
193 |
194 | 게다가, 비동등함(inequality)은 동등함(equality)의 반댓말 이다.
195 | 그래서 `!=` 연산자에 대한 어느 커스텀도 반드시 `a != b` 는 `!(a == b)` 을 암시함을 보장해야 한다.
196 |
197 | `!=` 연산자 함수가 이 조건을 만족하는 것은 기본(default) 적용이다.
198 |
199 |
200 |
201 | ## Comparable
202 | > `Protocol` Comparable
203 | > : A type that can be compared using the relational operators <, <=, >=, and >.
204 |
205 | ```swift
206 | protocol Comparable: Equatable
207 | ```
208 |
209 |
210 |
211 | `Comparable` 프로토콜은 숫자나 문자열과 같이 고유한 순서를 가진 타입에 사용된다.
212 | 표준 라이브러리의 많은 타입이 이미 `Comparable` 프로토콜을 준수한다.
213 | 관계 연산자(relational operators)를 사용하여 인스턴스를 비교하거나 비교 가능한 유형을 위해 설계된 표준 라이브러리 메서드를 사용하려면 사용자 정의 타입에 `Comparable` 을 준수하도록 추가해주어야 한다.
214 |
215 |
216 |
217 | 관계 연산자의 가장 익숙한 사용법은 숫자를 비교하는 것이다. 예를 들면:
218 | ```swift
219 | let currentTemp = 73
220 |
221 | if currentTemp ?= 90 {
222 | print("It's a scorcher!")
223 | } else if currentTemp < 65 {
224 | print("Might need a sweater today.")
225 | } else {
226 | print("Seems like picnic weather!")
227 | }
228 |
229 | // Prints "Seems like picnic weather!"
230 | ```
231 |
232 |
233 |
234 | `Comparable` 타입으로 작업할 때 일부 sequence, collection의 특수한 버전을 사용할 수 있다.
235 | 예를 들어, 배열의 요소가 `Comparable`을 준수할 경우 인자를 사용하지 않고 `sort()` 메서드를 호출하여 배열의 요소를 오름차순으로 정렬할 수 있다.
236 | ```swift
237 | var measurements = [1.1, 1.5, 2.9, 1.2, 1.5, 1.3, 1.2]
238 | measurements.sort()
239 | print(measurements)
240 |
241 | // Prints "[1.1, 1.2, 1.2, 1.3, 1.5, 1.5, 2.9]"
242 | ```
243 |
244 |
245 |
246 | ## Comparable 프로토콜을 준수하는 방법
247 | `Comparable` 을 준수하는 타입은 less-than 연산자(`<`), equal-to 연산자(`==`) 를 적용할 수 있다.
248 | 이 두 연산은 타입의 값에 엄격한 전체적인 순서를 부여하고, `a`와 `b` 두 값에 대해 다음 중 하나가 `true` 여야 한다.
249 | - a == b
250 | - a < b
251 | - a > b
252 |
253 |
254 |
255 | 또한 아래의 조건이 충족되어야 한다:
256 | - `a < a` 는 항상 false 다. (= Irrefelxivity)
257 | - `a < b` 는 `!(b < a)` 를 암시한다. (= Asymmetry)
258 | - `a < b` 그리고 `b > c` 는 `a < c` 를 암시한다. (= Transitivity)
259 |
260 |
261 |
262 | 사용자 정의 타입에 `Comparable`을 추가하려면 `<` 및 `==` 연산자를 `static` 메서드로 정의해야 한다.
263 | `==` 연산자는 `Comparable`이 준수하는 `Equatable` 프로토콜의 요구사항이다.
264 | - (Swift의 동등성에 대한 자세한 내용은 위의 `Equatable` 내용을 참고.)
265 |
266 |
267 |
268 | 나머지 관계 연산자의 기본 구현은 표준 라이브러리에 의해 제공되므로,
269 | 추가 코드 없이 사용자 타입의 인스턴스와 함께 `!=`, `>`, `<=`, `>=`를 사용할 수 있다.
270 |
271 |
272 |
273 | 아래의 예를 보자.
274 | 여기의 Date 구조체는 날짜의 year, month, day 정보를 담고 있다:
275 | ```swift
276 | struct Date {
277 | let year: Int
278 | let month: Int
279 | let day: Int
280 | }
281 | ```
282 |
283 |
284 |
285 | `Comparable`을 `Date` 타입에 준수시키려면, 일단 `Comparable`을 준수하도록 선언하고 `<` 연산자 함수를 작성해야 한다:
286 | ```swift
287 | extension Date: Comparable { // 프로토콜 준수
288 | static func < (lhs: Date, rhs: Date) -> Bool { // 연산자 함수 추가
289 | if lhs.year != rhs.year {
290 | return lhs.year < rhs.year
291 | } else if lhs.month != rhs.month {
292 | return lhs.month < rhs.month
293 | } else {
294 | return lhs.day < rhs.day
295 | }
296 | }
297 | ```
298 |
299 |
300 |
301 | 이 함수는 날짜의 최소 특정 비일치 속성(least specific nonmatching property)를 사용하여 비교 결과를 결정한다.
302 | - 비교를 할 때, 여러개의 다른 속성이 존재한다면 크고 작음을 나타내는 기준이 모호하다.
303 | - 최소한의 특정 속성을 활용하여 크고 작음을 비교할 수 있도록 나만의 비교 연산자를 정의하는 것이다.
304 | - 여러 속성을 활용해야하는 경우, 속성 간의 우선순위를 정한다.
305 |
306 | 예를 들어, 두 인스턴스의 `year` 속성은 같아도 `month` 속성이 동일하지 않은 경우 월 값이 작은 날짜는 두 날짜 중 작은 날짜가 된다.
307 | ```swift
308 | Date(year: 2022, month: 12, day: 20) // a
309 | Date(year: 2022, month: 11, day: 30) // b
310 |
311 | // a와 b를 비교하면 year는 2022로 같고, month는 다르다.
312 | // a의 month가 더 큰 수 이므로 두 인스턴스를 비교시 a > b 이다.
313 | ```
314 |
315 |
316 |
317 | 다음은 `==` 연산자 함수를 적용하는 것이다.
318 | 이는 `Equatable` 프로토콜을 준수하기 위한 필수 요구조건이다.
319 | ```swift
320 | ...
321 |
322 | static func == (lhs: Date, rhs: Date) -> Bool {
323 | return lhs.year == rhs.year && lhs.month == rhs.month
324 | && lhs.day == rhs.day
325 | }
326 | }
327 | ```
328 |
329 | 두 `Date` 인스턴스는 각각의 속성이 같으면 동일하다고 정의하였다.
330 |
331 |
332 |
333 | 이제 `Date`가 `Comparable`을 준수하므로, 이 타입의 인스턴스는 어떠한 관계 연산자로도 비교가 가능하다.
334 | 비교 예시를 하나 들어보겠다:
335 | ```swift
336 | let myBirthday = Date(year: 1996, month: 12, day: 13) // Dec 13, 1996
337 | let today = Date(year: 2022, month: 12, day: 28) // Dec 28, 2022
338 |
339 | if myBirthday > today {
340 | print("당신은 아직 생일이 아닙니다.")
341 | } else {
342 | print("올해 당신의 생일은 지났습니다! 한 살 더 먹은 것을 축하합니다 ^^")
343 | }
344 | // Prints "올해 당신의 생일은 지났습니다! 한 살 더 먹은 것을 축하합니다 ^^"
345 | ```
346 |
347 |
348 |
349 | 이 예시에서 사용된 표준 라이브러리에 의해 제공되는 `>` 연산자는 위에서 정의하고 적용한 `<` 연산자가 아니라는 점 참고하자.
350 | ```
351 | Comparable을 준수하는 타입은 예외로 취급하는 값의 하위 집합,
352 | 즉 Comparable 프로토콜의 목적을 위해 의미 있는 인자의 영역 밖에 있는 값을 포함할 수도 있다.
353 |
354 | 예를 들어,
355 | 부동 소수 타입 (Floating Point nan)에 대한 특수값인 'not a number' 값은 정규 부동 소수점 값보다 작거나 크지 않다.
356 | 예외 값이 엄격한 전체적인 순서에 포함될 필요는 없다. (즉, 크고 작음을 비교할 수 없다.)
357 | ```
--------------------------------------------------------------------------------