[Swift 고급] Protocol X Delegate : 코드 리팩토링과 파일 간 데이터 전송의 끝판왕

2020. 4. 12. 18:18컴퓨터언어/Swift

728x90
반응형

Protocol과 Delegate 개념이 왜 필요한데?

 

이전에 Protocol을 다룬 적이 있다.

 

이번 시간을 이해하기 위해서는 Protocol의 이해가 필수이므로 다시 한번 정리하고 시작해보겠다.

 

Protocol이란,  말그대로 "정해놓은 규칙"이라는 뜻이다.

 

그래, 말은 참 쉽다. 그런데 Protocol이 도대체 뭐길래, 즐거웠던 앱개발 초보의 길에 갑자기 나타나서 큰 산으로 와닿는 것일까?

 

축하한다. Protocol에 온 이상 우리는 초보를 벗어난 것이다.

 

초보시절 비교적 간단한 Toy 앱에서는 데이터나 네비게이션이 많이 필요없다.

 

그렇기 때문에 .swift 파일의 개수 역시 상당히 적고, 따라서 내가 코드를 발로 짜도 몇 줄 내로 금방 눈에 띄기 때문에 수정하기도 쉽다.

 

하지만 적어도 진짜 App을 개발한다면,

 

유저로부터 받아야 하는 터치나 텍스트 등의 입력값부터 시작해서

 

API 데이터, 회원인증 등등 수많은 데이터를 저장하고 각 함수에 넘겨주어야 하는 상황이 당연해진다.

 

그리고 스스로뿐만 아니라 협업하는 사람들과 코드를 보며 머리랑 눈 뽀개지지 않게 직관적으로 소통하려면

 

App 내 각 기능마다 .swift 파일을 별도로 분리하여 저장함으로써 가독성과 업무 효율을 높여야만 한다.

 

그러한 점에서 나온 개념 중 하나가 Model + View + Controller 패턴이지 않은가.

 

이번 시간에 다루는 Delegate도 이러한 철학을 바탕으로 우리를 도와주는 패턴이다.

 

Delegate란,

 

우리가 (또는 Apple이) 미리 정의해 둔 Protocol을 채택하는 Controller마다,

 

(심지어) 서로 다른 파일에서도 손쉽게 데이터를 주고받을 수 있도록 Model을 짜는 패턴이다.

 

쉽게 말해,

Delegate는 유지보수를 위해 분리해놓은 파일들을 연결해주는 파이프이고,

Protocol은 연결해서 무엇을 할 건지 정의한 규칙이다.


여기까지 다시 한번 정리해보자.

 

그러니까 평화롭던 개발 중에 갑자기 Protocol과 Delegate가 필요해지는 순간은 예를 들어 다음과 같다.

 


 

이번에 만들 앱에는 메인화면에 날씨정보가 떴으면 좋겠네..

 

흠.. 그러기 위해서는 어디 보자.. 일단 뭐 openweathermap.org에 가서 JSON 받아오면 되겠다!

 

그건 좀 이따가 가져오도록 하고, 일단 메인화면 UI 디자이너님이 그려주신대로 View를 그려봐야지

 

 

 

(고생 후)

다 그렸다! IBOutlet 연결도 다했고.. 그럼 JSON 받아오는 URL Model 짜야겠다

 

 

여기까지 했는데, 다음과 같은 수많은 한계점이 있다.

  1. 검색버튼을 터치하는 것은 된다. 그런데 막상 입력할 때 나타나는 키보드에서 Go 버튼을 눌러도 반응하지 않는다. 이는 사용자 경험을 해친다.

  2. 검색버튼을 눌러서 결과가 나와도 키보드가 사라지지 않고 그대로 있다.

  3. 검색이 완료되면 다시 검색창에 내가 썼던 글씨가 없었으면 좋겠다. 그래야 다른 지역 다시 검색 시 더 편리하니까.

  4. Model로 따로 분리한 .swift파일들의 함수들은 수많은 return을 내보내는데, 이를 다시 ViewController에서 받아주어야만 유저들이 결과 화면을 볼 수 있을 텐데.... 기존 초보 방식으로 Controller에서도 함수로 다 짜려니 너무 비직관적이고 그냥 더럽다.

 


 

위 4개를 통해 우리가 해결해야 할 과제를 2가지로 정리해보면, 다음과 같다.

 

1. ViewController 내부에서 검색버튼을 눌렀을때의 1~3번 문제 해결하기

2. JSON 데이터 결과를 ViewController로 넘겨주기(4번 문제 해결하기)

 


 

