├── 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 | 스크린샷 2022-04-18 오전 11 34 54 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 | ![선택정렬](https://user-images.githubusercontent.com/59866819/170263121-f94d8b4d-d890-4fec-b9d0-862c836ea06a.gif) 10 | 11 | ## 2. 삽입정렬 (Insertion Sort) 12 | - 데이터 집합을 순회하면서 정렬이 필요한 요소롤 뽑아내어 이를 다시 적당한곳으로 삽입하는 알고리즘 13 | - 성능은 버블정렬보다 좋다 14 | 15 | ### 시간복잡도 16 | - 최악, 평균 : `O(N^2)` 17 | - 최선 : `O(N)` 18 | - 이미 정렬되어 있는 경우 19 | 20 | ![삽입정렬](https://user-images.githubusercontent.com/59866819/170263125-6fd5696c-4ce2-48fb-b05f-38a48ce29f20.gif) 21 | 22 | 23 | ## 3. 버블정렬(Bubble Sort) 24 | - 거품이 수면으로 올라오는 듯 하여 붙여진 버블정렬 25 | - 인접한 두 수를 비교하여 오름차순 or 내림차순으로 정렬 26 | 27 | ### 시간복잡도 28 | - 최선, 평균, 최악 모두 `O(N^2)` 29 | 30 | ![버블정렬](https://user-images.githubusercontent.com/59866819/170263131-712f5476-1d83-4077-a203-ec6cb47af0d3.gif) 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 | ![퀵정렬](https://user-images.githubusercontent.com/59866819/170263137-8cfa2bf7-e5e3-40aa-b4a6-2196bd1ee3d8.gif) 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 | ![presented_presenting 001](https://user-images.githubusercontent.com/59866819/179002674-e0e5c5f5-77a6-4b1b-a223-b8ba3b36e6eb.png) 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 | ![what_is_thread_1](https://user-images.githubusercontent.com/59866819/154971501-e7be4cce-5339-40ee-886a-7c770f51bf54.png) 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 | all 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 | ``` --------------------------------------------------------------------------------