개발 디자인패턴(Singleton, MVC)

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


디자인패턴이란?

개발에서 디자인 패턴이란, 다양한 개발 환경에서도 비슷한 문제가 발생할 수 있는데 이 비슷한 문제들을 해결하는 정형화된 해결책을 의미한다. 즉, 개발에서 비슷한 문제를 해결하는 일종의 템플릿이나 개발패턴을 말한다고 생각하면 쉽다. 이 패턴을 적용해 코드의 가독성, 효율성, 디버깅, 협업 등이 쉬워지게 만들수 있다.

Singleton 패턴

싱글턴 패턴은 어떤 클래스의 인스턴스가 단 하나만 존재하도록 강제하는 패턴을 의미한다. 앱 전체에서 해당 클래스의 인스턴스는 딱 하나만 만들어질 수 있게끔 설계하여 여러 객체들이 이를 참조하도록 하는 방식이다. 이는 데이터 자체가 하나의 원본으로 유지되어야 하거나, 인스턴스를 만드는 것이 리소스가 많이 드는 일이기 때문에 등 여러 이유로 사용되어지는 패턴이다.

UserDefault.standard  // 앱의 디폴트 설정상태를 정의, 저장하는 공간으로 하나의 앱에는 하나의 설정 인스턴스만 존재
UIApplication.shared  // 앱의 유저이벤트에 대응하는 라우팅을 담당하여 특성에 따라 하나의 인스턴스만 존재
UIScreen.main  // 유저가 들고있는 아이폰의 스크린자체를 의미하며 스크린이 하나니 하나의 인스턴스만 존재
FileManager.default  // 유저의 아이폰 하드드라이브에 저장된 파일들에 접근하는 것으로 하나의 인스턴스만 존재

싱글턴을 만들 때 가장 중요한 것은 인스턴스가 단 하나만 만들어질 수 있도록 확실하게 구조를 짜야한다는 것이다. 이를 위해서는 해당 인스턴스를 외부에서 새로 생성할 수 없도록 막아야 한다. init()을 private으로 선언해 외부에서 인스턴스를 만들 수 있는 여지를 완벽하게(원천적으로) 차단하는 방법이 있다.

class SingleTon {
  statie let shared = SingleTon()
  private init() {}
}

오직 타입프로퍼티인 shared를 통해서만 인스턴스를 활용할 수 있을 것이다.

MVC 구조 패턴

MVC구조(Model-View-Controller)는 프로그램을 짜는 대표적인 디자인패턴 중 하나이다. MVC의 Model에는 앱의 비즈니스 로직이나 데이터를 담당하는 코드를 담아놓고, View에는 사용자에게 보여지는 모든 객체들을 다루는 코드를 담아놓으며, Controller에는 Model과 View 사이 중간자 역할을 하는 코드를 담아놓는다. 이러한 패턴을 MVC 패턴이라고 칭한다.

이렇게 코드를 MVC 패턴으로 짜는 이유는 Model과 View 코드들을 각각 분리함으로써 범용성과 재사용성을 높이고, 각각의 역할을 분리하여 개발과 유지보수를 쉽게 하기 위함이다. Model과 View 코드는 분리되어야 하지 않는데 해당 코드들이 섞이기 시작한다면 해당 코드들의 범용성이 떨어져 다른곳에 재사용도 불가능할 뿐더러 코드 구조가 꼬여버려 분석이나 디버깅 및 확장이 어려운 코드가 되어버릴 위험이 커진다.

Model

앱의 비즈니스 로직과 데이터를 관장하는 영역이다. 앱의 두뇌역을 하는 것으로 생각하면 쉬는데 계산기앱이라면 계산하는 로직과 관련된 데이터가, 채팅앱이라면 채팅 기능 관련 로직과 데이터가 Model 영역에 포함되게 된다. 서버로부터 데이터를 불러오거나 데이터 정합성을 관리하는 것도 Model의 역할이다.

View

