오늘은 iOS App Design Pattern Challenge 실습을 진행하였다. 이전에 배운 MVC (Model View Controller)라는 디자인 패턴을 나름 적용해보는 실습이었는데, 많은 도움이 된 것 같아 복습하고자 글을 쓴다.
우선 MVC 는 디자인 패턴의 일종이다. 대학교 소프트웨어설계 강의에서 다양한 디자인 패턴에 대해 배운 적 있다. Singleton, Observer, Prototype, Adapter 등 다양한 디자인 패턴을 다룬 적이 있는데, MVC에 대해서는 배운 적이 없다.
우선 파일을 MVC 형태로 나눠야하는데, XCode 프로젝트 디렉토리에서 Model, View, Controller 폴더를 따로 만든다. Controller 폴더 안에는 ViewController 와 같은 파일을 넣고, View 폴더 안에는 스토리보드 파일을 넣는다. Model 폴더 안에는 따로 정의한 구조체, 클래스 등을 넣으면 된다.
여기서 Model 은 나의 앱이 정확히 "무엇"을 할 지 코딩하는 것이다. MVC 디자인 패턴을 배우기 전에는 ViewController 안에 모든 데이터를 저장하고, 이 데이터를 처리하는 함수를 다 작성했는데, 그렇게 하면 코드가 복잡해지고, 가독성이 심각하게 떨어진다. Model 에는 Business Logic 을 담당하는 함수들이 정의되고, 처리되는 데이터 (클래스, 구조체 등)와 내부 알고리즘이 정의된다.
View 는 한 마디로 사용자에게 "보여지는" 영역으로 볼 수 있다. Storyboard 파일을 비롯해서 인터페이스를 구축하는 영역으로 생각하면 될 것 같다.
Controller 는 Model 과 View 사이의 다리라고 보면 된다. Controller 는 Model 이 가지고 있는 데이터를 "어떻게" 할 것인지 명령을 내린다. 그리고 이 명령을 토대로 사용자에게 보여지는 인터페이스 부분도 수정을 한다. 보통 여기서 @IBAction 함수들이 정의된다. 즉, 사용자가 View 를 통해 interaction 을 하면, Controller 가 이를 "control"한다는 것이다.
그래서 이런 MVC 디자인 패턴을 배운 후, 실제 앱에 적용해 보는 연습을 했다. 위와 같은 앱을 만드는 실습이었는데, 대략 사용자의 입력에 따라 다르게 전개되는 Story 앱이다. 정해져있는 답 보다, 사용자가 둘 중 하나의 버튼을 클릭했을 때 다른 스토리가 전개될 수 있도록 하는 그런 앱이다.
우선 MVC 디자인 패턴을 적용해 폴더부터 나눠줬다. Model, View, Controller 이렇게 3개의 폴더를 생성하였다.
다음으로 Story.swift 파일을 생성하여 여기다가 필요한 Story 구조체를 정의하였다. 앱에 필요한 요소로는 버튼을 누를 때마다 다르게 나오는 title, 그리고 버튼 2개를 나타낼 수 있는 choice1 과 choice2. 그리고 어떤 버튼을 눌렀는지에 따라 전개되는 스토리가 다르기 때문에, 이런 destination 을 나타낼 수 있는 변수 2개인 choice1Destination 이랑 choice2Destination 을 정의하였다.
구조체는 이렇게 별도의 파일에서 관리하는 것이 모듈화에 도움이 된다.
다음은 StoryBrain.swift 파일이다. 여기서 이제 데이터의 처리, 즉 Business Logic을 담당하는 구조체와 함수들을 정의할 것이다. 우선은 위와 같이 stories 배열을 StoryBrain 이라는 구조체 안에 만들어줬다. 중간에 저 많은 데이터는 수업에서 제공한 데이터고, 모두 방금 정의한 Story 라는 구조체 타입으로 배열을 선언하였다.
그 아래에는 필요한 변수와 함수들을 동일한 구조체 안에 선언하였다. 학교에서 C언어를 배울 때는 구조체 안에 함수를 사용을 해 본 경험이 없는 것 같은데 (아니면 있는데 기억을 못하는건가..) 스위프트에서는 자주 쓰이는 것 같다. 특히 앱 개발을 할 때 말이다.
storyNumber 변수는 사용자가 버튼을 누를 때 마다 어떤 story 로 가야하는지 추적하기 위한 변수다.
getStoryLabelText( ) 는 인터페이스 중앙에 어떤 story 를 표시할건지 매번 계산해주는 함수다.
getChoice1ButtonText( )과 getChoice2ButtonText( ) 는 매번 바뀌는 첫 번째 버튼의 text 를 반환하는 함수다.
getChoice1Destination( ) 과 getChoice2Destination( ) 함수는 다음에 어떤 story 로 가야하는지 그 숫자를 반환하는 함수다. 앞서 선언한 Story 구조체를 보면, 안에 choice1Destination 과 choice2Destination 함수가 있는 걸 볼 수 있다. 여기에 저장된 값은 다음에 stories 배열의 몇 번째 인덱스로 가야하는지 나타낸다.
changeStoryNumber( ) 함수는 구조체 내의 storyNumber 변수를 바꾸는데, 구조체 내부의 값을 바꾸는 것이기 때문에 mutating 키워드가 func 앞에 필요하다.
여기까지가 MVC 디자인 패턴 중에서 Model 파일들에 대한 설명이다. 다음은 View 다.
Main.storyboard 인데, 간단한 인터페이스로 구현이 되어있다. Stack View 안에 레이블 1개, 그리고 버튼 2개를 배치하였다.
다음으로는 대망의 Controller 부분이다. 우선 View 에서 생성한 3개의 요소를 @IBOutlet 으로 모두 연결한다. 그 다음 viewDidLoad() 함수가 실행되기 전에 StoryBrain( ) 인스턴스를 하나 storyBrain 으로 생성해준다.
viewDidLoad() 함수에서는 우선 화면의 초기설정을 정의한다. storyBrain 인스턴스의 storyNumber 는 현재 0이기 때문에 모두 stories 배열의 첫 번째 인덱스 값으로 모든게 설정이 되게 한다.
storyLabel.text = storyBrain.getStoryLabelText()
choice1Button.setTitle(storyBrain.getChoice1ButtonText(), for: .normal)
choice2Button.setTitle(storyBrain.getChoice2ButtonText(), for: .normal)
choiceMade( ) 함수는 스토리보드에서 생성한 2개의 버튼과 @IBAction 으로 연결되어 있고, 매개변수는 기본값인 sender로 설정하였다.
사용자는 버튼 2개 중 하나를 터치할 것이기 때문에, 어떤 버튼을 클릭했는지 정확히 알기 위해서 userAnswer 라는 변수를 따로 선언하였다. sender.currentTitle! 을 대입하면 String 형태로 userAnswer 변수에 들어가게 된다.
choice1 과 choice2 변수는 매번 바뀌니, 앞서 생성한 storyBrain 인스턴스로 컨트롤한다. 각 버튼의 String 값을 getChoiceButtonText( ) 로 가져와 if문으로 답변을 비교한다.
choice1 을 골랐다면 현재 storyNumber 에 해당하는 stories 배열 인덱스의 destination 을 받아와, changeStoryNumber( ) 함수를 통해 storyBrain 인스턴스의 storyNumber 값에 변경을 가한다. choice2도 마찬가지다.
그 다음 updateUI( ) 가 실행이 되는데, 이 부분도 모듈화를 위해 따로 함수로 빼두었다. 해당 함수는 이제 storyBrain 인스턴스에서 최종적으로 변경된 storyNumber 변수를 토대로 View 에 있는 레이블과 버튼 2개를 적절히 변경한다.
여기까지 위와 같은 아주 간단한 Story 앱을 MVC 디자인 패턴을 토대로 만들었다.
MVC 디자인 패턴을 배우고, 실제 적용을 해보니, 하나하나 모듈화를 하고 적용을 하려니 꽤 귀찮았지만, 이후에 데이터가 추가되거나 삭제되면, 이것만큼 관리하기 쉬운 것도 없을 것 같다. Model 에서는 비즈니스 로직을, View에서는 사용자에게 보여지는 것들을, 그리고 Controller 에서는 어떻게 Model 을 활용해서 View 에게 보여질 것인지만 정의하면 되니, 코드가 간결해지고, 관리가 용이해진 것 같다.