장인 정신을 익히기 위해서는 2단계가 필요하다.

  1. 이론
  2. 실전

이론 : 필요한 원칠, 패턴, 기법, 경험이라는 지식을 습득해야 한다.

실전 : 열심히 일하고 연습하여 지식을 나의 것으로 만든다.

ex) 자전거 타기 : 물리적인 식을 통해 자전거를 '처음타는 사람도 자전거를 탈 수 있다' 라는 식은 설명이 가능하다.
해당 식을 증명하면 그 사람은 자전거를 탈 수 있다는 것이 된다.

하지만 자전거를 처음 타는 사람은 100%(내 생각은 99%, 안넘어지는 사람도 있긴 있다...) 넘어진다.

깨끗한 코드를 작성하는 방법은 배우기 어렵다.
이론적으로 모든 것을 안다고 해서 깨끗한 코드가 나오기는 어렵다. 고생을 해야 한다.

책 자체는 3부분으로 나뉜다.

  1. 깨끗한 코드를 작성하는 원칙, 패턴, 실기를 설명
  2. 사례 연구를 알아보며 코드 복잡도가 증가한다. -> 코드를 깨끗하게 고치는 연습
  3. 수집한 휴리스틱을 마지막 장에서 열거한다.

'스터디 > 클린코드' 카테고리의 다른 글

[Clean Code] 클린 코드 - 5  (0) 2021.03.08
[Clean Code] 클린 코드 - 4  (0) 2021.03.08
[Clean Code] 클린 코드 - 3  (0) 2021.03.08
[Clean Code] 클린 코드 - 2  (0) 2021.03.08
[Clean Code] 클린 코드 - 1  (0) 2021.03.08

문제 상황

Header(최근 검색어 Label)

Footer(전체삭제, 최근 검색 닫기 Button)

스크롤을 하게 되면 저것들이 따라온다...

 

해결방법

1. StoryBoard or Nib

테이블 뷰의 Inspector에서 Style을 Plain에서 Group으로 변경해준다

 

더이상 따라오지 않는 것을 볼 수 있다

 

해결 상황

 

2. Frame 사용

//Frame은 임시
myTableView = UITableView(frame: .zero, style: .grouped)

 

해결!

방법들

Data.init(contentsOf url: URL)

기본적으로 네트워크를 통해 이미지를 가져오기 위해 사용했던 첫번째 방식이었다.

그 당시, DispatchQueue를 사용하여 UI는 Main Thread에서 변경했지만, 이미지를 가져오기위한 작업은 저 Data(contentsOf:)를 사용했다.

간단한 코드였으며, CollectionView나 TableView처럼 많은 이미지를 가져온다거나 이미지의 크기가 크지 않기에 앱이 죽지 않고 돌아갔지 싶다.

 

쩃든, 네트워크 통신을 통한 파일/이미지를 받을 때는 해당 방법을 사용하면 안된다.

Important
Don't use this synchronous initializer to request network-based URLs. For network-based URLs, this method can block the current thread for tens of seconds on a slow network, resulting in a poor user experience, and in iOS, may cause your app to be terminated.
Instead, for non-file URLs, consider using the dataTask(with:completionHandler:) method of the URLSession class. See Fetching Website Data into Memory for an example.

첫줄에서도 나와있다시피 이 init은 synchronous하다 -> 동기방식으로 동작한다.

만약, 해당 코드를 Main Thread에서 수행한다면, 응답이 올때까지 Thread는 멈추게된다. (안드에서는 이러한 상황을 ANR이라 했던 것 같은데, iOS에서는 어떻게 부르는지 모르겠다)

따라서 아래에 적혀있는 것처럼 URLSession Class에 있는 dataTask 메소드 사용을 권장한다.

이미지를 다운로드하는 상황에는 downloadTask도 있다.

 

URLSession - downloadTask

  • HTTP / HTTPS 를 통해 데이터를 주고받기 위해 API를 제공해주는 클래스

Session 과 Task 등 자세한 이야기는 이후의 포스팅에서 다루겠다.

 