UI. 유저에게 데이터를 보여주거나, 유저 인터렉션(UI)을 담당하는 영역이다. UI와 UI로직을 담당한다고 보면 된다. ‘어떻게’ 보여질지에 대해서만 관장하며 어떤 것을 보여줄 지에 대해서는 알지 못한다. 단순히 보여지는 영역을 어떻게 어떤 모양으로 보여줄지만 담겨있지 그 안의 세부적인 데이터나 무엇이 넘어오는지에 대해서는 View에서 알 수 없음을 의미한다. 즉, 자신의 Controller가 누군지, 어떤 데이터가 보여지는 지 등에 대해서는 알 수 없다.

Controller

View와 Model을 이어주는 역할을 담당한다. View와 Model은 서로 독립적이어야 하기에 중간에서 Controller가 중간다리 역할을 해주게 된다. Controller는 View와 Model 사시에서 자유로이 소통을 하지만 반대로 View와 Model은 자신에게 연결되어있는 Controller가 누군지 알 수가 없다.


Controller to View: Outlet

Controller가 View에게 말을 걸기 위해서는 outlet 을 사용한다. 스토리보드에서 코드로 레이블이나 테이블뷰를 당겨오면 IBOutlet이 생기게 되는데 이게 바로 Controller가 View에게 말을 걸기 위해 해당 레이블이나 테이블 뷰 등의 인스턴스를 만드는 것을 의미한다. 스토리보드에서 코드영역으로 드래그를 하기에 View가 Controller에게 말을 거는 것처럼 보이겠지만 사실 그 반대이다. IB는 Interface Builder의 약자로 IBOutlet을 통해 Controller는 View에게 ‘너가 보여줄 이미지는 이것이다’ 등과 같은 구체적인 지시를 하는 것으로 이해해보자.

@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var ageTextField: UITextField!

View to Controller

IBAction

View는 Controller의 꼭두각시일 뿐이지만 때에 따라 View가 Controller에게 말을 걸 수도 있다. 이는 슈퍼갑갑갑갑인 사용자가 View를 통해 메시지를 던질 수도 있기 때문이다. 간단하게 버튼을 생각하면 되는데 사용자가 바라보면 뷰에서 어떤 버튼을 눌러 이루어지는 메시지가 있을때 해당 버튼이 해야하는 일을 정해줘야 한다.

이를 처리하는 방법 중 하나가 Target-Action이다. Controller가 자신에게 과녁(Target)을 설정해놓고 View에게 그 과녁에 쏠 화살(Action)들을 건네주는 벙법이다.

@IBAction func nextBtn() {
  let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
  let vc: SecondViewController = storyboard.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController

  self.navigationController?.pushViewController(vc, animated: true)
}

버튼이 눌러져 Action이 발생하면 본 Controller의 nextBtn() 메서드가 Target으로 대응하게 될 것이다.

Delegation

두번째 방법은 Delegation Protocol을 사용하는 것이다. Controller class 안에 View class의 인스턴스를 만드는 IBOutlet처럼 View class 안에 Controller class를 만드는 것과 유사하다. 다만 class 대신 Protocol이 활용되는 것으로 Delegation은 View가 자신에 대한 제어권을 Controller에게 위임하는 protocol로서 앱을 사용하는 유저가 다음 5가지 행동을 하는 경우에 대한 권한이 위임된다.

  • will
  • did
  • should
  • data at
  • count

Controller to Model: 인스턴스

Model class의 인스턴스를 만든다.

Model to Controller: Notification 및 KVO

View가 실질적으로 Controller에게 말을 걸 수 없듯(다이렉트로) Model도 Controller에게 바로 말을 걸수는 없다. Model은 자신의 데이터가 변할때와 무엇인가 알려줄 필요가 있을 때, 라디오 스테이션처럼 자신의 주파수에 맞춘 Controller 및 Model들에게 관련 안내를 하는 구조로 교신을 하게 된다. 이러한 과정은 딕셔너리처럼 Key, Value를 관찰하는 Key-Value Observation(KVO)를 통해 수행되며 변경사항이 있을 경우 통지(Notification)가 가게 된다.

