처음 열린 KWDC 2023

한국에서 KWDC가 처음 열린다는 소식에 바로 신청했다. iOS를 하면서 한 번은 이런 자리에 가보고 싶었고, 어떤 사람들이 와서 무슨 이야기를 하는지도 궁금했다.

세션 목록에는 현대자동차의 TCA 도입, 애니메이션, LiDAR로 실내 측위를 시도한 발표가 있었다. 카젠에서 TCA를 쓰고 있어서 현대자동차 세션이 특히 궁금했다. 학생 할인으로 티켓을 샀고, 시간대가 겹치는 세션이 많아서 타임테이블을 보고 들을 것을 미리 정했다.

KWDC 2023 세션 안내

아이디어스의 Localization

첫 세션은 아이디어스 애플사이다 님의 '글로벌 앱을 위한 Localization, 번역은 시작일뿐'이었다. 다국어 지원을 언어별 문자열만 바꿔 끼우는 일로 생각하고 들어갔는데, 발표가 다루는 범위가 그보다 넓었다.

Locale의 축

iOS의 Locale은 한 덩어리가 아니라고 했다.

  • 언어와 지역은 별개다. 한국에 살면서 기기를 영어로 쓰는 사람도 있고, 미국에 살면서 한국어를 쓰는 사람도 있다.
  • 시간대(TimeZone)도 지역을 따라가지 않는다. 지역이 미국이라고 미국 시간대를 쓰는 것은 아니다.
  • 확인은 Locale.current.identifier, Locale.preferredLanguages로 한다.

언어와 지역 변경에 따른 Locale 시나리오

지역에 따라 온도 단위와 통화 표기가, 언어에 따라 날짜와 숫자 표기가 달라진다고 했다. 날짜 형식과 숫자 체계, 온도 단위는 사용자가 개별로 바꿀 수도 있어서, 지역만 보고 시간대까지 추측하면 틀린다고 했다.

String(localized:)와 String Catalog

WWDC 2022와 2023 API부터 소개했다.

  • SwiftUI Text에 문자열 리터럴을 직접 넘길 때와 String 변수를 넘길 때 Localization 동작이 다르다. 같은 문자열처럼 보여도 타입과 초기화 방식에 따라 처리가 갈린다.
  • String(localized:)를 쓰면 번역 대상임을 명시할 수 있고, 컴파일러가 문자열을 추출하거나 보간 값의 타입을 파악하는 데도 쓰인다.

SwiftUI의 LocalizedStringKey와 Text

Xcode 15의 String Catalog는 .strings와 .stringsdict로 나눠 관리하던 번역 문자열을 하나로 모은다. 코드에서 쓰는 문자열을 자동으로 추출해 추가와 삭제를 카탈로그에 반영한다고 했다.

Xcode 15의 String Catalog

Dynamic Line Height

영어, 아랍어, 힌디어, 태국어는 같은 뜻의 단어라도 글자 높이가 제각각이다.

언어별 Dynamic Line Height 차이

발표에서는 iOS 17에서 개선된 Dynamic Line Height와 UIFont.preferredFont(forTextStyle:)로 이를 처리한다고 했다. 텍스트가 몇 줄까지 들어갈지는 생각해 봤어도, 언어에 따라 줄 높이 자체가 달라진다는 건 이때 처음 들었다.

숫자도 지역마다 다르다. 소수점을 마침표로 쓰는 곳도, 쉼표로 쓰는 곳도 있고 천 단위 구분자도 다르다. 구분자를 직접 넣으면 다른 지역에서는 틀린 숫자가 되고, NumberFormatter가 지역 설정에 맞춰 붙인다고 했다.

문자열의 의미도 같이 봐야 한다고 했다. 영어 book은 '책'일 수도, '예약하다'일 수도 있어서 같은 단어라고 번역 키를 공유하면 다른 언어에서 엉뚱한 말이 된다. 키를 나누고 번역자가 맥락을 알도록 주석을 단다고 했다. 스페인어처럼 단어에 따라 문법 형태가 달라지는 언어를 위한 Grammatical Agreement API도 소개했다.

RTL과 SF Symbols

아랍어나 히브리어처럼 오른쪽에서 왼쪽으로 읽는 언어는 텍스트 정렬만 바꾼다고 끝이 아니었다.

  • 탐색 방향
  • 버튼 배치
  • 화살표처럼 방향을 가진 아이콘

RTL 환경에서 달라지는 UI와 SF Symbols

SF Symbols의 방향별 아이콘 처리와 Airbnb 적용 사례를 보여줬다. 다만 모든 아이콘을 뒤집는 건 아니고, 읽기 방향에 맞춰 바꿀 것과 그대로 둘 것을 구분해야 한다고 했다.

Airbnb의 Right-to-Left 적용 사례

