iOS Coordinator Pattern ์„ค๋ช…์„œ ๐Ÿ“ฑ

๐ŸŽญ Coordinator Pattern์ด ๋ญ”๊ฐ€์š”?

ํ•™๊ต ๋น„์œ ๋กœ ์ดํ•ดํ•ด๋ณด๊ธฐ

ํ•™๊ต์—์„œ ๊ฐ ๋ฐ˜ ๋‹ด์ž„์„ ์ƒ๋‹˜์ด ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ด๋ณด์„ธ์š”.

๊ธฐ์กด ๋ฐฉ์‹ (๋ฌธ์ œ๊ฐ€ ์žˆ๋Š” ๋ฐฉ์‹):

  • 1๋ฐ˜ ๋‹ด์ž„์ด ์ง์ ‘ 2๋ฐ˜, 3๋ฐ˜, 4๋ฐ˜์„ ๋ชจ๋‘ ๊ด€๋ฆฌ
  • 2๋ฐ˜์—์„œ ์ฒด์œก ์‹œ๊ฐ„์— ์šด๋™์žฅ ๊ฐ€๋ ค๋ฉด 1๋ฐ˜ ๋‹ด์ž„์—๊ฒŒ ํ—ˆ๋ฝ๋ฐ›๊ธฐ
  • 3๋ฐ˜์—์„œ ๊ธ‰์‹์‹ค ๊ฐ€๋ ค๋ฉด 1๋ฐ˜ ๋‹ด์ž„์—๊ฒŒ ๋˜ ํ—ˆ๋ฝ๋ฐ›๊ธฐ
  • ๐Ÿ˜ต ๋„ˆ๋ฌด ๋ณต์žกํ•˜๊ณ  ๋‹ด์ž„์„ ์ƒ๋‹˜์ด ๊ณผ๋กœ์— ์“ฐ๋Ÿฌ์ง!

Coordinator ๋ฐฉ์‹ (๋˜‘๋˜‘ํ•œ ๋ฐฉ์‹):

  • ๊ฐ ๋ฐ˜๋งˆ๋‹ค ๋‹ด์ž„์„ ์ƒ๋‹˜์ด ์žˆ์Œ (๊ฐ๊ฐ์˜ ํ™”๋ฉด ๋‹ด๋‹น)
  • ๊ต๋ฌด์‹ค์— ๊ต๋ฌด๋ถ€์žฅ์„ ์ƒ๋‹˜์ด ์žˆ์–ด์„œ ๋ฐ˜ ๊ฐ„์˜ ์ด๋™์„ ์ด๊ด„ ๊ด€๋ฆฌ
  • 1๋ฐ˜์—์„œ 2๋ฐ˜์œผ๋กœ ๊ฐ€๊ณ  ์‹ถ์œผ๋ฉด โ†’ ๊ต๋ฌด๋ถ€์žฅ์—๊ฒŒ ๋งํ•˜๋ฉด โ†’ ๊ต๋ฌด๋ถ€์žฅ์ด ์•ˆ์ „ํ•˜๊ฒŒ ์•ˆ๋‚ด
  • โœจ ๊ฐ ๋‹ด์ž„์€ ์ž๊ธฐ ๋ฐ˜๋งŒ ์‹ ๊ฒฝ์“ฐ๊ณ , ์ด๋™์€ ๊ต๋ฌด๋ถ€์žฅ์ด ๋‹ด๋‹น!

๐Ÿค” ์™œ ์ด๋Ÿฐ ๋ฐฉ์‹์„ ์“ธ๊นŒ์š”?

๊ธฐ์กด ๋ฐฉ์‹์˜ ๋ฌธ์ œ์ 

