2025/07/15

8. Swift 프로퍼티와 메서드: 객체의 속성과 동작 정의하기

안녕하세요! 지난 블로그에는 열거형을 통해 관련된 값들을 하나의 타입으로 묶는 방법을 배웠고, 그전에는 구조체와 클래스를 통해 사용자 정의 타입을 만드는 방법을 익혔죠. 오늘은 이렇게 정의한 타입들이 어떤 데이터(속성)를 가지고, 어떤 행동(동작)을 할 수 있는지 정의하는 방법에 대해 알아보겠습니다. 바로 프로퍼티(Property)와 메서드(Method)입니다.

프로퍼티와 메서드는 객체 지향 프로그래밍의 핵심 개념인 캡슐화(Encapsulation)를 구현하는 데 필수적인 요소입니다. 이들을 통해 우리는 실제 세계의 객체처럼 '무엇을 가지고 있는지'와 '무엇을 할 수 있는지'를 코드 안에서 표현할 수 있습니다.


1. 프로퍼티 (Properties): 객체의 속성 정의하기

프로퍼티는 클래스, 구조체, 또는 열거형 내부에 선언되어 특정 값을 저장하거나 계산하는 데 사용되는 상수 또는 변수입니다. 객체가 어떤 데이터를 가지고 있는지를 나타내죠. Swift에서는 크게 두 가지 종류의 프로퍼티를 제공합니다: 저장 프로퍼티와 연산 프로퍼티.

1.1. 저장 프로퍼티 (Stored Properties): 값을 직접 저장하는 공간

저장 프로퍼티는 인스턴스의 특정 타입 값을 저장하는 변수(var) 또는 상수(let)입니다. 가장 일반적인 형태의 프로퍼티입니다.

//Swift 예제
struct Person {
    var name: String // 저장 변수 프로퍼티
    let yearOfBirth: Int // 저장 상수 프로퍼티

    // 모든 저장 프로퍼티는 인스턴스 초기화 시점에 초기화되어야 합니다.
    // 구조체는 멤버와이즈 초기화(Memberwise Initializer)를 자동으로 제공합니다.
    // 클래스는 직접 init() 메서드를 구현해야 합니다.
}

let kim = Person(name: "김민수", yearOfBirth: 1990)
print("\(kim.name)은 \(kim.yearOfBirth)년에 태어났습니다.")
// 출력: 김민수는 1990년에 태어났습니다.

var lee = Person(name: "이영희", yearOfBirth: 2000)
lee.name = "이수진" // var로 선언했으므로 변경 가능
// lee.yearOfBirth = 1995 // let으로 선언했으므로 변경 불가능 (컴파일 에러)

1.2. 연산 프로퍼티 (Computed Properties): 값을 직접 저장하지 않고 계산하여 반환

연산 프로퍼티는 값을 직접 저장하지 않고, 다른 프로퍼티의 값을 기반으로 값을 계산하여 반환합니다. C++에서는 값과 그 값을 읽거나 쓰기 위한 함수를 별도로 만들었어야 했는데, Swift에서는 프로퍼티 선언하고 get, set을 설정해줘서, 별도의 프로퍼티를 읽거나 쓸때, 계산해서 넣는 것이 가능합니다.

읽기 전용(get만 구현)이거나 읽기-쓰기(get과 set 모두 구현)가 가능합니다. 

//Swift 예제
struct Rectangle {
    var width: Double
    var height: Double

    // 연산 프로퍼티: 사각형의 넓이를 계산하여 반환
    var area: Double {
        get { // 값을 읽을 때 실행되는 코드 블록
            return width * height
        }
        set(newArea) { // 값을 설정할 때 실행되는 코드 블록 (newValue가 기본 인자 이름)
            // 넓이가 변경되면, 비율을 유지하며 높이를 조정
            let ratio = height / width
            width = sqrt(newArea / ratio)
            height = width * ratio
        }
    }

    // 읽기 전용 연산 프로퍼티 (get 키워드 생략 가능)
    var perimeter: Double {
        return (width + height) * 2
    }
}

var myRect = Rectangle(width: 10.0, height: 5.0)
print("초기 넓이: \(myRect.area)") // get 호출 -> 출력: 초기 넓이: 50.0

myRect.area = 100.0 // set 호출
print("새로운 넓이: \(myRect.area), 너비: \(myRect.width), 높이: \(myRect.height)")
// 출력: 새로운 넓이: 100.0, 너비: 14.142135623730951, 높이: 7.0710678118654755
print("둘레: \(myRect.perimeter)") // 출력: 둘레: 42.42640687119285

1.3. 타입 프로퍼티 (Type Properties): 특정 타입 전체에 속한 프로퍼티

타입 프로퍼티는 특정 타입의 인스턴스가 아닌, 타입 자체에 속하는 프로퍼티입니다. 모든 인스턴스가 공유하는 값을 저장할 때 유용합니다. 

static 키워드를 사용하여 선언합니다. 클래스에서는 class 키워드를 사용하여 오버라이드 가능한 타입 프로퍼티를 만들 수도 있습니다.