MVC의 단점

1. Massive View Controller

모델에 넣기도 애매하고 뷰에 넣기도 애매한 코드들은 모두 컨트롤러에 들어가게 되다보니 컨트롤러가 비대해지게 된다.

2. View와 Controller가 너무 친하다.

애플의 MVC 패턴은 기존 MVC패턴과는 조금 다르게 View와 Controller가 강하게 연결되어 있어 ViewController가 거의 모든일을 다 한다. ViewController에서는 Controller가 View의 라이프사이클에 관여하기 때문에 View와 Controller를 분리하기 어렵다. 그래서 앱을 테스트할 때 Model은 따로 분리되어 테스트 할 수 있어도 View와 Controller는 강하게 연결되어 있어 각각 테스트하기 어렵다고 한다.

참고한 블로그 참고한 블로그

iOS Application state(iOS 앱의 생명주기)

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


일반적으로 생명주기를 생각해보면 우리가 홈버튼을 누르거나, 전화가 왔을때와 같이 앱이 화면상에 보이지 않는 Background 상태, 화면에 앱이 올라와있는 Foreground 상태를 떠올릴 수 있다. 제대로 된 앱을 만들기 위해서는 이 생명주기를 정확히 이해하는 것이 중요하며, 이러한 생명주기를 이해하기 이전에 iOS앱이 시작할 때 일어나는 일들을 짧게나마 이해하는 것 또한 중요하다. » 참고할 글: View Controller의 생명주기

더 근본적으로 이야기해서 생명주기에 대해 간략하게 정리해보자면,

생명주기라는 것은 앱의 최초 실행부터 앱이 완전히 종료되기까지 앱이 가지는 상태와 그 상태들 사이의 전이를 뜻한다. 앱의 상태는 앱이 현재 어떠한 것을 할 수 있는 가를 결정한다. 예로 들어, Foreground 상태의 애플리케이션은 유저와 직접 상호작용하기 때문에 시스템 자원 사용 등에서 우선순위를 갖는다. 반면 Background 상태의 앱은 스크린에 드러나지 않기 때문에 작업을 하지 않거나, 하더라도 최소한의 작업만 수행해야 한다. 앱의 상태가 변하게 되면, 이 상태에 따라 앱의 행동을 조절해줘야 하는데, 이 조절작업을 하기 위해 UIKit는 특정 위임객체(delegate object)에 메시지를 보내주게 된다.

iOS 13이후에는 SceneDelegate를 사용할 수 있다. 이는 햐나의 앱이 여러개의 UI를 띄울 수 있게 되면서, 이 UI가 별도의 생명주기를 가질 필요가 생겼기 때문으로 기존 AppDelegate가 수행했던 생명주기 관리의 많은 부분이 SceneDelegate로 옮겨가게 되었다. 여기서 AppDelegate는 앱의 초기 구동과 앱 전체에 관련된 이벤트의 처리정도 만을 담당하도록 기능이 축소되었다.
iOS 12 이하의 경우에는 여전히 생명주기 관리에 AppDelegate를 사용하며, iOS 13 이상이더라도 Scene을 지원하지 않게 만들었다면 여전히 AppDelegate를 사용한다.

Scene을 지원하지 않는 경우, 모든 생명주기 관련 이벤트들은 appDelegate에 전달된다. appDelegate객체는 앱의 모든 window를 관리하기 때문에 앱의 상태 변화는 앱의 모든 UI에 영향을 미친다.

Application state

iOS에서 앱은 5가지의 상태로 구분이 가능하며 항상 하나의 상태를 가지고 있다.

  • Not running(Unattached): 앱이 실행되지 않았거나 시스템에서 종료됨
  • In-Active: 앱이 Foreground에서 실행중이지만, 이벤트를 받을 수 없음 > 이 상태에 잠시 머물다가 다른 상태로 전이됨
  • Active: 앱이 화면에 떠 있을 때의 상태 > 이벤트를 받을 수 있음
  • Background: 앱이 백그라운드에서 코드를 실행중. 대부분의 앱은 일시중지 상태로 잠시 이 상태가 된다. 그러나 추가 시간을 요청하는 앱은 일정기간 동안 이 상태로 남아있을 수 있다. 또한 백그라운드로 직접 실행되는 앱은 비활성 상태 대신 이 상태로 전환된다
  • Suspended: 앱이 백그라운드에 있지만 코드를 실행하지 않음. 시스템은 앱을 자동으로 이 상태로 이동시키고 그렇게 하기 전에 앱에 알리지 않는다. 일시 중지 된 앱은 메모리에 남아는 있지만 코드는 실행되지 않는다. 메모리 부족상태가 발생하면 시스템은 예공ㅂㅅ이 일시 중단 된 앱을 제거하여 메모리를 확보한다.

사용자나 시스템이 새로운 scene을 만들어달라고 요청하면, UIKit는 이를 만들어 unattached 상태로 만든다. 사용자가 요청한 scene은 곧장 Foreground inactive 상태를 거쳐 active 상태가 되고, 시스템이 요청한 scene은 보통 Background 상태가 되어 이벤트를 처리한 뒤에 Foreground로 올라온다. 사용자가 앱의 UI를 닫게 되면, 해당하는 scene은 UIKit에 의해 Background 상태로 갔다가 Suspended 상태가 된다. UIKit는 언제든지 이 Background나 Suspended 상태인 scene을 회수해 unattached 상태로 되돌려 놓을 수 있다.

View Controller Life Cycle

앱의 라이프사이클은 앱을 터치해서 실행시키고 완전히 종료되기 까지 아래 단계로 요약 해볼 수 있다.

  1. main 함수 실행
  2. main 함수는 UIApplicationMain 함수를 호출
  3. UIApplicationMain함수는 앱의 본체에 해당하는 객체인 UIApplication 객체를 생성
  4. nib 파일을 사용하는 경우나, Info.plist 파일을 읽어들여 파일에 기록된 정보를 참고해 그 외에 필요한 데이터를 로드
  5. App Delegate 객체를 만들고 앱 객체와 연결해 Main를 만드는 등 실행에 필요한 준비
  6. 실행 완료를 앞두고 앱 객체가 App Delegate에게 application:didFinishLaunchingWithOptions: 메시지를 보냄

상태전이

사용자의 동작에 따라 앱의 상태가 변경된다.

  1. 사용자가 앱을 실행한다: Not running » In-Active » Active
  2. 앱 실행 도중 홈버튼을 누른다: Active » In-Active » Background
  3. 앱을 다시 켠다: Background » Active
  4. 앱이 백그라운에 있다가 Suspended 상태로 전이: Active » In-Active » Background » Suspended

앱이 In-Active 상태가 되는 시나리오를 설명해보자

우선 일반적으로 이런경우는 흔하지 않다.

가장 흔한 예시는 우리가 네이버에서 기사를 읽고있을때 기사를 카카오톡으로 공유하려고 해보자.
이때 네이버 앱 위로 카카오톡 앱이 실행되는데, 이때 네이버 앱은 In-Active 상태가 되어진다고 생각하면 된다.

Notification Center와 Notification +예제

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


Notification

등록된 노티피케이션에 노티피케이션 센터를 통해 정보를 전달하기 위한 구조체  » 게임을 생각하면 쉬움(크아에서 물풍선에 갇히는 범위)

주요 프로퍼티

// name : 알림을 식별하는 태그
var name: Notification.Name

// object : 발송자가 옵저버에게 보내려고 하는 객체. 주로 발송자 객체를 전달하는 데 쓰임
var object: Any?

// userInfo : 노티피케이션과 관련된 값 또는 객체의 저장소
var userInfo: [AnyHashable : Any]?

예) 특정 행동으로 인해 작업이 시작되거나 완료되는 시점에 다른 인스턴스로 노티피케이션이 발생 시 필요한 데이터를 같이 넘겨 줄 수 있다. 간단한 예로 네트워킹을 이용하는 애플리케이션이라면 네트워킹이 시작 및 완료되는 시점, 음악 및 동영상 재생 등에도 재생이 끝나는 시점에 관련 정보를 넘겨 줄 수 있다.

