logo
기술 블로그
logo
효율 최강 파인튜닝 솔루션 Unsloth (ft. Continued Pre-Training)
지난 포스팅에서는 코딩 없이 모델을 파인튜닝 할 수 있는 대표적인 No-Code 파인튜닝 툴인 LLaMA-Factory와 Axolotl을 소개해드렸습니다.오늘은 가난한 자의 파인튜닝 솔루션 Unsloth 라는 툴을 소개하려고 합니다.'Unsloth'는 사전적으로 게으르지 않은, 나태하지 않은 이라는 의미를 가지고 있으며, 끊임없이 노력하고 발전하는 사람들을 칭찬하는 의미의 신조어입니다.이름에서 유추할 수 있듯이, Unsloth 개발자들은 파인튜닝의 성능과 효율성을 끌어올리기 위해 부단히 노력하고 있다고 합니다.하지만 재미 있게도, 마스코트는 느림의 대명사 나무늘보를 내세우고 있습니다.제가 '가난한자의 파인튜닝 솔루션'이라고 표현한 이유는, Unsloth가 적은 자원으로도, 심지어 GTX와 같은 소비자용 그래픽 카드에서도 LLM을 파인튜닝할 수 있는 방법을 제공하기 때문입니다.GPU 자원이 적어서, GPU 모델이 낮아서 LLM을 직접 튜닝하지 못했던 여러 사용자들에게 유용한 솔루션이 될 수 있을 것으로 생각 됩니다.더불어 학습 속도를 높여 좀 더 생산적인 파인튜닝을 할 수도 있을 것입니다.이전 포스팅을 통해 툴 자체의 소개뿐만 아니라 데이터 전처리, 프롬프트 템플릿, Instruction tuning, Alignment 기법 등 여러 가지 파인튜닝에 필요한 기법들을 같이 소개해드렸습니다.이번 포스팅에서는 그동안 언급만 하고 실제 구현해보지 못했던 Full 파인튜닝에 대해서도 간단히 다루려고 합니다.이 글에서 다루는 내용은 다음과 같습니다.• None Unsloth은 파인튜닝을 어떻게 효율화 했을까?• None Continued pre-training이 필요한 이유와 Unsloth을 통한 학습 방법Unsloth는 Daniel과 Micheal Han 형제가 파인튜닝 효율화를 위해 시작한 프로젝트입니다.시작한 지 얼마되지 않은 신생 프로젝트임에도 불구하고, 최근 파인튜닝 분야에서 많은 사용자들의 지지를 받고 있는 솔루션입니다.트랜디한 최신 기술을 비교적 빠르게 적용하고 있고, 두 형제를 주축으로 커뮤니티 활성화도 잘 되어 있는 편입니다.솔루션은 기본적으로 무료 버전으로 제공되지만, 2개 이상의 GPU를 사용하는 멀티 GPU 학습의 경우 유료 서비스가 필요합니다.이로 인해 솔루션의 한계가 명확하며, 오픈소스와 유료 서비스 간의 간극을 어떻게 해결할지가 향후 솔루션의 대중화에 영향을 미칠 것으로 보입니다.자세한 내용은 아래 링크를 참조하세요Unsloth는 LLM의 파인튜닝을 최적화하기 위한 툴입니다. 여기서 '최적화'란 파인튜닝 시 필요한 메모리 사용을 줄이고 학습 속도를 높이는 것을 의미합니다.다음과 같은 특징들로 사용자로 부터 좋은 피드백을 받고 있습니다.• None• None Unsloth는 전통적인 학습 기법에 비해 현저한 학습 속도 향상을 제공합니다.• None 사용하는 LLM 모델과 GPU 종류에 따라 성능 차이가 있을 수 있지만, 대부분의 조합에서 기존 학습 기법을 능가하는 성능을 발휘합니다.• None• None Unsloth는 메모
SK텔레콤
·
오늘
logo
[Swift] 한글 검색 라이브러리 만들어보기
그동안 다양한 오픈소스 라이브러리를 사용해 오면서 그동안 직접 라이브러리를 만들어보고 싶다는 생각을 하고 있었는데,이번 토스에서 만든 es-hangul(https://github.com/toss/es-hangul) 을 보고 여러 한글 관련 프로젝트를 진행해왔던 경험을 살려Swift를 활용한 한글 검색 라이브러리를 만들게 되었습니다.• None 초성 검색: 사용자가 입력한 초성에 맞는 데이터를 검색합니다.• None 자동 완성: 사용자의 입력에 기반한 자동 완성 결과를 제공합니다.• None 전체 문자열 검색: 입력된 전체 문자열을 포함하는 데이터를 검색합니다.• None 종합 검색 모드: 초성 검색과 자동 완성을 결합하여 더 넓은 범위의 검색을 지원합니다.• None 기본: 정렬을 수행하지 않음. 데이터 순서대로 반환• None 자모 순서: 항목들을 한글 자모 순서대로 정렬• None 자모 역순: 항목들을 한글 자모 역순으로 정렬• None 편집 거리: Levenshtein (편집) distance 기준으로 가장 유사한 순서대로 정렬한글의 초성 검색은 한글 음절을 초성, 중성, 종성으로 분리하여 초성만을 추출하고 이를 이용해 검색하는 방식입니다. 한글 음절은 유니코드 표준을 활용하여 분리됩니다. 초성 검색의 기본 원리는 다음과 같습니다:• None 한글 음절은 유니코드 범위 0xAC00에서 0xD7A3까지의 값을 가집니다. 이 범위 내에서 각 음절의 유니코드 값을 이용하여 초성, 중성, 종성을 분리할 수 있습니다.• None 한글 음절의 유니코드 값에서 '가'의 유니코드 값을 뺀 후 초성, 중성, 종성의 인덱스를 계산합니다.• None 한글 문자열의 경우, 입력된 문자열을 분해하여 접두사 비교를 보다 정확하게 수행할 수 있습니다. 이를 위해 한글 음절을 초성, 중성, 종성으로 분해합니다.• None 입력된 문자열을 분해하여 접두사 비교를 수행합니다.iOS에서 초성 검색의 필요성iOS의 기본 문자열 검색 기능은 한글의 초성을 인식하지 못하는 경우가 많아, 로컬에서 검색을 수행하는 앱들의 경우 초성 검색 및 자동 완성 검색을 지원하지 못하는 경우가 많았습니다.이를 통해 사용자들이 iOS 환경에서 쾌적한 한글 검색 경험을 할 수 있으면 좋겠습니다.
SK텔레콤
·
오늘
logo
대규모 CDC Pipeline 운영을 위한 Debezium 개선 여정
Change Data Capture(이하 CDC)는 쉽게 말하면 정적인 데이터를 동적인 형태로 복제하는 것인데요. 데이터베이스 안에서 일어나는 변경사항들을 감지하고, 각 변경사항을 이벤트로 변환해서 이벤트 스트림으로 전송할 수 있게 해줘요. CDC의 장점은 데이터베이스의 변경사항을 실시간으로 받을 수 있다는 점입니다. 이런 데이터를 새로운 데이터베이스에 저장하거나, 새로운 기능을 만들거나, 데이터 후속처리할 때 사용할 수 있어요.토스증권에서는 이미 다양한 데이터들에 대해 CDC 기술을 도입하고 있습니다. Data Analyst 분들이 사용하는 분석계 데이터, ML Engineer 분들이 학습용 데이터, 토스 앱에서 사용되는 서비스용 데이터 등 다양한 분야에서 CDC를 도입하여 실시간으로 데이터를 제공하고 있었습니다. 특히 CDC를 오픈소스로 제공하는 Debezium을 적극 도입하여 사용 중에 있습니다. 문제 없이 여러 데이터들을 CDC를 통해 제공하던 어느 날, 근본적인 질문이 하나 떠오릅니다. 우리의 CDC는 얼마나 잘 운영되고 있는가? CDC가 잘 운영되고 있다는걸 우리는 어떻게 믿을 수 있을까?CDC를 잘 운영한다는 것CDC는 좋은 도구이지만 hourly 혹은 daily로 처리하던 배치(Batch) 처리하던 데이터들을 스트리밍(Streaming) 처리로 바꾼다는 건 늘 큰 부담이 됩니다. 배치 처리는 처리한 데이터의 처음과 끝을 알 수 있고 처리에 소요된 시간을 알 수 있는 반면, 스트리밍 처리를 하게 되면 특정 시간에 어떤 데이터가 처리되었는지, 처리되는데 소요된 시간 등을 정확하게 확인하기 어렵고 이는 곧 운영의 어려움으로 이어집니다.현재의 Debezium을 활용한 CDC에서는 이 질문에 답할 수 없었습니다. CDC Pipeline들에서 발생한 문제들을 짐작만 하고 있거나, 데이터 사용자들에게 문제를 보고 받았을 때야 문제를 인지할 수 있었습니다. Pipeline의 상태에 대한 무지로 인해 CDC를 더 많은 곳에 활용하기 꺼려지도록 만들었고, 새로운 pipeline이 늘어남에 따라 CDC 운영이 큰 짐이 되어갈 뿐이었습니다.그래서 저희는 CDC Pipeline을 잘 운영하기 위해, 여러 지표를 세우고 그 지표를 개선하기 위해 노력했어요. Connector의 개선, Debezium Source Code 수정, 운영 방식에 대한 변경을 통해 더 가시적인, 더 활용도 높은 CDC Pipeline 생애 주기를 만들고자 하였고, 오늘은 그 여정의 일부를 소개하고자 합니다. 많은 여정 중, 특히 CDC의 주된 사용 케이스인 원천 데이터베이스를 통해 만들어낸 Event를 다른 Target System에 저장하는 경우를 다뤄보겠습니다.우선 지표를 세워야 합니다. CDC Pipeline이 잘 운영된다고 믿고 싶다면, 앞으로도 변화에도 잘 운영될 수 있다면 어떤 지표들을 세워야 할까요. 저희는 Target System에 데이터를 저장하는 CDC Pipeline의 운영을 위해서는 크게 4가지 핵심 지표를 선정했어요.• None 원천 데이터베이스에서
비바리퍼블리카
·
하루 전
logo
네이버 검색 SRE - 상위 레벨 모니터링 시스템
네이버에는 검색, 쇼핑, 플레이스 등 대한민국 사람들의 삶에 밀접한 관계가 있는 수많은 서비스가 있습니다. 이렇게 필수적인 서비스를 안정적으로 제공하는 것은 매우 중요합니다. 서비스의 가용성과 성능을 유지하고 문제 발생 시 빠르게 대응하기 위해서는 효과적인 모니터링 시스템과 대응 체계가 필요합니다.이 글에서는 상위 레벨 모니터링 시스템을 구축하고 사용한 경험을 소개하고자 합니다. 특히 네이버 통합 검색에 상위 레벨 모니터링 시스템을 적용한 사례를 통해 상위 레벨 모니터링 시스템 구축에 중요한 요소와 경험, 노하우를 공유합니다.하위 레벨 모니터링 시스템과의 차이점본격적으로 설명하기 전에 하위 레벨 모니터링 시스템(micro monitoring system)과의 차이점을 설명하겠습니다. 하위 레벨 모니터링 상위 레벨 모니터링 모니터링 범위 서비스 내부의 특정 레이어 혹은 특정 서버, 인스턴스 개별 서비스, 전체 서비스와 연관 서비스 지표 호스트 단위의 지표 서비스별, 레이어별 통합 지표 먼저 하위 레벨 모니터링 시스템의 관심사는 서비스 내부의 특정 계층 혹은 개별 서버 단위입니다. 특정 서비스의 컴포넌트 담당자가 직접 지표를 보고 문제 지점을 확인해 대응할 수 있을 정도의 해상도를 제공해야 합니다.반면, 상위 레벨 모니터링 시스템의 관심사는 서비스 전체, 개별 서비스의 레이어와 같이 하나의 서비스 혹은 여러 서비스를 구성하는 모든 계층을 아우릅니다. 그렇기 때문에 모니터링의 근간이 되는 지표는 하위 레벨 모니터링 시스템과 다르게 더 통합된(aggregated) 지표입니다.네이버 검색은 통합 검색과 하위 서비스들, 그리고 수백 개의 개별 서비스로 이루어져 있습니다. 그리고 각 서비스에는 여러 레이어가 있습니다. 또한, 검색 결과를 만들기 위해 여러 서비스는 서로 트래픽을 주고받기도 합니다. 이렇게 복잡한 네이버 검색 시스템의 효과적인 모니터링을 위해 구축해 사용하고 있는 상위 레벨 모니터링 시스템의 구축 과정과 효과를 설명하겠습니다.네이버 검색의 하위 레벨 모니터링 시스템의 한계기존의 하위 레벨 모니터링(micro monitoring) 시스템은 단일 호스트나 단일 서버를 대상으로 지표를 수집 및 가시화하여, 단일 서버의 문제 또는 서비스의 특정 레이어의 문제를 파악하기에 유용했습니다.하지만 네이버 검색이 성장하고 다양한 시스템과 도구가 생겨나면서 모니터링 대상 또한 수만 대의 서버와 수백 개의 서비스로 늘어났고 각 서비스는 여러 레이어로 구성된 거대하고 복잡한 시스템이 되었습니다. 그 결과 기존의 하위 레벨 모니터링 시스템만으로는 모니터링 및 장애 관제를 효율적으로 하기에는 어려움이 생겼습니다.하위 레벨 모니터링 시스템에서는 단일 서버, 단일 서비스 특정 레이어의 지표를 확인할 수는 있었지만 정보 체계가 일원화되지 않았고 개별 서비스의 레이어별 대시보드를 보며 관제해야만 했습니다.그러다 보니 네이버 검색 시스템 전체 서비스의 건강도를 확인하기 위해 개별 대시보드를 찾고 서비스 담당자와 소통해 문제를 파악해야 했습니다. 서비스가 많아지면서 개별 담당자와의 커뮤니케이션 비용 및 대시보드를 찾는 비용이 증가했고 상위 레벨 모니터링 시스템이 필요해졌습니다.상위 레벨 모니터링 시스템을 위한 정보 체계 일원화기존의 개별 서비스 레이어별 대시보드에서 제공하는 정보를 하나의 대시보드에 동일한 기준으로 모아서 한눈에 확인하기 위해서는 정보 체계를 일원화해야 했습니다.서비스별 ID 발급상위 레벨 모니터링 시스템의 첫걸음으로, 각 서비스에 고유한 식별자(Search Service Unique ID)를 발급했습니다. 발급한 서비스별 ID를 서비스별 장비 지표, 컨테이너 지표에 매핑하여 서비스 단위의 지표를 구분할 수 있었습니다. 이는 서비스 단위 대시보드 및 알림의 기반이 되었습니다.표준 서비스 구조 가시화대부분의 검색 서비스는 표준화된 호출 구조로 이루어져 있습니다. 데이터 흐름을 이해하기 위해 각 서비스의 전체 레이어 구조를 가시화했습니다. 이렇게 가시화된 정보는 상위 레벨 모니터링 시스템의 통합 대시보드를 구성할 때 사용되었습니다.서비스별 담당자 매핑특정 서비스 레이어에 문제가 발생했을 때 실제 담당자를 확인하고 빠르게 소통해 대응할 수 있도록, 서비스별로 각 레이어의 담당자(directly responsible individual, DRI) 매핑 시스템을 개발하고 지속적으로 갱신했습니다. 특정 서비스 레이어에서 이상이 감지될 경우 담당자에게 바로 알림을 전송할 수 있습니다.상위 레벨 모니터링 시스템 - 통합 대시보드와 알림정보 체계를 일원화해 상위 레벨 모니터링 시스템을 위한 기반을 마련했고 이를 사용해 대시보드와 알림을 만들 수 있었습니다. 상위 레벨 모니터링 시스템이 제공하는 기능과 특징을 소개하고 운영을 위해 어떤 노력을 하고 있는지 소개합니다.통합 대시보드 오버뷰통합 대시보드의 오버뷰의 모습입니다. 통합 검색과 하위 서비스들의 레이어별 현재 상항을 한눈에 보여주도록 구성되어 있습니다.통합 대시보드 상세보기통합 대시보드 상세보기에서는 네이버 통합 검색의 이용자 요청이 들어오는 최상단 레이어부터 최하단 레이어까지 레이어별 트래픽과 가용량을 계산해 한눈에 보여줍니다.장애 발생 시 대시보드에서 바로 확인하고 장애가 각 레이어에 어떻게 영향을 주는지 파악할 수 있었습니다. 또한, 모든 담당자가 볼 수 있는 공통된 대시보드가 존재하기 때문에 특정 서비스, 레이어의 개별 대시보드를 찾아보는 비용과 담당자와의 커뮤니케이션 비용을 줄일 수 있었습니다.서비스별 지표 뷰잉 및 알림개별 서비스의 레이어별 대시보드를 공통된 뷰로 제공해, 상위 레벨 모니터링 시스템 안에서 개별 서비스 내부의 문제 식별이 가능합니다.서비스 내 개별 레이어의 트래픽을 확인할 수 있어, 문제 및 영향도 파악에 도움이 되었습니다.간단하지만 강력한 가용량 규칙 개발최대가용배수와 부하증가배수라는 개념을 도입하여 갑작스러운 트래픽 증가와 같은 가용량 임계 상황에 대비했습니다.최대가용배수: 현재 받는 트래픽을 기준으로 더 받을 수 있는 트래픽의 배수부하증가배수: 복제 서버 중 1대에서 문제 발생해 복제에서 빠질 경우 남은 서버에 생기는
네이버
·
하루 전
logo
네이버 검색 SRE - 지진과 비상 대응 시스템
잠자던 개발자 L 씨는 2024년 7월 12일 아침 8시 26분 휴대폰에서 재난문자 알림 음을 들었습니다.알림 음에 깜짝 놀라서 깬 L 씨는 지진 재난문자를 보고 바로 네이버 메인에서 지진을 검색하고 뉴스 속보를 확인했습니다. 개발자 L 씨뿐만 아니라 재난문자를 받은 수많은 사람이 똑같이 네이버에서 동시에 지진을 검색했습니다. 이렇게 갑작스럽게 많은 사람이 네이버를 접속하여 지진을 검색했는데도 네이버 검색은 평소와 다름없이 동작했습니다.네이버 검색에서는 서비스 건강을 지키기 위해 비상 대응 시스템이 존재합니다. 이번 지진과 같은, 재난, 재해 발생으로 갑자기 많은 트래픽이 들어와도 견고한 네이버 검색 시스템을 위한 비상 대응 시스템은 무엇인지 그리고 왜 필요한지, 네이버 검색 SRE - 상위 레벨 모니터링 시스템에 이어 이야기해보겠습니다.비상 대응 시스템의 조건지진과 같은 재난 상황에서의 패턴은 평상시 트래픽이 증가할 때와 다른 점이 있습니다.매우 빠르게 트래픽이 유입되고 피크까지 올라간다는 것입니다. 이는 매우 짧은 시간 내에 이상을 탐지하고 대응해야 한다는 것을 의미합니다. 성공적으로 대응하려면 병목이 되는 계층이나 시스템을 빠르게 확인해야 하고 서비스 간의 영향도 파악 및 의사 결정 또한 신속해야 합니다.비상 대응 시스템의 조건은 다음과 같습니다.1. 빠른 이상 탐지 및 규칙 설정평상시와 다른 트래픽 급증이 발생하면 시스템과 하단의 계층에 영향을 미치기 전에 빠르게 탐지되어야 합니다. 네이버 검색 비상 대응 시스템은 빠르게 이상을 탐지하기 위해 지표 수집 주기를 기존의 1분에서 10초로 줄인 별도의 트래픽 지표를 수집하고 평상시 피크 트래픽의 n배 이상의 트래픽이 들어오면 이상으로 판단합니다.또한 효과적인 규칙을 설정해야 합니다. 실제로 시스템이 임계 상황을 겪기 전에 빠르게 문제를 파악할 수 있어야 하지만 너무 민감한 규칙은 불필요하게 비상 모드를 발동시킬 수 있기 때문에 적정한 수준의 규칙을 설정하는 것이 중요합니다. 비상 모드에서는 서비스의 안정성을 위해 검색 품질이 희생될 수 있으므로 꼭 필요한 상황에만 비상 모드를 발동해야 합니다.2. 비상 대응 동작 자동화이상을 탐지하고 나면 그에 맞는 비상 대응 동작이 빠르게 수행되어야 합니다. 앞에서 설명했듯이 재난 상황에서는 트래픽이 매우 빠르게 증가하기 때문에 이상 탐지부터 비상 대응 동작까지 빠르게 이루어져야 합니다. 만약 수동으로 동작한다면 사람의 상황 인식 및 의사 결정에 걸리는 시간이 치명적일 수 있습니다.네이버 검색 비상 대응 시스템에서는 지진이 발생하거나 평상시 피크 대비 n배의 트래픽이 들어오면 아래의 동작이 자동으로 실행됩니다.캐시 만료 시간(cache expire time) 연장: 캐시를 연장해 하단 레이어로 전파되는 트래픽을 감소시킵니다. 비상 모드 발동을 IC(Incident commander), IO(Incident Operator) 담당자에게 전파3. 손쉽게 비상 모드 동작 실행이 가능한 UI/UX자동화 규칙만으로는 충분하지 않은 경우가 있습니다. 그런 상황에서 병목 서비스나 컴포넌트의 담당자들에게 추가 비상 대응을 요청하는 것은 1초가 아쉬운 비상 대응 상황에서는 적절하지 않습니다. 추가 비상 대응 동작은 수동으로 실행할 수 있어야 하고, IC나 IO가 손쉽고 빠르게 실행할 수 있어야 합니다. 단, 부작용이 큰 기능이기 때문에 허가된 인원만 접근을 허용해야 한다는 점을 주의해야 합니다.네이버 검색 비상 대응 시스템에서는 캐시 만료 시간 추가 연장이나 하단 레이어의 재시도 횟수 조절과 같은 동작을 수동으로 실행할 수 있습니다.4. 상위 레벨 모니터링 시스템과의 연동의사 결정자는 시스템의 건강도와 현재 상황을 정확하고 빠르게 알 수 있어야 합니다.네이버의 검색 시스템은 거대하고 복잡한 시스템입니다. 상위 레벨 모니터링 시스템이 없다면 각 서비스 담당자와 직접 소통해야 각 서비스의 건강도를 파악할 수 있을 것입니다. 수백 개의 서비스가 존재하는 네이버 검색에서 긴급한 재난 상황에 각 서비스의 담당자와 직접 소통하고 서비스 간의 영향도를 파악하는 것은 불가능합니다.네이버 검색의 상위 레벨 모니터링 시스템은 비상 대응 시스템의 기반으로서 다음과 같은 역할을 합니다.빠르게 시스템 및 서비스의 건강도를 확인하고 영향도를 파악할 수 있습니다. 이는 곧 커뮤니케이션 비용과 의사 결정까지의 시간을 줄일 수 있습니다.평상시 트래픽을 모니터링함으로써 이상 탐지를 위한 규칙의 기준을 제공합니다.평상시 가용량을 기준으로 재난 상황에 대비하여 필요한 가용량을 확보하는 근거를 제공합니다.비상 모드를 연장하거나 해제하는 판단의 근거가 될 정보를 제공합니다.마치며담당자 출근 전의 취약 시간(오전 8시 26분)에 지진이 발생했음에도 불구하고 상위 레벨 모니터링 시스템과 비상 대응 시스템을 통해, 평상시 병목 지점을 파악하고 빠르게 이상을 탐지하고 자동으로 비상 모드를 발동함으로써 네이버 검색 서비스를 안정적으로 제공할 수 있었습니다.연관 서비스를 한눈에 손쉽게 볼 수 있는 모니터링 시스템을 만들고 이를 활용해 자동화된 비상 대응 시스템을 구축하는 것은 거대한 시스템을 만들고 유지하는 것만큼이나 수많은 고민과 커뮤니케이션을 동반합니다. 이 두 시스템은 효과적으로 서비스의 안정성을 지키고 조기에 이상을 탐지하고 대응할 수 있게 하지만 바닥부터 만들어가는 데는 많은 어려움이 있을 것입니다. 이 글이 SRE 및 서비스 안정성을 지키기 위해 노력하는 분들에게 도움이 되었으면 합니다.
네이버
·
하루 전
logo
infer, never만 보면 두려워지는 당신을 위한 타입 추론 - 응용 문제
'infer, never만 보면 두려워지는 당신을 위한 타입 추론' 관련 영상은 링크에서 보실 수 있습니다.앞에서 배운 내용을 토대로 어려운 타입 문제를 하나 풀어 보겠습니다.자, 여기 예쁜 재귀 함수가 있습니다.function flattenObject(obj: any, result: any = {}): any { for (const key in obj) { if (typeof obj[key] === 'object' && obj[key] && !(obj[key] instanceof Array)) { flattenObject(obj[key], result) } else { result[key] = obj[key] } } return result}함수 구현부는 신경쓰지 마시고, 동작만 한 번 볼까요?flattenObject({ x: 0, y: 'babo', z: ['hi'], a: { b: { c: null, }, d: undefined, }}) // { x: 0, y: 'babo', z: ['hi'], c: null, d: undefined }네, 그렇습니다. 이 함수는 중첩된 객체를 평탄하게 펴는 함수입니다. 이 함수에 타입을 달아봅시다. 단, 아래의 전제 조건이 붙습니다.모든 속성의 이름은 유일성을 만족한다(=겹치지 않는다). 배열은 펼치지 않는다. null이나 undefined 값은 그대로 유지되어야 한다.평탄한 객체와 중첩된 객체 분리하기제일 먼저 생각나는 접근은 '중첩되는 속성'과 '중첩되지 않는 속성'을 쪼개서 처리한 뒤, 조립하는 것입니다.먼저, 중첩되지 않은 속성부터 추출해 봅시다. T에 undefined나 never, unknown 같은 이상한 타입을 고려하지 않기 위해 extends object로 방어합니다. 그 뒤 지우고 싶은 값인 경우에 never로 처리해 버리면 되지 않을까요?type SimpleFlattendObject = { [K in keyof T]: FilterValueType}type FilterValueType = T extends object ? T extends null | unknown[] ? T : never : T한 번 테스트해 볼까요?const test: SimpleFlattendObject<{ x: number, y: { z: [] }}> = { x: 0,}// Property 'y' is missing in type '{ x: number; }' but required in type `FlattenKeys<{ ... }>`의도한 대로 동작하지 않습니다. 객체 리터럴에서 자동 완성을 확인해 보니, y가 never로 간주됩니다. y가 test 객체에 없어야 되는 건 맞지만, 값이 never 타입이라고 해서 키까지 자동으로 지우진 않습니다. 따라서, 값이 아닌 속성 키를 원천적으로 지워야 합니다.type SimpleFlattendObject = { [K in FilterPrimitiveKeys]: T[K]}type FilterPrimitiveKeys = K extends keyof T // 모든 배열을 허용하기 위해 가장 큰 타입인 unknown 할당 ? T[K] extends unknown[] // 조건을 만족했을 때 키를 반환. 일종의 early return ? K : T[K] extends object ? never : K : never테스트를 해 볼까요?type X = SimpleFlattendObject<{ x: number, y: { z: string, }, a: null, b: [1],}>/*{ x: number, a: null, b: [1]}*/이제 잘 되네요! 이제 중첩된 객체만 잘 처리하면 되겠군요. 중첩되지 않은 값을 처리한 코드를 완전히 반전시키면 되겠네요.type NestedObject = { [K in FilterNestedKeys]: T[K]}type FilterNestedKeys = K extends keyof T ? T[K] extends unknown[] ? never // 아까랑 정반대! : T[K] extends object ? K : never : never테스트해 볼까요?// 예시type X = NestedObject<{ x: number y: { z: number } a: { b: { c: [] }} d: undefined}>/*{ y: { z: number } a: { b: { c: [] }}}*/좋습니다.중첩된 객체 한 단계 들어올리기이제 y 안에 들어있는 z, a 안에 들어있는 b를 한 단계 올리고 싶습니다. 일단 재귀적인 건 생각하지 말고, 한 단계만 올려봅시다.우선 저 안에 있는 값의 '키'는 더 이상 중요하지 않습니다. 그러니 값의 타입만 모조리 추출합시다.type Values = T[keyof T]type X = Values<{ a: string, b: number }> // string | number 아까 만들었던 NestedObject를 여기에 넣어봅시다.type UnwrappedObject = Values> 테스트해 봅시다.type X = UnwrappedObject<{ x: number y: { z: number } a: { b: { c: [] }} d: undefined}>/*{ z: number} | { b: { c: [] }}*/의도한 것과 비슷해졌습니다. |를 &로 바꾸기만 하면 원하는 타입이 될 것 같네요. 그런데 |를 어떻게 &로 바꾸죠?합집합을 교집합으로Stack Overflow에 있던 한 재야의 고수는 아래와 같은 핵을 제시했습니다(출처: Transform union type to intersection type).type ToIntersection = ( T extends any ? (_: T) => void : never ) extends (_: infer S) => void ? S : never생긴 게 상당히 당황스럽지만, 차근차근 하나씩 분석해 봅시다. 먼저 첫 번째 괄호를 임시로 F라고 합시다.F = T extends any ? (_: T) => void : never 이 타입 표현식은 T를 (_: T) => void라는 함수 타입으로 바꿉니다
네이버
·
하루 전
기술 블로그 더 보기
Copyright © 2024. Codenary All Rights Reserved.