아이디어스의 배송지

아이디어스가 실제로 겪는 사례도 나왔다. 싱가포르에 사는 사용자가 베트남 친구에게 선물을 보내는 경우였다.

아이디어스의 국가별 배송지 및 주문 처리 사례

거주지와 배송지가 다르고, 팔 수 있는 상품과 배송비, 가격도 지역마다 달라서 기기 지역 설정만으로는 처리할 수 없다고 했다. 앞에서 본 온도나 날짜, 숫자 표기가 정보를 어떻게 보여주느냐의 문제였다면, 이건 지역에 따라 제공할 기능과 데이터 자체가 달라지는 문제였다. 배송 가능 여부는 앱 언어보다 실제 배송지가 기준이 된다고 했다.

카카오뱅크 모듈화

다음은 카카오뱅크 제온 님의 '우리가 모듈화를 할 수밖에 없었던 이유'였다. 코드를 모듈로 잘게 나눈 경험담일 줄 알고 들어갔다.

카카오뱅크의 모듈화 발표

모듈화를 시작하게 된 이유

  1. 코드량이 늘었다.

    2020년 109만 라인, 2021년 123만, 2022년 158만, 2023년 6월 197만.

  2. 사람 수와 상품이 늘면서 로컬 빌드 횟수와 한 번 빌드에 걸리는 시간도 같이 늘었다.

    빌드가 느려지면 테스트도, 그 빌드를 기다리는 다른 직군 시간도 같이 묶인다.

  3. 파일이 많아지자 Xcode 빌드가 Argument list too long으로 실패하기 시작했다.

    Xcode는 컴파일러를 부를 때 파일 경로를 하나씩, 절대경로로 넘긴다. 파일이 많고 경로가 깊을수록 운영체제가 한 번에 받을 수 있는 인수 크기(getconf ARG_MAX, 1024 * 1024)를 넘는다.

Xcode 빌드 과정에서 발생하는 Argument list too long

파일이 늘면 빌드가 느려지는 줄만 알았지, 어느 선을 넘으면 컴파일러를 호출하는 것 자체가 실패한다는 건 처음 들었다. 발표에서는 데모 타깃만 따로 만들어 작업하는 방법과, 프로젝트에 모듈을 두고 작업하는 방법 중 후자를 골랐다고 했다. 앞쪽은 임시방편에 가깝고, 뒤쪽은 익히는 데 시간이 걸리지만 그쪽으로 갔다고 했다.

모듈 현황과 빌드 속도

모듈 수는 2020년 43개, 2021년 49개, 2022년 153개, 2023년 6월 319개였다. 본격적으로 나누기 시작한 해는 2022년이었고, 그 뒤 반년 만에 그 전 한 해보다 많이 늘었다고 했다. 발표자가 바로 이어서 물은 건, 모듈이 많다고 무조건 나은가였다.

2020년부터 2023년 6월까지 카카오뱅크의 모듈 개수 변화

그래프에서 보여준 빌드 시간은 이랬다.

  • 모듈화 시작 무렵: 전체 빌드 650초, 증분 빌드 250초
  • 2023년 6월: 전체 빌드 505초, 증분 빌드 153초

전체 빌드보다 증분 빌드가 더 많이 줄었다. 증분 빌드는 바뀐 파일과 그 파일에 의존하는 쪽만 다시 돌린다. 모듈 안에도 태스크 순서가 있어서, 의존 없는 모듈은 병렬로 돌릴 수 있고, 의존이 있는 쪽은 순서가 보장되어야 한다고 했다.

특정 기능을 확인하려고 앱 전체를 타고 들어갈 필요 없이, 그 기능만 올리는 샘플 앱을 만들어 생산성을 높였다는 이야기도 나왔다.

모듈 기준

카카오뱅크는 RIBs를 쓴다. RIB은 다른 RIB을 라우팅할 수 있고, 하나 이상의 RIB에 의존할 수 있다. 이 연결을 RIB 트리라고 불렀다. 의존성을 나눠 주입하는 형태라 모듈화와 잘 맞는 편이라고 했다.

그래도 자르는 기준은 따로 정해야 했다. RIB 하나를 모듈로 둘지, 기능 단위로 둘지, 화면 단위로 둘지를 두고 고민했다고 했다.

  • 한 모듈에 RIB을 다 넣으면 나눠 봤자 다시 같이 빌드된다.
  • 의존성과 상관없이 자르면 공통으로 쓰이는 코드가 애매해진다.
  • 너무 잘게 쪼개면 모듈 개수 자체가 일이 된다.

정답은 없다고 보고, 기준은 세 가지였다.

  1. 빌드가 어떻게 도는지
  2. 프로젝트 전체의 의존성이 어떻게 이어지는지
  3. 병렬로 돌렸을 때 이득이 있는지