//Swift 예제
struct SomeStructure {
    static var storedTypeProperty = "일부 값" // 저장 타입 프로퍼티
    static var computedTypeProperty: Int { // 연산 타입 프로퍼티
        return 1
    }
}

enum SomeEnumeration {
    static var storedTypeProperty = "또 다른 값"
}

class SomeClass {
    static var storedTypeProperty = "클래스의 저장 타입 프로퍼티"
    class var computedTypeProperty: Int { // class 키워드 사용 시 하위 클래스에서 오버라이드 가능
        return 2
    }
}

print(SomeStructure.storedTypeProperty) // 출력: 일부 값
SomeStructure.storedTypeProperty = "새로운 값"
print(SomeStructure.storedTypeProperty) // 출력: 새로운 값
print(SomeClass.computedTypeProperty) // 출력: 2


2. 메서드 (Method): 객체의 동작 정의하기

메서드는 특정 클래스, 구조체, 또는 열거형과 연관된 함수입니다. 객체가 어떤 '행동'을 할 수 있는지를 정의하죠. 함수와 마찬가지로 func 키워드를 사용하여 선언합니다.

2.1. 인스턴스 메서드 (Instance Methods): 특정 인스턴스가 수행하는 동작

인스턴스 메서드는 특정 타입의 인스턴스가 호출하여 사용하는 함수입니다. 인스턴스의 프로퍼티에 접근하거나 수정하는 등의 작업을 수행합니다.

//Swift 예제
class Counter {
    var count = 0

    // count 값을 1 증가시키는 메서드
    func increment() {
        self.count += 1 // self는 현재 인스턴스를 의미 (생략 가능)
    }

    // 특정 양만큼 count 값을 증가시키는 메서드
    func increment(by amount: Int) {
        count += amount
    }

    // count 값을 초기화하는 메서드
    func reset() {
        count = 0
    }
}

let counter = Counter() // Counter 클래스의 인스턴스 생성
counter.increment() // 메서드 호출
print("카운터 값: \(counter.count)") // 출력: 카운터 값: 1

counter.increment(by: 5) // 매개변수를 사용하는 메서드 호출
print("카운터 값: \(counter.count)") // 출력: 카운터 값: 6

counter.reset()
print("카운터 값: \(counter.count)") // 출력: 카운터 값: 0

메서드 내부에서 self 키워드는 현재 인스턴스를 명확하게 참조할 때 사용됩니다.

2.2. 타입 메서드 (Type Methods): 특정 타입 자체에 속한 동작

타입 메서드는 특정 타입의 인스턴스가 아닌, 타입 자체에서 호출하는 함수입니다. static 키워드(구조체, 열거형, 클래스) 또는 class 키워드(클래스)를 사용하여 선언합니다. 주로 특정 타입과 관련된 유틸리티 함수나 팩토리 메서드(인스턴스를 생성하는 메서드)로 사용됩니다.

//Swift 예제
struct MathUtility {
    static let pi = 3.14159 // 타입 프로퍼티

    // 원의 넓이를 계산하는 타입 메서드
    static func calculateCircleArea(radius: Double) -> Double {
        return pi * radius * radius
    }
}

class Logger {
    static var messageCount = 0 // 모든 인스턴스가 공유하는 메시지 수

    // 로그 메시지를 기록하는 타입 메서드
    static func log(message: String) {
        print("[LOG] \(message)")
        messageCount += 1
    }
}

print(MathUtility.calculateCircleArea(radius: 5.0)) // 출력: 78.53975
Logger.log(message: "앱 시작")
Logger.log(message: "사용자 로그인")
print("총 로그 메시지 수: \(Logger.messageCount)") // 출력: 총 로그 메시지 수: 2

정리하며

오늘은 Swift에서 객체의 데이터와 행동을 정의하는 프로퍼티와 메서드에 대해 자세히 알아보았습니다.

프로퍼티: 객체의 속성을 나타내며, 값을 직접 저장하는 저장 프로퍼티와 다른 값을 기반으로 계산하는 연산 프로퍼티, 그리고 타입 자체에 속하는 타입 프로퍼티가 있습니다.

메서드: 객체가 수행할 수 있는 동작을 나타내며, 특정 인스턴스가 호출하는 인스턴스 메서드와 타입 자체에서 호출하는 타입 메서드가 있습니다.

프로퍼티와 메서드를 적절히 사용하여 클래스, 구조체, 열거형을 정의함으로써, 우리는 더욱 직관적이고 재사용 가능하며 유지보수가 쉬운 코드를 작성할 수 있습니다. 객체 지향 프로그래밍의 기본이자 핵심인 이 개념들을 충분히 연습하여 여러분의 Swift 실력을 한 단계 업그레이드하세요!


다음 시간에는 Swift 객체 지향 프로그래밍의 중요한 원칙 중 하나인 상속(Inheritance)에 대해 알아보겠습니다.


궁금한 점이 있다면 언제든지 댓글로 남겨주세요.

0 comments:

댓글 쓰기