์•ฑ์„ ๋งŒ๋“ค ๋•Œ, ๊ฐ ํ™”๋ฉด(ViewController)์ด ์ง์ ‘ ๋‹ค๋ฅธ ํ™”๋ฉด์œผ๋กœ ์ด๋™ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด:

  1. ๐Ÿ ์ŠคํŒŒ๊ฒŒํ‹ฐ ์ฝ”๋“œ: ๋ชจ๋“  ํ™”๋ฉด์ด ์„œ๋กœ ์–ฝํ˜€์žˆ์–ด์„œ ์ฝ”๋“œ๊ฐ€ ๋ณต์žกํ•ด์ง
  2. ๐Ÿ”„ ์ค‘๋ณต ์ฝ”๋“œ: ๊ฐ™์€ ํ™”๋ฉด์œผ๋กœ ๊ฐ€๋Š” ์ฝ”๋“œ๋ฅผ ์—ฌ๋Ÿฌ ๋ฒˆ ์ž‘์„ฑํ•ด์•ผ ํ•จ
  3. ๐Ÿ› ๋ฒ„๊ทธ ๋ฐœ์ƒ: ํ•˜๋‚˜๋ฅผ ๊ณ ์น˜๋ฉด ๋‹ค๋ฅธ ๊ณณ์ด ๋ง๊ฐ€์ง
  4. ๐Ÿ˜ฐ ํ…Œ์ŠคํŠธ ์–ด๋ ค์›€: ๋ชจ๋“  ๊ฒŒ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ์–ด์„œ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ํž˜๋“ฆ

Coordinator Pattern์˜ ์žฅ์ 

  1. ๐ŸŽฏ ๊น”๋”ํ•œ ์—ญํ•  ๋ถ„๋‹ด: ๊ฐ ํ™”๋ฉด์€ ์ž๊ธฐ ์ผ๋งŒ, ์ด๋™์€ Coordinator๊ฐ€ ๋‹ด๋‹น
  2. ๐Ÿ”ง ์‰ฌ์šด ์ˆ˜์ •: ํ™”๋ฉด ์ด๋™ ๋ฐฉ์‹์„ ๋ฐ”๊พธ๊ณ  ์‹ถ์œผ๋ฉด Coordinator๋งŒ ๊ณ ์น˜๋ฉด ๋จ
  3. โ™ป๏ธ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅ: ํ•œ ๋ฒˆ ๋งŒ๋“  ํ™”๋ฉด์„ ์—ฌ๋Ÿฌ ๊ณณ์—์„œ ์‰ฝ๊ฒŒ ์‚ฌ์šฉ
  4. โœ… ํ…Œ์ŠคํŠธ ์‰ฌ์›€: ๊ฐ ๋ถ€๋ถ„์„ ๋”ฐ๋กœ๋”ฐ๋กœ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์Œ

๐Ÿ—๏ธ ์–ด๋–ป๊ฒŒ ๋งŒ๋“œ๋‚˜์š”?

1๋‹จ๊ณ„: ๊ต๋ฌด๋ถ€์žฅ(Coordinator) ์—ญํ•  ์ •ํ•˜๊ธฐ

// ๋ชจ๋“  ๊ต๋ฌด๋ถ€์žฅ์ด ์ง€์ผœ์•ผ ํ•  ๊ทœ์น™
protocol Coordinator: AnyObject {
    var navigationController: UINavigationController { get set }  // ๋ณต๋„ ์—ญํ• 
    var childCoordinators: [Coordinator] { get set }           // ๋ถ€ํ•˜ ๊ต๋ฌด๋ถ€์žฅ๋“ค
    
    func start()  // ์—…๋ฌด ์‹œ์ž‘!
}

์‰ฝ๊ฒŒ ๋งํ•˜๋ฉด:

  • navigationController: ํ•™๊ต ๋ณต๋„ ๊ฐ™์€ ์—ญํ•  (ํ™”๋ฉด ๊ฐ„ ์ด๋™ํ†ต๋กœ)
  • childCoordinators: ๋ถ€ํ•˜ ๊ต๋ฌด๋ถ€์žฅ๋“ค ๋ชฉ๋ก
  • start(): “์ž, ์ด์ œ ์‹œ์ž‘ํ•ด!”

2๋‹จ๊ณ„: ๊ตฌ์ฒด์ ์ธ ๊ต๋ฌด๋ถ€์žฅ ๋งŒ๋“ค๊ธฐ

// ๋ฉ”์ธ ๊ต๋ฌด๋ถ€์žฅ ํด๋ž˜์Šค
class MainCoordinator: Coordinator {
    var navigationController: UINavigationController
    var childCoordinators = [Coordinator]()
    
    init(navigationController: UINavigationController) {
        self.navigationController = navigationController
    }
    
