'접근제어'에 해당하는 글 1건

제목: Thoughts on Swift access control
그리고 스위프트 에볼루션의 기회비용


스위프트에서의 접근제어에관한 엄청난 양의 swift-evolution mailing lists에 있다. 몇일전에 (Private 접근 수준을 고치자는) SE-0159제안이 거절되었다. 이것에 대한 내 생각을 공유하고 싶고, 전반적으로 접근 제어의 커다란 이야기에대한 내 생각도 들려주고 싶다. 그러나 먼저, 스위프트의 접근 제어 이력을 간단하게 알려주겠다.

⚠️주의: 나의 의견이 들어있을 것이다.



접근제어의 간단한 이력
스위프트의 이른 시기(1.0이전)에는 접근제어가 없었다. 이때가 스위프트의 황금기였다. 모든것이 퍼블릭이고 글로벌하게 어디에서나 접근할 수 있었다. 누구도 적절한 캡슐화에대해 생각하지 않았다. 한달동안 토론한 이메일도 없었다(스위프트-에볼루션이 아직 존재하지 않았었다). 접근제어가 없는것은 가장 간단하게 접근하여 제어하게 했고, 에볼루션이 없는것이 최고의 에볼루션이었다.

접근 제어가 나오다
Xcode6 베타4에서 스위프트는 접근제어를 추가하여 지원했다. 이해하기 쉬웠고, 사용하기도 쉬웠으며, 꽤 우아했었다. 즉시 이후에, 접근제어 모델과 함께 스위프트 1.0이 배포되었다. 스위프트 1.0은 Xcode 6과함께 나왔다. 상속과함께 합쳐진 "protected" 접근수준은 없었다. "friend" 접근수준도 없었는데, 이것은 그냥 지겨운 것이기 때문이다. 오직 세개의 접근수준만 있었다.
  • public은 모듈(프레임워크나 라이브러리)을 임포트한 모든 파일로부터 접근할 수 있다
  • internal은 (디폴트로서) 현재 모듈 안에서만 접근할 수 있다(앱이나 프레임워크 타겟도 "현재 디렉토리"라 생각한다 à la Swift Package Manager)
  • private은 이것이 정의된 소스파일 안에서만 접근할 수 있다

라이브러리 저자, 앱 개발자로서 이런 접근수준은 내가 원하는 모든 것을 표현하기위해 나에게 필요한 모든 기능을 제공한다. 명백한 유스케이스를 떠나서, "기존의" "private" 접근의 노테이션은 그 파일안에 타입이나 다른 엔티티를 정의하기만하면 달성할 수 있다. 예를들어 class A를 정의하고 그 모든 (private) 프로퍼티를 다른 클래스로부터 접근하지 못하게 하고 싶으면, 그 파일에 class A를 정의하고 다른것을 할 필요가 없었다. 이것이 바로 내가 좋아했던 스위프트 접근제어이다 - 디스크의 파일과 디렉토리의 "물리적인" 구조를 기본적으로 반영한 접근수준을 제공하여 최고의 실현을 만들어준다(파일이 부푸는것과 모듈이 부푸는것을 줄여준다). 적절한 캡슐화를 설계하려면, 명확하게 정의된 모듈(디렉토리)에 파일을 이동해야하고, 한 파일에 관련된 타입들을 정의하고, 한 파일안에 여러 타입 정의를 완전히 피하면된다. 코드 구성과 접근제어는 좋게 엮여있고 개발자에게 코드를 잘 구성할 수 있게 해준다. (엮이는것 일반적으로 소프트웨어 설계에서 나쁘나, 이 경우 필요한 엮임이다.)

fileprivate가 나오다
다음단계는 스위프트가 오픈소스화되고 스위프트 에볼루션 프로세스가 나왔다. 영역을 지정한 접근수준의 이 SE-0025 제안은 검토되고, 수정되서 마침내 스위프트3에서 수용되었다. 이 제안은 현재 선언된 스코프안에 엔티티에게 엄격한 접근을 하기위해 private의 의미가 바뀌었고, 이전 기능을 보존하기위해 fileprivate라는 키워드가 새로 나왔다. 이때는 새로운 (그리고 다소 의도적으로 못낳게 만든) fileprivate 키워드는 드불게 사용되어서 스위프트의 단계적 공개의 설계 철학에의해 지켜진다. 우리가 이것에대해 조금 알고있는 것은 이것이 실전에는 옳지 않다는 것이다. fileprivateprivate의 기능이 오버랩되고 private용어가 오버로드되면서, 인지적 로드가 증가하는 또다른 사이드이펙트가 있었다.

