Swift namespace header

Swift의 네임스페이스와 typealias

Swift의 네임스페이스와 typealias를 어떻게 이용할 수 있을까라는 주제로 지난 Swift Korea Meetup에서 발표된 내용입니다.


소개

저는 ME2Day, BAND, LINE을 개발하고 있는 iOS 개발자로 Swift 1이 나올 때부터 프로젝트에 적용하고 있습니다.

네임스페이스

Why Namespace?

네임스페이스를 다루게 된 계기는 프로젝트 중간에 PIMS(Personal Information Managing System)를 추가하게 된 것이었습니다. 캘린더, 알람, 타이머, 메모, 리마인더를 만드는 것이었는데요. CRUD를 만들고 제네릭으로 프로토콜도 만들면 될 거라고 생각하다가 문득 재미로 네임스페이스를 넣기로 했습니다. 하지만 결과는 야근으로 돌아와 버렸죠.

꿈꾸던 세상

Structure/Feature Calendar - Alarm - Timer - Memo - Reminder
Model 모델 관점 개별 기능
CRUD 저장 관점 개발 기능
TableView 리스트 관점 개별 기능
UIViewController 표현 관점 개별 기능

이런 개발 뉴스를 더 만나보세요



  • Model: 모델 관점 개별 기능
  • CRUD: 저장 관점 개발 기능
  • TableView: 리스트 관점 개별 기능
  • UIViewController: 표현 관점 개별 기능

처음에는 위와 같은 구조를 꿈꿨고, Objective-C를 사용하던 입장에서 기존의 불편함을 없앨 수 있을 것으로 생각했습니다. 실제로 Java는 강력하고 제약이 심한 네임스페이스를 가지고 있기도 했고요.

실제 네임스페이스

실제로 Swift의 네임스페이스가 어떤 것인지 찾아봤더니 모듈 네임을 네임스페이스에서 지원한다는 것이 전부였습니다. 즉, import ModuleA, import ModuleB를 하면 네임스페이스가 생기는 거죠. 그래서 이를 호출하려면 moduleA.foo()와 같이 부릅니다. 하지만 이 기능만으로는 꿈꾸던 세상을 이룰 수 없었기 때문에 우회법을 찾아봤습니다.

네임스페이스 우회법

선구자들이 제시하는 우회법은 중첩 타입(Nested Type)을 쓰라는 것이었습니다. 클래스 안에 클래스를, 구조체 안에 구조체를 쓰는 형태죠. 구조체 안에 클래스를 넣을 수도 있습니다.


struct NameSpace {
  class NameSpaceObject {
    //
  }
}

하지만 이 방식의 문제는 NameSpace()를 인스턴스로 만들 수 있다는 점입니다. 따라서 이 문제를 해결하려면 중첩 타입이 아니라 중첩 열거형(Nested Enum)을 사용해야 합니다.


enum NameSpace {
  class NameSpaceObject {
    //
  }
}

이를 적용해서 아래와 같은 코드를 만들었습니다.


enum PIMS {
}
extension PIMS {
  enum Reminder {
    static var reminders:[PIMS.Reminder] = []
  }
}
extension PIMS.Reminder {
  enum Error: Swift.Error {
    case accessDefined
    case invalidState
    case nestedError(error: Swift.Error)
  }
}

에러 역시 Swift.Error를 상속받아서 만들었습니다. 하지만 실전에서는 어떨까요?

실전에서의 네임스페이스

프로토콜 미지원

실전에서 네임스페이스를 사용하려니 여러 문제가 발견됐습니다. 그 중 첫 번째 문제는 프로토콜을 지원하지 않는다는 것이었습니다. 다만 중첩 타입에서 지원하지 않던 제네릭을 Swift 3.1에서 지원하게 개선된 것을 보면 프로토콜도 곧 지원할 가능성이 높다고 생각합니다.

스코프

두 번째 문제는 스코프였습니다. 실재하는 것을 참조하지 못하는 경우가 있었습니다. 아래처럼 익스텐션을 사용하면 Model을 인식하지 못하면서 컴파일되지 않으므로 전체 이름인 PIMS.Reminder.Model이라는 전체 이름을 모두 적어줘야 합니다.


// 컴파일되지 않는 코드
extension PIMS.Reminder {
  struct Model {
    var reminderID: String
    var content: String
    var done: Bool
  }
}