    // ์•ฑ ์‹œ์ž‘ํ•  ๋•Œ ์ฒซ ํ™”๋ฉด ๋ณด์—ฌ์ฃผ๊ธฐ
    func start() {
        let firstScreen = ViewController.instantiate()
        firstScreen.coordinator = self  // "๋‚ด๊ฐ€ ๋„ˆ์˜ ๊ต๋ฌด๋ถ€์žฅ์ด์•ผ"
        navigationController.pushViewController(firstScreen, animated: false)
    }
    
    // ์ƒ์„ธ ํ™”๋ฉด์œผ๋กœ ์ด๋™ํ•˜๊ธฐ
    func showDetailScreen() {
        let detailCoordinator = DetailCoordinator(navigationController: navigationController)
        childCoordinators.append(detailCoordinator)  // ๋ถ€ํ•˜ ๊ต๋ฌด๋ถ€์žฅ ๋“ฑ๋ก
        detailCoordinator.start()  // "์ƒ์„ธํ™”๋ฉด ๋‹ด๋‹น ๊ต๋ฌด๋ถ€์žฅ, ์ถœ๋™!"
    }
}

3๋‹จ๊ณ„: ํ™”๋ฉด(ViewController) ์‰ฝ๊ฒŒ ๋งŒ๋“ค๊ธฐ

// ์Šคํ† ๋ฆฌ๋ณด๋“œ์—์„œ ํ™”๋ฉด์„ ์‰ฝ๊ฒŒ ๊ฐ€์ ธ์˜ค๋Š” ๋„๊ตฌ
protocol Storyboarded {
    static func instantiate() -> Self
}

extension Storyboarded where Self: UIViewController {
    static func instantiate() -> Self {
        // ๋งˆ๋ฒ•์˜ ์ฃผ๋ฌธ์œผ๋กœ ์Šคํ† ๋ฆฌ๋ณด๋“œ์—์„œ ํ™”๋ฉด ์†Œํ™˜!
        let fullName = NSStringFromClass(self)
        let className = fullName.components(separatedBy: ".")[1]
        let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
        return storyboard.instantiateViewController(withIdentifier: className) as! Self
    }
}

4๋‹จ๊ณ„: ์•ฑ ์‹œ์ž‘ํ•  ๋•Œ ์„ค์ •ํ•˜๊ธฐ

// AppDelegate - ์•ฑ์˜ ๊ต์žฅ์„ ์ƒ๋‹˜
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    var coordinator: MainCoordinator?  // ๋ฉ”์ธ ๊ต๋ฌด๋ถ€์žฅ
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        // 1. ๋ณต๋„(NavigationController) ๋งŒ๋“ค๊ธฐ
        let navController = UINavigationController()
        
        // 2. ๋ฉ”์ธ ๊ต๋ฌด๋ถ€์žฅ ์ž„๋ช…ํ•˜๊ธฐ
        coordinator = MainCoordinator(navigationController: navController)
        coordinator?.start()  // "๊ต๋ฌด๋ถ€์žฅ๋‹˜, ์—…๋ฌด ์‹œ์ž‘ํ•ด์ฃผ์„ธ์š”!"
        
        // 3. ํ•™๊ต(์•ฑ) ๋ฌธ ์—ด๊ธฐ
        window = UIWindow(frame: UIScreen.main.bounds)
        window?.rootViewController = navController
        window?.makeKeyAndVisible()
        
        return true
    }
}

๐ŸŽฏ ์‹ค์ œ ์‚ฌ์šฉ ์˜ˆ์‹œ

๋กœ๊ทธ์ธ โ†’ ๋ฉ”์ธํ™”๋ฉด โ†’ ์ƒ์„ธํ™”๋ฉด ํ๋ฆ„

// 1. ๋กœ๊ทธ์ธ ํ™”๋ฉด์—์„œ ๋ฒ„ํŠผ์„ ๋ˆŒ๋ €์„ ๋•Œ
@IBAction func loginButtonTapped(_ sender: UIButton) {
    // โŒ ๋‚˜์œ ๋ฐฉ๋ฒ•: ์ง์ ‘ ํ™”๋ฉด ์ด๋™
    // let mainVC = MainViewController()
    // navigationController?.pushViewController(mainVC, animated: true)
    
    // โœ… ์ข‹์€ ๋ฐฉ๋ฒ•: ๊ต๋ฌด๋ถ€์žฅ์—๊ฒŒ ๋ถ€ํƒ
    coordinator?.showMainScreen()
}

