웹사이트를 Swift로 전면 재작성한 방법
This Website Is Now Made in Swift — Sindre Sorhus
핵심 요약
- Astro로 만들던 웹사이트를 페이지, 컴포넌트, 스타일까지 Swift로 재작성했으며, 브라우저에는 기존처럼 HTML과 CSS, 소량의 JavaScript를 전달한다.
- 타입과 컴파일 검사를 활용해 페이지 링크, 스타일 변형, 콘텐츠 메타데이터 오류를 빌드 단계에서 발견하고, 생성된 HTML의 링크와 ID도 별도로 검사한다.
- 컴포넌트별 CSS와 스크립트만 페이지에 포함하며, 블로그 글은 압축 기준 약 14KB다. 저자는 SwiftUI와 유사한 작성 방식과 자동완성, 리팩터링을 장점으로 든다.
도입
Sindre Sorhus는 Astro로 운영하던 웹사이트를 Swift 기반으로 다시 만들었다. 페이지와 컴포넌트, 스타일은 Swift로 작성하지만 브라우저에는 HTML과 CSS, 소량의 JavaScript를 보낸다. Swift를 선택한 이유는 앱과 웹사이트를 같은 언어로 작성하는 점뿐 아니라, 링크와 스타일, 콘텐츠 데이터의 오류를 브라우저에 도달하기 전에 컴파일러에서 찾을 수 있기 때문이다.
상세 요약
Swift로 작성하고 브라우저에는 표준 자산 전달
사이트는 이전에 Astro로 만들어졌지만, 이제 모든 페이지와 컴포넌트, 스타일을 Swift로 작성한다. 브라우저가 받는 결과물은 HTML과 CSS, 소량의 JavaScript다. 사이트의 HTML 생성에는 Elementary를 사용하고, 스타일과 라우트, 콘텐츠는 저자가 만든 소규모 프레임워크로 구성한다.
컴포넌트는 body를 가진 struct로 정의한다. 이 구조는 SwiftUI와 비슷한 형태다. 코드 예시에서 BackLink는 앱 데이터를 받아 링크를 만들고, .style modifier로 스타일을 적용한다. 코드 예시의 구문 강조도 Swift로 처리한다. 빌드 과정에서 swift-syntax로 코드를 파싱해 토큰별 색을 지정하므로 구문 강조용 JavaScript는 필요하지 않다.
타입으로 스타일과 링크 검사
스타일은 요소에 modifier처럼 붙인다. .style { … }은 파일 이름과 스타일 해시를 바탕으로 CSS 클래스를 생성한다. 클래스가 다른 요소로 새어 나가지 않으며, 생성된 HTML에서 어느 파일의 스타일인지 확인할 수 있다. 공통 스타일과 변형은 enum case로 정의해 자동완성과 컴파일 검사를 사용한다. 각 페이지에는 해당 페이지에서 쓰는 컴포넌트의 CSS만 들어간다.
CSS 변수도 StyleVariable로 선언하고 지정된 타입의 값을 사용한다. RegisteredProperty를 쓰면 브라우저가 애니메이션할 수 있도록 @property 규칙도 페이지에 생성된다. 반응형 스타일의 breakpoint와 미디어 쿼리 역시 타입으로 표현하며, 여러 조건을 결합할 수 있다. 색상은 의미를 나타내는 이름으로 지정하고 밝은 테마와 어두운 테마 값을 둔다. 대비 설정에 따라 텍스트, 링크, 선의 색을 더 강하게 표시하는 구성도 컴포넌트에 적용된다.
페이지는 정적 멤버로 표현한다. 따라서 링크 대상은 자동완성되며, 존재하지 않는 페이지를 지정하면 컴파일되지 않는다. 빌드 후에는 생성된 HTML에 있는 모든 링크와 ID도 검사한다. 동작이 필요한 컴포넌트는 custom element로 만든다. 작은 스크립트는 Swift 파일 안에, 큰 스크립트는 컴포넌트 옆의 .js 파일에 둔다. 스크립트는 자신이 속한 요소만 다루며, 프레임워크나 번들러는 사용하지 않는다.
콘텐츠 검증과 페이지별 자산
각 앱의 콘텐츠는 타입이 지정된 메타데이터를 포함한 Markdown 파일이다. Swift macro인 @Frontmatter가 컴파일 시 디코더를 생성한다. @NonEmpty는 빈 값을 거부하고, @CalendarDay는 날짜를 Date로 변환하며 존재하지 않는 날짜를 거부한다. 알 수 없는 플랫폼이나 잘못 쓴 키도 파일과 줄을 표시하는 오류가 된다. swift run website check는 철자 오류와 App Store 정보에 어긋나는 앱 요구사항 등 컴파일러가 잡지 못하는 항목을 검사한다.
블로그 글 하나는 압축 기준 약 14KB로, HTML 6.6KB, CSS 5.4KB, JavaScript 1.8KB다. Lungo 같은 앱 페이지는 스크린샷을 제외하고 약 29KB다. 공통 스타일시트 외에는 페이지에 포함된 컴포넌트의 스타일과 스크립트만 전달한다. 웹 폰트는 없고, 화면 아래쪽의 이미지는 지연 로딩한다. Chrome에서는 링크 위에 포인터를 두면 페이지를 백그라운드에서 불러온다.
변경을 지원하는 빌드 구조
경고가 있으면 빌드를 실패시키고, 수백 개의 테스트로 타입만으로 확인할 수 없는 항목도 검사한다. 예를 들어 모든 CSS 클래스에 규칙이 있는지 확인한다. 컴포넌트는 마크업, 스타일, 스크립트를 한곳에 보관하며 삭제하면 연결된 CSS와 JavaScript도 함께 제거된다. swift run website serve는 파일 저장 시 페이지를 다시 불러온다. 저자는 자동완성과 이름 변경 중심의 리팩터링을 통해 큰 변경을 부담 없이 할 수 있다고 설명한다. 공개된 사이트 코드는 GitHub에서 확인할 수 있다.
결론
이 구현은 Swift의 타입 검사와 자동완성을 웹사이트 구성 요소에 적용한다. 존재하지 않는 페이지 링크와 잘못된 스타일 변형은 컴파일 오류가 되고, 콘텐츠 메타데이터는 타입과 별도 검사 명령으로 검증한다. 컴포넌트의 마크업·스타일·스크립트를 한곳에 두고 페이지에 필요한 자산만 포함한다. 저자는 이런 구조가 리팩터링과 큰 변경을 쉽게 하고 웹사이트 작업을 앱 개발만큼 즐겁게 만들었다고 설명한다.