JerryiOS 2023. 4. 28. 18:16

Clean Architecture

์†Œํ”„ํŠธ์›จ์–ด ์‹œ์Šคํ…œ์„ ํŠน์ • ๋ชฉ์ ๊ณผ ์ฑ…์ž„์„ ๊ฐ€์ง„ ๊ณ„์ธต์œผ๋กœ ๋ถ„๋ฆฌํ•˜๋Š” ๋””์ž์ธ ์ ‘๊ทผ๋ฐฉ์‹

ํด๋ฆฐ ์•„ํ‚คํ…์ฒ˜์˜ ์ฃผ์š” ๋ชฉํ‘œ๋Š” ๊ฐ ๊ณ„์ธต์„ ๋…๋ฆฝ์ ์œผ๋กœ ๊ฐœ๋ฐœ, ํ…Œ์ŠคํŠธ ๋ฐ ๋ฐฐํฌํ•  ์ˆ˜ ์žˆ๋„๋กํ•˜์—ฌ ์œ ์—ฐํ•˜๊ณ  ์œ ์ง€ ๋ณด์ˆ˜๊ฐ€ ์šฉ์ดํ•œ ์‹œ์Šคํ…œ์„ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด๋‹ค.

์™ผ์ชฝ์˜ ๊ทธ๋ž˜ํ”„๋ฅผ ๋ณด๋ฉด ์—ฌ๋Ÿฌ๊ฐœ์˜ ๋ ˆ์ด์–ด๋“ค์ด ์กด์žฌํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

 

์ด ๊ทธ๋ž˜ํ”„์—์„œ ํ•ต์‹ฌ์€ ์•ˆ์ชฝ ๋ ˆ์ด์–ด๋“ค์€ ๋ฐ”๊นฅ ๋ ˆ์ด์–ด์— ์žˆ๋Š” ๊ฒƒ๋“ค์„ ๋ชฐ๋ผ์•ผ ํ•œ๋‹ค. ์ฆ‰ ์•ˆ์ชฝ ๋ ˆ์ด์–ด์—์„œ ๋ฐ”๊นฅ ์ชฝ์— ์˜์กดํ•˜์ง€ ๋ง์•„์•ผํ•œ๋‹ค.๋Š” ๊ฒƒ์ด๋‹ค.

 

๋‹ค์‹œ๋งํ•˜๋ฉด, ๋ฐ”๊นฅ์ชฝ ๋ ˆ์ด์–ด์˜ ๊ฐ์ฒด๋ฅผ ๋‚ด๋ถ€์—์„œ ์ƒ์„ฑํ•˜๋ฉด ์•ˆ๋œ๋‹ค๋Š” ๋ง์ด๋‹ค.

๊ทธ๋ž˜์„œ Dependency Rule ํ™”์‚ดํ‘œ๊ฐ€ ์•ˆ์ชฝ์„ ํ–ฅํ•˜๊ณ  ์žˆ๋‹ค.

์•ˆ์ชฝ ๋ ˆ์ด์–ด์— ์˜์กดํ•ด์•ผ ํ•œ๋‹ค๋Š” ์˜๋ฏธ๋‹ค.

 

Layers

์ด๋ ‡๊ฒŒ ๋ ˆ์ด์–ด๋ฅผ ๋‚˜๋ˆด๋Š”๋ฐ, ์—ฌ๊ธฐ์„œ ๊ทธ๋ฃน์„ ์ง€์„ ์ˆ˜ ์žˆ๋‹ค.

Domain ๋ ˆ์ด์–ด, Presentation ๋ ˆ์ด์–ด, ๊ทธ๋ฆฌ๊ณ  Data ๋ ˆ์ด์–ด๊ฐ€ ์žˆ๋‹ค.

 

Domain Layer(Business Logic)

๊ทธ๋ž˜ํ”„์˜ ๊ฐ€์žฅ ์•ˆ์ชฝ์— ์œ„์น˜ํ•œ ๋ถ€๋ถ„์ด๋‹ค.

