프로그래밍언어

[Swift] Swift의 유연한 문법 기능을 활용한 코드 설계

애기공룡훈련병 2025. 2. 16. 07:23
반응형

Swift는 강력한 문법적 유연성을 제공하는 언어로, 오퍼레이터 오버로딩(operator overloading)과 첨자 표기법(subscript) 외에도 여러 기능을 활용하면 더욱 직관적이고 읽기 쉬운 코드를 작성할 수 있습니다. 이 글에서는 이러한 기능을 활용하여 사용자 정의 타입을 설계하는 방법을 자세히 설명하겠습니다.

1. 오퍼레이터 오버로딩

오퍼레이터 오버로딩

Swift에서는 기존의 연산자(+, -, *, /, == 등)를 사용자 정의 타입에서 재정의할 수 있습니다. 이를 오퍼레이터 오버로딩이라고 하며, 이를 활용하면 연산을 더욱 직관적으로 표현할 수 있습니다.

오퍼레이터 오버로딩의 장점

  • 코드 가독성 향상: 수학적인 개념을 코드에 직접 적용할 수 있음
  • 유지보수성 증가: 별도의 메서드를 정의하지 않고 연산자만으로 표현 가능
  • 객체 간 연산 표현 가능: 사용자 정의 타입에서도 기본 타입처럼 연산 가능

예제: 2D 벡터 연산 구현

import Foundation

struct Vector2D {
    var x: Double
    var y: Double
    
    // 벡터 덧셈 연산자 오버로딩
    static func + (lhs: Vector2D, rhs: Vector2D) -> Vector2D {
        return Vector2D(x: lhs.x + rhs.x, y: lhs.y + rhs.y)
    }
    
    // 벡터 뺄셈 연산자 오버로딩
    static func - (lhs: Vector2D, rhs: Vector2D) -> Vector2D {
        return Vector2D(x: lhs.x - rhs.x, y: lhs.y - rhs.y)
    }
    
    // 벡터 스칼라 곱 연산자 오버로딩
    static func * (lhs: Vector2D, scalar: Double) -> Vector2D {
        return Vector2D(x: lhs.x * scalar, y: lhs.y * scalar)
    }
    
    // 벡터 크기 비교 연산자 오버로딩
    static func == (lhs: Vector2D, rhs: Vector2D) -> Bool {
        return lhs.x == rhs.x && lhs.y == rhs.y
    }
}

let v1 = Vector2D(x: 3, y: 4)
let v2 = Vector2D(x: 1, y: 2)

let sum = v1 + v2  // Vector2D(x: 4, y: 6)
let diff = v1 - v2 // Vector2D(x: 2, y: 2)
let scaled = v1 * 2 // Vector2D(x: 6, y: 8)
print(sum, diff, scaled)

연산자 오버로딩을 적용한 벡터 연산 다이어그램

2. 첨자 표기법을 활용한 사용자 정의 컬렉션 구현

첨자 표기법(subscript)

Swift에서는 subscript 키워드를 사용하여 객체를 배열처럼 인덱스를 이용해 접근할 수 있도록 만들 수 있습니다. 이를 이용하면 사용자 정의 컬렉션 타입을 보다 직관적으로 설계할 수 있습니다.

예제: 사용자 정의 행렬(Matrix) 타입

struct Matrix {
    private var data: [[Double]]
    
    init(rows: Int, columns: Int, defaultValue: Double = 0.0) {
        self.data = Array(repeating: Array(repeating: defaultValue, count: columns), count: rows)
    }
    
    var rowCount: Int { data.count }
    var columnCount: Int { data.first?.count ?? 0 }
    
    // 행렬의 원소에 접근하는 첨자 표기법 구현
    subscript(row: Int, col: Int) -> Double {
        get {
            precondition(row >= 0 && row < rowCount && col >= 0 && col < columnCount, "Index out of range")
            return data[row][col]
        }
        set {
            precondition(row >= 0 && row < rowCount && col >= 0 && col < columnCount, "Index out of range")
            data[row][col] = newValue
        }
    }
}

var matrix = Matrix(rows: 3, columns: 3)
matrix[1, 1] = 5.5
print(matrix[1, 1]) // 5.5

3. 기타 Swift의 강력한 문법 기능

프로퍼티 래퍼(Property Wrappers)

Swift의 @propertyWrapper를 활용하면 변수의 동작을 캡슐화할 수 있습니다.

@propertyWrapper
struct Uppercase {
    private var value: String = ""
    
    var wrappedValue: String {
        get { value }
        set { value = newValue.uppercased() }
    }
}

struct User {
    @Uppercase var name: String
}

var user = User(name: "john")
print(user.name) // "JOHN"

키 경로(KeyPath)

키 경로를 활용하면 객체 속성을 더 유연하게 다룰 수 있습니다.

struct Person {
    let name: String
    let age: Int
}

let person = Person(name: "Alice", age: 25)
let nameKeyPath = \Person.name
print(person[keyPath: nameKeyPath]) // "Alice"

 

반응형