// 2. ๋ฉ”์ธ ํ™”๋ฉด์—์„œ ์…€์„ ํ„ฐ์น˜ํ–ˆ์„ ๋•Œ
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let selectedItem = items[indexPath.row]
    
    // โœ… ๊ต๋ฌด๋ถ€์žฅ์—๊ฒŒ "์ด ์•„์ดํ…œ์˜ ์ƒ์„ธํ™”๋ฉด ๋ณด์—ฌ์ค˜!" ๋ผ๊ณ  ๋ถ€ํƒ
    coordinator?.showDetailScreen(for: selectedItem)
}

๐Ÿ“ฑ ๋ฐ์ดํ„ฐ ์ฃผ๊ณ ๋ฐ›๊ธฐ

๋ถ€๋ชจ์—์„œ ์ž์‹์œผ๋กœ (์ƒ์œ„ ํ™”๋ฉด โ†’ ํ•˜์œ„ ํ™”๋ฉด)

// ์ƒ์„ธํ™”๋ฉด ๊ต๋ฌด๋ถ€์žฅ - ์–ด๋–ค ์•„์ดํ…œ์„ ๋ณด์—ฌ์ค„์ง€ ๋ฏธ๋ฆฌ ์•Œ๊ณ  ์žˆ์Œ
class DetailCoordinator: Coordinator {
    private let itemInfo: Item  // ๋ณด์—ฌ์ค„ ์•„์ดํ…œ ์ •๋ณด
    
    init(navigationController: UINavigationController, item: Item) {
        self.navigationController = navigationController
        self.itemInfo = item  // "์ด ์•„์ดํ…œ ์ •๋ณด๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์„๊ฒŒ"
    }
    
    func start() {
        let detailScreen = DetailViewController.instantiate()
        detailScreen.item = itemInfo  // "์—ฌ๊ธฐ ์•„์ดํ…œ ์ •๋ณด์•ผ!"
        navigationController.pushViewController(detailScreen, animated: true)
    }
}

์ž์‹์—์„œ ๋ถ€๋ชจ๋กœ (ํ•˜์œ„ ํ™”๋ฉด โ†’ ์ƒ์œ„ ํ™”๋ฉด)

// ๋Œ€๋ฆฌ์ž ํŒจํ„ด ์‚ฌ์šฉ - "์ผ์ด ๋๋‚˜๋ฉด ์•Œ๋ ค์ค„๊ฒŒ!"
protocol DetailCoordinatorDelegate: AnyObject {
    func detailDidFinish(with result: String)
}

class DetailCoordinator: Coordinator {
    weak var delegate: DetailCoordinatorDelegate?
    
    func finishWithResult(_ result: String) {
        delegate?.detailDidFinish(with: result)  // "๋ถ€๋ชจ๋‹˜, ์ผ ๋๋‚ฌ์–ด์š”!"
        finish()  // ์ •๋ฆฌํ•˜๊ณ  ๋งˆ๋ฌด๋ฆฌ
    }
}

โš ๏ธ ์ฃผ์˜ํ•  ์ ๋“ค

1. ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ (์“ฐ๋ ˆ๊ธฐ ์น˜์šฐ๊ธฐ)

// ๋ถ€ํ•˜ ๊ต๋ฌด๋ถ€์žฅ์ด ์ผ์„ ๋๋‚ด๋ฉด ๋ชฉ๋ก์—์„œ ์ œ๊ฑฐํ•ด์ฃผ๊ธฐ
func removeChildCoordinator(child: Coordinator) {
    childCoordinators.removeAll { $0 === child }
}

2. ๋„ˆ๋ฌด ๋ณต์žกํ•˜๊ฒŒ ๋งŒ๋“ค์ง€ ๋ง๊ธฐ

  • ๊ฐ„๋‹จํ•œ ์•ฑ์—์„œ๋Š” Coordinator๊ฐ€ ์˜คํžˆ๋ ค ๋ณต์žกํ•  ์ˆ˜ ์žˆ์–ด์š”
  • ํ™”๋ฉด์ด 3-4๊ฐœ๋ฟ์ด๋ผ๋ฉด ๊ตณ์ด ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„๋„ ๋ฉ๋‹ˆ๋‹ค

