<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Daegun Choi on Medium]]></title>
        <description><![CDATA[Stories by Daegun Choi on Medium]]></description>
        <link>https://medium.com/@choiysapple?source=rss-1add52282342------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*zxM0cg9NkLDsgq_JPZrQEg.jpeg</url>
            <title>Stories by Daegun Choi on Medium</title>
            <link>https://medium.com/@choiysapple?source=rss-1add52282342------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Tue, 28 Apr 2026 13:20:02 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@choiysapple/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Checklist when Xcode MCP keep fails]]></title>
            <link>https://medium.com/@choiysapple/checklist-when-xcode-mcp-keep-fails-0e3c4d6a133f?source=rss-1add52282342------2</link>
            <guid isPermaLink="false">https://medium.com/p/0e3c4d6a133f</guid>
            <category><![CDATA[mcp-server]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[xcode]]></category>
            <category><![CDATA[claude]]></category>
            <dc:creator><![CDATA[Daegun Choi]]></dc:creator>
            <pubDate>Sat, 28 Mar 2026 11:06:52 GMT</pubDate>
            <atom:updated>2026-03-28T11:25:25.748Z</atom:updated>
            <content:encoded><![CDATA[<p>Apple is now providing <a href="https://developer.apple.com/documentation/xcode/giving-agentic-coding-tools-access-to-xcode">MCP for Xcode</a> since Xcode 26.3<br>But There’s some cases that codex or claude code keep failing to connect xcode mcp.</p><p>Here’s some checklist for you to fix this situation.</p><p><a href="https://developer.apple.com/documentation/xcode/giving-agentic-coding-tools-access-to-xcode">Giving external agentic coding tools access to Xcode | Apple Developer Documentation</a></p><h3>Minimum Requirements</h3><p>Before you start, Here’s requirements to use Xcode MCP (which is same with requirements of Xcode Agentic Coding)</p><ol><li>Apple Silicon (Not Intel Mac 😢)</li><li>Xcode 26.3</li><li>MacOS 26</li></ol><h3>1. Settings in Xcode</h3><p>This is setting that enables Xcode mcp. The toggle switch should be on.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fqkiTEYYl0ptb5YxKWD0Mw.png" /><figcaption>Xcode &gt; Settings &gt; Intelligence &gt; Model Context Protocol</figcaption></figure><h3>⭐ 2.️ Check default Xcode version</h3><p>If you installed Xcode from <a href="https://xcodereleases.com/">Xcode Releases</a> or <a href="https://developer.apple.com/download/applications/">Apple Developer</a>, your default Xcode version might be older one.</p><p><strong>Default Xcode version should be 26.3 or above<br></strong>Use xcode-select command to change default setting to your latest Xcode</p><pre>sudo xcode-select -s &lt;path/to/xcode&gt;</pre><h3>3. Allow Claude &amp; Codex to access Xcode</h3><p>Every time Claude &amp; Codex launches, Claude and Codex should get permission from Xcode.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/744/1*CS82F4rK1VepHz_OwZF2Bg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/744/1*WgF8v8q2xJP_xdE7Ptsq2g.png" /></figure><p>Make sure press “<strong>Allow</strong>” from this popup.<br>Accidentally dismissed this? Just <strong>reopen</strong> Claude or Codex.</p><h3>4. Make sure Xcode is running</h3><p>Xcode should be running to use this MCP.<br>But You don’t have to open project’ s window alive. Just not quitting (⌘ + q) is enough.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/216/1*zSfdolGnztfd7JfM6_JeqA.png" /><figcaption>Make sure there’s dot under the Xcode Icon</figcaption></figure><h3>⭐ 5. Check current directory</h3><p>If you run Xcode MCP setting command on terminal, it’s available only at current directory.</p><p>You should run claude mcp add or codex mcp add if you are using xcode mcp on that directory for the first time.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=0e3c4d6a133f" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[WWDC25 — iOS 개발자가 체크해야 할 부분들 (UIKit)]]></title>
            <link>https://medium.com/@choiysapple/wwdc25-ios-%EA%B0%9C%EB%B0%9C%EC%9E%90%EA%B0%80-%EC%B2%B4%ED%81%AC%ED%95%B4%EC%95%BC-%ED%95%A0-%EB%B6%80%EB%B6%84%EB%93%A4-uikit-6075741cb4b8?source=rss-1add52282342------2</link>
            <guid isPermaLink="false">https://medium.com/p/6075741cb4b8</guid>
            <category><![CDATA[xcode]]></category>
            <category><![CDATA[ios-app-development]]></category>
            <category><![CDATA[liquid-glass]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[swift]]></category>
            <dc:creator><![CDATA[Daegun Choi]]></dc:creator>
            <pubDate>Sun, 21 Dec 2025 15:17:48 GMT</pubDate>
            <atom:updated>2025-12-21T15:17:48.291Z</atom:updated>
            <content:encoded><![CDATA[<h3>WWDC25 — iOS 개발자가 체크해야 할 부분들 (UIKit)</h3><p>WWDC25가 반년정도 지났고, 슬슬 Xcode 26으로 프로젝트를 마이그레이션 마쳐야 할 시기가 다가오고 있습니다. (항상 그렇듯, 이번에도 <strong>4월</strong>까지 마쳐야 합니다.)</p><p><a href="https://developer.apple.com/app-store/submitting/#latest-releases">Submitting - App Store - Apple Developer</a></p><p>신규 SDK 대응은 필요하지만, WWDC 영상들을 여럿 보기에는 시간이 좀 걸리죠</p><p>새로운 변경사항에 대응하기 위해 개인적으로 정리한 내용과, Xcode 26 SDK로 마이그레이션 하면서 신경썻던 부분에 대해 공유하려고 합니다.</p><p>늦게라도 마이그레이션을 시작하시는 분들께 도움이 되었으면 좋겠네요 😀</p><blockquote>‼️ UIkit 베이스의 iOS 프로젝트를 운영하고 있어, SwiftUI와 타 OS에 특화된 내용은 생략되어 있습니다. 양해 부탁드립니다 🙇</blockquote><h3>1. 바로 확인해야 할 부분</h3><h4>1.1. Liquid Glass</h4><p><a href="https://medium.com/@chcs1370/wwdc25-%EC%83%88%EB%A1%9C%EC%9A%B4-%EB%94%94%EC%9E%90%EC%9D%B8%EC%9C%BC%EB%A1%9C-uikit-%EC%95%B1-%EB%B9%8C%EB%93%9C%ED%95%98%EA%B8%B0-b53ffb495b67">WWDC25 — 새로운 디자인으로 UIKit 앱 빌드하기</a></p><p>이번 업데이트의 가장 큰 부분은 단연 Liquid Glass 입니다.<br>Liquid Glass가 적용되면 외형이나 레이아웃이 바뀌거나, 원하지 않는 Liquid Glass 효과가 추가되기 때문에 확인이 필요합니다.</p><p><strong>‼️ Navigation Bar</strong>, <strong>Tab bar</strong>, <strong>Toggle Switch</strong>는 꼭 체크해 보시는 것을 추천드립니다.</p><p><strong>UIDesignRequiresCompatibility<br></strong>info.plist에서 UIDesignRequiresCompatibility를 추가해 Liquid Glass 디자인을 비활성화 할 수 있습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/720/1*mpYDp69qGqSsS_YXOj7oEA.png" /></figure><ul><li>YES → Liquid glass 기존 디자인으로 빌드</li><li>NO → Liquid glass 디자인으로 빌드</li></ul><p><strong>‼️ </strong>해당 옵션은 <strong>임시로 지원</strong>되는 기능이기 때문에, Liquid Glass 활성화 상태에서도 문제가 없도록 조치가 필요합니다.</p><p>되도록이면 iOS 27이 출시되기 전에 하는게 안전하겠죠?</p><p>참고 자료:</p><ul><li><a href="https://developer.apple.com/documentation/BundleResources/Information-Property-List/UIDesignRequiresCompatibility">UIDesignRequiresCompatibility | Apple Developer Documentation</a></li><li><a href="https://medium.com/@battello.theo/how-to-disable-liquid-glass-when-building-for-ios-26-ed81d03f7633">How to Disable Liquid Glass When Building for iOS 26</a></li><li><a href="https://www.youtube.com/watch?v=y8gnwh2cpxo">How to Temporarily Disable the New Liquid Glass UI in Xcode 26</a></li></ul><h4>2. Swift <strong>Explicit Module</strong></h4><p><strong>Xcode 26.2</strong>에서는 지난 WWDC24에서 소개된 <a href="https://developer.apple.com/kr/videos/play/wwdc2024/10171/">Explicit Modules </a>기능이 기본으로 활성화 되게 변경되었습니다.</p><p>Explicit Modules 기능이 활성화되면, 기존에 암시적으로 프레임워크를 참조하는 모듈에서 빌드 에러가 발생할 수 있습니다. <br>(제 경우에는, 암시적 참조중인 프레임워크들에 대해 ‘Unable to find module dependency…’ 라는 에러가 발생되었습니다.)</p><p>아래 링크를 참조하셔서 수정하실 수 있습니다만, 장기적으로는 모듈 의존성 개선을 고려해 봐야겠습니다.</p><ol><li>후이수님 블로그 포스트 — <a href="https://huisoo.tistory.com/34">[Xcode26] Unable to find module dependency 빌드 에러 해결</a></li><li><a href="https://developer.apple.com/documentation/xcode-release-notes/xcode-26-release-notes">Xcode 26 Release Notes</a></li></ol><h4>1.3. Accent</h4><p>Liquid Glass와 함께 새로운 강조 모드가 추가되면서, 강조 모드에서 위젯이 어떻게 보이는지 확인할 필요가 있습니다.</p><p>제 포스트에 사진과 함께 정리되어 있으니, 확인해 보세요</p><p><a href="https://medium.com/@chcs1370/wwdc25-%EC%9C%84%EC%A0%AF%EC%9D%98-%EC%83%88%EB%A1%9C%EC%9A%B4-%EA%B8%B0%EB%8A%A5-27d5ee0611de">WWDC25 — 위젯의 새로운 기능</a></p><h3>2. 추후 검토해 봐야 할 부분</h3><h4>2.1. UIScene으로의 변화</h4><p><a href="https://medium.com/@chcs1370/wwdc25-uikit%EC%9D%98-%EC%83%88%EB%A1%9C%EC%9A%B4-%EA%B8%B0%EB%8A%A5-16598a8ecbd6">WWDC25 — UIKit의 새로운 기능</a></p><p>많은 분들이 UIApplication를 기반으로 프로젝트가 구성되어 계실텐데요 애플이 UIScene 라이프사이클을 사용하도록 변경을 예고했습니다.</p><ul><li>UIApplicaiton 중심 API deprecated 예정<br>(UIWindow 용 init(windowScene:) 생성자만 남을 것)</li><li>iOS 26이후 릴리즈에서, 최신 SDK로 구축된 UIKit 앱은 전부 UIScene 라이프사이클을 사용해야만 실행 가능</li></ul><h4>2.2. Swift 6 vs. Swift 6.2</h4><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2F7MGLTYxIlXs%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D7MGLTYxIlXs&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2F7MGLTYxIlXs%2Fhqdefault.jpg&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/170f0fef8577dc8d5b38acb81a887286/href">https://medium.com/media/170f0fef8577dc8d5b38acb81a887286/href</a></iframe><p>Swift 6를 도입하면 수많은 컴파일 에러를 마주하게 됩니다. Actor를 명시해주느라 너무 많은 작업이 필요했죠.</p><p>Swift 6.2 부터는 Actor가 명시적으로 지정되어 있지 않으면, 암시적으로 @MainActor로 간주하도록 바뀌었습니다.</p><p>아직 Swift 5 버전을 사용중이시라면, Swift 6이 아닌 Swift 6.2로 마이그레이션 하는 것을 고려해볼 만 할 것 같네요.</p><h4>3. 번외: Xcode 추가 기능</h4><p><a href="https://medium.com/@chcs1370/wwdc25-xcode-26%EC%9D%98-%EC%83%88%EB%A1%9C%EC%9A%B4-%EA%B8%B0%EB%8A%A5-7c6b7fb627fe">WWDC25 — Xcode 26의 새로운 기능</a></p><p>Xcode에 Intelligence도입과 더불어, 쉽게 사용해 볼 수 있는 기능들이 추가되었습니다.디버그 관련 기능도 대폭 개선되었네요.</p><p>개인적으로는 Multiple word search와 #Playground를 요긴하게 쓸 것 같네요</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6075741cb4b8" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[WWDC25 — Xcode 26의 새로운 기능]]></title>
            <link>https://medium.com/@choiysapple/wwdc25-xcode-26%EC%9D%98-%EC%83%88%EB%A1%9C%EC%9A%B4-%EA%B8%B0%EB%8A%A5-7c6b7fb627fe?source=rss-1add52282342------2</link>
            <guid isPermaLink="false">https://medium.com/p/7c6b7fb627fe</guid>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[wwdc]]></category>
            <category><![CDATA[ios-app-development]]></category>
            <category><![CDATA[xcode]]></category>
            <dc:creator><![CDATA[Daegun Choi]]></dc:creator>
            <pubDate>Sat, 20 Dec 2025 08:40:15 GMT</pubDate>
            <atom:updated>2025-12-20T08:40:15.524Z</atom:updated>
            <content:encoded><![CDATA[<h3>WWDC25 — Xcode 26의 새로운 기능</h3><p><a href="https://developer.apple.com/kr/videos/play/wwdc2025/247/">Xcode 26의 새로운 기능 - WWDC25 - 비디오 - Apple Developer</a></p><h3>Optimizations</h3><ul><li>용량 24% 감소</li><li>타이핑 지연 시간 50% 개선</li><li>워크스페이스 로딩 시간 40% 개선</li></ul><h3>Workspace and editing</h3><h4>Intuitive editor tabs</h4><ul><li>시작 페이지 (검색창, 최근 연 파일)</li><li>⭐️ 상단 탭 고정 기능</li></ul><h4>⭐️ Multiple word search</h4><blockquote>단어가 여러 줄에 나뉘어 있어도 검색하는 기능</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7tuiSYXR7Y7cFFmLYjRzeg.png" /></figure><h4>Coding by voice</h4><ul><li>입코딩 가능</li><li>접근성 기능에 더 가깝고, 신기하지만 실제로 사용할 일을 없을 것 같네요.</li></ul><h4>⭐️ #Playground</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*pW2uf39Ijo-7H0aotxx_8Q.png" /></figure><ul><li>빌드 안해보고도 코드 실행, 결과 확인해 볼 수 있는 기능 (Playground 우측 탭 기능)</li><li>breakpoint 찍어서 po 명령어 사용하는것과 비슷하게 사용 가능</li></ul><h4>Icon Composer</h4><blockquote>OS, 테마별 아이콘 생성 기능</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*RbhebQy8Wh4tkp33C-9H9Q.png" /></figure><h4>String Catalogs</h4><ul><li>타입 안정성 추가</li><li>온디바이스 모델로 자동 주석</li></ul><h3>⭐️ Intelligence</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HOVkuSZ4r-hPrvb0MczyQQ.png" /></figure><ul><li>gpt에 직접 물어보는 기본 기능</li><li>프로젝트에 연결되어 있기 때문에, 기존 코드 분석 &amp; 수정까지 가능</li><li>프로젝트 맥락에 맞춰 답변할지에 관한 기능 on/off</li><li>gpt가 변경한 히스토리 백업 → 특정 시점으로 롤백, 비교</li><li>Error 발생시 자동 솔루션 제공</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/782/1*vgBrEYz-mmrgRfgx0w_aYg.png" /></figure><ul><li>원하는 모델 임포트 가능</li><li>로컬 모델도 임포트 가능</li></ul><h3>Debugging and performance</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kMmGhXhiVW_D_a_PHeOSiA.png" /></figure><ul><li>Swift Concurrency 디버깅 개선</li><li>Task의 ID 조회 가능</li><li>Task, TaskGroups, Actor 정보 표시</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Z7gjn7waTi0v9ZDvQeqYOg.png" /></figure><ul><li>비공개 리소스 접근(i.e. 카메라) 설명 없이 오류가 발생하는 경우</li><li>이제는 에러에 자세한 설명, 문서까지 제공</li></ul><h3>Instruments</h3><h4>Processor Trace</h4><ul><li>기존에는 주기적 샘플링 (비교적 정확도 떨어짐) → 측정 방법 변경으로 거의 모든 부분을 캡쳐할 수 있음</li><li>Xcode 16.3+</li><li>iPhone 16 or M4</li></ul><h4>CPU Counters</h4><ul><li>CPU 성능 최적화 도구</li><li>병목 체크나, 더 자세한 CPU 사용량 체크</li></ul><h4>SwiftUI Percormance</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/994/1*5BNzYXFQBEWfQOt5DrSmAA.png" /></figure><ul><li>이번 SwiftUI 업데이트로 성능 상당히 개선됨</li><li>SwiftUI 전용 Instruments 지원</li><li>뷰 업데이트 시점 같은 정보 제공, 그래프화까지</li></ul><h4>Power Profiler</h4><ul><li>전력 사용량 체크</li></ul><h4>Trending Insights</h4><ul><li>Xcode 16에서 지원되었던 기능 개선</li></ul><h4>Metrics</h4><ul><li>배터리 사용량, Disk Write, Launch Time, Memory 등의 지표 체크</li><li>비슷한 앱들의 수치 제공</li></ul><h3>Building</h3><h4>Explicit Modules for Swift</h4><p><a href="https://developer.apple.com/videos/play/wwdc2024/10171/">Demystify explicitly built modules - WWDC24 - Videos - Apple Developer</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*80cvhK2aNE9thjkimQbsfw.png" /></figure><ul><li>빌드 파이프라인 분리됨</li><li>모듈 공유로 빌드 효율성 / 신뢰성 강화</li><li>코드 디버깅 속도 향상 (빌드된 모듈 재사용성 향상)</li></ul><h4>Swift Build+</h4><ul><li>스위프트 빌드 엔진개선</li></ul><h3>Enhance Security</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*iLFy7OenDWrPvp3ZIe7KWw.png" /></figure><ul><li>Xcode의 Signing &amp; Capabilities에서 지원하는 정보 보호 기능</li></ul><h3>Testing</h3><h4>UI Test Recording</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fIJSxm5s2gWtu2Uowk_i0A.png" /></figure><ul><li>시뮬레이터에서 유저 인터렉션 녹화</li><li>녹화된 내용으로 UI 테스트 코드 자동 생성</li></ul><h4>Test Report</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*WaEZbApTOtADQZAeKF44vg.png" /></figure><h4>XCTHitchMetric</h4><blockquote>UI테스트에서 발견되는 끊김 현상 기록</blockquote><pre>// XCTHitchMetric<br>func testScrollingAnimationPerformance() throws {<br>    // Custom performance test measure options.<br>    let measureOptions = XCTMeasureOptions()<br>    measureOptions.invocationOptions = .manuallyStop<br>    // App being tested.<br>    let app = XCUIApplication()<br>    // Launch app and get reference to scroll view.<br>    app.launch()<br>    let scrollView = app.scrollViews.firstMatch<br>    measure(metrics: [XCTHitchMetric(application: app)], options: measureOptions) {<br>        scrollView.swipeUp(velocity: .fast)<br>        stopMeasuring()<br>        scrollView.swipeDown(velocity: .fast)<br>    }<br>}</pre><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7c6b7fb627fe" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[WWDC25 — 위젯의 새로운 기능]]></title>
            <link>https://medium.com/@choiysapple/wwdc25-%EC%9C%84%EC%A0%AF%EC%9D%98-%EC%83%88%EB%A1%9C%EC%9A%B4-%EA%B8%B0%EB%8A%A5-27d5ee0611de?source=rss-1add52282342------2</link>
            <guid isPermaLink="false">https://medium.com/p/27d5ee0611de</guid>
            <dc:creator><![CDATA[Daegun Choi]]></dc:creator>
            <pubDate>Sat, 20 Dec 2025 08:19:07 GMT</pubDate>
            <atom:updated>2025-12-20T08:24:48.254Z</atom:updated>
            <content:encoded><![CDATA[<h3>WWDC25 — 위젯의 새로운 기능</h3><p><a href="https://developer.apple.com/kr/videos/play/wwdc2025/278/">위젯의 새로운 기능 - WWDC25 - 비디오 - Apple Developer</a></p><h3>Widgets in new places</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/690/1*Kp6md5vGOhXXXmnHNW5SiQ.png" /></figure><h4>Accented Rendering mode</h4><ul><li>Liquid Glass 스타일 위젯</li><li>투명 스타일 위젯</li><li>투명 + tint color가 들어간 스타일 위젯</li><li>랜더링 방식<br>1. 위젯 컨텐츠가 강조 모드로 변경, tint color가 흰색으로 강제 지정<br>2. 뒷배경 삭제<br>3. 뒷배경을 유리 또는 tint color 효과로 변경</li><li>특정 위젯의 경우에는, 해당 랜더링에 대응하는 처리 필요</li></ul><h3>강조 모드 대응</h3><pre>struct MostFrequentBeverageWidgetView: View {<br>    @Environment(\.widgetRenderingMode) var renderingMode<br><br>    var entry: Entry<br><br>    var body: some View {<br>        ZStack {<br>            if renderingMode == .fullColor {<br>                Image(entry.beverageImage)<br>                    .resizable()<br>                    .aspectRatio(contentMode: .fill)<br>                LinearGradient(gradient: Gradient(colors: [.clear, .clear, .black.opacity(0.8)]), startPoint: .top, endPoint: .bottom)<br>            }<br>            VStack {<br>                if renderingMode == .accented {<br>                    Image(entry.beverageImage)<br>                        .resizable()<br>                        .widgetAccentedRenderingMode(.desaturated)<br>                        .aspectRatio(contentMode: .fill)<br>                }<br>                BeverageTextView()<br>            }<br>        }<br>    }<br>}</pre><ul><li>./widgetRenderingMode 환경 변수를 사용해 랜더링 모드 판단</li><li>widgetAccentedRenderingMode modifier에서 강조 모드에서 보여질 방식 설정</li></ul><h3>widgetAccentedRenderingMode</h3><h4>1. nil</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/993/1*oIoyTK95z3YLnNDvhnxMBA.png" /></figure><ul><li>기본 tint color가 이미지에 적용</li><li>iOS와 macOS는 tint color가 흰색으로 변경된 후 적용</li></ul><h4>2. accented</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1006/1*bICUJrKbd4Dul2Dt4GuTaA.png" /></figure><ul><li>이미지가 accentColor로 변경</li><li>iOS와 macOS는 흰색 / WatchOS의 경우는 와치페이스의 색상이 accentColor</li></ul><h4>3. desaturated</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/981/1*WVbn-yxID06jqueeHWUIpA.png" /></figure><ul><li>이미지 색상 채도가 낮아지는 옵션</li><li>iOS, WatchOS 동일하게 표출</li></ul><h4>4. accentedDesaturated</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1005/1*WhCHmkIY4KbTok0wpmQc3g.png" /></figure><ul><li>.desaturated + tint color 적용</li></ul><h4>5. fullColor</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/928/1*GfsBY0UpbSViI4UK_W4BHQ.png" /></figure><ul><li>이미지 풀컬러로 그대로 표출</li><li>WatchOS에서는 와치페이스와 어울리도록 이 옵션이 무시됨</li></ul><h3>Relevance widgets</h3><ul><li>WatchOS에서 필요할 때만 Widget이 Smart Stack에 표출되게 하는 기능</li></ul><h3>Widget push updates</h3><blockquote><em>여러 플랫폼에서 위젯을 최신 상태로 업데이트 하는 방법</em></blockquote><h4>TimelineReloadPolicy</h4><blockquote><em>일정 간격으로 업데이트 할 때 사용</em></blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/632/1*Vh7wJAWKZst_DG-DIKrmIA.png" /></figure><ul><li>WidgetKit이 Widget에 타임라인 요청</li><li>Widget은 TimelineReloadPolicy를 포함한 타임라인 정보 반환</li><li>WidgetKit에서 위젯을 업데이트할 적절한 시간 결정</li></ul><h4>Widget Center API → reloadAllTimelines</h4><blockquote><em>주로 앱 내에서 컨텐츠가 변경되는 경우</em></blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/630/1*oCXvB4e_fIRq_sAzykIyZg.png" /></figure><ul><li>위젯 컨텐츠가 오래되어 업데이트가 필요함을 WidgetKit에 알림</li><li>WidgetKit이 위젯에 타임라인 요청</li><li>Widget에서 타임라인 갱신</li></ul><h4>Widget push update</h4><blockquote><em>서버나 다른 기기에서 데이터가 변경되는 경우</em></blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/899/1*kCSVVFhwUnEj4NNyQol5IA.png" /></figure><ul><li>APNs를 통해서 WidgetKit에 업데이트 요청을 전달하는 방식</li><li>여러 기기에 있는 모든 위젯 최신화할 때 사용</li></ul><h4>Adding push notification support</h4><p><strong>1. </strong><strong>WidgetPushHandler 를 Widget Configuraion에 추가</strong></p><pre>// pushTokenDidChange 메서드로 서버에 푸시 토큰 &amp; 위젯 정보 전송<br>struct CaffeineTrackerPushHandler: WidgetPushHandler {<br>    func pushTokenDidChange(_ pushInfo: WidgetPushInfo, widgets: [WidgetInfo]) {<br>        // Send push token and subscription info to server<br>    }<br>}</pre><pre>// pushHandler 를 사용해 푸시 알림에 대한 지원 등록<br>struct CaffeineTrackerWidget: Widget {<br>    var body: some WidgetConfiguration {<br>        StaticConfiguration(<br>            kind: Constants.widgetKind,<br>            provider: Provider()<br>        ) { entry in<br>            CaffeineTrackerWidgetView(entry: entry)<br>        }<br>        .configurationDisplayName(&quot;Caffeine Tracker&quot;)<br>        .pushHandler(CaffeineTrackerPushHandler.self)<br>    }<br>}</pre><p><strong>2. Push Notification entitlement 추가</strong></p><ul><li>푸시를 사용하는 기능이니, entitlement 작업이 필요합니다.</li></ul><p><strong>3. Widget update 푸시 요청 구성</strong></p><pre>:method = POST<br>:scheme = https<br>:path = /3/device/&lt;DEVICE_TOKEN&gt;<br><br>// Headers<br>host = api.sandbox.puth.apple.com<br>apns-push-type = widgets<br>apns-topic = com.example.myApp.push-type.widgets   // 앱 번들 ID<br>{<br>    &quot;aps&quot;: {<br>        &quot;content-changed&quot;: true<br>    }<br>}<br></pre><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=27d5ee0611de" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[WWDC25 — UIKit의 새로운 기능]]></title>
            <link>https://medium.com/@choiysapple/wwdc25-uikit%EC%9D%98-%EC%83%88%EB%A1%9C%EC%9A%B4-%EA%B8%B0%EB%8A%A5-16598a8ecbd6?source=rss-1add52282342------2</link>
            <guid isPermaLink="false">https://medium.com/p/16598a8ecbd6</guid>
            <dc:creator><![CDATA[Daegun Choi]]></dc:creator>
            <pubDate>Thu, 18 Dec 2025 11:45:05 GMT</pubDate>
            <atom:updated>2025-12-18T11:50:22.929Z</atom:updated>
            <content:encoded><![CDATA[<h3>WWDC25 — UIKit의 새로운 기능</h3><p><a href="https://developer.apple.com/kr/videos/play/wwdc2025/243/">UIKit의 새로운 기능 - WWDC25 - 비디오 - Apple Developer</a></p><h3>New design system</h3><blockquote>Liquid Glass 디자인에 맞게 변경되었습니다</blockquote><h3>Containers and adaptivity</h3><blockquote>iPadOS와 MacOS에서 반응형 UI 관련 부분이 변경되었습니다.</blockquote><h3>The menu bar</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1pbSKAfGHFQ-wh-CGgeC2g.png" /></figure><ul><li>iPad에 macOS의 메뉴 막대가 등장합니다.</li><li>기존 Storyboard로 지원하던 menubar 미지원이기 때문에 직접 구현해야 합니다.</li></ul><h3>Architectural improvements</h3><h4>Automatic observation tracking</h4><blockquote><em>UIKit에서 Swift Observable 지원 기능 추가</em></blockquote><ul><li>layoutSubViews() 처럼 update 메서드에 연결 → 수동 setNeedsLayout 없어도 된다!</li><li>Info.plist의 UIObservationTrackingEnabled 추가해 재배포하면 iOS 18 에서도 지원 가능 (iOS 26에선 default)</li></ul><pre>// Using an Observable object and automatic observation tracking<br>@Observable class UnreadMessagesModel {<br>    var showStatus: Bool<br>    var statusText: String<br>}<br>class MessageListViewController: UIViewController {<br>    var unreadMessagesModel: UnreadMessagesModel<br>    var statusLabel: UILabel<br>    override func viewWillLayoutSubviews() {<br>        super.viewWillLayoutSubviews()<br>        statusLabel.alpha = unreadMessagesModel.showStatus ? 1.0 : 0.0<br>        statusLabel.text = unreadMessagesModel.statusText<br>    }<br>}</pre><ul><li>@Observable 객체인 UnreadMessagesModel 가 변경<br> → viewWillLayoutSubviews() 가 동작하면서 자동으로 화면 업데이트</li></ul><pre>// Configuring a UICollectionView cell with automatic observation tracking<br>@Observable class ListItemModel {<br>    var icon: UIImage<br>    var title: String<br>    var subtitle: String<br>}<br>func collectionView(<br>    _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath<br>) -&gt; UICollectionViewCell {<br>    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: &quot;Cell&quot;, for: indexPath)<br>    let listItemModel = listItemModel(for: indexPath)<br>    cell.configurationUpdateHandler = { cell, state in<br>        var content = UIListContentConfiguration.subtitleCell()<br>        content.image = listItemModel.icon<br>        content.text = listItemModel.title<br>        content.secondaryText = listItemModel.subtitle<br>        cell.contentConfiguration = content<br>    }<br>    return cell<br>}</pre><ul><li>cell에 관찰 추적을 지원하는 configurationUpdateHandler 사용</li><li>해당 메서드 내부의 handler closure에서 @Observable 사용 → 모델 handler를 재실행해 cell 업데이트</li></ul><h3>New UI update Method → updateProperties()</h3><ul><li>UIView 와 UIViewController 에 추가된 메서드</li><li>layoutSubviews() 직전에 실행 But 독립적이기 때문에 레이아웃 강제 없이 속성 무효화 / 세밀한 업데이트 가능</li><li>layoutSubviews() 대체가 아닌 보완 (콘텐츠 추가, 스타일 적용 등)</li><li>setNeedsUpdateProperties() 를 호출해 수동으로 트리거 가능</li></ul><pre>// Using automatic observation tracking and updateProperties()<br>@Observable class BadgeModel {<br>   var badgeCount: Int?<br>}<br>class MyViewController: UIViewController {<br>   var model: BadgeModel<br>   let folderButton: UIBarButtonItem<br>    override func updateProperties() {<br>        super.updateProperties()<br>        if let badgeCount = model.badgeCount {<br>            folderButton.badge = .count(badgeCount)<br>        } else {<br>            folderButton.badge = nil<br>        }<br>   }<br>}</pre><ul><li>BadgeModel 이 변경될 떄 마다 updateProperties() 가 실행되어 badge 업데이트</li><li>layoutSubviews() 대신 updateProperties() 를 사용하면, 크기 설정 같은 코드는 동작하지 않음 → 불필요한 작업을 줄이고 성능 개선</li></ul><h3>Update passes</h3><h4>기존 Update passes</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Q3SPt89u9UOMK_d5ksSPqA.png" /><figcaption>스크린에 UI가 표시되기 전까지의 Flow</figcaption></figure><ol><li>layout pass가 먼저 실행<br>- 최상위 계층에서 부터 각 뷰의 traits 업데이트 &amp; layoutSubViews() 실행</li><li>레이아웃이 자리를 잡으면 display pass 실행<br>- 각 뷰에서 draw() 실행<br>- 더이상 어떤 뷰에도 display pass가 필요 없을 때 까지 반복</li><li>두 pass가 모두 끝나면 다음 프레임 랜더링, 화면에 UI 표시</li></ol><h3>신규 Update passes</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nfZfz4VX1bYsHHjRJgsIag.png" /></figure><ul><li>layout pass 중간에 updateProperties() 추가</li><li>trait 업데이트 직후에 실행되기 때문에, <strong>trait을 안전하게 read 가능</strong></li><li>layoutSubViews() 직전에 실행되기 때문에 <strong>미리 레이아웃 무효화 가능</strong></li></ul><h3>Automatically flush updates with animations</h3><h3>기존 Animation 동작 (iOS 18 이하)</h3><pre>// Manually animating changes with Observable objects<br>UIView.animage {<br>        // Set the new badge color on the Observable model object<br>        model.badgeColor = .red<br>// Manually flush updates on the view with animation<br>        badgeView.layoutIfNeeded()<br>}</pre><ul><li>수동으로 trait과 View 의존성을 유지하면 오류가 빈번하다</li><li>업데이트 or 애니메이션이 너무 많거나 적게 생성</li></ul><h3>Automatic flush update (iOS 26)</h3><ul><li>flushUpdates 라는 새로운 애니메이션 추가</li><li>보류하던 업데이트를 애니메이션 직전/직후에 적용 → layoutIfNeeded() 호출 필요 없음</li><li>flushUpdates 를 적용하려면 변경사항이 무조건 클로저 내에 있어야 한다</li></ul><pre>// Using the flushUpdates animation option to automatically animate updates<br>// Automatically animate changes with Observable objects<br>UIView.animate(options: .flushUpdates) {<br>    model.badgeColor = .red<br>}</pre><ul><li>@Observable 객체인 badgeColor 를 사용하는 모든 뷰가 업데이트 자동 사용</li></ul><pre>// Automatically animate changes to Auto Layout constraints<br>UIView.animate(options: .flushUpdates) {<br>    // Change the constant of a NSLayoutConstraint<br>    topSpacingConstraint.constant = 20<br>    // Change which constraints are active<br>    leadingEdgeConstraint.isActive = false<br>    trailingEdgeConstraint.isActive = true<br>}</pre><ul><li>Constraint에서도 당연히 사용 가능</li></ul><h3>Scene updates</h3><blockquote><em>SwiftUI와 UIKit의 혼용</em></blockquote><ul><li>UIHostingSceneDelegate 추가</li><li>해당 delegate를 사용하는 UIkit 앱에서 SwiftUI Scene 지원 → UIKit에 VisionOS의 몰입형 공간과 볼륨 사용 가능</li></ul><h3>General enhancements</h3><h4>HDR color support</h4><ul><li>이미지 뿐만 아니라 UI에도 HDR 색상 사용 가능</li><li>기본 색상 값 + 노출 값</li><li>기본 Color Picker에서도 노출값 지원</li></ul><pre>// Create an HDR red relative to a 2.5x peak white<br>let hdrRed = UIColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0, linearExposure: 2.5)</pre><h3>Swift nofications</h3><blockquote><em>기존 NSNotificaiton이 NotificaitonCenter으로 변경</em></blockquote><ul><li>Strongly typed Payload → 이제 일일이 타입캐스팅 안해도 된다</li><li>Swift Concurrency 호환성 개선</li></ul><pre>// Adopting Swift notifications<br>override func viewDidLoad() {<br>    super.viewDidLoad()<br>    let keyboardObserver = NotificationCenter.default.addObserver(<br>        of: UIScreen.self<br>        for: .keyboardWillShow<br>    ) { message in<br>        UIView.animate(<br>            withDuration: message.animationDuration, delay: 0, options: .flushUpdates<br>        ) {<br>            // Use message.endFrame to animate the layout of views with the keyboard<br>            let keyboardOverlap = view.bounds.maxY - message.endFrame.minY<br>            bottomConstraint.constant = keyboardOverlap<br>        }<br>    }<br>}</pre><h3>Embrace flexibility</h3><ul><li>UIScene 으로의 변화 필요</li><li>UIApplicaiton 중심 API deprecated 예정</li><li>UIWindow 용 init(windowScene:) 생성자만 남을 것</li><li>iOS 26이후 릴리즈에서, 최신 SDK로 구축된 UIKit 앱은 전부 UIScene 라이프사이클을 사용해야만 실행 가능</li><li>UIRequieresFullScreen 이 deprecated 예정 (iPadOS)</li></ul><h3>OpenURL supprots file URLs</h3><blockquote><em>기존 openURL메서드에 파일 URL도 허용</em></blockquote><ul><li>앱에서 미지원하는 문서도 사용가능</li><li>앱에서 미지원 되는 문서는 시스템이 외부 앱을 실행하고 URL을 따라 전달</li></ul><h3>SF Symbols 7</h3><ul><li>애니메이션 모드 추가</li><li>버튼 Configuration에 적용 가능하도록 지원</li><li>컬러 랜더링 모드 추가</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=16598a8ecbd6" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[WWDC25 — 새로운 디자인으로 UIKit 앱 빌드하기]]></title>
            <link>https://medium.com/@choiysapple/wwdc25-%EC%83%88%EB%A1%9C%EC%9A%B4-%EB%94%94%EC%9E%90%EC%9D%B8%EC%9C%BC%EB%A1%9C-uikit-%EC%95%B1-%EB%B9%8C%EB%93%9C%ED%95%98%EA%B8%B0-b53ffb495b67?source=rss-1add52282342------2</link>
            <guid isPermaLink="false">https://medium.com/p/b53ffb495b67</guid>
            <category><![CDATA[ios-26]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[uikit]]></category>
            <category><![CDATA[liquid-glass]]></category>
            <category><![CDATA[wwdc25]]></category>
            <dc:creator><![CDATA[Daegun Choi]]></dc:creator>
            <pubDate>Thu, 18 Dec 2025 11:34:27 GMT</pubDate>
            <atom:updated>2025-12-18T11:52:59.541Z</atom:updated>
            <content:encoded><![CDATA[<h3>WWDC25 — 새로운 디자인으로 UIKit 앱 빌드하기</h3><p><a href="https://developer.apple.com/kr/videos/play/wwdc2025/284/">새로운 디자인으로 UIKit 앱 빌드하기 - WWDC25 - 비디오 - Apple Developer</a></p><h3>Tab views and split views</h3><h4>UITabBarController</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/552/1*-3p-Fef-uReHWCp-m0czfQ.png" /><figcaption>기존처럼 영역을 차지하는게 아니라, 컨텐츠 위에 떠있는 느낌으로 변경</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/526/1*NNtfcYECWqStY9mhnDsJqQ.png" /><figcaption>스크롤시 최소화</figcaption></figure><pre>// Minimize tab bar on scroll<br>tabBarController.tabBarMinimizeBehavior = .onScrollDown</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/544/1*bFn0MS7OtexA5_mVkc-MMg.png" /><figcaption>AccessaoryView 추가됨</figcaption></figure><pre>// Add a bottom accessory<br>let nowPlayingView = NowPlayingView()<br>let accessory = UITabAccessory(contentView: nowPlayingView)<br>tabBarController.bottomAccessory = accessory</pre><h3>Navigation and toolbars</h3><blockquote>네비게이션 바의 디자인이 변경되었습니다.</blockquote><h4>Navigation의 BarButton</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/556/1*vH5xNDX2YuqTxtA8FCarpw.png" /><figcaption>barButton 알아서 그룹핑됩니다.</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*G9YoFpYHNxqoPDwnAsTnuA.png" /><figcaption>BarButtons 에 liquid glass스러운 옵션들 추가됩니다.</figcaption></figure><h4>Titles and Subtitles</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/564/1*ZAIvKnQCKhCT3kCbWaJlTw.png" /><figcaption>타이틀 부분 UI 변경되고, subtitle 영역이 추가됩니다.</figcaption></figure><pre>navigationItem.title = &quot;Inbox&quot;<br>navigationItem.subtitle = &quot;49 Unread&quot;</pre><h4>Bar Background</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/850/1*4tFkElkjZA9yeT_qkZFzbg.png" /><figcaption>투명한 bar 배경이 디폴트로 변경됩니다.</figcaption></figure><h4>네비바 스크롤 시 보이는 Edge Effect가 다른 부분에도 적용됩니다.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/512/1*q8JjUM3C8SfnemSUV4mSDw.png" /><figcaption>스크롤 뷰 위에 겹치는 뷰</figcaption></figure><pre>let interaction = UIScrollEdgeElementContainerInteraction()<br>interaction.scrollView = contentScrollView<br>interaction.edge = .bottom<br>buttonsContainerView.addInteraction(interaction)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/588/1*KME2OuL0OZqXEuv-fqfVVA.png" /><figcaption>기존처럼 보이게 설정</figcaption></figure><pre>scrollView.topEdgeEffect.style = .hard</pre><h4>스크롤 동작</h4><blockquote>1. 화면 스와이프 도중에 스크롤 가능하도록 변경됩니다.<br>2. 화면 끝이 아니더라도 스와이프 백 가능하도록 변경됩니다</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/990/1*gt8qV3Prh0_PzyVZEuGkIw.png" /></figure><h4>스와이프 정책 &amp; 커스텀</h4><ul><li>스와이프 백 될지 안될지는 시스템이 알아서 판단</li><li>스와이프 액션 같은게 있는 경우, 뒤로가기 안함</li><li>상호작용 가능한 UI가 없는 경우, 뒤로가기 지원</li><li>스와이프 기능 우선순위 설정 <br>→ 추가된 `interactiveContentPopGestureRecognizer`에 스와이프 백 실패 조건 정의</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Pk5t4kvNnC9kl1bsK__6Cg.png" /></figure><h3>Presentation</h3><ul><li>Popover UI 변경</li><li>Sheet → 26 부터는 커스텀 배경 지우는걸 권장</li><li>Action Sheet의 source 추가<br>→ 기존에는 그냥 다른 곳 터치시 내려갔으나 이제 source를 정의하지 않으면 cancel 버튼 표시</li></ul><h3>Search</h3><p>상단에 위치했던 서치바가 이제 하단으로 이동</p><h4>UISwitch</h4><p>UISwitch 등의 컴포넌트 크기 변경됨<br> → <strong>기존 UI 해치지 않는지 확인 필요</strong></p><h4>UIButton</h4><p><em>Configuration</em> 에 <em>.glass()</em> 추가, 색조 커스텀 가능</p><h4>UISlider</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OjnJg3oG3w-Ctxce-DuRZQ.png" /></figure><ul><li>모멘텀, 스트레치 애니메이션 지원</li><li>눈금 추가 기능 + 값 제한 가능 (값 단위 설정, 10 씩만 설정 가능하거나 등등)</li><li>슬라이더 안에서 움직일 수 있는 범위 제한 기능</li><li>thumb 핸들 없앨 수 있음 <em>slider.sliderStyle = .thumbless</em></li></ul><h3>Custom elements</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/526/1*itmNK-ySb3uXFCAq4AFG0g.png" /></figure><ul><li>Liquid Glass ≠ UIBlurEffect</li><li>글래스 이벤트는 상호작용 가능한 레이어</li><li>다른 UI의 최상단에 위치</li><li>UIView 처럼 여러 커스텀 가능, 다크모드 지원, 애니메이션</li></ul><pre>// Adopting glass for custom views<br>let effectView = UIVisualEffectView()<br>addSubview(effectView)<br>let glassEffect = UIGlassEffect()<br>// Animating setting the effect results in a materialize animation<br>UIView.animate {<br> effectView.effect = glassEffect<br>}</pre><pre>// Animating glass out using dematerialize animation<br>UIView.animate {<br> effectView.effect = nil<br>}</pre><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b53ffb495b67" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Tidy First? 정리노트]]></title>
            <link>https://medium.com/@choiysapple/tidy-first-dbc0f11c6a20?source=rss-1add52282342------2</link>
            <guid isPermaLink="false">https://medium.com/p/dbc0f11c6a20</guid>
            <category><![CDATA[tidy-first]]></category>
            <category><![CDATA[coding]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[refactoring]]></category>
            <dc:creator><![CDATA[Daegun Choi]]></dc:creator>
            <pubDate>Mon, 15 Dec 2025 12:19:42 GMT</pubDate>
            <atom:updated>2025-12-15T12:26:05.565Z</atom:updated>
            <content:encoded><![CDATA[<p>더 좋은 코드를 작성하기 위한 책을 읽고 막상 현업에 적용하려고 해보면, 정확하게 어떤 내용이었는지 기억이 잘 안나는 경우가 있습니다.</p><p>동료들과 같이 보면서 생각하고, 혼자 작업 할 때에도 찾아 참고하기 쉽도록 책 각 부분의 <strong><em>결론</em></strong>을 모아 한 페이지(이지만… 짧진 않네요)로 정리해 보았습니다.</p><p>3장은 이론에 대한 이야기이므로, 직접 읽고 생각해 보시는 것을 추천드립니다!</p><h3>1. 코드 정리법</h3><h4>1. 보호 구문 (Guard Clause)</h4><pre>// ❌ 이런 식의 중첩된 조건은 헷갈립니다.<br>if (조건)<br>    if (다른 조건 부정)<br>        ...<br><br>// ✅ 코드 시작 전 전제 조건을 말하는 것 같아 보기 좋습니다.<br>if (조건 부정) { return }<br>if (다른 조건) { return }</pre><blockquote><strong><em>주의:</em></strong><em> 보호 구문을 남용하면 오히려 읽기 어렵습니다. → 조건들을 </em><em>helper 메서드로 추출해서 보호 구문을 정리하세요.</em></blockquote><h3>2. 안 쓰는 코드</h3><blockquote><strong><em>제발 지워라.</em></strong></blockquote><p><strong>나중에 필요하면 어쩌지?<br></strong>형상 관리 툴(Git 등)을 사용해서 롤백하면 됩니다.</p><p><em>⚠️ </em><strong><em>항상 그렇듯, 각 정리 과정에서는 코드를 조금만 삭제할 것</em></strong><em> → 잘못해도 복구가 쉽습니다.</em></p><h4>3. 대칭으로 맞추기 (일관성)</h4><blockquote><em>일관성이 중요하다.</em></blockquote><ul><li>동작이 일관적이면, 이해가 빠릅니다.</li><li>두 가지 이상의 패턴을 섞어 쓰면 혼란스럽습니다.</li><li>공통성이 있어도 세부사항에 묻혀 드러나지 않을 수 있습니다 → 같은 부분들 속에 다른 부분을 분리하세요.</li></ul><h4>4. 새로운 인터페이스로 기존 루틴 부르기</h4><blockquote><em>기존 인터페이스를 호출하는 새 인터페이스를 만든다 → </em><em>pass-through interface</em></blockquote><h4>5. 읽는 순서</h4><blockquote><em>읽기 좋은 순서로 정렬하기</em></blockquote><p>파일의 마지막 부분에 전체를 이해하는 주요한 부분이 있다면 어렵습니다.</p><p><strong>⚠️ 다른 코드 정리를 하고 싶어도 참자. 섞이면 안 됩니다.</strong></p><h4>6. 응집도를 높이는 배치</h4><blockquote><em>코드의 순서를 바꿔서 변경할 요소들을 가까이 놓자. <br>결합도가 있는 파일은 같은 디렉토리에 두자.</em></blockquote><p>변경해야 할 동작을 찾았는데, 같이 변경해야 할 부분이 여러 곳에 흩어져 있습니다. 🤬<br><strong><em>응집도를 높이는 순서로 정리하면, 더 쉽게 변경이 가능합니다.</em></strong></p><h4>7. 선언과 초기화를 함께 옮기기</h4><blockquote><em>선언과 초기화는 같이 둬라.</em></blockquote><ul><li>초기화는 이름이 주는 의미를 강화합니다.</li><li>한참 뒤에 초기화가 있으면 어떤 변수인지 까먹기 쉽습니다.</li></ul><h4>8. 설명하는 변수</h4><blockquote><em>표현식의 의도가 드러나도록 변수를 만들어 할당하세요.</em></blockquote><pre>❌ 변수가 무슨 의미인지 잘 모르겠습니다.<br>return new Point(<br>    ... 긴 표현식..., <br>    ... 다른 긴 표현식...,<br>)</pre><pre>✅ 표현식의 의미가 명확합니다.<br>x := ...긴 표현식...<br>y := ...다른 긴 표현식...<br>return new Point(x, y)</pre><h4>9. 설명하는 상수</h4><blockquote><em>리터럴 상수는 상징적인 상수로 바꾼다.</em></blockquote><pre>❌ 404가 뭔데, 뭐 어떻게 하라고?<br><br>if response.code = 404<br>    ...코드...</pre><pre>✅ 무슨 코드인지 알겠다.<br><br>PAGE_NOT_FOUND := 404<br>if response.code = PAGE_NOT_FOUND<br>    ...코드...</pre><pre>❌ 이런 식으로 너무 빈약한 의미를 가진 코드 정리는 도움이 안 됩니다.<br><br>ONE = 1<br>...ONE... // 1이 필요한 어디든 사용</pre><h4>10. 명시적인 매개변수</h4><p>일부 데이터가 명시적으로 전달되지 않는 경우:</p><blockquote><em>루틴을 나눈다.</em></blockquote><ol><li>매개 변수 값을 채운 후</li><li>명시적으로 전달한다.</li></ol><h4>11. 비슷한 코드끼리</h4><blockquote><em>긴 코드를 읽다가 의미 구분이 되면 </em><strong><em>빈 줄로 분리</em></strong><em>하자.</em></blockquote><h4>12. 도우미 추출 (Helper Extraction)</h4><blockquote><em>목적이 분명하고 나머지 코드와 상호작용이 적은 코드 → 도우미(helper)로 추출한다.</em></blockquote><pre>// ASIS<br>routine()<br>    ...유지할 코드...<br>    ...변경할 코드...<br>    ...유지할 코드...<br><br>// TOBE<br>helper()<br>    ...변경할 코드...<br>routine()<br>    ...유지할 코드...<br>    helper()<br>    ...유지할 코드...</pre><p><strong>시간적 결합을 표현하려는 경우 (순서가 중요한 경우)</strong></p><pre>// ASIS<br>foo.a()<br>foo.b()<br><br>// TOBE<br>ab()<br>    a()<br>    b()</pre><h4>13. 하나의 더미 (One Lump)</h4><blockquote><em>필요한 만큼 코드를 하나의 더미처럼 느껴질 때까지 흩어진 코드를 모은 뒤, 정리하라.</em></blockquote><p>한번에 머릿속에 기억하고 있어야 할 코드의 상세 내용을 줄여줍니다.<br>잘못된 작은 코드 조각은, 오히려 교류하는 과정을 알기 어렵게 합니다.</p><p><strong>찾아볼 증상</strong></p><ul><li>길고 반복되는 인자 목록</li><li>반복되는 코드, 특히 조건문</li><li>도우미에 대한 부적절한 이름</li><li>공유되어 변경에 노출된 데이터 구조</li></ul><h4>14. 설명하는 주석</h4><blockquote><em>코드를 이해한 순간은 소중합니다. 기록하자.</em></blockquote><ul><li>코드에서 명확하지 않은 내용만 적자 → 이 코드를 처음 읽기 시작한 나를 떠올려보세요.</li><li>팀원이 읽을 것을 생각하며 적자.</li><li>코드 결함을 발견하면 달자.</li></ul><h4>15. 불필요한 주석 지우기</h4><blockquote><em>코드만으로 내용을 이해할 수 있다면, 필요 없는 주석입니다.</em></blockquote><h3>2. 관리</h3><h4>16. 코드 정리 구분 (PR 분리)</h4><blockquote><em>어딘가에는 코드 정리가 올라가야 하는데, 대체 어디에? → </em><strong><em>가급적 코드 정리는 별도의 작은 PR들로 올리자.</em></strong></blockquote><p><strong>안 좋은 사례:</strong></p><ol><li>동작 변경과 코드 정리를 한 PR에 넣는다.</li><li>사람들이 PR이 길다고 불평한다.</li><li>코드 정리만 분리해서 PR에 넣는다.</li><li>리뷰어들이 코드 정리만 담긴 PR이 무슨 의미로 만들었는지 모르겠다고 불평한다.</li><li>다시 1로 돌아간다.</li></ol><h4>17. 연쇄적인 정리 (Sequential Tidy)</h4><blockquote><em>너무 </em><strong><em>대대적</em></strong><em>으로 코드 정리를 시작하지 말자. 너무 </em><strong><em>빠르게</em></strong><em> 바꾸지 말자. </em><strong><em>작게 순차적</em></strong><em>으로 실행해 실패하지 않는 것이 시간을 아껴줍니다.</em></blockquote><ul><li>코드 정리는 새우깡과 같다 (계속 손이 간다) → 이 충동을 관리하는 것도 기술입니다.</li><li>정리의 크기는 본인 나름이지만, <strong>아주 작은 단계로 나누어보자.</strong></li><li>한 단계를 마치면, 다음 단계가 보일 것입니다.</li></ul><p><strong>연쇄 정리의 예시</strong></p><ul><li><strong>보호 구문</strong> → 조건이 설명하는 도우미 / 설명하는 변수 추출</li><li><strong>안 쓰는 코드</strong> → 코드를 읽는 순서 / 응집도를 높이는 배치</li><li><strong>대칭으로 맞추기</strong> → 유사한 코드가 묶여진 순서대로 읽을 수 있다</li><li>새로운 인터페이스로 기존 루틴 부르기 → 하나를 정리하면 뒤따른 정리가 다발로 이어진다</li><li><strong>읽는 순서</strong> → 너무 멀어 유사한지도 몰랐던 요소들이 보인다</li><li><strong>응집도를 높이는 배치</strong> → 하위 요소로 추출할 후보를 뽑을 수 있다</li><li><strong>설명하는 변수</strong> → 변수 이름으로 주석이 필요없어질 수 있다</li><li><strong>명시적인 매개변수</strong> → 매개변수 집합을 묶을 수 있다, 새로운 추상화를 도출할 수 있다</li><li><strong>비슷한 코드끼리</strong> → 코드 덩어리에 주석 붙이거나 도우미로 바꾸기</li><li><strong>도우미 추출</strong> → 보호 구문, 설명하는 상수나 변수, 불필요한 주석 지우기</li><li><strong>하나의 더미</strong> → 크고 명백하게 엉망인 하나의 덩어리가 보이면, 정리할 거리가 생긴다</li><li><strong>설명하는 주석</strong> → 설명하는 변수, 상수, 도우미를 쓸 수 있다면 주석을 코드로 옮긴다</li><li><strong>불필요한 주석 지우기</strong> → 중복 주석을 지우면, 명시적인 매개변수를 쓸 기회가 생긴다</li></ul><h4>18. 코드 정리의 일괄 처리량</h4><blockquote><em>하나의 변경사항을 검토, 배포하는 데 드는 고정 비용은 큽니다. → 코드 정리 비용을 줄이려면, </em><strong><em>코드 정리 개수를 늘려</em></strong><em>서 동작 변경에 소용되는 비용을 줄여라.</em></blockquote><p><strong>통합 / 배포 전 고려사항</strong></p><ul><li>코드 정리를 변경을 돕는 작업으로 본다면, 다음 변경사항을 쉽게 하기 위해 얼마나 바꿔야 하나?</li><li>코드 정리의 크기가 어느 정도일 때 통합 / 배포가 쉬울까?</li><li><strong>충돌</strong>: 일괄 처리 작업량이 많다 → 통합 지연시간, 충돌 가능성 ⬆️</li><li><strong>상호작용</strong>: 코드 정리 사이에 상호작용이 있으면, 병합 비용 증가</li><li><strong>추측</strong>: 필요한 것보다 더 많이 정리하면, 예상치 못한 추가 비용 발생</li></ul><h4>19. 리듬 (Rhythm)</h4><blockquote><strong><em>코드 정리는 몇 분에서 한 시간이면 충분합니다.</em></strong></blockquote><ul><li>한 번의 코드 정리에 <strong>한 시간을 넘기지 말 것</strong>.</li><li>코드가 너무 엉망이라서 몇 시간을 들이더라도 정리하는 게 나을 수도 있습니다.</li><li>코드 정리를 선행하면, 코드 정리한 내용도 뭉쳐집니다.</li></ul><h4>20. 얽힘 풀기 (Untangling)</h4><blockquote><em>교착 상태에선, 리셋하고 코드 정리 먼저 해보자. </em><strong><em>선후 관계가 명확해지는 건 시간이 해결해 준다.</em></strong></blockquote><h4>21. 코드 정리 시점</h4><blockquote><em>그래서 코드정리, 동작변경 어떤거 먼저?</em></blockquote><ol><li>변경 안될 코드 / 개선해도 배울게 없다 → 코드 <strong>정리 포기</strong></li><li>분량에 비해 보상이 없거나 잠재적이다 / 작게 여러번 할 수 있는 양이다 → 정리를 <strong>미루기</strong></li><li>정리 안하고 두면 비용이 늘어난다 / 정리를 안하면 일이 안끝난거 같다 → <strong>동작 변경 먼저</strong> 하고 정리</li><li>정리하면 이해나 변경이 쉬운 즉각적 효과 / 어떻게 정리할 지 알고 있다 → <strong>정리 먼저</strong>하고 동작 변경</li></ol><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=dbc0f11c6a20" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>