๊ฐ€์žฅ ์•ˆ์ชฝ์— ์žˆ์œผ๋ฏ€๋กœ ์™„์ „ํžˆ ๊ณ ๋ฆฝ๋˜์–ด ์žˆ์œผ๋ฉฐ, ๋ฐ”๊นฅ์˜ ์ •๋ณด๋“ค์„ ์•„๋ฌด ๊ฒƒ๋„ ์•Œ ์ˆ˜ ์—†๋‹ค.

 

Entity(Business Model), Use Case, Repository Interface๋ฅผ ํฌํ•จํ•œ๋‹ค.

์ด ๋ ˆ์ด์–ด๋Š” ๋‹ค๋ฅธ ํ”„๋กœ์ ํŠธ์—์„œ๋„ ์žฌ์‚ฌ์šฉ ๋  ์ˆ˜ ์žˆ๋‹ค.

์ด๋ ‡๊ฒŒ ๋ ˆ์ด์–ด๊ฐ€ ๋ถ„๋ฆฌ๋˜๋ฉด ๋‹ค๋ฅธ ์˜์กด์„ฑ์ด๋‚˜ ์„œ๋“œํŒŒํ‹ฐ๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ํ…Œ์ŠคํŠธ๋ฅผ ์‰ฝ๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

Domain Layer์˜ ํ•ต์‹ฌ์€ ๋‹ค๋ฅธ ๋ ˆ์ด์–ด(Presentation์˜ UI๋‚˜ Data์˜ Mapping Codable)์˜ ์–ด๋–ค ๊ฒƒ๋„ ํฌํ•จ์‹œํ‚ค๋ฉด ์•ˆ๋œ๋‹ค.

 

Presentation Layer

UI(UIViewController / SwiftUI View)๋ฅผ ํฌํ•จํ•œ๋‹ค.

๊ฐ View๋Š” ํ•˜๋‚˜ ์ด์ƒ์˜ UseCase๋ฅผ ์‹คํ–‰์‹œํ‚ค๋Š” ViewModel(Presenters)์™€ ๋Œ€์‘๋œ๋‹ค.

์ฆ‰ ํ•œ view์— ๋Œ€์‘๋˜๋Š” ํ•˜๋‚˜์˜ viewModel์ด ์žˆ๋‹ค.

 

์œ„์˜ ๊ณผ๋…๋ชจ์–‘ ๊ทธ๋ž˜ํ”„์—์„œ๋„ ๋ดค๋“ฏ์ด, ๋„๋ฉ”์ธ ๋ ˆ์ด์–ด์—๋งŒ ์˜์กดํ•˜๊ณ  ์žˆ๋‹ค.

 

Data Layer

Repository Implementation, ๊ทธ๋ฆฌ๊ณ  ํ•˜๋‚˜ ์ด์ƒ์˜ Data Source๋ฅผ ํฌํ•จํ•œ๋‹ค.

 

Data Source๋Š” ์›๊ฒฉ์ด๊ฑฐ๋‚˜ ๋กœ์ปฌ์ผ ์ˆ˜ ์žˆ๋‹ค.

์›๊ฒฉ์ผ ๊ฒฝ์šฐ API ํ˜ธ์ถœ์„ ํ†ตํ•ด JSON ๋ฐ์ดํ„ฐ๋ฅผ ๋‚ด๋ ค๋ฐ›๋Š” ๊ฒฝ์šฐ์ด๊ณ , ๋กœ์ปฌ์ผ ๊ฒฝ์šฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์ผ ๊ฒƒ์ด๋‹ค.

Data Layer๋Š” ์˜ค๋กœ์ง€ Domain Layer์—๋งŒ ์˜์กดํ•˜๊ณ  ์žˆ๋‹ค.

๊ณผ๋…๋ชจ์–‘์˜ ๊ทธ๋ž˜ํ”„์—์„œ๋Š” ์•ˆ์ชฝ์˜ Presenters, ๋ฐ”๊นฅ์˜ DB, API, UI ๋“ฑ์ด ์žˆ์—ˆ๋Š”๋ฐ Presenters๋Š” Presentation Layer์— ๋Œ€์‘๋˜๊ณ  Data Layer์— ๋Œ€์‘๋˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค.

 