open에서 바깥
단지 삼개월후에 초기의 토론이 다른 접근제어 변화가 시작되었다. 세번의 논쟁적 검토기간 과 수정 후에 public 접근과 public 오버라이드가능함의 차이를 구별하자는 SE-0117 제안은 스위프트3에서 받아드려졌다. 이 제안은 open이라 부르는 새로운 접근수준을 만들었고 어떤 문맥에서 public의 정의가 바뀌었다. public의 의미가 서브클래스하는것과 오버라이딩하는것에 관련하여 좁아졌다. public 클래스는 더이상 이것이 정의된 곳에 모듈의 바깥에서 서브클래스될 수 없고 클래스의 모든 public 맴버들은 더이상 클래스 모듈의 바깥에서 서브클래스에의해 오버라이드 될 수 없다. 따라서 open 클래스들은 public이며 서브클래스가능하고, open 클래스에있는 모든 open 프로퍼티나 open 함수는 서브클래스에서 오버라이드될 수 있다. 이해할 수 있겠는가? publicopen에 관련된 규칙들은 혼란스럽고, 오버라이드와 서브클래스를 만드는것을 막는 final에의해 더 복잡해진다. 따라서 final publicpublic 클래스는 같은 모듈안이나 그 밖깥에 클라이언트가 있는지 없는지에따라 의미가 달라진다. 다시말해, 접근제어에대해 생각할때 인지적 로드가 증가한다. 이번시간은, openpublic의 기능을 오버랩하는것을 더 판별해야하고 구별하기 어렵다.

open의 복잡함과 그 규칙의 미로와 엣지 케이스에도 불구하고 단계적 공개 철학에의해 너무 잘 지켜졌다. 많은 스위프트 사용자(특히 초보자들)는 open를 사용하거나 알려고 하지 않을 것이다. 라이브러리 저자나 앞서간 사용자들만이 open 사용을 마주할 것으로 보인다. open의 단계적 공개는 스위프트 설계 성질에서 open이 적용되지 않은 값타입에 더 명백하게 되었다. 왜 우리가 SE-0117를 되돌리는 제안이나 open을 더 수정하는 제안을 보지 못했는지 그 이유라고 생각한다.

open은 성공적으로 단계적 공개가 되었을지라도, 적어도 내 경험상 이것은 메리트가 작다는 주장을 여전히 하고싶다. 특별히 퍼블릭 API의 부분일때, 대부분 나는 클래스를 final로 정의한다. 드물게 모듈안의 클래스를 서브클래스로 만들고 싶을때가 있지만, 모듈 바깥에서는 아니다. 특히 스위프트만큼 표현력있고 풍부한 언어에서는 동작을 내부적으로 공유하기위해 클래스와 모듈을 설계하는 다양한 방법이 있으나, 외부적으로 그 동작을 노출시키는것은 피한다. open같은 기능은 명백하게 주로 사용되지 않는 것이다. 이것이 실망스러움을 넘어 public 의미에 영향을 주게 만든다.

게다가, SE-0117SE-0025가 받아드려지고 구현된 후에 의논되고 검토되었더라도 이것은 본래 SE-0025로부터 고립된 제안이 되었다는 점을 짚어야한다. 물론, 그때 커뮤니티는 SE-0025에대해 알았으나 누구도 실제로 스위프트 3과 새로운 privatefileprivate를 사용해보지 않았었다. (스냅샷에서 누군가가 이것을 시험해보았을수 있으나, 매우 적은 개발자들이 그렇게 했을 것이다.) 우리는 그 암시와 SE-0025의 현실성에대해서 완전히 깜깜한 상태였다. 접근제어를 증대하는것에 취해있는동안, 커뮤니티는 변경하기위해 다른 프로포절을 다루고 있었다. 명확하게하면, 누구의 책임도 아니다. 그냥 우리는 인지하지 못했었다.

