iOS Development Guide

let과 var의 의미 차이

`let`은 바꿀 수 없는 바인딩이고 `var`는 바꿀 수 있는 바인딩이다. 하지만 값 타입과 참조 타입에서 체감이 다르게 나타나므로, 선언 키워드 수준에서 끝내지 않고 실무 의미까지 같이 정리한다.

Swift core syntax Practical behavior focus Knowledge item #3
학습 날짜

2026-04-09

가장 먼저 알아야 할 것

  • `let`은 재할당이 안 된다.
  • `var`는 재할당이 된다.
  • 값 타입에서는 `let`이 내부 상태 변경까지 막는다.
  • 참조 타입에서는 `let`이 참조 재할당만 막고, 객체 내부 변경은 막지 못할 수 있다.

Why This Work Exists

`let이면 다 안 바뀐다`고 생각하다가 class에서 헷갈리는 경우가 많다. 반대로 `var`를 습관적으로 쓰면 불필요한 mutable state가 늘어나 설계가 약해진다.

빠른 비교

상황 `let` `var`
값 타입 변수 값 자체가 고정된다. 내부 프로퍼티 변경도 막힌다. 값을 다시 넣거나 내부 프로퍼티를 바꿀 수 있다.
참조 타입 변수 다른 객체로 바꾸는 것은 막지만, 객체 내부 상태는 바뀔 수 있다. 다른 객체로도 바꿀 수 있고, 내부 상태도 바뀔 수 있다.

값 타입에서의 의미

`struct`를 `let`으로 선언하면 그 값 전체가 상수가 된다. 그래서 `mutating` 메서드도 호출할 수 없고, 내부 `var` 프로퍼티도 바꿀 수 없다.

struct Counter {
    var count = 0

    mutating func increment() {
        count += 1
    }
}

let counter = Counter()
// counter.increment() // 불가

참조 타입에서의 의미

`class`를 `let`으로 선언하면 참조 자체만 고정된다. 즉 다른 객체를 다시 넣을 수는 없지만, 그 객체 내부 `var` 프로퍼티는 바뀔 수 있다.

final class UserBox {
    var name: String
    init(name: String) { self.name = name }
}

let user = UserBox(name: "A")
user.name = "B"   // 가능
// user = UserBox(name: "C") // 불가

실무 포인트

핵심 요약

`let`과 `var`의 본질은 재할당 가능 여부다. 다만 값 타입은 값 전체가 고정되고, 참조 타입은 참조만 고정된다는 차이를 반드시 같이 봐야 한다.

Then 1

`let`이면 무조건 아무 것도 못 바꾸는 것 아닌가요?

값 타입에서는 거의 그렇게 느껴지지만, 참조 타입에서는 다르다. `let`은 변수에 담긴 참조를 다른 객체로 바꾸지 못하게 할 뿐, 그 객체 내부의 mutable state까지 자동으로 막아주지는 않는다.

Then 2

그럼 실무에서는 왜 `let`을 먼저 쓰라고 하나요?

불필요한 상태 변경을 줄이면 코드 예측 가능성이 높아진다. 특히 화면 상태, 네트워크 모델, 설정값처럼 자주 안 바뀌는 데이터는 `let` 중심으로 설계하는 편이 버그를 줄이기 쉽다.

Then 3

`let`인데 class 프로퍼티가 바뀌는 것이 왜 가능한가요?

변수에 저장된 것은 객체 자체가 아니라 그 객체를 가리키는 참조이기 때문이다. `let`은 그 참조를 다른 객체로 바꾸지 못하게 할 뿐이다. 객체 내부 변경 가능 여부는 그 객체의 프로퍼티 선언과 타입 설계가 결정한다.

짧게 말하면

`let a = ClassA()`에서 `a.property = ...`가 가능한 이유는 `a`가 참조를 들고 있고, 그 참조 자체를 다른 객체로 바꾸는 것은 아니기 때문이다.

Then 4

동시성 관점에서도 `let`이 중요한가요?

중요하다. immutable state는 data race 대상이 되지 않기 때문에, 공유되는 값이 많을수록 `let`이 설계 안정성에 도움이 된다. 반대로 `var`가 많은 shared state는 actor, lock, queue 같은 보호 장치가 더 필요해진다.

Then 5

`class`를 `let`으로 선언했다고 해서 왜 thread-safe가 아닌가요?

`let`은 참조 재할당만 막는다. 하지만 그 참조가 가리키는 객체 내부의 `var` 프로퍼티는 여러 스레드에서 동시에 바뀔 수 있다. 즉 `let class`는 참조 고정일 뿐이고, 내부 mutable state에 대한 동기화 보장은 아니다.

Then 6

왜 `struct`는 `let`이면 내부 `var`를 못 바꾸고, `class`는 바꿀 수 있나요?

`struct`는 값 타입이라 변수 안에 값 자체가 들어 있다. 그래서 `let struct`는 값 전체가 상수가 되어 내부 프로퍼티 수정도 막힌다. 반면 `class`는 변수 안에 객체 자체가 아니라 참조가 들어 있으므로, `let class`는 참조만 고정하고 객체 내부 상태 변경은 여전히 가능할 수 있다.