Data Layer์—๋Š” ๋„คํŠธ์›Œํฌ๋กœ ๋ฐ›์€ JSON ๋ฐ์ดํ„ฐ๋ฅผ Domain Model๋กœ ๋งคํ•‘ํ•˜๋Š” ๊ฒƒ์„ ํฌํ•จ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.

 

Data Flow

์œ„ ๊ทธ๋ฆผ์—์„œ๋Š” ์˜์กด์„ฑ ๋ฐฉํ–ฅ์ด Domain Layer๋กœ ํ–ฅํ•˜๊ณ  ์žˆ๋‹ค.

ํŠนํžˆ Repository Interface(protocol)์—์„œ ์˜์กด์„ฑ ์—ญ์ „์ด ์ผ์–ด๋‚˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ทผ๋ฐ ์˜์กด์„ฑ ์—ญ์ „์ด ๋ญ์ง€?

์˜์กด์„ฑ ์—ญ์ „ ์›์น™

์ƒ์œ„ ๋ชจ๋“ˆ์ด ํ•˜์œ„ ๋ชจ๋“ˆ์— ์˜์กดํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ๋‘˜ ๋ชจ๋‘๊ฐ€ ์ถ”์ƒํ™”์— ์˜์กดํ•ด์•ผ ํ•œ๋‹ค๋Š” ์†Œํ”„ํŠธ์›จ์–ด ๋””์ž์ธ ์›์น™

์˜์กด์„ฑ ์—ญ์ „ ์›์น™์—์„œ๋Š” ํ•˜์œ„ ๋ชจ๋“ˆ์ด ์ƒ์œ„๋ชจ๋“ˆ์— ์˜์กดํ•˜๋„๋ก ์„ค๊ณ„ํ•œ๋‹ค.

๊ทธ๋ž˜์„œ ์ธํ„ฐํŽ˜์ด์Šค(Swift์—์„œ๋Š” ํ”„๋กœํ† ์ฝœ)์„ ํ†ตํ•ด ์ ‘๊ทผํ•œ๋‹ค.

์ƒ์œ„ ๋ชจ๋“ˆ๊ณผ ํ•˜์œ„ ๋ชจ๋“ˆ์˜ ๊ฒฐํ•ฉ๋„๋ฅผ ๋‚ฎ์ถ”๊ณ , ๋ชจ๋“ˆ ๊ฐ„ ์˜์กด์„ฑ์„ ์ œ์–ดํ•˜์—ฌ ์†Œํ”„ํŠธ์›จ์–ด ์‹œ์Šคํ…œ์„ ์œ ์—ฐํ•˜๊ณ  ํ™•์žฅ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“ ๋‹ค.

์•„๋ž˜์˜ ์˜ˆ์‹œ๋ฅผ ๋ณด์ž.

protocol Printer {
    func printMessage(message: String)
}

class ConsolePrinter: Printer {
    func printMessage(message: String) {
        print(message)
    }
}

class Message {
    let printer: Printer
    
    init(printer: Printer) {
        self.printer = printer
    }
    
    func printMessage(message: String) {
        printer.printMessage(message: message)
    }
}

let printer = ConsolePrinter()
let message = Message(printer: printer)
message.printMessage(message: "Hello, World!")

Messageํด๋ž˜์Šค(์ƒ์œ„๋ชจ๋“ˆ)๋Š” ConsolePrinterํด๋ž˜์Šค(ํ•˜์œ„๋ชจ๋“ˆ)์— ์ง์ ‘์ ์œผ๋กœ ์˜์กดํ•˜์ง€ ์•Š๊ณ ,

Printerํ”„๋กœํ† ์ฝœ์— ์˜์กดํ•˜๊ฒŒ ๋˜์–ด ์˜์กด์„ฑ ์—ญ์ „์›์น™์ด ์ ์šฉ๋œ๋‹ค.

์ด๋ ‡๊ฒŒ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ชจ๋“ˆ ๊ฐ„์˜ ๊ฒฐํ•ฉ๋„๋ฅผ ๋‚ฎ์ถ”๊ณ  ์˜์กด์„ฑ์„ ์ œ์–ดํ•œ๋‹ค.

 

 