스위프트 3.0의 접근제어
이것은 스위프트의 현재 접근제어 상태로 나온 것이다. The Swift Programming Language eBook에서 나온 문장이다.
  • open 접근은 정의하는 모듈을 불러온 다른 모듈로부터, 혹은 정의하는 다른 모듈로부터 어떤 소스파일에서도 엔티티를 사용될 수 있다. open은 클래스에만 적용하고, 그것이 접근가능한것에서 서브클래스로 만드는게 가능하다. 또한 모든 open 클래스는 그 맴버를 오버라이드되게 하기위해 open으로 정의할 수 있다.
  • public 접근은 정의하는 모듈 안에서만 서브클래스하고 오버라이드할 수 있는 점만 빼면 open과 비슷하다.
  • internal접근(디폴트이다)은 정의하는 모듈로부터 모든 소스 파일에서 사용할 수 있으나, 모듈 밖에서는 안된다.
  • fileprivate 접근은 소유하는 정의하는 소스파일에만 엔티티를 사용할 수 있다.
  • private 접근은 정의나 스코프로 감싸진 곳에만 엔티티를 사용할 수 있다.

굉장히 짧은 시간에 스위프트는 접근수준의 갯수가 세개에서 다섯개로 두배가까이 뛰었고, 이전의 두 키워드의 의미를 고쳤다. 나는 경험한 프로그래머들이 드것들의 차이를 설명하고, 적절한 사용을 글에 담으려고 노력하는 모습을 보아왔다. 초보자에게 모나드(monads)를 설명하는게 접근제어수준을 설명하는것보다 쉬울땨 뭔가 잘못되었다는것을 알 수 있을 것이다.

단계적 공개의 철학으로 돌아와서, 이 접근수준의 어떤것이 계속 우리에게 필요한것으로 고려될까? 위에서 말한 이유료 open은 없앨 수 있다. internal도 디폴트고 명시적으로 할 필요가 없으므로 없앨 수 있다. 매일 사용하고 일반적인 것으로 public, fileprivate, private이 남았다. 이전보다 더 복잡하게 동작하게하는, 키워드 하나가 늘었다.

fileprivate을 살리거나, 혹은 커다란 타협(compromise)
최근에 private 접근수준을 고치자는 SE-0159프로포절은 SE-0025 변경을 되돌리자는 것으로 되었다. 이것은 fileprivate를 없애고 (스위프트1, 스위프트2의) private 원래 의미로 복구하자는 것이다. 왜이렇게 됐을까? 쉽게 말하면, fileprivate가 꽤 자주 사용되어서 단계적 공개를 부셨다. 새로운 private의 맴버 타입은 같은 파일안에 익스텐션으로 정의되있더라도 더이상 접근할 수 없게되므로, 근본적인 스위프트의 확장-지향 스타일을 망가뜨린다. 이 프로포절은 SE-0025만큼 논쟁적이고 열혈하게 검토되었다. 아이러니하게(혹은 우연히?) 거의 일년만에 SE-0025에대한 마지막 결정 후, 접근제어가 바뀌지 않는 상태로 놔두기로하여 거절되었다고 발표했다. 이 프로포절은 스위프트 4의 소스 안정성에서 너무 많은 영향을 주는게 중요했기때문에 받아드려질 수 없었다. 나도 이런 해결하기 어려운점에는 동의한다.

그러나 SE-0025가 예상의 결과로 되지 않은 접근제어 이휴가 있음은 명확하고, 이것을 어떻게 해결할지에대한 스위프트 커뮤니티에서의 불일치는 존재한다. 코어 팀도 잘 알고있다. Doug Gregor은 다행히도 절충안을 찾고 당장을위해, 아마도 좋은 이유료 스위프트의 접근제어 이야기를 진정시키려고 새로운 토론을 시작했다.

