﻿<?xml version="1.0" encoding="UTF-8"?>
    <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Xheldon Blog</title>
        <description>The Answer to Life, the Universe and Everything is...</description>
        <link>https://www.xheldon.com/ko/</link>
        <atom:link href="https://www.xheldon.com/ko/feed.xml" rel="self" type="application/rss+xml" />
        <pubDate>Wed, 03 Jun 2026 01:26:21 +0000</pubDate>
        <lastBuildDate>Wed, 03 Jun 2026 01:26:21 +0000</lastBuildDate>
        <generator>Hexo v7.3.0</generator>
        
        <item>
            <title>듀얼 부팅 시스템에서 동일한 단축키를 사용하는 방법</title>
            <description>&lt;h2 id=&#34;서문&#34;&gt;서문&lt;/h2&gt;
&lt;p&gt;제가 마지막으로 Windows를 사용했던 경험은 10년 전 대학 시절로 거슬러 올라갑니다. 당시 사용했던 레노버 Y470-P는 설계 결함이 있어서, 제가 한 번 뚜껑을 닫았을 때 부품 하나가 타버리는 사고가 발생했습니다. 결국 메인보드를 교체해야 했죠.&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/input-more-efficiently/249800e8-7a61-809c-9b03-ca17b71cfe98.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;p&gt;졸업 후 바로 2015년형 15인치 Macbook Pro를 구입했습니다. 그 후 몇 년 동안 컴퓨터를 바꾸었지만 계속 애플 생태계에 머물렀기 때문에, Windows의 사용 경험은 10년 전 그대로였습니다: 생태계의 분열, 사용하기 어려운 소프트웨어, 추악한 디자인(이 부분은 여전히 변함없습니다), 터무니없는 인터랙션.&lt;/p&gt;
&lt;p&gt;618 행사에서 중고급 사양의 PC를 조립했습니다. 14700KF, 5070Ti, 크루셜 64GB 6400MHz 메모리와 크루셜 2TB SSD로, 손에서 놓을 수 없을 정도로 만족스럽습니다: 고사양 Windows가 이렇게 부드러울 줄이야. 시스템 디자인 언어는 여전히 터무니없지만, 소프트웨어 실행 속도나 훌륭한 소프트웨어의 디자인 언어(예: Windows용 Apple Music)는 매우 훌륭했습니다. 물론 가장 중요한 것은, 제가 Windows를 구입한 이유는 게임(80% 이유)과 플랫폼 특화 코드인 C# 작성(20% 이유) 때문이었습니다.&lt;/p&gt;
&lt;h2 id=&#34;효율적인-입력의-본질&#34;&gt;효율적인 입력의 본질&lt;/h2&gt;
&lt;p&gt;저는 오랜 시간(9년) 동안 Mac에서 HHKB를 사용해왔기 때문에, Mac의 HHKB 키 매핑을 Windows에 적용하려고 합니다. 이렇게 하는 이유는 습관적인 측면뿐만 아니라 다음과 같은 고려 사항이 있습니다:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;커서를 행의 시작이나 끝으로 이동하거나 오른쪽 삭제와 같은 텍스트 편집에 자주 사용되는 기능은 Windows에서 전용 키를 눌러야 합니다. 예를 들어 행 시작은 Home, 행 끝은 End, 오른쪽 삭제는 Del 등입니다. 수정 키 + 키 조합으로 이 작업을 수행할 수 없지만, Mac에서는 시스템 수준에서 Emacs 단축키를 지원하기 때문에 행 시작은 Control + A, 행 끝은 Control + E, 오른쪽 삭제는 Control + D로 쉽게 구현할 수 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;커서를 상하좌우로 이동하는 것도 마찬가지입니다. Windows에서는 전용 방향키로 커서를 이동하지만, Mac에서는 Control + B, F, N, P를 사용할 수 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;아마도 궁금증이 생길 수 있습니다. 왜 저는 Windows에서 전용 키를 사용하는 대신 Mac의 수정 키 + 단축키 조합을 선호할까요?&lt;/p&gt;
&lt;p&gt;이 질문에 대한 답은 제가 HHKB의 60% 배열을 특히 좋아하는 이유이기도 합니다: &lt;strong&gt;제 양손 손바닥이 크게 움직이지 않고도 모든 작업을 완료할 수 있어 입력 효율을 보장합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 우리 프로그래머 세계에서는 마우스를 사용할 수 있을 때 절대로 키보드를 사용하지 않으면 처벌을 받습니다! &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;예를 들어, Windows에서 빠르게 내용을 입력하고 있는데 이전에 작성한 부분에 오타가 있어 앞의 몇 글자를 삭제해야 한다고 가정해보겠습니다. 이 경우 다음 단계가 필요합니다:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;오른손을 알파벳 영역에서 떼고 검지로 오른쪽 하단의 방향키 위치를 더듬어 찾습니다(물론 87키 이상 키보드는 더듬을 필요 없이 방향키 위치를 쉽게 찾을 수 있습니다).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;방향키를 눌러 커서를 목표 위치로 이동합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;오른손을 방향키 위치에서 떼고 Backspace 위치로 이동합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;삭제 키를 누릅니다(Backspace 위치).&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Mac의 HHKB 배열 키보드에서는 동일한 작업을 왼손 하나로만 수행할 수 있습니다:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;왼손 새끼손가락으로 Control + B를 눌러 커서를 목표 위치로 이동합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;오른손 새끼손가락으로 Backspace를 누르거나 왼손 새끼손가락으로 Control + H/D(왼쪽/오른쪽 삭제)를 누릅니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이렇게 간단합니다. &lt;strong&gt;양손을 알파벳 영역에서 떼지 않아도 됩니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;심지어 수정이 완료된 후, Windows에서는 오른손을 다시 알파벳 영역에서 떼고 End 키를 눌러 최신 커서 위치로 돌아가야 하지만, Mac에서는 왼손으로 Control + E만 누르면 됩니다.&lt;/p&gt;
&lt;h2 id=&#34;힘든-백틱-입력&#34;&gt;힘든 백틱(`) 입력&lt;/h2&gt;
&lt;p&gt;제가 HHKB를 좋아하는 또 다른 이유는, 저는 프론트엔드 개발자라서 백틱(물결표시 키)을 비교적 자주 사용해야 하기 때문입니다. 일반적인 60% 키보드에서는 이 키가 ESC 위에 배치되어 있고, 단축키 조합으로 눌러야 합니다. 하지만 HHKB는 이 키를 독립적으로 오른쪽 상단에 배치하고, 삭제 키를 아래로 내리고, &lt;code&gt;｜&lt;/code&gt; 키를 위로 올려 구현했습니다(즉, 일반적인 Backspace가 두 개의 1u 키로 분할되었습니다).&lt;/p&gt;
&lt;p caption=&#39;普通60配列&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/input-more-efficiently/248800e8-7a61-8098-b185-c92a8eaf2c94.webp&#39; alt=&#39;普通60配列&#39; title=&#39;普通60配列&#39;&gt;&lt;/p&gt;
&lt;p caption=&#39;HHKB 配列&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/input-more-efficiently/248800e8-7a61-804c-bc1a-e7a740ecc5e9.webp&#39; alt=&#39;HHKB 配列&#39; title=&#39;HHKB 配列&#39;&gt;&lt;/p&gt;
&lt;h2 id=&#34;힘든-control-입력&#34;&gt;힘든 Control 입력&lt;/h2&gt;
&lt;p&gt;일반적인 배열의 키보드에서 입력 효율에 영향을 미치는 또 다른 설계는 Control 키가 왼쪽 하단에 위치한다는 점입니다. 따라서 Control + 키를 누를 때마다 손목이 왼쪽으로 구부러지고 손바닥이 오른쪽으로 이동합니다. 손바닥을 움직이게 하는 어떤 입력 방식도 우아하지 않으며, 결국 효율성에 영향을 미칩니다.&lt;/p&gt;
&lt;p caption=&#39;普通键盘位置尴尬的 Ctrl&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/input-more-efficiently/248800e8-7a61-809a-af2b-fab48765e5cb.webp&#39; alt=&#39;普通键盘位置尴尬的 Ctrl&#39; title=&#39;普通键盘位置尴尬的 Ctrl&#39;&gt;&lt;/p&gt;
&lt;p&gt;하지만 인터넷에서 어떤 사람은 새끼손가락 아래쪽 손바닥 부분으로 왼쪽 하단의 Ctrl 키를 누르려는 시도를 하기도 하는데, 터무니없습니다:&lt;/p&gt;
&lt;p caption=&#39;左侧手掌按 Ctrl 法&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/input-more-efficiently/248800e8-7a61-80c4-8ba9-ffdccbbec543.webp&#39; alt=&#39;左侧手掌按 Ctrl 法&#39; title=&#39;左侧手掌按 Ctrl 法&#39;&gt;&lt;/p&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 저는 심지어 극단적으로 생각하기도 합니다. 입을 벌리고 닫아서 발음하는 어떤 언어도 우아하지 않다고요. 우아한 언어의 발음은 입을 크게 벌리거나 닫을 필요 없이 혀와 목의 조합만으로도 명확하게 발음할 수 있어야 합니다. 그래서 저는 일본어가 가장 우아한 언어라고 생각합니다—이는 일본어의 표현력, 특히 강한 감정을 표현하는 욕설 측면에서 약점이 되기도 하지만요. &lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;불필요한-capslock-키&#34;&gt;불필요한 CapsLock 키&lt;/h2&gt;
&lt;p&gt;대문자 입력을 자주 그리고 오랫동안 하는 사람은 거의 없을 거라고 생각합니다. 따라서 일반적인 키보드 배열에서 CapsLock 키를 왼손 새끼손가락이라는 중요한 위치에 배치한 것은 정말 말도 안 되는 일입니다.&lt;/p&gt;
&lt;p caption=&#39;用处不多的 CapLock 占据了一个黄金位置&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/input-more-efficiently/248800e8-7a61-8088-9b22-efdde222997e.webp&#39; alt=&#39;用处不多的 CapLock 占据了一个黄金位置&#39; title=&#39;用处不多的 CapLock 占据了一个黄金位置&#39;&gt;&lt;/p&gt;
&lt;p&gt;종합해 보면, 저는 HHKB의 키 배열이 가장 완벽하다고 생각합니다. 단순히 대칭적인 아름다움뿐만 아니라 입력 효율성도 뛰어나기 때문입니다. 또한 Mac에서의 단축키, 적어도 텍스트 작업 효율성 측면에서는 Windows보다 우수합니다.&lt;/p&gt;
&lt;p&gt;따라서 이제 &lt;strong&gt;Mac의 키 입력을 Windows에서 재현하는 방법&lt;/strong&gt;을 공유하겠습니다. 이를 통해 두 운영체제에서 동일한 근육 기억으로 효율적으로 편집할 수 있습니다.&lt;/p&gt;
&lt;p caption=&#39;HHKB 配列 Control 在 CapLock 位置&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/input-more-efficiently/249800e8-7a61-80e6-8b9e-cda2586acf71.webp&#39; alt=&#39;HHKB 配列 Control 在 CapLock 位置&#39; title=&#39;HHKB 配列 Control 在 CapLock 位置&#39;&gt;&lt;/p&gt;
&lt;p&gt;HHKB의 가장 큰 단점으로 지적되는 방향키에 대해 말하자면, 기본적으로 방향키로 커서를 이동할 수 있는 모든 곳에서 Emacs 단축키를 사용할 수 있습니다. 하지만… 일부 터미널, 예를 들어 Python에서는 Control-D가 CLI를 종료하는 명령이고, Remote SSH로 연결한 Linux 호스트에서는 약간 불편할 수 있습니다. 하지만 Warp 같은 좋은 터미널을 사용하면 이러한 문제를 어느 정도 피할 수 있습니다. Warp는 텍스트 편집기처럼 명령을 입력할 수 있어 Emacs 단축키로 명령을 입력할 수 있습니다. 설명만으로는 이해하기 어려울 수 있지만, 사용해 보면 일반 터미널과의 차이를 바로 알 수 있습니다.&lt;/p&gt;
&lt;h2 id=&#34;키보드-요구-사항&#34;&gt;키보드 요구 사항&lt;/h2&gt;
&lt;p&gt;FN 키와 다른 키의 조합을 지원하는 다중 레이어 키 매핑이 가능한 키보드가 필요합니다. 매크로 지원은 필수는 아닙니다. 일부 키보드는 최대 6개의 레이어를 지원하기도 합니다(듀얼 시스템 전환 시 각 시스템별 3개 레이어). 저는 새로 구입한 &lt;strong&gt;Nuphy Air75 V3&lt;/strong&gt; 키보드를 사용하고 있습니다. HHKB 대신 이 키보드를 선택한 이유는 Windows에서 게임을 할 때 F 키 영역(F1~F12)이 필수적이기 때문입니다. 따라서 75키 배열이 제가 받아들일 수 있는 한계입니다.&lt;/p&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 누가 포탑을 놓치면 물음표를 보낸다 —— Faker&lt;/p&gt;&lt;/blockquote&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/input-more-efficiently/249800e8-7a61-8072-a06b-f6c9ead05059.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;h2 id=&#34;fn-키-매핑으로-ecmacs-텍스트-편집-구현&#34;&gt;FN 키 매핑으로 Ecmacs 텍스트 편집 구현&lt;/h2&gt;
&lt;p&gt;다음과 같은 텍스트 편집 기능을 구현해야 합니다(아래 설명 형식은 Windows 키 -&amp;gt; Mac 단축키):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;커서를 행의 처음으로 이동: Home -&amp;gt; Control + A&lt;/li&gt;
&lt;li&gt;커서를 행의 끝으로 이동: End -&amp;gt; Control + E&lt;/li&gt;
&lt;li&gt;커서를 아래로 이동: ↓ -&amp;gt; Control + N(Next)&lt;/li&gt;
&lt;li&gt;커서를 위로 이동: ↑ -&amp;gt; Control + P(Previous)&lt;/li&gt;
&lt;li&gt;커서를 왼쪽으로 이동: ← -&amp;gt; Control + B(Back)&lt;/li&gt;
&lt;li&gt;커서를 오른쪽으로 이동: → -&amp;gt; Control + F(Front)&lt;/li&gt;
&lt;li&gt;오른쪽 삭제: Del -&amp;gt; Control + D(Delete)&lt;/li&gt;
&lt;li&gt;왼쪽 삭제: Backspace -&amp;gt; Control + H(H는 삭제를 의미하며 역사적인 이유로 해당 단어가 없습니다. 관심이 있다면 직접 검색해 보세요.)&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 저는 Mac에서 복사/붙여넣기에 Control+C/V 대신 Cmd+C/V를 사용하는 디자인을 더 선호합니다(하지만 매핑하지는 않았습니다). 아마도 Ecmacs에서 복사/붙여넣기를 지원하지 않는 역사적인 이유와 텍스트 편집에 특화된 단축키가 아니기 때문에 Mac에서는 복사/붙여넣기 단축키를 CMD로 설정한 것 같습니다.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;따라서 Windows에서 Mac의 HHKB와 동일한 키 입력을 구현하려면 &lt;strong&gt;Caplock을 FN 레이어로 변경&lt;/strong&gt;해야 합니다(저는 FN6으로 변경했습니다). 그런 다음 각 키를 하나씩 매핑하면 됩니다:&lt;/p&gt;
&lt;p caption=&#39;修改 CapLock 为 FN6 层&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/input-more-efficiently/249800e8-7a61-80d2-bbf1-e143863958db.webp&#39; alt=&#39;修改 CapLock 为 FN6 层&#39; title=&#39;修改 CapLock 为 FN6 层&#39;&gt;&lt;/p&gt;
&lt;p&gt;그리고 FN6 레이어에서 각 키를 Windows의 해당 기능 키로 매핑합니다:&lt;/p&gt;
&lt;p caption=&#39;FN6 层映射&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/input-more-efficiently/249800e8-7a61-809e-897f-e5af7ec726c4.webp&#39; alt=&#39;FN6 层映射&#39; title=&#39;FN6 层映射&#39;&gt;&lt;/p&gt;
&lt;h2 id=&#34;알파벳-영역-키-수정&#34;&gt;알파벳 영역 키 수정&lt;/h2&gt;
&lt;p&gt;HHKB에서는 엔터 키 위에 삭제 키 &lt;code&gt;Backspace&lt;/code&gt;가 있고, &lt;code&gt;｜&lt;/code&gt; 키가 아닙니다. 따라서 처음에는 Backspace와 ｜ 키를 서로 바꿨지만, 어떤 이유에서인지 제 손이 75키 배열에 적응해 버려 삭제 키를 항상 위쪽의 ｜ 키로 누르게 되었습니다. HHKB로 전환하면 아래 줄을 강제로 눌러야 해서 불편했습니다. 그래서 둘 다 BackSpace로 변경하고, 거의 사용하지 않는 ｜ 키를 F 키 영역의 F13 위치로 옮겼습니다.&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/input-more-efficiently/249800e8-7a61-806c-acc9-fde5c028f922.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;p&gt;다음으로 Control과 Alt의 위치를 바꿨습니다. 이렇게 하면 Mac에서 CMD + C/V/T/W 등의 작업과 Windows에서 동일한 기능의 Control + C/V/T/W 등의 손가락 동작을 일치시킬 수 있습니다.&lt;/p&gt;
&lt;p&gt;마지막으로 Win 키와 Alt를 서로 바꿔서(즉, Win 키를 키보드 왼쪽 하단에 배치) Windows와 Mac에서 Alt(Opt) 키의 위치를 동일하게 만들었습니다.&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/input-more-efficiently/249800e8-7a61-80ae-98c6-ceb3f49fd02f.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;p&gt;이제 스페이스 바 왼쪽의 키(Windows에서는 Ctrl, Mac에서는 CMD)를 누른 후 다른 키를 누르면 효과가 완전히 동일합니다. 예를 들어 Ctrl/Cmd + C로 복사, Ctrl/Cmd + V로 붙여넣기, Ctrl/Cmd + T로 새 탭 열기, Ctrl/Cmd + Shift + T로 닫힌 탭 복구 등이 가능합니다.&lt;/p&gt;
&lt;h2 id=&#34;기타-키-수정&#34;&gt;기타 키 수정&lt;/h2&gt;
&lt;p&gt;이미 Home과 End 단축키가 있기 때문에 키보드 가장 오른쪽에 있는 Home과 End는 불필요합니다. 그래서 저는 두 가지 조작을 했습니다. Home을 자주 사용하는 단축키로 변경했는데, 저는 Win + V로 설정했습니다. 이 단축키는 Windows에서는 클립보드 기록을 열고, Mac에서는 Cmd + Opt + C로 매핑하여 Alfred의 클립보드 기록 창을 열도록 했습니다.&lt;/p&gt;
&lt;p&gt;Home 키는 M1 매크로로 매핑하여 복사 기록을 열고, End 키는 WinLock으로 변경하여 손이 기억하는 대로 왼쪽 아래의 Win 키를 누르는 것을 방지했습니다. 이제는 게임을 할 때도 Win 키를 잠그고, 습관이 들면 다시 열 계획입니다.&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/input-more-efficiently/249800e8-7a61-8011-957a-e63f205dd9e1.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;p caption=&#39;Win + V 打开复制历史&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/input-more-efficiently/249800e8-7a61-80e7-9c7b-fa95908c6985.webp&#39; alt=&#39;Win + V 打开复制历史&#39; title=&#39;Win + V 打开复制历史&#39;&gt;&lt;/p&gt;
&lt;p caption=&#39;Cmd + Opt + C 打开复制历史（Alfred）&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/input-more-efficiently/249800e8-7a61-80a0-a366-ecc5f5024d0a.webp&#39; alt=&#39;Cmd + Opt + C 打开复制历史（Alfred）&#39; title=&#39;Cmd + Opt + C 打开复制历史（Alfred）&#39;&gt;&lt;/p&gt;
&lt;h2 id=&#34;아쉬운-점&#34;&gt;아쉬운 점&lt;/h2&gt;
&lt;p&gt;Windows에서는 Alt + Tab을 매핑할 수 없습니다. 이 키의 주요 기능은 다른 애플리케이션 창을 전환하는 것인데, 위와 같이 설정한 후에는 스페이스 바 왼쪽 두 번째 키 + Tab 키로 동작합니다.&lt;/p&gt;
&lt;p&gt;하지만 동일한 기능, 즉 Mac에서 다른 애플리케이션을 전환할 때는 Cmd + Tab을 사용합니다.&lt;/p&gt;
&lt;p&gt;따라서 제 설정은 애플리케이션 전환 키만 두 시스템에서 시스템 수준으로 매핑할 수 없을 뿐, 다른 키들은 모두 동일한 근육 기억으로 누를 수 있어 Windows 단축키 위치와 Mac 단축키 위치의 차이를 구분할 필요가 없습니다.&lt;/p&gt;
&lt;h2 id=&#34;후기&#34;&gt;후기&lt;/h2&gt;
&lt;p&gt;자신에게 맞는 것이 최고입니다. 어떤 키 방식이 더 나은지 논쟁할 필요도 없습니다. 습관의 힘은 강력하며, 일단 익숙해지면 효율성이 가장 높아집니다.&lt;/p&gt;
&lt;p&gt;모두 Happy Hacking 하시길 바랍니다!&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/input-more-efficiently/249800e8-7a61-8028-96b3-f2d7bb973946.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;</description>
            <pubDate>Thu, 07 Aug 2025 00:00:00 +0000</pubDate>
            <link>https://www.xheldon.com/ko/life/input-more-efficiently.html</link>
            <guid isPermaLink="true">https://www.xheldon.com/ko/life/input-more-efficiently.html</guid>
            
                <category>生活</category>
            
                <category>Apple</category>
            
                <category>经验</category>
            
                <category>总结</category>
            
                <category>VSCode</category>
            
                <category>工具</category>
            
                <category>踩坑</category>
            
                <category>细节</category>
            
                <category>Windows</category>
            
            
                <category>life</category>
            
        </item>
        
        <item>
            <title>앱 개발 여정 (1): 마하불사의 Swift</title>
            <description>&lt;h2 id=&#34;설명&#34;&gt;설명&lt;/h2&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 누군가 왜 JavaScript가 아닌지 물었는데, 그것은 스크립트 언어이며 타입 시스템 등이 없어 Swift와 전혀 비교할 수 없기 때문이다.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;저는 프론트엔드 개발자로, C, Java, Python 언어는 입문 수준만 이해하고 있으며, TypeScript(이하 TS)의 기능과 사용에는 고급 수준(자평)에 도달했습니다. 따라서 저에게 충격적인 점이 다른 분들께는 &amp;quot;그냥 평범한 언어 기능일 뿐&amp;quot;이거나 &amp;quot;TypeScript를 설계한 사람이 천재인가?&amp;quot;라고 느껴질 수도 있습니다.&lt;/p&gt;
