본문 바로가기
C.W.K.
Stream
Lesson 04 of 04 · published

경로의 이빨, 그리고 임포트가 아닌 이음매

~14 min · gates, seams, swift, process

Level 0흩어진 부품
0 XP0/41 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete

앉은 그대로가 나가는 바이너리야

다음 트랙이 복사본의 이빨을 설명해. 머리말 떼고, 변수 치환하고, 본문 비교하고, 소비자 자기 테스트에서 돌려. 그건 복사본의 이빨이야. 경로 패키지엔 틀린 이빨이고, 그래도 쓰면 장식용 초록이 나와.

복사본 쪽 이빨은 바이트 비교야. 원본이랑 도착이 머리말을 떼고 같아야 해. 경로 패키지엔 도착이 없어. 소비자는 디스크에 앉아 있는 키트 트리를 그대로 컴파일해. 더러운 작업 트리가 곧 나가는 바이너리야. 어긋남 검사가 비교할 두 번째 파일이 없어.

그래서 이빨이 자리를 옮겼어. 키트-클린 게이트가 소비자 업로드 경로에 앉아. 패키지 소스가 키트의 커밋된 트리와 다르면 아카이브를 거절해. 아카이브는 더러운 키트를 거절해. 로컬 실험은 업로드가 아닐 때만, 그리고 명시 예외를 켰을 때만 통과해. 업로드 경로에선 그 예외가 안 먹어. 비교가 아니라 거절이야. 같은 이빨, 다른 자리.

임포트가 이음매가 되는 순간

두 메커니즘은 한 저장소에 살아. 잎은 벤더되고, 그래프는 패키지로 컴파일돼. 유혹은 패키지가 잎을 임포트하는 거야. 문은 벤더된 인증 잎을 알고, 그 잎의 타입으로 컴파일되고, 소비자가 그 잎을 제때 벤더했기를 바라. 그게 숨은 의존이야. 패키지가 자기 밖에 있는 파일의 존재를 전제하는 거지.

이음매는 주입이야. 문은 프로토콜을 선언해. 검증기를 받아. 벤더된 잎을 컴파일하지 않아. 소비자가 잎을 자기 타깃에 넣고, 검증기를 문에 건네. 패키지는 잎을 몰라. 잎은 패키지를 몰라. 둘 다 프로토콜만 알아. 그게 소속 시험을 이음매에 적용한 거야. 이 타입이 앱이 무엇에 관한 것인지를 알아야 해? 알아야 하면 앱이 넣어. 모르면 패키지가 선언하고 앱이 채워.

컴파일 타임 존재 검사는 이음매가 아니야. #if canImport 는 "있으면 쓰고, 없으면 건너뛰어"야. 그건 의존을 숨기는 거지, 선언하는 게 아니야. 이음매는 프로토콜이고 주입이야. 문이 잎의 존재에 컴파일되면, 맵이 그 잎을 제때 배달했기를 바라는 거고, 그게 버전 정합을 다른 이름으로 부르는 거야.

이빨이 빠지는 자리

게이트가 소비자 업로드 경로에만 있으면, 키트 저장소 자체는 더러운 채로 커밋될 수 있어. 그건 괜찮아. 키트는 바이너리를 안 내보내. 소비자가 더러운 키트를 가리키고 아카이브를 올리면, 게이트가 잡아. 빠진 이빨은 그 반대야. 키트가 깨끗하고 소비자가 벤더된 잎을 빼먹은 채 패키지만 집어가는 것. 문은 프로토콜만 알니까 컴파일돼. 런타임에 검증기가 없으면 문이 거절하거나, 더 나쁘게, 조용히 통과해.

그래서 소비자의 이빨은 두 개야. 키트-클린이 패키지 쪽을 지키고 — 자리는 업로드·아카이브 경로야. 어긋남 검사는 잎 쪽을 지키고 — 자리는 다음 트랙의 소비자 스위트야. 같은 스크립트일 필요는 없어. 더러운 키트가 나가는 바이너리가 되면 안 된다는 요구가 같을 뿐이야. 하나만 있으면 다른 메커니즘이 구멍을 열어. 통과하면 그 바이너리가 실은 키트 커밋을 로그에 찍어. 컴파일된 상품엔 머리말을 못 넣으니까.

Code

주입이지, 임포트가 아니야·swift
// 문 — 패키지 안에 살아. 벤더된 잎을 몰라.
protocol PINVerifier {
    func verify(_ pin: String) -> Bool
}

struct Gate {
    let verifier: PINVerifier
    func open(with pin: String) -> Bool {
        verifier.verify(pin)
    }
}

// 잎 — 소비자 타깃에 벤더돼. 패키지를 몰라.
struct LocalPINHelper: PINVerifier {
    func verify(_ pin: String) -> Bool { pin.count == 4 }
}

// 소비자가 이음매를 닫아.
let gate = Gate(verifier: LocalPINHelper())

// 하지 마:
//   #if canImport(LocalPINHelper)
//   그건 의존을 숨기는 거지, 선언하는 게 아니야.
//   맵이 그 잎을 제때 배달했기를 바라는 거고,
//   그게 버전 정합을 다른 이름으로 부르는 거야.

External links

Exercise

두 메커니즘이 한 저장소에 사는 자리를 찾아. 패키지(또는 워크스페이스 멤버)가 벤더된 잎을 임포트하는지, 아니면 프로토콜을 선언하고 소비자가 넣는지. 임포트면 이음매를 한 문장으로 다시 써. 누가 프로토콜을 선언하고, 누가 구현을 넣고, 어느 게이트가 빠진 쪽을 거절하는지.
Hint
컴파일 타임 존재 검사는 답이 아냐. #if canImport 는 의존을 숨겨. 이음매는 프로토콜이고 주입이야.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.