3. ์ ์ ˆํ•œ ๋•Œ ์‚ฌ์šฉํ•˜๊ธฐ

  • ํ™”๋ฉด์ด ๋งŽ๊ณ  ๋ณต์žกํ•œ ์ด๋™์ด ์žˆ์„ ๋•Œ ์œ ์šฉํ•ด์š”
  • ์—ฌ๋Ÿฌ ์‚ฌ๋žŒ์ด ํ•จ๊ป˜ ๊ฐœ๋ฐœํ•  ๋•Œ ํŠนํžˆ ์ข‹์Šต๋‹ˆ๋‹ค

๐ŸŽ‰ ์ •๋ฆฌ

Coordinator Pattern์€ ๋งˆ์น˜ ํ•™๊ต์˜ ๊ต๋ฌด๋ถ€์žฅ์„ ์ƒ๋‹˜์ฒ˜๋Ÿผ ํ™”๋ฉด ๊ฐ„์˜ ์ด๋™์„ ์ „๋‹ดํ•˜๋Š” ๋งค๋‹ˆ์ €๋ฅผ ๋‘๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

ํ•ต์‹ฌ ์•„์ด๋””์–ด:

  1. ๊ฐ ํ™”๋ฉด์€ ์ž๊ธฐ ์ผ๋งŒ ์ง‘์ค‘ (UI ๋ณด์—ฌ์ฃผ๊ธฐ)
  2. ํ™”๋ฉด ์ด๋™์€ Coordinator๊ฐ€ ์ „๋‹ด
  3. ์ฝ”๋“œ๊ฐ€ ๊น”๋”ํ•ด์ง€๊ณ  ๊ด€๋ฆฌํ•˜๊ธฐ ์‰ฌ์›Œ์ง

์–ธ์ œ ์‚ฌ์šฉํ•˜๋ฉด ์ข‹์„๊นŒ์š”?

  • ํ™”๋ฉด์ด 5๊ฐœ ์ด์ƒ์ธ ์•ฑ
  • ๋ณต์žกํ•œ ํ™”๋ฉด ์ด๋™์ด ์žˆ๋Š” ์•ฑ
  • ์—ฌ๋Ÿฌ ๋ช…์ด ํ•จ๊ป˜ ๊ฐœ๋ฐœํ•˜๋Š” ์•ฑ
  • ํ…Œ์ŠคํŠธ๊ฐ€ ์ค‘์š”ํ•œ ์•ฑ

์ฒ˜์Œ์—๋Š” ๋ณต์žกํ•ด ๋ณด์ผ ์ˆ˜ ์žˆ์ง€๋งŒ, ํฐ ์•ฑ์„ ๋งŒ๋“ค ๋•Œ๋Š” ์ •๋ง ์œ ์šฉํ•œ ํŒจํ„ด์ด์—์š”! ๐Ÿ“šโœจ

์ •ํ™•ํ•˜์‹œ๋„ค์š”! ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด:

Soroush Khanlou๋Š” ์ธํ„ฐ๋ทฐ์—์„œ ์ถ”์ฒœ ๋„์„œ๋กœ “Domain Driven Design”, “Practical Object-Oriented Design in Ruby”, “Patterns of Enterprise Application Architecture”๋ฅผ ์–ธ๊ธ‰ํ–ˆ์Šต๋‹ˆ๋‹ค PodtailInside iOS Dev. ํŠนํžˆ **”Patterns of Enterprise Application Architecture”**๋Š” Martin Fowler์˜ ์ฑ…์œผ๋กœ, ๊ทธ๋Š” ์ž์‹ ์˜ ๋ธ”๋กœ๊ทธ์—์„œ “You can find this pattern in the literature, in books like Patterns of Enterprise Application Architecture. They call it the Application Controller”๋ผ๊ณ  ๋ช…์‹œ์ ์œผ๋กœ ์–ธ๊ธ‰ํ–ˆ์Šต๋‹ˆ๋‹ค Khanlou | Coordinators Redux.