downloadTask

서버로부터 데이터를 다운로드 받아서 파일의 형태로 저장한다.

 

1. URL을 사용하는 방법 -> URL을 알고 있는 상황에서 사용

2. URLRequest를 사용하는 방법-> Query, HTTP메소드, 헤더 등 설정이 필요한 경우 사용

 

//4개의 메소드가 존재
downloadTask(with url: URL) -> URLSessionDownloadTask

downloadTask(with url: URL, completionHandler: @escaping(URL?, URLResponse?, Error?) -> Void)

downloadTask(with request: URLRequest) -> URLSessionDownloadTask

downloadTask(with request: URLRequest, completionHandler: @escaping(URL?, URLResponse?, Error?) -> Void)

 

URLSession.shared.downloadTask(with: imageURL){ url, response, error in
	.........
    
    if let url = url{
    	let image = UIImage(contentsOfFile: url){
        	DisptchQueue.main.async{
            	......
            }
        }
    }
    
    .........
}.resume()

 

위와 같은 코드로 동작시킬 수 있다.

이미지를 계속해서 불러오는 것은 비효율적일 수 있다. 따라서 Cache(캐시)를 사용할 수 있는데

FileManager를 사용하면 캐시를 구현할 수 있다.

 

Kingfisher (https://github.com/onevcat/Kingfisher)

캐시 전략, 코드의 길이, 사용법 등 번거롭고 귀찮음을 해결하기 위해 나온 라이브러리이다.

 

  • 비동기 이미지 다운로드 및 캐싱을 위해 사용
  • URLSession 기반이기 떄문에, 네크워킹 및 로컬 데이터도 로드 가능
  • 유용한 이미지 프로세서 및 필터가 제공됨
  • 메모리와 디스크의 계층 하이브리드 캐시
  • 성능 향상을 위해 이전에 다운로드한 콘텐츠를 취소 가능, 재사용 가능
  • Indicator
  • SwiftUI 지원
  • GIF 형식 지원

 

//정말 쉬웠다

imgView.kf.setImage(with: imageURL)
//끝...
//비동기로 동작한다

 

오늘 처음 써보며... 오픈소스의 대단함을 새로 느꼈다. 이미지를 불러오는 데에 조금씩 버벅이길래 성능 향상을 위해 Disk에 Cache도 구현하고 CollectionView의 Prefetching도 사용해보았지만 눈에 띄게 향상되진 않았었다.

그런데 KingFisher를 사용하니 눈에 띄게 성능이 향상된 것을 볼 수 있었다. -> 코드를 까보면서 뭐가 다른건지 확인해봐야겠다

 

Processor

이미지를 좀더 유연하게 만들어주는 역할을 한다.

setImage의 매개변수에는 Resource, Placeholder, options, progressBlock, completionHandler 등이 있다.

이중 options에 해당하며 없다면 default를 사용한다고 한다.

 

  • cornerRadius
  • Resizing
  • Blur
  • TintColor
  • Sampling

등의 기능을 제공하는 프로세서들을 사용할 수 있다.

 

또한, 싱글톤 객체를 이용해서 함수를 만들 수도 있다.

func downloadImage(with url: URL){
	let resource = ImageResource(downloadURL: url)
    
    KingfisherManager.shared.retrieveImage(with: resource){ result in
    	
        switch result{
        case .success(let value):
	        print("Image : \(value.image)")
        case .failure(let error):
        	print(error)
        }
    }
}

 

Cache

KingFisher가 알아서 메모리와 디스크에 캐시를 하고 전략 또한 정해져있는 것으로 알고 있다. -> (각 애플리케이션의 구조에 따라 캐시전략이 다를 수도 있는데, 이건 더 알아봐야겠다)

위에서 precessor를 넣을 수 있었던 options에 .cacheMemoryOnly 를 넣어주면 메모리캐싱만 수행하기된다. -> 메모리에만 캐싱하면 빠르긴하겠지만.... 굳이 좋을까...? 궁금증이 든다

캐시는 메소드를 통해 지울 수 있다.

 

 

이후에 보면 좋은 글 : 1consumption.github.io/posts/about-kingfisher(1)/

 

 

오토레이아웃

해당 뷰에 적용된 제약조건에 따라 뷰 계층 구조에 있는 모든 뷰의 크기와 위치를 동적으로 계산한다.

 

외부변경

SuperView의 크기나 모양이 변경되면 외부변경이 발생한다. 변경할 때마다 사용 가능한 공간을 최대한 활용하도록 레이아웃을 업데이트해야 한다.

 

내부변경

View의 크기 or UI 조작시 발생한다.

 

속성

SafeArea

애플리케이션이 상태바, 네비게이션바, 탭바 등을 가릴 수 없는 애플리케이션만의 영역이다.

iPhoneX에서부터 Notch(노치)가 생겨나며 생겨났다.

최대한 SafeArea를 이용해야 하며, SuperView를 사용하지 않는 편이 좋다. (동영상에서는 사용하는 것 같기는 한데, 가로모드와 세로모드의 차이때문인지 가이드가 다른 건지는 확실히 모르겠다...)

 

Constraint

뷰와 뷰 사이의 관계(하나의 뷰는 여러개의 뷰와 관계를 생성할 수 있다)를 정의하는 것이다.

위치는 방적식으로 나타낸다.

속성 설명
Width 너비
Height 높이
Top 상단
Bottom 하단
Baseline 텍스트 하단
Leading 텍스트 방향 시작
Trailing 텍스트 방향 끝
CenterX 수평 중심
CenterY 수직 중심

 

제약의 방정식 :

A.Leading = 1.0 * B.Trailing + 12.0 -> A의 Leading(텍스트방향의 시작위치)는 B의 Trailing(텍스트 방향의 끝위치)의 1.0배에 12.0을 더한 위치 -> (Interface Builder로 직접해보면 감이 쉽게 온다...)

 

Intrinsic Content Size

뷰가 원래 가지고 있었던 크기

 

Priority Constraint

제약 사이의 우선도

  • hugging Priority
    • 우선 순위(가로/세로)가 겹칠 때(정확한 판단이 어려울 때) 높은 우선순위의 Constraint를 적용
  • Compression Priority
    • 낮은 우선순위를 가진 제약(뷰)를 밀면서까지 높은 우선순위를 가진 제약을 지킴

Margin

레이아웃에 사용되는 간격

 

 

구현 방식

Programmatically

코드를 사용하여 AutoLayout을 적용한다.

iOS를 공부하는 초반에는 굉장히 어렵다. -> 내가 적용한 제약조건들이 적용되었을 때, 어떤 모양을 가질 지 상상이 가지 않기 때문

(분명 나는 사자를 그렸는데, 꼬리, 머리, 다리 위치가 뒤죽박죽인 상황...? 혹은 아예 없어지는 상황?)

 

하지만, 장점도 존재한다.

IB(Interface Builder)를 사용하는 것보다 훨씬 가독성이 좋아 협업에 적합하다.

-> IB로 제약사항을 작성 시, 변경/유지보수할 때, 하나하나 확인해야하며 굉장히 불편하다 즉, 자신 외에는 알아보는 데에 오래걸릴 수 있다.

(같은 View를 그리기 위해 다양한 방법이 존재하기 때문, 자신도 며칠 지나면 못 알아봄...)

BUT, 코드로 작성한다면, 누가 봐도 IB보다는 쉽고 빠르게 이해할 수 있을 것이다.

 

IB(Interface Builder)

XCode내에 내장된 IB를 사용하여 AutoLayout을 적용한다.

iOS를 공부하는 초반에 권장(이후에도 IB를 사용하여 개발을 진행한다고도 한다) -> 내가 적용한 제약조건들이 적용되는 것을 실시간으로 볼 수 있기 때문에, 보다 쉽게 View를 그릴 수 있다.

또한 IBOutlet, IBAction등을 사용하여 쉽게 UI Components와 Event를 동작/구현할 수 있음

 

 

+ Recent posts