Ver3.0 ๐ Swift ๋ฆฌํ๋ ์ ๊ณผ Mirror๋ฅผ ํ์ฉํ ๋ฐํ์ ํ์ ๊ฒ์ฌ ์์ ์ ๋ณต

๐ Swift ๋ฆฌํ๋ ์ ๊ณผ Mirror๋ฅผ ํ์ฉํ ๋ฐํ์ ํ์ ๊ฒ์ฌ ์์ ์ ๋ณต
๋ฐํ์์ ํ์ ์ ๋ค์ฌ๋ค๋ณด๋ ๋ง๋ฒ ๊ฐ์ ๊ธฐ์ , ํจ๊ป ํํค์ณ๋ณผ๊น์? ๐ฏ
์๋
! ์ค๋์ Swift ๊ฐ๋ฐ์๋ผ๋ฉด ๊ผญ ์์์ผ ํ ๋ฆฌํ๋ ์
(Reflection)๊ณผ Mirror์ ๋ํด ์ด์ผ๊ธฐํด๋ณผ๊ฒ. ๐
์ฝ๋๋ฅผ ์์ฑํ๋ค ๋ณด๋ฉด "์, ์ด ๋ณ์๊ฐ ๋ฐํ์์ ์ด๋ค ํ์
์ธ์ง ํ์ธํ๊ณ ์ถ์๋ฐ..." ํ๋ ์๊ฐ์ด ์์ง? ์ปดํ์ผ ํ์์๋ ๋ชจ๋ ๊ฒ ์ ํด์ ธ ์์ง๋ง, ์ค์ ๋ก ํ๋ก๊ทธ๋จ์ด ๋์๊ฐ ๋๋ ์์์น ๋ชปํ ์ํฉ๋ค์ด ์๊ธฐ๊ฑฐ๋ . ๋ฐ๋ก ์ด๋ด ๋ ๋ฆฌํ๋ ์
์ด ์ง๊ฐ๋ฅผ ๋ฐํํด! ๐ญ
๐ช ๋ฆฌํ๋ ์ ์ด ๋ญ๊ธธ๋?
๋ฆฌํ๋ ์
์ ํ๋ก๊ทธ๋จ์ด ์๊ธฐ ์์ ์ ๊ตฌ์กฐ๋ฅผ ๋ค์ฌ๋ค๋ณด๊ณ ๋ถ์ํ ์ ์๋ ๋ฅ๋ ฅ์ด์ผ. ๋ง์น ๊ฑฐ์ธ(Mirror)์ ๋ณด๋ฏ์ด ์์ ์ ๋ชจ์ต์ ํ์ธํ๋ ๊ฑฐ์ง! ๐ช
์ผ๋ฐ์ ์ผ๋ก ์ฐ๋ฆฌ๊ฐ ์ฝ๋๋ฅผ ์์ฑํ ๋๋ ์ปดํ์ผ๋ฌ๊ฐ ๋ชจ๋ ํ์
์ ๋ณด๋ฅผ ๋ฏธ๋ฆฌ ์๊ณ ์์ด. ํ์ง๋ง ๋๋ก๋ ๋ฐํ์์ "์ด ๊ฐ์ฒด๊ฐ ์ด๋ค ํ๋กํผํฐ๋ฅผ ๊ฐ์ง๊ณ ์์ง?", "์ด ๊ฐ์ ์ค์ ํ์
์ด ๋ญ์ง?" ๊ฐ์ ์ง๋ฌธ์ ๋ตํด์ผ ํ ๋๊ฐ ์๊ฑฐ๋ .
โข JSON ๋ฐ์ดํฐ๋ฅผ ์๋์ผ๋ก ๊ฐ์ฒด๋ก ๋ณํํ ๋
โข ๋๋ฒ๊น ๋๊ตฌ๋ ๋ก๊น ์์คํ ์ ๋ง๋ค ๋
โข ORM(Object-Relational Mapping) ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ตฌํ
โข ํ ์คํธ ํ๋ ์์ํฌ์์ ๊ฐ์ฒด ์ํ ๊ฒ์ฆ
โข ๋์ UI ์์ฑ์ด๋ ํผ ๋น๋ ์ ์
โข ์๋ฆฌ์ผ๋ผ์ด์ ์ด์ /๋์๋ฆฌ์ผ๋ผ์ด์ ์ด์ ๊ตฌํ
๐ฌ Swift์ Mirror: ํ์ ์ ๋น์ถ๋ ๋ง๋ฒ์ ๊ฑฐ์ธ
Swift์์๋ Mirror๋ผ๋ ๊ตฌ์กฐ์ฒด๋ฅผ ํตํด ๋ฆฌํ๋ ์
๊ธฐ๋ฅ์ ์ ๊ณตํด. ์ด๋ฆ๋ถํฐ ์ง๊ด์ ์ด์ง? ๊ฑฐ์ธ์ฒ๋ผ ๊ฐ์ฒด์ ๋ด๋ถ๋ฅผ ๋น์ถฐ์ฃผ๋ ์ญํ ์ ํ๋ค๋ ์๋ฏธ์ผ! โจ
๐ Mirror์ ๊ธฐ๋ณธ ๊ตฌ์กฐ
Mirror๋ ๋ค์๊ณผ ๊ฐ์ ํต์ฌ ์ ๋ณด๋ค์ ์ ๊ณตํด:
let mirror = Mirror(reflecting: someObject)
// ์ฃผ์ ํ๋กํผํฐ๋ค
mirror.children // ๊ฐ์ฒด์ ํ๋กํผํฐ๋ค
mirror.displayStyle // ํ์ ์คํ์ผ (struct, class, enum ๋ฑ)
mirror.subjectType // ๋์์ ํ์
mirror.superclassMirror // ๋ถ๋ชจ ํด๋์ค์ Mirror (์๋ค๋ฉด)
๊ฐ๋จํ ์์ ๋ก ์์ํด๋ณผ๊น? ๐ฏ
struct Person {
let name: String
let age: Int
let email: String
}
let person = Person(name: "๊น์ฒ ์", age: 28, email: "chulsoo@example.com")
let mirror = Mirror(reflecting: person)
print("ํ์
: \(mirror.subjectType)")
print("ํ๋กํผํฐ ๊ฐ์: \(mirror.children.count)")
for child in mirror.children {
if let propertyName = child.label {
print("\(propertyName): \(child.value)")
}
}
์ถ๋ ฅ ๊ฒฐ๊ณผ๋ ์ด๋ ๊ฒ ๋์:
ํ์
: Person
ํ๋กํผํฐ ๊ฐ์: 3
name: ๊น์ฒ ์
age: 28
email: chulsoo@example.com
๋ณด๋ค์ํผ Mirror๋ฅผ ์ฌ์ฉํ๋ฉด ๊ฐ์ฒด์ ๋ชจ๋ ํ๋กํผํฐ๋ฅผ ์ํํ๋ฉด์ ์ด๋ฆ๊ณผ ๊ฐ์ ํ์ธํ ์ ์์ด! ๐
๐ฏ Mirror.Children: ํ๋กํผํฐ ํํํ๊ธฐ
Mirror.children์ ๊ฐ์ฒด์ ํ๋กํผํฐ๋ค์ ๋ด๊ณ ์๋ ์ปฌ๋ ์
์ด์ผ. ๊ฐ child๋ (label: String?, value: Any) ํํ์ ํํ๋ก ๊ตฌ์ฑ๋์ด ์์ง.
๐ Children ๊น์ด ํํค์น๊ธฐ
struct Product {
let id: Int
let name: String
let price: Double
let inStock: Bool
let tags: [String]
}
let product = Product(
id: 101,
name: "๋ฌด์ ์ด์ดํฐ",
price: 89000,
inStock: true,
tags: ["์ ์์ ํ", "์ค๋์ค", "๋ธ๋ฃจํฌ์ค"]
)
let mirror = Mirror(reflecting: product)
// ๊ฐ ํ๋กํผํฐ์ ํ์
๊น์ง ํ์ธํ๊ธฐ
for child in mirror.children {
let propertyName = child.label ?? "unknown"
let propertyValue = child.value
let propertyType = type(of: propertyValue)
print("\(propertyName): \(propertyValue) (ํ์
: \(propertyType))")
}
์ถ๋ ฅ:
id: 101 (ํ์
: Int)
name: ๋ฌด์ ์ด์ดํฐ (ํ์
: String)
price: 89000.0 (ํ์
: Double)
inStock: true (ํ์
: Bool)
tags: ["์ ์์ ํ", "์ค๋์ค", "๋ธ๋ฃจํฌ์ค"] (ํ์
: Array<String>)
child.label์ ์ต์
๋์ด์ผ! ํํ์ด๋ ๋ฐฐ์ด ๊ฐ์ ๊ฒฝ์ฐ์๋ label์ด nil์ผ ์ ์์ผ๋ ํญ์ ์ต์
๋ ๋ฐ์ธ๋ฉ์ ์ฌ์ฉํ๋ ๊ฒ ์์ ํด. ๐
๐๏ธ DisplayStyle: ํ์ ์ ์ข ๋ฅ ํ์ ํ๊ธฐ
Mirror๋ displayStyle์ด๋ผ๋ ํ๋กํผํฐ๋ฅผ ํตํด ๋์์ด ์ด๋ค ์ข
๋ฅ์ ํ์
์ธ์ง ์๋ ค์ค. ์ด๊ฑด ์ ๋ง ์ ์ฉํ ๊ธฐ๋ฅ์ด์ผ! ๐จ
๐ DisplayStyle์ ์ข ๋ฅ๋ค
// DisplayStyle ์ด๊ฑฐํ์ ์ผ์ด์ค๋ค
.struct // ๊ตฌ์กฐ์ฒด
.class // ํด๋์ค
.enum // ์ด๊ฑฐํ
.tuple // ํํ
.optional // ์ต์
๋
.collection // ์ปฌ๋ ์
(Array, Set ๋ฑ)
.dictionary // ๋์
๋๋ฆฌ
.set // Set
์ค์ ๋ก ์ฌ์ฉํด๋ณด์:
func analyzeType(_ value: Any) {
let mirror = Mirror(reflecting: value)
print("=== ํ์
๋ถ์ ===")
print("ํ์
: \(mirror.subjectType)")
if let style = mirror.displayStyle {
switch style {
case .struct:
print("์ด๊ฒ์ ๊ตฌ์กฐ์ฒด์
๋๋ค! ๐๏ธ")
case .class:
print("์ด๊ฒ์ ํด๋์ค์
๋๋ค! ๐")
case .enum:
print("์ด๊ฒ์ ์ด๊ฑฐํ์
๋๋ค! ๐")
case .tuple:
print("์ด๊ฒ์ ํํ์
๋๋ค! ๐ฆ")
case .optional:
print("์ด๊ฒ์ ์ต์
๋์
๋๋ค! โ")
case .collection:
print("์ด๊ฒ์ ์ปฌ๋ ์
์
๋๋ค! ๐")
case .dictionary:
print("์ด๊ฒ์ ๋์
๋๋ฆฌ์
๋๋ค! ๐")
case .set:
print("์ด๊ฒ์ Set์
๋๋ค! ๐ฏ")
@unknown default:
print("์ ์ ์๋ ํ์
์
๋๋ค!")
}
}
print("ํ๋กํผํฐ ๊ฐ์: \(mirror.children.count)\n")
}
// ํ
์คํธ
struct Book { let title: String; let pages: Int }
class Author { let name: String; init(name: String) { self.name = name } }
analyzeType(Book(title: "Swift ๋ง์คํฐ", pages: 500))
analyzeType(Author(name: "๊น๊ฐ๋ฐ"))
analyzeType([1, 2, 3, 4, 5])
analyzeType(["name": "ํ๊ธธ๋", "age": "30"])
๐ ์ฌ๊ท์ Mirror: ์ค์ฒฉ๋ ๊ตฌ์กฐ ํ์ํ๊ธฐ
์ค์ ํ๋ก์ ํธ์์๋ ๊ฐ์ฒด ์์ ๋ ๋ค๋ฅธ ๊ฐ์ฒด๊ฐ ์๋ ์ค์ฒฉ ๊ตฌ์กฐ๊ฐ ํํ์ง? Mirror๋ฅผ ์ฌ๊ท์ ์ผ๋ก ์ฌ์ฉํ๋ฉด ์ด๋ฐ ๋ณต์กํ ๊ตฌ์กฐ๋ ์๋ฒฝํ๊ฒ ํ์ํ ์ ์์ด! ๐ณ
๐ ์ค์ฒฉ ๊ตฌ์กฐ ์์ ์ ๋ณต
struct Address {
let street: String
let city: String
let zipCode: String
}
struct Company {
let name: String
let address: Address
}
struct Employee {
let name: String
let age: Int
let company: Company
}
func printObjectStructure(_ object: Any, indent: String = "") {
let mirror = Mirror(reflecting: object)
for child in mirror.children {
let label = child.label ?? "unnamed"
let value = child.value
print("\(indent)โโ \(label): ", terminator: "")
let childMirror = Mirror(reflecting: value)
// ์์์ด ์์ผ๋ฉด ์ฌ๊ท์ ์ผ๋ก ํ์
if childMirror.children.count > 0 {
print("\(type(of: value))")
printObjectStructure(value, indent: indent + "โ ")
} else {
print("\(value)")
}
}
}
let employee = Employee(
name: "์ด์ํฌ",
age: 32,
company: Company(
name: "ํ
ํฌ์คํํธ์
",
address: Address(
street: "ํ
ํค๋๋ก 123",
city: "์์ธ",
zipCode: "06234"
)
)
)
print("๐ณ ๊ฐ์ฒด ๊ตฌ์กฐ ํธ๋ฆฌ:")
printObjectStructure(employee)
์ถ๋ ฅ ๊ฒฐ๊ณผ:
๐ณ ๊ฐ์ฒด ๊ตฌ์กฐ ํธ๋ฆฌ:
โโ name: ์ด์ํฌ
โโ age: 32
โโ company: Company
โ โโ name: ํ
ํฌ์คํํธ์
โ โโ address: Address
โ โ โโ street: ํ
ํค๋๋ก 123
โ โ โโ city: ์์ธ
โ โ โโ zipCode: 06234
์! ํธ๋ฆฌ ๊ตฌ์กฐ๋ก ๊น๋ํ๊ฒ ์ถ๋ ฅ๋์ง? ์ด๋ฐ ์์ผ๋ก ์๋ฌด๋ฆฌ ๋ณต์กํ ๊ฐ์ฒด ๊ตฌ์กฐ๋ ์๋ฒฝํ๊ฒ ํ์ํ ์ ์์ด! ๐
๐ญ ํ์ ์บ์คํ ๊ณผ Mirror์ ์กฐํฉ
Mirror์ ํ์ ์บ์คํ ์ ํจ๊ป ์ฌ์ฉํ๋ฉด ์ ๋ง ๊ฐ๋ ฅํ ๊ธฐ๋ฅ๋ค์ ๋ง๋ค ์ ์์ด. ํนํ ํ๋กํ ์ฝ ๊ธฐ๋ฐ ํ๋ก๊ทธ๋๋ฐ์์ ๋น์ ๋ฐํ์ง! โจ
๐ช ํ๋กํ ์ฝ๊ณผ Mirror์ ํ์ ์กฐํฉ
protocol Describable {
var description: String { get }
}
struct User: Describable {
let id: Int
let username: String
let email: String
var description: String {
return "User #\(id): \(username)"
}
}
struct Post: Describable {
let title: String
let content: String
let author: String
var description: String {
return "'\(title)' by \(author)"
}
}
func analyzeDescribable(_ items: [Any]) {
for item in items {
let mirror = Mirror(reflecting: item)
print("\n๐ฆ ํ์
: \(mirror.subjectType)")
// Describable ํ๋กํ ์ฝ์ ์ฑํํ๋์ง ํ์ธ
if let describable = item as? Describable {
print("โ
Describable ํ๋กํ ์ฝ ์ฑํ")
print("๐ ์ค๋ช
: \(describable.description)")
} else {
print("โ Describable ํ๋กํ ์ฝ ๋ฏธ์ฑํ")
}
// ๋ชจ๋ ํ๋กํผํฐ ์ถ๋ ฅ
print("๐ ํ๋กํผํฐ:")
for child in mirror.children {
if let label = child.label {
print(" โข \(label): \(child.value)")
}
}
}
}
let user = User(id: 1, username: "swiftmaster", email: "swift@example.com")
let post = Post(title: "Mirror ์์ ์ ๋ณต", content: "...", author: "๊น๊ฐ๋ฐ")
let number = 42
analyzeDescribable([user, post, number])
Mirror๋ ์ฝ๊ธฐ ์ ์ฉ์ด์ผ! ํ๋กํผํฐ์ ๊ฐ์ ํ์ธํ ์๋ ์์ง๋ง, ์์ ํ ์๋ ์์ด. ๊ฐ์ ๋ณ๊ฒฝํ๋ ค๋ฉด ๋ค๋ฅธ ๋ฐฉ๋ฒ์ ์ฌ์ฉํด์ผ ํด. ๐
๐ ๏ธ ์ค์ ํ์ฉ: JSON ์ง๋ ฌํ ๊ตฌํํ๊ธฐ
์ด์ ๋ฐฐ์ด ๋ด์ฉ์ ํ์ฉํด์ ์ค์ ๋ก ์ ์ฉํ ๊ธฐ๋ฅ์ ๋ง๋ค์ด๋ณผ๊น? ๊ฐ๋จํ JSON ์ง๋ ฌํ ํจ์๋ฅผ ๊ตฌํํด๋ณด์! ์ด๊ฑด ์ ๋ง ์ค๋ฌด์์๋ ์์ฃผ ์ฐ์ด๋ ํจํด์ด์ผ. ๐
๐ ์ปค์คํ JSON ์ธ์ฝ๋ ๋ง๋ค๊ธฐ
func toJSON(_ object: Any) -> String {
let mirror = Mirror(reflecting: object)
guard mirror.children.count > 0 else {
// ๊ธฐ๋ณธ ํ์
์ฒ๋ฆฌ
if let string = object as? String {
return "\"\(string)\""
} else if let number = object as? CustomStringConvertible {
return "\(number)"
}
return "\"\(object)\""
}
var json = "{"
var isFirst = true
for child in mirror.children {
guard let label = child.label else { continue }
if !isFirst {
json += ","
}
isFirst = false
json += "\"\(label)\":"
let value = child.value
let valueMirror = Mirror(reflecting: value)
// ์ค์ฒฉ ๊ฐ์ฒด ์ฒ๋ฆฌ
if valueMirror.children.count > 0 {
json += toJSON(value)
} else if let string = value as? String {
json += "\"\(string)\""
} else if let array = value as? [Any] {
json += "["
json += array.map { toJSON($0) }.joined(separator: ",")
json += "]"
} else {
json += "\(value)"
}
}
json += "}"
return json
}
// ํ
์คํธ
struct BlogPost {
let id: Int
let title: String
let author: String
let tags: [String]
let published: Bool
}
let post = BlogPost(
id: 42,
title: "Swift Mirror ๋ง์คํฐํ๊ธฐ",
author: "๊น๊ฐ๋ฐ",
tags: ["Swift", "iOS", "Programming"],
published: true
)
let jsonString = toJSON(post)
print("๐ ์์ฑ๋ JSON:")
print(jsonString)
์ถ๋ ฅ:
๐ ์์ฑ๋ JSON:
{"id":42,"title":"Swift Mirror ๋ง์คํฐํ๊ธฐ","author":"๊น๊ฐ๋ฐ","tags":["Swift","iOS","Programming"],"published":true}
์๋ฒฝํ์ง? ๋ฌผ๋ก ์ค์ ํ๋ก๋์
์์๋ Codable ํ๋กํ ์ฝ์ ์ฌ์ฉํ๋ ๊ฒ ๋ ์์ ํ๊ณ ํจ์จ์ ์ด์ง๋ง, Mirror์ ์๋ฆฌ๋ฅผ ์ดํดํ๋ ๋ฐ๋ ์ด๋ฐ ์์ ๊ฐ ์ต๊ณ ์ผ! ๐ช
๐ ํ์ ์์ ์ฑ๊ณผ Mirror
Swift๋ ํ์
์์ ์ฑ์ ์ค์ํ๊ฒ ์ฌ๊ธฐ๋ ์ธ์ด์ผ. ๊ทธ๋ฐ๋ฐ Mirror๋ฅผ ์ฌ์ฉํ๋ฉด Any ํ์
์ ๋ค๋ฃจ๊ฒ ๋๋๋ฐ, ์ด๋ ์ฃผ์ํด์ผ ํ ์ ๋ค์ด ์์ด. ๐ก๏ธ
๐ฏ ์์ ํ ํ์ ๊ฒ์ฌ ํจํด
func safelyExtractValue<T>(from object: Any, key: String, as type: T.Type) -> T? {
let mirror = Mirror(reflecting: object)
for child in mirror.children {
if child.label == key {
return child.value as? T
}
}
return nil
}
// ์ฌ์ฉ ์์
struct Config {
let apiKey: String
let timeout: Int
let enableLogging: Bool
}
let config = Config(
apiKey: "abc123xyz",
timeout: 30,
enableLogging: true
)
// ์์ ํ ๊ฐ ์ถ์ถ
if let apiKey = safelyExtractValue(from: config, key: "apiKey", as: String.self) {
print("โ
API Key: \(apiKey)")
}
if let timeout = safelyExtractValue(from: config, key: "timeout", as: Int.self) {
print("โ
Timeout: \(timeout)์ด")
}
// ์๋ชป๋ ํ์
์ผ๋ก ์๋ํ๋ฉด nil ๋ฐํ
if let wrongType = safelyExtractValue(from: config, key: "timeout", as: String.self) {
print("์ด ์ฝ๋๋ ์คํ๋์ง ์์")
} else {
print("โ ํ์
๋ถ์ผ์น: timeout์ String์ด ์๋๋๋ค")
}
๐จ ๊ณ ๊ธ ํ์ฉ: ๊ฐ์ฒด ๋น๊ต ํจ์ ๋ง๋ค๊ธฐ
Mirror๋ฅผ ํ์ฉํ๋ฉด ๋ ๊ฐ์ฒด๊ฐ ๊ฐ์์ง ๋น๊ตํ๋ ๋ฒ์ฉ ํจ์๋ ๋ง๋ค ์ ์์ด. ์ด๊ฑด ํ ์คํธ ์ฝ๋ ์์ฑํ ๋ ํนํ ์ ์ฉํ์ง! ๐งช
โ๏ธ ๋ฒ์ฉ ๊ฐ์ฒด ๋น๊ต๊ธฐ
func areEqual(_ lhs: Any, _ rhs: Any) -> Bool {
let lhsMirror = Mirror(reflecting: lhs)
let rhsMirror = Mirror(reflecting: rhs)
// ํ์
์ด ๋ค๋ฅด๋ฉด false
if lhsMirror.subjectType != rhsMirror.subjectType {
return false
}
// ํ๋กํผํฐ ๊ฐ์๊ฐ ๋ค๋ฅด๋ฉด false
if lhsMirror.children.count != rhsMirror.children.count {
return false
}
// ๊ฐ ํ๋กํผํฐ ๋น๊ต
let lhsChildren = Array(lhsMirror.children)
let rhsChildren = Array(rhsMirror.children)
for (index, lhsChild) in lhsChildren.enumerated() {
let rhsChild = rhsChildren[index]
// ๋ ์ด๋ธ ๋น๊ต
if lhsChild.label != rhsChild.label {
return false
}
// ๊ฐ ๋น๊ต (์ฌ๊ท์ ์ผ๋ก)
let lhsValue = lhsChild.value
let rhsValue = rhsChild.value
// ๊ธฐ๋ณธ ํ์
๋ค์ ์ง์ ๋น๊ต
if let lhsString = lhsValue as? String,
let rhsString = rhsValue as? String {
if lhsString != rhsString { return false }
} else if let lhsInt = lhsValue as? Int,
let rhsInt = rhsValue as? Int {
if lhsInt != rhsInt { return false }
} else if let lhsBool = lhsValue as? Bool,
let rhsBool = rhsValue as? Bool {
if lhsBool != rhsBool { return false }
} else {
// ๋ณต์กํ ํ์
์ ์ฌ๊ท์ ์ผ๋ก ๋น๊ต
if !areEqual(lhsValue, rhsValue) {
return false
}
}
}
return true
}
// ํ
์คํธ
struct Point {
let x: Int
let y: Int
}
let point1 = Point(x: 10, y: 20)
let point2 = Point(x: 10, y: 20)
let point3 = Point(x: 15, y: 25)
print("point1 == point2: \(areEqual(point1, point2))") // true
print("point1 == point3: \(areEqual(point1, point3))") // false
๐ SuperclassMirror: ์์ ๊ตฌ์กฐ ํ์ํ๊ธฐ
ํด๋์ค์ ๊ฒฝ์ฐ ์์ ๊ด๊ณ๊ฐ ์์ ์ ์์ง? Mirror๋ superclassMirror ํ๋กํผํฐ๋ฅผ ํตํด ๋ถ๋ชจ ํด๋์ค์ ์ ๋ณด๋ ์ ๊ทผํ ์ ์๊ฒ ํด์ค! ๐ฐ
๐จโ๐ฉโ๐งโ๐ฆ ์์ ์ฒด์ธ ๋ฐ๋ผ๊ฐ๊ธฐ
class Animal {
let species: String
let age: Int
init(species: String, age: Int) {
self.species = species
self.age = age
}
}
class Dog: Animal {
let name: String
let breed: String
init(name: String, breed: String, age: Int) {
self.name = name
self.breed = breed
super.init(species: "Canis familiaris", age: age)
}
}
class Puppy: Dog {
let isVaccinated: Bool
init(name: String, breed: String, age: Int, isVaccinated: Bool) {
self.isVaccinated = isVaccinated
super.init(name: name, breed: breed, age: age)
}
}
func printInheritanceChain(_ object: Any) {
var currentMirror: Mirror? = Mirror(reflecting: object)
var level = 0
while let mirror = currentMirror {
let indent = String(repeating: " ", count: level)
print("\(indent)๐ฆ \(mirror.subjectType)")
for child in mirror.children {
if let label = child.label {
print("\(indent) โโ \(label): \(child.value)")
}
}
currentMirror = mirror.superclassMirror
level += 1
if currentMirror != nil {
print("\(indent) โฌ๏ธ ์์")
}
}
}
let puppy = Puppy(
name: "์ด์ฝ",
breed: "๊ณจ๋ ๋ฆฌํธ๋ฆฌ๋ฒ",
age: 1,
isVaccinated: true
)
print("๐ ์์ ๊ตฌ์กฐ ๋ถ์:")
printInheritanceChain(puppy)
์ถ๋ ฅ:
๐ ์์ ๊ตฌ์กฐ ๋ถ์:
๐ฆ Puppy
โโ isVaccinated: true
โฌ๏ธ ์์
๐ฆ Dog
โโ name: ์ด์ฝ
โโ breed: ๊ณจ๋ ๋ฆฌํธ๋ฆฌ๋ฒ
โฌ๏ธ ์์
๐ฆ Animal
โโ species: Canis familiaris
โโ age: 1
์๋ฒฝํ๊ฒ ์์ ์ฒด์ธ์ ๋ฐ๋ผ๊ฐ๋ฉด์ ๋ชจ๋ ํ๋กํผํฐ๋ฅผ ํ์ธํ ์ ์์ด! ์ด๋ฐ ๊ธฐ๋ฅ์ ๋๋ฒ๊น ์ด๋ ๋ก๊น ์์คํ ์ ๋ง๋ค ๋ ์ ๋ง ์ ์ฉํด. ๐ฏ
๐ช ์ด๊ฑฐํ(Enum)๊ณผ Mirror
์ด๊ฑฐํ์ Swift์์ ์ ๋ง ๊ฐ๋ ฅํ ํ์ ์ด์ง? Mirror๋ฅผ ์ฌ์ฉํ๋ฉด ์ด๊ฑฐํ์ associated value๋ ์ฝ๊ฒ ์ถ์ถํ ์ ์์ด! ๐ญ
๐ฒ Associated Value ์ถ์ถํ๊ธฐ
enum NetworkResponse {
case success(data: Data, statusCode: Int)
case failure(error: Error, statusCode: Int)
case loading
case idle
}
struct MockError: Error {
let message: String
}
func analyzeResponse(_ response: NetworkResponse) {
let mirror = Mirror(reflecting: response)
print("๐ ์๋ต ๋ถ์")
print("ํ์
: \(mirror.subjectType)")
print("DisplayStyle: \(mirror.displayStyle ?? .enum)")
// ์ด๊ฑฐํ ์ผ์ด์ค ์ด๋ฆ ์ถ์ถ
if let firstChild = mirror.children.first {
print("์ผ์ด์ค: \(firstChild.label ?? "unknown")")
// Associated values ์ถ์ถ
let associatedMirror = Mirror(reflecting: firstChild.value)
if associatedMirror.children.count > 0 {
print("Associated Values:")
for (index, child) in associatedMirror.children.enumerated() {
let label = child.label ?? "value\(index)"
print(" โข \(label): \(child.value)")
}
} else {
print("Associated Values ์์")
}
}
print()
}
// ํ
์คํธ
let successResponse = NetworkResponse.success(
data: Data(),
statusCode: 200
)
let failureResponse = NetworkResponse.failure(
error: MockError(message: "๋คํธ์ํฌ ์ค๋ฅ"),
statusCode: 500
)
let loadingResponse = NetworkResponse.loading
analyzeResponse(successResponse)
analyzeResponse(failureResponse)
analyzeResponse(loadingResponse)
์ถ๋ ฅ:
๐ ์๋ต ๋ถ์
ํ์
: NetworkResponse
DisplayStyle: enum
์ผ์ด์ค: success
Associated Values:
โข data: 0 bytes
โข statusCode: 200
๐ ์๋ต ๋ถ์
ํ์
: NetworkResponse
DisplayStyle: enum
์ผ์ด์ค: failure
Associated Values:
โข error: MockError(message: "๋คํธ์ํฌ ์ค๋ฅ")
โข statusCode: 500
๐ ์๋ต ๋ถ์
ํ์
: NetworkResponse
DisplayStyle: enum
์ผ์ด์ค: loading
Associated Values ์์
๐ ์ฑ๋ฅ ๊ณ ๋ ค์ฌํญ
Mirror๋ ์ ๋ง ๊ฐ๋ ฅํ์ง๋ง, ์ฑ๋ฅ ์ธก๋ฉด์์ ์ฃผ์ํด์ผ ํ ์ ๋ค์ด ์์ด. ๋ฆฌํ๋ ์ ์ ๋ณธ์ง์ ์ผ๋ก ๋ฐํ์ ์ค๋ฒํค๋๊ฐ ์๊ฑฐ๋ ! โก
1. ๋ฐํ์ ์ค๋ฒํค๋
Mirror๋ฅผ ์ฌ์ฉํ ํ์ ๊ฒ์ฌ๋ ์ปดํ์ผ ํ์ ํ์ ๊ฒ์ฌ๋ณด๋ค ๋๋ ค. ์ฑ๋ฅ์ด ์ค์ํ ๋ฃจํ ์์์๋ ์ฌ์ฉ์ ์์ ํ์.
2. ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ
Mirror ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ฉด ์ถ๊ฐ ๋ฉ๋ชจ๋ฆฌ๊ฐ ํ ๋น๋ผ. ๋๋์ ๊ฐ์ฒด๋ฅผ ์ฒ๋ฆฌํ ๋๋ ์ฃผ์๊ฐ ํ์ํด.
3. ์ต์ ํ ์ ํ
์ปดํ์ผ๋ฌ๊ฐ ๋ฆฌํ๋ ์ ์ฝ๋๋ฅผ ์ต์ ํํ๊ธฐ ์ด๋ ค์. ๊ฐ๋ฅํ๋ฉด ์ ์ ํ์ ์์คํ ์ ํ์ฉํ์.
4. ๋์ ๊ณ ๋ ค
โข Codable: JSON ์ง๋ ฌํ/์ญ์ง๋ ฌํ
โข ํ๋กํ ์ฝ: ํ์ ๋ณ ๋์ ์ ์
โข ์ ๋ค๋ฆญ: ํ์ ์์ ํ ๋ฒ์ฉ ์ฝ๋
๐ ์ฑ๋ฅ ๋น๊ต ์์
import Foundation
struct TestData {
let id: Int
let name: String
let value: Double
}
// Mirror ์ฌ์ฉ
func extractWithMirror(_ data: TestData) -> String {
let mirror = Mirror(reflecting: data)
var result = ""
for child in mirror.children {
if let label = child.label {
result += "\(label): \(child.value), "
}
}
return result
}
// ์ง์ ์ ๊ทผ
func extractDirect(_ data: TestData) -> String {
return "id: \(data.id), name: \(data.name), value: \(data.value)"
}
let testData = TestData(id: 1, name: "ํ
์คํธ", value: 3.14)
// ์ฑ๋ฅ ์ธก์
let iterations = 100_000
let mirrorStart = Date()
for _ in 0..<iterations {
_ = extractWithMirror(testData)
}
let mirrorTime = Date().timeIntervalSince(mirrorStart)
let directStart = Date()
for _ in 0..<iterations {
_ = extractDirect(testData)
}
let directTime = Date().timeIntervalSince(directStart)
print("๐ ์ฑ๋ฅ ๋น๊ต (\(iterations)ํ ๋ฐ๋ณต)")
print("Mirror ์ฌ์ฉ: \(String(format: "%.4f", mirrorTime))์ด")
print("์ง์ ์ ๊ทผ: \(String(format: "%.4f", directTime))์ด")
print("์ฐจ์ด: \(String(format: "%.1f", mirrorTime / directTime))๋ฐฐ")
์ผ๋ฐ์ ์ผ๋ก Mirror๋ฅผ ์ฌ์ฉํ ๋ฐฉ์์ด ์ง์ ์ ๊ทผ๋ณด๋ค 10~20๋ฐฐ ์ ๋ ๋๋ ค. ํ์ง๋ง ์ด๊ฑด ๊ทน๋จ์ ์ธ ๋ฐ๋ณต ์ํฉ์ด๊ณ , ์ค์ ์ฑ์์๋ ๋๋ถ๋ถ ๋ฌธ์ ๊ฐ ์ ๋ผ. ์ ์ฌ์ ์์ ์ฌ์ฉํ๋ฉด ๋ผ! ๐
๐ฏ ์ค์ ํ๋ก์ ํธ: ๋๋ฒ๊น ๋๊ตฌ ๋ง๋ค๊ธฐ
์ด์ ๋ฐฐ์ด ๋ชจ๋ ๋ด์ฉ์ ์ข ํฉํด์ ์ค์ฉ์ ์ธ ๋๋ฒ๊น ๋๊ตฌ๋ฅผ ๋ง๋ค์ด๋ณด์! ์ด๊ฑด ์ค์ ํ๋ก์ ํธ์์ ๋ฐ๋ก ์ฌ์ฉํ ์ ์๋ ์์ค์ด์ผ. ๐ ๏ธ
๐ง ์์ ํ ๊ฐ์ฒด ๋คํ ์ ํธ๋ฆฌํฐ
class ObjectDumper {
enum DumpStyle {
case compact
case detailed
case json
}
static func dump(_ object: Any, style: DumpStyle = .detailed) -> String {
switch style {
case .compact:
return dumpCompact(object)
case .detailed:
return dumpDetailed(object)
case .json:
return dumpJSON(object)
}
}
// ๊ฐ๋จํ ํ์
private static func dumpCompact(_ object: Any) -> String {
let mirror = Mirror(reflecting: object)
var result = "\(mirror.subjectType)("
let properties = mirror.children.compactMap { child -> String? in
guard let label = child.label else { return nil }
return "\(label): \(child.value)"
}
result += properties.joined(separator: ", ")
result += ")"
return result
}
// ์์ธํ ํ์
private static func dumpDetailed(_ object: Any, indent: Int = 0) -> String {
let mirror = Mirror(reflecting: object)
let indentStr = String(repeating: " ", count: indent)
var result = ""
if indent == 0 {
result += "โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n"
result += "โ Object Dump - Detailed View โ\n"
result += "โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n\n"
}
result += "\(indentStr)๐ฆ Type: \(mirror.subjectType)\n"
if let displayStyle = mirror.displayStyle {
result += "\(indentStr)๐ท๏ธ Style: \(displayStyle)\n"
}
if mirror.children.count > 0 {
result += "\(indentStr)๐ Properties (\(mirror.children.count)):\n"
for child in mirror.children {
let label = child.label ?? "unnamed"
let value = child.value
let valueMirror = Mirror(reflecting: value)
result += "\(indentStr) โข \(label): "
if valueMirror.children.count > 0 {
result += "\n"
result += dumpDetailed(value, indent: indent + 2)
} else {
result += "\(value) (\(type(of: value)))\n"
}
}
}
// ์์ ์ ๋ณด
if let superMirror = mirror.superclassMirror {
result += "\(indentStr)โฌ๏ธ Inherits from:\n"
result += dumpDetailed(superMirror, indent: indent + 1)
}
return result
}
// JSON ํ์
private static func dumpJSON(_ object: Any, indent: Int = 0) -> String {
let mirror = Mirror(reflecting: object)
let indentStr = String(repeating: " ", count: indent)
guard mirror.children.count > 0 else {
if let string = object as? String {
return "\"\(string)\""
}
return "\(object)"
}
var json = "{\n"
let properties = Array(mirror.children)
for (index, child) in properties.enumerated() {
guard let label = child.label else { continue }
json += "\(indentStr) \"\(label)\": "
let value = child.value
let valueMirror = Mirror(reflecting: value)
if valueMirror.children.count > 0 {
json += dumpJSON(value, indent: indent + 1)
} else if let string = value as? String {
json += "\"\(string)\""
} else if let array = value as? [Any] {
json += "[\n"
for (i, item) in array.enumerated() {
json += "\(indentStr) \(dumpJSON(item, indent: indent + 2))"
if i < array.count - 1 { json += "," }
json += "\n"
}
json += "\(indentStr) ]"
} else {
json += "\(value)"
}
if index < properties.count - 1 {
json += ","
}
json += "\n"
}
json += "\(indentStr)}"
return json
}
}
// ํ
์คํธ์ฉ ๋ณต์กํ ๊ตฌ์กฐ
struct Project {
let name: String
let version: String
let contributors: [String]
let settings: Settings
}
struct Settings {
let debugMode: Bool
let maxConnections: Int
let apiEndpoint: String
}
let project = Project(
name: "SwiftMirrorDemo",
version: "1.0.0",
contributors: ["๊น๊ฐ๋ฐ", "์ด์ฝ๋ฉ", "๋ฐํ๋ก๊ทธ๋๋จธ"],
settings: Settings(
debugMode: true,
maxConnections: 100,
apiEndpoint: "https://api.example.com"
)
)
print("=== COMPACT ===")
print(ObjectDumper.dump(project, style: .compact))
print("\n=== DETAILED ===")
print(ObjectDumper.dump(project, style: .detailed))
print("\n=== JSON ===")
print(ObjectDumper.dump(project, style: .json))
์ด ๋๊ตฌ๋ ์ค์ ๋ก ๋๋ฒ๊น ํ ๋ ์์ฒญ ์ ์ฉํด! ๋ณต์กํ ๊ฐ์ฒด ๊ตฌ์กฐ๋ฅผ ํ๋์ ํ์ ํ ์ ์๊ฑฐ๋ . ๐
๐ Mirror์ ํ๊ณ์ ๋์
Mirror๋ ๊ฐ๋ ฅํ์ง๋ง ์๋ฒฝํ์ง๋ ์์. ๋ช ๊ฐ์ง ํ๊ณ์ ์ด ์๊ณ , ์ํฉ์ ๋ฐ๋ผ ๋ ๋์ ๋์์ด ์์ ์ ์์ด. ๐ค
๐ง Mirror์ ํ๊ณ์
Mirror๋ก๋ ํ๋กํผํฐ๋ฅผ ์ฝ์ ์๋ง ์๊ณ ์์ ํ ์ ์์ด. ๊ฐ์ ๋ณ๊ฒฝํ๋ ค๋ฉด KeyPath๋ ๋ค๋ฅธ ๋ฐฉ๋ฒ์ ์ฌ์ฉํด์ผ ํด.
2. ๋ฉํ๋ฐ์ดํฐ ์ ํ
ํ๋กํผํฐ์ ์ ๊ทผ ์ ์ด์(private, public ๋ฑ)๋ ์์ฑ(lazy, computed ๋ฑ)์ ์ ์ ์์ด.
3. ๋ฉ์๋ ์ ๋ณด ์์
Mirror๋ ์ ์ฅ ํ๋กํผํฐ๋ง ๋ณด์ฌ์ค. ๋ฉ์๋๋ ๊ณ์ฐ ํ๋กํผํฐ๋ ์ ๊ทผํ ์ ์์ด.
4. ํ์ ์ ๋ณด ์ ํ
์ ๋ค๋ฆญ ํ์ ์ ๊ตฌ์ฒด์ ์ธ ํ์ ํ๋ผ๋ฏธํฐ๋ฅผ ์์ ํ ํ์ ํ๊ธฐ ์ด๋ ค์.
โจ ์ํฉ๋ณ ๋์
// 1. ๊ฐ ์์ ์ด ํ์ํ ๋: KeyPath ์ฌ์ฉ
struct User {
var name: String
var age: Int
}
var user = User(name: "ํ๊ธธ๋", age: 30)
let nameKeyPath = \User.name
// KeyPath๋ก ๊ฐ ์ฝ๊ธฐ์ ์ฐ๊ธฐ
print(user[keyPath: nameKeyPath]) // "ํ๊ธธ๋"
user[keyPath: nameKeyPath] = "๊น์ฒ ์"
print(user[keyPath: nameKeyPath]) // "๊น์ฒ ์"
// 2. ์ง๋ ฌํ๊ฐ ํ์ํ ๋: Codable ์ฌ์ฉ
struct Product: Codable {
let id: Int
let name: String
let price: Double
}
let product = Product(id: 1, name: "๋
ธํธ๋ถ", price: 1500000)
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
if let jsonData = try? encoder.encode(product),
let jsonString = String(data: jsonData, encoding: .utf8) {
print(jsonString)
}
// 3. ํ์
๋ณ ๋์์ด ํ์ํ ๋: ํ๋กํ ์ฝ ์ฌ์ฉ
protocol Printable {
func prettyPrint() -> String
}
extension User: Printable {
func prettyPrint() -> String {
return "๐ค \(name) (๋์ด: \(age))"
}
}
extension Product: Printable {
func prettyPrint() -> String {
return "๐๏ธ \(name) - โฉ\(price)"
}
}
// 4. ๋ฒ์ฉ ์ฒ๋ฆฌ๊ฐ ํ์ํ ๋: ์ ๋ค๋ฆญ ์ฌ์ฉ
func process<T>(_ items: [T], transform: (T) -> String) {
for item in items {
print(transform(item))
}
}
๐ผ ์ค๋ฌด ํ์ฉ ์ฌ๋ก
์ด๋ก ์ ์ถฉ๋ถํ ๋ฐฐ์ ์ผ๋, ์ด์ ์ค์ ํ๋ก์ ํธ์์ Mirror๋ฅผ ์ด๋ป๊ฒ ํ์ฉํ๋์ง ์ดํด๋ณด์! ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์ ๊ฐ๋ฐํ๋ค๊ณ ๊ฐ์ ํ๊ณ ์ค์ฉ์ ์ธ ์์ ๋ฅผ ๋ง๋ค์ด๋ณผ๊ฒ. ๐จ
๐ฏ ์ฌ๋ก 1: ํผ ๊ฒ์ฆ ์์คํ
protocol Validatable {
func validate() -> [String] // ์๋ฌ ๋ฉ์์ง ๋ฐฐ์ด
}
struct ValidationRule {
let fieldName: String
let validator: (Any) -> String?
}
class FormValidator {
static func validate(_ object: Any, rules: [ValidationRule]) -> [String: String] {
let mirror = Mirror(reflecting: object)
var errors: [String: String] = [:]
for rule in rules {
// ํด๋น ํ๋ ์ฐพ๊ธฐ
for child in mirror.children {
if child.label == rule.fieldName {
if let error = rule.validator(child.value) {
errors[rule.fieldName] = error
}
break
}
}
}
return errors
}
// ์ผ๋ฐ์ ์ธ ๊ฒ์ฆ ๊ท์น๋ค
static func required(_ value: Any) -> String? {
if let string = value as? String, string.isEmpty {
return "ํ์ ์
๋ ฅ ํญ๋ชฉ์
๋๋ค"
}
return nil
}
static func minLength(_ min: Int) -> (Any) -> String? {
return { value in
if let string = value as? String, string.count < min {
return "์ต์ \(min)์ ์ด์ ์
๋ ฅํด์ฃผ์ธ์"
}
return nil
}
}
static func email(_ value: Any) -> String? {
guard let string = value as? String else { return nil }
let emailRegex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
let predicate = NSPredicate(format: "SELF MATCHES %@", emailRegex)
if !predicate.evaluate(with: string) {
return "์ฌ๋ฐ๋ฅธ ์ด๋ฉ์ผ ํ์์ด ์๋๋๋ค"
}
return nil
}
}
// ์ฌ๋ฅ๋ท ํ์๊ฐ์
ํผ
struct SignupForm {
let username: String
let email: String
let password: String
let bio: String
}
let form = SignupForm(
username: "๊ฐ๋ฐ์",
email: "invalid-email",
password: "123",
bio: "์๋
ํ์ธ์"
)
let rules = [
ValidationRule(fieldName: "username", validator: FormValidator.required),
ValidationRule(fieldName: "username", validator: FormValidator.minLength(2)),
ValidationRule(fieldName: "email", validator: FormValidator.email),
ValidationRule(fieldName: "password", validator: FormValidator.minLength(8))
]
let errors = FormValidator.validate(form, rules: rules)
if errors.isEmpty {
print("โ
๊ฒ์ฆ ํต๊ณผ!")
} else {
print("โ ๊ฒ์ฆ ์คํจ:")
for (field, error) in errors {
print(" โข \(field): \(error)")
}
}
์ถ๋ ฅ:
โ ๊ฒ์ฆ ์คํจ:
โข email: ์ฌ๋ฐ๋ฅธ ์ด๋ฉ์ผ ํ์์ด ์๋๋๋ค
โข password: ์ต์ 8์ ์ด์ ์
๋ ฅํด์ฃผ์ธ์
๐ฏ ์ฌ๋ก 2: ์๋ ๋ก๊น ์์คํ
enum LogLevel: String {
case debug = "๐"
case info = "โน๏ธ"
case warning = "โ ๏ธ"
case error = "โ"
}
class AutoLogger {
static func log(_ object: Any, level: LogLevel = .info, context: String = "") {
let timestamp = DateFormatter.localizedString(
from: Date(),
dateStyle: .none,
timeStyle: .medium
)
print("\(level.rawValue) [\(timestamp)] \(context)")
print(generateLogMessage(object, indent: 1))
print(String(repeating: "โ", count: 50))
}
private static func generateLogMessage(_ object: Any, indent: Int) -> String {
let mirror = Mirror(reflecting: object)
let indentStr = String(repeating: " ", count: indent)
var message = ""
if mirror.children.count == 0 {
return "\(indentStr)\(object)"
}
message += "\(indentStr)Type: \(mirror.subjectType)\n"
for child in mirror.children {
let label = child.label ?? "value"
let value = child.value
let valueMirror = Mirror(reflecting: value)
if valueMirror.children.count > 0 {
message += "\(indentStr)\(label):\n"
message += generateLogMessage(value, indent: indent + 1)
} else {
message += "\(indentStr)\(label): \(value)\n"
}
}
return message
}
}
// ์ฌ๋ฅ๋ท ๊ฑฐ๋ ์ ๋ณด
struct Transaction {
let id: String
let buyer: String
let seller: String
let amount: Int
let status: String
let timestamp: Date
}
let transaction = Transaction(
id: "TXN-2024-001",
buyer: "๊น๊ตฌ๋งค์",
seller: "์ดํ๋งค์",
amount: 50000,
status: "completed",
timestamp: Date()
)
AutoLogger.log(transaction, level: .info, context: "๊ฑฐ๋ ์๋ฃ")
๐ฎ ๊ณ ๊ธ ํจํด: ์ปค์คํ Mirror ๊ตฌํ
์ฌ์ค Mirror๋ CustomReflectable ํ๋กํ ์ฝ์ ์ฑํํ๋ฉด ์ปค์คํฐ๋ง์ด์งํ ์ ์์ด! ์ด๊ฑธ ํ์ฉํ๋ฉด ๋ฏผ๊ฐํ ์ ๋ณด๋ฅผ ์จ๊ธฐ๊ฑฐ๋ ํน๋ณํ ํํ ๋ฐฉ์์ ์ ์ํ ์ ์์ง. ๐ญ
๐จ CustomReflectable ํ์ฉํ๊ธฐ
struct SecureUser: CustomReflectable {
let id: Int
let username: String
let email: String
private let password: String
private let apiKey: String
// ์ปค์คํ
Mirror ์ ์
var customMirror: Mirror {
return Mirror(
self,
children: [
"id": id,
"username": username,
"email": email,
"password": "********", // ๋น๋ฐ๋ฒํธ ์จ๊น
"apiKey": "********" // API ํค ์จ๊น
],
displayStyle: .struct
)
}
init(id: Int, username: String, email: String, password: String, apiKey: String) {
self.id = id
self.username = username
self.email = email
self.password = password
self.apiKey = apiKey
}
}
let secureUser = SecureUser(
id: 1,
username: "secure_user",
email: "user@example.com",
password: "super_secret_password",
apiKey: "sk_live_abc123xyz789"
)
// Mirror๋ก ํ์ธํ๋ฉด ๋ฏผ๊ฐํ ์ ๋ณด๊ฐ ์จ๊ฒจ์ ธ ์์
let mirror = Mirror(reflecting: secureUser)
print("๐ ๋ณด์ ์ฌ์ฉ์ ์ ๋ณด:")
for child in mirror.children {
if let label = child.label {
print(" \(label): \(child.value)")
}
}
์ถ๋ ฅ:
๐ ๋ณด์ ์ฌ์ฉ์ ์ ๋ณด:
id: 1
username: secure_user
email: user@example.com
password: ********
apiKey: ********
์๋ฒฝํ์ง? ์ด๋ ๊ฒ ํ๋ฉด ๋ก๊น ์ด๋ ๋๋ฒ๊น ์์๋ ๋ฏผ๊ฐํ ์ ๋ณด๊ฐ ๋ ธ์ถ๋์ง ์์! ๐ก๏ธ
๐ ๋ฒ ์คํธ ํ๋ํฐ์ค
์ง๊ธ๊น์ง ๋ฐฐ์ด ๋ด์ฉ์ ๋ฐํ์ผ๋ก Mirror๋ฅผ ํจ๊ณผ์ ์ผ๋ก ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ์ ๋ฆฌํด๋ณผ๊ฒ! ๐
โ ๊ถ์ฅ์ฌํญ
โข ๋๋ฒ๊น ๋ฐ ๋ก๊น
โข ๊ฐ๋ฐ ๋๊ตฌ ์ ์
โข ๋ฒ์ฉ ์ง๋ ฌํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
โข ํ ์คํธ ์ ํธ๋ฆฌํฐ
2. ์ฑ๋ฅ ๊ณ ๋ ค
โข ํซ ํจ์ค(์์ฃผ ์คํ๋๋ ์ฝ๋)์์๋ ์ฌ์ฉ ์์
โข ๊ฐ๋ฅํ๋ฉด ๊ฒฐ๊ณผ๋ฅผ ์บ์ฑ
โข ํ๋ก๋์ ์ฝ๋๋ณด๋ค๋ ๊ฐ๋ฐ/๋๋ฒ๊น ์ฉ๋๋ก
3. ํ์ ์์ ์ฑ ์ ์ง
โข ๊ฐ๋ฅํ ํ ์ ๋ค๋ฆญ๊ณผ ํ๋กํ ์ฝ ํ์ฉ
โข Any ํ์ ์ฌ์ฉ ์ ํญ์ ํ์ ์ฒดํฌ
โข ์ต์ ๋ ๋ฐ์ธ๋ฉ ์ ๊ทน ํ์ฉ
4. ๋ณด์ ๊ณ ๋ ค
โข ๋ฏผ๊ฐํ ์ ๋ณด๋ CustomReflectable๋ก ์จ๊ธฐ๊ธฐ
โข ๋ก๊ทธ์ ๊ฐ์ธ์ ๋ณด ๋ ธ์ถ ์ฃผ์
โข ํ๋ก๋์ ํ๊ฒฝ์์๋ ๋ก๊น ๋ ๋ฒจ ์กฐ์
โ ํผํด์ผ ํ ๊ฒ๋ค
Mirror์ ๋๋ฌด ์์กดํ๋ฉด ์ฝ๋๊ฐ ์ทจ์ฝํด์ ธ. ๊ฐ๋ฅํ๋ฉด ์ ์ ํ์ ์์คํ ์ ํ์ฉํ์.
2. ํ๋ก๋์ ํต์ฌ ๋ก์ง์ ์ฌ์ฉ
๋น์ฆ๋์ค ๋ก์ง์ ํต์ฌ ๋ถ๋ถ์์๋ Mirror ์ฌ์ฉ์ ํผํ์. ์ฑ๋ฅ๊ณผ ์์ ์ฑ ๋ฌธ์ ๊ฐ ์๊ธธ ์ ์์ด.
3. ๊ฐ ์์ ์๋
Mirror๋ ์ฝ๊ธฐ ์ ์ฉ์ด์ผ. ๊ฐ์ ์์ ํ๋ ค๊ณ ์ต์ง๋ก ์ฐํํ์ง ๋ง๊ณ ์ ์ ํ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ์.
4. ๋ณต์กํ ํ์ ์ถ๋ก
์ ๋ค๋ฆญ์ด๋ ๋ณต์กํ ํ์ ์ ๊ฒฝ์ฐ Mirror๋ง์ผ๋ก๋ ์์ ํ ํ์ ์ ๋ณด๋ฅผ ์ป๊ธฐ ์ด๋ ค์.
๐ ์ค์ ํ๋ก์ ํธ: ์์ ํ ๊ฐ์ฒด ๋น๊ต ํ๋ ์์ํฌ
๋ง์ง๋ง์ผ๋ก ์ง๊ธ๊น์ง ๋ฐฐ์ด ๋ชจ๋ ๋ด์ฉ์ ํ์ฉํด์ ์์ ํ ๊ฐ์ฒด ๋น๊ต ํ๋ ์์ํฌ๋ฅผ ๋ง๋ค์ด๋ณด์! ์ด๊ฑด ํ ์คํธ ์ฝ๋ ์์ฑํ ๋ ์ ๋ง ์ ์ฉํ ๊ฑฐ์ผ. ๐
๐ฏ ์์ฑ๋ ๋์ ๋น๊ต ํ๋ ์์ํฌ
struct ComparisonResult {
let isEqual: Bool
let differences: [String]
var description: String {
if isEqual {
return "โ
๊ฐ์ฒด๊ฐ ๋์ผํฉ๋๋ค"
} else {
var result = "โ ๊ฐ์ฒด๊ฐ ๋ค๋ฆ
๋๋ค\n์ฐจ์ด์ :\n"
for diff in differences {
result += " โข \(diff)\n"
}
return result
}
}
}
class ObjectComparator {
static func compare(_ lhs: Any, _ rhs: Any, path: String = "root") -> ComparisonResult {
var differences: [String] = []
let lhsMirror = Mirror(reflecting: lhs)
let rhsMirror = Mirror(reflecting: rhs)
// ํ์
๋น๊ต
if lhsMirror.subjectType != rhsMirror.subjectType {
differences.append("\(path): ํ์
๋ถ์ผ์น (\(lhsMirror.subjectType) != \(rhsMirror.subjectType))")
return ComparisonResult(isEqual: false, differences: differences)
}
// ์์์ด ์๋ ๊ฒฝ์ฐ (๊ธฐ๋ณธ ํ์
)
if lhsMirror.children.count == 0 {
if !areValuesEqual(lhs, rhs) {
differences.append("\(path): ๊ฐ ๋ถ์ผ์น (\(lhs) != \(rhs))")
}
return ComparisonResult(isEqual: differences.isEmpty, differences: differences)
}
// ํ๋กํผํฐ ๊ฐ์ ๋น๊ต
if lhsMirror.children.count != rhsMirror.children.count {
differences.append("\(path): ํ๋กํผํฐ ๊ฐ์ ๋ถ์ผ์น")
return ComparisonResult(isEqual: false, differences: differences)
}
// ๊ฐ ํ๋กํผํฐ ๋น๊ต
let lhsChildren = Array(lhsMirror.children)
let rhsChildren = Array(rhsMirror.children)
for (index, lhsChild) in lhsChildren.enumerated() {
let rhsChild = rhsChildren[index]
let label = lhsChild.label ?? "[\(index)]"
let newPath = "\(path).\(label)"
// ๋ ์ด๋ธ ๋น๊ต
if lhsChild.label != rhsChild.label {
differences.append("\(newPath): ํ๋กํผํฐ ์ด๋ฆ ๋ถ์ผ์น")
continue
}
// ์ฌ๊ท์ ์ผ๋ก ๊ฐ ๋น๊ต
let childResult = compare(lhsChild.value, rhsChild.value, path: newPath)
differences.append(contentsOf: childResult.differences)
}
return ComparisonResult(isEqual: differences.isEmpty, differences: differences)
}
private static func areValuesEqual(_ lhs: Any, _ rhs: Any) -> Bool {
// String ๋น๊ต
if let lhsString = lhs as? String, let rhsString = rhs as? String {
return lhsString == rhsString
}
// Int ๋น๊ต
if let lhsInt = lhs as? Int, let rhsInt = rhs as? Int {
return lhsInt == rhsInt
}
// Double ๋น๊ต
if let lhsDouble = lhs as? Double, let rhsDouble = rhs as? Double {
return abs(lhsDouble - rhsDouble) < 0.0001
}
// Bool ๋น๊ต
if let lhsBool = lhs as? Bool, let rhsBool = rhs as? Bool {
return lhsBool == rhsBool
}
// ๋ฐฐ์ด ๋น๊ต
if let lhsArray = lhs as? [Any], let rhsArray = rhs as? [Any] {
if lhsArray.count != rhsArray.count { return false }
for (l, r) in zip(lhsArray, rhsArray) {
if !areValuesEqual(l, r) { return false }
}
return true
}
// ๊ธฐ๋ณธ์ ์ผ๋ก ๋ฌธ์์ด ํํ ๋น๊ต
return "\(lhs)" == "\(rhs)"
}
}
// ํ
์คํธ
struct UserProfile {
let name: String
let age: Int
let email: String
let preferences: Preferences
}
struct Preferences {
let theme: String
let notifications: Bool
let language: String
}
let profile1 = UserProfile(
name: "๊น์ฒ ์",
age: 28,
email: "chulsoo@example.com",
preferences: Preferences(
theme: "dark",
notifications: true,
language: "ko"
)
)
let profile2 = UserProfile(
name: "๊น์ฒ ์",
age: 29, // ๋ค๋ฆ!
email: "chulsoo@example.com",
preferences: Preferences(
theme: "light", // ๋ค๋ฆ!
notifications: true,
language: "ko"
)
)
let result = ObjectComparator.compare(profile1, profile2)
print(result.description)
์ถ๋ ฅ:
โ ๊ฐ์ฒด๊ฐ ๋ค๋ฆ
๋๋ค
์ฐจ์ด์ :
โข root.age: ๊ฐ ๋ถ์ผ์น (28 != 29)
โข root.preferences.theme: ๊ฐ ๋ถ์ผ์น (dark != light)
์๋ฒฝํ์ง? ์ด ํ๋ ์์ํฌ๋ ์ค์ฒฉ๋ ๊ฐ์ฒด ๊ตฌ์กฐ์์๋ ์ ํํ ์ด๋ค ๋ถ๋ถ์ด ๋ค๋ฅธ์ง ์๋ ค์ค! ํ ์คํธ ์ฝ๋ ์์ฑํ ๋ ์ ๋ง ์ ์ฉํ๊ฒ ์ธ ์ ์์ด. ๐
๐ฌ ๋ง๋ฌด๋ฆฌํ๋ฉฐ
์, ์ฌ๊ธฐ๊น์ง Swift์ ๋ฆฌํ๋ ์
๊ณผ Mirror์ ๋ํด ๊น์ด ์๊ฒ ์์๋ดค์ด! ๐
์ฒ์์๋ ๋ณต์กํด ๋ณด์ผ ์ ์์ง๋ง, ์ค์ ๋ก ์ฌ์ฉํด๋ณด๋ฉด ์ ๋ง ๊ฐ๋ ฅํ ๋๊ตฌ๋ผ๋ ๊ฑธ ์๊ฒ ๋ ๊ฑฐ์ผ. ํนํ ๋๋ฒ๊น
๋๊ตฌ๋ ํ
์คํธ ์ ํธ๋ฆฌํฐ๋ฅผ ๋ง๋ค ๋, ๋๋ ๋ฒ์ฉ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๊ฐ๋ฐํ ๋ Mirror๋ ์์ด์๋ ์ ๋ ์กด์ฌ์ง! ๐ช
1. Mirror์ ๋ณธ์ง
๋ฐํ์์ ๊ฐ์ฒด์ ๊ตฌ์กฐ๋ฅผ ๋ค์ฌ๋ค๋ณผ ์ ์๋ ๋ฆฌํ๋ ์ ๋๊ตฌ
2. ์ฃผ์ ๊ธฐ๋ฅ
โข children: ํ๋กํผํฐ ์ํ
โข displayStyle: ํ์ ์ข ๋ฅ ํ์
โข superclassMirror: ์์ ๊ตฌ์กฐ ํ์
โข CustomReflectable: ์ปค์คํ ํํ
3. ํ์ฉ ๋ถ์ผ
โข ๋๋ฒ๊น ๋ฐ ๋ก๊น
โข ๊ฐ์ฒด ์ง๋ ฌํ
โข ํ ์คํธ ์ ํธ๋ฆฌํฐ
โข ํผ ๊ฒ์ฆ ์์คํ
4. ์ฃผ์์ฌํญ
โข ์ฑ๋ฅ ์ค๋ฒํค๋ ๊ณ ๋ ค
โข ์ฝ๊ธฐ ์ ์ฉ ํน์ฑ
โข ํ์ ์์ ์ฑ ์ ์ง
โข ๋ณด์ ์ ๋ณด ๋ณดํธ
์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์ ๊ฐ๋ฐํ๊ฑฐ๋, ๋ณต์กํ iOS ์ฑ์ ๋ง๋ค ๋ Mirror๋ ์ ๋ง ์ ์ฉํ ๋๊ตฌ๊ฐ ๋ ๊ฑฐ์ผ. ํนํ ์ฌ์ฉ์ ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฃจ๊ฑฐ๋, ๋์ ์ธ UI๋ฅผ ์์ฑํ๊ฑฐ๋, ๋๋ฒ๊น
๋๊ตฌ๋ฅผ ๋ง๋ค ๋ ๋น์ ๋ฐํ์ง! โจ
๋ฌผ๋ก ๋ชจ๋ ์ํฉ์์ Mirror๋ฅผ ์ฌ์ฉํ ํ์๋ ์์ด. ๋๋ก๋ ํ๋กํ ์ฝ์ด๋ ์ ๋ค๋ฆญ, Codable ๊ฐ์ ๋ค๋ฅธ Swift ๊ธฐ๋ฅ๋ค์ด ๋ ์ ํฉํ ์ ์๊ฑฐ๋ . ์ค์ํ ๊ฑด ๊ฐ ๋๊ตฌ์ ํน์ฑ์ ์ดํดํ๊ณ , ์ํฉ์ ๋ง๊ฒ ์ ์ ํ ์ ํํ๋ ๊ฑฐ์ผ! ๐ฏ
์ด์ ์ฌ๋ฌ๋ถ๋ Swift Mirror ๋ง์คํฐ๊ฐ ๋์ด! ๐
์์ผ๋ก ์ฝ๋๋ฅผ ์์ฑํ๋ฉด์ "์ด ๊ฐ์ฒด ์์ ๋ญ๊ฐ ๋ค์ด์์ง?", "๋ฐํ์์ ํ์
์ ํ์ธํ๋ ค๋ฉด ์ด๋ป๊ฒ ํ์ง?" ๊ฐ์ ๊ณ ๋ฏผ์ด ์๊ธธ ๋๋ง๋ค Mirror๋ฅผ ๋ ์ฌ๋ ค๋ด. ๋ถ๋ช
ํด๊ฒฐ์ฑ
์ ์ฐพ์ ์ ์์ ๊ฑฐ์ผ! ๐ก
๊ทธ๋ฆฌ๊ณ ์ฌ๋ฅ๋ท์์ ๋ค์ํ ๊ฐ๋ฐ ์ฌ๋ฅ์ ๊ณต์ ํ๊ฑฐ๋ ๋ฐฐ์ธ ์ ์์ผ๋, Swift ๊ฐ๋ฐ์ ๊ด์ฌ ์๋ค๋ฉด ํ๋ฒ ๋๋ฌ๋ณด๋ ๊ฒ๋ ์ข์ ๊ฒ ๊ฐ์. ํจ๊ป ์ฑ์ฅํ๋ ๊ฐ๋ฐ์ ์ปค๋ฎค๋ํฐ๋๊น! ๐ฑ
Mirror๋ฅผ ๋ง์คํฐํ๋ค๋ฉด ์ด๋ฐ ๊ฒ๋ค๋ ๊ณต๋ถํด๋ณด๋ฉด ์ข์:
โข KeyPath: ํ์ ์์ ํ ํ๋กํผํฐ ์ ๊ทผ
โข Codable: ๊ฐ๋ ฅํ ์ง๋ ฌํ ์์คํ
โข Property Wrappers: ํ๋กํผํฐ ๋์ ์ปค์คํฐ๋ง์ด์ง
โข Result Builders: DSL ๋ง๋ค๊ธฐ
โข Metatypes: ํ์ ์ ํ์ ๋ค๋ฃจ๊ธฐ
Happy Coding! ๐โจ
Mirror๋ก ๋ ๋์ Swift ์ฝ๋๋ฅผ ์์ฑํ์ธ์!
๊ด๋ จ ํค์๋
๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.