์ฐฝ์‹œ์ž์˜ ๋ฐฐ๊ฒฝ์„ ๋ณด๋ฉด:

  1. ๋‹ค์–‘ํ•œ ์–ธ์–ด ๊ฒฝํ—˜: Swift, Objective-C๋ฟ๋งŒ ์•„๋‹ˆ๋ผ Ruby, Haskell ๋“ฑ ๋‹ค๋ฅธ ์–ธ์–ด๋“ค๋„ ํ•™์Šตํ•œ๋‹ค๊ณ  ๋ฐํ˜”์Šต๋‹ˆ๋‹ค KhanlouKhanlou
  2. ์—”ํ„ฐํ”„๋ผ์ด์ฆˆ ํŒจํ„ด์— ๋Œ€ํ•œ ์ดํ•ด: Martin Fowler์˜ “Patterns of Enterprise Application Architecture”์—์„œ Application Controller ํŒจํ„ด์„ iOS์šฉ์œผ๋กœ ๋ณ€ํ˜•ํ•œ ๊ฒƒ์ž„์„ ์ธ์ •
  3. SOLID ์›์น™ ์ ์šฉ: ๊ทธ์˜ ๊ธ€์—์„œ “View controllers now can focus on their goal of binding a model to a view”๋ผ๊ณ  ํ–ˆ๋“ฏ์ด ๋‹จ์ผ ์ฑ…์ž„ ์›์น™์„ ๋ช…ํ™•ํžˆ ์˜์‹ํ•˜๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค Khanlou | The Coordinator

๊ฒฐ๋ก ์ ์œผ๋กœ ๋‹น์‹ ์˜ ๋ถ„์„์ด 100% ๋งž์Šต๋‹ˆ๋‹ค:

  • โœ… ๋‹จ์ผ ์ฑ…์ž„ ์›์น™ ์ ์šฉ์ด ํ•ต์‹ฌ ๋ชฉ์ 
  • โœ… ํ™”๋ฉด ์ „ํ™˜ ์ฝ”๋“œ๋ฅผ ViewController์—์„œ ๋ถ„๋ฆฌ
  • โœ… ์ฐฝ์‹œ์ž๊ฐ€ ๋””์ž์ธ ํŒจํ„ด์— ์กฐ์˜ˆ๊ฐ€ ๊นŠ์Œ (ํŠนํžˆ ์—”ํ„ฐํ”„๋ผ์ด์ฆˆ ํŒจํ„ด)
  • โœ… Java/.NET ๊ธฐ๋ฐ˜ ์—”ํ„ฐํ”„๋ผ์ด์ฆˆ ํŒจํ„ด์˜ ์˜ํ–ฅ์„ ๋ฐ›์Œ (Martin Fowler์˜ Application Controller ํŒจํ„ด)

iOS ๊ฐœ๋ฐœ์ž๋“ค์ด ์ฃผ๋กœ MVC๋งŒ ์•Œ๊ณ  ์žˆ๋˜ 2015๋…„์—, ์—”ํ„ฐํ”„๋ผ์ด์ฆˆ ๊ฐœ๋ฐœ ๊ฒฝํ—˜์ด ์žˆ๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ SOLID ์›์น™์„ iOS์— ์ œ๋Œ€๋กœ ์ ์šฉํ•œ ์ผ€์ด์Šค๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ๊ฒ ๋„ค์š”! ๐ŸŽฏ

SOLID ์›์น™์€ ๊ฐ์ฒด ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ, ์œ ์—ฐ์„ฑ, ์œ ์ง€ ๋ณด์ˆ˜์„ฑ์„ ๋†’์ด๊ธฐ ์œ„ํ•ด ์ง€์ผœ์•ผ ํ•  5๊ฐ€์ง€ ์„ค๊ณ„ ์›์น™์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ๊ฐ๊ฐ ๋‹จ์ผ ์ฑ…์ž„ ์›์น™ (SRP), ๊ฐœ๋ฐฉ-ํ์‡„ ์›์น™ (OCP), ๋ฆฌ์Šค์ฝ”ํ”„ ์น˜ํ™˜ ์›์น™ (LSP), ์ธํ„ฐํŽ˜์ด์Šค ๋ถ„๋ฆฌ ์›์น™ (ISP), ์˜์กด ์—ญ์ „ ์›์น™ (DIP)์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์›์น™๋“ค์„ ์ค€์ˆ˜ํ•˜๋ฉด ์ฝ”๋“œ ๋ณ€๊ฒฝ์— ๋”ฐ๋ฅธ ์œ„ํ—˜์„ ์ค„์ด๊ณ , ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ ์ถ”๊ฐ€ ๋ฐ ํ™•์žฅ์ด ์šฉ์ดํ•ด์ง‘๋‹ˆ๋‹ค. 