๋‹ค์‹œ ๋ณธ๋ก ์œผ๋กœ ๋Œ์•„์™€์„œ,

ํด๋ฆฐ์•„ํ‚คํ…์ฒ˜์—์„œ๋Š”  Domain UseCase์—์„œ Data Repository์— ์˜์กดํ•˜๊ณ  ์žˆ์œผ๋ฉด ์•ˆ๋œ๋‹ค.

์™œ ์˜์กดํ•˜๋ฉด ์•ˆ๋˜๋Š” ์ง€ ๊ถ๊ธˆํ•ด์„œ ChatGPT์— ๋ฌผ์–ด๋ดค๋‹ค.

DIP๋Š” ์˜์กด์„ฑ ์—ญ์ „ ์›์น™์ด๋‹ค.

ํ…Œ์ŠคํŠธ๊ฐ€ ์–ด๋ ค์›Œ์ง€๊ณ  Data Repository์˜ ๋ณ€๊ฒฝ์ด UseCase์— ์˜ํ–ฅ์„ ๋ฏธ์ณ์„œ ๊ทธ๋ ‡๋‹ค๊ณ  ํ•œ๋‹ค.

์•„.. ๊ทธ๋Ÿฌ๋ฉด UseCase๊ฐ€ ์˜ํ–ฅ์„ ๋ฐ›๊ฒŒ๋˜๋‹ˆ๊นŒ ์˜์กดํ•˜๋ฉด ์•ˆ๋œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ์•˜๋‹ค.

 

๊ทธ๋ ‡์ง€๋งŒ Data Repository(์ƒ์œ„๋ชจ๋“ˆ)์—์„œ๋Š” request์™€ response๋ฅผ ๋ฐ›์•„์™€์•ผํ•œ๋‹ค.

 

์ด๋Ÿฐ ์ƒํ™ฉ์—์„œ Data Repository๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์˜์กด์„ฑ ์—ญ์ „ ์›์น™์„ ํ†ตํ•ด ์˜์กด์„ฑ์„ ์—†์• ๋ฉด์„œ(์œ„์˜ ์˜ˆ์ œ์—์„œ ํ”„๋กœํ† ์ฝœ ์“ด ๊ฒƒ์ฒ˜๋Ÿผ) Domain์—์„œ Data Repository๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•œ ๊ฒƒ์ด๋‹ค.

 

๊ทธ๋ž˜์„œ ๋ฐ์ดํ„ฐ๊ฐ€ ์˜ค๊ณ  ๊ฐ€๋Š” ํ๋ฆ„์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

1. ์‚ฌ์šฉ์ž๊ฐ€ ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ View(UI)๊ฐ€ ViewModel(Presenter)์˜ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.
2. ViewModel์ด UseCase๋ฅผ ์‹คํ–‰์‹œํ‚จ๋‹ค.
3. UseCase๊ฐ€ User์™€ Repository์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ทจํ•ฉํ•œ๋‹ค.
4. ๊ฐ Repository๋Š” Remote Data(NW), Persistent DB์ €์žฅ์†Œ๋‚˜ ๋ฉ”๋ชจ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
5. ๋ฐ˜ํ™˜๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์šฐ๋ฆฌ๊ฐ€ ์•„์ดํ…œ๋“ค์„ ํ™”๋ฉด์— ์ถœ๋ ฅํ•  View์— ์ „๋‹ฌ๋œ๋‹ค.

 

์˜์กด์„ฑ ๋ฐฉํ–ฅ

Presentation Layer โžก๏ธ Domain Layer โฌ…๏ธ Data Layer

  • Presentation Layer(MVVM) = ViewModel(Presenters) + Views(UI)
  • Domain Layer = Entities + UseCases + Repositories Interfaces(์˜์กด์„ฑ ์—ญ์ „ ์›์น™์„ ์œ„ํ•œ Protocol)
  • Data Layer = Repositories Implementations + API(NW) + Persistence DB

๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ ์˜ˆ์ œ ํ”„๋กœ์ ํŠธ๋ฅผ ๋‹ค๋ค„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.


Ref

https://yoojin99.github.io/app/%ED%81%B4%EB%A6%B0-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98/

๋ฐ˜์‘ํ˜•