NotificationCenter

등록된 옵저버에게 동시에 노티피케이션을 전달하는 클래스이다. NotificationCenter 클래스는 노티피케이션을 발송하면 노티피케이션 센터에서 메세지를 전달한 옵저버의 처리할 때까지 대기한다. 즉, 흐름이 동기적(synchronous)으로 흘러가게 되어 노티피케이션을 비동기적으로 사용하려면 NotificationQueue를 사용하면 된다

기본 노티피케이션 센터 얻기

// default : 애플리케이션의 기본 노티피케이션 센터
class var `default`: NotificationCenter { get }

옵저버 추가 및 제거

실습

Notification을 보낼 Request.swift 파일을 만들어준다.

import Foundation

// Notification 이름을 하나 만들어줌
let DidReceiveFriendNotification: Notification.Name = Notification.Name("DidReceiveFriend")

func requestFriend() {
    guard let url: URL = URL(string: "https://randomuser.me/api/?results=20&inc=name,email,picture") else {return}

    let session: URLSession = URLSession(configuration: .default)
    let dataTask: URLSessionDataTask = session.dataTask(with: url) { (data: Data?, response: URLResponse?, error: Error?) in

        if let error = error {
            print(error.localizedDescription)
            return
        }

        guard let data = data else {return}
        do {
            let apiResponse: APIResponse = try JSONDecoder().decode(APIResponse.self, from: data)

            // userInfo 딕셔너리에 friends와 받아온 results값을 실어서 보내도록 한다
            NotificationCenter.default.post(name: DidReceiveFriendNotification, object: nil, userInfo: ["friends": apiResponse.results])

        } catch(let err) {
            print(err.localizedDescription)
        }
    }
    dataTask.resume()
}

그리고 notification center를 통해 보낸 request를 받아주는 함수를 viewController에 작성해준다.

// request.swift에서 center통해 보냈으니 받아주는 함수
@objc func didReceiveFriendNotification(_ noti: Notification) {
    // friends를 vc의 프로퍼티에 셋팅해준다음 테이블뷰 리로드 해줘야 함
    // noti.UserInfo에 실어서 보냈던 정보를 friends라고 해서 해당 정보를 받아오도록 한다
    // UserInfo가 있는지 없는지 모르니까 ?로 까본다
    guard let friends: [Friend] = noti.userInfo?["friends"] as? [Friend] else { return }

    // 위에서 만들어준 friends를 넣어주면 됨
    self.friends = friends

    // notification을 발송하는 쓰레드와 받는 쪽도 같은 쓰레드에서 동작하도록 해야한다.
    // 따라서 메인쓰레드에서 동작해야하는 쓰레드는 반드시 해당 코드로 작성해주어야 한다.
    // 혹은 notification 센터를 호출할때 메인쓰레드에서 호출하도록 하면 되기도 한다.
    DispatchQueue.main.async {
        self.tableView.reloadData()
    }
}

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.

    // Notification을 받을 것이라고 센터에 알려줌
    // 내가 받고(self) 어떤 notification을 함수로 받을 것인지, 어떤 이름으로 가져온 것을 받을 것인지를 등록
    // notification 센터에 등록을 하고 notification이 발생하면 해당 메서드를 통해 알려주면 해당 메서드가 실행될 것이라고 알려줌
    NotificationCenter.default.addObserver(self, selector: #selector(self.didReceiveFriendNotification(_:)), name: DidReceiveFriendNotification, object: nil)
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    requestFriend()
}

notification은 잘 사용하면 굉장히 좋은 무기지만 잘못 사용하게 되면 굉장히 헷갈리게 된다.

따라서 해당 관련된 것은 상수로 잘 정리해놓고 key값도 정리하는것이 좋으며, 위 실습에서의 코드에서만 보아도 코드가 굉장히 분산히 되어보이는데, 그렇기 때문에 코드가 간단한 경우에는 안쓰는 것이 효율적이지만 한번에 여러 인스턴스에게 notification을 전달해야한다면 이를 사용하는 것이 좋다.