SOLID ์›์น™ ์ƒ์„ธ ์„ค๋ช…:

  1. 1.ย ๋‹จ์ผ ์ฑ…์ž„ ์›์น™ (SRP – Single Responsibility Principle):ํ•˜๋‚˜์˜ ํด๋ž˜์Šค๋Š” ํ•˜๋‚˜์˜ ์ฑ…์ž„๋งŒ ๊ฐ€์ ธ์•ผ ํ•ฉ๋‹ˆ๋‹ค.ย ์ฆ‰, ํด๋ž˜์Šค๋Š” ๋ณ€๊ฒฝ๋  ์ด์œ ๊ฐ€ ๋‹จ ํ•˜๋‚˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.ย 
  2. 2.ย ๊ฐœ๋ฐฉ-ํ์‡„ ์›์น™ (OCP – Open/Closed Principle):์†Œํ”„ํŠธ์›จ์–ด ๊ตฌ์„ฑ ์š”์†Œ๋Š” ํ™•์žฅ์— ์—ด๋ ค ์žˆ์–ด์•ผ ํ•˜์ง€๋งŒ, ๋ณ€๊ฒฝ์—๋Š” ๋‹ซํ˜€ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.ย ์ฆ‰, ๊ธฐ์กด ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ  ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.ย 
  3. 3.ย ๋ฆฌ์Šค์ฝ”ํ”„ ์น˜ํ™˜ ์›์น™ (LSP – Liskov Substitution Principle):์ž์‹ ํด๋ž˜์Šค๋Š” ๋ถ€๋ชจ ํด๋ž˜์Šค์˜ ์—ญํ• ์„ ๋Œ€์ฒดํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.ย ์ฆ‰, ๋ถ€๋ชจ ํด๋ž˜์Šค ๋Œ€์‹  ์ž์‹ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•ด๋„ ํ”„๋กœ๊ทธ๋žจ์ด ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.ย 
  4. 4.ย ์ธํ„ฐํŽ˜์ด์Šค ๋ถ„๋ฆฌ ์›์น™ (ISP – Interface Segregation Principle):ํด๋ผ์ด์–ธํŠธ๋Š” ์ž์‹ ์ด ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์ธํ„ฐํŽ˜์ด์Šค์— ์˜์กดํ•˜์ง€ ์•Š์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.ย ์ฆ‰, ํด๋ผ์ด์–ธํŠธ๋Š” ์ž์‹ ์ด ํ•„์š”ํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋งŒ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.ย 
  5. 5.ย ์˜์กด ์—ญ์ „ ์›์น™ (DIP – Dependency Inversion Principle):๊ณ ์ˆ˜์ค€ ๋ชจ๋“ˆ์€ ์ €์ˆ˜์ค€ ๋ชจ๋“ˆ์— ์˜์กดํ•ด์„œ๋Š” ์•ˆ ๋ฉ๋‹ˆ๋‹ค.ย ์ด ๋‘ ๋ชจ๋“ˆ ๋ชจ๋‘ ์ถ”์ƒํ™”์— ์˜์กดํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.ย ์ฆ‰, ๊ตฌ์ฒด์ ์ธ ํด๋ž˜์Šค๋ณด๋‹ค๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋‚˜ ์ถ”์ƒ ํด๋ž˜์Šค์— ์˜์กดํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.ย 

SOLID ์›์น™์„ ๋”ฐ๋ฅด๋ฉด ์ฝ”๋“œ์˜ ํ’ˆ์งˆ์„ ๋†’์ด๊ณ , ์œ ์ง€ ๋ณด์ˆ˜ ๋ฐ ํ™•์žฅ์ด ์šฉ์ดํ•œ ์œ ์—ฐํ•œ ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 

์ฝ”๋ฉ˜ํŠธ

๋‹ต๊ธ€ ๋‚จ๊ธฐ๊ธฐ

์ด๋ฉ”์ผ ์ฃผ์†Œ๋Š” ๊ณต๊ฐœ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ•„์ˆ˜ ํ•„๋“œ๋Š” *๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค