# SE-0502, 멤버와이즈 이니셜라이저에서 빠지는 private 프로퍼티

- 게시: 2026.07.14
- 원문(HTML): https://trendbrief.news/articles/se-0502-%EB%A9%A4%EB%B2%84%EC%99%80%EC%9D%B4%EC%A6%88-%EC%9D%B4%EB%8B%88%EC%85%9C%EB%9D%BC%EC%9D%B4%EC%A0%80%EC%97%90%EC%84%9C-%EB%B9%A0%EC%A7%80%EB%8A%94-private-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0.html
- 발행: [트렌드브리프](https://trendbrief.news/)

> 스위프트 6.4에 반영된 SE-0502는 초깃값을 가진 private 프로퍼티를 구조체의 자동 생성 멤버와이즈 이니셜라이저에서 제외해, 비공개 프로퍼티 하나 때문에 생성자가 잠기던 문제를 없앤다. 바뀐 규칙과 호환 동작을 정리했다.

SE-0502는 스위프트 구조체(struct)에서 컴파일러가 자동으로 만들어 주는 멤버와이즈 이니셜라이저, 즉 각 저장 프로퍼티를 인자로 받는 기본 생성자의 규칙을 손본 제안이다. 초깃값을 가진 `private` 프로퍼티 하나 때문에 이 생성자 전체가 `private`로 잠겨 타입 밖에서 못 쓰게 되던 문제를 없앤다. 스위프트 에볼루션 저장소의 SE-0502 문서에 따르면 이 제안은 '일부 수정과 함께 채택'됐고, 문서 상단 상태 표기는 2026년 7월 기준 'Implemented (Swift 6.4)'다. 스위프트 6.4부터는 초깃값이 있는 비공개 프로퍼티가 자동 생성자의 인자 목록에서 조용히 빠진다.

이 글은 struct의 컴파일러 합성 멤버와이즈 이니셜라이저만 다룬다. 직접 손으로 쓴 `init`이나, 멤버와이즈 이니셜라이저 자체가 없는 클래스에는 해당하지 않는다. 접근 제어를 세밀하게 나눠 쓰는 라이브러리·모듈 작성자가 특히 바로 도움을 받는다.

## 생성자가 private로 잠기던 문제

스위프트는 struct에 이니셜라이저를 직접 쓰지 않아도 저장 프로퍼티를 순서대로 받는 생성자를 자동으로 만들어 준다. 문제는 이 생성자의 접근 수준이었다. 자동 생성자는 안에 포함된 프로퍼티 중 가장 낮은 접근 수준을 따라갔다. 그래서 계산용 캐시나 내부 상태 같은 `private` 프로퍼티에 초깃값을 하나 붙이는 순간, 멀쩡히 `internal`이던 생성자가 `private`로 내려앉았다.

```
struct Counter {
 var name: String
 var limit: Int
 private var current = 0
}

// 스위프트 6.4 이전: 아래 호출이 컴파일 에러
let c = Counter(name: "요청", limit: 10)
// initializer is inaccessible due to 'private' protection level
```

공개 API에는 `current`가 드러나지도 않는데, 그 프로퍼티 하나 때문에 타입 밖에서는 생성 자체가 막혔다. 해결책은 뻔했다. 필요한 인자만 받는 `init`을 손으로 다시 쓰는 것이다. 프로퍼티가 두세 개면 참을 만하지만, 필드가 늘거나 프로퍼티 래퍼·매크로가 뒤에서 비공개 저장소를 심는 코드에서는 이 보일러플레이트가 계속 쌓였다.

## SE-0502가 바꾼 규칙

핵심은 두 단계다. [스위프트 에볼루션 저장소의 SE-0502 문서](https://github.com/swiftlang/swift-evolution)에 따르면, 컴파일러는 먼저 멤버와이즈 초기화가 가능한 프로퍼티 중 가장 높은 접근 수준을 계산한다. 단 이 값은 `internal`을 넘지 않는다. 그다음, 이 수준보다 낮으면서 이미 초깃값을 가진 프로퍼티를 생성자에서 뺀다.

앞의 예에서 `name`과 `limit`은 `internal`이라 최대 수준이 `internal`로 정해진다. 초깃값을 가진 `private` 프로퍼티 `current`는 그보다 낮으니 제외 대상이 된다.

```
struct Counter {
 var name: String
 var limit: Int
 private var current = 0
}

// 스위프트 6.4: current가 빠져 생성자는 internal로 유지
let c = Counter(name: "요청", limit: 10) // 정상 동작
```

결과적으로 생성자 시그니처는 `init(name:limit:)`가 되고 접근 수준도 원래대로 돌아온다. `current`는 선언에 붙은 `0`으로 채워진다. 비공개 구현 세부를 숨기려고 생성자를 손수 작성하던 수고가 사라지는 셈이다.

## 그럼 모든 비공개 프로퍼티가 빠질까

아니다. 초깃값이 있는 프로퍼티만 빠진다. 이게 규칙에서 가장 중요한 조건이다. 제외되려면 두 가지를 동시에 만족해야 한다. 하나는 앞서 계산한 최대 접근 수준보다 낮을 것, 다른 하나는 선언 자체에 초깃값이 있을 것이다.

초깃값 없는 `private` 프로퍼티는 생성자가 어디선가 값을 받아야 하므로 뺄 수가 없다. 이때는 예전과 똑같이 그 프로퍼티가 인자로 남고, SE-0502가 풀려던 접근 수준 문제도 그대로 남는다. 제안 제목이 그냥 'private properties'가 아니라 'private initialized properties'인 이유가 여기 있다. 빠뜨릴 값을 컴파일러가 이미 알고 있어야만 그 프로퍼티를 안전하게 감출 수 있기 때문이다.

```
struct Session {
 var id: String
 private var token: String // 초깃값 없음, 제외 불가
 private var retries = 0  // 초깃값 있음, 제외
}
```

이 struct에서 `retries`는 생성자에서 빠지지만 `token`은 남는다. 그래서 `token`이 남아 있는 한 예전의 제약이 이어진다. 비공개 상태를 밖에서 감추고 싶다면 그 프로퍼티에 합당한 초깃값을 주는 편이 새 규칙과 잘 맞는다.

## 기존 호출 코드는 그대로 컴파일될까

대체로 그렇다. 제안이 가장 신경 쓴 부분도 이 지점이다. 규칙이 바뀌면 옛 시그니처(`init(name:limit:current:)`)를 직접 부르던 코드가 깨질 수 있다. SE-0502 문서에 따르면 컴파일러는 이를 막기 위해 예전과 동일한 프로퍼티 목록을 가진 호환용 생성자(compatibility overload)를 하나 더 합성한다. 새 시그니처와 옛 시그니처가 함께 존재하는 것이다.

스위프트 언어 운영 그룹(Language Steering Group)은 채택 과정에서 원안을 한 번 손봤다. 옛 생성자를 곧바로 폐기 예정으로 못 박는 대신, 폐기 논의를 향후 과제(Future Directions)로 미루고 두 생성자를 나란히 두는 순수 추가 방식으로 바꿨다. 덕분에 6.4로 올려도 기존 호출부가 갑자기 경고를 쏟아 내지는 않는다.

## 실무에서 달라지는 것

가장 크게 체감하는 쪽은 모듈 경계를 넘나드는 코드다. 라이브러리에서 공개 struct에 내부 캐시나 상태 플래그를 `private`로 몰아넣던 사람이라면, 그동안 손으로 유지하던 생성자를 지워도 된다. 프로퍼티 래퍼나 매크로가 뒤에서 `private` 저장소를 심는 코드도 자동 생성자를 다시 활용할 수 있다. 콜백을 순차 코드로 정리한 [async/await](https://trendbrief.news/articles/%EC%8A%A4%EC%9C%84%ED%94%84%ED%8A%B8-async-await-%EC%BD%9C%EB%B0%B1%EC%97%90%EC%84%9C-%EC%88%9C%EC%B0%A8-%EC%BD%94%EB%93%9C%EB%A1%9C.html)처럼, 스위프트는 이런 잔손질을 언어 차원에서 꾸준히 줄여 왔다.

SE-0502 문서 상단 상태는 2026년 7월 기준 'Implemented (Swift 6.4)'로 표기돼 있고, 문서에는 실험적 기능 플래그 `ExcludePrivateFromMemberwiseInit`가 함께 적혀 있다. 스위프트 버전과 릴리스 소식은 [스위프트 공식 사이트](https://www.swift.org)에서 확인할 수 있다. 새 버전으로 올리기 전에 자신의 코드베이스가 옛 생성자 시그니처에 얼마나 기대는지 점검해 두면, 호환 생성자 덕에 대부분 무리 없이 넘어가더라도 한결 안심할 수 있다.

## 관련 글

- [SE-0504 Task Cancellation Shields로 정리 코드를 취소에서 지키기](https://trendbrief.news/articles/se-0504-task-cancellation-shields%EB%A1%9C-%EC%A0%95%EB%A6%AC-%EC%BD%94%EB%93%9C%EB%A5%BC-%EC%B7%A8%EC%86%8C%EC%97%90%EC%84%9C-%EC%A7%80%ED%82%A4%EA%B8%B0.md)
- [SE-0532로 배우는 스위프트 에볼루션 제안 읽기](https://trendbrief.news/articles/se-0532%EB%A1%9C-%EB%B0%B0%EC%9A%B0%EB%8A%94-%EC%8A%A4%EC%9C%84%ED%94%84%ED%8A%B8-%EC%97%90%EB%B3%BC%EB%A3%A8%EC%85%98-%EC%A0%9C%EC%95%88-%EC%9D%BD%EA%B8%B0.md)
- [SE-0508: 배열·딕셔너리에 후행 클로저를 허용하는 스위프트 제안](https://trendbrief.news/articles/se-0508-%EB%B0%B0%EC%97%B4-%EB%94%95%EC%85%94%EB%84%88%EB%A6%AC%EC%97%90-%ED%9B%84%ED%96%89-%ED%81%B4%EB%A1%9C%EC%A0%80%EB%A5%BC-%ED%97%88%EC%9A%A9%ED%95%98%EB%8A%94-%EC%8A%A4%EC%9C%84%ED%94%84%ED%8A%B8-%EC%A0%9C%EC%95%88.md)
- [WWDC26 MetricKit, iOS 27의 Swift 새 API와 상태별 측정](https://trendbrief.news/articles/wwdc26-metrickit-ios-27%EC%9D%98-swift-%EC%83%88-api%EC%99%80-%EC%83%81%ED%83%9C%EB%B3%84-%EC%B8%A1%EC%A0%95.md)
- [에이전트 브라우저, 작동 원리와 개발 프레임워크 선택](https://trendbrief.news/articles/%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EC%9E%91%EB%8F%99-%EC%9B%90%EB%A6%AC%EC%99%80-%EA%B0%9C%EB%B0%9C-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC-%EC%84%A0%ED%83%9D.md)