extension PIMS.Reminder.Model: Equatable {
  static func ==(lhs: Modle, rhs: Model) -> Bool {
    return lhs.reminderID == rhs.reminderID
  }
}

// 컴파일되도록 고친 코드
extension PIMS.Reminder {
  struct Model {
    var reminderID: String
    var content: String
    var done: Bool
  }
}

extension PIMS.Reminder.Model: Equatable {
  static func ==(lhs: PIMS.Reminder.Model,
                rhs: PIMS.Reminder.Model) -> Bool {
    return lhs.reminderID == rhs.reminderID
  }
}

스코프 우회책: typealias

익스텐션은 별도의 스코프이므로 아래처럼 Coding이라는 짧은 열거형을 사용하면 컴파일 에러가 납니다.


// 컴파일되지 않는 코드
extension PIMS.Alarm.Model {
  var newCoding: PIMS.Alarm.Coding {
    return Coding(alarm: self)
  }
}

// 컴파일되도록 typealias 추가
private typealias Coding = PIMS.Alarm.Coding
extension PIMS.Alarm.Model {
  var newCoding: PIMS.Alarm.Coding {
    return Coding(alarm: self)
  }
}

따라서 우회법으로 typealias를 쓰면 인식이 가능해집니다. 하지만 다시 PIMS.Alarm.Coding이라고 길게 써야 하는 부분이 귀찮게 느껴지기에 해결 방법을 찾아봤습니다.

한 방법은 private typealias를 사용하면 것이었지만 public으로 변경하면 너무 많은 타입이 등장하므로 좋은 해결법은 아닌 것 같았습니다.

스토리보드

또한 스토리보드에서 커스텀 클래스를 지정할 때 중첩 타입으로 접근할 수 없다는 문제점이 있었습니다. 그래서 코드상으로는 클래스를 하나 만들고 중첩 타입을 쓰는 곳에서 typealias를 적용해서 접근할 수 있게 하고 스토리보드에서는 예전처럼 사용할 수 있도록 시도했습니다.


class PIMSReminderViewController: PIMS.BaseViewController {
  private typealias Model = PIMS.Reminder.Model
}

extension PIMS.Reminder {
  typealias ViewConroller = PIMSReminderViewController
}

이렇게 만든 결과 스토리보드에서 PIMSReminderViewController라는 클래스를 인식할 수 있었습니다. 하지만 다시 아울렛이 인식되지 않는 문제가 발견됐습니다. 인터페이스 빌더를 새로 만들 수는 없으니 결국 포기할 수밖에 없었습니다.

네임스페이스 결론

결론을 말씀드리면 네임스페이스는 모듈만 지원한다는 것입니다. 중첩 타입을 네임스페이스로 쓸 수 있는데 이렇게 사용한 경우 열거형을 사용하는 것이 좋습니다. 하지만 전체 이름을 일일이 쳐야 하는 귀찮음이 있으므로 해당 객체를 선언한 익스텐션에서만 사용하는 것을 추천합니다. 필요하면 typealias로 전체 이름을 단축할 수 있지만, 아직 스토리보드나 xib 등에서 지원하지 않으므로 신중히 사용하셔야 할 것 같습니다.

보너스 영상

추가로 tuple과 typealias로 타입을 잘 만들어 나가는 방법에 대한 보너스 강연이 맨 뒤에 있으니 궁금한 분들은 영상을 확인해 주세요!


본 영상과 글은 Swift Korea Meetup의 비디오 스폰서인 Realm에서 제공합니다. 모바일 개발자가 더 나은 앱을 더 빠르게 만들도록 돕는 Realm 모바일 데이터베이스Realm 모바일 플랫폼을 통해 핵심 로직에 집중하고 개발 효율을 높여 보세요! 공식 문서에서 단 몇 분 만에 시작할 수 있습니다. 또한 Realm 홈페이지에서는 모바일 개발자를 위한 다양한 최신 기술 뉴스와 튜토리얼을 제공하고 있으니 즐겨찾기하고 자주 들러 주세요!

다음: Realm Swift를 사용하면 iOS 앱 모델 레이어를 효과적으로 작성할 수 있습니다.

General link arrow white

컨텐츠에 대하여

이 컨텐츠는 저자의 허가 하에 이곳에서 공유합니다.

Hyuk Hur

Hyuk is a source code maintainer who want to make congruent software and service good service. One of his talents is finding bugs in the broken source.

4 design patterns for a RESTless mobile integration »

close