정적 링킹과 동적 링킹

모듈을 나눈 다음에도 어떻게 링크하느냐가 빌드 시간과 실행 시간을 다시 가른다고 했다. WWDC 2022 링킹 세션을 같이 보라고도 했다.

정적 라이브러리

  • 링크할 때 쓰는 코드를 앱 바이너리에 복사한다.
  • 링크 시간과 앱 크기에 영향을 준다.

동적 라이브러리

  • 코드는 실행 파일에 복사되지 않고 앱 번들에 들어간다.
  • 정적 링커가 코드를 복사하지 않아서 빌드는 빨라질 수 있다.
  • 실행 때 동적 링커가 붙어서 구동은 느려질 수 있다.

정적 라이브러리와 동적 라이브러리

Xcode의 Mergeable Libraries도 짧게 소개했다. 모듈화할 때는 open과 public 차이도 알아야 한다고 했다. open은 모듈 밖에서도 상속과 오버라이드가 되고, public은 모듈 밖에서 상속과 오버라이드가 안 된다.

점심에는 후원사 부스를 둘러봤다. 채용 상담도 있었지만 대부분 경력직 대상이라 학생인 내가 상담받을 자리는 없었다. 대신 부스에 있던 개발자들에게 학교에서 배우는 개발과 회사에서 제품을 만드는 과정의 차이, 다른 회사가 쓰는 기술 같은 걸 몇 가지 물어봤다.

현대자동차의 TCA

카젠에서 TCA로 개발하고 있어서, 다른 조직은 이 아키텍처를 어떻게 쓰는지 들으러 갔다. 세션 제목은 강성규 님의 '제조업에서 SwiftUI와 TCA로 앱 개발하기'였다.

현대자동차와 Point-Free 협업 세션

아직 정식 1.0도 안 나온 TCA 0.5.x를 프로덕션 앱에 넣고, 라이브러리를 만든 Point-Free와 직접 협업하고 있다고 했다. 평소 오픈소스는 공개된 문서와 예제를 보고 갖다 쓰고, API가 바뀌면 코드를 따라 고친다. 현대자동차는 만든 쪽과 버전 변경을 가까이에서 논의하고 있다고 했다.

개발 조직은 아웃소싱 중심에서 내부 개발로 바꾸는 중이고, 소프트웨어 조직은 스타트업처럼 움직인다고 소개했다.

TCA 구조

발표에서 정리한 흐름은 이렇다.

  • State: 화면이 가진 값
  • Action: 버튼 탭처럼 일어나는 일
  • Reducer: 액션을 받아 State를 바꾼다
  • Effect: 네트워크나 파일 I/O처럼 바깥과 맞닿는 일
  • Dependency: 예전에 Environment로 붙어 있던 의존성을 따로 떼어 낸 부분

단방향으로 흐른다. Composable은 작은 구성 요소를 모아 더 큰 기능을 만드는 쪽에 가깝다고 했다. SwiftUI가 뷰를 조합하는 것과 비슷한 쓰임이라고 소개했다. 카젠에서 쓰면서도 예제 밖의 큰 앱에서는 상태를 어디에 둘지 잘 몰랐는데, 그다음이 그 이야기였다.

Global State와 Dependency

Home 탭과 Control 탭에서 차량 원격 제어 상태를 같이 봐야 하는 경우가 있었다고 했다. 하위 화면에서 같이 쓰면 부모로 올리게 되는데, 그렇게 올리면 최상위 역할이 너무 커져서, 최상위에서 쓰는 상태는 Dependency로 옮겼다고 했다. 싱글톤처럼 보이지만 Dependency 라이브러리에서 테스트나 교체 도구를 제공해서 큰 문제는 없었다고 했다.

Tree-Based Navigation

네비게이션은 트리 구조로 뒀고, 딥링크로 특정 화면까지 연결할 수 있다고 했다. NavigationLink가 push 뒤에 바로 pop되는 버그가 있어서 처음에는 딜레이를 넣었고, 나중에는 마지막 depth 애니메이션만 남기고 중간 과정 애니메이션을 빼는 쪽으로 바꿨다고 했다. 뷰가 푸시되기 전에 State가 먼저 만들어지기 때문에 가능했다고 했다. 이 부분은 현장에서 코드를 따라가긴 어려웠고, 상태를 트리에 두고 화면을 연다는 정도만 남았다.

혼자 와서 세션만 듣고 돌아갔다. 모르는 용어도 많았고 중간에 놓친 세션도 있었다. 대부분은 다른 회사가 실제로 막힌 지점과, 그다음에 고른 기준을 들려주는 자리였다.

이전 노트팀원과 팀장으로 치른 로봇대회다음 노트제대로 알고 싶었던 한 해 (2024 회고)