&lt;p&gt;저는 Swift와 TS의 모든 차이점에 무조건 충격을 받지는 않을 것입니다. 어떤 차이는 TS의 자체적인 부족함 때문이며, 또 어떤 설계는 컴퓨터 언어에서 흔히 볼 수 있는 것(예: 숫자를 Int와 Double로 구분하고 Number 타입만 있는 것이 아닌)이기 때문입니다. 다만 JS의 설계가 충격적일 뿐(결국 일주일 만에 급하게 설계된 것이니까)이지 Swift가 아닙니다.&lt;/p&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 제 생각에: 만약 어떤 언어의 규칙이 너무 많고, 예외가 너무 많고, 예약어가 너무 많다면 그것은 좋은 언어가 아닙니다.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;서문&#34;&gt;서문&lt;/h2&gt;
&lt;p&gt;이 글은 Swift 공식 언어 소개 순서에 따라 체계적으로 충격을 받은 내용을 담고 있으며, 충격을 받지 않았거나 이해하지 못한 내용(예: &lt;code&gt;추가 매크로&lt;/code&gt;)은 생략했습니다.&lt;/p&gt;
&lt;h2 id=&#34;기본-지식&#34;&gt;기본 지식&lt;/h2&gt;
&lt;h3 id=&#34;주석&#34;&gt;주석&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 충격 포인트: XCode에는 `/** */` 블록 주석 단축키가 없고, `cmd + /` 라인 주석만 있습니다.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;VSCode의 블록 주석 단축키도 누르기 어려운 &lt;code&gt;Alt + Shift + A&lt;/code&gt;인데, 이 기능을 자주 사용하지 않는 걸까요?&lt;/p&gt;
&lt;p&gt;물론 VSCode와 XCode 모두 &lt;code&gt;/**&lt;/code&gt;을 입력하고 엔터를 누르면 자동으로 블록 주석이 생성됩니다.&lt;/p&gt;
&lt;h3 id=&#34;옵셔널-바인딩&#34;&gt;옵셔널 바인딩&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 충격 포인트: 디버깅할 때 `if`에 항상 `true`인 값을 테스트용으로 쓰려고 해도 안 됩니다. `if` 문의 옵셔널 바인딩은 반드시 옵셔널 타입의 값이어야 합니다.&lt;/p&gt;&lt;/blockquote&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; a &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt; &amp;#123; &amp;#125; &lt;span class=&#34;hljs-comment&#34;&gt;// 错误！&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;이렇게까지 해야 할까?&lt;/p&gt;
&lt;h2 id=&#34;컬렉션-타입&#34;&gt;컬렉션 타입&lt;/h2&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 놀라운 점: 배열 메서드 중 `sort`는 원본 배열을 정렬하고, `sorted`는 새로운 배열을 반환한다는 점입니다. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;하나의 언어가 프레임워크의 역할까지 수행한다니, 훌륭하고 애플의 스타일에 잘 맞습니다. 이와 같은 예는 많지만, 일일이 놀라기에는 한계가 있네요.&lt;/p&gt;
&lt;h2 id=&#34;제어-흐름&#34;&gt;제어 흐름&lt;/h2&gt;
&lt;h3 id=&#34;if-표현식&#34;&gt;if 표현식&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 놀라운 점: `if-else`로 동일한 변수에 값을 할당하는 경우를 해결하기 위해 `if` 표현식 문법을 제공합니다. 마찬가지로 `switch`도 비슷한 표현식 형태를 가지고 있습니다. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Swift는 정말 많은 것을 하고 있네요:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 平平无奇的写法&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; a &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; str: &lt;span class=&#34;hljs-type&#34;&gt;String&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; a &lt;span class=&#34;hljs-operator&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt; &amp;#123;&lt;br&gt;    str &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;小&amp;quot;&lt;/span&gt;&lt;br&gt;&amp;#125; &lt;span class=&#34;hljs-keyword&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; a &lt;span class=&#34;hljs-operator&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;30&lt;/span&gt; &amp;#123;&lt;br&gt;    str &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;大&amp;quot;&lt;/span&gt;&lt;br&gt;&amp;#125; &lt;span class=&#34;hljs-keyword&#34;&gt;else&lt;/span&gt; &amp;#123;&lt;br&gt;    str &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;中&amp;quot;&lt;/span&gt;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 简写法&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; str &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; a &lt;span class=&#34;hljs-operator&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt; &amp;#123;&lt;br&gt;  &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;小&amp;quot;&lt;/span&gt;&lt;br&gt;&amp;#125; &lt;span class=&#34;hljs-keyword&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; a &lt;span class=&#34;hljs-operator&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;30&lt;/span&gt; &amp;#123;&lt;br&gt;  &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;大&amp;quot;&lt;/span&gt;&lt;br&gt;&amp;#125; &lt;span class=&#34;hljs-keyword&#34;&gt;else&lt;/span&gt; &amp;#123;&lt;br&gt;  &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;中&amp;quot;&lt;/span&gt;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h3 id=&#34;switch의-강력한-판단력&#34;&gt;Switch의 강력한 판단력&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 놀라운 점: switch에는 암묵적인 fall-through가 없다&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;하지만 이는 이해할 수 있고 더 합리적이다. 정상적인 사람 중에 case 1에 추가로 &lt;code&gt;break&lt;/code&gt;를 넣지 않으면 자동으로 case 2로 넘어가길 바라는 사람이 있을까?&lt;/p&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 놀라운 점: switch가 객체 값을 판단할 수 있다!&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;이는 사실 &lt;code&gt;feature&lt;/code&gt;라고 할 수 있으며, 직관적으로 더 합리적이다. 정말 전문가다운 방식이다.&lt;/p&gt;
&lt;p&gt;TS에서 &lt;code&gt;switch&lt;/code&gt;의 &lt;code&gt;case&lt;/code&gt; 문은 완전 항등(===) 비교를 사용하기 때문에, 일반적으로 &lt;code&gt;switch&lt;/code&gt; 괄호 안에 객체를 전달하지 않는다. 객체는 참조를 비교하는데, TS에서는 객체 동등성을 판단해야 하는 경우가 드물며, &lt;code&gt;switch&lt;/code&gt; 문으로 이를 판단하는 경우는 더욱 드물다.&lt;/p&gt;
&lt;p&gt;하지만 Swift에서는 &lt;code&gt;case&lt;/code&gt; 문에서 판단하는 값을 &amp;quot;캡처&amp;quot;할 수 있다(공식 용어로 &amp;quot;패턴&amp;quot;이라고 함):&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; somePoint &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; (&lt;span class=&#34;hljs-number&#34;&gt;1&lt;/span&gt;, &lt;span class=&#34;hljs-number&#34;&gt;1&lt;/span&gt;) &lt;span class=&#34;hljs-comment&#34;&gt;// 一个平平无奇的元组罢了&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 我现在，开 始 判 断：&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;switch&lt;/span&gt; somePoint &amp;#123;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;case&lt;/span&gt; (&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;, &lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;):&lt;br&gt;    &lt;span class=&#34;hljs-built_in&#34;&gt;print&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&lt;span class=&#34;hljs-subst&#34;&gt;\(somePoint)&lt;/span&gt; 在原点&amp;quot;&lt;/span&gt;)&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;case&lt;/span&gt; (&lt;span class=&#34;hljs-keyword&#34;&gt;_&lt;/span&gt;, &lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;): &lt;span class=&#34;hljs-comment&#34;&gt;// 忽略第一个值&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-built_in&#34;&gt;print&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&lt;span class=&#34;hljs-subst&#34;&gt;\(somePoint)&lt;/span&gt; 在 x 轴&amp;quot;&lt;/span&gt;)&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;case&lt;/span&gt; (&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;, &lt;span class=&#34;hljs-keyword&#34;&gt;_&lt;/span&gt;): &lt;span class=&#34;hljs-comment&#34;&gt;// 忽略第二个值&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-built_in&#34;&gt;print&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&lt;span class=&#34;hljs-subst&#34;&gt;\(somePoint)&lt;/span&gt; 在 y 轴&amp;quot;&lt;/span&gt;)&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;case&lt;/span&gt; (&lt;span class=&#34;hljs-operator&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;hljs-operator&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;, &lt;span class=&#34;hljs-operator&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;hljs-operator&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;): &lt;span class=&#34;hljs-comment&#34;&gt;// 判断元组的两个值是否分别在给定区间，在就匹配成功&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-built_in&#34;&gt;print&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&lt;span class=&#34;hljs-subst&#34;&gt;\(somePoint)&lt;/span&gt; 在盒子内部&amp;quot;&lt;/span&gt;)&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;default&lt;/span&gt;:&lt;br&gt;    &lt;span class=&#34;hljs-built_in&#34;&gt;print&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&lt;span class=&#34;hljs-subst&#34;&gt;\(somePoint)&lt;/span&gt; 在盒子外部&amp;quot;&lt;/span&gt;)&lt;br&gt;&amp;#125;&lt;br&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 还可以值绑定！&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;switch&lt;/span&gt; somePoint &amp;#123;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;case&lt;/span&gt; (&lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; x, &lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;): &lt;span class=&#34;hljs-comment&#34;&gt;// 匹配第二个值，捕获第一个值&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-built_in&#34;&gt;print&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;x轴值：&lt;span class=&#34;hljs-subst&#34;&gt;\(x)&lt;/span&gt;&amp;quot;&lt;/span&gt;)&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;case&lt;/span&gt; (&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;, &lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; y): &lt;span class=&#34;hljs-comment&#34;&gt;// 匹配第一个值，捕获第二个值&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-built_in&#34;&gt;print&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;y 轴值：&lt;span class=&#34;hljs-subst&#34;&gt;\(y)&lt;/span&gt;&amp;quot;&lt;/span&gt;)&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;case&lt;/span&gt; (&lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; x, &lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; y): &lt;span class=&#34;hljs-comment&#34;&gt;// 兜底，因为 x 和 y 都是 let，还可以写作 case let (x, y)&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-built_in&#34;&gt;print&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&lt;span class=&#34;hljs-subst&#34;&gt;\(x)&lt;/span&gt; 和 &lt;span class=&#34;hljs-subst&#34;&gt;\(y)&lt;/span&gt;&amp;quot;&lt;/span&gt;)&lt;br&gt;&amp;#125;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 还可以加 where 限定：&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; (x, y) &lt;span class=&#34;hljs-keyword&#34;&gt;where&lt;/span&gt; x &lt;span class=&#34;hljs-operator&#34;&gt;==&lt;/span&gt; y:&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;//还可以多个匹配&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;a&amp;quot;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;b&amp;quot;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;c&amp;quot;&lt;/span&gt;:&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;주의할 점은 TS에서도 &#39;다중 매칭’을 지원하지만 Swift의 case 다중 매칭과는 완전히 다르다는 것입니다.&lt;/p&gt;
&lt;p&gt;Swift의 다중 매칭에서는 쉼표로 구분된 내용 중 하나라도 매칭되면 case 문이 실행됩니다. 그러나 TS의 쉼표로 구분된 매칭은 본질적으로 마지막 항목만 매칭하는데, 이는 TS에서 쉼표로 구분된 문장이 마지막 값만 반환하기 때문입니다:&lt;/p&gt;
&lt;figure class=&#34;highlight typescript&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs typescript&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; a = &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;d&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;switch&lt;/span&gt; (a) &amp;#123;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;d&amp;quot;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;c&amp;quot;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;b&amp;quot;&lt;/span&gt;:&lt;br&gt;  &lt;span class=&#34;hljs-variable language_&#34;&gt;console&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;log&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;OK&amp;quot;&lt;/span&gt;)&lt;span class=&#34;hljs-comment&#34;&gt;// 此处不会执行，因为此 case 匹配 &amp;quot;b&amp;quot; &lt;/span&gt;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h2 id=&#34;함수&#34;&gt;함수&lt;/h2&gt;
&lt;h3 id=&#34;함수의-라벨-매개변수&#34;&gt;함수의 라벨 매개변수&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 놀라운 점: 형식 매개변수(라벨 매개변수)가 동일한 이름을 가질 수 있다??? 그리고 규정(제약이 또 등장하네요, 정말): 가변 매개변수 뒤의 매개변수는 반드시 인자 라벨이 있어야 합니다(만약 하나뿐이라면 실제 인자가 형식 매개변수가 됩니다) 생략할 수 없습니다. &lt;/p&gt;&lt;/blockquote&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 下面两个 a 标签参数，完全合法（因为是 label 无所谓）&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 而且 a b 后面的参数 a c（或者单独的一个参数）必须存在，不能用 _ 省略，&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;//  也可以理解，要不咋知道是第二个参数，而不是前面的可变参数来的？&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;a&lt;/span&gt; (&lt;span class=&#34;hljs-params&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;hljs-params&#34;&gt;b&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;Double&lt;/span&gt;..., &lt;span class=&#34;hljs-params&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;hljs-params&#34;&gt;c&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;Double&lt;/span&gt;) -&amp;gt; &lt;span class=&#34;hljs-type&#34;&gt;Double&lt;/span&gt; &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; b.reduce(&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;, &lt;span class=&#34;hljs-operator&#34;&gt;+&lt;/span&gt;) &lt;span class=&#34;hljs-operator&#34;&gt;+&lt;/span&gt; c&lt;br&gt;&amp;#125;&lt;br&gt;a(a: &lt;span class=&#34;hljs-number&#34;&gt;1&lt;/span&gt;, &lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;, &lt;span class=&#34;hljs-number&#34;&gt;3&lt;/span&gt;, &lt;span class=&#34;hljs-number&#34;&gt;4&lt;/span&gt;, &lt;span class=&#34;hljs-number&#34;&gt;5&lt;/span&gt;, a: &lt;span class=&#34;hljs-number&#34;&gt;6&lt;/span&gt;)&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h2 id=&#34;클로저-closure&#34;&gt;클로저(Closure)&lt;/h2&gt;
&lt;h3 id=&#34;클로저의-n가지-축약-형태&#34;&gt;클로저의 N가지 축약 형태&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 놀라운 점: 클로저의 문법 설탕(syntactic sugar)이 너무 많아 여기서 일일이 나열하지 않겠지만, 가장 황당한 것은 단 하나의 `&gt;` 기호로 표현할 수 있는 형태입니다. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;이렇게 작성할 수 있는 근본적인 이유는 String에 &lt;code&gt;&amp;gt;&lt;/code&gt;라는 이름의 함수가 존재하기 때문입니다. 네, 잘못 보신 게 아닙니다. 기호도 함수가 될 수 있습니다! 이에 대한 자세한 내용은 아래에서 더 놀라운 사실로 다루겠습니다.&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; num &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; [&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;9&amp;quot;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;2&amp;quot;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;1&amp;quot;&lt;/span&gt;]&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; sortedNum &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; num.sroted(by: &lt;span class=&#34;hljs-operator&#34;&gt;&amp;gt;&lt;/span&gt;)&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;정말 어이없다.&lt;/p&gt;
&lt;h3 id=&#34;return-생략-반환&#34;&gt;return 생략 반환&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 충격 포인트: &#34;한 줄 반환은 return을 생략할 수 있다&#34;는 이해할 수 있지만, &#34;여러 줄도 return을 생략할 수 있다&#34;는 절대 이해할 수 없을 거다&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;TS에서 return을 생략하는 문법은 Swift의 일반적인 작성 방식과 같다. 한 줄 return 생략:&lt;/p&gt;
&lt;figure class=&#34;highlight typescript&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs typescript&#34;&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 省略写法&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;a&lt;/span&gt; = (&lt;span class=&#34;hljs-params&#34;&gt;&lt;/span&gt;) =&amp;gt; &lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 正常写法是：&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;a&lt;/span&gt; = (&lt;span class=&#34;hljs-params&#34;&gt;&lt;/span&gt;) =&amp;gt; &amp;#123;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;일반적인 Swift 문법:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;a&lt;/span&gt;()-&amp;gt; &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt; &amp;#123;&lt;br&gt; &lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;hljs-comment&#34;&gt;// -&amp;gt; 因为单行，所以省略了 return&lt;/span&gt;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;하지만! SwiftUI에서의 작성 방식:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-type&#34;&gt;VStack&lt;/span&gt; &amp;#123;&lt;br&gt; &lt;span class=&#34;hljs-type&#34;&gt;Text&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;Hello&amp;quot;&lt;/span&gt;)&lt;br&gt; &lt;span class=&#34;hljs-type&#34;&gt;Text&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;World&amp;quot;&lt;/span&gt;)&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;당신이 본 것은 맞습니다. 이것도 후행 클로저 함수입니다. 여기서 &lt;code&gt;VStack&lt;/code&gt;은 함수이며, 클로저 매개변수가 마지막이자 유일한 매개변수로 사용될 때 &lt;code&gt;VStack&lt;/code&gt;의 괄호를 생략할 수 있습니다. 하지만! 여기서는 두 개의 &lt;code&gt;Text&lt;/code&gt; 함수 호출을 반환하면서도 &lt;code&gt;return&lt;/code&gt;을 쓰지 않았습니다. 왜일까요?&lt;/p&gt;
&lt;p&gt;왜냐하면 &lt;code&gt;ViewBuilder&lt;/code&gt;를 사용했기 때문입니다. 이것은 뒤에 나오는 &lt;code&gt;resultBuilder&lt;/code&gt;와 같은 문법 설탕입니다.&lt;/p&gt;
&lt;h3 id=&#34;자동-클로저&#34;&gt;자동 클로저&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 놀라운 점: 평범한 클로저 할당에는 특별한 것이 없지만, 그것의 지연 계산 능력은 나를 놀라게 만들었습니다. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;자동 클로저를 처음 볼 때는 이 예제로 소개되었고, ‘지연 계산’ 능력에 대해 설명했습니다:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; customersInLine &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; [&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;Chris&amp;quot;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;Alex&amp;quot;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;Ewa&amp;quot;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;Barry&amp;quot;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;Daniella&amp;quot;&lt;/span&gt;]&lt;br&gt;&lt;span class=&#34;hljs-built_in&#34;&gt;print&lt;/span&gt;(customersInLine.count)&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 打印 “5”&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; customerProvider &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &amp;#123; customersInLine.remove(at: &lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;) &amp;#125;&lt;br&gt;&lt;span class=&#34;hljs-built_in&#34;&gt;print&lt;/span&gt;(customersInLine.count)&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 打印 ”5“&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-built_in&#34;&gt;print&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;Now serving &lt;span class=&#34;hljs-subst&#34;&gt;\(customerProvider())&lt;/span&gt;!&amp;quot;&lt;/span&gt;)&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 打印 “Now serving Chris!”&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-built_in&#34;&gt;print&lt;/span&gt;(customersInLine.count)&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 打印 “4”&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;이것이 평범한 클로저라고 생각했는데, TS와 마찬가지로 customerProvider 변수가 클로저에 할당된 것뿐이라고 여겼습니다. 동일한 구현은 TS에서 다음과 같습니다:&lt;/p&gt;
&lt;figure class=&#34;highlight javascript&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs javascript&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;customerProvider&lt;/span&gt; = (&lt;span class=&#34;hljs-params&#34;&gt;&lt;/span&gt;) =&amp;gt; &amp;#123;&lt;br&gt; &lt;span class=&#34;hljs-comment&#34;&gt;// 省略逻辑&lt;/span&gt;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;그리고 물론 호출될 때 클로저의 로직이 실행되는데, 이게 무슨 지연 계산이냐고 할 수 있습니다!&lt;/p&gt;
&lt;p&gt;하지만 클로저가 매개변수로 전달될 때의 지연 계산 형태가 진정으로 강력합니다:&lt;/p&gt;
&lt;p&gt;평범한 명시적 클로저 호출은 TS 호출 방식과 기본적으로 유사하며, 클로저가 매개변수로 전달될 때도 여전히 중괄호 안에 작성됩니다:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// customersInLine 是 [&amp;quot;Alex&amp;quot;, &amp;quot;Ewa&amp;quot;, &amp;quot;Barry&amp;quot;, &amp;quot;Daniella&amp;quot;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;serve&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;customer&lt;/span&gt; &lt;span class=&#34;hljs-params&#34;&gt;customerProvider&lt;/span&gt;: () -&amp;gt; &lt;span class=&#34;hljs-type&#34;&gt;String&lt;/span&gt;) &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-built_in&#34;&gt;print&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;Now serving &lt;span class=&#34;hljs-subst&#34;&gt;\(customerProvider())&lt;/span&gt;!&amp;quot;&lt;/span&gt;)&lt;br&gt;&amp;#125;&lt;br&gt;serve(customer: &amp;#123; customersInLine.remove(at: &lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;) &amp;#125; )&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 打印 “Now serving Alex!”&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;하지만 &lt;code&gt;customerProvider&lt;/code&gt;를 &lt;code&gt;@autoclosuer&lt;/code&gt;로 표시하면 상황이 달라집니다. 이제 &lt;code&gt;serve&lt;/code&gt; 함수 호출은 다음과 같이 작성할 수 있습니다:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// customersInLine 是 [&amp;quot;Ewa&amp;quot;, &amp;quot;Barry&amp;quot;, &amp;quot;Daniella&amp;quot;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;serve&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;customer&lt;/span&gt; &lt;span class=&#34;hljs-params&#34;&gt;customerProvider&lt;/span&gt;: &lt;span class=&#34;hljs-keyword&#34;&gt;@autoclosure&lt;/span&gt; () -&amp;gt; &lt;span class=&#34;hljs-type&#34;&gt;String&lt;/span&gt;) &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-built_in&#34;&gt;print&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;Now serving &lt;span class=&#34;hljs-subst&#34;&gt;\(customerProvider())&lt;/span&gt;!&amp;quot;&lt;/span&gt;)&lt;br&gt;&amp;#125;&lt;br&gt;serve(customer: customersInLine.remove(at: &lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;))&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 打印 “Now serving Ewa!”&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;마지막 &lt;code&gt;serve&lt;/code&gt; 함수 호출을 주목하세요. 이 함수의 매개변수 customer은 &lt;code&gt;customersInLine.remove(at: 0)&lt;/code&gt;라는 구문입니다. TS에서는 어떤 경우든 호출 스택이 먼저 이 값을 평가한 후 &lt;code&gt;serve&lt;/code&gt; 함수를 호출합니다. 그러나 Swift에서 이 작성 방식은 위의 형태와 단지 형태만 다를 뿐, 논리는 동일합니다. 즉, &lt;code&gt;serve&lt;/code&gt; 함수가 먼저 실행된 후 내부에서 이 구문이 실행됩니다(&lt;code&gt;customerProvider&lt;/code&gt;가 호출될 때). 이것이 바로 전설적인 &#39;지연 계산(lazy evaluation)&#39;이겠죠.&lt;/p&gt;
&lt;p&gt;이런 패턴을 자주 사용하다 보면, 다음과 같은 코드를 자주 마주하게 될 것입니다:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;a(b.remove(&lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;))&lt;br&gt;c(d.add4())&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;이때 괄호 안의 문장을 먼저 실행할지, 외부 함수를 먼저 실행할지 구분하기 어렵습니다. 따라서 Swift 공식 문서에도 다음과 같이 설명되어 있습니다:&lt;/p&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 자동 클로저를 과도하게 사용하면 코드를 이해하기 어려울 수 있습니다. 컨텍스트와 함수 이름은 계산이 지연되고 있음을 명확히 나타내야 합니다. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;정말 어이없네요, 권장하지 않는다면 애초에 설계하지 말았어야죠!&lt;/p&gt;
&lt;h2 id=&#34;열거형-enum&#34;&gt;열거형(Enum)&lt;/h2&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 놀라운 점: 다른 언어에서 열거형은 단순히 편의를 위한 선택적 상태 판별 타입에 불과하지만, Swift에서는 가장 흔히 사용되는 1급 타입이며, 일부 기능에서는 Struct를 대체할 수 있다는 사실을 믿을 수 있나요? &lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 놀라운 점 2: 열거형은 값 타입(value type)입니다. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;TS에서 열거형은 평범한 &#39;열거형’일 뿐이며, 단순히 값을 나열하는 용도로 사용됩니다. 대부분 상태 머신에서 상태를 설명하는 데 사용되죠:&lt;/p&gt;
&lt;figure class=&#34;highlight typescript&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs typescript&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;enum&lt;/span&gt; A &amp;#123;&lt;br&gt; B = &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;B&amp;quot;&lt;/span&gt;&lt;br&gt; C = &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;c&amp;quot;&lt;/span&gt;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;물론, TS에서 런타임과 컴파일 타임의 차이로 인한 다양한 기법들은 논외로 하겠습니다. TS에서는 객체를 완전히 열거형 대신 사용할 수 있습니다:&lt;/p&gt;
&lt;figure class=&#34;highlight typescript&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs typescript&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;enum&lt;/span&gt; A &amp;#123;&lt;br&gt;  B = &lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;,&lt;br&gt;  C = &lt;span class=&#34;hljs-number&#34;&gt;1&lt;/span&gt;,&lt;br&gt;&amp;#125;&lt;br&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; A = &amp;#123;&lt;br&gt;  &lt;span class=&#34;hljs-attr&#34;&gt;B&lt;/span&gt;: &lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;,&lt;br&gt;  &lt;span class=&#34;hljs-attr&#34;&gt;C&lt;/span&gt;: &lt;span class=&#34;hljs-number&#34;&gt;1&lt;/span&gt;,&lt;br&gt;&amp;#125; &lt;span class=&#34;hljs-keyword&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt;;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h3 id=&#34;연관-값&#34;&gt;연관 값&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 놀라운 점: Swift에서 열거형을 이렇게 설계한 목적이 뭐야!&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Swift에서 열거형의 중요성은 첫 번째입니다. 모든 case를 순회할 수 있고(&lt;code&gt;CaseIterable&lt;/code&gt; 프로토콜을 준수해야 함), case를 함수처럼看待하여 호출 시 값을 전달해 열거형 인스턴스가 처리하도록 할 수 있습니다. 이를 &#39;연관 값(Associated Values)&#39;이라고 합니다:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;enum&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;A&lt;/span&gt; &amp;#123;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;case&lt;/span&gt; b(&lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;, &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;) &lt;span class=&#34;hljs-comment&#34;&gt;// 声明方式好像一个协议（也就是抽象类）&lt;/span&gt;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;case&lt;/span&gt; c(&lt;span class=&#34;hljs-type&#34;&gt;String&lt;/span&gt;)&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;그리고 사용할 때 값을 전달할 수 있습니다:&lt;/p&gt;
&lt;figure class=&#34;highlight javascript&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs javascript&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; a = A.&lt;span class=&#34;hljs-title function_&#34;&gt;b&lt;/span&gt;(&lt;span class=&#34;hljs-number&#34;&gt;1024&lt;/span&gt;, &lt;span class=&#34;hljs-number&#34;&gt;9527&lt;/span&gt;)&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;열거형(enum)은 switch 문에서 가장 많이 사용되며, 놀라운 switch와 함께 놀라운 case를 결합하여 다음과 같이 작성할 수 있습니다:&lt;/p&gt;
&lt;figure class=&#34;highlight javascript&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs javascript&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;switch&lt;/span&gt; a &amp;#123;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;case&lt;/span&gt; .&lt;span class=&#34;hljs-title function_&#34;&gt;b&lt;/span&gt;(&lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; d, &lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; f):&lt;br&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;print&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;A.b 关联值为:\(d) 和 \(f)&amp;quot;&lt;/span&gt;)&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; .&lt;span class=&#34;hljs-title function_&#34;&gt;c&lt;/span&gt;(g): &lt;span class=&#34;hljs-comment&#34;&gt;// 上面提到的另一种 switch case 写法&lt;/span&gt;&lt;br&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;print&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;A.c:\(g)&amp;quot;&lt;/span&gt;)&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;처음 봤을 때 나는 상당히 추상적으로 느껴졌고, 실제 사용 사례가 무엇인지도 잘 떠오르지 않았습니다(결국 저는 그렇게 해본 적이 없으니까요).&lt;/p&gt;
&lt;h3 id=&#34;암시적-할당&#34;&gt;암시적 할당&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 놀라운 점: 열거형의 타입 선언은 열거형 자체의 타입(키-값 쌍)이 아닌, case의 타입을 선언한다는 것입니다. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Swift의 암시적 할당은 TS나 다른 C 계열 언어들과 일치합니다. 첫 번째 숫자는 n, 그다음은 n + 1이 되는 방식이죠.&lt;/p&gt;
&lt;p&gt;하지만 Swift는 한 걸음 더 나아가, 선언한 타입에 따라 암시적 할당을 수행합니다. 기본적으로는 그렇지 않는데, 예를 들어:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;enum&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;A&lt;/span&gt;: &lt;span class=&#34;hljs-title class_&#34;&gt;Int&lt;/span&gt; &amp;#123; &lt;span class=&#34;hljs-comment&#34;&gt;// &amp;lt;- 声明了 Int 类型，所以隐式赋值了&lt;/span&gt;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;case&lt;/span&gt; b, c, d &lt;span class=&#34;hljs-comment&#34;&gt;// b c d 分别为 0 1 2(需要使用 A.b.rawValue 才能访问，这又是另一个震惊点了）&lt;/span&gt;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;enum&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;B&lt;/span&gt;: &lt;span class=&#34;hljs-title class_&#34;&gt;String&lt;/span&gt; &amp;#123; &lt;span class=&#34;hljs-comment&#34;&gt;// &amp;lt;- 声明了 String 类型，所以隐式赋值了&lt;/span&gt;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;case&lt;/span&gt; bb, cc, dd &lt;span class=&#34;hljs-comment&#34;&gt;// bb cc dd分别为 &amp;quot;bb&amp;quot; &amp;quot;cc&amp;quot; &amp;quot;dd&amp;quot;&lt;/span&gt;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;TS에서는 Swift에서 지정할 수 있는 Int 타입(이 타입은 실제로 case의 타입을 의미함)을 지정할 수 없으며, 오직 enum의 타입(일반적으로 &lt;code&gt;Record&lt;/code&gt;로 정의된 키-값 쌍)만 지정할 수 있습니다.&lt;/p&gt;
&lt;h2 id=&#34;구조체와-클래스&#34;&gt;구조체와 클래스&lt;/h2&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 놀라운 점: 구조체가 값 타입이라고???&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;이렇게 단단해 보이는 구조체가 값 타입이었습니다(성능 최적화를 위해 &lt;code&gt;Immutable&lt;/code&gt; 방식을 사용함):&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;a&lt;/span&gt; &amp;#123;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; b: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; c: &lt;span class=&#34;hljs-type&#34;&gt;String&lt;/span&gt;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;중괄호로 둘러싸인 것이 어떻게 값처럼 보이지 않을 수 있죠! 말도 안 돼! 충격적이네요!&lt;/p&gt;
&lt;h2 id=&#34;속성&#34;&gt;속성&lt;/h2&gt;
&lt;h3 id=&#34;다양한-xx-속성-래퍼-옵저버&#34;&gt;다양한 xx 속성/래퍼/옵저버&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 충격 포인트: Swift가 너무 많은 것을 해냈습니다. 계산 속성, 저장 속성, 속성 래퍼(TS에도 있음), 속성 옵저버(TS에도 있으며 래퍼와 함께 제공됨)와 같은 개념들은 일반적으로 Vue의 `computed`, `watch` 등과 같이 프레임워크에서 제공하는 기능인데, Swift는 Struct와 클래스 내에서 이를 직접 구현했습니다. 말도 안 되죠! &lt;/p&gt;&lt;/blockquote&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;A&lt;/span&gt; &amp;#123;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; b &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;hljs-comment&#34;&gt;// &amp;lt;- 存储属性，直接有值，在 struct 实例化的时候就确定了&lt;/span&gt;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; c: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt; &amp;#123;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; b &lt;span class=&#34;hljs-operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;hljs-comment&#34;&gt;// &amp;lt;- 一个只读计算属性，不存储值，因为单行，return 可以省略&lt;/span&gt;&lt;br&gt; &amp;#125;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;추가: 속성 래퍼는 전역 변수에 사용할 수 없습니다.&lt;/p&gt;
&lt;h2 id=&#34;서브스크립트&#34;&gt;서브스크립트&lt;/h2&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 놀라운 점: 이 부분은 특별히 설명할 것이 없습니다. &#39;서브스크립트&#39;라는 개념 자체가 이미 놀라운데, 이는 컬렉션, 리스트, 딕셔너리 요소에 접근하는 빠른 방법을 제공합니다. 심지어 서브스크립트는 함수처럼 여러 값을 전달할 수도 있습니다. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;기본적으로 서브스크립트 호출 방식은 데이터 구조(위에서 언급한 컬렉션, 리스트, 딕셔너리)에 대한 함수 호출로 값을 접근하는 것으로 볼 수 있습니다. 다른 점은 함수 호출은 &lt;code&gt;()&lt;/code&gt;를 사용하는 반면, 서브스크립트는 &lt;code&gt;[]&lt;/code&gt;를 사용한다는 것입니다.&lt;/p&gt;
&lt;h2 id=&#34;상속&#34;&gt;상속&lt;/h2&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 놀라운 점: 이전의 모든 놀라운 점과 유사하게, Swift는 다양한 목적과 효과를 달성하기 위해 매우 자유롭게 다양한 키워드를 추가하는 것 같습니다. 따라서 상속과 관련된 예약어(물론 &#39;예약어&#39;라고 부를 수도 없습니다. Swift에서는 모든 예약어를 백틱으로 감싸면 사용할 수 있기 때문입니다. 이에 대해서는 나중에 다시 놀라겠습니다)가 매우 많습니다. 예를 들어: `final`, `override`, `open`, `required` 등이 있습니다. &lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;생성-과정&#34;&gt;생성 과정&lt;/h2&gt;
&lt;h3 id=&#34;struct의-멤버별-생성자&#34;&gt;Struct의 멤버별 생성자&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 놀라운 점: Struct는 값 타입이므로 초기화 시 전통적인 Class의 생성자 규칙과 차이가 있습니다. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;원래 Struct와 Class는 모두 평범한 데이터 구조로, TS의 Class와 다르지 않습니다. 하지만 Swift는 여기서 또 다른 특이한 점을 만들어냈습니다.&lt;/p&gt;
&lt;p&gt;Struct는 값 타입이기 때문에 특별하게, &#39;멤버별 생성자’라는 생성자 형태를 가집니다. 즉, Struct에 &lt;code&gt;init&lt;/code&gt;이 전혀 없을 때, 속성을 초기화하기 위해 매개변수를 전달하는 방식으로 init할 수 있으며, 명시적으로 작성할 필요가 없습니다.&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 此 Struct 没有 init，因此适用逐一成员构造器&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;A&lt;/span&gt; &amp;#123; &lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; a: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; b: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 上述效果等同于：&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;A&lt;/span&gt; &amp;#123;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; a: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; b: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;init&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;a&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;, &lt;span class=&#34;hljs-params&#34;&gt;b&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;) &amp;#123;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;self&lt;/span&gt;.a &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; a&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;self&lt;/span&gt;.b &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; b&lt;br&gt; &amp;#125;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 上面两个都可以这么用：&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-type&#34;&gt;A&lt;/span&gt;(a: &lt;span class=&#34;hljs-number&#34;&gt;1&lt;/span&gt;, b: &lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;)&lt;br&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;하지만 이 규칙은 Class에는 적용되지 않으며, Class는 반드시 명시적으로 init 생성자 메서드를 통해 속성을 초기화해야 합니다.&lt;/p&gt;
&lt;h3 id=&#34;클래스의-지정-생성자와-편의-생성자&#34;&gt;클래스의 지정 생성자와 편의 생성자&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 놀라운 점: 뭐? 생성자가 두 종류라고???&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;클래스의 지정 생성자는 TS의 일반적인 &lt;code&gt;construct&lt;/code&gt;와 동일한 역할을 하는 &lt;code&gt;init&lt;/code&gt; 메서드입니다. 하지만 편의 생성자는… init 앞에 &lt;code&gt;convenience&lt;/code&gt; 키워드를 붙인 것입니다(또 등장!).&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;convenience&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;init&lt;/span&gt;() &amp;#123;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;사실 여기 Swift 문서는 독자들에게 왜 편의 생성자(convenience initializer)가 필요한지 명확히 설명하지 않고, 바로 클래스의 생성, 상속 과정 및 생성자 위임에 대해 이야기합니다. 여기서 초보자들을 위해 왜 편의 생성자라는 것이 존재해야 하는지 설명해 드리겠습니다. 아주 간단한 이유입니다: &lt;code&gt;클래스 내의 여러 지정 생성자(designated initializer)는 서로를 호출할 수 없습니다.&lt;/code&gt; 이렇게 간단한 이유입니다. 참을 수 없이 호출하고 싶으신가요? 초기화 과정을 단순화하고 싶으신가요? 편의 생성자를 사용하세요!&lt;/p&gt;
&lt;p&gt;일반적인 작성 방식:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;A&lt;/span&gt; &amp;#123;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; b: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; c: &lt;span class=&#34;hljs-type&#34;&gt;String&lt;/span&gt;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;init&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;b&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;, &lt;span class=&#34;hljs-params&#34;&gt;c&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;String&lt;/span&gt;) &amp;#123;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;self&lt;/span&gt;.b &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; b&lt;br&gt; &amp;#125;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;init&lt;/span&gt;() &amp;#123;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;self&lt;/span&gt;.b &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;self&lt;/span&gt;.c &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;UnKnown&amp;quot;&lt;/span&gt;&lt;br&gt; &amp;#125;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;위에서 self.b 같은 것을 두 번 작성하는 것이 번거롭다고 느껴지시나요? 그래서 두 번째 &lt;code&gt;init()&lt;/code&gt; 에서는 이렇게 하고 싶습니다:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;init&lt;/span&gt;() &amp;#123;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;self&lt;/span&gt;.&lt;span class=&#34;hljs-keyword&#34;&gt;init&lt;/span&gt;(b: &lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;, c: &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;UnKnown&amp;quot;&lt;/span&gt;)	&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;컴파일러 오류 발생: &lt;code&gt;Designated initializer for &#39;A&#39; cannot delegate (with &#39;self.init&#39;); did you mean this to be a convenience initializer?&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;이 경우에는 반드시 편의 생성자(convenience initializer)를 사용해야 합니다:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;converience &lt;span class=&#34;hljs-keyword&#34;&gt;init&lt;/span&gt;() &amp;#123;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;self&lt;/span&gt;.&lt;span class=&#34;hljs-keyword&#34;&gt;init&lt;/span&gt;(b: &lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;, c: &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;UnKnown&amp;quot;&lt;/span&gt;)	&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;편의를 추구하는 거죠!&lt;/p&gt;
&lt;h3 id=&#34;실패-가능한-생성자-failable-initializer&#34;&gt;실패 가능한 생성자(Failable Initializer)&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 놀라운 점: 생성자도 실패할 수 있다고?&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;사실 실패 가능한 생성자란, 생성 과정(init 함수 호출 과정)에서 오류가 발생할 수 있다는 것을 의미합니다.&lt;/p&gt;
&lt;p&gt;따라서 생성자에서 오류가 발생하면 TS처럼 외부에서 &lt;code&gt;try-catch&lt;/code&gt;로 감쌀 필요 없이, 간단히 표시할 수 있습니다. 방법은 &lt;code&gt;init&lt;/code&gt; 뒤에 물음표를 붙여 &lt;code&gt;init?&lt;/code&gt;로 만들고, 오류가 발생할 수 있는 부분에서 &lt;code&gt;nil&lt;/code&gt;을 반환하면 됩니다. 인스턴스화할 때 &lt;code&gt;nil&lt;/code&gt;인지 확인하면 되죠.&lt;/p&gt;
&lt;p&gt;Swift에서는 일반 생성자가 값을 반환하지 않는데, 이는 TS와 동일합니다. 하지만 TS의 생성자는 값을 반환할 수 있으며, 반환 값이 객체 타입인 경우 &lt;code&gt;this&lt;/code&gt; 객체를 대체합니다. 이는 더욱 놀라운 사실이네요.&lt;/p&gt;
&lt;h2 id=&#34;옵셔널-체이닝-optional-chaining&#34;&gt;옵셔널 체이닝(Optional Chaining)&lt;/h2&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 놀라운 점: 질문: 어떤 경우에 함수에 ()를 썼는데도 실행되지 않을까요? 답: 옵셔널 체이닝 상황에서입니다.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;말보다 예시를 보는 게 나을 거예요:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;A&lt;/span&gt;() -&amp;gt; &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt; &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-built_in&#34;&gt;print&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;OK&amp;quot;&lt;/span&gt;)&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;1&lt;/span&gt;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;B&lt;/span&gt; &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; c: &lt;span class=&#34;hljs-type&#34;&gt;C&lt;/span&gt;?&lt;br&gt;&amp;#125;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;C&lt;/span&gt; &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; d: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;?&lt;br&gt;&amp;#125;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; d &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;B&lt;/span&gt;()&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 震惊的事情发生了：&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 这里，A 函数未执行，不会打印 OK，赋值也不会成功，因为 c 是 nil，这个语句返回 nil（通常 Swift 的赋值语句都返回 Void 的，也就是空元组）&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 返回 nil 意味着你可以使用 if-let 语句判断&lt;/span&gt;&lt;br&gt;d.c&lt;span class=&#34;hljs-operator&#34;&gt;?&lt;/span&gt;.d &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;A&lt;/span&gt;()&lt;br&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h2 id=&#34;오류-처리&#34;&gt;오류 처리&lt;/h2&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 놀라운 점: 열거형과 마찬가지로 Swift의 try-catch(실제로는 do-catch) 설계가 매우 복잡하다는 것입니다. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;catch 문은 임의의 오류를 포착할 수 있을 뿐만 아니라 특정 오류 유형도 포착할 수 있으며, is를 사용하거나 switch와 같이 여러 catch 분기를 통해 매칭할 수 있어서 매우 독특합니다:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;do&lt;/span&gt; &amp;#123;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;try&lt;/span&gt; someError()&lt;br&gt;&amp;#125; &lt;span class=&#34;hljs-keyword&#34;&gt;catch&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;ErrorA&lt;/span&gt; &amp;#123;&lt;br&gt; &lt;span class=&#34;hljs-comment&#34;&gt;// xxx&lt;/span&gt;&lt;br&gt;&amp;#125; &lt;span class=&#34;hljs-keyword&#34;&gt;catch&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;ErrorB&lt;/span&gt;, &lt;span class=&#34;hljs-type&#34;&gt;ErrorC&lt;/span&gt;, &lt;span class=&#34;hljs-type&#34;&gt;ErrorD&lt;/span&gt;.a &amp;#123;&lt;br&gt; &lt;span class=&#34;hljs-comment&#34;&gt;// xxx&lt;/span&gt;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h2 id=&#34;동시성&#34;&gt;동시성&lt;/h2&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 놀라운 점: 문서를 볼 때, 처음에는 `withTaskGroup`이 Group Task를 수행하는 내장 메서드라는 것을 깨닫지 못했습니다...&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;한편 &lt;code&gt;await&lt;/code&gt;는 TS의 &lt;code&gt;await&lt;/code&gt;와 완전히 동일한 사용법으로, 정말 편리합니다!&lt;/p&gt;
&lt;h2 id=&#34;확장-extension&#34;&gt;확장(Extension)&lt;/h2&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 놀라운 점: 확장은 Swift에서 가장 강력한 설계 중 하나로, 내장 객체든 서드파티 객체든 상관없이 어떤 객체라도 자유롭고 저렴한 비용으로, 복잡한 선언이나 키워드 없이 확장할 수 있습니다.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;TS에서는 기존 클래스를 확장하려면 프로토타입 체인을 조작하고 객체의 this를 해당 클래스로 수정해야 합니다.&lt;/p&gt;
&lt;p&gt;하지만 Swift에서는 단순히 &lt;code&gt;extension&lt;/code&gt;을 사용하면 원하는 메서드나 프로퍼티를 마음껏 작성할 수 있으며, 이들의 &lt;code&gt;this&lt;/code&gt;는 인스턴스를 가리키거나 확장 유형(즉 정적) 메서드/프로퍼티로 동작합니다.&lt;/p&gt;
&lt;p&gt;정말 끝내주게 강력합니다.&lt;/p&gt;
&lt;h2 id=&#34;프로토콜&#34;&gt;프로토콜&lt;/h2&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 놀라운 점: 프로토콜은 Struct의 추상화 문제를 해결하기 위해 등장한 것 같습니다. 대부분의 언어에서 클래스 자체는 상속이 가능하므로(절대적인 표현일 수 있음), 추가적으로 &#34;프로토콜&#34;을 구현할 필요 없이 추상 클래스로 충분합니다. Swift는 클래스가 단일 부모만 상속할 수 있도록 제한하면서, 프로토콜이 클래스, Struct, 열거형에서 동시에 작동하도록 한 부수적인 효과가 있습니다.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;더 설명할 필요 없이, 위에 다 말해두었습니다.&lt;/p&gt;
&lt;h2 id=&#34;제네릭&#34;&gt;제네릭&lt;/h2&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 놀라운 점: 제네릭에는 &#34;연관 타입(associated type)&#34;이라는 개념이 있는데, 이는 기본적으로 선언 시 사용하는 제네릭과 유사하지만 더 강력한 기능을 제공합니다.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Swift의 제네릭 연관 타입은 다음과 같습니다:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;protocol&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;Container&lt;/span&gt; &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;associatedtype&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;Item&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;mutating&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;append&lt;/span&gt;(&lt;span class=&#34;hljs-keyword&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;hljs-params&#34;&gt;item&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;Item&lt;/span&gt;)&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; count: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt; &amp;#123; &lt;span class=&#34;hljs-keyword&#34;&gt;get&lt;/span&gt; &amp;#125;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;subscript&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;i&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;) -&amp;gt; &lt;span class=&#34;hljs-type&#34;&gt;Item&lt;/span&gt; &amp;#123; &lt;span class=&#34;hljs-keyword&#34;&gt;get&lt;/span&gt; &amp;#125;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;왜 이런 방식으로 설계하지 않았을까:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;protocol&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;Container&lt;/span&gt;&amp;lt;&lt;span class=&#34;hljs-title class_&#34;&gt;Item&lt;/span&gt;&amp;gt; &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;mutating&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;append&lt;/span&gt;(&lt;span class=&#34;hljs-keyword&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;hljs-params&#34;&gt;item&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;Item&lt;/span&gt;)&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; count: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt; &amp;#123; &lt;span class=&#34;hljs-keyword&#34;&gt;get&lt;/span&gt; &amp;#125;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;subscript&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;i&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;) -&amp;gt; &lt;span class=&#34;hljs-type&#34;&gt;Item&lt;/span&gt; &amp;#123; &lt;span class=&#34;hljs-keyword&#34;&gt;get&lt;/span&gt; &amp;#125;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;아마도 이 Item이 너무 길어서 우아하지 않기 때문일 것입니다. 예를 들어:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;protocol&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;SuffixableContainer&lt;/span&gt;: &lt;span class=&#34;hljs-title class_&#34;&gt;Container&lt;/span&gt; &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;associatedtype&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;Suffix&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;SuffixableContainer&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;where&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;Suffix&lt;/span&gt;.&lt;span class=&#34;hljs-type&#34;&gt;Item&lt;/span&gt; &lt;span class=&#34;hljs-operator&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;Item&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;suffix&lt;/span&gt;(&lt;span class=&#34;hljs-keyword&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;hljs-params&#34;&gt;size&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;) -&amp;gt; &lt;span class=&#34;hljs-type&#34;&gt;Suffix&lt;/span&gt;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;이름 뒤에 작성하면, 제네릭 선언이 자주 화면을 벗어나게 되어 버거울 수 있습니다.&lt;/p&gt;
&lt;h2 id=&#34;불투명-타입과-캡슐화-프로토콜&#34;&gt;불투명 타입과 캡슐화 프로토콜&lt;/h2&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 놀라운 점: 컴파일러는 타입을 알지만 호출 측에서는 구체적인 타입을 모르고 특정 프로토콜을 준수한다는 것만 알 수 있는 효과를 구현할 수 있습니다. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;기본적으로, 불투명 타입과 캡슐화 프로토콜이 달성하고자 하는 효과는 한편으로 호출자에게 구현 세부 사항을 숨기면서도 코드 리팩토링 시 많은 부분을 수정하지 않아도 되도록 하는 것입니다. 예를 들어:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;protocol&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;Shape&lt;/span&gt; &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;area&lt;/span&gt;() -&amp;gt; &lt;span class=&#34;hljs-type&#34;&gt;Double&lt;/span&gt;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;Circle&lt;/span&gt;: &lt;span class=&#34;hljs-title class_&#34;&gt;Shape&lt;/span&gt; &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; radius: &lt;span class=&#34;hljs-type&#34;&gt;Double&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;area&lt;/span&gt;() -&amp;gt; &lt;span class=&#34;hljs-type&#34;&gt;Double&lt;/span&gt; &amp;#123;&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; .pi &lt;span class=&#34;hljs-operator&#34;&gt;*&lt;/span&gt; radius &lt;span class=&#34;hljs-operator&#34;&gt;*&lt;/span&gt; radius&lt;br&gt;    &amp;#125;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;makeShape&lt;/span&gt;() -&amp;gt; &lt;span class=&#34;hljs-keyword&#34;&gt;some&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;Shape&lt;/span&gt; &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;Circle&lt;/span&gt;(radius: &lt;span class=&#34;hljs-number&#34;&gt;5&lt;/span&gt;)&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;여기서 &lt;code&gt;makeShape&lt;/code&gt;이 반환하는 타입은 &lt;code&gt;Shape&lt;/code&gt; 프로토콜을 준수하는 어떤 타입이든 가능하며, 명시적으로 &lt;code&gt;Circle&lt;/code&gt; 타입을 반환할 필요가 없습니다. 이렇게 하면 이후에 &lt;code&gt;Shape&lt;/code&gt; 타입을 준수하는 새로운 &lt;code&gt;Struct&lt;/code&gt;를 추가하더라도 해당 함수가 반환할 수 있습니다(이는 SwiftUI에서 흔히 사용되는 방식 중 하나로, &lt;code&gt;some View&lt;/code&gt; 등이 해당합니다).&lt;/p&gt;
&lt;h2 id=&#34;메모리-안전성&#34;&gt;메모리 안전성&lt;/h2&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 놀라운 점: Swift는 값을 참조로 전달할 수 있기 때문에 동일한 메모리 영역에 대한 동시 읽기/쓰기가 발생할 수 있습니다(이건 합리적이죠?)&lt;/p&gt;&lt;/blockquote&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; a &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;add&lt;/span&gt;(&lt;span class=&#34;hljs-keyword&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;hljs-params&#34;&gt;b&lt;/span&gt;: &lt;span class=&#34;hljs-keyword&#34;&gt;inout&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;) &amp;#123;&lt;br&gt;    b &lt;span class=&#34;hljs-operator&#34;&gt;+=&lt;/span&gt; a&lt;br&gt;&amp;#125;&lt;br&gt;add(&lt;span class=&#34;hljs-operator&#34;&gt;&amp;amp;&lt;/span&gt;a)&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;위 코드를 실행하면 오류가 발생하지만 컴파일러는 경고하지 않습니다. 이와 같은 경우가 많기 때문에 특히 주의해야 합니다. TS에는 참조에 의한 전달이 없기 때문에 이런 문제가 발생하지 않아 편리합니다.&lt;/p&gt;
&lt;h2 id=&#34;고급-연산자&#34;&gt;고급 연산자&lt;/h2&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 놀라운 점: 기호를 함수명으로 사용하는 함수를 재정의하거나 커스터마이징하여 동일한 클래스나 구조체의 인스턴스 간 상호 작용을 구현할 수 있습니다. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;예를 들어:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;A&lt;/span&gt; &amp;#123;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; b &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;1&lt;/span&gt;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; c &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;+&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;left&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;A&lt;/span&gt;, &lt;span class=&#34;hljs-params&#34;&gt;right&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;A&lt;/span&gt;) -&amp;gt; &lt;span class=&#34;hljs-type&#34;&gt;A&lt;/span&gt; &amp;#123;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;A&lt;/span&gt;(b: left.b &lt;span class=&#34;hljs-operator&#34;&gt;+&lt;/span&gt; right.b, c: left.c &lt;span class=&#34;hljs-operator&#34;&gt;+&lt;/span&gt; right.c)&lt;br&gt; &amp;#125;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 然后你就可以&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; a &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;A&lt;/span&gt;()&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; b &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;A&lt;/span&gt;()&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;let&lt;/span&gt; c &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; a &lt;span class=&#34;hljs-operator&#34;&gt;+&lt;/span&gt; b &lt;span class=&#34;hljs-comment&#34;&gt;// &amp;lt;- 是的你没看错，struct 实例可以相加！&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;이 디자인 정말 좋은데, 내가 왜 생각 못했을까? 대단하다 대단해.&lt;/p&gt;
&lt;p&gt;주의할 점은 연산자 메서드는 자체 속성을 가지고 있다는 것입니다. 예를 들어 &lt;code&gt;+&lt;/code&gt;는 이항 연산자이면서 중위 연산자이므로 두 개의 함수를 받습니다. &lt;code&gt;-&lt;/code&gt;는 중위 연산자(이항 연산자)일 수도 있고, 접두사 연산자(단항 연산자, &lt;code&gt;func&lt;/code&gt; 앞에 &lt;code&gt;prefix&lt;/code&gt;를 추가해야 함)일 수도 있어서 오버로드할 수 있습니다.&lt;/p&gt;
&lt;p&gt;하지만 Swift는 할당 연산자 &lt;code&gt;=&lt;/code&gt;는 오버로드할 수 없고, 삼항 조건 연산자(&lt;code&gt;a ? b : c&lt;/code&gt;)도 오버로드할 수 없다고 규정하고 있습니다.&lt;/p&gt;
&lt;p&gt;이 연산자는 너무 흔해서 Swift 내장 객체/Foundation 객체 어디에서나 &lt;code&gt;==&lt;/code&gt; 같은 동등 판단 연산자 함수를 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;심지어 자신만의 연산자를 구현할 수도 있습니다!&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 需要先声明，这是一个操作符，因为有两个值在操作符，所以这是个中缀操作符(infix)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;infix&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;operator&lt;/span&gt; &lt;span class=&#34;hljs-title&#34;&gt;+++++++&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 然后扩展一下整数类型&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;extension&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;Int&lt;/span&gt; &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;+++++++&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;left&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;, &lt;span class=&#34;hljs-params&#34;&gt;right&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;) -&amp;gt; &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt; &amp;#123;&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; (left &lt;span class=&#34;hljs-operator&#34;&gt;+&lt;/span&gt; right) &lt;span class=&#34;hljs-operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;7&lt;/span&gt; &lt;span class=&#34;hljs-comment&#34;&gt;// &amp;lt;- 因为有 7 个 + 号，所以我写了乘以 7&lt;/span&gt;&lt;br&gt;    &amp;#125;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 这么用：&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;hljs-operator&#34;&gt;+++++++&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;7&lt;/span&gt; &lt;span class=&#34;hljs-comment&#34;&gt;// &amp;lt;- 63&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;진짜 대단하다.&lt;/p&gt;
&lt;h3 id=&#34;결과-빌더&#34;&gt;결과 빌더&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 놀라운 점: Swift는 &#34;우아함&#34;과 &#34;재사용&#34;을 위해 극한까지 활용한다. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;아래 코드가 보기 좋게 보이도록 하기 위해 Swift는 &lt;code&gt;@resultBuilder&lt;/code&gt;라는 신기한 것을 &amp;quot;발명&amp;quot;했는데, 다음과 같다:&lt;/p&gt;
&lt;p&gt;일반적인 작성 방식:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; a &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; b &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;A&lt;/span&gt; &amp;#123;&lt;br&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; a: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 这里会有一个三元判断&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; c &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;A&lt;/span&gt;(a: a &lt;span class=&#34;hljs-operator&#34;&gt;&amp;gt;&lt;/span&gt; b &lt;span class=&#34;hljs-operator&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;666&lt;/span&gt; : &lt;span class=&#34;hljs-number&#34;&gt;999&lt;/span&gt; )&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;Swift는 위의 삼항 연산자 판단이 너무 번거롭고, 복잡한 판단의 경우 길어져 가독성이 떨어진다고 말하며 더 우아한 방법을 원했습니다. 그래서 결과 빌더(Result Builder)가 탄생했습니다(제가 이해한 것이 맞다면):&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;@resultBuilder&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;BBuilder&lt;/span&gt; &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;buildBlock&lt;/span&gt;(&lt;span class=&#34;hljs-keyword&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;hljs-params&#34;&gt;b&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;) -&amp;gt; &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt; &amp;#123; &lt;span class=&#34;hljs-comment&#34;&gt;// 对应 if/else 分支的执行结果&lt;/span&gt;&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; b&lt;br&gt;    &amp;#125;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;buildEither&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;aaa&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;) -&amp;gt; &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt; &amp;#123; &lt;span class=&#34;hljs-comment&#34;&gt;// 对应 if 分支&lt;/span&gt;&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; aaa&lt;br&gt;    &amp;#125;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;buildEither&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;bbb&lt;/span&gt;: &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;) -&amp;gt; &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt; &amp;#123; &lt;span class=&#34;hljs-comment&#34;&gt;// 对应 else 分支&lt;/span&gt;&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; bbb&lt;br&gt;    &amp;#125;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;그런 다음 위의 &lt;code&gt;c&lt;/code&gt; 변수에 다음과 같이 값을 할당합니다:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;BB&lt;/span&gt;(&lt;span class=&#34;hljs-meta&#34;&gt;@BBuilder&lt;/span&gt; &lt;span class=&#34;hljs-params&#34;&gt;b&lt;/span&gt;: () -&amp;gt; &lt;span class=&#34;hljs-type&#34;&gt;Int&lt;/span&gt;) -&amp;gt; &lt;span class=&#34;hljs-type&#34;&gt;A&lt;/span&gt; &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;A&lt;/span&gt;(a: b())&lt;br&gt;&amp;#125;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 终于等到 c 了，这里的 c 和最开始的 c 完全一样&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;var&lt;/span&gt; c &lt;span class=&#34;hljs-operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;hljs-type&#34;&gt;BB&lt;/span&gt; &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; a &lt;span class=&#34;hljs-operator&#34;&gt;&amp;gt;&lt;/span&gt; b &amp;#123;&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;666&lt;/span&gt;&lt;br&gt;    &amp;#125; &lt;span class=&#34;hljs-keyword&#34;&gt;else&lt;/span&gt; &amp;#123;&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;hljs-number&#34;&gt;999&lt;/span&gt;&lt;br&gt;    &amp;#125;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;재사용, 우아함, 효율성, 완벽하다!&lt;/p&gt;
&lt;h2 id=&#34;어휘-구조&#34;&gt;어휘 구조&lt;/h2&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 놀라운 점: Swift에는 예약어가 매우 많지만, 예약어를 식별자로 사용할 수 있다는 것입니다. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;방법은 역따옴표로 감싸는 것입니다:&lt;/p&gt;
&lt;figure class=&#34;highlight swift&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs swift&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;`func`&lt;/span&gt;() &amp;#123;&amp;#125; &lt;span class=&#34;hljs-comment&#34;&gt;// &amp;lt;- 一个叫做 `func` 的函数&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;// 调用也是 &lt;/span&gt;&lt;br&gt;`func`()&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;하지만 키워드를 제외하면, &lt;code&gt;x&lt;/code&gt;와 x는 동일한 변수입니다.&lt;/p&gt;
</description>
            <pubDate>Thu, 27 Mar 2025 00:00:00 +0000</pubDate>
            <link>https://www.xheldon.com/ko/tech/app-dev-journey-swift.html</link>
            <guid isPermaLink="true">https://www.xheldon.com/ko/tech/app-dev-journey-swift.html</guid>
            
                <category>Apple</category>
            
                <category>苹果</category>
            
                <category>初体验</category>
            
                <category>教程</category>
            
                <category>技巧</category>
            
                <category>技术</category>
            
                <category>Swift</category>
            
                <category>全栈</category>
            
            
                <category>tech</category>
            
        </item>
        
        <item>
            <title>Cusor로 iOS 앱 개발하고 실시간 미리보기하는 방법</title>
            <description>&lt;p&gt;이 글은 원작자의 확장판이며, 원작자의 원문은 다음에서 확인할 수 있습니다:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://blog.imjp.uk/fxxk-xcode&#34; target=&#34;_blank&#34;&gt; 在 Cursor 打造高效 iOS 开发环境： AI 编程 + 实时预览完整指南&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;이 글에서 추가 설명을 하는 이유는, 제가 이 튜토리얼을 따라할 때 XCode와 iOS 개발에 대해 전혀 알지 못해 몇 가지 어려움을 겪었기 때문입니다. 따라서 여기에 기록으로 남깁니다.&lt;/p&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 이 글은 원문에 대한 보충 설명이므로, 먼저 원문을 확인해 주세요.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3 id=&#34;「swift-플러그인과-codelldb-플러그인-설치」에-대한-보충&#34;&gt;「Swift 플러그인과 CodeLLDB 플러그인 설치」에 대한 보충:&lt;/h3&gt;
&lt;p&gt;여기에 언급된 Swift 플러그인은 현재 더 이상 사용되지 않습니다. 최신 플러그인을 사용해야 하며, 플러그인 마켓에서 &lt;strong&gt;Swift Programming Language&lt;/strong&gt;를 검색하면 됩니다.&lt;/p&gt;
&lt;h3 id=&#34;「핫-리로딩-설정」-두-번째-단계에-대한-보충&#34;&gt;「&lt;strong&gt;핫 리로딩 설정&lt;/strong&gt;」 두 번째 단계에 대한 보충:&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 프로젝트의 해당 Target 설정 열기 -&gt; Build Settings -&gt; Other Linker Flags 검색 후, 각각 -Xlinker와 -interposable을 추가합니다.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;여기서 주의할 점은 두 가지를 동시에 추가해야 한다는 것입니다. 즉, &lt;code&gt;-Xlinker -interposable&lt;/code&gt;을 복사하여 아래 이미지에 붙여넣으세요:&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/use-cursor-develop-ios/197800e8-7a61-8012-b016-c6caf5e72435.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;새-파일-추가-후-source-kit이-파일을-찾지-못하는-문제-발생-시&#34;&gt;새 파일 추가 후 Source Kit이 파일을 찾지 못하는 문제 발생 시:&lt;/h3&gt;
&lt;p&gt;Xcode-Build-Server를 사용하여 원문에서 언급한 명령어를 다시 실행해야 합니다:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs bash&#34;&gt;// 然后在项目根目录下根据你的项目文件类型执行对应的命令:&lt;br&gt;xcode-build-server config -workspace *.xcworkspace -scheme &amp;lt;XXX&amp;gt; &lt;br&gt;xcode-build-server config -project *.xcodeproj -scheme &amp;lt;XXX&amp;gt;&lt;br&gt;&lt;br&gt;//例如你用的是 exampleProject.xcodeproj：&lt;br&gt;xcode-build-server config -project exampleProject.xcodeproj -scheme exampleProject&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h3 id=&#34;swiftui-디버깅-시-수정-사항이-실시간으로-미리보기되지-않는-문제&#34;&gt;SwiftUI 디버깅 시 수정 사항이 실시간으로 미리보기되지 않는 문제:&lt;/h3&gt;
&lt;p&gt;일부 ViewModifier를 추가/수정한 경우 실제로 실시간 업데이트가 되지 않을 수 있습니다. 하지만 Text 내의 String 수정과 같은 일반적인 경우에는 대부분 문제없이 작동합니다. 정확한 이유는 저도 모르겠습니다.&lt;/p&gt;
</description>
            <pubDate>Tue, 11 Feb 2025 00:00:00 +0000</pubDate>
            <link>https://www.xheldon.com/ko/tech/use-cursor-develop-ios.html</link>
            <guid isPermaLink="true">https://www.xheldon.com/ko/tech/use-cursor-develop-ios.html</guid>
            
                <category>Apple</category>
            
                <category>教程</category>
            
                <category>技巧</category>
            
                <category>AI</category>
            
                <category>技术</category>
            
                <category>XCode</category>
            
            
                <category>tech</category>
            
        </item>
        
        <item>
            <title>Jekyll에서 Hexo로 마이그레이션 시 발생한 문제 기록</title>
            <description>&lt;h2 id=&#34;서문&#34;&gt;서문&lt;/h2&gt;
&lt;p&gt;저는 프론트엔드 개발자이기 때문에 Ruby 같은 다른 스크립트 언어에는 익숙하지 않습니다. 처음 블로그를 시작할 때 Github Pages를 블로그 플랫폼으로 사용했는데, 기본적으로 Jeklly 프레임워크를 사용했습니다. 내용이 프레임워크보다 중요하다고 생각해서 그냥 사용하게 되었고, 이렇게 10년이 흘렀습니다.&lt;/p&gt;
&lt;p&gt;그동안 몇 가지 테마를 바꿨고, 나중에는 Hux가 제공하는 테마로 정착했습니다. 깔끔하고 세련되며 오픈 소스였기 때문입니다:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://huangxuan.me/&#34; target=&#34;_blank&#34;&gt; 黄玄的博客 | Hux Blog&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;이 테마에 맞춰 많은 커스터마이징을 했습니다. 예를 들어 오른쪽 콘텐츠 사용자 정의, 데이터 사용자 정의, Notion을 데이터 소스로 사용하여 렌더링하는 등이 있었습니다.&lt;/p&gt;
&lt;p&gt;하지만 Apple의 M 시리즈 칩이 출시되면서 Intel과 Apple 칩 간의 Ruby 차이점을 처리하는 것이 점점 어려워졌습니다. 예를 들어 빌드할 때 특정 x86 아키텍처 명령어를 사용해야만 간신히 올바르게 빌드되는 경우가 있었고, 이는 어떤 의존성도 마음대로 변경할 수 없는 상태에서였습니다:&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs bash&#34;&gt;&lt;span class=&#34;hljs-built_in&#34;&gt;arch&lt;/span&gt; -x86_64 bundle &lt;span class=&#34;hljs-built_in&#34;&gt;exec&lt;/span&gt; jekyll server --trace --config=_config.dev.yml --ssl-key local.xheldon.cn.key --ssl-cert local.xheldon.cn.pem&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;그래서 더 이상 프레임워크를 교체하지 않으면 앞으로 블로그를 전혀 게시할 수 없을 것이라는 사실을 깨달았습니다.&lt;/p&gt;
&lt;h2 id=&#34;기술-선택&#34;&gt;기술 선택&lt;/h2&gt;
&lt;p&gt;이 부분은 특별히 설명할 것이 없습니다. 기본적으로 Hexo는 Jekyll의 JavaScript 구현체라고 볼 수 있습니다. 많은 개념이 95% 동일하기 때문에 마이그레이션하는 데 어려움이 없습니다.&lt;/p&gt;
&lt;p&gt;더 중요한 것은 Hexo에도 Hux의 블로그 테마 템플릿을 구현한 사람이 있었다는 점입니다. 그래서 바로 가져다 사용했고, 이 과정을 간단히 기록해 보겠습니다.&lt;/p&gt;
&lt;h2 id=&#34;마이그레이션-과정&#34;&gt;마이그레이션 과정&lt;/h2&gt;
&lt;h3 id=&#34;플러그인-마이그레이션&#34;&gt;플러그인 마이그레이션&lt;/h3&gt;
&lt;p&gt;이 부분은 비교적 쉬웠습니다. Jekyll의 플러그인은 Hexo에서 헬퍼 함수로 구현했습니다. 다음은 Notion의 Bookmark 태그를 처리하는 함수입니다(경로는 &lt;code&gt;_plugins/add-attribute.rb&lt;/code&gt;:)&lt;/p&gt;
&lt;figure class=&#34;highlight ruby&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs ruby&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;Jekyll&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;RenderBookMarkBlock&lt;/span&gt; &amp;lt; &lt;span class=&#34;hljs-title class_ inherited__&#34;&gt;Liquid::Block&lt;/span&gt;&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;initialize&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;tag_name, attr, tokens&lt;/span&gt;)&lt;br&gt;            &lt;span class=&#34;hljs-variable language_&#34;&gt;super&lt;/span&gt;&lt;br&gt;            &lt;span class=&#34;hljs-comment&#34;&gt;# 普通的链接没有 yid 和 bid&lt;/span&gt;&lt;br&gt;            attrs = attr.scan(&lt;span class=&#34;hljs-regexp&#34;&gt;/url\=\&amp;quot;(.*)\&amp;quot;\stitle\=\&amp;quot;(.*)\&amp;quot;\simg\=\&amp;quot;(.*)\&amp;quot;\syid\=\&amp;quot;(.*)\&amp;quot;\sbid\=\&amp;quot;(.*)\&amp;quot;/&lt;/span&gt;)&lt;br&gt;            &lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; !attrs.empty?&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@url&lt;/span&gt; = attrs[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;][&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;]&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@title&lt;/span&gt; = attrs[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;][&lt;span class=&#34;hljs-number&#34;&gt;1&lt;/span&gt;]&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@img&lt;/span&gt; = attrs[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;][&lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;]&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@yid&lt;/span&gt; = attrs[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;][&lt;span class=&#34;hljs-number&#34;&gt;3&lt;/span&gt;]&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@bid&lt;/span&gt; = attrs[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;][&lt;span class=&#34;hljs-number&#34;&gt;4&lt;/span&gt;]&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@firstChar&lt;/span&gt; = &lt;span class=&#34;hljs-variable&#34;&gt;@title&lt;/span&gt;.empty? ? &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;quot;&lt;/span&gt; : (&lt;span class=&#34;hljs-variable&#34;&gt;@title&lt;/span&gt;)[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;].upcase&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@error&lt;/span&gt; = &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br&gt;            &lt;span class=&#34;hljs-keyword&#34;&gt;else&lt;/span&gt;&lt;br&gt;                attrs = attr.scan(&lt;span class=&#34;hljs-regexp&#34;&gt;/url\=\&amp;quot;(.*)\&amp;quot;\stitle\=\&amp;quot;(.*)\&amp;quot;\simg\=\&amp;quot;(.*)\&amp;quot;/&lt;/span&gt;)&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@url&lt;/span&gt; = attrs[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;][&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;]&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@title&lt;/span&gt; = attrs[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;][&lt;span class=&#34;hljs-number&#34;&gt;1&lt;/span&gt;]&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@img&lt;/span&gt; = attrs[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;][&lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;]&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@firstChar&lt;/span&gt; = &lt;span class=&#34;hljs-variable&#34;&gt;@title&lt;/span&gt;.empty? ? &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;quot;&lt;/span&gt; : (&lt;span class=&#34;hljs-variable&#34;&gt;@title&lt;/span&gt;)[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;].upcase&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@error&lt;/span&gt; = &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br&gt;            &lt;span class=&#34;hljs-keyword&#34;&gt;end&lt;/span&gt;&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;end&lt;/span&gt;&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;render&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;context&lt;/span&gt;)&lt;br&gt;            &lt;span class=&#34;hljs-variable&#34;&gt;@desc&lt;/span&gt; = &lt;span class=&#34;hljs-variable language_&#34;&gt;super&lt;/span&gt;&lt;br&gt;            &lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; !&lt;span class=&#34;hljs-variable&#34;&gt;@yid&lt;/span&gt;.&lt;span class=&#34;hljs-literal&#34;&gt;nil&lt;/span&gt;? &amp;amp;&amp;amp; !&lt;span class=&#34;hljs-variable&#34;&gt;@yid&lt;/span&gt;.empty?&lt;br&gt;                &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;lt;p class=&amp;#x27;embed-responsive embed-responsive-16by9&amp;#x27;&amp;gt;&amp;lt;iframe src=&amp;#x27;https://www.youtube.com/embed/&lt;span class=&#34;hljs-subst&#34;&gt;#&amp;#123;&lt;span class=&#34;hljs-variable&#34;&gt;@yid&lt;/span&gt;&amp;#125;&lt;/span&gt;?rel=0&amp;#x27; title=&amp;#x27;YouTube video player&amp;#x27; frameborder=&amp;#x27;0&amp;#x27; allow=&amp;#x27;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&amp;#x27; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&amp;lt;/p&amp;gt;&amp;quot;&lt;/span&gt;&lt;br&gt;            &lt;span class=&#34;hljs-keyword&#34;&gt;elsif&lt;/span&gt; !&lt;span class=&#34;hljs-variable&#34;&gt;@bid&lt;/span&gt;.&lt;span class=&#34;hljs-literal&#34;&gt;nil&lt;/span&gt;? &amp;amp;&amp;amp; !&lt;span class=&#34;hljs-variable&#34;&gt;@bid&lt;/span&gt;.empty?&lt;br&gt;                &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;lt;p class=&amp;#x27;embed-responsive embed-responsive-16by9&amp;#x27; style=&amp;#x27;border-bottom: 1px solid #ddd;&amp;#x27;&amp;gt;&amp;lt;iframe src=&amp;#x27;//player.bilibili.com/player.html?bvid=&lt;span class=&#34;hljs-subst&#34;&gt;#&amp;#123;&lt;span class=&#34;hljs-variable&#34;&gt;@bid&lt;/span&gt;&amp;#125;&lt;/span&gt;&amp;amp;high_quality=1&amp;amp;as_wide=1&amp;#x27; scrolling=&amp;#x27;no&amp;#x27; border=&amp;#x27;0&amp;#x27; frameborder=&amp;#x27;no&amp;#x27; framespacing=&amp;#x27;0&amp;#x27; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&amp;lt;/p&amp;gt;&amp;quot;&lt;/span&gt;&lt;br&gt;            &lt;span class=&#34;hljs-keyword&#34;&gt;else&lt;/span&gt;&lt;br&gt;                &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;lt;p&amp;gt;&amp;lt;a class=&amp;#x27;link-bookmark&amp;#x27; href=&amp;#x27;&lt;span class=&#34;hljs-subst&#34;&gt;#&amp;#123;&lt;span class=&#34;hljs-variable&#34;&gt;@url&lt;/span&gt;&amp;#125;&lt;/span&gt;&amp;#x27; target=&amp;#x27;_blank&amp;#x27;&amp;gt;&amp;lt;span data-bookmark-img=&amp;#x27;&lt;span class=&#34;hljs-subst&#34;&gt;#&amp;#123;&lt;span class=&#34;hljs-variable&#34;&gt;@img&lt;/span&gt;&amp;#125;&lt;/span&gt;&amp;#x27; data-bookmark-title=&amp;#x27;&lt;span class=&#34;hljs-subst&#34;&gt;#&amp;#123;&lt;span class=&#34;hljs-variable&#34;&gt;@firstChar&lt;/span&gt;&amp;#125;&lt;/span&gt;&amp;#x27;&amp;gt;&amp;lt;img src=&amp;#x27;&lt;span class=&#34;hljs-subst&#34;&gt;#&amp;#123;&lt;span class=&#34;hljs-variable&#34;&gt;@img&lt;/span&gt;&amp;#125;&lt;/span&gt;&amp;#x27;/&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;&amp;lt;span&amp;gt;&lt;span class=&#34;hljs-subst&#34;&gt;#&amp;#123;&lt;span class=&#34;hljs-variable&#34;&gt;@title&lt;/span&gt;&amp;#125;&lt;/span&gt;&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;&lt;span class=&#34;hljs-subst&#34;&gt;#&amp;#123;&lt;span class=&#34;hljs-variable&#34;&gt;@desc&lt;/span&gt;&amp;#125;&lt;/span&gt;&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;&lt;span class=&#34;hljs-subst&#34;&gt;#&amp;#123;&lt;span class=&#34;hljs-variable&#34;&gt;@url&lt;/span&gt;&amp;#125;&lt;/span&gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&amp;quot;&lt;/span&gt;&lt;br&gt;            &lt;span class=&#34;hljs-keyword&#34;&gt;end&lt;/span&gt;&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;end&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;end&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&#34;hljs-title class_&#34;&gt;Liquid&lt;/span&gt;&lt;span class=&#34;hljs-symbol&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;hljs-symbol&#34;&gt;:Template&lt;/span&gt;.register_tag(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;render_bookmark&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;hljs-title class_&#34;&gt;Jekyll&lt;/span&gt;&lt;span class=&#34;hljs-symbol&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;hljs-symbol&#34;&gt;:RenderBookMarkBlock&lt;/span&gt;)&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;Hexo에서는 이렇게 작성했습니다(경로는 &lt;code&gt;scripts/liquid.js&lt;/code&gt;):&lt;/p&gt;
&lt;figure class=&#34;highlight javascript&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs javascript&#34;&gt;hexo.&lt;span class=&#34;hljs-property&#34;&gt;extend&lt;/span&gt;.&lt;span class=&#34;hljs-property&#34;&gt;tag&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;register&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;render_bookmark&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;hljs-keyword&#34;&gt;function&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;args, content&lt;/span&gt;) &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; [url, title, img, yid, bid] = args.&lt;span class=&#34;hljs-title function_&#34;&gt;map&lt;/span&gt;(getValue);&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; firstChar = title ? title[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;].&lt;span class=&#34;hljs-title function_&#34;&gt;toUpperCase&lt;/span&gt;() : &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;&amp;#x27;&lt;/span&gt;;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; strip_html = hexo.&lt;span class=&#34;hljs-property&#34;&gt;extend&lt;/span&gt;.&lt;span class=&#34;hljs-property&#34;&gt;helper&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;get&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;strip_html&amp;#x27;&lt;/span&gt;).&lt;span class=&#34;hljs-title function_&#34;&gt;bind&lt;/span&gt;(hexo);&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; trim = hexo.&lt;span class=&#34;hljs-property&#34;&gt;extend&lt;/span&gt;.&lt;span class=&#34;hljs-property&#34;&gt;helper&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;get&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;trim&amp;#x27;&lt;/span&gt;).&lt;span class=&#34;hljs-title function_&#34;&gt;bind&lt;/span&gt;(hexo);&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; (yid) &amp;#123;&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;`&amp;lt;p class=&amp;#x27;embed-responsive embed-responsive-16by9&amp;#x27;&amp;gt;&amp;lt;iframe src=&amp;#x27;https://www.youtube.com/embed/&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;yid&amp;#125;&lt;/span&gt;?rel=0&amp;#x27; title=&amp;#x27;YouTube video player&amp;#x27; frameborder=&amp;#x27;0&amp;#x27; allow=&amp;#x27;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&amp;#x27; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&amp;lt;/p&amp;gt;`&lt;/span&gt;&lt;br&gt;    &amp;#125; &lt;span class=&#34;hljs-keyword&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; (bid) &amp;#123;&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;`&amp;lt;p class=&amp;#x27;embed-responsive embed-responsive-16by9&amp;#x27; style=&amp;#x27;border-bottom: 1px solid #ddd;&amp;#x27;&amp;gt;&amp;lt;iframe src=&amp;#x27;//player.bilibili.com/player.html?bvid=&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;bid&amp;#125;&lt;/span&gt;&amp;amp;high_quality=1&amp;amp;as_wide=1&amp;#x27; scrolling=&amp;#x27;no&amp;#x27; border=&amp;#x27;0&amp;#x27; frameborder=&amp;#x27;no&amp;#x27; framespacing=&amp;#x27;0&amp;#x27; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&amp;lt;/p&amp;gt;`&lt;/span&gt;;&lt;br&gt;    &amp;#125;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;`&amp;lt;p&amp;gt;&amp;lt;a class=&amp;#x27;link-bookmark&amp;#x27; href=&amp;#x27;&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;url&amp;#125;&lt;/span&gt;&amp;#x27; target=&amp;#x27;_blank&amp;#x27;&amp;gt;&amp;lt;span data-bookmark-img=&amp;#x27;&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;img&amp;#125;&lt;/span&gt;&amp;#x27; data-bookmark-title=&amp;#x27;&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;firstChar&amp;#125;&lt;/span&gt;&amp;#x27;&amp;gt;&amp;lt;img src=&amp;#x27;&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;img&amp;#125;&lt;/span&gt;&amp;#x27;/&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;&amp;lt;span&amp;gt; &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;title&amp;#125;&lt;/span&gt;&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt; &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;strip_html(trim(content))&amp;#125;&lt;/span&gt;&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt; &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;url&amp;#125;&lt;/span&gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;`&lt;/span&gt;;&lt;br&gt;&amp;#125;, &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-attr&#34;&gt;ends&lt;/span&gt;: &lt;span class=&#34;hljs-literal&#34;&gt;true&lt;/span&gt;,&lt;br&gt;&amp;#125;);&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h3 id=&#34;permalink-문제&#34;&gt;Permalink 문제&lt;/h3&gt;
&lt;p&gt;이유는 모르겠지만 Hexo에서 &lt;code&gt;:category/:name.html&lt;/code&gt;을 설정했는데도 &lt;code&gt;life/2024-life-xxx.html&lt;/code&gt;(&lt;code&gt;-&lt;/code&gt; 디렉토리 구분 사용)가 생성되었습니다. 예상대로라면 &lt;code&gt;life/xxx.html&lt;/code&gt;이어야 했습니다. 소스 코드 &lt;code&gt;name&lt;/code&gt;를 보면 &lt;code&gt;slug&lt;/code&gt;의 &lt;code&gt;basename&lt;/code&gt;를 사용하지만, &lt;code&gt;slug&lt;/code&gt; 생성 로직은 folder 경로를 기반으로 하여 -를 추가하는 방식이었습니다. 그래서 파일 이름을 수동으로 수정할 수밖에 없었습니다. Jekyll에서는 파일 이름 앞의 날짜 형식(예: &lt;a href=&#34;http://2024-02-12-xxx.md&#34;&gt;2024-02-12-xxx.md&lt;/a&gt;)에서 2024-02-12는 무시되고 title은 바로 xxx가 됩니다. 하지만 Hexo에서는 post에서 읽는 title이 front matter의 title이기 때문에 permalink 주소를 최종 결정하기 위해 filter 플러그인을 작성해야 했습니다:&lt;/p&gt;
&lt;figure class=&#34;highlight javascript&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs javascript&#34;&gt;&lt;span class=&#34;hljs-comment&#34;&gt;/**&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt; * permalink 中的 name 不符合预期，对于 _posts/life/2015/xxx.md 来说，在文档中 :name 表示的是 xxx，但是实际是 life-2015-xxx&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt; */&lt;/span&gt;&lt;br&gt;hexo.&lt;span class=&#34;hljs-property&#34;&gt;extend&lt;/span&gt;.&lt;span class=&#34;hljs-property&#34;&gt;filter&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;register&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;post_permalink&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;hljs-keyword&#34;&gt;function&lt;/span&gt; (&lt;span class=&#34;hljs-params&#34;&gt;data&lt;/span&gt;) &amp;#123;&lt;br&gt;  &lt;span class=&#34;hljs-comment&#34;&gt;// 在这里修改 post.name 的值&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; arr = data.&lt;span class=&#34;hljs-title function_&#34;&gt;split&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;/&amp;#x27;&lt;/span&gt;).&lt;span class=&#34;hljs-title function_&#34;&gt;filter&lt;/span&gt;(&lt;span class=&#34;hljs-title class_&#34;&gt;Boolean&lt;/span&gt;);&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; categories = arr[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;];&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; name = arr[&lt;span class=&#34;hljs-number&#34;&gt;1&lt;/span&gt;];&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;`&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;categories&amp;#125;&lt;/span&gt;/&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;name.split(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;-&amp;#x27;&lt;/span&gt;).filter(&lt;span class=&#34;hljs-built_in&#34;&gt;Boolean&lt;/span&gt;).slice(&lt;span class=&#34;hljs-number&#34;&gt;3&lt;/span&gt;).join(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;-&amp;#x27;&lt;/span&gt;)&amp;#125;&lt;/span&gt;`&lt;/span&gt;;&lt;br&gt;&amp;#125;);&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;또한 permalink는 순수 숫자가 될 수 없고 문자열이어야 합니다:&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/jekyll-2-hexo/147800e8-7a61-8006-a660-e8ecfbe7da9a.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;p&gt;그렇지 않으면 오류가 발생합니다(.endsWith를 보면 왜 오류가 나는지 알 수 있습니다):&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/jekyll-2-hexo/147800e8-7a61-8032-8e51-d6528f277306.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;ejs-템플릿-문법-문제&#34;&gt;EJS 템플릿 문법 문제&lt;/h3&gt;
&lt;p&gt;ejs 템플릿을 중첩할 때 Jekyll의 liquid 문법과 달리 템플릿 내에서 front-matter를 사용자 정의할 수 없습니다. 즉, 템플릿 간의 매개변수 전달은 외부에서 내부로만 가능하고 내부에서 외부로는 불가능합니다. 예를 들어 index의 레이아웃이 page이고 front-matter도 설정했지만 이 front-matter는 page.ejs 템플릿에서 읽을 수 없습니다. 그래서 사용하는 모든 곳에서 매개변수를 전달해야 했습니다.&lt;/p&gt;
&lt;h3 id=&#34;markdown-문법-렌더링-문제&#34;&gt;Markdown 문법 렌더링 문제&lt;/h3&gt;
&lt;p&gt;Hexo의 기본 marked 마크다운 렌더러는 jekyll의 karmarkdown 렌더러와 차이가 있습니다. 전자는 ## h2 (줄바꿈+빈 줄) 단락에서 빈 줄+단락을 무시하는 반면 후자는 그렇지 않습니다.&lt;/p&gt;
&lt;p&gt;큰 문제는 아니지만 이렇게 되면 홈페이지에서 콘텐츠 요약에 공백이 하나 빠져 완전히 일치하지 않게 됩니다. 저는 가능한 한 완전히 일치시키고 싶었습니다. 그래야 SEO 순위가 떨어지지 않고 검색 엔진이 콘텐츠에 큰 변경이 있었다고 판단하지 않을 수 있습니다.&lt;/p&gt;
&lt;p&gt;그래서 markdown-it을 사용해 처리했고, hexo-renderer-markdown-it을 설치했습니다.&lt;/p&gt;
&lt;h3 id=&#34;liquid-정렬-문제&#34;&gt;Liquid 정렬 문제&lt;/h3&gt;
&lt;p&gt;Jekyll의 liquid 정렬은 이렇게 사용했습니다: &lt;code&gt;&amp;#123;&amp;#123; tags | split:&#39;`**`SEPARATOR`**`&#39; | sort &amp;#125;&amp;#125;&lt;/code&gt; :&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/jekyll-2-hexo/0389dcbb-8bb7-413c-a968-4a77a8b76b71.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;p&gt;문제는 Liquid의 정렬에서 sort 부분이 동일한 경우 href 문자열로 정렬된다는 점입니다. 따라서 이를 상속받으면:&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/jekyll-2-hexo/a24e2d73-ab8b-4b5d-a57d-7e0f2f5f0a66.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;mermaid-문법-문제&#34;&gt;Mermaid 문법 문제&lt;/h3&gt;
&lt;p&gt;mermaid는 highlight로 강조 표시할 수 없으며 hexo의 config에서 제외해야 합니다:&lt;/p&gt;
&lt;figure class=&#34;highlight yaml&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs yaml&#34;&gt;&lt;span class=&#34;hljs-attr&#34;&gt;exclude_languages:&lt;/span&gt;&lt;br&gt; &lt;span class=&#34;hljs-bullet&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;mermaid&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h3 id=&#34;markdown에서-ejs-문법-사용-불가-문제&#34;&gt;Markdown에서 EJS 문법 사용 불가 문제&lt;/h3&gt;
&lt;p&gt;일단은 건드리지 않고 우회했습니다.&lt;/p&gt;
&lt;h3 id=&#34;페이지네이션-생성-문제&#34;&gt;페이지네이션 생성 문제&lt;/h3&gt;
&lt;p&gt;hexo-generator-index의 pagination은 page/2, page/3 형식으로 생성되지만 jekyll에서는 page2, page3 형식을 사용합니다. 그래서 해당 플러그인 소스코드를 복사하여 로컬에서 수정했습니다(&lt;code&gt;scripts/pagination.js&lt;/code&gt; 위치):&lt;/p&gt;
&lt;figure class=&#34;highlight javascript&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs javascript&#34;&gt;&lt;span class=&#34;hljs-meta&#34;&gt;&amp;#x27;use strict&amp;#x27;&lt;/span&gt;;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; pagination = &lt;span class=&#34;hljs-built_in&#34;&gt;require&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;hexo-pagination&amp;#x27;&lt;/span&gt;);&lt;br&gt;hexo.&lt;span class=&#34;hljs-property&#34;&gt;config&lt;/span&gt;.&lt;span class=&#34;hljs-property&#34;&gt;index_generator&lt;/span&gt; = &lt;span class=&#34;hljs-title class_&#34;&gt;Object&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;assign&lt;/span&gt;(&lt;br&gt;  &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-attr&#34;&gt;per_page&lt;/span&gt;:&lt;br&gt;      &lt;span class=&#34;hljs-keyword&#34;&gt;typeof&lt;/span&gt; hexo.&lt;span class=&#34;hljs-property&#34;&gt;config&lt;/span&gt;.&lt;span class=&#34;hljs-property&#34;&gt;per_page&lt;/span&gt; === &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;undefined&amp;#x27;&lt;/span&gt; ? &lt;span class=&#34;hljs-number&#34;&gt;10&lt;/span&gt; : hexo.&lt;span class=&#34;hljs-property&#34;&gt;config&lt;/span&gt;.&lt;span class=&#34;hljs-property&#34;&gt;per_page&lt;/span&gt;,&lt;br&gt;    &lt;span class=&#34;hljs-attr&#34;&gt;order_by&lt;/span&gt;: &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;-date&amp;#x27;&lt;/span&gt;,&lt;br&gt;  &amp;#125;,&lt;br&gt;  hexo.&lt;span class=&#34;hljs-property&#34;&gt;config&lt;/span&gt;.&lt;span class=&#34;hljs-property&#34;&gt;index_generator&lt;/span&gt;&lt;br&gt;);&lt;br&gt;hexo.&lt;span class=&#34;hljs-property&#34;&gt;extend&lt;/span&gt;.&lt;span class=&#34;hljs-property&#34;&gt;generator&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;register&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;index&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;hljs-keyword&#34;&gt;function&lt;/span&gt; (&lt;span class=&#34;hljs-params&#34;&gt;locals&lt;/span&gt;) &amp;#123;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; config = &lt;span class=&#34;hljs-variable language_&#34;&gt;this&lt;/span&gt;.&lt;span class=&#34;hljs-property&#34;&gt;config&lt;/span&gt;;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; posts = locals.&lt;span class=&#34;hljs-property&#34;&gt;posts&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;sort&lt;/span&gt;(config.&lt;span class=&#34;hljs-property&#34;&gt;index_generator&lt;/span&gt;.&lt;span class=&#34;hljs-property&#34;&gt;order_by&lt;/span&gt;);&lt;br&gt;  posts.&lt;span class=&#34;hljs-property&#34;&gt;data&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;sort&lt;/span&gt;(&lt;span class=&#34;hljs-function&#34;&gt;(&lt;span class=&#34;hljs-params&#34;&gt;a, b&lt;/span&gt;) =&amp;gt;&lt;/span&gt; (b.&lt;span class=&#34;hljs-property&#34;&gt;sticky&lt;/span&gt; || &lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;) - (a.&lt;span class=&#34;hljs-property&#34;&gt;sticky&lt;/span&gt; || &lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;));&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; paginationDir = config.&lt;span class=&#34;hljs-property&#34;&gt;pagination_dir&lt;/span&gt; || &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;page&amp;#x27;&lt;/span&gt;;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; path = config.&lt;span class=&#34;hljs-property&#34;&gt;index_generator&lt;/span&gt;.&lt;span class=&#34;hljs-property&#34;&gt;path&lt;/span&gt; || &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;&amp;#x27;&lt;/span&gt;;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;pagination&lt;/span&gt;(path, posts, &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-attr&#34;&gt;perPage&lt;/span&gt;: config.&lt;span class=&#34;hljs-property&#34;&gt;index_generator&lt;/span&gt;.&lt;span class=&#34;hljs-property&#34;&gt;per_page&lt;/span&gt;,&lt;br&gt;    &lt;span class=&#34;hljs-attr&#34;&gt;layout&lt;/span&gt;: [&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;index&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;archive&amp;#x27;&lt;/span&gt;],&lt;br&gt;    &lt;span class=&#34;hljs-attr&#34;&gt;format&lt;/span&gt;: paginationDir + &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;%d/&amp;#x27;&lt;/span&gt;,&lt;br&gt;    &lt;span class=&#34;hljs-attr&#34;&gt;data&lt;/span&gt;: &amp;#123;&lt;br&gt;      &lt;span class=&#34;hljs-attr&#34;&gt;__index&lt;/span&gt;: &lt;span class=&#34;hljs-literal&#34;&gt;true&lt;/span&gt;,&lt;br&gt;    &amp;#125;,&lt;br&gt;  &amp;#125;);&lt;br&gt;&amp;#125;);&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;hr /&gt;
&lt;h3 id=&#34;취소선-구현-문제&#34;&gt;취소선 구현 문제&lt;/h3&gt;
&lt;p&gt;기본적으로 취소선은 s 태그를 사용하지만 jekyll은 del 태그를 사용합니다. after_render:html을 이용해 직접 교체했습니다(&lt;code&gt;scripts/tag-del.js&lt;/code&gt; 위치):&lt;/p&gt;
&lt;figure class=&#34;highlight javascript&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs javascript&#34;&gt;hexo.&lt;span class=&#34;hljs-property&#34;&gt;extend&lt;/span&gt;.&lt;span class=&#34;hljs-property&#34;&gt;filter&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;register&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;after_render:html&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;hljs-keyword&#34;&gt;function&lt;/span&gt; (&lt;span class=&#34;hljs-params&#34;&gt;str&lt;/span&gt;) &amp;#123;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; str.&lt;span class=&#34;hljs-title function_&#34;&gt;replace&lt;/span&gt;(&lt;span class=&#34;hljs-regexp&#34;&gt;/&amp;lt;s&amp;gt;/g&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;&amp;lt;del&amp;gt;&amp;#x27;&lt;/span&gt;).&lt;span class=&#34;hljs-title function_&#34;&gt;replace&lt;/span&gt;(&lt;span class=&#34;hljs-regexp&#34;&gt;/&amp;lt;\/s&amp;gt;/g&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;&amp;lt;/del&amp;gt;&amp;#x27;&lt;/span&gt;);&lt;br&gt;&amp;#125;);&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;hr /&gt;
&lt;h3 id=&#34;빌드-후-불필요한-파일-문제&#34;&gt;빌드 후 불필요한 파일 문제&lt;/h3&gt;
&lt;p&gt;일부 파일은 불필요하므로 빌드 후 삭제합니다:&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2025/jekyll-2-hexo/147800e8-7a61-8077-911f-f5d55f484628.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;p&gt;구체적으로 다음 파일들이 해당됩니다:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;categories/*&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;i_dont_wanna_use_default_archives/*&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;i_dont_wanna_use_default_tags/*&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;less/*&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;rss-문제&#34;&gt;RSS 문제&lt;/h3&gt;
&lt;p&gt;Notion Bookmark에서 가져온 콘텐츠를 Notion의 Bookmark처럼 보기 좋게 렌더링하기 위해 커스텀 태그 스타일을 사용했습니다. 하지만 이렇게 하면 Reeder 같은 RSS 리더에서 스타일이 제대로 렌더링되지 않는 문제가 발생했습니다. 그래서 Jekyll에서는 템플릿 문법 처리 함수를 사용해 빌드 시점에 커스텀 스타일을 동적으로 교체하도록 처리했습니다:&lt;/p&gt;
&lt;figure class=&#34;highlight javascript&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs javascript&#34;&gt;&lt;span class=&#34;hljs-variable language_&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;Jekyll&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-variable language_&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;BookmarkFilter&lt;/span&gt;&lt;br&gt;      def &lt;span class=&#34;hljs-title function_&#34;&gt;bookmark_filter&lt;/span&gt;(input)&lt;br&gt;        input.&lt;span class=&#34;hljs-title function_&#34;&gt;gsub&lt;/span&gt;(&lt;span class=&#34;hljs-regexp&#34;&gt;/^\&amp;lt;p\&amp;gt;\&amp;lt;a\s+class=\&amp;quot;link-bookmark\&amp;quot;\shref=(.*)\starget=\&amp;quot;_blank\&amp;quot;\&amp;gt;\&amp;lt;span\&amp;gt;(.*)\&amp;lt;\/span\&amp;gt;\&amp;lt;span\&amp;gt;\&amp;lt;span\&amp;gt;(.*)\&amp;lt;\/span\&amp;gt;\&amp;lt;span\&amp;gt;\n(.*)\n\&amp;lt;\/span\&amp;gt;\&amp;lt;span\&amp;gt;(.*)\&amp;lt;\/span\&amp;gt;\&amp;lt;\/span\&amp;gt;\&amp;lt;\/a\&amp;gt;\&amp;lt;\/p\&amp;gt;$/&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;&amp;lt;p&amp;gt;&amp;lt;a href=\1 target=&amp;quot;_blank&amp;quot;&amp;gt;\3&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&amp;#x27;&lt;/span&gt;);&lt;br&gt;      end&lt;br&gt;    end&lt;br&gt;  end&lt;br&gt;  &lt;br&gt;  &lt;span class=&#34;hljs-title class_&#34;&gt;Liquid&lt;/span&gt;::&lt;span class=&#34;hljs-title class_&#34;&gt;Template&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;register_filter&lt;/span&gt;(&lt;span class=&#34;hljs-title class_&#34;&gt;Jekyll&lt;/span&gt;::&lt;span class=&#34;hljs-title class_&#34;&gt;BookmarkFilter&lt;/span&gt;)&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;Hexo에서는 빌드 후 패치 방식으로 처리했습니다(&lt;code&gt;scripts/rss-gene.js&lt;/code&gt; 위치):&lt;/p&gt;
&lt;figure class=&#34;highlight javascript&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;61&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;62&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;63&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;64&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;65&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;66&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;67&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;68&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;69&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;70&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;71&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;72&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;73&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;74&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;75&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;76&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;77&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;78&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;79&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;80&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;81&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;82&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;83&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;84&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;85&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;86&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;87&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;88&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;89&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;90&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;91&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;92&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;93&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;94&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;95&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;96&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;97&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs javascript&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; fs = &lt;span class=&#34;hljs-built_in&#34;&gt;require&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;fs&amp;#x27;&lt;/span&gt;);&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; path = &lt;span class=&#34;hljs-built_in&#34;&gt;require&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;path&amp;#x27;&lt;/span&gt;);&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; ejs = &lt;span class=&#34;hljs-built_in&#34;&gt;require&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;ejs&amp;#x27;&lt;/span&gt;);&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; rootDate = &lt;span class=&#34;hljs-keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;Date&lt;/span&gt;();&lt;br&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;getDate&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;_date&lt;/span&gt;) &amp;#123;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; date = _date ? &lt;span class=&#34;hljs-keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;Date&lt;/span&gt;(_date) : rootDate;&lt;br&gt;  &lt;span class=&#34;hljs-comment&#34;&gt;// 获取各个部分&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; days = [&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Sun&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Mon&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Tue&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Wed&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Thu&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Fri&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Sat&amp;#x27;&lt;/span&gt;];&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; months = [&lt;br&gt;    &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Jan&amp;#x27;&lt;/span&gt;,&lt;br&gt;    &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Feb&amp;#x27;&lt;/span&gt;,&lt;br&gt;    &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Mar&amp;#x27;&lt;/span&gt;,&lt;br&gt;    &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Apr&amp;#x27;&lt;/span&gt;,&lt;br&gt;    &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;May&amp;#x27;&lt;/span&gt;,&lt;br&gt;    &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Jun&amp;#x27;&lt;/span&gt;,&lt;br&gt;    &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Jul&amp;#x27;&lt;/span&gt;,&lt;br&gt;    &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Aug&amp;#x27;&lt;/span&gt;,&lt;br&gt;    &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Sep&amp;#x27;&lt;/span&gt;,&lt;br&gt;    &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Oct&amp;#x27;&lt;/span&gt;,&lt;br&gt;    &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Nov&amp;#x27;&lt;/span&gt;,&lt;br&gt;    &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Dec&amp;#x27;&lt;/span&gt;,&lt;br&gt;  ];&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; dayName = days[date.&lt;span class=&#34;hljs-title function_&#34;&gt;getDay&lt;/span&gt;()];&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; day = &lt;span class=&#34;hljs-title class_&#34;&gt;String&lt;/span&gt;(date.&lt;span class=&#34;hljs-title function_&#34;&gt;getDate&lt;/span&gt;()).&lt;span class=&#34;hljs-title function_&#34;&gt;padStart&lt;/span&gt;(&lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;0&amp;#x27;&lt;/span&gt;);&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; month = months[date.&lt;span class=&#34;hljs-title function_&#34;&gt;getMonth&lt;/span&gt;()];&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; year = date.&lt;span class=&#34;hljs-title function_&#34;&gt;getFullYear&lt;/span&gt;();&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; hours = &lt;span class=&#34;hljs-title class_&#34;&gt;String&lt;/span&gt;(date.&lt;span class=&#34;hljs-title function_&#34;&gt;getHours&lt;/span&gt;()).&lt;span class=&#34;hljs-title function_&#34;&gt;padStart&lt;/span&gt;(&lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;0&amp;#x27;&lt;/span&gt;);&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; minutes = &lt;span class=&#34;hljs-title class_&#34;&gt;String&lt;/span&gt;(date.&lt;span class=&#34;hljs-title function_&#34;&gt;getMinutes&lt;/span&gt;()).&lt;span class=&#34;hljs-title function_&#34;&gt;padStart&lt;/span&gt;(&lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;0&amp;#x27;&lt;/span&gt;);&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; seconds = &lt;span class=&#34;hljs-title class_&#34;&gt;String&lt;/span&gt;(date.&lt;span class=&#34;hljs-title function_&#34;&gt;getSeconds&lt;/span&gt;()).&lt;span class=&#34;hljs-title function_&#34;&gt;padStart&lt;/span&gt;(&lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;0&amp;#x27;&lt;/span&gt;);&lt;br&gt;&lt;br&gt;  &lt;span class=&#34;hljs-comment&#34;&gt;// 获取时区偏移（以分钟为单位）&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; timezoneOffset = -date.&lt;span class=&#34;hljs-title function_&#34;&gt;getTimezoneOffset&lt;/span&gt;();&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; sign = timezoneOffset &amp;gt;= &lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt; ? &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;+&amp;#x27;&lt;/span&gt; : &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;-&amp;#x27;&lt;/span&gt;;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; offsetHours = &lt;span class=&#34;hljs-title class_&#34;&gt;String&lt;/span&gt;(&lt;br&gt;    &lt;span class=&#34;hljs-title class_&#34;&gt;Math&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;floor&lt;/span&gt;(&lt;span class=&#34;hljs-title class_&#34;&gt;Math&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;abs&lt;/span&gt;(timezoneOffset) / &lt;span class=&#34;hljs-number&#34;&gt;60&lt;/span&gt;)&lt;br&gt;  ).&lt;span class=&#34;hljs-title function_&#34;&gt;padStart&lt;/span&gt;(&lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;0&amp;#x27;&lt;/span&gt;);&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; offsetMinutes = &lt;span class=&#34;hljs-title class_&#34;&gt;String&lt;/span&gt;(&lt;span class=&#34;hljs-title class_&#34;&gt;Math&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;abs&lt;/span&gt;(timezoneOffset) % &lt;span class=&#34;hljs-number&#34;&gt;60&lt;/span&gt;).&lt;span class=&#34;hljs-title function_&#34;&gt;padStart&lt;/span&gt;(&lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;0&amp;#x27;&lt;/span&gt;);&lt;br&gt;&lt;br&gt;  &lt;span class=&#34;hljs-comment&#34;&gt;// 格式化为 RFC 2822 格式的字符串&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;`&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;dayName&amp;#125;&lt;/span&gt;, &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;day&amp;#125;&lt;/span&gt; &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;month&amp;#125;&lt;/span&gt; &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;year&amp;#125;&lt;/span&gt; &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;hours&amp;#125;&lt;/span&gt;:&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;minutes&amp;#125;&lt;/span&gt;:&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;seconds&amp;#125;&lt;/span&gt; &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;sign&amp;#125;&lt;/span&gt;&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;offsetHours&amp;#125;&lt;/span&gt;&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;offsetMinutes&amp;#125;&lt;/span&gt;`&lt;/span&gt;;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;br&gt;hexo.&lt;span class=&#34;hljs-property&#34;&gt;extend&lt;/span&gt;.&lt;span class=&#34;hljs-property&#34;&gt;generator&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;register&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;xml&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;hljs-keyword&#34;&gt;function&lt;/span&gt; (&lt;span class=&#34;hljs-params&#34;&gt;locals&lt;/span&gt;) &amp;#123;&lt;br&gt;  &lt;span class=&#34;hljs-comment&#34;&gt;// 仿照 Liquid 内置的日期格式写法&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-comment&#34;&gt;// 注意如果前面不加这个 \uFEFF 则不会被识别为 xml&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; template =&lt;br&gt;    &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;\uFEFF&amp;#x27;&lt;/span&gt; +&lt;br&gt;    &lt;span class=&#34;hljs-string&#34;&gt;`&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;    &amp;lt;rss version=&amp;quot;2.0&amp;quot; xmlns:atom=&amp;quot;http://www.w3.org/2005/Atom&amp;quot;&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;    &amp;lt;channel&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;        &amp;lt;title&amp;gt;Xheldon Blog&amp;lt;/title&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;        &amp;lt;description&amp;gt;The Answer to Life, the Universe and Everything is...&amp;lt;/description&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;        &amp;lt;link&amp;gt;https://www.xheldon.com&amp;lt;/link&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;        &amp;lt;atom:link href=&amp;quot;https://www.xheldon.com/feed.xml&amp;quot; rel=&amp;quot;self&amp;quot; type=&amp;quot;application/rss+xml&amp;quot; /&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;        &amp;lt;pubDate&amp;gt;&amp;lt;%= getDate() %&amp;gt;&amp;lt;/pubDate&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;        &amp;lt;lastBuildDate&amp;gt;&amp;lt;%= getDate() %&amp;gt;&amp;lt;/lastBuildDate&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;        &amp;lt;generator&amp;gt;Hexo v&amp;lt;%= version %&amp;gt;&amp;lt;/generator&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;        &amp;lt;% for (post of posts.sort((a, b) =&amp;gt; (new Date(b.date).getTime()) - (new Date(a.date).getTime())).slice(0, 10)) &amp;#123; %&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;        &amp;lt;item&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;            &amp;lt;title&amp;gt;&amp;lt;%= post.title %&amp;gt;&amp;lt;/title&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;            &amp;lt;description&amp;gt;&amp;lt;%= bookmark_filter(post.content) %&amp;gt;&amp;lt;/description&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;            &amp;lt;pubDate&amp;gt;&amp;lt;%= getDate(post.date) %&amp;gt;&amp;lt;/pubDate&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;            &amp;lt;link&amp;gt;&amp;lt;%= post.permalink %&amp;gt;&amp;lt;/link&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;            &amp;lt;guid isPermaLink=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;%= post.permalink %&amp;gt;&amp;lt;/guid&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;            &amp;lt;% for (tag of post.tags.data) &amp;#123; %&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;                &amp;lt;category&amp;gt;&amp;lt;%= tag.name %&amp;gt;&amp;lt;/category&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;            &amp;lt;% &amp;#125; %&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;            &amp;lt;% for (cat of post.categories.data) &amp;#123; %&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;                &amp;lt;category&amp;gt;&amp;lt;%- escape_html(cat.name) %&amp;gt;&amp;lt;/category&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;            &amp;lt;% &amp;#125; %&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;        &amp;lt;/item&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;        &amp;lt;% &amp;#125; %&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;    &amp;lt;/channel&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-string&#34;&gt;    &amp;lt;/rss&amp;gt;`&lt;/span&gt;;&lt;br&gt;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; bookmark_filter = hexo.&lt;span class=&#34;hljs-property&#34;&gt;extend&lt;/span&gt;.&lt;span class=&#34;hljs-property&#34;&gt;helper&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;get&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;bookmark_filter&amp;#x27;&lt;/span&gt;).&lt;span class=&#34;hljs-title function_&#34;&gt;bind&lt;/span&gt;(hexo);&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; escape_html = hexo.&lt;span class=&#34;hljs-property&#34;&gt;extend&lt;/span&gt;.&lt;span class=&#34;hljs-property&#34;&gt;helper&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;get&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;escape_html&amp;#x27;&lt;/span&gt;).&lt;span class=&#34;hljs-title function_&#34;&gt;bind&lt;/span&gt;(hexo);&lt;br&gt;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; data = &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-attr&#34;&gt;posts&lt;/span&gt;: locals.&lt;span class=&#34;hljs-property&#34;&gt;posts&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;toArray&lt;/span&gt;(),&lt;br&gt;    getDate,&lt;br&gt;    &lt;span class=&#34;hljs-attr&#34;&gt;version&lt;/span&gt;: hexo.&lt;span class=&#34;hljs-property&#34;&gt;version&lt;/span&gt;,&lt;br&gt;    escape_html,&lt;br&gt;    bookmark_filter,&lt;br&gt;  &amp;#125;;&lt;br&gt;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; jsonContent = ejs.&lt;span class=&#34;hljs-title function_&#34;&gt;render&lt;/span&gt;(template, data);&lt;br&gt;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; outputPath = path.&lt;span class=&#34;hljs-title function_&#34;&gt;join&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;source/_posts&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;feed.xml&amp;#x27;&lt;/span&gt;);&lt;br&gt;  fs.&lt;span class=&#34;hljs-title function_&#34;&gt;writeFileSync&lt;/span&gt;(outputPath, jsonContent, &amp;#123; &lt;span class=&#34;hljs-attr&#34;&gt;encoding&lt;/span&gt;: &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;utf8&amp;#x27;&lt;/span&gt; &amp;#125;);&lt;br&gt;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-attr&#34;&gt;path&lt;/span&gt;: &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;feed.xml&amp;#x27;&lt;/span&gt;,&lt;br&gt;    &lt;span class=&#34;hljs-attr&#34;&gt;data&lt;/span&gt;: jsonContent,&lt;br&gt;  &amp;#125;;&lt;br&gt;&amp;#125;);&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h3 id=&#34;service-worker-문제&#34;&gt;Service-Worker 문제&lt;/h3&gt;
&lt;p&gt;서비스 워커를 제거했습니다. 매번 빌드할 때마다 페이지의 tags 부분이 반드시 변경되어 html 페이지가 업데이트되기 때문에 수동으로 페이지를 새로 고쳐야 하는 번거로움이 있었기 때문입니다.&lt;/p&gt;
&lt;h3 id=&#34;기타-문제&#34;&gt;기타 문제&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;일부 필요한 파일이 포함되지 않아 빌드 후 추가해야 합니다. 예: ads.txt 등.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;layout&lt;/code&gt; 값이 &lt;code&gt;post&lt;/code&gt; 유형인 글에서 &lt;code&gt;page.path&lt;/code&gt; 값이 &lt;code&gt;/&lt;/code&gt;으로 시작하지 않도록 주의해야 합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;마무리&#34;&gt;마무리&lt;/h2&gt;
&lt;p&gt;기본적으로 이 정도가 주요 변경 사항입니다. BeyondCompare로 줄 단위 비교를 통해 변화를 최소화하면서 마이그레이션할 수 있었습니다.&lt;/p&gt;
</description>
            <pubDate>Tue, 07 Jan 2025 00:00:00 +0000</pubDate>
            <link>https://www.xheldon.com/ko/tech/jekyll-2-hexo.html</link>
            <guid isPermaLink="true">https://www.xheldon.com/ko/tech/jekyll-2-hexo.html</guid>
            
                <category>使用体验</category>
            
                <category>折腾</category>
            
                <category>总结</category>
            
                <category>教程</category>
            
                <category>技巧</category>
            
                <category>JavaScript</category>
            
                <category>技术</category>
            
                <category>框架</category>
            
            
                <category>tech</category>
            
        </item>
        
        <item>
            <title>사이버펑크 2024: 내가 사용 중인 도구들</title>
            <description>&lt;h2 id=&#34;서문&#34;&gt;서문&lt;/h2&gt;
&lt;p&gt;2024년이 끝나가며, WeChat 콘텐츠 부스팅 쿠폰이 곧 만료될 예정인 이 시점에, 올해 제가 일상적으로 개발 과정에서 자주 사용한 다양한 효율성 도구들을 정리해보려 합니다. 소프트웨어와 하드웨어를 모두 포함하며, 참고용으로만 활용해 주시기 바랍니다.&lt;/p&gt;
&lt;p caption=&#39;AI 生成图&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-80d5-99b3-da02d3eacde4.webp&#39; alt=&#39;AI 生成图&#39; title=&#39;AI 生成图&#39;&gt;&lt;/p&gt;
&lt;h2 id=&#34;유료-구독-소프트웨어&#34;&gt;유료 구독 소프트웨어&lt;/h2&gt;
&lt;h3 id=&#34;crusor&#34;&gt;Crusor&lt;/h3&gt;
&lt;p&gt;AI 프로그래밍 분야에서 가장 먼저 언급할 도구는 Cursor입니다.&lt;/p&gt;
&lt;p&gt;Windsurf, Github Copilot과 비교했을 때, 새 코드 생성 능력은 거의 비슷하지만, Cursor는 기존 코드 수정/리팩토링 기능에서 다른 AI 코드 보조 도구들을 한 차원 앞서 있습니다. 전체 과정에서 기본적으로 Tab 키만 누르면, Cursor가 자동으로 리팩토링/수정이 필요한 위치로 커서를 이동시킵니다. Tab을 눌러 수락한 후 계속 Tab을 누르면 다음 수정 위치로 이동합니다. 또한 그들의 Composer/Agent는 AI와의 페어 프로그래밍을 개척한 선구자로, 그 실력은 의심의 여지가 없습니다.&lt;/p&gt;
&lt;p caption=&#39;使用 Cursor 的日常编程界面&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-8053-865f-cbdc451a31ad.webp&#39; alt=&#39;使用 Cursor 的日常编程界面&#39; title=&#39;使用 Cursor 的日常编程界面&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;notion&#34;&gt;Notion&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 제가 사용하는 기능은 무료 버전으로도 충분하며, 유료 버전은 주로 협업용으로 사용됩니다. 따라서 내년에는 구독을 갱신하지 않을 계획입니다.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;저는 노트 앱을 선택할 때 가장 중요하게 여기는 두 가지는 &amp;quot;제한 없는 가져오기/내보내기&amp;quot;와 &amp;quot;크로스 플랫폼 지원&amp;quot;입니다. Notion은 이 두 가지를 모두 충족시킵니다:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;API 기능을 지원하므로 서비스 중단 시 데이터를 내보낼 수 없는 문제가 없습니다(비공개 포맷을 사용하는 Evernote 등 국내 노트 앱들을 지적합니다);&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;API 기반으로 Notion-Flow 브라우저 확장 프로그램을 함께 사용하면 언제 어디서나 Github 기반 정적 블로그를 작성할 수 있습니다;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;database 모듈 기능을 이용해 약간의 서버 코드만 작성하면 네트워크 데이터베이스로 사용할 수 있으며, 제 블로그 페이지에서도 이 방식을 사용하고 있습니다:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a href=&#34;https://www.xheldon.cn/subscribe/&#34; target=&#34;_blank&#34;&gt; 订阅&amp;付费软件 - Xheldon Blog&lt;/a&gt;&lt;/p&gt;
&lt;p caption=&#39;Notion Flow 插件界面&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-80ed-b18b-fd360fdc9098.webp&#39; alt=&#39;Notion Flow 插件界面&#39; title=&#39;Notion Flow 插件界面&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;ticktick&#34;&gt;TickTick&lt;/h3&gt;
&lt;p&gt;이전에는 Things를 구매해 사용했지만, 캘린더와의 연동 과정에서 불편함을 느껴 친구의 추천으로 TickTick으로 전환했습니다.&lt;/p&gt;
&lt;p&gt;캘린더, 작업, 리스트 뷰가 매우 편리하며, 작업 상세 내용에 리치 텍스트를 지원합니다. 이 기능이 아마도 일회성 구매가 아닌 구독 모델을 채택한 이유일 것입니다. 지속적인 서버 저장 비용이 발생하기 때문입니다.&lt;/p&gt;
&lt;p caption=&#39;滴答清单&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-807e-aa46-e617dece267c.webp&#39; alt=&#39;滴答清单&#39; title=&#39;滴答清单&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;1password&#34;&gt;1Password&lt;/h3&gt;
&lt;p&gt;비밀번호 관리 소프트웨어의 확실한 선두주자입니다. 다양한 상황에서 비밀번호 입력 없이 손쉽게 사용할 수 있으며, API 토큰, 신용카드 정보, SSH 정보, 복구 코드 일반 텍스트, WiFi 계정 비밀번호 등 다양한 형식의 저장을 지원합니다.&lt;/p&gt;
&lt;p&gt;시스템과의 깊은 통합은 &amp;quot;비밀번호 입력 없음&amp;quot;을 극대화했습니다. 물론 iOS 플랫폼에서는 시스템 기본 키보드를 사용해야 호출할 수 있으며, 이게 제가 휴대폰에서 어떤 타사 입력법도 사용하지 않는 가장 큰 이유입니다.&lt;/p&gt;
&lt;p&gt;보안 측면에서는 상용 소프트웨어를 더 신뢰합니다.&lt;/p&gt;
&lt;p caption=&#39;1Password&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-80d7-aea0-cecb9be5a8ab.webp&#39; alt=&#39;1Password&#39; title=&#39;1Password&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;youtube-premium&#34;&gt;Youtube Premium&lt;/h3&gt;
&lt;p&gt;이를 효율성 도구로 분류한 이유는, 주로 새로운 것을 배우기 위해 사용하기 때문입니다. 이해가 안 되는 부분이 있으면 검색해보면 관련 내용을 항상 찾을 수 있습니다. 최근에는 AI 관련, 조금 더 오래된 것으로는 Swift 관련 등 다양한 주제가 있으며, 심지어 성적 기술을 공유하는 블로버들도 있습니다(물론 자체 제작 콘텐츠이니 진위는 스스로 판단해야 합니다). 하지만 쓰레기 콘텐츠도 있으니 주의가 필요합니다.&lt;/p&gt;
&lt;p&gt;또한 Premium 회원의 &amp;quot;혜택&amp;quot;으로 YouTube Music도 무료로 사용할 수 있습니다. Apple Music 재생 목록을 가져올 수 있고(타사 서비스 필요), 직접 음악 파일을 업로드할 수도 있습니다. Apple Music과 비교했을 때 YouTube Music의 장점은:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;YouTube Music은 온라인 음악 라이브러리로 업로드한 음악을 임의로 대체하지 않습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;YouTube Music은 최대 10만 곡의 음악을 업로드할 수 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;YouTube Premium 구독을 해지하더라도 업로드한 음악은 계속 사용할 수 있어, 일종의 음악 클라우드 스토리지 역할을 합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p caption=&#39;我的 YouTube Music&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/161800e8-7a61-8089-b694-c40de53f4c15.webp&#39; alt=&#39;我的 YouTube Music&#39; title=&#39;我的 YouTube Music&#39;&gt;&lt;/p&gt;
&lt;p&gt;따라서 소비 다운그레이드 원칙에 따라, 2024년 말 Apple Music 구독이 만료되는 시점에 Apple Music을 해지하고 YouTube Premium을 구독했습니다.&lt;/p&gt;
&lt;p caption=&#39;我的 YouTube 主页&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15f800e8-7a61-801c-bdb9-dade7616324d.webp&#39; alt=&#39;我的 YouTube 主页&#39; title=&#39;我的 YouTube 主页&#39;&gt;&lt;/p&gt;
&lt;h2 id=&#34;일회성-구매-소프트웨어&#34;&gt;일회성 구매 소프트웨어&lt;/h2&gt;
&lt;h3 id=&#34;popclip&#34;&gt;Popclip&lt;/h3&gt;
&lt;p&gt;오른손만으로 0프레임 대기 시간에 검색, 번역 및 다양한 작업을 수행할 수 있으며, 왼손과 키보드가 필요 없습니다.&lt;/p&gt;
&lt;p&gt;제 사용 시나리오는 텍스트를 선택한 후 OpenAI Translator를 호출하고, OpenAI Translator에서 작업을 설정하는 것입니다(아래에서 설명).&lt;/p&gt;
&lt;p caption=&#39;PopClip 主页&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-809a-b407-fedb0270ab0b.webp&#39; alt=&#39;PopClip 主页&#39; title=&#39;PopClip 主页&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;alfred-5&#34;&gt;Alfred 5&lt;/h3&gt;
&lt;p&gt;4 버전에서 일회성 구매 후 바로 업그레이드했습니다. 주로 앱 런처로 사용하며 OpenAITranslate와 연동해 AI에게 질문하는 작업을 수행합니다. 사용자 정의 스크립트를 작성했으며, 호출 방법은 위와 동일합니다.&lt;/p&gt;
&lt;p caption=&#39;Alfred 主界面&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-8013-ba20-fde096aa3bf9.webp&#39; alt=&#39;Alfred 主界面&#39; title=&#39;Alfred 主界面&#39;&gt;&lt;/p&gt;
&lt;p caption=&#39;Alfred 自定义脚本界面&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/161800e8-7a61-800e-a495-cd4e85400abd.webp&#39; alt=&#39;Alfred 自定义脚本界面&#39; title=&#39;Alfred 自定义脚本界面&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;bettermouse&#34;&gt;Bettermouse&lt;/h3&gt;
&lt;p&gt;저는 계속 로지텍 게이밍 마우스를 사용해 왔습니다. 로지텍에는 G HUB 소프트웨어가 있지만, 이 소프트웨어에는 수많은 문제점들이 있습니다:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;작업 표시줄에서 검은색 아이콘만 보이는 것은 여러 흰색 아이콘 사이에서 눈에 띄게 튀어보입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p caption=&#39;特立独行的 G Hub 软件&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-80ea-8bc1-f301acb45cdd.webp&#39; alt=&#39;特立独行的 G Hub 软件&#39; title=&#39;特立独行的 G Hub 软件&#39;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;상호작용 로직이 이상합니다. 소프트웨어를 열려면 작업 표시줄을 클릭한 후 &#39;G HUB 시작’을 눌러야 합니다. 일반적인 소프트웨어는 클릭하면 바로 메인 창이 열리고, 우클릭 시 상호작용 메뉴가 표시됩니다. 최소한 사용자가 상호작용 방식을 선택할 수 있는 설정 옵션을 제공해주면 좋겠습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;작동이 느립니다. 시작하는 데 30초가 걸리며, 가끔 애니메이션 화면에서 멈춰버리는 경우도 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;문구 설명이 이상합니다. 마우스를 온보드에 넣고 싶을 때, 이 옵션을 &#39;켜기’를 클릭해야 할지, 아니면 현재 상태를 유지하면 될지 알려주세요.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p caption=&#39;离谱的文案描述&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-807e-bc82-e1b1057cf7b7.webp&#39; alt=&#39;离谱的文案描述&#39; title=&#39;离谱的文案描述&#39;&gt;&lt;/p&gt;
&lt;p&gt;Bettermouse는 마우스 이동 속도를 커스터마이즈할 수 있을 뿐만 아니라, 부드러운 스크롤, 우클릭 드래그 슬라이드 같은 고급 상호작용도 설정할 수 있습니다(예: Figma에서 페이지를 스크롤하려면 cmd를 누른 상태에서 마우스 왼쪽 버튼을 눌러 이동해야 하지만, Bettermouse를 사용하면 우클릭으로 페이지를 드래그할 수 있습니다).&lt;/p&gt;
&lt;p caption=&#39;BetterMouse 软件设置&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-80d2-b103-c9c785b85f43.webp&#39; alt=&#39;BetterMouse 软件设置&#39; title=&#39;BetterMouse 软件设置&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;magnet&#34;&gt;Magnet&lt;/h3&gt;
&lt;p&gt;여러 년 전에 구입한 창 관리 도구로, Apple ID를 따라 한 대씩 Mac에 설치되었습니다. 단축키로 창의 화면 비율을 조정할 수 있어 아~주 편리합니다. 시중에 비슷한 기능의 소프트웨어가 많지만, 이 제품으로 충분했고 여러 버전을 거치며 기능이 더 풍부해져 교체할 이유가 전혀 없습니다.&lt;/p&gt;
&lt;p caption=&#39;Magnet 软件设置&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-8051-81d6-d51835aaeb7b.webp&#39; alt=&#39;Magnet 软件设置&#39; title=&#39;Magnet 软件设置&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;cleanshotx&#34;&gt;CleanshotX&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 대형 버전 일회성 구매&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;최고의 스크린샷/녹화 도구로, 중간에 유료로 대형 버전을 업그레이드한 적도 있습니다. 가장 좋아하는 기능은: 스크린샷 후 클립보드에 복사하기, 스크린샷 후 바로 화면에 고정하기(가장 자주 사용). 사용 시나리오는 어떤 것을 보고도 기억하지 못할 때(나이가 들수록 뇌의 컨텍스트 용량이 줄어드는 것 같아요) 반복해서 전환해야 하는 번거로움을 없애줍니다.&lt;/p&gt;
&lt;p caption=&#39;置顶截图&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-808f-90e9-c31105514fac.webp&#39; alt=&#39;置顶截图&#39; title=&#39;置顶截图&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;surge-quantumultx&#34;&gt;Surge/QuantumultX&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 대형 버전 일회성 구매&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Clash 커널이 더 이상 업데이트되지 않아 ClashX를 포기하고 친구들과 Mac Surge 5인용을 공동 구매했습니다. 휴대폰에서는 QuantumultX를 사용합니다.&lt;/p&gt;
&lt;p caption=&#39;使用 Surge 当网关&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-80f7-8620-c8391a537a25.webp&#39; alt=&#39;使用 Surge 当网关&#39; title=&#39;使用 Surge 当网关&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;xpic-사심-추천&#34;&gt;xPic(사심 추천)&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 이것은 사심 추천입니다, 제가 개발했어요, 하하&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;작업 중 이미지 압축과 변환이 필요했는데, 온라인 도구들은 기대에 미치지 못하거나 업로드/다운로드가 번거롭거나 개인정보 문제가 있었습니다. 그래서 xPic를 개발했는데, 이미지와 비디오 압축, 형식 변환, 이미지 시퀀스 프레임 합성, 비디오를 Gif로 변환하는 기능을 지원합니다.&lt;/p&gt;
&lt;p&gt;또한 작업 중 SVGA를 사용하게 되어 SVGA 편리한 미리보기 기능도 추가했습니다. 아직 버그가 있고 내부 테스트 중입니다.&lt;/p&gt;
&lt;p caption=&#39;xPic SVGA 预览&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-8072-a02d-f69887f17f73.webp&#39; alt=&#39;xPic SVGA 预览&#39; title=&#39;xPic SVGA 预览&#39;&gt;&lt;/p&gt;
&lt;h2 id=&#34;무료-오픈소스-소프트웨어&#34;&gt;무료/오픈소스 소프트웨어&lt;/h2&gt;
&lt;h3 id=&#34;openai-translator&#34;&gt;OpenAI Translator&lt;/h3&gt;
&lt;p&gt;그 자체로는 추천할 만한 번역 도구는 아니지만, PopClip으로 호출할 수 있습니다(원리는 소프트웨어에 등록된 Unix 소켓에 데이터를 보내는 것으로, 명령어: &lt;code&gt;curl -d &amp;quot;$1&amp;quot; --unix-socket /tmp/openai-translator.sock http://openai-translator&lt;/code&gt;로 호출 가능). 따라서 PopClip과 빠르게 연동해 AI에게 질문할 수 있으며, Alfred도 마찬가지입니다.&lt;/p&gt;
&lt;p caption=&#39;OpenAI Translator 界面&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-8071-9f06-d2abc7033bb2.webp&#39; alt=&#39;OpenAI Translator 界面&#39; title=&#39;OpenAI Translator 界面&#39;&gt;&lt;/p&gt;
&lt;p&gt;새로운 작업을 추가했습니다:&lt;/p&gt;
&lt;p caption=&#39;OpenAI Translator 设置界面&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-80fe-8c06-fdfbe61d1174.webp&#39; alt=&#39;OpenAI Translator 设置界面&#39; title=&#39;OpenAI Translator 设置界面&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;웨이신-입력법-微信输入法&#34;&gt;웨이신 입력법(微信输入法)&lt;/h3&gt;
&lt;p&gt;iPhone에서는 웨이신 입력법을 사용하지 않고, 회사/집의 Mac에서만 사용하며, 공유 단어장이 매우 유용합니다. 이전에는 계속 소우구 입력법을 사용했는데, 텐센트에 인수된 후 의도적으로 기능을 저하시켜 사용자를 이전시키려는 건지 다른 이유인지 모르겠지만, 소우구 입력법은 자주 전혀 입력하지 않은 단어를 추천했습니다. 예를 들어 &lt;code&gt;zhuomian&lt;/code&gt;를 입력하면 첫 번째 후보 단어가 &#39;탁면’이 아니라 &#39;탁자’였습니다.&lt;/p&gt;
&lt;p&gt;또 다른 어색한 점은, 키보드로 싸울 때 &#39;비’라는 글자를 입력한 적이 있는데, 소우구 입력법이 제 취향을 발견한 것처럼, bi의 동음이의어를 여러 번 입력해 &#39;비’라는 후보 단어를 뒤로 이동시키려고 해도 항상 실패했습니다. 또는 당시에는 두 번째 페이지 후보 단어로 이동시켰는데, 시간이 지나면 다시 첫 번째 후보 단어로 돌아왔습니다(이 기간 동안 그 글자를 입력하지 않았습니다).&lt;/p&gt;
&lt;p&gt;또 다른 관련 문제는, 어떤 때든(방금 조정한 후에도) &lt;code&gt;vi&lt;/code&gt;을 입력하면, 입력법이 &lt;code&gt;bi&lt;/code&gt;을 입력하려는 것으로 오해할 수 있습니다(키보드에서 &lt;code&gt;v&lt;/code&gt;와 &lt;code&gt;b&lt;/code&gt;이 붙어 있으니 이런 판단은 자연스럽습니다). 이때 제가 의도적으로 조정한 &lt;code&gt;bi&lt;/code&gt;의 후보 단어 순서를 무시하고 &#39;비’라는 글자를 우선 표시해주는데, 이는 화면 공유 시 매우 어색합니다.&lt;/p&gt;
&lt;p&gt;iPhone에서 사용하지 않는 이유는, 휴대폰에서 1Password의 자동 채우기/인증 코드 자동 채우기를 호출하는 것은 기본 키보드만 가능하기 때문입니다.&lt;/p&gt;
&lt;p caption=&#39;微信输入法设置界面&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-80e8-9e42-c558615951bf.webp&#39; alt=&#39;微信输入法设置界面&#39; title=&#39;微信输入法设置界面&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;orbstack&#34;&gt;OrbStack&lt;/h3&gt;
&lt;p&gt;네이티브 Docker/Linux/k8s 관리 도구로, 인터페이스가 우아하고 조작이 편리하며 메모리 사용량이 적습니다. 무료 버전으로도 충분하며, Docker Desktop과 비교할 수 없을 정도로 뛰어납니다. 공식 비교 자료가 여기 있습니다:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://docs.orbstack.dev/compare/docker-desktop&#34; target=&#34;_blank&#34;&gt; OrbStack vs. Docker Desktop · OrbStack Docs&lt;/a&gt;&lt;/p&gt;
&lt;p caption=&#39;OrbStack 界面&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-8025-bacc-d810a6a81df6.webp&#39; alt=&#39;OrbStack 界面&#39; title=&#39;OrbStack 界面&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;immersive-translate&#34;&gt;Immersive translate&lt;/h3&gt;
&lt;p&gt;AI 번역 플러그인으로, 웹 페이지를 읽을 때 단축키를 사용해 전체 텍스트를 번역하거나 마우스로 가리킨 내용만 번역할 수 있습니다. 사용자 정의 API 제공을 지원하며, Youtube 자막 번역도 가능합니다. 구글의 기계 번역보다 훨씬 뛰어난 성능을 보입니다.&lt;/p&gt;
&lt;p&gt;(PS: 구글이 대형 모델을 개발하면서 왜 자사 문서 전체를 AI로 번역하지 않고 여전히 읽기 어려운 기계 번역을 사용하는지 이해할 수 없습니다.)&lt;/p&gt;
&lt;p caption=&#39;Immersive translate 官网&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-8087-8c69-f9aa44bfb9be.webp&#39; alt=&#39;Immersive translate 官网&#39; title=&#39;Immersive translate 官网&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;warp&#34;&gt;Warp&lt;/h3&gt;
&lt;p&gt;킬러 기능: 텍스트 편집하듯이 터미널에서 bash 명령어를 입력할 수 있습니다.&lt;/p&gt;
&lt;p&gt;iTerm2나 기본 터미널에서 명령어를 입력할 때는 &lt;code&gt;vim&lt;/code&gt; 단축키 등이 사용 불가능한 등 여러 제약이 있습니다. 이 기능이 없다면 저는 절대 Warp를 사용하지 않을 것입니다. AI, 협업 등 제게 전혀 필요 없는 기능이 너무 많기 때문입니다.&lt;/p&gt;
&lt;p caption=&#39;Wrap 界面&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-8003-bec7-c767006042fb.webp&#39; alt=&#39;Wrap 界面&#39; title=&#39;Wrap 界面&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;syntax-highlight&#34;&gt;Syntax Highlight&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 오픈소스&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;스페이스 바를 누르면 파일을 미리 볼 수 있으며, 다양한 형식을 지원해 매우 편리합니다.&lt;/p&gt;
&lt;p caption=&#39;Syntax Highlight 界面&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-8096-af2a-e71c652a4594.webp&#39; alt=&#39;Syntax Highlight 界面&#39; title=&#39;Syntax Highlight 界面&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;monitorcontrol&#34;&gt;MonitorControl&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 오픈소스&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;이름 그대로 모니터 밝기 조절 소프트웨어로, 단축키를 지원해 매우 편리합니다.&lt;/p&gt;
&lt;p caption=&#39;MonitorControl 界面&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-8089-9377-d223950afe0c.webp&#39; alt=&#39;MonitorControl 界面&#39; title=&#39;MonitorControl 界面&#39;&gt;&lt;/p&gt;
&lt;h2 id=&#34;크랙-소프트웨어&#34;&gt;크랙 소프트웨어&lt;/h2&gt;
&lt;h3 id=&#34;tableplus&#34;&gt;TablePlus&lt;/h3&gt;
&lt;p&gt;가끔 서버 데이터베이스에 연결해 확인할 때 사용합니다. 사용 빈도는 매우 낮습니다(제가 전문적인 운영/서버 개발자가 아니기 때문). 인터페이스가 깔끔하고 조작이 간단합니다.&lt;/p&gt;
&lt;p caption=&#39;TablePlus 界面&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-8039-8cd7-c4d3be294f4a.webp&#39; alt=&#39;TablePlus 界面&#39; title=&#39;TablePlus 界面&#39;&gt;&lt;/p&gt;
&lt;h2 id=&#34;하드웨어&#34;&gt;하드웨어&lt;/h2&gt;
&lt;h3 id=&#34;g309-마우스&#34;&gt;G309 마우스&lt;/h3&gt;
&lt;p&gt;네 글자로 요약: 매우 만족합니다.&lt;/p&gt;
&lt;p&gt;청명한 클릭감, 블루투스/무선 듀얼 모드(블루투스는 있는 거나 마찬가지), 무선 연결 시 정확한 반응 속도(보고율), 약간 볼록한 뒷부분, 수직 측면 디자인, 함께 제공된 미끄럼 방지 스티커까지 모든 것이 완벽합니다.&lt;/p&gt;
&lt;p&gt;참고: 이전에 사용한 G403과 G502 유선 마우스는 비대칭 디자인(오른쪽 버튼이 더 낮음)이었는데, G309 대칭 마우스로 바꾸니 오른쪽 버튼이 같은 높이에 있어 중지가 오른쪽 버튼을 실수로 누를 수 있어 약간 신경 써야 했지만, 금방 적응했습니다.&lt;/p&gt;
&lt;p caption=&#39;小手友好的 G309（虽然我是大手）&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-8015-b435-d15e54c1bd29.webp&#39; alt=&#39;小手友好的 G309（虽然我是大手）&#39; title=&#39;小手友好的 G309（虽然我是大手）&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;apple-watch-스트랩&#34;&gt;Apple Watch 스트랩&lt;/h3&gt;
&lt;p&gt;어느 탑오픈 비밀 상점에서 구입한 스트랩으로, 정품이라고 주장하며 개봉 후 반품 불가입니다. 정가의 1/3 ~ 1/2 가격에 구입했는데, 정품과 전혀 차이가 없었습니다. 상점에는 포장 없는 정품/99% 새것/95% 새것 제품도 있는데, 리뷰를 보면 대부분 애플 공장에서 유출된 제품이나 불량품, 반품 제품임을 확인할 수 있었습니다. 저는 중고로 팔기 위해 원포장 제품을 구입했지만, 상점에는 99% 새것인 포장 없는 스트랩도 많이 있습니다.&lt;/p&gt;
&lt;p caption=&#39;各种表带&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-806d-a1b5-eb2218516b61.webp&#39; alt=&#39;各种表带&#39; title=&#39;各种表带&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;airpods-4-노이즈-캔슬링-버전&#34;&gt;AirPods 4 노이즈 캔슬링 버전&lt;/h3&gt;
&lt;p&gt;AirPods Pro 2를 처분했습니다. 착용감이 불편했고, 거의 2년 동안 사용했으니 새 제품으로 바꾸고 싶었기 때문입니다. 그래서 4세대 노이즈 캔슬링 버전을 구입했습니다. 노이즈 캔슬링 효과는 Pro 2세대와 비교해 약간 차이가 있지만, 크게 다르지 않습니다. 유일하게 놀란 점은 이어폰 스템을 위아래로 밀어 볼륨을 조절할 수 없다는 것이었습니다. 그 외에는 모두 만족합니다.&lt;/p&gt;
&lt;p caption=&#39;AirPods 4 降噪版&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-8069-8274-f67ac1401a27.webp&#39; alt=&#39;AirPods 4 降噪版&#39; title=&#39;AirPods 4 降噪版&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;샤오미-ih-전기밥솥-s1&#34;&gt;&lt;strong&gt;샤오미 IH 전기밥솥 S1&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;8년 동안 사용한 미디 전기밥솥을 교체했습니다(아직 고장나지는 않았지만). 사용감은 좋은데, 유일하게 충격적인 점은 죽(흰죽)을 끓이는 데 1시간 30분이 걸린다는 것입니다.&lt;/p&gt;
&lt;p caption=&#39;小米电饭锅&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-804b-9e0a-c48863912c40.webp&#39; alt=&#39;小米电饭锅&#39; title=&#39;小米电饭锅&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;메리다-익스플로러x-플랫바-로드-바이크&#34;&gt;메리다 익스플로러X 플랫바 로드 바이크&lt;/h3&gt;
&lt;p&gt;전자 제품은 아니지만, 프로그래머 생활의 일부로 추가했습니다. 출퇴근용으로 매일 총 20분 정도 타는데 매우 편리합니다.&lt;/p&gt;
&lt;p caption=&#39;探索者 X 在雁栖湖&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/the-tools-i-used-in-2024/15a800e8-7a61-80ba-bf2e-e14e392280c4.webp&#39; alt=&#39;探索者 X 在雁栖湖&#39; title=&#39;探索者 X 在雁栖湖&#39;&gt;&lt;/p&gt;
&lt;h2 id=&#34;후기&#34;&gt;후기&lt;/h2&gt;
&lt;p&gt;여러분의 유용한 도구를 공유해 주세요~&lt;/p&gt;
</description>
            <pubDate>Thu, 12 Dec 2024 00:00:00 +0000</pubDate>
            <link>https://www.xheldon.com/ko/life/the-tools-i-used-in-2024.html</link>
            <guid isPermaLink="true">https://www.xheldon.com/ko/life/the-tools-i-used-in-2024.html</guid>
            
                <category>生活</category>
            
                <category>经验</category>
            
                <category>工具</category>
            
                <category>AI</category>
            
            
                <category>life</category>
            
        </item>
        
        <item>
            <title>TP-Link에서 DHCP를 끈 후에 어떻게 인터넷에 접속하나요</title>
            <description>&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 이미 네트워크 설정을 마쳤기 때문에 일부 페이지는 이미지 대신 텍스트로 대체하겠습니다. 블로그 작성을 위해 네트워크를 다시 리셋하는 것은 번거롭기 때문입니다.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;서론&#34;&gt;서론&lt;/h2&gt;
&lt;p&gt;제가 구매한 TP-Link XDR-5480에는 이상한 설정이 있습니다: 라우터의 DHCP 서비스를 끄면 모든 포트(SPF 변환 포트 포함)가 Lan 포트로 변합니다. 이로 인해 광모뎀 브리지 모드 + 라우터 PPPoE 연결이 불가능해지며, Lan 포트 IP를 지정할 수 없고 모두 상위 라우터(즉, 광모뎀)에서 할당받게 됩니다.&lt;/p&gt;
&lt;p&gt;이 문제는 오래된 TP-Link 라우터에는 없어서 Wan 포트에 영향을 주지 않고 자유롭게 끌 수 있었습니다. 아마도 새 TP-Link 제품 매니저들은 이렇게 하면 Wan 포트가 낭비된다고 생각했고, 새 라우터들은 모두 Wan/Lan 포트 혼용 설계이기 때문에 이 문제가 나타난 것 같습니다.&lt;/p&gt;
&lt;p&gt;며칠 전 M4 칩을 탑재한 Mac mini가 출시되면서 크기가 더 작아지고 전력 소모가 줄어들어, 네트워크 상에서는 다시 &amp;quot;Mac mini를 소프트라우터로 + Mac Surge로 가정 네트워크 관리&amp;quot;라는 유행이 일고 있습니다. 현재 제 네트워크 구조는 매우 안정적이어서 1년 넘게 새로 건드리지 않았지만, 마음이 간질간질해졌습니다. 또 생각해보니 제 Mac Studio도 항상 켜져 있으니 mini처럼 소프트라우터로 사용할 수 있지 않을까 싶었습니다. 더욱이 네티즌들이 Surge(Mac Surge를 말함)가 DHCP 서비스를 관리하면 집 전체에서 무감각하게 마법 같은 인터넷 사용이 가능하고 각 기기의 연결 상태를 실시간으로 “우아하게” 확인할 수 있다고 홍보하더군요. &amp;quot;우아함은 영원하다&amp;quot;는 마음가짐으로 TP-Link 문제를 해결할 방법을 모색하기 시작했습니다.&lt;/p&gt;
&lt;p&gt;인터넷을 검색해보니 다음과 같은 글들이 있었습니다:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cn.v2ex.com/t/1053641&#34; target=&#34;_blank&#34;&gt; tp link 的路由器如何能在关闭 dhcp 服务的同时让 wan 口能够拨号上网？ - V2EX&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;그리고:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.chiphell.com/forum.php?mod=viewthread&amp;tid=2521336&amp;highlight=&amp;mobile=no&#34; target=&#34;_blank&#34;&gt; 请教各位大佬关于华硕和tplink路由器关闭dhcp设置的问题 - 电脑讨论(新) -  Chiphell - 分享与交流用户体验&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;현황&#34;&gt;현황&lt;/h2&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 주의: TP-Link(이하 제 XDR-5480 모델을 말함) 설정 화면에서 라우터 설정을 미리 백업하세요. 실수로 라우터에 접근할 수 없게 되어 초기화해야 할 때 설정이 손실되는 상황을 방지하기 위함입니다. 아래 그림처럼 설정을 내보내면 됩니다. 참고로 이렇게 내보낸 설정에는 포트 설정과 네트워크 설정이 포함되지 않습니다. 이 제품 설계는 네트워크 설정을 잘못 변경하여 초기화한 후에도 잘못된 설정을 가져와 네트워크 접근이 불가능한 상황을 방지해주는데, TP-Link 제품 매니저에게 박수를 보냅니다.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/how-to-pppoe-after-close-tp-link-dhcp/134800e8-7a61-80e6-9e16-eb66a08e2ff7.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;네트워크-토폴로지&#34;&gt;네트워크 토폴로지:&lt;/h3&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/how-to-pppoe-after-close-tp-link-dhcp/134800e8-7a61-8003-89a9-e3f05024c8b5.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;목표&#34;&gt;목표&lt;/h3&gt;
&lt;p&gt;현재 모든 기기의 연결 방식과 네트워크 세그먼트를 변경하지 않은 상태에서(혼날까 봐) Mac Studio의 Surge를 게이트웨이로 사용하여 DHCP 서비스를 관리하는 것입니다.&lt;/p&gt;
&lt;h3 id=&#34;난제&#34;&gt;난제&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;TP-Link의 DHCP 서비스를 끄면 모든 포트(SPF 포트 포함)가 Lan 포트로 변한다는 알림이 나타나며, 포트 주소를 사용자 정의할 수 없게 됩니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;TP-Link의 DHCP 서비스를 끄면 현재 모든 Lan 포트의 IP 주소가 상위 라우터(즉, 광모뎀)에서 할당받는다는 알림이 나타납니다. 이는 순수 AP로 동작하기 때문에 당연하지만, Surge가 광모뎀의 DHCP 서비스를 감지하게 되는 문제가 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Mac Surge가 게이트웨이로 동작할 때 자체적으로 DHCP 서비스를 시작하며, 네트워크상에 다른 DHCP 서비스가 없어야 합니다. 그렇지 않으면 충돌로 인해 연결이 끊길 수 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;2번과 3번을 종합하면, 광모뎀과 라우터의 DHCP 서비스를 모두 꺼야 합니다. 하지만! 이 작업을 하기 전에 광모뎀/라우터에 IP와 Mac 주소를 바인딩해두지 않으면 WiFi로 둘에 접속할 수 없게 되어 라우터와 광모뎀을 초기화해야 합니다!(제가 어떻게 아는지 묻지 마세요) 따라서 광모뎀의 DHCP 서비스를 끌 수 없습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;해결-방법&#34;&gt;해결 방법&lt;/h2&gt;
&lt;p&gt;위의 목표와 난제를 바탕으로 “여러분, 다음 배포 조정을 기록해두세요”:&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/how-to-pppoe-after-close-tp-link-dhcp/134800e8-7a61-80ce-8790-d3cef3ef7d8f.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;단계-1-광모뎀-전원-끄기&#34;&gt;단계 1: 광모뎀 전원 끄기&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; 실제로는 광모뎀과 TP-Link의 연결만 끊으면 됩니다. 여기서 전원을 끄라고 한 것은 실수로 광모뎀 WiFi에 연결되는 것을 방지하기 위함입니다. 광모뎀 WiFi를 &#39;잊어버리기&#39;로 선택했다면 전원을 끄지 않고 TP-Link와의 연결만 끊어도 됩니다.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;먼저 광모뎀을 설정하지 않아 Surge가 DHCP 서비스를 감지하지 못하도록 합니다. 이렇게 하면 Surge가 DHCP 서비스를 시작할 수 없게 됩니다. 또한 이미 광모뎀과 TP-Link의 연결을 끊었기 때문에, 다음 단계에서 TP-Link의 DHCP 서비스를 끄면 TP-Link가 더 이상 상위 라우터의 DHCP 서비스를 감지하여 자신의 LAN 포트 IP를 변경하지 않습니다.&lt;/p&gt;
&lt;h3 id=&#34;단계-2-라우터의-dhcp-서비스-끄기&#34;&gt;단계 2: 라우터의 DHCP 서비스 끄기&lt;/h3&gt;
&lt;p&gt;설정에서 간단히 끄면 됩니다. 여기서는 &amp;quot;끄기&amp;quot;를 선택하고, 설정이 성공한 후에는 &amp;quot;자동&amp;quot;으로 변경할 예정입니다. &amp;quot;자동&amp;quot;으로 변경하는 것은 네트워크 안정성을 위한 것으로, 이에 대해서는 뒤에서 설명하겠습니다.&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/how-to-pppoe-after-close-tp-link-dhcp/134800e8-7a61-804e-a69d-edb6a6eaabc1.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;단계-3-surge-dhcp-활성화&#34;&gt;단계 3: Surge DHCP 활성화&lt;/h3&gt;
&lt;p&gt;이 단계는 Surge의 &amp;quot;개요&amp;quot;와 “장치” 탭에서 모두 찾을 수 있습니다:&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/how-to-pppoe-after-close-tp-link-dhcp/134800e8-7a61-8091-83b9-e144224f35d5.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;p&gt;Surge가 Mac의 IP 주소를 수동으로 고정할 것이라는 메시지가 표시됩니다. 여기서는 &lt;code&gt;192.168.5.100&lt;/code&gt;로 설정되었으며, 구체적인 설정 방법은 온라인에 많이 나와 있으므로 여기서는 자세히 설명하지 않겠습니다.&lt;/p&gt;
&lt;p&gt;주의할 점은 라우터 주소가 &lt;code&gt;192.168.5.1&lt;/code&gt;로 표시되고, 주소 풀이 &lt;code&gt;192.168.5.100&lt;/code&gt;부터 &lt;code&gt;192.168.5.200&lt;/code&gt;까지로 설정된다는 것입니다. 여기서는 주소 풀을 &lt;code&gt;192.168.5.2&lt;/code&gt;부터 &lt;code&gt;192.168.5.254&lt;/code&gt;까지로 변경하여 범위를 더 넓혔습니다.&lt;/p&gt;
&lt;h3 id=&#34;단계-4-새로운-라우터-주소-찾기&#34;&gt;단계 4: 새로운 라우터 주소 찾기&lt;/h3&gt;
&lt;p&gt;Surge 설정을 완료한 후, 라우터의 &lt;code&gt;192.168.5.1&lt;/code&gt;에 접속하면 더 이상 접속할 수 없음을 발견할 것입니다! 당황하지 마세요. 이제 Mac Studio가 DHCP 서비스를 제공하고 있으며, TP-Link 라우터에 새로운 IP 주소를 할당하기 때문입니다(왜 이런 현상이 발생하는지는 저도 모릅니다). &amp;quot;장치&amp;quot;를 클릭한 후 &amp;quot;Hostname Unsuitable for Printing&amp;quot;이라는 이름의 장치를 찾아(반드시 이 이름일 필요는 없음) 주소 표시줄에 입력하면 익숙한 라우터 인터페이스가 다시 나타납니다. 여기서 새로운 라우터 IP 주소는 &lt;code&gt;192.168.5.114&lt;/code&gt;입니다.&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/how-to-pppoe-after-close-tp-link-dhcp/134800e8-7a61-809d-894e-fbe831126b06.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;단계-5-라우터의-lan-포트-수정-및-pppoe-연결&#34;&gt;단계 5: 라우터의 LAN 포트 수정 및 PPPoE 연결&lt;/h3&gt;
&lt;p&gt;이제 인터넷에 연결할 수 없습니다. 광모뎀이 전원이 꺼져 있기 때문입니다! 이 단계에서 광모뎀에 전원을 연결하고 몇 분 기다린 후 라우터 설정 페이지에서 &lt;code&gt;Wan 口已经断开连接&lt;/code&gt;을 클릭해도 아무런 반응이 없을 것입니다.&lt;/p&gt;
&lt;p&gt;하지만 포기하지 마세요! 지금 Studio의 역할이 소프트웨어 라우터를 설정할 때와 유사하다는 것을 떠올려보세요. 당시에는 광모뎀이 PPPoE 연결을 담당하고, 소프트웨어 라우터가 메인 라우터 또는 광모뎀에 연결되어 DHCP 서비스를 제공했습니다. 이제는 PPPoE 장치를 라우터로 옮기고 Studio가 메인 라우터에 연결되어 있으므로, Studio가 소프트웨어 라우터 역할을 하며 DHCP 서비스를 자체적으로 제공하게 됩니다.&lt;/p&gt;
&lt;p&gt;따라서 핵심은 TP-Link의 LAN 포트 IP 주소를 &lt;code&gt;192.168.5.1&lt;/code&gt;로 수동으로 지정하는 것입니다(그렇지 않으면 네트워크상에 &lt;code&gt;192.168.5.1&lt;/code&gt;라는 IP 주소를 가진 장치가 없게 됩니다).&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/how-to-pppoe-after-close-tp-link-dhcp/134800e8-7a61-80e0-8bb3-f208b1f9c874.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;p&gt;LAN 포트 IP를 수동으로 지정한 후(이름은 LAN 포트이지만 광모뎀에 연결되어 PPPoE 연결이 가능합니다!), 인터넷 설정으로 돌아가 &amp;quot;연결&amp;quot;을 클릭합니다. 처음 연결에는 시간이 걸릴 수 있으므로 4~5번 클릭해야 할 수도 있지만, 결국 성공할 것입니다.&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/how-to-pppoe-after-close-tp-link-dhcp/134800e8-7a61-8092-a5bc-cc3e8f728305.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;단계-6-마무리&#34;&gt;단계 6: 마무리&lt;/h3&gt;
&lt;p&gt;이제 설정이 완료되었습니다. 연결된 장치를 마우스 오른쪽 버튼으로 클릭하고 &amp;quot;Surge를 게이트웨이로 설정&amp;quot;을 선택한 후 장치를 네트워크에 다시 연결하면 됩니다:&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/how-to-pppoe-after-close-tp-link-dhcp/134800e8-7a61-8044-b913-da4aafc3219f.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;p&gt;또한 “새 장치는 기본적으로 Surge를 게이트웨이로 사용” 옵션을 설정할 수 있습니다:&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/how-to-pppoe-after-close-tp-link-dhcp/134800e8-7a61-80fc-9142-f5520e0a2756.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;p&gt;이제 모든 장치가 Surge를 게이트웨이로 사용하며, 실시간 트래픽을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;2단계에서 TP-Link의 DHCP 서비스를 끈 설정을 기억하시나요? 이제 TP-Link로 돌아가 DHCP 서비스를 &amp;quot;자동&amp;quot;으로 변경할 수 있습니다. 이는 만약을 대비한 조치로, &lt;strong&gt;Surge의 DHCP 서비스가 중단되거나 Mac이 고장 나더라도 가족과 장치들이 여전히 인터넷에 접속할 수 있도록 하기 위함입니다. 이렇게 하면 TP-Link가 자동으로 LAN의 DHCP 서비스를 감지하여, 없으면 자신의 DHCP를 활성화하고 있으면 비활성화합니다:&lt;/strong&gt;&lt;/p&gt;
&lt;p caption=&#39;&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2024/how-to-pppoe-after-close-tp-link-dhcp/134800e8-7a61-805c-98c5-e3895ef7dfe0.webp&#39; alt=&#39;&#39; title=&#39;&#39;&gt;&lt;/p&gt;
&lt;p&gt;참고: &amp;quot;끄기&amp;quot;를 선택하고 저장하면 라우터 주소가 다시 &lt;code&gt;192.168.5.114&lt;/code&gt;로 변경됩니다. 또한 수정할 수 없게 됩니다(광모뎀에 연결되어 있고 DHCP를 껐기 때문에 위에서 언급한 문제 1과 2가 발생함). 이 경우 처음부터(광모뎀 전원 끄기) 과정을 다시 진행해야 합니다.&lt;/p&gt;
&lt;h2 id=&#34;마치며&#34;&gt;마치며&lt;/h2&gt;
&lt;p&gt;이로써 모든 과정이 끝났습니다. 이 과정에서 몇 가지 이상한 문제를 겪었고, 실패한 시도도 있었습니다. 예를 들어, 왜 광모뎀 전원을 끈 상태에서 라우터를 설정한 후 다시 켜는 방법을 생각해냈는지 아시겠나요?&lt;/p&gt;
&lt;p&gt;모두가 WiFi 자유를 누리시길 바랍니다!&lt;/p&gt;
</description>
            <pubDate>Mon, 04 Nov 2024 00:00:00 +0000</pubDate>
            <link>https://www.xheldon.com/ko/tech/how-to-pppoe-after-close-tp-link-dhcp.html</link>
            <guid isPermaLink="true">https://www.xheldon.com/ko/tech/how-to-pppoe-after-close-tp-link-dhcp.html</guid>
            
                <category>折腾</category>
            
                <category>软路由</category>
            
                <category>网络</category>
            
                <category>路由器</category>
            
                <category>技巧</category>
            
                <category>技术</category>
            
                <category>旁路由</category>
            
                <category>千兆</category>
            
            
                <category>tech</category>
            
        </item>
        
        <item>
            <title>Notion Flow 모듈을 사용하여 변환하는 방법</title>
            <description>&lt;p&gt;일주일 전, 저는 Notion Flow 브라우저 확장 프로그램을 구축했습니다:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://twitter.com/_Xheldon/status/1770466495560294583&#34; target=&#34;_blank&#34;&gt; Xheldon on Twitter / X&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;방금 업데이트된 0.4.1 버전:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://notion-flow.xheldon.com/blog/2024/03/31/0.4.1&#34; target=&#34;_blank&#34;&gt; 0.4.1 | Notion Flow&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;AWS S3 API와 호환되는 자체 구축 OSS 서비스(예: Cloudflare R2)를 지원합니다:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.cloudflare.com/zh-cn/developer-platform/r2/&#34; target=&#34;_blank&#34;&gt; Cloudflare R2 | 零出口费用分布式对象存储 | Cloudflare | Cloudflare&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;이 글에서는 제가 어떻게 이 브라우저 확장을 Github Jekyll 블로그에 활용하고 있는지 간단히 소개하겠습니다.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Jekyll 정적 블로그는 Ruby 기반으로 구축되었으며 플러그인을 지원합니다. 그래서 저는 Liquid 템플릿 언어를 처리하기 위해 몇 가지 플러그인을 직접 작성했습니다(Jekyll 블로그의 플러그인은 &lt;code&gt;_plugins&lt;/code&gt; 디렉토리에 위치하며, ruby 파일을 작성한 후 해당 디렉토리에 넣고 서비스를 재시작하면 됩니다). 내용은 Notion Flow로 변환된 Notion 콘텐츠에서 가져옵니다. 예를 들어 북마크를 처리하는 플러그인 내용은 다음과 같습니다:&lt;/p&gt;


&lt;figure class=&#34;highlight ruby&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs ruby&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;Jekyll&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;RenderBookMarkBlock&lt;/span&gt; &amp;lt; &lt;span class=&#34;hljs-title class_ inherited__&#34;&gt;Liquid::Block&lt;/span&gt;&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;initialize&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;tag_name, attr, tokens&lt;/span&gt;)&lt;br&gt;            &lt;span class=&#34;hljs-variable language_&#34;&gt;super&lt;/span&gt;&lt;br&gt;            &lt;span class=&#34;hljs-comment&#34;&gt;# 普通的链接没有 yid 和 bid&lt;/span&gt;&lt;br&gt;            attrs = attr.scan(&lt;span class=&#34;hljs-regexp&#34;&gt;/url\=\&amp;quot;(.*)\&amp;quot;\stitle\=\&amp;quot;(.*)\&amp;quot;\simg\=\&amp;quot;(.*)\&amp;quot;\syid\=\&amp;quot;(.*)\&amp;quot;\sbid\=\&amp;quot;(.*)\&amp;quot;/&lt;/span&gt;)&lt;br&gt;            &lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; !attrs.empty?&lt;br&gt;              &lt;span class=&#34;hljs-comment&#34;&gt;# 外部的 video 链接，youtube、bilibili（如本文上一篇博客就是）&lt;/span&gt;&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@url&lt;/span&gt; = attrs[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;][&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;]&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@title&lt;/span&gt; = attrs[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;][&lt;span class=&#34;hljs-number&#34;&gt;1&lt;/span&gt;]&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@img&lt;/span&gt; = attrs[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;][&lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;]&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@yid&lt;/span&gt; = attrs[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;][&lt;span class=&#34;hljs-number&#34;&gt;3&lt;/span&gt;]&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@bid&lt;/span&gt; = attrs[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;][&lt;span class=&#34;hljs-number&#34;&gt;4&lt;/span&gt;]&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@firstChar&lt;/span&gt; = (&lt;span class=&#34;hljs-variable&#34;&gt;@title&lt;/span&gt;)[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;].upcase&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@error&lt;/span&gt; = &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br&gt;            &lt;span class=&#34;hljs-keyword&#34;&gt;else&lt;/span&gt;&lt;br&gt;             &lt;span class=&#34;hljs-comment&#34;&gt;# 正常和 notion 一样的 bookmark（如本文上面三个链接就是）&lt;/span&gt;&lt;br&gt;                attrs = attr.scan(&lt;span class=&#34;hljs-regexp&#34;&gt;/url\=\&amp;quot;(.*)\&amp;quot;\stitle\=\&amp;quot;(.*)\&amp;quot;\simg\=\&amp;quot;(.*)\&amp;quot;/&lt;/span&gt;)&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@url&lt;/span&gt; = attrs[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;][&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;]&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@title&lt;/span&gt; = attrs[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;][&lt;span class=&#34;hljs-number&#34;&gt;1&lt;/span&gt;]&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@img&lt;/span&gt; = attrs[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;][&lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;]&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@firstChar&lt;/span&gt; = (&lt;span class=&#34;hljs-variable&#34;&gt;@title&lt;/span&gt;)[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;].upcase&lt;br&gt;                &lt;span class=&#34;hljs-variable&#34;&gt;@error&lt;/span&gt; = &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br&gt;            &lt;span class=&#34;hljs-keyword&#34;&gt;end&lt;/span&gt;&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;end&lt;/span&gt;&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;render&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;context&lt;/span&gt;)&lt;br&gt;            &lt;span class=&#34;hljs-variable&#34;&gt;@desc&lt;/span&gt; = &lt;span class=&#34;hljs-variable language_&#34;&gt;super&lt;/span&gt;&lt;br&gt;            &lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; !&lt;span class=&#34;hljs-variable&#34;&gt;@yid&lt;/span&gt;.&lt;span class=&#34;hljs-literal&#34;&gt;nil&lt;/span&gt;? &amp;amp;&amp;amp; !&lt;span class=&#34;hljs-variable&#34;&gt;@yid&lt;/span&gt;.empty?&lt;br&gt;                &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;lt;p class=&amp;#x27;embed-responsive embed-responsive-16by9&amp;#x27;&amp;gt;&amp;lt;iframe src=&amp;#x27;https://www.youtube.com/embed/&lt;span class=&#34;hljs-subst&#34;&gt;#&amp;#123;&lt;span class=&#34;hljs-variable&#34;&gt;@yid&lt;/span&gt;&amp;#125;&lt;/span&gt;?rel=0&amp;#x27; title=&amp;#x27;YouTube video player&amp;#x27; frameborder=&amp;#x27;0&amp;#x27; allow=&amp;#x27;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&amp;#x27; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&amp;lt;/p&amp;gt;&amp;quot;&lt;/span&gt;&lt;br&gt;            &lt;span class=&#34;hljs-keyword&#34;&gt;elsif&lt;/span&gt; !&lt;span class=&#34;hljs-variable&#34;&gt;@bid&lt;/span&gt;.&lt;span class=&#34;hljs-literal&#34;&gt;nil&lt;/span&gt;? &amp;amp;&amp;amp; !&lt;span class=&#34;hljs-variable&#34;&gt;@bid&lt;/span&gt;.empty?&lt;br&gt;                &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;lt;p class=&amp;#x27;embed-responsive embed-responsive-16by9&amp;#x27; style=&amp;#x27;border-bottom: 1px solid #ddd;&amp;#x27;&amp;gt;&amp;lt;iframe src=&amp;#x27;//player.bilibili.com/player.html?bvid=&lt;span class=&#34;hljs-subst&#34;&gt;#&amp;#123;&lt;span class=&#34;hljs-variable&#34;&gt;@bid&lt;/span&gt;&amp;#125;&lt;/span&gt;&amp;amp;high_quality=1&amp;amp;as_wide=1&amp;#x27; scrolling=&amp;#x27;no&amp;#x27; border=&amp;#x27;0&amp;#x27; frameborder=&amp;#x27;no&amp;#x27; framespacing=&amp;#x27;0&amp;#x27; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&amp;lt;/p&amp;gt;&amp;quot;&lt;/span&gt;&lt;br&gt;            &lt;span class=&#34;hljs-keyword&#34;&gt;else&lt;/span&gt;&lt;br&gt;                &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;lt;p&amp;gt;&amp;lt;a class=&amp;#x27;link-bookmark&amp;#x27; href=&amp;#x27;&lt;span class=&#34;hljs-subst&#34;&gt;#&amp;#123;&lt;span class=&#34;hljs-variable&#34;&gt;@url&lt;/span&gt;&amp;#125;&lt;/span&gt;&amp;#x27; target=&amp;#x27;_blank&amp;#x27;&amp;gt;&amp;lt;span data-bookmark-img=&amp;#x27;&lt;span class=&#34;hljs-subst&#34;&gt;#&amp;#123;&lt;span class=&#34;hljs-variable&#34;&gt;@img&lt;/span&gt;&amp;#125;&lt;/span&gt;&amp;#x27; data-bookmark-title=&amp;#x27;&lt;span class=&#34;hljs-subst&#34;&gt;#&amp;#123;&lt;span class=&#34;hljs-variable&#34;&gt;@firstChar&lt;/span&gt;&amp;#125;&lt;/span&gt;&amp;#x27;&amp;gt;&amp;lt;img src=&amp;#x27;&lt;span class=&#34;hljs-subst&#34;&gt;#&amp;#123;&lt;span class=&#34;hljs-variable&#34;&gt;@img&lt;/span&gt;&amp;#125;&lt;/span&gt;&amp;#x27;/&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;&amp;lt;span&amp;gt;&lt;span class=&#34;hljs-subst&#34;&gt;#&amp;#123;&lt;span class=&#34;hljs-variable&#34;&gt;@title&lt;/span&gt;&amp;#125;&lt;/span&gt;&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;&lt;span class=&#34;hljs-subst&#34;&gt;#&amp;#123;&lt;span class=&#34;hljs-variable&#34;&gt;@desc&lt;/span&gt;&amp;#125;&lt;/span&gt;&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;&lt;span class=&#34;hljs-subst&#34;&gt;#&amp;#123;&lt;span class=&#34;hljs-variable&#34;&gt;@url&lt;/span&gt;&amp;#125;&lt;/span&gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&amp;quot;&lt;/span&gt;&lt;br&gt;            &lt;span class=&#34;hljs-keyword&#34;&gt;end&lt;/span&gt;&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;end&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;end&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;# 上传的 video&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;Jekyll&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;hljs-title class_&#34;&gt;RenderVideoBlock&lt;/span&gt; &amp;lt; &lt;span class=&#34;hljs-title class_ inherited__&#34;&gt;Liquid::Block&lt;/span&gt;&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;initialize&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;tag_name, attr, tokens&lt;/span&gt;)&lt;br&gt;            &lt;span class=&#34;hljs-variable language_&#34;&gt;super&lt;/span&gt;&lt;br&gt;            attrs = attr.scan(&lt;span class=&#34;hljs-regexp&#34;&gt;/caption\=\&amp;quot;(.*)\&amp;quot;\simg\=\&amp;quot;(.*)\&amp;quot;\ssuffix\=\&amp;quot;(.*)\&amp;quot;/&lt;/span&gt;)&lt;br&gt;            &lt;span class=&#34;hljs-variable&#34;&gt;@caption&lt;/span&gt; = attrs[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;][&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;]&lt;br&gt;            &lt;span class=&#34;hljs-variable&#34;&gt;@img&lt;/span&gt; = attrs[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;][&lt;span class=&#34;hljs-number&#34;&gt;1&lt;/span&gt;]&lt;br&gt;            &lt;span class=&#34;hljs-variable&#34;&gt;@suffix&lt;/span&gt; = attrs[&lt;span class=&#34;hljs-number&#34;&gt;0&lt;/span&gt;][&lt;span class=&#34;hljs-number&#34;&gt;2&lt;/span&gt;]&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;end&lt;/span&gt;&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;render&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;context&lt;/span&gt;)&lt;br&gt;            text = &lt;span class=&#34;hljs-variable language_&#34;&gt;super&lt;/span&gt;&lt;br&gt;            &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;lt;p caption=&amp;#x27;&lt;span class=&#34;hljs-subst&#34;&gt;#&amp;#123;&lt;span class=&#34;hljs-variable&#34;&gt;@caption&lt;/span&gt;&amp;#125;&lt;/span&gt;&amp;#x27;&amp;gt;&amp;lt;video controls muted&amp;gt;&amp;lt;source src=&amp;#x27;&lt;span class=&#34;hljs-subst&#34;&gt;#&amp;#123;&lt;span class=&#34;hljs-variable&#34;&gt;@img&lt;/span&gt;&amp;#125;&lt;/span&gt;&amp;#x27; type=&amp;#x27;video/&lt;span class=&#34;hljs-subst&#34;&gt;#&amp;#123;&lt;span class=&#34;hljs-variable&#34;&gt;@suffix&lt;/span&gt;&amp;#125;&lt;/span&gt;&amp;#x27; /&amp;gt;&amp;lt;/video&amp;gt;&amp;lt;/p&amp;gt;&amp;quot;&lt;/span&gt;&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;end&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;end&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&#34;hljs-title class_&#34;&gt;Liquid&lt;/span&gt;&lt;span class=&#34;hljs-symbol&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;hljs-symbol&#34;&gt;:Template&lt;/span&gt;.register_tag(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;render_bookmark&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;hljs-title class_&#34;&gt;Jekyll&lt;/span&gt;&lt;span class=&#34;hljs-symbol&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;hljs-symbol&#34;&gt;:RenderBookMarkBlock&lt;/span&gt;)&lt;br&gt;&lt;span class=&#34;hljs-title class_&#34;&gt;Liquid&lt;/span&gt;&lt;span class=&#34;hljs-symbol&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;hljs-symbol&#34;&gt;:Template&lt;/span&gt;.register_tag(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;render_video&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;hljs-title class_&#34;&gt;Jekyll&lt;/span&gt;&lt;span class=&#34;hljs-symbol&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;hljs-symbol&#34;&gt;:RenderVideoBlock&lt;/span&gt;)&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;


&lt;p&gt;이 코드의 로직은 Notion의 북마크 모듈 링크가 Youtube나 Bilibili인 경우 임베드 비디오 HTML(iframe)로 변환하고, 그렇지 않으면 Notion 북마크와 유사한 HTML로 변환하는 것입니다(CSS와 함께 구현 필요).&lt;/p&gt;
&lt;p&gt;따라서 저는 Notion Flow를 사용해 Notion 내용을 Markdown 형식으로 변환하는 동시에 북마크 등 모듈의 변환 규칙을 커스터마이징하여 블로그에서 Youtube, Bilibili 및 Notion과 같은 북마크 스타일 콘텐츠를 표시할 수 있도록 했습니다. 예시는 다음과 같습니다:&lt;/p&gt;
&lt;figure class=&#34;highlight javascript&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs javascript&#34;&gt;&lt;span class=&#34;hljs-attr&#34;&gt;video&lt;/span&gt;: &lt;span class=&#34;hljs-keyword&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;video&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;block&lt;/span&gt;) &amp;#123;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; (block.&lt;span class=&#34;hljs-property&#34;&gt;type&lt;/span&gt; === &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;file&amp;#x27;&lt;/span&gt;) &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-comment&#34;&gt;// 用户自己上传的 video 文件，用默认 video 插件处理&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;`&amp;#123;% render_video caption=&amp;quot;&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;block.caption&amp;#125;&lt;/span&gt;&amp;quot; img=&amp;quot;&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;block.url&amp;#125;&lt;/span&gt;&amp;quot; suffix=&amp;quot;&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;block.suffix&amp;#125;&lt;/span&gt;&amp;quot; %&amp;#125;\n![&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;block.caption&amp;#125;&lt;/span&gt;](&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;block.url&amp;#125;&lt;/span&gt;)\n&amp;#123;% endrender_video %&amp;#125;\n`&lt;/span&gt;;&lt;br&gt;  &amp;#125; &lt;span class=&#34;hljs-keyword&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; (block.&lt;span class=&#34;hljs-property&#34;&gt;type&lt;/span&gt; === &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;external&amp;#x27;&lt;/span&gt;) &amp;#123;&lt;br&gt;    &lt;span class=&#34;hljs-comment&#34;&gt;// 外部链接、youtube 和 bilibili 视频链接，用 bookmark 处理&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;`&amp;#123;% render_bookmark url=&amp;quot;&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;block.url&amp;#125;&lt;/span&gt;&amp;quot; title=&amp;quot;&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-subst&#34;&gt;&lt;span class=&#34;hljs-string&#34;&gt;      block.caption || &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;&amp;#x27;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-subst&#34;&gt;&lt;span class=&#34;hljs-string&#34;&gt;    &amp;#125;&lt;/span&gt;&amp;quot; img=&amp;quot;&amp;quot; yid=&amp;quot;&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;block.yid&amp;#125;&lt;/span&gt;&amp;quot; bid=&amp;quot;&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-subst&#34;&gt;&lt;span class=&#34;hljs-string&#34;&gt;      block.bid&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-subst&#34;&gt;&lt;span class=&#34;hljs-string&#34;&gt;    &amp;#125;&lt;/span&gt;&amp;quot; %&amp;#125;&amp;#123;% endrender_bookmark %&amp;#125;\n`&lt;/span&gt;;&lt;br&gt;  &amp;#125;&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;여기서 주의할 점은(저는 ruby를 잘 모릅니다) Liquid 템플릿의 태그 사이에는 반드시 텍스트 내용이 있어야 합니다(사용하지 않아도 됨). 그렇지 않으면 ruby 플러그인이 HTML을 생성할 수 없습니다. 즉:&lt;/p&gt;


&lt;figure class=&#34;highlight javascript&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs javascript&#34;&gt;&amp;#123;% render_video  %&amp;#125;这里必须有任意内容！&amp;#123;% endrender_video %&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;


&lt;p&gt;루비 플러그인에서 &lt;code&gt;super&lt;/code&gt; 변수는 &amp;quot;여기에는 반드시 임의의 내용이 있어야 합니다!&amp;quot;라는 문장을 가져옵니다(해당 변수를 사용하지 않아도 됩니다). 이 내용이 없으면 플러그인은 아무것도 반환하지 않습니다.&lt;/p&gt;
</description>
            <pubDate>Sun, 31 Mar 2024 00:00:00 +0000</pubDate>
            <link>https://www.xheldon.com/ko/tech/how-i-use-notion-flow.html</link>
            <guid isPermaLink="true">https://www.xheldon.com/ko/tech/how-i-use-notion-flow.html</guid>
            
                <category>教程</category>
            
                <category>技巧</category>
            
                <category>工作流</category>
            
                <category>技术</category>
            
                <category>Jekyll</category>
            
                <category>Notion</category>
            
            
                <category>tech</category>
            
        </item>
        
        <item>
            <title>【동영상】Notion Flow로 블로그 게시 프로세스 간소화하기</title>
            <description>&lt;p&gt;이 글은 Notion Flow를 사용하여 블로그 게시 프로세스를 간소화하는 방법을 소개하는 동영상을 공유합니다. 자세한 내용은 공식 웹사이트를 참조하세요:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://notion-flow.xheldon.com&#34; target=&#34;_blank&#34;&gt; Notion Flow | Notion Flow&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;유튜브:&lt;/p&gt;
&lt;p class=&#39;embed-responsive embed-responsive-16by9&#39;&gt;&lt;iframe src=&#39;https://www.youtube.com/embed/aPitTcsruhM?rel=0&#39; title=&#39;YouTube video player&#39; frameborder=&#39;0&#39; allow=&#39;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&#39; allowfullscreen&gt;&lt;/iframe&gt;&lt;/p&gt;
&lt;p&gt;빌리빌리:&lt;/p&gt;
&lt;p class=&#39;embed-responsive embed-responsive-16by9&#39; style=&#39;border-bottom: 1px solid #ddd;&#39;&gt;&lt;iframe src=&#39;//player.bilibili.com/player.html?bvid=BV1Ar421h7tM&amp;high_quality=1&amp;as_wide=1&#39; scrolling=&#39;no&#39; border=&#39;0&#39; frameborder=&#39;no&#39; framespacing=&#39;0&#39; allowfullscreen&gt;&lt;/iframe&gt;&lt;/p&gt;</description>
            <pubDate>Thu, 21 Mar 2024 00:00:00 +0000</pubDate>
            <link>https://www.xheldon.com/ko/tech/use-notion-flow.html</link>
            <guid isPermaLink="true">https://www.xheldon.com/ko/tech/use-notion-flow.html</guid>
            
                <category>折腾</category>
            
                <category>教程</category>
            
                <category>工作流</category>
            
                <category>技术</category>
            
                <category>插件</category>
            
                <category>视频</category>
            
                <category>Notion</category>
            
            
                <category>tech</category>
            
        </item>
        
        <item>
            <title>VSCode를 더 효율적으로 사용하는 설정 - 프론트엔드 개발 관점에서</title>
            <description>&lt;p class=&#39;content-callout&#39; style=&#39;background: rgb(241, 241, 239); color: ;&#39;&gt;&lt;span class=&#39;content-callout-icon&#39;&gt;☝🏻&lt;/span&gt;&lt;span&gt;이후에 동영상으로 설명할 계획입니다. 일부 설정의 효과는 시연을 통해야 차이를 확인할 수 있는데, 블로그에 GIF를 만드는 게 귀찮아서요. &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&#34;서문&#34;&gt;서문&lt;/h2&gt;
&lt;p&gt;프론트엔드를 처음 배울 때는 VSCode가 없어 WebStorm을 사용했습니다(당시 학생이어서 불법 복제판을 썼죠). 바로 사용할 수 있는 경험은 정말 매력적이었습니다. 하지만 사무용 컴퓨터 성능 저하, 4K 및 다중 모니터에서의 지연 문제 해결 부재, 동료들의 영향, 결정적으로 ‘설정 동기화’ 기능이 VSCode로 전환하는 마지막 계기가 되었습니다.&lt;/p&gt;
&lt;p&gt;별도의 호버링 검색 상자가 없다는 초기 불편함을 겪은 후, 금세 제가 생각하기에 편리한 설정을 찾아냈습니다. 아래에서 자세히 설명하겠습니다. 더 나은 설정을 가지고 계신 분들은 댓글로 이유와 효과 스크린샷을 첨부해 주시기 바랍니다.&lt;/p&gt;
&lt;p class=&#39;content-callout&#39; style=&#39;background: rgb(241, 241, 239); color: ;&#39;&gt;&lt;span class=&#39;content-callout-icon&#39;&gt;📖&lt;/span&gt;&lt;span&gt;기본 설정은 잘 작동하는 경우를 제외하고 설명하지 않겠습니다. VSCode에서는 대부분의 설정을 변경할 수 있습니다. 예를 들어 &#34;미니맵 오른쪽에 커서 행을 표시할지 여부&#34; 같은 것도 조정 가능합니다. &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&#34;스타일&#34;&gt;스타일&lt;/h2&gt;
&lt;h3 id=&#34;테마-폰트&#34;&gt;테마/폰트&lt;/h3&gt;
&lt;p&gt;테마는 One Dark Pro입니다:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://marketplace.visualstudio.com/items?itemName=zhuangtongfa.Material-theme&#34; target=&#34;_blank&#34;&gt; marketplace.visualstudio.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;폰트는 Fira Code입니다:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/tonsky/FiraCode?tab=readme-ov-file#download--install&#34; target=&#34;_blank&#34;&gt; GitHub - tonsky/FiraCode: Free monospaced font with programming ligatures&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Fira Code는 공식 추천 폰트로, &lt;a href=&#34;https://code.visualstudio.com/docs/getstarted/tips-and-tricks#:~:text=zoomLevel%22%3A%205-,Font%20ligatures,-%22editor.fontFamily%22&#34;&gt;내부적으로도 사용&lt;/a&gt;됩니다.&lt;/p&gt;
&lt;p&gt;Fira Code는 &lt;code&gt;===&lt;/code&gt; 및 &lt;code&gt;&amp;lt;=&lt;/code&gt; 같은 기호 변형을 아름답게 지원합니다(일부는 문자 세트와 변형을 수동으로 활성화해야 함):&lt;/p&gt;
&lt;p caption=&#39;Fira Code 字体&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/3371f847-fdd8-410c-82e1-b1d63cd91035.webp&#39; alt=&#39;Fira Code 字体&#39; title=&#39;Fira Code 字体&#39;&gt;&lt;/p&gt;
&lt;p&gt;많은 분들이 Fira Code의 기본 &lt;code&gt;&amp;amp;&lt;/code&gt; 기호에 익숙하지 않습니다. 이 변형은 설정에서 비활성화할 수 있으며, GitHub 설명을 참조하세요. 제 설정은 다음과 같습니다:&lt;/p&gt;
&lt;figure class=&#34;highlight json&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs json&#34;&gt;&lt;span class=&#34;hljs-punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-attr&#34;&gt;&amp;quot;workbench.colorTheme&amp;quot;&lt;/span&gt;&lt;span class=&#34;hljs-punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;One Dark Pro&amp;quot;&lt;/span&gt;&lt;span class=&#34;hljs-punctuation&#34;&gt;,&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-attr&#34;&gt;&amp;quot;editor.fontFamily&amp;quot;&lt;/span&gt;&lt;span class=&#34;hljs-punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;#x27;Fira Code&amp;#x27;, Monaco, &amp;#x27;Courier New&amp;#x27;, monospace&amp;quot;&lt;/span&gt;&lt;span class=&#34;hljs-punctuation&#34;&gt;,&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-attr&#34;&gt;&amp;quot;editor.fontLigatures&amp;quot;&lt;/span&gt;&lt;span class=&#34;hljs-punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;&amp;#x27;ss01&amp;#x27;, &amp;#x27;ss02&amp;#x27; off, &amp;#x27;ss03&amp;#x27;, &amp;#x27;ss04&amp;#x27;, &amp;#x27;ss05&amp;#x27;, &amp;#x27;ss07&amp;#x27;, &amp;#x27;cv29&amp;#x27;, &amp;#x27;cv28&amp;#x27;, &amp;#x27;cv13&amp;#x27;&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;줄 간격은 1.4, 글자 크기는 13입니다.&lt;/p&gt;
&lt;h2 id=&#34;편집기&#34;&gt;편집기&lt;/h2&gt;
&lt;p&gt;편집기 설정이 가장 중요합니다. 좋은 편집기는 코딩 효율을 높이기 위한 것이죠. 하나씩 설명하겠습니다.&lt;/p&gt;
&lt;h3 id=&#34;공백-문자-렌더링&#34;&gt;공백 문자 렌더링&lt;/h3&gt;
&lt;p caption=&#39;Editor: Render Whitespace&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/9175365e-0357-4b2f-abf7-cdf20062b2ca.webp&#39; alt=&#39;Editor: Render Whitespace&#39; title=&#39;Editor: Render Whitespace&#39;&gt;&lt;/p&gt;
&lt;p&gt;저는 기본값인 &#39;selection’을 사용합니다. 즉, 선택 영역에 공백 문자가 있을 때만 표시되며, 그렇지 않으면 보이지 않아 시각적으로 깔끔합니다. &lt;code&gt;boundary&lt;/code&gt; 설정은 항상 표시로, 보기 좋지 않습니다:&lt;/p&gt;
&lt;p caption=&#39;选区渲染空白符号&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/12c4e471-e6fc-4b6d-aed7-61d4db16cd18.webp&#39; alt=&#39;选区渲染空白符号&#39; title=&#39;选区渲染空白符号&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;자동-괄호-쌍-추가-삭제&#34;&gt;자동 괄호 쌍 추가/삭제&lt;/h3&gt;
&lt;p caption=&#39;Auto Closing 设置&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/49d09c46-5456-441a-9f72-fccc3a5d761e.webp&#39; alt=&#39;Auto Closing 设置&#39; title=&#39;Auto Closing 设置&#39;&gt;&lt;/p&gt;
&lt;p&gt;이 설정들은 &lt;code&gt;(&amp;#123;[&lt;/code&gt; 같은 시작 괄호를 입력하면 자동으로 &lt;code&gt;)&amp;#125;]&lt;/code&gt;을 생성해 줍니다. 삭제 설정도 마찬가지 원리입니다. 기본값은 삽입 시 쌍을 만들고, 삭제 시에도 쌍을 같이 삭제합니다.&lt;/p&gt;
&lt;h3 id=&#34;괄호-색상-풀&#34;&gt;괄호 색상(풀)&lt;/h3&gt;
&lt;p caption=&#39;Bracket Pair Colorization&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/899eeaba-8737-4f07-8e22-9480f915fcbc.webp&#39; alt=&#39;Bracket Pair Colorization&#39; title=&#39;Bracket Pair Colorization&#39;&gt;&lt;/p&gt;
&lt;p&gt;첫 번째를 활성화하면 각 괄호에 색상이 부여됩니다(흰색 대신). 두 번째를 켜면 각 괄호 유형마다 독립적인 색상 세트를 가집니다(실제로는 다른 괄호 색상이 반복될 수 있지만, 괄호 유형별 표시 순서에 따라 색상이 지정됩니다—제 이해와 테스트 결과입니다).&lt;/p&gt;
&lt;h3 id=&#34;직사각형-선택-영역&#34;&gt;직사각형 선택 영역&lt;/h3&gt;
&lt;p caption=&#39;Column Selection&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/c64604e0-b420-4845-86e2-bac8d40aaa3d.webp&#39; alt=&#39;Column Selection&#39; title=&#39;Column Selection&#39;&gt;&lt;/p&gt;
&lt;p&gt;기본적으로 위에서 아래로 선택할 때, 행의 시작과 끝을 지나면 전체 행이 선택됩니다:&lt;/p&gt;
&lt;p caption=&#39;默认选中效果&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/d97e73d0-91ae-4e0a-befb-c708ceae1bc0.webp&#39; alt=&#39;默认选中效果&#39; title=&#39;默认选中效果&#39;&gt;&lt;/p&gt;
&lt;p&gt;이 스위치를 켜면 마우스 드래그로 직사각형 영역을 선택할 수 있습니다(코드 위치가 아닌 마우스 위치 기준). JSON 키 같은 여러 줄의 유사한 들여쓰기를 동시에 편집할 때 유용합니다:&lt;/p&gt;
&lt;p caption=&#39;列选择&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/99dcc5e1-65bd-4048-bf8a-b06443bc7745.webp&#39; alt=&#39;列选择&#39; title=&#39;列选择&#39;&gt;&lt;/p&gt;
&lt;p caption=&#39;列选择的一个应用场景&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/85a0ac88-34a6-4843-adfd-ea95f01c1806.gif&#39; alt=&#39;列选择的一个应用场景&#39; title=&#39;列选择的一个应用场景&#39;&gt;&lt;/p&gt;
&lt;p&gt;터미널에서 선택할 때 Opt 키를 누르면 같은 효과가 적용됩니다.&lt;/p&gt;
&lt;h3 id=&#34;복사-시-구문-강조-포함&#34;&gt;복사 시 구문 강조 포함&lt;/h3&gt;
&lt;p caption=&#39;Copy With Syntax Highlighting&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/2e507e99-524a-41c0-978c-42cb1bcbebca.webp&#39; alt=&#39;Copy With Syntax Highlighting&#39; title=&#39;Copy With Syntax Highlighting&#39;&gt;&lt;/p&gt;
&lt;p&gt;일부 리치 텍스트 편집기는 특별한 처리가 없어 VSCode에서 코드를 복사할 때 색상 정보도 함께 복사됩니다. 이 설정으로 색상 없이 내용만 복사할 수 있습니다.&lt;/p&gt;
&lt;h3 id=&#34;드래그-앤-드롭&#34;&gt;드래그 앤 드롭&lt;/h3&gt;
&lt;p caption=&#39;Drag And Drop&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/319dae0d-0369-4469-abd9-ed2e0c36649c.webp&#39; alt=&#39;Drag And Drop&#39; title=&#39;Drag And Drop&#39;&gt;&lt;/p&gt;
&lt;p&gt;저는 코딩할 때 코드 블록 이동을 위해 드래그를 거의 사용하지 않아 비활성화를 권장합니다. 두 번째 설정은 shift 키를 누른 상태에서 파일을 VSCode로 드래그하면 미디어 파일인 경우 파일명만 표시되고, shift 없이 드래그하면 미디어 파일이 열립니다(기본 활성화).&lt;/p&gt;
&lt;h3 id=&#34;빈-선택-영역-시-현재-줄-복사&#34;&gt;빈 선택 영역 시 현재 줄 복사&lt;/h3&gt;
&lt;p caption=&#39;Empty Selection Clipboard&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/32f95652-6320-4347-8c9e-b7f03dbecd79.webp&#39; alt=&#39;Empty Selection Clipboard&#39; title=&#39;Empty Selection Clipboard&#39;&gt;&lt;/p&gt;
&lt;p&gt;선택 영역이 없이 커서만 있을 때 복사하면 현재 줄이 선택됩니다. 현재 줄 복사가 더 쉬워집니다(기본 활성화).&lt;/p&gt;
&lt;h3 id=&#34;자동-접기&#34;&gt;자동 접기&lt;/h3&gt;
&lt;p caption=&#39;Folding&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/96927f79-74ed-4949-b3e0-7cff488269ed.webp&#39; alt=&#39;Folding&#39; title=&#39;Folding&#39;&gt;&lt;/p&gt;
&lt;p&gt;코드 접기 기능은 분명히 필요합니다. 접기 범위를 강조 표시하는 것도 필요합니다(마우스를 해당 줄에 올렸을 때 효과, 현재 줄 하이라이트). 그렇지 않으면 현재 줄이 접혀 있는지 알 수 없습니다. 마지막으로 import 부분 자동 접기는 제 생각에는 필요 없습니다.&lt;/p&gt;
&lt;p&gt;개인적으로 접기 표시는 항상 보이도록 설정하는 것을 선호합니다. 이 기능은 너무 자주 사용되기 때문에, 먼저 마우스를 올려서 어느 줄이 접혀 있는지 확인한 다음 클릭해서 펼치는 것은 효율이 너무 낮습니다. 접힌 부분을 한눈에 확인할 수 있도록 always로 설정하는 것이 좋습니다:&lt;/p&gt;
&lt;p caption=&#39;Show Folding Controls&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/0a382e11-522d-490a-a086-703b291ef90e.webp&#39; alt=&#39;Show Folding Controls&#39; title=&#39;Show Folding Controls&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;괄호-들여쓰기-가이드라인&#34;&gt;괄호/들여쓰기 가이드라인&lt;/h3&gt;
&lt;p caption=&#39;（缩进/括号）参考线&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/b537a5b7-b989-4346-837b-919a4705599c.webp&#39; alt=&#39;（缩进/括号）参考线&#39; title=&#39;（缩进/括号）参考线&#39;&gt;&lt;/p&gt;
&lt;p&gt;아래 그림과 같지만, &#39;들여쓰기 가이드라인’이 무엇인지 테스트해보지 못했습니다. 일단 활성화해 둡니다.&lt;/p&gt;
&lt;p caption=&#39;图中高亮的括号&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/f8b0495e-2bd6-4dd5-9eb1-36a33821f1e8.webp&#39; alt=&#39;图中高亮的括号&#39; title=&#39;图中高亮的括号&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;호버-시-팝업-위치&#34;&gt;호버 시 팝업 위치&lt;/h3&gt;
&lt;p caption=&#39;Hover 位置&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/da26e746-9e8b-4a3a-9b24-2a38abae431d.webp&#39; alt=&#39;Hover 位置&#39; title=&#39;Hover 位置&#39;&gt;&lt;/p&gt;
&lt;p&gt;일반적으로 코드는 위에서 아래로 읽기 때문에, 이 설정은 코드에 마우스를 올리면 팝업이 위쪽에 나타나 내용을 가립니다. 팝업이 사라지도록 마우스를 움직인 다음 다시 나타나게 해야 하므로, 취소하는 것을 권장합니다.&lt;/p&gt;
&lt;p caption=&#39;始终显示提示在下方更合适&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/6df3c907-16b9-4cba-b7f3-daa4a5fd4532.webp&#39; alt=&#39;始终显示提示在下方更合适&#39; title=&#39;始终显示提示在下方更合适&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;호버-시-힌트-표시&#34;&gt;호버 시 힌트 표시&lt;/h3&gt;
&lt;p caption=&#39;消失延迟其实不需要&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/6b18c5da-1193-4e89-b49f-b7775dcdb192.webp&#39; alt=&#39;消失延迟其实不需要&#39; title=&#39;消失延迟其实不需要&#39;&gt;&lt;/p&gt;
&lt;p&gt;마우스를 벗어나는 것은 일반적으로 표시하지 않으려는 것이므로, 바로 0으로 설정합니다.&lt;/p&gt;
&lt;h3 id=&#34;마우스로-글자-크기-조정&#34;&gt;마우스로 글자 크기 조정&lt;/h3&gt;
&lt;p caption=&#39;完全没用的功能…&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/2e05860a-01cb-45ff-9d3b-230d8871ebd6.webp&#39; alt=&#39;完全没用的功能…&#39; title=&#39;完全没用的功能…&#39;&gt;&lt;/p&gt;
&lt;p&gt;자주 실수로 작동하므로 비활성화합니다.&lt;/p&gt;
&lt;h3 id=&#34;에디터-영역-상단-패딩&#34;&gt;에디터 영역 상단 패딩&lt;/h3&gt;
&lt;p caption=&#39;统一视觉间隔&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/07183c4a-3dcc-412d-a7bb-f5167443874d.webp&#39; alt=&#39;统一视觉间隔&#39; title=&#39;统一视觉间隔&#39;&gt;&lt;/p&gt;
&lt;p&gt;저는 2로 설정했습니다. 하단 패딩은 필요 없습니다.&lt;/p&gt;
&lt;p caption=&#39;优雅，永不过时&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/9f1cd71d-f579-44c6-8b23-58f558dfeeaf.webp&#39; alt=&#39;优雅，永不过时&#39; title=&#39;优雅，永不过时&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;스크롤-바&#34;&gt;스크롤 바&lt;/h3&gt;
&lt;p&gt;수평 스크롤 바 너비는 6, 수직은 25로 설정합니다(기본값: 수평 12, 수직 14):&lt;/p&gt;
&lt;p caption=&#39;Scrollbar&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/2b7e8382-836f-4fe4-a7b5-c88e24332838.webp&#39; alt=&#39;Scrollbar&#39; title=&#39;Scrollbar&#39;&gt;&lt;/p&gt;
&lt;p&gt;개인적으로 스크롤 범위를 벗어나는 것을 좋아하지 않습니다. 한 화면에 완전히 표시될 수 있는 내용에 스크롤 바가 생기므로, 마지막 옵션인 Scroll Beyond Last Line은 비활성화합니다.&lt;/p&gt;
&lt;p caption=&#39;滚动条显示信息&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/361fa784-0d98-4055-a56e-58678f0c3a31.webp&#39; alt=&#39;滚动条显示信息&#39; title=&#39;滚动条显示信息&#39;&gt;&lt;/p&gt;
&lt;p&gt;수직 스크롤 바를 20으로 넓게 설정한 이유는 해당 영역에 단순히 스크롤 바뿐만 아니라 세 가지 정보가 표시되기 때문입니다:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;스크롤 바 오른쪽의 밝은 노란색은 에디터 경고 메시지입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;스크롤 바 중간의 어두운 노란색 블록은 검색 결과(전체 검색 및 현재 에디터 검색)와 일치하는 항목입니다. 어두운 노란색 블록은 회색(커서로 선택한 부분 및 유사 내용) 또는 연한 분홍색(커서로 선택한 내용의 선언 부분)일 수도 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;스크롤 바 전체 줄에 걸친 파란색 선은 커서가 위치한 줄입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;스크롤 바 왼쪽의 녹색 부분은 코드 변경 사항입니다. Git을 사용 중이라면 연한 노란색으로 수정된 부분이 표시될 수도 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이 부분의 정보 표시가 매우 풍부하므로 넓게 설정하는 것이 좋습니다.&lt;/p&gt;
&lt;h3 id=&#34;부드러운-스크롤&#34;&gt;부드러운 스크롤&lt;/h3&gt;
&lt;p caption=&#39;动画，优雅&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/190452c5-f985-47b4-b238-000edce28c4b.webp&#39; alt=&#39;动画，优雅&#39; title=&#39;动画，优雅&#39;&gt;&lt;/p&gt;
&lt;p&gt;강력히 권장합니다. 이 옵션을 활성화하면 스크롤할 때 대략 몇 줄을 스크롤했는지 알 수 있으며, 갑작스럽게 이동하여 “어디로 스크롤되었는지 모르는” 상황을 방지할 수 있습니다.&lt;/p&gt;
&lt;h3 id=&#34;스크롤-고정&#34;&gt;스크롤 고정&lt;/h3&gt;
&lt;p caption=&#39;吸顶，好用&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/37741a88-7ba7-481a-9fe5-06ed276af9d3.webp&#39; alt=&#39;吸顶，好用&#39; title=&#39;吸顶，好用&#39;&gt;&lt;/p&gt;
&lt;p&gt;스크롤할 때 현재 화면을 벗어나는 범위를 확인해야 할 수 있으므로 이 옵션을 활성화합니다. 또한 수평 스크롤 시 sticky 함수가 스크롤되어 사라질 수 있으므로, 마지막 옵션은 취소하는 것을 선호합니다.&lt;/p&gt;
&lt;p caption=&#39;左右滚动不跟随&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/b7b7e17c-ac89-4d69-b1c2-648f0e582a40.webp&#39; alt=&#39;左右滚动不跟随&#39; title=&#39;左右滚动不跟随&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;커서&#34;&gt;커서&lt;/h3&gt;
&lt;p caption=&#39;Cursor Blinking&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/5736c4cc-e07c-40f1-ae31-e1c16e458a00.webp&#39; alt=&#39;Cursor Blinking&#39; title=&#39;Cursor Blinking&#39;&gt;&lt;/p&gt;
&lt;p&gt;첫 번째는 커서 깜빡임의 페이드 효과이고, 두 번째는 다른 위치를 클릭할 때 커서가 이전 위치에서 애니메이션으로 이동하는 효과입니다. 이전 편집 위치에 비해 현재 커서 위치가 어디인지 알 수 있어 정보가 더 풍부해집니다.&lt;/p&gt;
&lt;h3 id=&#34;찾기&#34;&gt;찾기&lt;/h3&gt;
&lt;p caption=&#39;编辑器右上角查找小部件&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/eb174538-47bb-44b8-ba84-54df4a37555e.webp&#39; alt=&#39;编辑器右上角查找小部件&#39; title=&#39;编辑器右上角查找小部件&#39;&gt;&lt;/p&gt;
&lt;p&gt;이 옵션은 비활성화하는 것을 권장합니다. 검색 시 비활성화하지 않으면 파일 상단에 불필요한 공간이 생기고 검색 창을 닫을 때 에디터가 흔들리는 현상이 발생할 수 있습니다.&lt;/p&gt;
&lt;p caption=&#39;空白，不优雅&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/83dcf800-6ec9-4a8a-8b49-2e69e419bd72.webp&#39; alt=&#39;空白，不优雅&#39; title=&#39;空白，不优雅&#39;&gt;&lt;/p&gt;
&lt;p&gt;하지만 이 옵션을 활성화하면 에디터 내용이 가려질 수 있으므로 상황에 따라 선택하세요(일반적으로 상단은 import 후의 줄바꿈 내용이므로 가려도 무방합니다).&lt;/p&gt;
&lt;p caption=&#39;没空白，优雅&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/6878a013-d547-4ccb-9792-32e03a97e9d4.webp&#39; alt=&#39;没空白，优雅&#39; title=&#39;没空白，优雅&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;자동-검색-위젯-채우기&#34;&gt;자동 검색 위젯 채우기&lt;/h3&gt;
&lt;p caption=&#39;自动带入，优雅&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/f671a305-d7c8-4ae0-b91c-5aa094bbd3a6.webp&#39; alt=&#39;自动带入，优雅&#39; title=&#39;自动带入，优雅&#39;&gt;&lt;/p&gt;
&lt;p&gt;이 옵션은 비활성화하는 것을 권장합니다. 검색을 자주 사용하는데, 검색 후 특정 내용을 선택하고 다시 검색(선택하지 않은 내용)할 때 에디터가 자동으로 선택한 내용을 검색창에 넣어 이전 검색 내용이 사라지는 경우가 있어 불편합니다.&lt;/p&gt;
&lt;h3 id=&#34;미니맵&#34;&gt;미니맵&lt;/h3&gt;
&lt;p caption=&#39;右侧小地图&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/b8986040-1b86-4bbd-a99b-1b7d551c8b28.webp&#39; alt=&#39;右侧小地图&#39; title=&#39;右侧小地图&#39;&gt;&lt;/p&gt;
&lt;p&gt;에디터 오른쪽의 미니맵은 항상 표시하도록 설정합니다. 미니맵은 현재 편집 위치와 특정 함수/컴포넌트에 대한 상대적 위치를 파악하는 데 도움이 되므로, 미니맵이 스크롤되지 않고 색상 블록만 렌더링되도록 설정합니다. 모든 줄을 렌더링할 필요는 없습니다.&lt;/p&gt;
&lt;h3 id=&#34;제안&#34;&gt;제안&lt;/h3&gt;
&lt;p caption=&#39;建议预览&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/1cbc255e-2784-461d-a723-ce5f325130a2.webp&#39; alt=&#39;建议预览&#39; title=&#39;建议预览&#39;&gt;&lt;/p&gt;
&lt;p&gt;이 옵션은 기본값으로 비활성화하는 것을 권장합니다. Copilot 제안과 혼동될 수 있기 때문입니다. 아래 그림은 Copilot의 제안입니다:&lt;/p&gt;
&lt;p caption=&#39;copilot 建议&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/89587ee7-0329-43e7-9a68-814e55bd8e07.webp&#39; alt=&#39;copilot 建议&#39; title=&#39;copilot 建议&#39;&gt;&lt;/p&gt;
&lt;p&gt;그리고 이것은 미리보기 제안입니다:&lt;/p&gt;
&lt;p caption=&#39;整个一没必要咱就是说&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/26edace2-f059-4f51-a873-47a743946229.webp&#39; alt=&#39;整个一没必要咱就是说&#39; title=&#39;整个一没必要咱就是说&#39;&gt;&lt;/p&gt;
&lt;p caption=&#39;最近建议&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/203628d4-4e0c-4f9a-8289-31ab679750dc.webp&#39; alt=&#39;最近建议&#39; title=&#39;最近建议&#39;&gt;&lt;/p&gt;
&lt;p&gt;이 옵션의 기본값은 first로, 항상 기본적으로 첫 번째 제안을 사용하도록 설정되어 있습니다. 하지만 제가 자주 겪는 문제는 CSS에서 &lt;code&gt;wid&lt;/code&gt;를 입력할 때 &lt;code&gt;width&lt;/code&gt;을 기대하지만, 실제로는 &lt;code&gt;widow&lt;/code&gt;이 제안되는 경우입니다. 저는 이 속성을 사용하지 않지만, 매번 첫 번째로 제안되기 때문에 화살표로 전환해야 하는 번거로움이 있습니다. 따라서 여기서는 &amp;quot;최근 사용&amp;quot;으로 변경할 것을 권장합니다. 이는 입력기의 &amp;quot;동적 조정&amp;quot;과 유사한 개념입니다:&lt;/p&gt;
&lt;p caption=&#39;css 最近建议&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/a1edc147-031b-4347-be78-8c6ec3c71bd7.webp&#39; alt=&#39;css 最近建议&#39; title=&#39;css 最近建议&#39;&gt;&lt;/p&gt;
&lt;h2 id=&#34;작업-영역&#34;&gt;작업 영역&lt;/h2&gt;
&lt;h3 id=&#34;명령-프롬프트&#34;&gt;명령 프롬프트&lt;/h3&gt;
&lt;p caption=&#39;命令建议&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/a62c1989-314d-4675-abbb-cb9f09534480.webp&#39; alt=&#39;命令建议&#39; title=&#39;命令建议&#39;&gt;&lt;/p&gt;
&lt;p&gt;동일한 명령을 반복해서 입력하는 경우가 많기 때문에 명령 기록 목록을 열어보는 것이 유용합니다. 또한 입력 내용을 유지하는 기능도 유용한데, 예를 들어 toggle로 시작하는 명령(예: Toggle Screen Capture Mode)의 경우가 그렇습니다.&lt;/p&gt;
&lt;p&gt;주의할 점은, 입력 후 esc를 눌러 입력창이 사라진 경우, 다음에 다시 열었을 때 입력 내용이 유지되지 않습니다. 명령을 선택하여 실행한 후 다시 열면 이전 입력 내용이 유지됩니다.&lt;/p&gt;
&lt;h3 id=&#34;디렉토리-트리&#34;&gt;디렉토리 트리&lt;/h3&gt;
&lt;p caption=&#39;目录树滚动&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/ff187f9a-5588-40fb-927d-89e701495d94.webp&#39; alt=&#39;目录树滚动&#39; title=&#39;目录树滚动&#39;&gt;&lt;/p&gt;
&lt;p&gt;일반적으로 저는 애니메이션을 활성화합니다. “우아함은 영원하지 않기” 때문입니다. 이 설정은 “설정” 화면의 스크롤에도 영향을 미칩니다(이전에는 편집기 설정에서 부드러운 스크롤을 활성화해도 “설정” 화면과 디렉토리 트리 화면의 스크롤 효과에는 영향을 주지 않았습니다).&lt;/p&gt;
&lt;h3 id=&#34;빠른-열기-기록&#34;&gt;빠른 열기 기록&lt;/h3&gt;
&lt;p caption=&#39;快速打开带入上次记录&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/b4633d53-e671-437c-a4e9-241fe722c953.webp&#39; alt=&#39;快速打开带入上次记录&#39; title=&#39;快速打开带入上次记录&#39;&gt;&lt;/p&gt;
&lt;p&gt;cmd + p를 누르면 quick open 입력창이 나타나며, 기록을 유지하는 기능은 매우 유용합니다. 또 다른 옵션은 포커스를 잃을 때 자동으로 사라지는지 여부인데, 대부분의 경우 자동으로 사라지는 것이 필요하지만 가끔은 그렇지 않을 수도 있습니다. 일단은 기본값인 자동 사라짐으로 유지했습니다.&lt;/p&gt;
&lt;h3 id=&#34;작업-영역-애니메이션-효과-감소&#34;&gt;작업 영역 애니메이션 효과 감소&lt;/h3&gt;
&lt;p caption=&#39;绝不减少动画&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/24d1bc51-76bf-415b-9c79-561bc6eb7caf.webp&#39; alt=&#39;绝不减少动画&#39; title=&#39;绝不减少动画&#39;&gt;&lt;/p&gt;
&lt;p&gt;64G 메모리의 M1 Max를 사용하는 저는 애니메이션을 줄일 필요가 없습니다(기본값은 auto로, 시스템 구성에 따라 자동으로 조정되며, 여러 컴퓨터 간의 설정 동기화 문제에 적합합니다).&lt;/p&gt;
&lt;h3 id=&#34;글꼴-스무딩&#34;&gt;글꼴 스무딩&lt;/h3&gt;
&lt;p caption=&#39;字体平滑&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/093ce91b-5349-48a0-b563-2a44726f3bf2.webp&#39; alt=&#39;字体平滑&#39; title=&#39;字体平滑&#39;&gt;&lt;/p&gt;
&lt;p&gt;이는 CSS의 &lt;code&gt;-webkit-font-smoothing: antialiased;&lt;/code&gt;와 유사합니다. default는 대부분의 non-retina 화면에서 가장 선명한 글꼴을 표시하기 위해 사용됩니다(서브픽셀 레벨). antialiased는 픽셀 레벨 스무딩으로, 글꼴이 더 얇아질 수 있습니다. 다음 이미지를 참조하세요:&lt;/p&gt;
&lt;p caption=&#39;default 设置&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/81378425-4fd6-4954-8d27-5317c822b237.webp&#39; alt=&#39;default 设置&#39; title=&#39;default 设置&#39;&gt;&lt;/p&gt;
&lt;p caption=&#39;antialiased 设置&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/e24ea63a-7536-48bd-8dec-302b335224a9.webp&#39; alt=&#39;antialiased 设置&#39; title=&#39;antialiased 设置&#39;&gt;&lt;/p&gt;
&lt;p&gt;이 설정은 “작업 영역” 섹션에 있지만 편집기 영역에도 영향을 미칩니다. antialiased를 활성화하면 편집기 영역과 작업 영역 모두에서 글꼴이 더 어둡고(대비가 약해짐) 얇아지는 것을 확인할 수 있습니다. 저는 후자를 선호하므로 활성화했습니다.&lt;/p&gt;
&lt;p&gt;참고로, 이 &amp;quot;서브픽셀 레벨&amp;quot;은 픽셀보다 더 작은 레벨을 의미하는 것이 아니라, “픽셀에 미치지 못하는” 레벨을 의미합니다. 즉, 더 낮은 레벨이지 더 높은 레벨이 아닙니다.&lt;/p&gt;
&lt;h3 id=&#34;디렉토리-트리-고정&#34;&gt;디렉토리 트리 고정&lt;/h3&gt;
&lt;p caption=&#39;目录树滚动吸顶&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/ed8074f7-0e31-4a1f-80f8-1618d2264e73.webp&#39; alt=&#39;目录树滚动吸顶&#39; title=&#39;目录树滚动吸顶&#39;&gt;&lt;/p&gt;
&lt;p&gt;매우 유용한 기능으로, 스크롤할 때 현재 경로를 확인할 수 있습니다. 유일한 아쉬운 점은 box-shadow를 추가하면 더 좋을 것 같다는 점입니다. 그렇지 않으면 구분이 잘 안 될 수 있습니다:&lt;/p&gt;
&lt;p caption=&#39;目录树吸顶效果&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/5a7c2102-2730-47a0-b225-738a42d86342.webp&#39; alt=&#39;目录树吸顶效果&#39; title=&#39;目录树吸顶效果&#39;&gt;&lt;/p&gt;
&lt;p&gt;sticky의 최대 레벨도 수정할 수 있으며, 기본값은 7로 충분합니다(편집기 sticky의 기본값은 5입니다).&lt;/p&gt;
&lt;p&gt;참고로, 이 설정은 “설정” 화면에도 동일하게 적용됩니다(원래 설정 화면은 편집기가 아닌 작업 영역에 속함):&lt;/p&gt;
&lt;p caption=&#39;设置项界面也归它管&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/031c3170-cd43-4775-8401-a95e6c474a75.webp&#39; alt=&#39;设置项界面也归它管&#39; title=&#39;设置项界面也归它管&#39;&gt;&lt;/p&gt;
&lt;p&gt;디렉토리 트리의 들여쓰기는 14로 변경했으며, 가이드라인은 항상 표시하도록 설정했습니다. 그렇지 않으면 동일한 레벨의 파일이 너무 많아 찾기 어려울 수 있습니다:&lt;/p&gt;
&lt;p caption=&#39;目录树缩进&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/7a67020e-c5a4-4114-9ac0-ff3afcb5cb61.webp&#39; alt=&#39;目录树缩进&#39; title=&#39;目录树缩进&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;디렉토리-탐색&#34;&gt;디렉토리 탐색&lt;/h3&gt;
&lt;p caption=&#39;目录导航显示 icon&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/eae4d00a-68d6-4c64-bab8-34df8fc7f458.webp&#39; alt=&#39;目录导航显示 icon&#39; title=&#39;目录导航显示 icon&#39;&gt;&lt;/p&gt;
&lt;p&gt;디렉토리 탐색은 필요하지만 파일/폴더 아이콘은 필요하지 않습니다. 이렇게 하면 파일 내의 배열 및 클래스와 명확하게 구분할 수 있어 매우 유용합니다:&lt;/p&gt;
&lt;p caption=&#39;面包屑显示效果&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/57e4fefd-4c76-42cd-b79c-d3d49eb2d1df.webp&#39; alt=&#39;面包屑显示效果&#39; title=&#39;面包屑显示效果&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;수정된-탭&#34;&gt;수정된 탭&lt;/h3&gt;
&lt;p&gt;이와 관련된 여러 옵션이 있습니다. 예를 들어 수정 후 저장되지 않은 파일 상단에 강조선을 표시하는 옵션:&lt;/p&gt;
&lt;p caption=&#39;高亮修改的 tab&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/e4a60c98-ec6f-452d-84ea-e0835ed963bf.webp&#39; alt=&#39;高亮修改的 tab&#39; title=&#39;高亮修改的 tab&#39;&gt;&lt;/p&gt;
&lt;p&gt;기본적으로 점이 표시되지만, 이 옵션을 활성화하면 점과 선이 동시에 표시됩니다. 편집기를 다시 시작하면 상단의 파란색 선만 표시됩니다(버그일 수 있으며, 실제로는 편집기를 다시 시작하지 않아도 적용되어야 합니다).&lt;/p&gt;
&lt;p&gt;효과:&lt;/p&gt;
&lt;p caption=&#39;修改过的 tab 效果&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/aea54cb4-158e-4662-a6b5-285fc56fd836.webp&#39; alt=&#39;修改过的 tab 效果&#39; title=&#39;修改过的 tab 效果&#39;&gt;&lt;/p&gt;
&lt;p&gt;&amp;quot;점&amp;quot;도 탭 공간의 일부를 차지하기 때문에 더 많은 탭 내용 정보를 표시할 수 없게 됩니다. 따라서 이 옵션을 활성화하는 것을 권장합니다.&lt;/p&gt;
&lt;h3 id=&#34;마우스-탐색&#34;&gt;마우스 탐색&lt;/h3&gt;
&lt;p caption=&#39;鼠标前进后退&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/254a8290-8b0a-4ebc-ac36-d453a855719d.webp&#39; alt=&#39;鼠标前进后退&#39; title=&#39;鼠标前进后退&#39;&gt;&lt;/p&gt;
&lt;p&gt;이것은 기본 옵션이지만 설명하자면, 왼쪽 버튼(오른손 사용자의 경우 4, 5 버튼)이 있는 마우스의 경우 직접 탐색에 사용할 수 있어 매우 유용합니다.&lt;/p&gt;
&lt;h3 id=&#34;탭-고정&#34;&gt;탭 고정&lt;/h3&gt;
&lt;p caption=&#39;允许 tab 固定，好用&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/b8c68b52-f2ec-4fb4-ac39-1fd1af2c9ebd.webp&#39; alt=&#39;允许 tab 固定，好用&#39; title=&#39;允许 tab 固定，好用&#39;&gt;&lt;/p&gt;
&lt;p&gt;고정된 탭은 기본적으로 편집기 그룹의 왼쪽에 나타나지만, 별도의 행으로 배치하면 더 직관적이며 고정되지 않은 탭과 구분하기 쉽습니다. 효과는 다음과 같습니다:&lt;/p&gt;
&lt;p caption=&#39;tab 固定效果&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/b6868fe5-65b6-4c6b-bd81-3bd15fada494.webp&#39; alt=&#39;tab 固定效果&#39; title=&#39;tab 固定效果&#39;&gt;&lt;/p&gt;
&lt;p&gt;참고로, 기본적으로 고정된 탭은 마우스 중간 버튼이나 cmd + w로 닫을 수 없습니다(누르면 고정되지 않은 탭이 열리며 고정된 탭이 닫히지 않습니다). 이 동작은 수정할 수 있습니다:&lt;/p&gt;
&lt;p caption=&#39;cmd + w 效果&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/0259919c-dd23-44f5-89e6-026c2140a424.webp&#39; alt=&#39;cmd + w 效果&#39; title=&#39;cmd + w 效果&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;탭-닫기-버튼&#34;&gt;탭 닫기 버튼&lt;/h3&gt;
&lt;p caption=&#39;隐藏关闭按钮&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/c1e66c49-5cab-4d42-9c04-713a6530bb50.webp&#39; alt=&#39;隐藏关闭按钮&#39; title=&#39;隐藏关闭按钮&#39;&gt;&lt;/p&gt;
&lt;p&gt;왼손으로 cmd + w를 사용해 탭을 닫는 습관이 있어 이 옵션을 해제했습니다. 또, 사실 탭을 더블 클릭해 닫는 게 더 편하지만 공식적으로 구현하지 않겠다고 답변했는데, 아래 참고:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/Microsoft/vscode/issues/52628#issuecomment-420887497&#34; target=&#34;_blank&#34;&gt; Allow to double click on a tab to close it · Issue #52628 · microsoft/vscode&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&#34;탭-줄바꿈&#34;&gt;탭 줄바꿈&lt;/h3&gt;
&lt;p caption=&#39;tab wrap&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/b72dfdff-c447-457d-89de-8ca3a682281d.webp&#39; alt=&#39;tab wrap&#39; title=&#39;tab wrap&#39;&gt;&lt;/p&gt;
&lt;p&gt;탭을 많이 열면 스크롤할 때 불편하고 전체를 파악하기 어려워 탭 줄바꿈을 선호합니다. 효과는 다음과 같습니다:&lt;/p&gt;
&lt;p caption=&#39;wrap 效果&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/3738c007-8e6d-4397-a3fb-0a5210402ed2.webp&#39; alt=&#39;wrap 效果&#39; title=&#39;wrap 效果&#39;&gt;&lt;/p&gt;
&lt;p&gt;다만, 줄바꿈된 탭은 앞서 언급한 &#39;탭 상단 파란색 강조’와 혼동될 수 있습니다(파란 선이 위 탭의 것인지 아래 탭의 것인지 직관적으로 파악하기 어렵습니다). 탭에 더 많은 내용을 표시할지, 직관성을 우선할지는 선택의 문제입니다:&lt;/p&gt;
&lt;p caption=&#39;高亮修改 + tab 高亮效果&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/bba12f32-b355-4550-aba5-c59950ed66fc.webp&#39; alt=&#39;高亮修改 + tab 高亮效果&#39; title=&#39;高亮修改 + tab 高亮效果&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;탭-높이&#34;&gt;탭 높이&lt;/h3&gt;
&lt;p caption=&#39;紧凑 tab 布局&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/3224c016-4adb-41d2-82f8-83391960259d.webp&#39; alt=&#39;紧凑 tab 布局&#39; title=&#39;紧凑 tab 布局&#39;&gt;&lt;/p&gt;
&lt;p&gt;콤팩트 레이아웃은 전체를 파악하기 쉽고 공간을 덜 차지합니다.&lt;/p&gt;
&lt;h3 id=&#34;탭-더블-클릭-닫기&#34;&gt;&lt;s&gt;탭 더블 클릭 닫기(?)&lt;/s&gt;&lt;/h3&gt;
&lt;p caption=&#39;没懂这个设置&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/3385a990-b192-4aec-963a-ee5996faf19d.webp&#39; alt=&#39;没懂这个设置&#39; title=&#39;没懂这个设置&#39;&gt;&lt;/p&gt;
&lt;p&gt;이 옵션은 공식적으로 구현하지 않겠다고 한 ‘탭 더블 클릭 닫기’ 기능처럼 보이지만(위에서 언급한 대로), 충돌 가능성이 있는 &#39;탭 더블 클릭 시 에디터 그룹 자동 확장’을 꺼도 이 설정은 작동하지 않습니다. 제가 잘못 이해한 건지 버그인지 모르겠습니다.&lt;/p&gt;
&lt;h3 id=&#34;네이티브-탭&#34;&gt;네이티브 탭&lt;/h3&gt;
&lt;p&gt;관련 설정이 두 가지 있습니다:&lt;/p&gt;
&lt;p caption=&#39;原生 tab&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/09ad718f-b4c3-40a3-8c92-8e399613540a.webp&#39; alt=&#39;原生 tab&#39; title=&#39;原生 tab&#39;&gt;&lt;/p&gt;
&lt;p&gt;첫 번째 설정을 활성화하면 여러 프로젝트 창을 하나의 창으로 합칠 수 있습니다. ‘창’ 메뉴에 ‘모든 창 합치기’ 옵션이 나타나, 하나의 창에서 여러 프로젝트를 전환할 수 있어 매우 유용합니다:&lt;/p&gt;
&lt;p caption=&#39;合并所有窗口&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/8d13f532-cc6a-49a6-9d2c-88aabf5904e3.webp&#39; alt=&#39;合并所有窗口&#39; title=&#39;合并所有窗口&#39;&gt;&lt;/p&gt;
&lt;p&gt;하지만 이 경우 사용자 정의 제목을 사용할 수 없습니다(사실 별로 유용하지 않다고 생각합니다). 사용자 정의 제목은 다음과 같습니다:&lt;/p&gt;
&lt;p caption=&#39;自定义标题栏效果&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/1d16fa15-9ee4-4647-8458-1103a659a165.webp&#39; alt=&#39;自定义标题栏效果&#39; title=&#39;自定义标题栏效果&#39;&gt;&lt;/p&gt;
&lt;p&gt;첫 번째 설정이 켜져 있으면 두 번째 설정은 native나 custom으로 설정해도 효과가 없습니다. 첫 번째 설정이 꺼져 있고 두 번째 설정이 native로 설정되면 &#39;모든 창 합치기’와 &#39;사용자 정의 제목 표시줄’이 모두 사라집니다(이 설정의 의미를 모르겠습니다).&lt;/p&gt;
&lt;h3 id=&#34;디렉토리-트리-드래그-앤-드롭&#34;&gt;디렉토리 트리 드래그 앤 드롭&lt;/h3&gt;
&lt;p caption=&#39;最好禁用拖拽文件&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/f4f866b8-c0be-456f-8336-2a229c532f61.webp&#39; alt=&#39;最好禁用拖拽文件&#39; title=&#39;最好禁用拖拽文件&#39;&gt;&lt;/p&gt;
&lt;p&gt;자주 실수로 터치해 수백 개의 수정 사항이 발생하므로 껐습니다.&lt;/p&gt;
&lt;h3 id=&#34;검색-결과-자동-접기&#34;&gt;검색 결과 자동 접기&lt;/h3&gt;
&lt;p caption=&#39;少于 10 个的文件夹展开&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/5aab9aef-806d-42a0-ab7c-7d58ae4b4849.webp&#39; alt=&#39;少于 10 个的文件夹展开&#39; title=&#39;少于 10 个的文件夹展开&#39;&gt;&lt;/p&gt;
&lt;p&gt;기본적으로 항상 펼쳐져 있지만, 검색 결과가 너무 많으면(보통 입력을 완료하지 않았을 때) 펼치는 것은 불필요하고 전체를 파악하는 데 방해가 됩니다.&lt;/p&gt;
&lt;p&gt;또, 검색창에서 &#39;제외할 파일’을 지정하지 않으면 NextJS 프로젝트의 .next 디렉토리 같은 곳에서 방대한 검색 결과가 나올 수 있으므로 이 설정도 필요합니다.&lt;/p&gt;
&lt;p&gt;주의할 점은, 이 ‘펼치기’, &#39;접기’의 10개 파일 제한은 검색 결과에서 특정 폴더 아래에 나타나는 파일 수를 의미하며, 전체 검색 결과의 폴더 수가 아닙니다:&lt;/p&gt;
&lt;p caption=&#39;多余 10 个的文件夹折叠&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/67c50309-319c-474a-addf-bbd33925d827.webp&#39; alt=&#39;多余 10 个的文件夹折叠&#39; title=&#39;多余 10 个的文件夹折叠&#39;&gt;&lt;/p&gt;
&lt;p&gt;따라서 특정 폴더에서 검색 결과에 해당하는 파일이 너무 많으면(폴더가 접힌 상태) 더 많은 검색 정보를 제공해야 할지 확인해야 합니다.&lt;/p&gt;
&lt;h3 id=&#34;검색창에-선택-내용-자동-입력&#34;&gt;검색창에 선택 내용 자동 입력&lt;/h3&gt;
&lt;p caption=&#39;全局搜索自动带入选择内容&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/42995472-6453-4e61-881e-d7e0ae5da443.webp&#39; alt=&#39;全局搜索自动带入选择内容&#39; title=&#39;全局搜索自动带入选择内容&#39;&gt;&lt;/p&gt;
&lt;p&gt;보통 내용을 선택한 후 검색하려 하므로 ‘Seed On Focus’ 옵션은 cmd + v 작업을 하나 줄여줍니다.&lt;/p&gt;
&lt;p&gt;참고로, 이는 &#39;검색 위젯’에서의 &#39;선택 후 검색에 포커스 시 자동 입력’과 다릅니다. 에디터에서 내용을 선택한 후 검색 위젯에 포커스하는 것은 검색을 위한 것만이 아니라, 현재 에디터에서 선택한 내용을 강조해 보기 위한 것일 수도 있습니다. 하지만 이때 선택 내용이 자동으로 검색창에 입력되면 종종 예상과 다릅니다.&lt;/p&gt;
&lt;p&gt;반면, 내용을 선택한 후 검색 뷰(오른쪽)에 포커스하면 대부분 검색을 위한 것입니다.&lt;/p&gt;
&lt;p&gt;또, 검색 결과에서 해당 내용이 문서 내 어느 위치에 있는지 알기 위해 줄 번호를 표시하는 것도 유용합니다.&lt;/p&gt;
&lt;p&gt;마지막으로 Smart Case는 작은 트릭입니다. 모두 소문자로 입력하면 검색 이름을 잘 기억하지 못한다는 의미이고, 검색 내용(예: 카멜 케이스 함수명)의 특정 문자가 대문자인지 확실하면 대소문자를 구분해 검색하면 매우 유용합니다.&lt;/p&gt;
&lt;p&gt;이 외에도, 이전에 입력한 내용을 기억하고 있다면 기억된 내용이 선택된 상태여도, 입력 예상과 다르면 그냥 입력하면 됩니다. 이전 검색 내용이 여전히 유용하다면 더 좋지 않을까요? ↓&lt;/p&gt;
&lt;p caption=&#39;注意与搜索小组件的差别&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/b84b5d58-94cb-448f-bd1a-cc129aac13bd.webp&#39; alt=&#39;注意与搜索小组件的差别&#39; title=&#39;注意与搜索小组件的差别&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;검색-시-전역-ignore-무시&#34;&gt;검색 시 전역 ignore 무시&lt;/h3&gt;
&lt;p caption=&#39;全局忽略设置&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/21a936ed-29c9-4613-b6d4-5ad910fc04f0.webp&#39; alt=&#39;全局忽略设置&#39; title=&#39;全局忽略设置&#39;&gt;&lt;/p&gt;
&lt;p&gt;Git에는 전역 기본 ignore 기능이 있습니다. 이 옵션을 활성화하면 검색 시 해당 목록에 있는 파일 및 폴더를 무시할 수 있어 일반적으로 유용합니다.&lt;/p&gt;
&lt;p&gt;또한 상위 디렉토리에서 ignore를 활성화하는 옵션이 있는데, 정확한 의미는 잘 모르겠지만 다단계 Git 관리와 관련된 것 같습니다. 어차피 무시할 거라면 체크해두었습니다:&lt;/p&gt;
&lt;p caption=&#39;统统勾上&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/fa5ad841-87ee-460f-ad01-d1c7a34f2553.webp&#39; alt=&#39;统统勾上&#39; title=&#39;统统勾上&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;디버깅과-테스트&#34;&gt;디버깅과 테스트&lt;/h3&gt;
&lt;p&gt;제 기술 수준이 아직 부족하여 VSCode의 디버깅 및 테스트 기능은 거의 사용하지 않습니다. NextJS 같은 NodeJS 애플리케이션 디버깅에만 사용해봤는데, Chrome과 사용법이 비슷했습니다. 자주 사용하지 않아 특별히 개선할 설정이 없어 이 부분은 생략하겠습니다.&lt;/p&gt;
&lt;h3 id=&#34;파일-수정-효과&#34;&gt;파일 수정 효과&lt;/h3&gt;
&lt;p caption=&#39;实线比「装订线」好看&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/ef52ba2e-5ff7-4f8f-963d-32da4f899fe6.webp&#39; alt=&#39;实线比「装订线」好看&#39; title=&#39;实线比「装订线」好看&#39;&gt;&lt;/p&gt;
&lt;p&gt;줄 번호가 표시되는 열에서 차이점을 실선으로 표시할지 &#39;거터’로 표시할지 설정할 수 있습니다:&lt;/p&gt;
&lt;p caption=&#39;实在不知道装订线存在的意义&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/4c009676-4e9a-4c90-8eab-5ceffae9593d.webp&#39; alt=&#39;实在不知道装订线存在的意义&#39; title=&#39;实在不知道装订线存在的意义&#39;&gt;&lt;/p&gt;
&lt;p&gt;저는 실선이 더 선호되어 두 옵션 모두 해제했습니다.&lt;/p&gt;
&lt;h3 id=&#34;git-커밋-버튼-비활성화&#34;&gt;Git 커밋 버튼 비활성화&lt;/h3&gt;
&lt;p caption=&#39;移除多余的 UI 按钮&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/6343ab7e-7fc4-4cdc-8b0d-b65eae20b0b7.webp&#39; alt=&#39;移除多余的 UI 按钮&#39; title=&#39;移除多余的 UI 按钮&#39;&gt;&lt;/p&gt;
&lt;p&gt;솔직히 왼쪽에 있는 이 커밋 버튼은 한 번도 사용해본 적 없습니다. 항상 명령줄로 Git 작업을 하기 때문에 이 옵션을 해제했습니다.&lt;/p&gt;
&lt;p&gt;마찬가지로 (GitHub Copilot 버튼으로 보이는) 이 자동 커밋 메시지 생성 기능도 해제했습니다. 특히 회사 프로젝트에서는 요구사항/버그 카드 번호를 반드시 입력해야 하므로 이 스마트 커밋 메시지 기능은 더욱 쓸모가 없습니다:&lt;/p&gt;
&lt;p caption=&#39;移除自动写提交信息&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/111f48be-42e0-4f6a-b490-7dc832b15045.webp&#39; alt=&#39;移除自动写提交信息&#39; title=&#39;移除自动写提交信息&#39;&gt;&lt;/p&gt;
&lt;h2 id=&#34;확장-프로그램&#34;&gt;확장 프로그램&lt;/h2&gt;
&lt;h3 id=&#34;알림-비활성화&#34;&gt;알림 비활성화&lt;/h3&gt;
&lt;p caption=&#39;取消全部扩展通知&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/caa23ff0-089b-4035-90e1-c914e44c61ac.webp&#39; alt=&#39;取消全部扩展通知&#39; title=&#39;取消全部扩展通知&#39;&gt;&lt;/p&gt;
&lt;p&gt;어떤 확장 프로그램도 제게 지시할 필요가 없습니다. 필요할 때 직접 찾아서 사용할 것입니다.&lt;/p&gt;
&lt;h2 id=&#34;터미널&#34;&gt;터미널&lt;/h2&gt;
&lt;h3 id=&#34;우클릭-동작&#34;&gt;우클릭 동작&lt;/h3&gt;
&lt;p caption=&#39;终端邮件默认居然是选中+菜单&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/8c75e65f-ae00-49f1-982f-82c682b4a2f9.webp&#39; alt=&#39;终端邮件默认居然是选中+菜单&#39; title=&#39;终端邮件默认居然是选中+菜单&#39;&gt;&lt;/p&gt;
&lt;p&gt;일반적으로 마우스 왼쪽 버튼으로 선택한 후 우클릭으로 컨텍스트 메뉴가 나타납니다. 하지만 VSCode 기본 동작은 내용(단어)을 선택하면 바로 우클릭 메뉴가 나타납니다. 가능하지만 필요하지 않습니다.&lt;/p&gt;
&lt;h3 id=&#34;터미널-최대-행-수&#34;&gt;터미널 최대 행 수&lt;/h3&gt;
&lt;p caption=&#39;最大记录行&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/67d6cb52-ce20-40d2-ab50-548b7a4aab41.webp&#39; alt=&#39;最大记录行&#39; title=&#39;最大记录行&#39;&gt;&lt;/p&gt;
&lt;p&gt;사실 이 설정은 변경하지 않아도 됩니다. 가끔 오래된 로그 정보를 확인해야 할 때가 있고, 64GB 메모리를 가지고 있어 크게 늘려도 무방합니다.&lt;/p&gt;
&lt;h3 id=&#34;터미널-스크롤-애니메이션&#34;&gt;터미널 스크롤 애니메이션&lt;/h3&gt;
&lt;p caption=&#39;奇怪的动画，关了&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/7bd7b784-c3e5-456a-9aea-f0374dfa62b0.webp&#39; alt=&#39;奇怪的动画，关了&#39; title=&#39;奇怪的动画，关了&#39;&gt;&lt;/p&gt;
&lt;p&gt;애니메이션을 좋아하지만(우아하니까), 터미널의 스크롤 애니메이션은 관성 효과가 있어 스크롤 양을 조절하기 어렵습니다. 에디터나 작업 영역의 스크롤 효과와는 차이가 크므로 비활성화했습니다.&lt;/p&gt;
&lt;h2 id=&#34;css-less-sass&#34;&gt;CSS/Less/Sass&lt;/h2&gt;
&lt;h3 id=&#34;중복-속성-경고&#34;&gt;중복 속성 경고&lt;/h3&gt;
&lt;p caption=&#39;需要设置三次&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/067e3056-d82f-4910-9e47-519259d54577.webp&#39; alt=&#39;需要设置三次&#39; title=&#39;需要设置三次&#39;&gt;&lt;/p&gt;
&lt;p&gt;이 기능은 매우 유용합니다. 때로는 외부에서 여러 속성 값을 복사해 붙여넣기(주로 브라우저 요소 검사의 스타일에서 복사)한 후 중복 속성을 제거하는 경우가 많기 때문입니다.&lt;/p&gt;
&lt;h2 id=&#34;git&#34;&gt;Git&lt;/h2&gt;
&lt;h3 id=&#34;자동-stash&#34;&gt;자동 Stash&lt;/h3&gt;
&lt;p caption=&#39;少操作一次&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/4fee6e57-9088-4a39-bbfe-c86ea5c9beb2.webp&#39; alt=&#39;少操作一次&#39; title=&#39;少操作一次&#39;&gt;&lt;/p&gt;
&lt;p&gt;설명이 명확하게 되어 있습니다. 한 단계 작업을 줄일 수 있으므로 활성화를 권장합니다.&lt;/p&gt;
&lt;h2 id=&#34;서드파티-확장-프로그램&#34;&gt;서드파티 확장 프로그램&lt;/h2&gt;
&lt;p&gt;별로 말할 것이 없습니다. 확장 프로그램을 설치하는 것은 각자의 필요에 따른 것이므로 자신의 요구에 맞게 설정하면 됩니다.&lt;/p&gt;
&lt;h3 id=&#34;gitlens&#34;&gt;GitLens&lt;/h3&gt;
&lt;p&gt;하지만 일부 플러그인은 유료 기능 추천을 끌 수 있습니다. 바로 &lt;code&gt;GitLens&lt;/code&gt; 같은 경우인데, Git 브랜치 병합 상황을 (우연히) 확인할 때 유료 기능 알림이 나타납니다. 이 기능은 끌 수 있습니다(플러그인 개발자의 너그러움에 감사합니다):&lt;/p&gt;
&lt;p caption=&#39;关掉 GitLens 付费功能提醒&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/28974458-b244-4ed9-919e-affe90c409fe.webp&#39; alt=&#39;关掉 GitLens 付费功能提醒&#39; title=&#39;关掉 GitLens 付费功能提醒&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;one-dark-pro&#34;&gt;One Dark Pro&lt;/h3&gt;
&lt;p caption=&#39;高亮部分代码&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/a1ac82b3-7b1c-4fc4-9327-2cd04796bbee.webp&#39; alt=&#39;高亮部分代码&#39; title=&#39;高亮部分代码&#39;&gt;&lt;/p&gt;
&lt;p&gt;이 기능이 마음에 듭니다. 메서드와 함수 이름을 더욱 뚜렷하게 볼 수 있습니다:&lt;/p&gt;
&lt;p caption=&#39;效果&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/make-vscode-great-forever/d6235ab1-a532-4188-be52-2f29908b31e5.webp&#39; alt=&#39;效果&#39; title=&#39;效果&#39;&gt;&lt;/p&gt;
&lt;h2 id=&#34;후기&#34;&gt;후기&lt;/h2&gt;
&lt;p&gt;이렇게 많은 설정을 소개했지만, 자신에게 맞는 것이 가장 중요합니다. 모두 효율적으로 작업하고 일찍 퇴근하시길 바랍니다!&lt;/p&gt;
</description>
            <pubDate>Thu, 21 Dec 2023 00:00:00 +0000</pubDate>
            <link>https://www.xheldon.com/ko/life/make-vscode-great-forever.html</link>
            <guid isPermaLink="true">https://www.xheldon.com/ko/life/make-vscode-great-forever.html</guid>
            
                <category>生活</category>
            
                <category>折腾</category>
            
                <category>教程</category>
            
                <category>技巧</category>
            
                <category>JavaScript</category>
            
                <category>工作流</category>
            
                <category>VSCode</category>
            
                <category>设置</category>
            
            
                <category>life</category>
            
        </item>
        
        <item>
            <title>Apple TV로 베이징 유니콤(Beijing Unicom) IPTV 시청하기</title>
            <description>&lt;h2 id=&#34;서문&#34;&gt;서문&lt;/h2&gt;
&lt;p&gt;이전에 &lt;a href=&#34;/life/the-way-to-watching-tv.html&#34;&gt;이 블로그 글&lt;/a&gt;에서 가족과 함께 영상을 감상하는 방법에 대해 이야기했는데, 그중 하나는 다른 사람들이 추출한 IPTV 프로그램 주소(m3u 확장자)를 네트워크에서 찾아 iPlayTV에 넣으면 바로 재생할 수 있다는 점이었습니다. 하지만 이 프로그램 주소는 일정 시간이 지나면 더 이상 작동하지 않는데, 이는 중국연통(联通) IPTV 서버가 주기적으로 프로그램 재생 주소를 업데이트하기 때문입니다. Apple TV에 입력된 주소는 고정되어 있어 실시간으로 업데이트할 수 없으므로, 이 글에서는 이 문제를 해결하는 방법을 소개합니다.&lt;/p&gt;
&lt;p class=&#39;content-callout&#39; style=&#39;background: rgb(253, 235, 236); color: ;&#39;&gt;&lt;span class=&#39;content-callout-icon&#39;&gt;🚧&lt;/span&gt;&lt;span&gt;**사용 전 필독:** 저는 베이징(北京) 지역의 중국연통(联通) 광대역을 사용하며, **광모뎀(光猫)은 브리지 모드로 설정하고, 라우터가 PPPoE를 담당하며, 소프트웨어 라우터 R4S는 서브 라우터**로 연결되어 있습니다. 이 튜토리얼은 해당 토폴로지 구성을 기준으로 설명합니다. 다른 네트워크 토폴로지(예: &#34;광모뎀 브리지 + R4S를 메인 라우터로 PPPoE 연결&#34; 또는 &#34;광모뎀 PPPoE + R4S 서브 라우터&#34;)에서도 구현할 수 있지만, R4S 설정의 핵심 부분은 이 튜토리얼과 약간 다를 수 있습니다. 여러 자료를 참고하여 핵심 원리를 이해하고, 반드시 제 방법과 동일하게 할 필요는 없습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p class=&#39;content-callout&#39; style=&#39;background: rgb(251, 243, 219); color: ;&#39;&gt;&lt;span class=&#39;content-callout-icon&#39;&gt;🚧&lt;/span&gt;&lt;span&gt;마찬가지로, 광모뎀에서 직접 PPPoE를 사용하는 경우, 광모뎀에서 케이블을 하나 빼서 서브/메인 라우터에 연결하여 멀티캐스트를 유니캐스트로 변환할 수 있습니다. 또는 지원되는 기기에서 직접 멀티캐스트 주소를 사용하여 재생할 수도 있으므로 제 방법처럼 복잡할 필요는 없습니다. 이는 각 가정의 네트워크 토폴로지에 따라 달라집니다. &lt;/span&gt;&lt;/p&gt;
&lt;p class=&#39;content-callout&#39; style=&#39;background: rgb(251, 236, 221); color: ;&#39;&gt;&lt;span class=&#39;content-callout-icon&#39;&gt;🚧&lt;/span&gt;&lt;span&gt;이 튜토리얼은 베이징 지역의 중국연통 IPTV에서만 테스트되었으며, 다른 지역에서는 차이가 있을 수 있습니다. 본문에서 지역과 강하게 연관된 내용을 발견하면 해당 지역에 맞게 내용을 수정하세요. &lt;/span&gt;&lt;/p&gt;
&lt;p class=&#39;content-callout&#39; style=&#39;background: rgb(244, 238, 238); color: ;&#39;&gt;&lt;span class=&#39;content-callout-icon&#39;&gt;🚧&lt;/span&gt;&lt;span&gt;설정을 변경하기 전에 모든 라우터 및 기기의 설정을 미리 백업하여 문제가 발생할 경우 대비하세요. &lt;/span&gt;&lt;/p&gt;
&lt;p class=&#39;content-callout&#39; style=&#39;background: rgb(251, 243, 219); color: ;&#39;&gt;&lt;span class=&#39;content-callout-icon&#39;&gt;🚧&lt;/span&gt;&lt;span&gt;제가 사용하는 소프트웨어 라우터는 R4S이며, 두 개의 네트워크 포트가 있습니다. 저와 같은 네트워크 토폴로지를 사용하는 경우, 소프트웨어 라우터에 최소 두 개 이상의 네트워크 포트가 있어야 합니다. 하나는 메인 라우터에 연결하고, 다른 하나는 광모뎀에 연결해야 하기 때문입니다. &lt;/span&gt;&lt;/p&gt;
&lt;p class=&#39;content-callout&#39; style=&#39;background: rgba(244, 240, 247, 0.8); color: ;&#39;&gt;&lt;span class=&#39;content-callout-icon&#39;&gt;🚧&lt;/span&gt;&lt;span&gt;저희 집의 광모뎀 네트워크 대역은 192.168.1.x이며, 메인 라우터 네트워크 대역은 192.168.5.x입니다. 광모뎀 주소는 192.168.1.1이고, R4S 주소는 192.168.5.2이며, 메인 라우터 LAN 포트 주소는 192.168.5.1, 라우터 WAN 포트 주소는 192.168.1.2입니다. &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&#34;무료-사용-가능-여부-확인&#34;&gt;무료 사용 가능 여부 확인&lt;/h2&gt;
&lt;p class=&#39;content-callout&#39; style=&#39;background: rgb(231, 243, 248); color: ;&#39;&gt;&lt;span class=&#39;content-callout-icon&#39;&gt;🚧&lt;/span&gt;&lt;span&gt;현재 베이징 지역의 IPTV에는 인증 절차가 추가되지 않았지만, 제가 확인한 정보에 따르면 다른 지역의 일부 통신사는 IPTV에 암호화 인증을 적용하고 있습니다. 즉, 통신사에서 제공한 IPTV 셋톱박스의 MAC 주소로 연결해야(셋톱박스가 복호화 역할을 함) 셋톱박스를 사용하지 않고 소프트웨어 라우터를 통해 LAN 내 모든 기기에서 재생할 수 있습니다. 이를 구현하는 방법은 복잡하므로 이 튜토리얼에서는 해당 시나리오를 다루지 않습니다. &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&#34;vlc-미리-다운로드&#34;&gt;VLC 미리 다운로드&lt;/h3&gt;
&lt;p&gt;광모뎀을 브리지 모드로 설정하고 라우터가 PPPoE를 담당하는 방식으로 인터넷을 사용하면, 광모뎀에 연결된 상태에서는 인터넷을 사용할 수 없습니다. 따라서 테스트를 위해 미리 컴퓨터에 VLC 플레이어를 다운로드하세요. VLC 플레이어는 다음 주소에서 다운로드할 수 있습니다:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.videolan.org/vlc/&#34; target=&#34;_blank&#34;&gt; Official download of VLC media player, the best Open Source player - VideoLAN&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&#34;광모뎀-연결&#34;&gt;광모뎀 연결&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;컴퓨터를 유선으로 광모뎀의 IPTV 포트에 연결&lt;/strong&gt;합니다(광모뎀에 IPTV 포트가 없다면, 광모뎀이 혼합 삽입을 지원한다는 의미로, 포트가 브로드밴드와 IPTV를 구분하지 않아 어떤 포트에 연결해도 상관없습니다). 그런 다음 VLC 소프트웨어에서:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;File(파일) - Open Network(네트워크 열기)를 엽니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;아래의 Open RTP/UDP Stream(RTP/UDP 스트림 열기)을 클릭합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Protocol(프로토콜)은 RTP를 선택하고, Mode(모드)는 Multicast(멀티캐스트)를 선택합니다. IP Address(IP 주소)에는 &lt;code&gt;239.3.1.241&lt;/code&gt; (또는 &lt;code&gt;rtp://239.3.1.241&lt;/code&gt;, 정확히 어느 것이었는지 기억나지 않습니다)를 입력하고, 포트에는 &lt;code&gt;8000&lt;/code&gt;을 입력합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Open(열기)을 클릭한 후, 베이징 TV(北京卫视)를 볼 수 있다면 무료로 시청할 수 있다는 의미입니다.&lt;/p&gt;
&lt;p caption=&#39;VLC RTP 播放&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/iptv-for-apple-tv-in-beijing/ea9168ee-6086-42df-b6b4-269900eb5592.webp&#39; alt=&#39;VLC RTP 播放&#39; title=&#39;VLC RTP 播放&#39;&gt;&lt;/p&gt;
&lt;p class=&#39;content-callout&#39; style=&#39;background: rgb(251, 243, 219); color: ;&#39;&gt;&lt;span class=&#39;content-callout-icon&#39;&gt;💡&lt;/span&gt;&lt;span&gt;여기의 `rtp://239.3.1.241:8000`는 베이징 위성 TV의 멀티캐스트 주소입니다. 이 주소는 나중에 변경될 수 있으며, 정확한 주소는 `https://raw.githubusercontent.com/qwerttvv/Beijing-IPTV/master/IPTV-Unicom.m3u` 여기에서 아무 rtp 경로 뒤의 IP 주소를 테스트해 볼 수 있습니다. &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&#34;기본-개념-설명&#34;&gt;기본 개념 설명&lt;/h2&gt;
&lt;p&gt;이해하지 않으려면 다음 섹션으로 건너뛰세요.&lt;/p&gt;
&lt;h3 id=&#34;iptv&#34;&gt;IPTV&lt;/h3&gt;
&lt;p&gt;위키백과의 IPTV 설명에 따르면:&lt;/p&gt;
&lt;p class=&#39;content-callout&#39; style=&#39;background: rgb(241, 241, 239); color: ;&#39;&gt;&lt;span class=&#39;content-callout-icon&#39;&gt;📖&lt;/span&gt;&lt;span&gt;인터넷 프로토콜 텔레비전(영어: Internet Protocol Television, 약자: IPTV)은 브로드밴드 TV의 일종입니다. IPTV는 브로드밴드 네트워크를 매개로 TV 정보를 전송하는 시스템으로, 방송 프로그램을 브로드밴드의 인터넷 프로토콜(Internet Protocol, IP)을 통해 가입자에게 디지털 TV 서비스를 제공합니다. 네트워크 사용이 필요하기 때문에 IPTV 서비스 제공업체는 종종 인터넷 연결 및 IP 전화 관련 서비스를 함께 제공하며, 이를 &#34;트리플 플레이&#34; 또는 &#34;삼합 서비스&#34;(Triple Play)라고도 합니다. IPTV는 디지털 TV의 일종이므로 일반 TV는 해당 셋톱박스와 함께 채널을 수신해야 하며, 이에 따라 공급업체는 일반적으로 고객에게 주문형 비디오 서비스를 함께 제공합니다. 브로드밴드 네트워크와 인터넷 프로토콜을 통해 전송되지만, IPTV는 반드시 인터넷을 통해 전송되는 것은 아니며, 전송 품질을 위해 로컬 영역 네트워크를 통해 전송될 수 있습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;이를 통해 일반적으로 IPTV는 인터넷 서비스 제공업체가 제공하는 서비스이며, 이를 통해 TV를 시청할 수 있음을 알 수 있습니다.&lt;/p&gt;
&lt;h3 id=&#34;멀티캐스트&#34;&gt;멀티캐스트&lt;/h3&gt;
&lt;p&gt;IPTV를 구현하는 기술적 수단 중 하나로, 영어로는 &#39;multicast’라고 하며, 다중 방송이라고도 번역됩니다. 구체적인 개념을 완전히 이해할 필요는 없지만, 유니캐스트에 비해 다음과 같은 &lt;strong&gt;장점&lt;/strong&gt;이 있습니다:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;인터넷 대역폭을 차지하지 않습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;IPTV 박스는 인증 역할을 하며, IPTV 사업자는 하나의 그룹에 브로드캐스트를 하기 때문에 서버 부하가 상대적으로 적습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;단점&lt;/strong&gt;은 다음과 같습니다:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;반드시 광모뎀의 IPTV 포트에 유선으로 연결해야 사용할 수 있습니다(일부 광모뎀은 IPTV 포트와 브로드밴드 포트를 구분하지 않는 혼합 삽입을 지원합니다). 따라서 IPTV 박스에 연결된 장치만 사용할 수 있으며, WiFi를 통해 가정 내 모든 장치에서 네트워크 TV를 시청할 수 없습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;유니캐스트&#34;&gt;유니캐스트&lt;/h3&gt;
&lt;p&gt;IPTV를 구현하는 또 다른 오래된 기술적 수단으로, 초기 IPTV 사용자가 많지 않을 때 이 방식을 사용했습니다. 멀티캐스트에 비해 상대방의 단점이 자신의 장점이 되며, 그 반대도 마찬가지입니다. 즉:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;장점&lt;/strong&gt;은 다음과 같습니다:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;연결 후 로컬 네트워크에서 WiFi를 지원하여 모든 장치에서 재생할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;단점&lt;/strong&gt;은 다음과 같습니다:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;서버와 1대1로 연결되며, 서버 부하가 크기 때문에 사용자가 많을 때 재생이 지연될 수 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;브로드밴드 대역폭을 차지하며, 인터넷 연결을 직접 사용하여 재생합니다(현재 라이브 스트리밍을 보는 것과 같습니다).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;udpxy&#34;&gt;udpxy&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; udpxy 서버는 **UDP 스트림을 HTTP 스트림으로 변환하는 프록시 서버**로, IP 라이브 스트림을 HTTP 스트림으로 변환하여 다양한 단말기에서 쉽게 재생할 수 있도록 합니다. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;멀티캐스트 주소를 직접 획득할 수 없는 경우, 멀티캐스트 주소를 유니캐스트 주소로 변환하는 데 사용됩니다. 예를 들어 멀티캐스트 주소가 a:b, d:e(a와 d는 IP 주소, b와 e는 포트)인 경우, 유니캐스트 주소로 변환하면 z/a/b, z/d/e와 같은 통일된 주소가 됩니다. 플레이어는 이 주소 z를 감지하면 됩니다.&lt;/p&gt;
&lt;h3 id=&#34;m3u&#34;&gt;m3u&lt;/h3&gt;
&lt;blockquote style=&#39;border-color: ;color: &#39;&gt;&lt;p&gt; **M3U**(MP3 URL의 약자)는 멀티미디어 재생 목록을 위한 파일 형식으로, 원래는 MP3와 같은 오디오 파일 재생을 위해 설계되었지만, 현재는 점점 더 많은 소프트웨어에서 비디오 파일 목록 재생 형식으로 사용되고 있습니다. &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;m3u 파일은 모든 멀티캐스트 재생 주소를 포함한 텍스트 파일입니다. Apple TV나 컴퓨터에서 이 파일 주소를 읽으면 해당 비디오 주소를 재생할 수 있습니다.&lt;/p&gt;
&lt;h3 id=&#34;epg&#34;&gt;EPG&lt;/h3&gt;
&lt;p&gt;프로그램 정보를 포함합니다. 이전 단계에서 m3u 주소를 알게 되면, 그 안에는 각각의 IP 주소가 있습니다. 그렇다면 각 IP 주소가 어떤 채널인지 어떻게 알 수 있을까요? 이때 EPG가 필요합니다. EPG는 각 주소의 프로그램 정보, 심지어 채널의 간단한 소개까지 포함하고 있습니다. EPG는 일반적으로 비디오 신호와 함께 방송됩니다.&lt;/p&gt;
&lt;h2 id=&#34;네트워크-토폴로지&#34;&gt;네트워크 토폴로지&lt;/h2&gt;
&lt;p caption=&#39;网络拓扑-主路由拨号&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/iptv-for-apple-tv-in-beijing/0349a1d5-caeb-4233-b36b-8bc1c7db7565.webp&#39; alt=&#39;网络拓扑-主路由拨号&#39; title=&#39;网络拓扑-主路由拨号&#39;&gt;&lt;/p&gt;
&lt;h2 id=&#34;실습-시작하기&#34;&gt;실습 시작하기&lt;/h2&gt;
&lt;p class=&#39;content-callout&#39; style=&#39;background: rgb(251, 243, 219); color: ;&#39;&gt;&lt;span class=&#39;content-callout-icon&#39;&gt;🚧&lt;/span&gt;&lt;span&gt;시작하기 전에 앞서 설명한 대로 무료로 사용할 수 있는지 확인하세요. &lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&#34;소프트라우터-설정하기&#34;&gt;소프트라우터 설정하기&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;udpxy와 luci-udpxy 설치하기&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;이 단계는 일반적인 작업입니다. UI를 통해 설치하는 것이 가장 편리하며, 아래 그림과 같습니다(설치 완료 후 바로 활성화하지 마세요. 마지막 단계에서 활성화합니다):&lt;/p&gt;
&lt;p caption=&#39;软件安装界面&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/iptv-for-apple-tv-in-beijing/2be1ca35-90a8-4936-89cd-5458557ac064.webp&#39; alt=&#39;软件安装界面&#39; title=&#39;软件安装界面&#39;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;새 네트워크 인터페이스 생성/설정&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;이 단계는 소프트 라우터가 광모뎀으로부터 오는 데이터를 인식할 수 있도록 하는 것입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;네트워크-인터페이스&lt;/code&gt;에서 새 인터페이스를 생성하고, 이름을 &lt;code&gt;IPTV&lt;/code&gt;로 지정합니다:&lt;/li&gt;
&lt;/ul&gt;
&lt;p caption=&#39;新建 IPTV 接口&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/iptv-for-apple-tv-in-beijing/a938e2a7-9723-40f5-8b86-3ed151ba12c5.webp&#39; alt=&#39;新建 IPTV 接口&#39; title=&#39;新建 IPTV 接口&#39;&gt;&lt;/p&gt;
&lt;p&gt;화살표 부분에 주의하세요. 이미 새로 생성했기 때문에 괄호 안에 IPTV라는 문구가 있습니다. 처음 생성했을 때는 없었습니다. 여기서 eth1은 내 Lan 포트로, 메인 라우터에 연결되어 있고; eth0은 다른 인터페이스로, 광모뎀(IPTV 포트)에 연결되어 있습니다. 이 부분은 제가 수정한 적이 있는데, 기본적으로 eht0은 Lan 포트, eth1은 Wan 포트입니다. 중요하지 않으며, 이 단계는 Wan 포트를 IPTV 포트로 사용하는 것입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;「IPTV」 인터페이스의 게이트웨이 홉 및 방화벽 설정 구성:&lt;/li&gt;
&lt;/ul&gt;
&lt;p caption=&#39;配置网关跃点&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/iptv-for-apple-tv-in-beijing/d1df1f84-7240-4eb9-872b-6871f9c895a6.webp&#39; alt=&#39;配置网关跃点&#39; title=&#39;配置网关跃点&#39;&gt;&lt;/p&gt;
&lt;p caption=&#39;防火墙配置到 wan 上&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/iptv-for-apple-tv-in-beijing/c0e4032b-f066-4fcf-b906-dae85a37d070.webp&#39; alt=&#39;防火墙配置到 wan 上&#39; title=&#39;防火墙配置到 wan 上&#39;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Wan 포트의 게이트웨이 홉 수 설정:&lt;/li&gt;
&lt;/ul&gt;
&lt;p caption=&#39;Wan 口网关跃点&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/iptv-for-apple-tv-in-beijing/21347824-ca57-4715-9fe9-cd7b6be0f282.webp&#39; alt=&#39;Wan 口网关跃点&#39; title=&#39;Wan 口网关跃点&#39;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;LAN 포트 IGMP 스니핑 구성:&lt;/li&gt;
&lt;/ul&gt;
&lt;p caption=&#39;IGMP 嗅探&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/iptv-for-apple-tv-in-beijing/b075aaec-042b-4f9d-9437-27ea4d17211e.webp&#39; alt=&#39;IGMP 嗅探&#39; title=&#39;IGMP 嗅探&#39;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;네트워크 방화벽 구성하기&lt;/li&gt;
&lt;/ul&gt;
&lt;p caption=&#39;网络防火墙配置&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/iptv-for-apple-tv-in-beijing/577418eb-c3f6-43be-b73b-b1cf3ab5dcea.webp&#39; alt=&#39;网络防火墙配置&#39; title=&#39;网络防火墙配置&#39;&gt;&lt;/p&gt;
&lt;p caption=&#39;防火墙配置2&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/iptv-for-apple-tv-in-beijing/b3ce719b-a05d-41ef-ab4a-12eb866178a8.webp&#39; alt=&#39;防火墙配置2&#39; title=&#39;防火墙配置2&#39;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;udpxy 서비스 구성&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;그림과 같이 구성하면 되며, 여기서 eth0은 Wan 포트이므로 주의하세요:&lt;/li&gt;
&lt;/ul&gt;
&lt;p caption=&#39;打开 UDPXY&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/iptv-for-apple-tv-in-beijing/bcd40c05-ed7f-43fd-b833-4cee94948150.webp&#39; alt=&#39;打开 UDPXY&#39; title=&#39;打开 UDPXY&#39;&gt;&lt;/p&gt;
&lt;p&gt;마지막으로 [&lt;a href=&#34;http://192.168.5.2:4022/status&#34;&gt;http://192.168.5.2:4022/status&lt;/a&gt;]([object Object])을 열어 udpxy 서비스가 성공적으로 시작되었는지 확인합니다:&lt;/p&gt;
&lt;p caption=&#39;看到这个就表示成功了&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/iptv-for-apple-tv-in-beijing/189e9cb4-80c9-4b5d-8f57-722660e66145.webp&#39; alt=&#39;看到这个就表示成功了&#39; title=&#39;看到这个就表示成功了&#39;&gt;&lt;/p&gt;
&lt;p&gt;이전에 VLC로 열었던 주소 &lt;code&gt;rtp://239.3.1.241:8000&lt;/code&gt;를 `&lt;a href=&#34;http://192.168.2.1:4022/rtp/239.3.1.241:8000%60%60%EB%A1%9C&#34;&gt;http://192.168.2.1:4022/rtp/239.3.1.241:8000``로&lt;/a&gt; 변경한 후 다시 열어보세요(아래의 Open RTP/UDP Stream을 클릭하지 말고 URL에서 직접 열기).&lt;/p&gt;
&lt;p caption=&#39;尝试将 RTP 变 HTTP 播放&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/iptv-for-apple-tv-in-beijing/582e015c-4260-4181-a81c-5eafc97a1132.webp&#39; alt=&#39;尝试将 RTP 变 HTTP 播放&#39; title=&#39;尝试将 RTP 变 HTTP 播放&#39;&gt;&lt;/p&gt;
&lt;p&gt;그런 다음 더블 클릭하여 재생하면 됩니다:&lt;/p&gt;
&lt;p caption=&#39;成功画面↑&#39;&gt;&lt;img src=&#39;https://static.xheldon.cn/img/in-post/2023/iptv-for-apple-tv-in-beijing/215aa895-e673-419a-a4fc-b94603af8baa.webp&#39; alt=&#39;成功画面↑&#39; title=&#39;成功画面↑&#39;&gt;&lt;/p&gt;
&lt;h3 id=&#34;재생-소프트웨어-사용하기&#34;&gt;재생 소프트웨어 사용하기&lt;/h3&gt;
&lt;p&gt;저는 Apple TV 4K로 TV를 시청하는데, 여러 재생 소프트웨어를 시도해 봤습니다. 간단히 설명드리겠습니다:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;iPlayTV는 재생이 안 되는데 이유를 모르겠습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fileball은 채널 위아래로 이동하는 건 문제없지만, 채널을 선택하면 바로 튕깁니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;결국 IIVA와 같은 개발자의 앱인 「TV+」를 선택했습니다. 홍콩 스토어에서 38홍콩 달러에 구매했는데(구매 다음 날 무료로 풀려서 좀 당황스러웠습니다).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;주소-자동-업데이트-구현-방법&#34;&gt;주소 자동 업데이트 구현 방법&lt;/h2&gt;
&lt;p&gt;연통(联通) IPTV의 프로그램 재생 주소는 일정 시간마다 한 번씩 변경됩니다. 때로는 포트가 바뀌고, 때로는 IP 주소가 바뀝니다. 이때 이 방법으로 재생하면 실패하게 되는데, 어떻게 해야 할까요?&lt;/p&gt;
&lt;p&gt;인터넷에 친절한 분들이 있어서, 다음과 같은 복잡한 감청 작업을 통해:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://blog.friskit.me/2020/05/31/bjunicom-network.html&#34; target=&#34;_blank&#34;&gt; 光纤入户光猫改桥接+内网转发IPTV=任意设备看电视直播 - Botian&#39;s Blog&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;IPTV 셋톱박스와 연통 서버 간의 통신 데이터를 얻어 그 주소를 확보했기 때문에, 우리는 이를 직접 사용하면 됩니다. 예를 들어:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/qwerttvv/Beijing-IPTV/blob/master/README.md&#34; target=&#34;_blank&#34;&gt; github.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;하지만 여기서 중요한 점은, 집에서 사용하는 소프트라우터의 주소가 이 친절한 분의 주소와 동일해야 한다는 것입니다(192.168.123.1). 그리고 udpxy 포트를 23234로 변경한 후, 이 주소를 TV+에 바로 입력하면 됩니다:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://raw.githubusercontent.com/qwerttvv/Beijing-IPTV/master/IPTV-Unicom.m3u&#34; target=&#34;_blank&#34;&gt; raw.githubusercontent.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;그렇지 않으면, 해당 파일의 변경 사항을 직접 감지하고 업데이트해야 합니다.&lt;/p&gt;
&lt;p&gt;여기서 저는 Vercel에 API 서비스를 10분 만에 배포했고, 동시에 Vercel의 Corn Jobs 서비스를 활성화하여 정기적으로 함수를 실행해 변경 사항을 감지할 수 있도록 했습니다. 코드는 다음과 같으며, 여러분도 직접 배포할 수 있습니다:&lt;/p&gt;
&lt;figure class=&#34;highlight javascript&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;61&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;62&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;63&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;64&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;65&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;66&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;67&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;68&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;69&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;70&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;71&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;72&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;73&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;74&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;75&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;76&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;77&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;78&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;79&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;80&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;81&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;82&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;83&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;84&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;85&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;86&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;87&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;88&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;89&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;90&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;91&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;92&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;93&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;94&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;95&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;96&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;97&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;98&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;99&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;100&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;101&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;102&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;103&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;104&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;105&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;106&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;107&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;108&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs javascript&#34;&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;import&lt;/span&gt; type &amp;#123; &lt;span class=&#34;hljs-title class_&#34;&gt;NextApiRequest&lt;/span&gt;, &lt;span class=&#34;hljs-title class_&#34;&gt;NextApiResponse&lt;/span&gt; &amp;#125; &lt;span class=&#34;hljs-keyword&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;next&amp;#x27;&lt;/span&gt;;&lt;br&gt;&lt;br&gt;type &lt;span class=&#34;hljs-title class_&#34;&gt;ResponseData&lt;/span&gt; = &amp;#123;&lt;br&gt;  &lt;span class=&#34;hljs-attr&#34;&gt;msg&lt;/span&gt;: string,&lt;br&gt;&amp;#125;;&lt;br&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt;/**&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt; * 该函数用来将网友通过 IPTV 盒子抓包获取的联通单播地址，转成自己的单播地址&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt; * 该函数每天 3 点触发一次，定时检测网友的单播地址是否有更新，使用 vercel corn 任务进行&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt; * &lt;span class=&#34;hljs-doctag&#34;&gt;TODO:&lt;/span&gt; 该函数未做鉴权，任何人都可以手动触发检测，为了防止滥用可以加上鉴权，但是 corn 似乎没这个功能&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-comment&#34;&gt; */&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-keyword&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;default&lt;/span&gt; &lt;span class=&#34;hljs-keyword&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;handler&lt;/span&gt;(&lt;span class=&#34;hljs-params&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-params&#34;&gt;  request: NextApiRequest,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-params&#34;&gt;  response: NextApiResponse&amp;lt;ResponseData&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-params&#34;&gt;&lt;/span&gt;) &amp;#123;&lt;br&gt;  &lt;span class=&#34;hljs-comment&#34;&gt;// Note: 步骤&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-comment&#34;&gt;// 1. 获取网友通过监听盒子数据包抓取的（自己搞比较费劲，直接用现成的了）联通 IPTV 永久地址（rtp 协议的组播地址，多用户通用），获取其内容&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-comment&#34;&gt;// 2. 添加本地 udpxy 转发地址&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-comment&#34;&gt;// 3. 获取之前的 github gist 内容以对比二者&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-comment&#34;&gt;// 4. 有差异，则更新 github gist 内容&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-comment&#34;&gt;// 5. 没有，则不做操作&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-comment&#34;&gt;// Note: 环境变量，自己在 Vercel 中设置好&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; token = process.&lt;span class=&#34;hljs-property&#34;&gt;env&lt;/span&gt;.&lt;span class=&#34;hljs-property&#34;&gt;GITHUB_TOKEN&lt;/span&gt;;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; gist = process.&lt;span class=&#34;hljs-property&#34;&gt;env&lt;/span&gt;.&lt;span class=&#34;hljs-property&#34;&gt;GIST_URL&lt;/span&gt;;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; id = process.&lt;span class=&#34;hljs-property&#34;&gt;env&lt;/span&gt;.&lt;span class=&#34;hljs-property&#34;&gt;GIST_ID&lt;/span&gt;;&lt;br&gt;  &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;fetch&lt;/span&gt;(&lt;br&gt;    &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;https://raw.githubusercontent.com/qwerttvv/Beijing-IPTV/master/IPTV-Unicom.m3u&amp;#x27;&lt;/span&gt;&lt;br&gt;  )&lt;br&gt;    .&lt;span class=&#34;hljs-title function_&#34;&gt;then&lt;/span&gt;(&lt;span class=&#34;hljs-keyword&#34;&gt;async&lt;/span&gt; (res) =&amp;gt; &amp;#123;&lt;br&gt;      &lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; (!res.&lt;span class=&#34;hljs-property&#34;&gt;ok&lt;/span&gt;) &amp;#123;&lt;br&gt;        &lt;span class=&#34;hljs-variable language_&#34;&gt;console&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;log&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;获取源地址异常&amp;#x27;&lt;/span&gt;);&lt;br&gt;        &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; response.&lt;span class=&#34;hljs-title function_&#34;&gt;status&lt;/span&gt;(&lt;span class=&#34;hljs-number&#34;&gt;200&lt;/span&gt;).&lt;span class=&#34;hljs-title function_&#34;&gt;json&lt;/span&gt;(&amp;#123;&lt;br&gt;          &lt;span class=&#34;hljs-attr&#34;&gt;msg&lt;/span&gt;: &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;获取源地址异常&amp;#x27;&lt;/span&gt;,&lt;br&gt;        &amp;#125;);&lt;br&gt;      &amp;#125;&lt;br&gt;      &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; src = &lt;span class=&#34;hljs-keyword&#34;&gt;await&lt;/span&gt; res.&lt;span class=&#34;hljs-title function_&#34;&gt;text&lt;/span&gt;();&lt;br&gt;      &lt;span class=&#34;hljs-comment&#34;&gt;// Note: 替换网友的本地单播地址为我的，其实你也可以将自己家的路由器网段设置成跟网友的一样（192.168.123.x），udpxy 端口转发设置成跟网友一样（23234），你就可以直接使用该地址了&lt;/span&gt;&lt;br&gt;      &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; newGist = src.&lt;span class=&#34;hljs-title function_&#34;&gt;replace&lt;/span&gt;(&lt;br&gt;        &lt;span class=&#34;hljs-regexp&#34;&gt;/http\:\/\/192\.168\.123\.1\:23234/g&lt;/span&gt;,&lt;br&gt;        &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;http://192.168.5.2:4022&amp;#x27;&lt;/span&gt;&lt;br&gt;      );&lt;br&gt;      response.&lt;span class=&#34;hljs-title function_&#34;&gt;setHeader&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Content-Type&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;text/html; charset=utf-8&amp;#x27;&lt;/span&gt;);&lt;br&gt;      &lt;span class=&#34;hljs-comment&#34;&gt;// Note: 获取 gist 的 raw 内容，需要加个 cache-bust 否则每次请求会被缓存&lt;/span&gt;&lt;br&gt;      &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;fetch&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;`&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;gist&amp;#125;&lt;/span&gt;?cache-bust=&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;&lt;span class=&#34;hljs-built_in&#34;&gt;Math&lt;/span&gt;.floor(&lt;span class=&#34;hljs-built_in&#34;&gt;Math&lt;/span&gt;.random() * &lt;span class=&#34;hljs-number&#34;&gt;100000&lt;/span&gt;)&amp;#125;&lt;/span&gt;`&lt;/span&gt;, &amp;#123;&lt;br&gt;        &lt;span class=&#34;hljs-attr&#34;&gt;headers&lt;/span&gt;: &amp;#123;&lt;br&gt;          &lt;span class=&#34;hljs-title class_&#34;&gt;Authorization&lt;/span&gt;: &lt;span class=&#34;hljs-string&#34;&gt;`Bearer &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;token&amp;#125;&lt;/span&gt;`&lt;/span&gt;,&lt;br&gt;        &amp;#125;,&lt;br&gt;      &amp;#125;)&lt;br&gt;        .&lt;span class=&#34;hljs-title function_&#34;&gt;then&lt;/span&gt;(&lt;span class=&#34;hljs-keyword&#34;&gt;async&lt;/span&gt; (pre) =&amp;gt; &amp;#123;&lt;br&gt;          &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; preGist = &lt;span class=&#34;hljs-keyword&#34;&gt;await&lt;/span&gt; pre.&lt;span class=&#34;hljs-title function_&#34;&gt;text&lt;/span&gt;();&lt;br&gt;          &lt;span class=&#34;hljs-comment&#34;&gt;// console.log(&amp;#x27;preGist:&amp;#x27;, preGist);&lt;/span&gt;&lt;br&gt;          &lt;span class=&#34;hljs-keyword&#34;&gt;if&lt;/span&gt; (&lt;span class=&#34;hljs-title class_&#34;&gt;JSON&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;stringify&lt;/span&gt;(newGist) !== &lt;span class=&#34;hljs-title class_&#34;&gt;JSON&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;stringify&lt;/span&gt;(preGist)) &amp;#123;&lt;br&gt;            &lt;span class=&#34;hljs-comment&#34;&gt;// Note: 更新 Gist&lt;/span&gt;&lt;br&gt;            &lt;span class=&#34;hljs-keyword&#34;&gt;const&lt;/span&gt; files = &amp;#123;&lt;br&gt;              &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;IPTV.m3u&amp;#x27;&lt;/span&gt;: &amp;#123;&lt;br&gt;                &lt;span class=&#34;hljs-attr&#34;&gt;content&lt;/span&gt;: newGist,&lt;br&gt;              &amp;#125;,&lt;br&gt;            &amp;#125;;&lt;br&gt;            &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;hljs-title function_&#34;&gt;fetch&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;`https://api.github.com/gists/&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;id&amp;#125;&lt;/span&gt;`&lt;/span&gt;, &amp;#123;&lt;br&gt;              &lt;span class=&#34;hljs-attr&#34;&gt;method&lt;/span&gt;: &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;PATCH&amp;#x27;&lt;/span&gt;,&lt;br&gt;              &lt;span class=&#34;hljs-attr&#34;&gt;headers&lt;/span&gt;: &amp;#123;&lt;br&gt;                &lt;span class=&#34;hljs-title class_&#34;&gt;Authorization&lt;/span&gt;: &lt;span class=&#34;hljs-string&#34;&gt;`Bearer &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;token&amp;#125;&lt;/span&gt;`&lt;/span&gt;,&lt;br&gt;                &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;Content-Type&amp;#x27;&lt;/span&gt;: &lt;span class=&#34;hljs-string&#34;&gt;&amp;#x27;text/plain&amp;#x27;&lt;/span&gt;,&lt;br&gt;              &amp;#125;,&lt;br&gt;              &lt;span class=&#34;hljs-attr&#34;&gt;body&lt;/span&gt;: &lt;span class=&#34;hljs-title class_&#34;&gt;JSON&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;stringify&lt;/span&gt;(&amp;#123; files &amp;#125;),&lt;br&gt;            &amp;#125;)&lt;br&gt;              .&lt;span class=&#34;hljs-title function_&#34;&gt;then&lt;/span&gt;(&lt;span class=&#34;hljs-function&#34;&gt;(&lt;span class=&#34;hljs-params&#34;&gt;s&lt;/span&gt;) =&amp;gt;&lt;/span&gt; &amp;#123;&lt;br&gt;                &lt;span class=&#34;hljs-variable language_&#34;&gt;console&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;log&lt;/span&gt;(&lt;br&gt;                  &lt;span class=&#34;hljs-string&#34;&gt;`更新成功: &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;gist&amp;#125;&lt;/span&gt;?cache-bust=&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;&lt;span class=&#34;hljs-built_in&#34;&gt;Math&lt;/span&gt;.floor(&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-subst&#34;&gt;&lt;span class=&#34;hljs-string&#34;&gt;                    &lt;span class=&#34;hljs-built_in&#34;&gt;Math&lt;/span&gt;.random() * &lt;span class=&#34;hljs-number&#34;&gt;1000000&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-subst&#34;&gt;&lt;span class=&#34;hljs-string&#34;&gt;                  )&amp;#125;&lt;/span&gt;`&lt;/span&gt;&lt;br&gt;                );&lt;br&gt;                &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; response.&lt;span class=&#34;hljs-title function_&#34;&gt;status&lt;/span&gt;(&lt;span class=&#34;hljs-number&#34;&gt;200&lt;/span&gt;).&lt;span class=&#34;hljs-title function_&#34;&gt;json&lt;/span&gt;(&amp;#123;&lt;br&gt;                  &lt;span class=&#34;hljs-attr&#34;&gt;msg&lt;/span&gt;: &lt;span class=&#34;hljs-string&#34;&gt;`更新成功: &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;gist&amp;#125;&lt;/span&gt;?cache-bust=&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;&lt;span class=&#34;hljs-built_in&#34;&gt;Math&lt;/span&gt;.floor(&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-subst&#34;&gt;&lt;span class=&#34;hljs-string&#34;&gt;                    &lt;span class=&#34;hljs-built_in&#34;&gt;Math&lt;/span&gt;.random() * &lt;span class=&#34;hljs-number&#34;&gt;1000000&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-subst&#34;&gt;&lt;span class=&#34;hljs-string&#34;&gt;                  )&amp;#125;&lt;/span&gt;`&lt;/span&gt;,&lt;br&gt;                &amp;#125;);&lt;br&gt;              &amp;#125;)&lt;br&gt;              .&lt;span class=&#34;hljs-title function_&#34;&gt;catch&lt;/span&gt;(&lt;span class=&#34;hljs-function&#34;&gt;(&lt;span class=&#34;hljs-params&#34;&gt;e&lt;/span&gt;) =&amp;gt;&lt;/span&gt; &amp;#123;&lt;br&gt;                &lt;span class=&#34;hljs-variable language_&#34;&gt;console&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;log&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;`更新失败: &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;e&amp;#125;&lt;/span&gt;`&lt;/span&gt;);&lt;br&gt;                &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; response.&lt;span class=&#34;hljs-title function_&#34;&gt;status&lt;/span&gt;(&lt;span class=&#34;hljs-number&#34;&gt;200&lt;/span&gt;).&lt;span class=&#34;hljs-title function_&#34;&gt;json&lt;/span&gt;(&amp;#123;&lt;br&gt;                  &lt;span class=&#34;hljs-attr&#34;&gt;msg&lt;/span&gt;: &lt;span class=&#34;hljs-string&#34;&gt;`更新失败: &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;e&amp;#125;&lt;/span&gt;`&lt;/span&gt;,&lt;br&gt;                &amp;#125;);&lt;br&gt;              &amp;#125;);&lt;br&gt;          &amp;#125;&lt;br&gt;          &lt;span class=&#34;hljs-variable language_&#34;&gt;console&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;log&lt;/span&gt;(&lt;br&gt;            &lt;span class=&#34;hljs-string&#34;&gt;`未变化: &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;gist&amp;#125;&lt;/span&gt;?cache-bust=&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;&lt;span class=&#34;hljs-built_in&#34;&gt;Math&lt;/span&gt;.floor(&lt;span class=&#34;hljs-built_in&#34;&gt;Math&lt;/span&gt;.random() * &lt;span class=&#34;hljs-number&#34;&gt;1000000&lt;/span&gt;)&amp;#125;&lt;/span&gt;`&lt;/span&gt;&lt;br&gt;          );&lt;br&gt;          &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; response.&lt;span class=&#34;hljs-title function_&#34;&gt;status&lt;/span&gt;(&lt;span class=&#34;hljs-number&#34;&gt;200&lt;/span&gt;).&lt;span class=&#34;hljs-title function_&#34;&gt;json&lt;/span&gt;(&amp;#123;&lt;br&gt;            &lt;span class=&#34;hljs-attr&#34;&gt;msg&lt;/span&gt;: &lt;span class=&#34;hljs-string&#34;&gt;`未变化: &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;gist&amp;#125;&lt;/span&gt;?cache-bust=&lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;&lt;span class=&#34;hljs-built_in&#34;&gt;Math&lt;/span&gt;.floor(&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-subst&#34;&gt;&lt;span class=&#34;hljs-string&#34;&gt;              &lt;span class=&#34;hljs-built_in&#34;&gt;Math&lt;/span&gt;.random() * &lt;span class=&#34;hljs-number&#34;&gt;1000000&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-subst&#34;&gt;&lt;span class=&#34;hljs-string&#34;&gt;            )&amp;#125;&lt;/span&gt;`&lt;/span&gt;,&lt;br&gt;          &amp;#125;);&lt;br&gt;        &amp;#125;)&lt;br&gt;        .&lt;span class=&#34;hljs-title function_&#34;&gt;catch&lt;/span&gt;(&lt;span class=&#34;hljs-function&#34;&gt;(&lt;span class=&#34;hljs-params&#34;&gt;e&lt;/span&gt;) =&amp;gt;&lt;/span&gt; &amp;#123;&lt;br&gt;          &lt;span class=&#34;hljs-variable language_&#34;&gt;console&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;log&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;`获取自己的 gist 失败: &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;e&amp;#125;&lt;/span&gt;`&lt;/span&gt;);&lt;br&gt;          &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; response.&lt;span class=&#34;hljs-title function_&#34;&gt;status&lt;/span&gt;(&lt;span class=&#34;hljs-number&#34;&gt;200&lt;/span&gt;).&lt;span class=&#34;hljs-title function_&#34;&gt;json&lt;/span&gt;(&amp;#123;&lt;br&gt;            &lt;span class=&#34;hljs-attr&#34;&gt;msg&lt;/span&gt;: &lt;span class=&#34;hljs-string&#34;&gt;`获取自己的 gist 失败: &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;e&amp;#125;&lt;/span&gt;`&lt;/span&gt;,&lt;br&gt;          &amp;#125;);&lt;br&gt;        &amp;#125;);&lt;br&gt;    &amp;#125;)&lt;br&gt;    .&lt;span class=&#34;hljs-title function_&#34;&gt;catch&lt;/span&gt;(&lt;span class=&#34;hljs-function&#34;&gt;(&lt;span class=&#34;hljs-params&#34;&gt;e&lt;/span&gt;) =&amp;gt;&lt;/span&gt; &amp;#123;&lt;br&gt;      &lt;span class=&#34;hljs-variable language_&#34;&gt;console&lt;/span&gt;.&lt;span class=&#34;hljs-title function_&#34;&gt;log&lt;/span&gt;(&lt;span class=&#34;hljs-string&#34;&gt;`获取别人的源失败: &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;e&amp;#125;&lt;/span&gt;`&lt;/span&gt;);&lt;br&gt;      &lt;span class=&#34;hljs-keyword&#34;&gt;return&lt;/span&gt; response.&lt;span class=&#34;hljs-title function_&#34;&gt;status&lt;/span&gt;(&lt;span class=&#34;hljs-number&#34;&gt;200&lt;/span&gt;).&lt;span class=&#34;hljs-title function_&#34;&gt;json&lt;/span&gt;(&amp;#123;&lt;br&gt;        &lt;span class=&#34;hljs-attr&#34;&gt;msg&lt;/span&gt;: &lt;span class=&#34;hljs-string&#34;&gt;`获取别人的源失败: &lt;span class=&#34;hljs-subst&#34;&gt;$&amp;#123;e&amp;#125;&lt;/span&gt;`&lt;/span&gt;,&lt;br&gt;      &amp;#125;);&lt;br&gt;    &amp;#125;);&lt;br&gt;&amp;#125;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;Corn Jobs 서비스 구성:&lt;/p&gt;
&lt;figure class=&#34;highlight json&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;code class=&#34;hljs json&#34;&gt;&lt;span class=&#34;hljs-punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-attr&#34;&gt;&amp;quot;crons&amp;quot;&lt;/span&gt;&lt;span class=&#34;hljs-punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;hljs-punctuation&#34;&gt;[&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;      &lt;span class=&#34;hljs-attr&#34;&gt;&amp;quot;path&amp;quot;&lt;/span&gt;&lt;span class=&#34;hljs-punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;/api/get&amp;quot;&lt;/span&gt;&lt;span class=&#34;hljs-punctuation&#34;&gt;,&lt;/span&gt;&lt;br&gt;      &lt;span class=&#34;hljs-attr&#34;&gt;&amp;quot;schedule&amp;quot;&lt;/span&gt;&lt;span class=&#34;hljs-punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;hljs-string&#34;&gt;&amp;quot;0 15 * * *&amp;quot;&lt;/span&gt;&lt;br&gt;    &lt;span class=&#34;hljs-punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;  &lt;span class=&#34;hljs-punctuation&#34;&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;hljs-punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;파일을 가져온 후 업데이트가 있으면 gist 파일이 자동으로 업데이트됩니다:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://gist.githubusercontent.com/Xheldon/73bf97cb5ac5db2f5237264556b20951/raw/ea44694028a38baefff04ea46c02795e448d76f0/IPTV.m3u&#34; target=&#34;_blank&#34;&gt; gist.githubusercontent.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;이렇게 하면, TV+에서 이 gist 주소를 하드코딩해두기만 하면, 네티즌이 이 주소를 업데이트할 때 자동으로 반영됩니다.&lt;/p&gt;
&lt;h2 id=&#34;참고-링크&#34;&gt;참고 링크&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://blog.lishun.me/iptvhelper-guide&#34; target=&#34;_blank&#34;&gt; 单线融合IPTV到家庭局域网最简单的方法：路由+桥接混合模式&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://blog.friskit.me/2020/05/31/bjunicom-network.html&#34; target=&#34;_blank&#34;&gt; 光纤入户光猫改桥接+内网转发IPTV=任意设备看电视直播 - Botian&#39;s Blog&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.haoyizebo.com/posts/6a0c2301/&#34; target=&#34;_blank&#34;&gt; 北京联通白嫖 IPTV&lt;/a&gt;&lt;/p&gt;</description>
            <pubDate>Mon, 30 Oct 2023 00:00:00 +0000</pubDate>
            <link>https://www.xheldon.com/ko/life/iptv-for-apple-tv-in-beijing.html</link>
            <guid isPermaLink="true">https://www.xheldon.com/ko/life/iptv-for-apple-tv-in-beijing.html</guid>
            
                <category>生活</category>
            
                <category>Apple</category>
            
                <category>折腾</category>
            
                <category>经验</category>
            
                <category>苹果</category>
            
                <category>网络</category>
            
                <category>路由器</category>
            
                <category>教程</category>
            
            
                <category>life</category>
            
        </item>
        
    </channel>
    </rss>