Grand Central Dispatch란?

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


Grand Central Dispatch(GCD)

Grand Central Dispatch(GCD)는 멀티코어와 멀티 프로세싱 환경에서 최적화된 프로그래밍을 할 수 있도록 애플이 개발한 기술이다다. 기본적으로 스레드 풀의 관리를 프로그래머가 아닌 운영체제에서 관리하기 때문에 프로그래머가 태스크(작업)을 비동기적으로 쉽게 사용할 수 있다. 프로그래머가 실행할 태스크(작업)을 생성하고 Dispatch Queue 에 추가하면 GCD는 태스크(작업)에 맞는 스레드를 자동으로 생성해서 실행하고 작업이 종료되면 해당 스레드를 제거한다.

디스패치 대기열(Dispatch Queue)

디스패치 대기열(Dispatch Queue)은 작업을 연속적 혹은 동시에 진행하기는 하지만, 언제나 먼저 들어오면 먼저 나가는 순서 로 실행된다. Serial Dispatch Queue는 한 번에 하나의 작업만을 실행하며, 해당 작업이 대기열에서 제외되고 새로운 작업이 시작되기 전까지 기다린다. 이와는 반대로 Concurrent Dispatch Queue는 이미 시작된 작업이 완료될 때까지 기다리지 않고 가능한 많은 작업을 진행한다. 디스패치 대기열(Dispatch Queue)은 GCD 기술 일부다.

Dispatch Queue의 종류

디스패치 소스 (Dispatch Source)

디스패치 소스(Dispatch Source)는 특정 유형의 시스템 이벤트를 비동기적으로 처리하기 위한 C 기반 메커니즘이다. 특정 유형의 시스템 이벤트에 대해 정보를 캡슐화하고, 해당 이벤트가 발생할 때마다 특정 클로저(블록) 객체 혹은 기능을 디스패치 대기열(Dispatch Queue)에 전달합니다. 디스패치 소스(Dispatch Source)는 GCD 기술 일부이다.

연산 대기열 (Operation Queue)

연산 대기열(Operation Queue)은 Concurrent Dispatch Queue와 동일하게 동작하며, Operation Queue 클래스에 의해 구현된다. 디스패치 대기열은 항상 먼저 들어오면 먼저 나가는 순서(FIFO - First in First out)로 작업을 실행하지만, 연산 대기열(Operation Queue)은 작업의 실행 순서를 결정할 때에 다른 요인들을 고려한다. 연산 대기열(Operation Queue)은 디스패치 대기열(Dispatch Queue)과 매우 유사한 클래스이다.

GCD와 연산 대기열 (Operation Queue)

차이점

  • Operation Queue에서는 동시에 실행할 수 있는 연산(Operation)의 최대 수를 지정할 수 있다.
  • Operation Queue에서는 KVO(Key Value Observing)을 사용할 수 있는 많은 프로퍼티들이 있다.
  • Operation Queue에서는 연산(Operation)을 일시 중지, 다시 시작 및 취소를 할 수 있다.

언제 사용해야 할까?

  • Operation Queue: 비동기적으로 실행되어야 하는 작업을 객체 지향적인 방법으로 사용하는 데 적합!
    • KVO(key Value Observing)를 사용해 작업 진행 상황을 감시하는 방법이 필요할 때도 적합하다
    • 앱 내에서 많은 동작을 지원할때는 이용하는게 더 편리함
  • GCD: 작업이 복잡하지 않고 간단하게 처리하거나 특정 유형의 시스템 이벤트를 비동기적으로 처리할 때 적합
    • 타이머, 프로세스 등의 관련 이벤트…
    • 얘를 많이 사용하는게 가장 일반적..

ATS(App Transport Security)란?

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


ATS(App Transport Security)