특별히 이 설계는 타입 "X"나 익스탠션으로 정의된 "private" 맴버가 어디서부터 접근할 수 있는지의 것이다.
  • 같은 파일안에 "X"라는 익스텐션
  • 같은 파일에 나타난 것이라면, "X"의 정의
  • 같은 파일안에 나타난 위의 것중 하나의 감싸진 타입(혹은 틱스텐션된 것)
이 설계는 여러 명확한 이점을 가진다.
  • private는 보기에 "전체 모듈보다 작다"는 올바른 디폴트가 되고, 여러 익스텐션에서 타입 정의를 나누는 스위프트 코딩스타일로서 잘 정리된다.
  • fileprivate는 현재 유스케이스로 남지만, 이제 더 작게 사용되는데, 이것은 몇몇 이점을 가진다.
    • 스위프트를 받치는 "단계적 공개" 철학에 잘 맞는다. 여러분은 fileprivate에대해 매우기전까지 당분간은 public, internal, private를 사용할 수 있다(주의: 이것이 SE-0025의 진실이 될것이라 생각하지만 명백하게 틀렸었다)
    • fileprivate가 나타나면, 같은 파일안에 다른 타입들 사이에 재미있는 엮임이 있다는 뜻이다. 이것은 잠재적으로 우리가 대강 사용하고 훑어보기 보다는 fileprivate가 유용한 경고를 해주어서 익스텐션안에 구현을 분리할 수 있게되나.
  • private는 다른 프로그래밍 언어와 비슷하게 정리되었는데, 이로하여금 스위프트로 넘어오는 프로그래머들에게 도움이 될것이다. 스위프트의 높은 익스텐션 사용때문에 약간의 스위프트 트위스트로, private를 만나게되면 그들이 생각하는 것과 비슷하게 생각할 것이다. (When they reach for private, they’re likely to get something similar to what they expect—with a little Swift twist due to Swift’s heavy use of extensions.)
  • private에서 접근제약을 느슨하게 하는것은 현재 코드를 고치지 않아도 될것 같다.

몇몇 단점도 있어보인다.
  • 현재 사전적-스코프의 private 접근재어에 의존하여 패턴을 사용하고있는 개발자들은 새로운 private 해석이 부적절한 제약이될것으로 보인다.
  • 스위프트의 접근제어는 "전체저으로 사전적인(entirely lexical)"것에서 "부분적으로 사전적이고(partly lexical) 부분적으로 타입기반인(partly type-based)" 것으로 갈것인데, 이것이 더 복잡하게 보일수 있다.

궁극적으로 나는 스위프트에 fileprivateopen 접근수준이 나타나는 변경에는 유감스럽다. 이런 변화들 모두 되돌릴수 있기를 바라고, 대신 Swift theme의 부분으로서 접착력있게 접근제어를 수정하는 방법을 고려하면 좋겠다. Doug이 말한것처럼, fileprivate가 드물게 사용될거라는 가정은 완전히 틀렸었다. 이것은 private의 사전적 스코핑을 부순 익스텐션의 주요 결과이다.

SE-0025요청된 수정은 익스텐션과 새로운 동작의 private의 잠재적 스코핑 이슈에 가볍게 알려놓았으나, 이런 암시는 메일링리스트에 넓게 의논되지 않고, 실제로 스위프트 3이 사용될때까지 개발자들에게 완전히 알리지도 못했다. 되돌아가보면, 이것은 메이저한 착오였다. 이것은 내 타입과 익스텐션을 어떻게 설계하는지 때문에 모든곳에서 fileprivate를 사용해야한다는 것을 알게되었을때, 틀림없이 나를 기습하려는 것이었다. 내 경험상, 새로운 privatefileprivate는 실제 문제에대한 솔루션이라기보다는 부담이다. 타입에서 익스텐션이 기능을 구성하고있는 곳에서는, private의 사전적 스코핑 제약이 이상적이고 규약을 잘 지키는 스위프트가 되려는 마음을 무너뜨리게 만든다. 프로토콜 익스텐션은 이런 망가짐의 징후를 증폭시킨다.