자 그런데 한번 생각해보자.

 

그냥 검색하고자 하는 도시 이름을 입력받아서 그곳의 날씨 정보를 출력해주는 것만 해도 이렇게 복잡하다.

 

그리고 이걸 해결하는 데도 꽤 복잡할 것 같다.

 

하물며 거대한 프로젝트에서는 얼마나 복잡할까?

 

바로 이럴 때 Delegate 패턴을 이용하면 코드와 파일들의 독립성을 유지하면서도 깔끔하게 코딩할 수 있다.

 


 

Delegate 패턴을 사용하기 위해서는 다음과 같은 3가지 순서만 기억하자.

1. 무슨 정보를 넘겨줄 건데?

2. 그 정보를 주는 쪽이 누군데?

3. 그 정보를 받는 쪽이 누군데?

 

 

 


 

1. 무슨 정보를 넘겨줄 건데의 그 "무슨 정보"가 Protocol이다.

protocol 프로토콜이름 {
	func 정보를전해주는로직을담을함수이름()
}

2. 정보를 주는 쪽에서는 내가 그 정보를 "주겠노라"고 말로 선언하듯이 변수를 Optional로 선언하면 된다.

var delegate: 프로토콜이름?

3. 마지막으로 정보를 받는 쪽에서는 내가 그 정보를 "받을 거에요" 라고 자기 자신을 등록하고 그 정보를 채택하면 된다.

class 정보를받는쪽이름: 프로토콜이름 {

	override func viewDidLoad() {
        super.viewDidLoad()
        정보를주는쪽이름.delegate = self
    }
}

이게 전부다.

 

그럼 이 개념을 먼저 첫번째 해결책(1~3번문제 해결)에 대입해보자.

 

 

1. 무슨 정보를 넘겨줄 건데?

검색창에 유저가 입력한 도시 이름

2. 그 정보를 주는 쪽이 누군데?

검색창

3. 그 정보를 받는 쪽이 누군데?

ViewController 전체

 

1. 검색창에 유저가 입력한 도시 이름이 Protocol이기 때문에 Protocol을 정의한다.

그런데 Apple에서는 UITextField에 유저가 입력한 정보를 이용할 수 있는 Protocol이 이미 존재하기 때문에, 우리는 따로 만들 필요 없이 그냥 갖다 쓰면 된다.

그것의 이름은 UITextFieldDelegate이다.

protocol UserInputData {
	func sendUserInputData()
}

즉 원래는 위와 같이 내가 만들어야 하지만, UserInputData 대신 Apple이 이미 UITextFieldDelegate를 만들어놨다는 뜻이다.

따라서 1번은 생략 가능하다.

 

2. 그 정보(=유저가 입력한 도시이름)를 주는 쪽은 UITextField 자체이다. 이는 이미 IBOutlet으로서 UITextField 클래스가 생성되어 있으므로 내가 따로 만들 변수가 없다.

즉 내가 커스텀으로 만든 class가 있고, 그 class가 정보를 주는 쪽이 되겠다면, 그 class 내에서 "var delegate: 프로토콜이름?" 을 선언해야 하지만, UITextField의 경우에는 Apple이 만든 Class이기 때문에 우리가 만들 필요가 없다는 것이다.

38번줄에 Apple이 미리 만들어놓은 UITextField 내의 delegate변수가 있다. 따라서 우리가 정보를 주겠노라고 선언할 변수는 여기서 생략 가능하다.

3. 그 정보를 받는 쪽은 ViewController 그 자체이다. 왜냐하면 검색창과 결과화면이 현재 같은 ViewController 화면에 위치하고 있기 때문이다. 만약 서로 다른 페이지에 있다면 정보를 받는 쪽은 그 결과가 나와야할 Controller가 될 것이다.

정보를 받기 위해서는 먼저 내가 어떤 정보를 받을 것인지 그 정보를 채택해야 한다.

이는 class나 struct 이름 뒤에 : 를 붙인 후 1번의 Protocol 이름을 쓰면 된다.(class의 경우 상속해준 class명 뒤로 이어 적으면 된다)

1번에서 말했듯이 Protocol이 바로 전해지는 정보이기 때문이다.

 

같은 맥락으로 JSON 데이터를 전송하기 위한 로직까지 추가하면 다음과 같다.

 

그럼 결과적으로 독립된 파일과 MVC 패턴을 유지함과 동시에 data를 깔끔하게 주고받을 수 있게 된다.

728x90
반응형