ATS는 애플리케이션과 웹 서비스 사이에 통신 시 보안 향상을 위한 기능으로 iOS 9.0, macOS 10.11부터 적용 가능하다. 모든 인터넷 통신 시 안전한 프로토콜을 사용하도록 보장하는 것으로 사용자의 민감한 정보가 유출되는 것을 방지한다.

ATS 등장 배경

다양한 종류의 애플리케이션이 개인의 여러 가지 정보(연락처, 사진, 건강정보, 메시지, 메일 등)를 다루게 되면서 사용자 정보보호에 대한 중요성이 한층 부각되었다. 그런데 기존의 보안/암호 기술은 오래되어 공격에 취약해졌지만, 컴퓨터 성능은 점점 발전하면서 새롭게 등장하는 네트워크 공격이 강력해지자 이에 대응하기 위해 2015년 ATS를 도입하게 되었다.

2016년부터 새롭게 만들어지는 애플리케이션은 반드시 ATS를 사용해야 하며, 기존에 개발된 애플리케이션은 ATS를 사용할 수 있도록 네트워크 보안을 강화해야 한다고 한다.

ATS 동작

URLSession, CFURL 그리고 NSURLConnection API를 이용해 데이터를 주고받을 때 ATS 기능을 기본적으로 사용하게 된다. ATS가 활성화되어있을 때는 HTTP 통신을 할 수 없으며 애플에서 권장하는 아래 요구 사항을 충족하지 않은 네트워크는 연결에 실패할 수 있다.

서버는 TLS(Transport Layer Security) 프로토콜 버전 1.2 이상을 지원해야 한다. 적어도 2048비트 이상의 RSA 키 또는 256비트 이상의 ECC(Elliptic-Curve) 키가 있는 SHA256을 인증서에 사용해야 한다. 암호 연결은 아래 허용된 암호 목록으로 제한한다.

  • TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
  • TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
  • TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
  • TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
  • TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
  • TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
  • TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
  • TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
  • TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
  • TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
  • TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA

용어 정리

  • 전송 계층 보안 (Transport Layer Security - TLS)
    • 암호 프로토콜로 서버와 클라이언트 애플리케이션이 네트워크로 통신하는 과정에서 도청, 간섭, 위조를 방지하기 위해서 설계되었음
  • HTTPS (Hypertext Transfer Protocol Secure)
    • TLS를 사용해 암호화된 연결을 하는 HTTP(Hypertext Transfer Protocol)를 HTTPS라고한다

한 번 더 생각해보기

TLS는 다양한 종류의 보안 통신을 하려는 프로토콜이고, HTTPS는 TLS 위에 HTTP 프로토콜을 얹어 보안된 HTTP 통신을 하는 프토로콜을 의미한다.

예외 사항

애플리케이션이 ATS가 요구하는 사항을 충족하기 힘든 경우, ATS 기능을 비활성화할 수 있다. 아래는 ATS 기능을 사용하지 않을 수 있는 예외사항이다.

  • AVFoundation 프레임워크를 통한 스트리밍 서비스
  • WebKit을 통한 콘텐츠 요청
  • 로컬 네트워크 연결
  • 그 외에는 서버가 최신 TLS 버전으로 업그레이드할 때까지 애플리케이션의 유지 보수를 위해 일시적으로 ATS 기능을 사용하지 않는 것이 가능하며, App Store 심사 시 정당한 이유를 설명하는 문서가 필요할 수도 있다.

ATS 기능 비활성화 방법: 해당 프로젝트의 info.plist 파일에서 설정

모든 HTTP 통신 허용: 암호화 하지 않은 통신이므로 불가피한 때 외에는 사용하지 않는 것이 좋다.

<key> NSAppTransportSecurity </key>
<dict>
 	<key> NSAllowsArbitraryLoads </key>
 	<true/>
</dict>

ATS에서 제외할 특정 도메인 지정

<key>NSAppTransportSecurity</key>
<dict>
   <key>NSExceptionDomains</key>
   <dict>
       <key>www.abc.com</key>
       <dict>
           <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
           <true/>
       </dict>
   </dict>
</dict>