Doug이 잡아놓은 아웃라인인 코어팀의 프로퍼절은 좋은 절충안이다. 이 글을 발행하기전에, David Hart는 이것을위한 Type-based Private Access Level이라는 이름의 프로퍼절 초안으로 풀리퀘스트를 열었다. 이것이 받아들여지고 구현되면 좋겠다. 이것이 스위프트 접근제어 시스템에을 더 복잡하게 할지라도, 많은 복잡성은 세부 구현 뒷편의 이야기이다. 사용자 관점에서 privatefileprivate 수정자는 이것에대해 설명하고 설득하는데에 더 쉽게들린다. SE-0025에 정의된 실제 private 사용 이전에, 나는 많은 스위프트 사용자가 private 맴버를 익스텐션에서 접근가능한 것으로 기대했을것이라 생각한다. Doug이 설명한 "부분적으로 사전적이고 부분적으로 타입기반인" 접근제어의 이점은 명료함이고, 내 생각엔 이것들이 그 결점보다 중요하다.

우리는 분명 최적의 위치에 있지 않다. 이것은 이상에서 멀어졌으나, 실제 문제를 해결한다. 위의 제안이 구현되면, 비록 우리 뒤에 깊이 남긴 발자국이 남겠지만, 우리스스로 그려놓은 막다른길로부터 탈출할 수 있다. 많은 사용자와 많은 시간동안 publicprivate만 필요료하게될 그런 상태로 되돌아가게 될것이다. (internal은 여전히 디폴트인데, 타이핑할 필요 없다.) fileprivate를 사용하는것은 드물고 눈에띄게 될것인데, 아마도 결국 나쁜 방법으로 고려될것이다.

기회비용
이번 스위프트 3.0 릴리즈에서 스위프트 커뮤니티가 많은것을 배웠을거라고 생각한다. 접근제어대해 단지 토론하고 휘젓는것 뿐만 아니라, 전반적인 스위프트 에볼루션 프로세스에대해서도 토론해야한다. 오늘날 우리가 있는 이곳에 어떻에 도달했는지 그들의 결과물로서, 우리는 앞으로 나아가는데 마음속에 담아두고, 프로퍼절을 계속 눈여겨봐얀다. 이 프로그래밍 언어로 붕엇을 하고 싶은가? 언어의 개선사항에서 무엇이 우선시되고 무엇이 연기될까? 현재 스위프트 배포 테마에 여러분의 프로퍼절이 적절한가?

스위프트 에볼루션은 무엇이든 되지만 값싸다. 몇몇은 능동적 위험(actively harmful) 이라 생각한다. 모든 연기처럼 모든 변화에는 비용이 있다. 몇몇 변화는 명백하게 비싼반면 다른것들은 더 미묘하다. 스위프트3은 대단한 비용과함께 도착했다(완전히 다른 목표(이 차이를 보자)과 어마어마하게 고통스러운 마이그레이션) 그러나 이것들은 단지 실제 비용들이었고, 그 변화의 결과물이 만들어졌다.

아마도 고려해야할 더 중요한 것은 만들어지지 않은 변화들이다. 각 스위프트 릴리즈에대한 기회비용은 우리가 버리기로 결정한 변화의 가치이다. (이것을 구현되지 않은 모든것들의 가치라 한다) 여기에는 버그를 고친다던지, 컴파일타임 이슈를 해결한다던지, 런타임 성능을 개선한다던지, 전반적인 안정성을 증가한다던지와같은 작업까지, 주요 기능을 포함한다. 이런 일이 일어나지 않았었다고 말하지 않았다, 그들은 틀림없이 그렇게 했었다. 그러나 많은 시간이 스위프트 에볼루션 프로포절에 쓰였고, 몇몇은 지나고보니 명백하게 연기되어왔을 것이다.

어떤것도 스위프트 커뮤니티가 잘못했다고 뜻하지 않는다. 단지 어떻게 하냐의 문제이다. 우리는 코어팀까지 포함해서 모두 배우는 중이다. 다행히도 코어팀은 스위프트 4 프로포절에대해 더 엄격하고 더 생각을 많이하는것은 틀림없으므로, 이런 상황이 다시 찾아올것이라 염려한다. 그러나 이것은 소프트웨어 개발이다. 여기에는 항상 트레이드오프(등가교환)가 있다.




WRITTEN BY
tucan.dev
개인 iOS 개발, tucan9389

,