<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>asher_joon.log</title>
        <link>https://velog.io/</link>
        <description>Asher입니다. 하지만 Joon이라고도 불리는</description>
        <lastBuildDate>Thu, 30 Apr 2026 04:49:46 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>asher_joon.log</title>
            <url>https://velog.velcdn.com/images/asher_joon/profile/a20019a7-676f-422c-a702-209e826fe5bb/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. asher_joon.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/asher_joon" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Challenge 2 회고] 디자인을 처음부터 다시 배우며 지나온 나의 두 번째 애플 디벨로퍼 아카데미 챌린지]]></title>
            <link>https://velog.io/@asher_joon/Challenge-2-%ED%9A%8C%EA%B3%A0-%EB%94%94%EC%9E%90%EC%9D%B8%EC%9D%84-%EC%B2%98%EC%9D%8C%EB%B6%80%ED%84%B0-%EB%8B%A4%EC%8B%9C-%EB%B0%B0%EC%9A%B0%EB%A9%B0-%EC%A7%80%EB%82%98%EC%98%A8-%EB%82%98%EC%9D%98-%EB%91%90-%EB%B2%88%EC%A7%B8-%EC%95%A0%ED%94%8C-%EB%94%94%EB%B2%A8%EB%A1%9C%ED%8D%BC-%EC%95%84%EC%B9%B4%EB%8D%B0%EB%AF%B8-%EC%B1%8C%EB%A6%B0%EC%A7%80</link>
            <guid>https://velog.io/@asher_joon/Challenge-2-%ED%9A%8C%EA%B3%A0-%EB%94%94%EC%9E%90%EC%9D%B8%EC%9D%84-%EC%B2%98%EC%9D%8C%EB%B6%80%ED%84%B0-%EB%8B%A4%EC%8B%9C-%EB%B0%B0%EC%9A%B0%EB%A9%B0-%EC%A7%80%EB%82%98%EC%98%A8-%EB%82%98%EC%9D%98-%EB%91%90-%EB%B2%88%EC%A7%B8-%EC%95%A0%ED%94%8C-%EB%94%94%EB%B2%A8%EB%A1%9C%ED%8D%BC-%EC%95%84%EC%B9%B4%EB%8D%B0%EB%AF%B8-%EC%B1%8C%EB%A6%B0%EC%A7%80</guid>
            <pubDate>Thu, 30 Apr 2026 04:49:46 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요, 애플 디벨로퍼 아카데미에서 끊임없이 &#39;본질&#39;을 탐구하는 러너 <strong>Asher(애셔)</strong>입니다.</p>
<p>Challenge 1이 &#39;적응과 탐색&#39;의 시간이었다면, 이번 <strong>Challenge 2</strong>의 미션은 &#39;Improve My Skills&#39;였습니다. 단순히 앱을 완성하는 것을 넘어, &quot;나는 어떤 개발자가 되고 싶은가?&quot;라는 근본적인 질문에 답을 찾아가는 과정이었죠. </p>
<p>C1의 프로토타입을 Hi-Fi 앱으로 발전시키며, 기술적 욕심보다는 <strong>&#39;의도된 디자인&#39;</strong>의 힘을 깨달았던 저의 치열했던 여정을 기록합니다.
<img src="https://velog.velcdn.com/images/asher_joon/post/589a1c5c-2350-4f85-83ff-cddd7111a843/image.png" alt=""></p>
<hr>
<h2 id="1-정말로-배우고-싶은-게-뭐예요-아이작의-한마디">1. &quot;정말로 배우고 싶은 게 뭐예요?&quot; 아이작의 한마디</h2>
<p>C2를 시작할 때 제 머릿속은 &#39;무엇을 구현할까&#39;로 가득했습니다. 
하지만 <strong>아이작(Isaac) 멘토</strong>와의 면담은 제 관점을 완전히 뒤흔들어 놓았습니다.</p>
<blockquote>
<p>*&quot;결과물에 연연하지 말고, 밖에서도 충분히 할 수 있는 것 말고, 아카데미에서만 도전해 볼 수 있는 &#39;진짜 배우고 싶은 것&#39;에 집중하세요.&quot;*</p>
</blockquote>
<p>이 한마디가 제 이정표가 되었습니다. 
저는 그동안 &#39;개발자니까&#39;라며 외면해왔던 <strong>디자인의 깊은 설계</strong>를 핵심 목표로 재설정했습니다. 예쁜 그림을 그리는 게 아니라, <strong>사용자의 경험을 설계하는 법</strong>을 배우기로 다짐한 것입니다.</p>
<hr>
<h2 id="2-디자인의-본질-어떻게가-아닌-왜에서-시작하다">2. 디자인의 본질: &quot;어떻게&quot;가 아닌 &quot;왜&quot;에서 시작하다</h2>
<p>디자인 컨셉을 잡기 위해 <strong>프라이데이(Friday) 멘토</strong>을 찾았을 때, 저는 충격적인 경험을 했습니다. 
저는 &quot;어떤 디자인 컨셉이 좋을까요?&quot;라고 물었지만, 
프라이데이는 서비스의 <strong>&#39;가장 뿌리가 되는 가치&#39;</strong>를 되물으셨습니다.</p>
<p>단순히 MVP를 강조할 디자인을 찾는 것이 아니라, 최초의 질문으로 돌아가는 활동을 시작했습니다.</p>
<ul>
<li><strong>집단의 특징 분석:</strong> 우리 앱을 사용할 사람들은 어떤 특징을 가졌는가?</li>
<li><strong>감각의 구체화:</strong> 그 집단을 보면 어떤 느낌이 드는가? 그 느낌에 맞는 폰트는 무엇인가?</li>
<li><strong>가치와 느낌의 연결:</strong> 어떤 앱을 만드는가보다, 어떤 &#39;해소&#39;를 제공하고 싶은가가 디자인의 시작이었습니다.</li>
</ul>
<p>결국 디자인 컨셉은 <strong>&quot;서비스가 해결하려는 문제를 어떻게 하면 가장 잘 해소해줄 수 있을까?&quot;</strong>에 대한 고민 끝에 자연스럽게 도출되는 것임을 배웠습니다.</p>
<hr>
<h2 id="3-꼬리에-꼬리를-무는-설계-프로젝트-계획의-재구성">3. 꼬리에 꼬리를 무는 설계: 프로젝트 계획의 재구성</h2>
<p><img src="https://velog.velcdn.com/images/asher_joon/post/cfbd3da2-4f76-4163-acf1-06ff39d53baa/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/asher_joon/post/98c91dc2-5fc2-419e-b892-e414f531bc4d/image.png" alt=""></p>
<p>저는 프로젝트 계획부터 완전히 다시 짰습니다. 단순히 기능을 나열하는 대신, 스스로에게 끊임없이 질문을 던졌습니다.</p>
<ol>
<li><strong>문제의식:</strong> 우리가 해결하려는 진짜 문제는 무엇인가?</li>
<li><strong>연상과 감정:</strong> 문제를 볼 때 어떤 색깔이나 사물이 떠오르는가?</li>
<li><strong>유저 경험:</strong> 유저가 앱을 열고 사용할 때 어떤 감정을 느껴야 하는가?</li>
</ol>
<p>이 질문들에 답하며 저는 <strong>&#39;가벼움&#39;, &#39;직관적&#39;, &#39;신선함&#39;</strong>이라는 키워드를 뽑아냈고, 이를 시각적으로 구현하기 위해 <strong>5번의 페이퍼 프로토타이핑</strong>을 진행했습니다. </p>
<p>디자인 지식이 거의 &#39;Zero&#39;에 가까웠기에 더 치열했습니다. 수많은 자료를 찾아보고, 피드백을 받고, 다시 깎아내는 과정을 반복한 끝에야 마지막 페이퍼 프로토타입에서 <strong>&quot;내가 전달하고 싶었던 느낌&quot;</strong>을 동료들과 멘토들에게 인정받을 수 있었습니다.</p>
<hr>
<h2 id="4-시련을-넘어-hi-fi로-종이에서-픽셀로">4. 시련을 넘어 Hi-Fi로: 종이에서 픽셀로</h2>
<p><img src="https://velog.velcdn.com/images/asher_joon/post/4c1a70a5-236d-4d54-9aca-b69ddb28321d/image.png" alt=""></p>
<p>하지만 &#39;느낌&#39;을 실체화하는 것은 또 다른 벽이었습니다. Sketch와 Figma를 통해 앱 전체를 디자인해 본 것은 이번이 처음이었기 때문입니다.</p>
<ul>
<li><strong>장벽:</strong> 툴 사용법이 익숙지 않아 남들보다 몇 배의 시간을 쏟아야 했습니다. </li>
<li><strong>사건:</strong> 작업 중 파일을 잃어버려 하루치 수정 사항이 통째로 날아가는 아찔한 순간도 있었습니다. 하지만 포기하지 않고 다시 그리며, 오히려 이전보다 더 정교한 레이아웃을 잡을 수 있었습니다.</li>
<li><strong>피드백의 힘:</strong> <ul>
<li><strong>스타크:</strong> &quot;버튼 사이즈(44x44)와 간격 등 HIG를 준수했는가?&quot;</li>
<li><strong>쉐인:</strong> &quot;불필요한 그림자를 줄이고 정보의 위계질서를 명확히 하라.&quot;</li>
</ul>
</li>
</ul>
<p>이러한 피드백을 거치며 단순히 &#39;이쁜 앱&#39;이 아니라, <strong>사용자의 경험과 서비스의 의도를 연결하는 디자인</strong>이 무엇인지 체득했습니다.</p>
<hr>
<h2 id="5-외부-데이터-수호자로서의-기술적-성장">5. &#39;외부 데이터 수호자&#39;로서의 기술적 성장</h2>
<p>학습 커뮤니티 <strong>&#39;무야호&#39;</strong>에서 저는 <strong>&#39;외부 데이터 수호자(노홍철 캐릭터)&#39;</strong>로서 팀원들의 성장을 지원했습니다.</p>
<ul>
<li><strong>공식 문서라는 나침반:</strong> 타인의 코드를 함께 고치는 것은 내 코드를 수정하는 것보다 훨씬 어려웠습니다. 설계 배경을 이해하기 위해 <strong>Apple 공식 문서와 WWDC 영상</strong>을 파고들었습니다.</li>
<li><strong>설명 가능한 코드:</strong> 막연한 검색 대신 공식 자료를 기준으로 &#39;왜 이렇게 작동해야 하는지&#39; 설명하려 노력했고, 이 과정에서 저 또한 SwiftUI의 구조를 더 깊게 이해하게 되었습니다.</li>
</ul>
<hr>
<h2 id="6-회고를-마치며-나는-결과보다-과정에서-더-많이-배우는-사람이-되었다">6. 회고를 마치며: &quot;나는 결과보다 과정에서 더 많이 배우는 사람이 되었다&quot;</h2>
<p>이번 Challenge 2는 저에게 기술적인 성장 그 이상의 의미를 주었습니다. 좋은 결과물은 갑자기 만들어지는 것이 아니라, <strong>문제를 바라보는 태도와 그것을 풀어가는 지루한 반복 속에서 나온다</strong>는 진리를 배웠기 때문입니다.</p>
<p>이제 저는 무엇을 만들었는지뿐만 아니라, <strong>&quot;왜 그렇게 만들었는지&quot;</strong>를 설명할 수 있는 개발자로 한 걸음 더 나아갔습니다. 이 뜨거운 마음을 가지고 다음 챌린지에서도 본질에 집중하는 배움을 이어가겠습니다.</p>
<p>함께 머리를 맞대준 퍼스널 팀원들 팀원들, 그리고 아이작, 프라이데이 감사합니다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Challenge 1 회고] 성과보다 성장에 집중했던, 나의 첫 번째 애플 디벨로퍼 아카데미 챌린지]]></title>
            <link>https://velog.io/@asher_joon/Challenge-1-%ED%9A%8C%EA%B3%A0-%EC%84%B1%EA%B3%BC%EB%B3%B4%EB%8B%A4-%EC%84%B1%EC%9E%A5%EC%97%90-%EC%A7%91%EC%A4%91%ED%96%88%EB%8D%98-%EB%82%98%EC%9D%98-%EC%B2%AB-%EB%B2%88%EC%A7%B8-%EC%95%A0%ED%94%8C-%EB%94%94%EB%B2%A8%EB%A1%9C%ED%8D%BC-%EC%95%84%EC%B9%B4%EB%8D%B0%EB%AF%B8-%EC%B1%8C%EB%A6%B0%EC%A7%80</link>
            <guid>https://velog.io/@asher_joon/Challenge-1-%ED%9A%8C%EA%B3%A0-%EC%84%B1%EA%B3%BC%EB%B3%B4%EB%8B%A4-%EC%84%B1%EC%9E%A5%EC%97%90-%EC%A7%91%EC%A4%91%ED%96%88%EB%8D%98-%EB%82%98%EC%9D%98-%EC%B2%AB-%EB%B2%88%EC%A7%B8-%EC%95%A0%ED%94%8C-%EB%94%94%EB%B2%A8%EB%A1%9C%ED%8D%BC-%EC%95%84%EC%B9%B4%EB%8D%B0%EB%AF%B8-%EC%B1%8C%EB%A6%B0%EC%A7%80</guid>
            <pubDate>Sun, 19 Apr 2026 07:59:12 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요, 애플 디벨로퍼 아카데미에서 열정적으로 항해 중인 러너 <strong>Asher(애셔)</strong>입니다. </p>
<p>아카데미에 들어와서 처음으로 맞이한 &#39;Challenge 1&#39;이 드디어 마무리되었습니다. 단순히 앱 하나를 뚝딱 만들어내는 과정이 아니라, Apple이 추구하는 디자인 철학(HIG)을 체득하고, CBL(Challenge Based Learning)이라는 생소하지만 매력적인 학습 방식에 푹 빠져들었던 시간이었습니다. </p>
<p>중구난방으로 적어두었던 생각의 파편들을 모아, 저의 첫 번째 CBL을 기록해 보려 합니다. </p>
<hr>
<h2 id="1-애플은-왜-이럴까-hig와-디자인에-대한-고찰">1. &quot;애플은 왜 이럴까?&quot; HIG와 디자인에 대한 고찰</h2>
<p>처음에는 그냥 &quot;목록 보여주는 거 아닌가?&quot; 싶었던 것들이 막상 <strong>HIG(Human Interface Guidelines)</strong>를 파고드니 용도가 확실히 다르다는 것을 알게 되었습니다.</p>
<h3 id="캐러셀carousel-vs-리스트list">캐러셀(Carousel) vs 리스트(List)</h3>
<ul>
<li><strong>캐러셀:</strong> 주목시키고 싶은 콘텐츠를 하나씩 강조하며 이미지에 좀 더 집중하게 만듭니다.</li>
<li><strong>리스트:</strong> 전체를 쭉 훑어볼 때 유리하며, 텍스트 위주로 정보를 전달할 때 핵심적인 역할을 합니다.
&quot;무엇을 보여주고 싶냐&quot;에 따라 선택이 달라져야 한다는, 아주 기본적이지만 중요한 포인트를 배웠습니다.</li>
</ul>
<h3 id="애플이-둥둥-떠다니는-버튼을-싫어하는-이유">애플이 &#39;둥둥 떠다니는 버튼&#39;을 싫어하는 이유</h3>
<p>안드로이드에서는 흔한 FAB(Floating Action Button)를 왜 iOS에서는 잘 쓰지 않을까? 처음엔 의아했지만, iOS의 네비게이션 구조와 맞지 않을뿐더러 좁은 화면에서 사용자의 시야를 계속 방해하기 때문이라는 걸 이해하게 되었습니다. 제한된 영역 내에서 최적의 정보 경험을 제공하려는 애플의 철학을 엿볼 수 있었습니다.</p>
<h3 id="구조의-명확성-탭--네비게이션--모달">구조의 명확성: 탭 / 네비게이션 / 모달</h3>
<p>이 세 가지를 헷갈리면 UX가 꼬이기 시작합니다.</p>
<ul>
<li><strong>탭:</strong> 앱의 최상위 영역을 이동할 때</li>
<li><strong>네비게이션:</strong> 같은 흐름 안에서 깊이 들어갈 때</li>
<li><strong>모달:</strong> 현재 흐름을 잠시 끊고 집중시킬 때 (Sheet 등)
애플 앱들이 왜 다 비슷하게 생겼는지 이제야 이해가 갔습니다. 그 규칙 안에서 디자인하는 게 결국 &#39;가장 iOS다운 앱&#39;을 만드는 지름길이었습니다.</li>
</ul>
<hr>
<h2 id="2-swiftui-데이터의-흐름을-지배하다">2. SwiftUI, 데이터의 흐름을 지배하다</h2>
<p>머릿속에 있는 디자인을 코드로 옮기는 게 생각보다 더 어려웠습니다. 억지로 절대값을 쓰기보다 SwiftUI의 레이아웃 시스템을 이해해야 원하는 대로 나오더라고요.</p>
<ul>
<li><strong>@State &amp; @Binding:</strong> 부모가 데이터를 갖고 자식에게 내려주며 양방향 싱크를 맞추는 구조. 이게 SwiftUI 데이터 흐름의 정수라는 걸 체감했습니다.</li>
<li><strong>AppStorage &amp; EnvironmentObject:</strong> 앱이 꺼져도 유지되어야 하는 데이터는 <code>AppStorage</code>로, 앱 전체에서 공유되는 메모리 데이터는 <code>@EnvironmentObject</code>로 관리했습니다. </li>
<li><strong>디스크 저장(Save/Load):</strong> 메모리는 앱을 끄면 사라지니, 디스크에서 데이터를 읽어 메모리로 올리고 바뀔 때마다 다시 저장하는 구조를 설계하며 데이터의 영속성에 대해 깊이 고민해 보았습니다.</li>
</ul>
<hr>
<h2 id="3-새천년-팀과-함께한-cbl의-여정-engage">3. &#39;새천년&#39; 팀과 함께한 CBL의 여정 (Engage)</h2>
<p>멘토 하워드, 프라이데이와 함께 팀 9, <strong>&#39;새천년&#39;</strong>이라는 이름으로 시작했습니다. &quot;앞으로의 새로운 천년을 이끄는 사람들이 되자&quot;는 다짐과 함께요.</p>
<h3 id="💡-big-idea의-변화">💡 Big Idea의 변화</h3>
<p>처음엔 &#39;웰빙&#39;을 키워드로 잡았지만, 우리 팀원들이 진짜 하고 싶은 건 결국 포항과 아카데미에서의 <strong>&#39;활동(Activity)&#39;</strong>이었습니다. </p>
<p><img src="https://velog.velcdn.com/images/asher_joon/post/2f0492c9-3f31-473e-a851-a30c9ee91d60/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/asher_joon/post/96e689b9-8d13-49e0-bb06-3368976bd7ce/image.png" alt=""></p>
<p>PM이 없다보니 정말 다양한 의견들이 나와서 때로는 어려움을 겪었지만, 서로의 관점에 대해 자유롭게 이야기하며 &quot;취향이 다양한 러너들이 포항 내 여가활동을 함께 할 수 있는 환경을 만들자&quot;는 마일스톤(CS)에 도달했습니다.</p>
<p><strong>우리의 Essential Question (EQ):</strong> </p>
<blockquote>
<p>&quot;어떻게 하면 다양한 사람들과 다양한 활동을 놓치지 않고 찾을 수 있을까?&quot;</p>
</blockquote>
<hr>
<h2 id="4-안-힘들어요---진실을-캐내는-스몰토크-리서치-investigate">4. &quot;안 힘들어요?&quot; - 진실을 캐내는 &#39;스몰토크&#39; 리서치 (Investigate)</h2>
<p>리서치 과정이 특히 기억에 남습니다. 대놓고 &quot;설문해 주세요&quot; 하면 진실되지 않은 답변이 온다는 걸 알았거든요. 모든 사람들이 인터뷰, 설문조사를 하게되면 바른 생활 어린이가 되는 것을 느꼈습니다😂. 그래서 우리는 <strong>&#39;잠입 리서치&#39;</strong>를 했습니다.</p>
<p>다른 팀 리서치를 도와준 뒤 &quot;안 힘드세요?&quot;, &quot;오늘 끝나고 뭐 하세요?&quot; 같은 스몰토크로 시작해, 대상자가 리서치임을 인지하지 못한 상태에서 진짜 속마음을 들었습니다. </p>
<p><img src="https://velog.velcdn.com/images/asher_joon/post/a9f4f2ab-deb8-46d6-af07-818dda56c06c/image.png" alt=""></p>
<p>그렇게 모은 데이터는 상당했습니다. 시각화도 진행해 보았죠.</p>
<p><img src="https://velog.velcdn.com/images/asher_joon/post/cb20b00c-22a0-4e7e-b3fe-a7301108df45/image.png" alt=""></p>
<p><strong>리서치를 통해 얻은 핵심 인사이트:</strong></p>
<ol>
<li>줌챗과 컨플루언스는 익숙지 않아 정보가 파편화되어 있다.</li>
<li>오후 세션이 끝나면 다들 방전된다. 그래서 &#39;주말&#39;이나 &#39;단기성 이벤트&#39;가 더 필요하다.</li>
<li>호스트를 하고 싶지만 사람이 안 모일까 봐, 혹은 참여 확정에 대한 심리적 부담감 때문에 망설인다.</li>
</ol>
<hr>
<h2 id="5-아이디어의-확장과-구체화">5. 아이디어의 확장과 구체화</h2>
<p>우리는 수집한 데이터를 바탕으로 네 가지 방향을 치열하게 검토했습니다.</p>
<ul>
<li>번개 및 이벤트성 모임 포커스</li>
<li>&#39;입소문&#39;을 시스템화하는 리뷰/추천 시스템</li>
<li>시각 자료(영상, 포스터)로 흥미를 끄는 릴스 스타일의 UI</li>
<li>호스트의 부담(참여 확정의 거부감)을 줄여주는 사전 조사 시스템</li>
</ul>
<p><img src="https://velog.velcdn.com/images/asher_joon/post/f9153375-8c20-472d-bf41-48e8327b704e/image.png" alt=""></p>
<p>결국 우리의 결론은 <strong>&quot;정기 모임이 부담스러운 러너를 위해 맞춤형 추천으로 허들을 낮춰주는 앱&quot;</strong>이었습니다.</p>
<hr>
<h2 id="6-프로토타이핑-종이에서-코드로">6. 프로토타이핑: 종이에서 코드로</h2>
<p>본격적으로 코드를 짜기 전, 페이퍼 프로토타입을 통해 흐름을 점검했습니다.</p>
<p><img src="https://velog.velcdn.com/images/asher_joon/post/d5485e86-3a02-4914-a3d3-4d5ad1042ee2/image.png" alt=""></p>
<p>이 과정에서 멘토님들께 소중한 피드백을 받았습니다. &quot;홈의 커뮤니티 기능은 과감히 빼고, 검색과 상세 페이지에 집중하라&quot;는 조언 덕분에 핵심 가치에 더 집중할 수 있었습니다.</p>
<p>그 결과, 아래와 같은 앱을 완성할 수 있었습니다.</p>
<p><img src="https://velog.velcdn.com/images/asher_joon/post/0e6ce755-7120-4ce6-a0c9-5c558b0a9acd/image.png" alt=""></p>
<hr>
<h2 id="7-회고를-마치며-나는-swiftui와-조금-더-친해졌다">7. 회고를 마치며: &quot;나는 SwiftUI와 조금 더 친해졌다&quot;</h2>
<p>이번 Challenge 1은 성과에만 집착하지 않고, 순전히 내가 배우고 싶은 것을 해나간 시간이라 만족도가 매우 높습니다. </p>
<p>처음 기획할 때 욕심이 많아 기능을 잔뜩 넣고 싶었지만, 결국 <strong>&quot;이 기능이 진짜 필요한가?&quot;</strong>를 자문하며 덜어내는 과정 자체가 실력이라는 걸 배웠습니다. 억지로 절대값을 쓰지 않고 프레임워크가 원하는 방식대로 따라가는 게 훨씬 자연스럽다는 것도요.</p>
<p>오랜만에 새로운 언어를 배우며 결과물에 집중하지 않고 학습 과정 그 자체를 즐겼던, 매우 의미 있는 시간이었습니다. 이제 이 자신감을 가지고 다음 챌린지로 나아갈 준비가 된 것 같습니다. </p>
<p>함께해 준 &#39;새천년&#39; 팀원들 (링쿠,유메,비비,쿼티,한), 그리고 멘토 하워드, 프라이데이 너무 감사합니다! 함께해 준 모든 분들 덕분에 아쉬움이 남지 않는 Challenge 1 이였습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SwiftUI Day 7 - ScoreKeeper (Identifiable, Stepper, ForEach 바인딩)]]></title>
            <link>https://velog.io/@asher_joon/SwiftUI-Day-7-ScoreKeeper-Identifiable-Stepper-ForEach-%EB%B0%94%EC%9D%B8%EB%94%A9</link>
            <guid>https://velog.io/@asher_joon/SwiftUI-Day-7-ScoreKeeper-Identifiable-Stepper-ForEach-%EB%B0%94%EC%9D%B8%EB%94%A9</guid>
            <pubDate>Wed, 18 Mar 2026 04:29:15 GMT</pubDate>
            <description><![CDATA[<h2 id="프로젝트-파일-구조">프로젝트 파일 구조</h2>
<pre><code>ScoreKeeper/
├── ScoreKeeperApp.swift   # @main 진입점
├── Player.swift           # Identifiable 모델 구조체
└── ContentView.swift      # 플레이어 목록 + 점수 관리 UI</code></pre><p><img src="https://velog.velcdn.com/images/asher_joon/post/c6e7846e-490f-4d69-8bc7-55303a488803/image.png" alt=""></p>
<hr>
<h2 id="identifiable-프로토콜--고유-식별-가능한-모델"><code>Identifiable</code> 프로토콜 — 고유 식별 가능한 모델</h2>
<pre><code class="language-swift">struct Player: Identifiable {
    let id = UUID()
    var name: String
    var score: Int = 0
}</code></pre>
<blockquote>
<p><code>Identifiable</code>을 채택하면 <code>ForEach</code>에서 <code>id:</code> 파라미터 없이 사용할 수 있다.
<code>UUID()</code>는 앱 실행마다 전 세계에서 유일한 식별자를 자동 생성한다.
<code>id</code>는 변하지 않으므로 <code>let</code>, 나머지는 수정 가능하므로 <code>var</code>.</p>
</blockquote>
<h3 id="identifiable-채택-전후-비교">Identifiable 채택 전후 비교</h3>
<pre><code class="language-swift">// Identifiable 없을 때 — id: 명시 필요
ForEach(players, id: \.name) { player in ... }

// Identifiable 있을 때 — 생략 가능
ForEach(players) { player in ... }</code></pre>
<hr>
<h2 id="foreach배열--배열-요소에-바인딩으로-접근"><code>ForEach($배열)</code> — 배열 요소에 바인딩으로 접근</h2>
<pre><code class="language-swift">ForEach($players) { $player in
    TextField(&quot;Name&quot;, text: $player.name)
    Stepper(&quot;\(player.score)&quot;, value: $player.score)
}</code></pre>
<blockquote>
<p><code>ForEach($players)</code> 처럼 <code>$</code>를 붙이면 각 요소를 <strong>바인딩</strong>으로 받을 수 있다.
<code>$player.name</code>, <code>$player.score</code> 처럼 하위 프로퍼티까지 직접 수정 가능하다.
이렇게 하면 부모 뷰의 <code>@State</code> 배열이 자동으로 업데이트된다.</p>
</blockquote>
<table>
<thead>
<tr>
<th>형태</th>
<th>용도</th>
</tr>
</thead>
<tbody><tr>
<td><code>ForEach(players)</code></td>
<td>읽기 전용 (값 표시)</td>
</tr>
<tr>
<td><code>ForEach($players)</code></td>
<td>읽기 + 쓰기 (값 수정)</td>
</tr>
</tbody></table>
<hr>
<h2 id="stepper--증감-버튼으로-숫자-조절"><code>Stepper</code> — 증감 버튼으로 숫자 조절</h2>
<pre><code class="language-swift">Stepper(&quot;\(player.score)&quot;, value: $player.score)</code></pre>
<blockquote>
<p><code>Stepper</code>는 <code>+</code> / <code>-</code> 버튼으로 숫자를 1씩 올리고 내리는 UI 컴포넌트다.
첫 번째 파라미터는 레이블(표시 텍스트), <code>value:</code>는 바인딩된 숫자 값이다.</p>
</blockquote>
<h3 id="범위-제한도-가능">범위 제한도 가능</h3>
<pre><code class="language-swift">Stepper(&quot;\(player.score)&quot;, value: $player.score, in: 0...100)</code></pre>
<hr>
<h2 id="버튼으로-배열에-새-항목-추가">버튼으로 배열에 새 항목 추가</h2>
<pre><code class="language-swift">@State private var newPlayer: String = &quot;&quot;
@State private var players: [Player] = [
    Player(name: &quot;Asher&quot;, score: 0),
    Player(name: &quot;Joon&quot;, score: 0),
    Player(name: &quot;Nessa&quot;, score: 0)
]

Button(&quot;Add Player&quot;, systemImage: &quot;plus.circle.fill&quot;) {
    players.append(Player(name: newPlayer, score: 0))
}</code></pre>
<blockquote>
<p><code>players.append()</code>로 새 <code>Player</code>를 추가하면 <code>ForEach</code>가 자동으로 UI를 갱신한다.</p>
</blockquote>
<hr>
<h2 id="전체-코드-요약">전체 코드 요약</h2>
<pre><code class="language-swift">// Player.swift
struct Player: Identifiable {
    let id = UUID()
    var name: String
    var score: Int = 0
}

// ContentView.swift
struct ContentView: View {
    @State private var newPlayer: String = &quot;&quot;
    @State private var players: [Player] = [
        Player(name: &quot;Asher&quot;, score: 0),
        Player(name: &quot;Joon&quot;, score: 0),
        Player(name: &quot;Nessa&quot;, score: 0)
    ]

    var body: some View {
        VStack {
            ForEach($players) { $player in
                TextField(&quot;Name&quot;, text: $player.name)
                Stepper(&quot;\(player.score)&quot;, value: $player.score)
            }

            Button(&quot;Add Player&quot;, systemImage: &quot;plus.circle.fill&quot;) {
                players.append(Player(name: newPlayer, score: 0))
            }
            .padding()
            Spacer()
        }
        .padding()
    }
}</code></pre>
<hr>
<h2 id="핵심-정리">핵심 정리</h2>
<table>
<thead>
<tr>
<th>개념</th>
<th>내용</th>
</tr>
</thead>
<tbody><tr>
<td><code>Identifiable</code></td>
<td>고유 <code>id</code>를 보장하는 프로토콜 — <code>ForEach</code>에서 <code>id:</code> 생략 가능</td>
</tr>
<tr>
<td><code>UUID()</code></td>
<td>전 세계 유일한 식별자 자동 생성</td>
</tr>
<tr>
<td><code>ForEach($배열)</code></td>
<td>배열 요소를 바인딩으로 받아 직접 수정</td>
</tr>
<tr>
<td><code>Stepper</code></td>
<td><code>+</code> / <code>-</code> 버튼으로 숫자 값 증감</td>
</tr>
<tr>
<td><code>players.append()</code></td>
<td>배열에 새 요소 추가 → UI 자동 갱신</td>
</tr>
</tbody></table>
<hr>
<h2 id="다음에-배울-것">다음에 배울 것</h2>
<ul>
<li><code>.onDelete</code> 로 리스트 항목 스와이프 삭제</li>
<li><code>@Binding</code>으로 자식 뷰에서 부모 배열 수정</li>
<li><code>NavigationStack</code> + <code>NavigationLink</code>로 화면 전환</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[iOS 26 & macOS Tahoe: 더 유연하고 투명해진 SwiftUI 디자인 시스템 살펴보기]]></title>
            <link>https://velog.io/@asher_joon/iOS-26-macOS-Tahoe-%EB%8D%94-%EC%9C%A0%EC%97%B0%ED%95%98%EA%B3%A0-%ED%88%AC%EB%AA%85%ED%95%B4%EC%A7%84-SwiftUI-%EB%94%94%EC%9E%90%EC%9D%B8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%82%B4%ED%8E%B4%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@asher_joon/iOS-26-macOS-Tahoe-%EB%8D%94-%EC%9C%A0%EC%97%B0%ED%95%98%EA%B3%A0-%ED%88%AC%EB%AA%85%ED%95%B4%EC%A7%84-SwiftUI-%EB%94%94%EC%9E%90%EC%9D%B8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%82%B4%ED%8E%B4%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Wed, 18 Mar 2026 04:24:56 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요! 이번 WWDC25 세션 중 가장 시각적으로 즐거웠던 <strong>&quot;Build a SwiftUI app with the new design&quot;</strong> 내용을 정리했습니다. Apple이 새롭게 제시한 디자인 언어인 <strong>Liquid Glass</strong>를 어떻게 SwiftUI 프로젝트에 녹여낼 수 있을지 핵심만 짚어보겠습니다. 🎨</p>
<hr>
<p><img src="https://velog.velcdn.com/images/asher_joon/post/03e19dca-dc5f-4293-b087-5524aadc5cd5/image.png" alt=""></p>
<h2 id="1-새로운-디자인의-심장-liquid-glass">1. 새로운 디자인의 심장: Liquid Glass</h2>
<p>이번 업데이트의 주인공은 단연 <strong>Liquid Glass(리퀴드 글래스)</strong>입니다. 기존의 Glassmorphism보다 한 단계 진화하여, 유리의 광학적 특성과 액체의 역동적인 움직임을 결합한 재질입니다. 새로운 디자인의 방식으로서 좀 더 편안한 디자인을 사용자에게 제공하고 있다고 생각합니다.</p>
<ul>
<li><strong>동적 적응:</strong> 스크롤되는 배경의 색상과 명암에 따라 실시간으로 빛을 굴절시키며 대비를 조절합니다.</li>
<li><strong>살아있는 인터랙션:</strong> 버튼을 누르거나 슬라이더를 옮길 때, 단순한 강조가 아니라 재질 자체가 반응하며 사용자에게 피드백을 줍니다.</li>
</ul>
<hr>
<h2 id="2-앱-구조app-structure의-진화">2. 앱 구조(App Structure)의 진화</h2>
<p>사용자가 앱을 탐색하는 방식에 맞춰 기본 컴포넌트들이 더 영리해졌습니다.</p>
<h3 id="📌-navigationsplitview--sidebars">📌 NavigationSplitView &amp; Sidebars</h3>
<p><img src="https://velog.velcdn.com/images/asher_joon/post/883cce6c-e24a-4380-9b92-a6f2e6126dc6/image.png" alt=""></p>
<p>사이드바가 이제 콘텐츠 위에 떠 있는 <strong>Floating</strong> 형태로 변했습니다.</p>
<ul>
<li><strong><code>.backgroundExtensionEffect</code></strong>: 사이드바 뒤쪽으로 배경 이미지를 확장하여 콘텐츠가 잘리지 않고 자연스럽게 이어지게 만듭니다.</li>
</ul>
<h3 id="📌-tabview의-최소화-동작">📌 TabView의 최소화 동작</h3>
<p><img src="https://velog.velcdn.com/images/asher_joon/post/34e89ab6-dc7e-46f1-83b9-67e52e1fc909/image.png" alt=""></p>
<p>아이폰의 한정된 화면을 더 넓게 쓸 수 있는 기능이 추가되었습니다.</p>
<pre><code class="language-swift">TabView {
    // 탭 콘텐츠
}
.tabBarMinimizeBehavior(.onScroll) // 스크롤 시 탭 바가 자동으로 축소됨</code></pre>
<hr>
<h2 id="3-주요-api-업데이트-및-코드-포인트">3. 주요 API 업데이트 및 코드 포인트</h2>
<h3 id="🔘-더-강력해진-buttons--controls">🔘 더 강력해진 Buttons &amp; Controls</h3>
<p>이제 표준 컨트롤에 Liquid Glass를 입히는 것이 매우 간단해졌습니다.</p>
<ul>
<li><strong>새로운 스타일</strong>: <code>.buttonStyle(.glass)</code> 또는 <code>.glassProminent</code>를 사용하여 즉시 적용 가능합니다.</li>
<li><strong>Concentricity(동심성)</strong>: 컨테이너의 곡률과 내부 버튼의 곡률을 완벽하게 맞추는 설정이 추가되었습니다.</li>
</ul>
<!-- end list -->

<pre><code class="language-swift">Rectangle()
    .fill(.blue)
    .clipShape(.concentricRectangle(corner: .container)) // 부모 뷰 곡률에 자동 맞춤</code></pre>
<h3 id="🔍-search-experience">🔍 Search Experience</h3>
<p>검색 인터페이스가 플랫폼별로 최적화되었습니다.</p>
<ul>
<li><strong>iOS</strong>: 검색 필드가 하단으로 이동하여 한 손 조작이 훨씬 쉬워졌습니다.</li>
<li><strong><code>.searchToolbarBehavior(.minimized)</code></strong>: 검색이 주 기능이 아닌 경우 아이콘으로 최소화해 두었다가 필요할 때 확장할 수 있습니다.</li>
</ul>
<h3 id="🏷️-toolbar-enhancements">🏷️ Toolbar Enhancements</h3>
<p><img src="https://velog.velcdn.com/images/asher_joon/post/74463832-eb88-4167-9531-f3d3e1d291f8/image.png" alt=""></p>
<ul>
<li><code>ToolbarSpacer</code>: 버튼 간의 논리적 그룹화를 위해 고정 간격이나 유연한 간격을 줄 수 있습니다.</li>
<li><code>.badge()</code>: 이제 탭 바뿐만 아니라 툴바 아이템에도 배지를 직접 달 수 있습니다.</li>
</ul>
<hr>
<h2 id="4-커스텀-뷰에-liquid-glass-입히기">4. 커스텀 뷰에 Liquid Glass 입히기</h2>
<p>시스템 UI 외에 내가 만든 커스텀 컴포넌트에도 이 아름다운 효과를 적용할 수 있습니다.</p>
<pre><code class="language-swift">struct CustomBadge: View {
    var body: some View {
        Text(&quot;New&quot;)
            .glossEffect(.capsule) // 리퀴드 글래스 효과 적용
            .interactive(true)     // 터치 시 시각적 반응 활성화
    }
}</code></pre>
<blockquote>
<p><strong>💡 핵심 팁: GlassEffectContainer</strong>
여러 개의 Glass 요소가 겹칠 때 서로의 굴절 값을 공유하려면 <code>GlassEffectContainer</code>로 묶어줘야 합니다. 그렇지 않으면 경계선이 어색하게 보일 수 있습니다.</p>
</blockquote>
<hr>
<h2 id="마치며-업데이트를-위한-체크리스트">마치며: 업데이트를 위한 체크리스트</h2>
<p>WWDC25 디자인 시스템을 앱에 적용하기 위한 3단계 요약입니다.</p>
<ol>
<li><strong>Xcode 26 SDK 빌드</strong>: 표준 컨트롤은 빌드만으로도 새로운 디자인이 상당 부분 자동 적용됩니다.</li>
<li><strong>커스텀 배경 제거</strong>: 기존에 <code>.presentationBackground</code> 등으로 직접 구현했던 어두운 배경이나 불투명한 레이어들을 제거하고 시스템의 Liquid Glass가 빛나게 하세요.</li>
<li><strong>아이콘 미세 조정</strong>: 새로운 시스템은 모노크롬(Monochrome) 렌더링을 선호합니다. 화려한 색상보다는 계층 구조를 통한 의미 전달에 집중하세요.</li>
</ol>
<hr>
<h3 id="🔗-참고-링크">🔗 참고 링크</h3>
<ul>
<li><a href="https://www.google.com/search?q=https://developer.apple.com/videos/play/wwdc2025/3MugGCtm26A">Apple Developer - Build a SwiftUI app with the new design</a></li>
</ul>
<hr>
<p><strong>도움이 되셨다면 공유와 댓글 부탁드립니다! 궁금한 점은 언제든 남겨주세요.</strong> 😊</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Swift에서 var인데도 값 변경이 안 되는 이유 (Struct 내부에서 발생한 에러 해결)]]></title>
            <link>https://velog.io/@asher_joon/Swift%EC%97%90%EC%84%9C-var%EC%9D%B8%EB%8D%B0%EB%8F%84-%EA%B0%92-%EB%B3%80%EA%B2%BD%EC%9D%B4-%EC%95%88-%EB%90%98%EB%8A%94-%EC%9D%B4%EC%9C%A0-Struct-%EB%82%B4%EB%B6%80%EC%97%90%EC%84%9C-%EB%B0%9C%EC%83%9D%ED%95%9C-%EC%97%90%EB%9F%AC-%ED%95%B4%EA%B2%B0</link>
            <guid>https://velog.io/@asher_joon/Swift%EC%97%90%EC%84%9C-var%EC%9D%B8%EB%8D%B0%EB%8F%84-%EA%B0%92-%EB%B3%80%EA%B2%BD%EC%9D%B4-%EC%95%88-%EB%90%98%EB%8A%94-%EC%9D%B4%EC%9C%A0-Struct-%EB%82%B4%EB%B6%80%EC%97%90%EC%84%9C-%EB%B0%9C%EC%83%9D%ED%95%9C-%EC%97%90%EB%9F%AC-%ED%95%B4%EA%B2%B0</guid>
            <pubDate>Tue, 17 Mar 2026 01:44:45 GMT</pubDate>
            <description><![CDATA[<h1 id="swift에서-var인데도-값-변경이-안-되는-이유">Swift에서 <code>var</code>인데도 값 변경이 안 되는 이유</h1>
<p>SwiftUI로 간단한 <code>DiceView</code>를 만들다가 예상하지 못한 에러를 만났습니다.</p>
<p>분명 <code>var</code>로 선언한 변수인데도 값 변경이 되지 않았습니다.</p>
<p>처음에는 <strong>&quot;var인데 왜 변경이 안 되지?&quot;</strong> 라고 생각했지만
알고 보니 <strong>변수 문제가 아니라 코드 위치 문제</strong>였습니다.</p>
<p>이번 글에서는 그 이유를 정리해보려고 합니다.</p>
<hr>
<h1 id="문제-상황">문제 상황</h1>
<p><img src="https://velog.velcdn.com/images/asher_joon/post/ddc47094-9bdf-4752-95d3-fc084d49754d/image.png" alt=""></p>
<p>다음과 같은 코드를 작성했습니다.</p>
<pre><code class="language-swift">struct DiceView: View {
    let diceNumber: Int = 1
    var canChangeNumber: Int = 1

    canChangeNumber = 2

    init(canChageNumber: Int) {
        self.canChangeNumber = canChageNumber
    }

    var body: some View {
        Image(systemName: &quot;die.face.1&quot;)
            .font(.largeTitle)
    }
}</code></pre>
<p>여기서 다음 코드에서 에러가 발생합니다.</p>
<pre><code class="language-swift">canChangeNumber = 2</code></pre>
<hr>
<h1 id="let과-var의-차이"><code>let</code>과 <code>var</code>의 차이</h1>
<p>Swift에서 값을 선언하는 방법은 크게 두 가지가 있습니다.</p>
<h2 id="let"><code>let</code></h2>
<p>변경할 수 없는 <strong>상수</strong></p>
<pre><code class="language-swift">let number = 1
number = 2 // ❌ 에러</code></pre>
<h2 id="var"><code>var</code></h2>
<p>변경할 수 있는 <strong>변수</strong></p>
<pre><code class="language-swift">var number = 1
number = 2 // ✅ 가능</code></pre>
<p>그래서 처음에는 <code>var</code>인데도 값 변경이 안 되는 것이 이해되지 않았습니다.</p>
<p>하지만 문제는 <strong>var 자체가 아니라 코드 위치</strong>에 있었습니다.</p>
<hr>
<h1 id="에러가-발생한-진짜-이유">에러가 발생한 진짜 이유</h1>
<p>Swift의 <code>struct</code> 내부에는 다음 요소만 올 수 있습니다.</p>
<ul>
<li>Property (프로퍼티)</li>
<li>Method (함수)</li>
<li>Initializer (<code>init</code>)</li>
</ul>
<p>하지만 아래 코드는 <strong>실행문(statement)</strong> 입니다.</p>
<pre><code class="language-swift">canChangeNumber = 2</code></pre>
<p>이 코드는 <strong>어떤 함수나 <code>init</code> 안에도 포함되어 있지 않습니다.</strong></p>
<p>그래서 Swift는</p>
<blockquote>
<p>&quot;이 코드는 언제 실행해야 하는 코드지?&quot;</p>
</blockquote>
<p>라고 판단하게 되고 <strong>컴파일 에러가 발생합니다.</strong></p>
<p>즉, <strong>struct 내부에는 실행 코드를 직접 작성할 수 없습니다.</strong></p>
<hr>
<h1 id="해결-방법">해결 방법</h1>
<h2 id="1️⃣-초기값을-직접-변경">1️⃣ 초기값을 직접 변경</h2>
<p>가장 간단한 방법입니다.</p>
<pre><code class="language-swift">var canChangeNumber: Int = 2</code></pre>
<hr>
<h2 id="2️⃣-init에서-변경-가장-일반적인-방법">2️⃣ <code>init</code>에서 변경 (가장 일반적인 방법)</h2>
<pre><code class="language-swift">struct DiceView: View {
    let diceNumber: Int = 1
    var canChangeNumber: Int = 1

    init(canChageNumber: Int) {
        self.canChangeNumber = canChageNumber
    }

    var body: some View {
        Image(systemName: &quot;die.face.1&quot;)
            .font(.largeTitle)
    }
}</code></pre>
<hr>
<h2 id="3️⃣-함수에서-변경">3️⃣ 함수에서 변경</h2>
<pre><code class="language-swift">func changeNumber() {
    canChangeNumber = 2
}</code></pre>
<hr>
<h1 id="swiftui에서-상태를-바꿀-때">SwiftUI에서 상태를 바꿀 때</h1>
<p>SwiftUI에서는 값이 바뀌면 <strong>UI도 함께 업데이트</strong>되어야 합니다.</p>
<p>그래서 보통 다음과 같이 <code>@State</code>를 사용합니다.</p>
<pre><code class="language-swift">@State var diceNumber = 1</code></pre>
<p>이렇게 하면 값이 바뀔 때 <strong>화면도 자동으로 다시 렌더링</strong>됩니다.</p>
<hr>
<h1 id="정리">정리</h1>
<p>이번 에러의 핵심은 다음과 같습니다.</p>
<ul>
<li><code>var</code>라서 문제가 발생한 것이 아니다</li>
<li><code>struct</code> 내부에는 <strong>실행문을 직접 작성할 수 없다</strong></li>
<li>값 변경은 <strong>init 또는 함수 내부에서 해야 한다</strong></li>
</ul>
<p>SwiftUI를 처음 공부할 때 <strong>struct 구조 때문에 발생하는 에러가 꽤 많습니다.</strong></p>
<p>이번 문제를 통해 <strong>Swift struct의 기본 구조를 이해하는 데 도움이 되었습니다.</strong></p>
<hr>
<p>💡 SwiftUI 공부하면서 겪은 문제들을 계속 정리해보려고 합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SwiftUI Day 4 - 온보딩 플로우 (TabView Page Style, 파라미터 컴포넌트)]]></title>
            <link>https://velog.io/@asher_joon/SwiftUI-Day-4-%EC%98%A8%EB%B3%B4%EB%94%A9-%ED%94%8C%EB%A1%9C%EC%9A%B0-TabView-Page-Style-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8</link>
            <guid>https://velog.io/@asher_joon/SwiftUI-Day-4-%EC%98%A8%EB%B3%B4%EB%94%A9-%ED%94%8C%EB%A1%9C%EC%9A%B0-TabView-Page-Style-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8</guid>
            <pubDate>Mon, 16 Mar 2026 17:45:29 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/asher_joon/post/adeb1180-4703-4022-b7c1-161d2109cf75/image.png" alt="">
<img src="https://velog.velcdn.com/images/asher_joon/post/6073c3aa-e076-449e-96cb-49495bd4d6da/image.png" alt=""></p>
<hr>
<h2 id="📁-프로젝트-파일-구조">📁 프로젝트 파일 구조</h2>
<pre><code>OnboardingFlow/
├── OnboardingFlowApp.swift      # @main 진입점
├── ContentView.swift            # TabView (page 스타일) + 그라디언트 배경
├── WelcomePage.swift            # 첫 번째 온보딩 페이지
├── FeaturePage.swift            # 두 번째 온보딩 페이지
└── FeatureCard.swift            # 재사용 가능한 기능 카드 컴포넌트</code></pre><hr>
<h2 id="📱-tabview--페이지-스타일-온보딩">📱 TabView — 페이지 스타일 (온보딩)</h2>
<p><code>TabView</code>에 <code>.tabViewStyle(.page)</code>를 적용하면 바텀탭바 대신 <strong>좌우 스와이프 페이지</strong> 형태가 된다.</p>
<pre><code class="language-swift">struct ContentView: View {
    let gradientColors: [Color] = [.grdientTop, .gradientBottom]

    var body: some View {
        TabView {
            WelcomePage()
            FeaturePage()
        }
        .background(Gradient(colors: gradientColors))
        .tabViewStyle(.page)   // 핵심: 페이지 스타일 적용
    }
}</code></pre>
<h3 id="tabview-스타일-비교">TabView 스타일 비교</h3>
<table>
<thead>
<tr>
<th>스타일</th>
<th>코드</th>
<th>결과</th>
</tr>
</thead>
<tbody><tr>
<td>바텀 탭바</td>
<td><code>.tabViewStyle(.automatic)</code> 또는 기본값</td>
<td>하단 탭 UI</td>
</tr>
<tr>
<td>페이지 스와이프</td>
<td><code>.tabViewStyle(.page)</code></td>
<td>좌우 스와이프 + 점(dot) 인디케이터</td>
</tr>
</tbody></table>
<blockquote>
<p>온보딩 화면처럼 &quot;순서대로 넘기는&quot; UX에는 <code>.page</code> 스타일이 적합하다.</p>
</blockquote>
<hr>
<h2 id="🌈-gradient-배경">🌈 Gradient 배경</h2>
<pre><code class="language-swift">let gradientColors: [Color] = [.grdientTop, .gradientBottom]

.background(Gradient(colors: gradientColors))</code></pre>
<blockquote>
<p><code>Gradient(colors:)</code>는 위→아래 방향의 선형 그라디언트를 생성한다.
색상 배열의 순서가 그라디언트 방향을 결정한다.</p>
</blockquote>
<hr>
<h2 id="🧩-파라미터-있는-컴포넌트-featurecard">🧩 파라미터 있는 컴포넌트 (FeatureCard)</h2>
<p>컴포넌트에 <code>let</code> 프로퍼티를 선언하면 <strong>외부에서 값을 주입</strong>할 수 있다.</p>
<pre><code class="language-swift">struct FeatureCard: View {
    let iconName: String      // 외부에서 받는 파라미터
    let description: String

    var body: some View {
        HStack {
            Image(systemName: iconName)   // 파라미터 사용
                .font(.largeTitle)
                .frame(width: 50)
                .padding(.trailing, 10)

            Text(description)

            Spacer()
        }
        .padding()
        .background(.tint, in: RoundedRectangle(cornerRadius: 12))
        .foregroundStyle(Color(.white))
    }
}

// 사용법 — 각각 다른 아이콘과 텍스트 전달
FeatureCard(iconName: &quot;person.2.crop.square.stack.fill&quot;, description: &quot;소셜 기능 설명&quot;)
FeatureCard(iconName: &quot;quote.bubble.fill&quot;, description: &quot;메시지 기능 설명&quot;)</code></pre>
<blockquote>
<p>파라미터 컴포넌트를 쓰면 <strong>같은 UI 구조, 다른 데이터</strong>를 반복 없이 표현할 수 있다.</p>
</blockquote>
<hr>
<h2 id="🎨-backgroundtint-in-문법">🎨 <code>.background(.tint, in:)</code> 문법</h2>
<pre><code class="language-swift">.background(.tint, in: RoundedRectangle(cornerRadius: 12))</code></pre>
<table>
<thead>
<tr>
<th>파라미터</th>
<th>역할</th>
</tr>
</thead>
<tbody><tr>
<td><code>.tint</code></td>
<td>앱의 액센트 컬러 (자동 적용)</td>
</tr>
<tr>
<td><code>in: RoundedRectangle(...)</code></td>
<td>배경의 모양 지정</td>
</tr>
</tbody></table>
<blockquote>
<p><code>.tint</code>는 앱 전체의 포인트 컬러를 자동으로 가져오므로, 특정 색상을 하드코딩하지 않아도 된다.</p>
</blockquote>
<hr>
<h2 id="🏠-welcomepage--첫-화면">🏠 WelcomePage — 첫 화면</h2>
<pre><code class="language-swift">struct WelcomePage: View {
    var body: some View {
        VStack {
            ZStack {
                RoundedRectangle(cornerRadius: 30)
                    .frame(width: 150, height: 150)
                    .foregroundColor(Color.blue)

                Image(systemName: &quot;figure.2.and.child.holdinghands&quot;)
                    .font(.system(size: 70))
                    .foregroundColor(.white)
            }
            Text(&quot;Welcome to My App&quot;)
                .font(.largeTitle)
                .fontWeight(.semibold)
                .padding(.top)

            Text(&quot;Description text&quot;)
                .font(.title2)
        }
        .padding()
    }
}</code></pre>
<blockquote>
<p>Day 1에서 배운 ZStack(배경 도형 + 아이콘) 패턴을 온보딩에 그대로 활용.</p>
</blockquote>
<hr>
<h2 id="📄-featurepage--기능-소개-화면">📄 FeaturePage — 기능 소개 화면</h2>
<pre><code class="language-swift">struct FeaturePage: View {
    var body: some View {
        VStack {
            Text(&quot;Feature Page&quot;)
                .font(.largeTitle)
                .fontWeight(.bold)
                .padding(.bottom)

            FeatureCard(iconName: &quot;person.2.crop.square.stack.fill&quot;,
                        description: &quot;A multiline description about a feature&quot;)
            FeatureCard(iconName: &quot;quote.bubble.fill&quot;,
                        description: &quot;Short description about a feature&quot;)
        }
        .padding()
    }
}</code></pre>
<blockquote>
<p>파라미터 컴포넌트 덕분에 화면은 단순히 카드를 나열하는 역할만 한다.</p>
</blockquote>
<hr>
<h2 id="✅-핵심-정리">✅ 핵심 정리</h2>
<table>
<thead>
<tr>
<th>개념</th>
<th>내용</th>
</tr>
</thead>
<tbody><tr>
<td><code>TabView</code> + <code>.page</code></td>
<td>좌우 스와이프 온보딩 UI</td>
</tr>
<tr>
<td><code>Gradient(colors:)</code></td>
<td>배열 순서대로 그라디언트 배경</td>
</tr>
<tr>
<td><code>let</code> 프로퍼티</td>
<td>컴포넌트에 외부에서 값 주입</td>
</tr>
<tr>
<td><code>.background(.tint, in:)</code></td>
<td>모양 + 색상을 한 번에 배경 지정</td>
</tr>
</tbody></table>
<hr>
<h2 id="✅-다음에-배울-것">✅ 다음에 배울 것</h2>
<ul>
<li><code>@State</code>, <code>@Binding</code>으로 온보딩 완료 여부 관리</li>
<li><code>AppStorage</code>로 온보딩 다시 안 보이게 처리</li>
<li>온보딩 완료 후 메인 화면으로 전환 (<code>fullScreenCover</code>)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[SwiftUI Day 3 - Apple 공식 튜토리얼 따라하기 (구조체, 연산 프로퍼티, 조건 표현식)]]></title>
            <link>https://velog.io/@asher_joon/SwiftUI-Day-3-Apple-%EA%B3%B5%EC%8B%9D-%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC-%EB%94%B0%EB%9D%BC%ED%95%98%EA%B8%B0-%EA%B5%AC%EC%A1%B0%EC%B2%B4-%EC%97%B0%EC%82%B0-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0-%EC%A1%B0%EA%B1%B4-%ED%91%9C%ED%98%84%EC%8B%9D</link>
            <guid>https://velog.io/@asher_joon/SwiftUI-Day-3-Apple-%EA%B3%B5%EC%8B%9D-%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC-%EB%94%B0%EB%9D%BC%ED%95%98%EA%B8%B0-%EA%B5%AC%EC%A1%B0%EC%B2%B4-%EC%97%B0%EC%82%B0-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0-%EC%A1%B0%EA%B1%B4-%ED%91%9C%ED%98%84%EC%8B%9D</guid>
            <pubDate>Mon, 16 Mar 2026 17:43:12 GMT</pubDate>
            <description><![CDATA[<h1 id="업로드중"><img src="blob:https://velog.io/dfc3f24e-8223-48a2-b4c8-48e14786bbd7" alt="업로드중.."></h1>
<blockquote>
<p>이 내용은 Apple 공식 SwiftUI 튜토리얼을 따라 실습한 내용입니다.</p>
</blockquote>
<hr>
<h2 id="📁-프로젝트-파일-구조">📁 프로젝트 파일 구조</h2>
<pre><code>Tutorial/
├── TutorialApp.swift        # @main 진입점
└── ContentView.swift        # DateInfo 구조체 + 날씨 UI</code></pre><hr>
<h2 id="🧱-데이터를-담는-구조체-struct">🧱 데이터를 담는 구조체 (Struct)</h2>
<p>뷰에서 사용하는 <strong>데이터를 구조체로 정의</strong>하면 재사용성과 가독성이 높아진다.</p>
<pre><code class="language-swift">struct DateInfo: View {
    let days: String    // 요일 (Mon, Tue, ...)
    let isRainy: Bool   // 비 여부
    let high: Int       // 최고 기온
    let low: Int        // 최저 기온

    var body: some View { ... }
}</code></pre>
<blockquote>
<p><code>View</code> 프로토콜을 채택한 구조체는 <code>body</code>를 통해 UI를 표현한다.
<code>let</code> 프로퍼티로 선언하면 외부에서 초기화 시 값을 주입받는다.</p>
</blockquote>
<hr>
<h2 id="🔁-연산-프로퍼티-computed-property">🔁 연산 프로퍼티 (Computed Property)</h2>
<p>저장된 값을 직접 리턴하는 게 아니라, <strong>조건에 따라 계산해서 반환</strong>하는 프로퍼티.</p>
<pre><code class="language-swift">var iconName: String {
    isRainy ? &quot;sun.max.fill&quot; : &quot;cloud.rain.fill&quot;
}

var iconColor: Color {
    isRainy ? Color.yellow : Color.blue
}</code></pre>
<blockquote>
<p>연산 프로퍼티를 쓰면 <code>body</code> 안에 분기 로직을 넣지 않아도 된다 — 코드가 깔끔해진다.</p>
</blockquote>
<hr>
<h2 id="❓-삼항-연산자-ternary-operator">❓ 삼항 연산자 (Ternary Operator)</h2>
<pre><code class="language-swift">조건 ? 참일 때 값 : 거짓일 때 값</code></pre>
<pre><code class="language-swift">isRainy ? &quot;sun.max.fill&quot; : &quot;cloud.rain.fill&quot;
// isRainy가 true면 &quot;sun.max.fill&quot;
// isRainy가 false면 &quot;cloud.rain.fill&quot;</code></pre>
<blockquote>
<p>Swift의 삼항 연산자는 <code>if/else</code> 한 줄 버전. 값을 반환해야 할 때 자주 쓰인다.</p>
</blockquote>
<hr>
<h2 id="🖊-문자열-보간-string-interpolation">🖊 문자열 보간 (String Interpolation)</h2>
<p>변수 값을 문자열 안에 직접 삽입하는 문법.</p>
<pre><code class="language-swift">Text(&quot;High: \(high)&quot;)   // &quot;High: 80&quot;
Text(&quot;Low: \(low)&quot;)     // &quot;Low: 30&quot;</code></pre>
<blockquote>
<p><code>\(변수명)</code> 형태로 사용. Swift에서 가장 기본적인 문자열 합성 방법.</p>
</blockquote>
<hr>
<h2 id="🎨-fontweight로-글자-굵기-조절">🎨 Font.Weight로 글자 굵기 조절</h2>
<pre><code class="language-swift">Text(&quot;High: \(high)&quot;)
    .fontWeight(Font.Weight.semibold)   // 반굵게

Text(&quot;Low: \(low)&quot;)
    .fontWeight(Font.Weight.medium)     // 중간 굵기
    .foregroundStyle(Color.secondary)   // 회색 계열 보조 색상</code></pre>
<h3 id="fontweight-종류-가벼운-순">Font.Weight 종류 (가벼운 순)</h3>
<table>
<thead>
<tr>
<th>Weight</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>.ultraLight</code></td>
<td>가장 얇음</td>
</tr>
<tr>
<td><code>.thin</code></td>
<td>얇음</td>
</tr>
<tr>
<td><code>.light</code></td>
<td>가벼움</td>
</tr>
<tr>
<td><code>.regular</code></td>
<td>기본</td>
</tr>
<tr>
<td><code>.medium</code></td>
<td>중간</td>
</tr>
<tr>
<td><code>.semibold</code></td>
<td>반굵게</td>
</tr>
<tr>
<td><code>.bold</code></td>
<td>굵게</td>
</tr>
<tr>
<td><code>.heavy</code></td>
<td>두껍게</td>
</tr>
<tr>
<td><code>.black</code></td>
<td>가장 두껍게</td>
</tr>
</tbody></table>
<hr>
<h2 id="🎨-colorsecondary">🎨 Color.secondary</h2>
<pre><code class="language-swift">.foregroundStyle(Color.secondary)</code></pre>
<blockquote>
<p><code>Color.secondary</code>는 시스템이 자동으로 정하는 <strong>보조 색상</strong> (보통 회색).
다크모드/라이트모드 대응이 자동으로 된다 — 하드코딩 색상보다 권장.</p>
</blockquote>
<hr>
<h2 id="📋-전체-코드">📋 전체 코드</h2>
<pre><code class="language-swift">struct ContentView: View {
    var body: some View {
        HStack {
            DateInfo(days: &quot;Mon&quot;, isRainy: true,  high: 80, low: 30)
            DateInfo(days: &quot;Tue&quot;, isRainy: false, high: 30, low: 20)
        }
    }
}

struct DateInfo: View {
    let days: String
    let isRainy: Bool
    let high: Int
    let low: Int

    var iconName: String {
        isRainy ? &quot;sun.max.fill&quot; : &quot;cloud.rain.fill&quot;
    }
    var iconColor: Color {
        isRainy ? Color.yellow : Color.blue
    }

    var body: some View {
        VStack {
            Text(days)
                .font(Font.headline)
            Image(systemName: iconName)
                .foregroundStyle(iconColor)
                .font(Font.largeTitle)
                .padding(5)
            Text(&quot;High: \(high)&quot;)
                .fontWeight(Font.Weight.semibold)
            Text(&quot;Low: \(low)&quot;)
                .fontWeight(Font.Weight.medium)
                .foregroundStyle(Color.secondary)
        }
        .padding(10)
    }
}</code></pre>
<hr>
<h2 id="✅-핵심-정리">✅ 핵심 정리</h2>
<table>
<thead>
<tr>
<th>개념</th>
<th>내용</th>
</tr>
</thead>
<tbody><tr>
<td>구조체 + <code>View</code></td>
<td>UI와 데이터를 하나의 단위로 묶기</td>
</tr>
<tr>
<td>연산 프로퍼티</td>
<td>조건 로직을 <code>body</code> 밖으로 분리</td>
</tr>
<tr>
<td>삼항 연산자</td>
<td>조건에 따른 값 반환 한 줄로 표현</td>
</tr>
<tr>
<td>문자열 보간 <code>\()</code></td>
<td>변수를 문자열 안에 직접 삽입</td>
</tr>
<tr>
<td><code>Color.secondary</code></td>
<td>다크모드 자동 대응 보조 색상</td>
</tr>
</tbody></table>
<hr>
<h2 id="✅-다음에-배울-것">✅ 다음에 배울 것</h2>
<ul>
<li><code>ForEach</code>로 데이터 배열을 리스트로 렌더링</li>
<li><code>@State</code>로 뷰 내부 상태 관리</li>
<li><code>ObservableObject</code>로 뷰 외부 데이터 바인딩</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SwiftUI] HStack, VStack, ZStack으로 헤더 컴포넌트 만들기]]></title>
            <link>https://velog.io/@asher_joon/SwiftUI-HStack-VStack-ZStack%EC%9C%BC%EB%A1%9C-%ED%97%A4%EB%8D%94-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@asher_joon/SwiftUI-HStack-VStack-ZStack%EC%9C%BC%EB%A1%9C-%ED%97%A4%EB%8D%94-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Wed, 11 Mar 2026 14:39:05 GMT</pubDate>
            <description><![CDATA[<h1 id="swiftui-입문--f1-앱-헤더-만들기">SwiftUI 입문 — F1 앱 헤더 만들기</h1>
<blockquote>
<p>Apple Developer Academy @ POSTECH 입과 후 첫 번째 SwiftUI 학습 기록입니다.
제가 스스로 연습과 학습을 위한 학습입니다. 애플아카데미에서 제공하는 학습자료가 아닌 점 알려드립니다. 전혀 무관함을 알려드립니다. 개인프로젝트입니다.</p>
</blockquote>
<hr>
<h2 id="들어가며">들어가며</h2>
<p>SwiftUI를 처음 접하면서 가장 먼저 만들어본 것은 F1 레이싱 앱의 헤더 컴포넌트입니다. 단순해 보이지만 레이아웃의 핵심 개념인 <code>HStack</code>, <code>VStack</code>, <code>ZStack</code>, <code>Spacer</code>를 모두 활용할 수 있는 좋은 출발점이었습니다.</p>
<p>완성된 헤더는 아래와 같습니다.</p>
<pre><code>☰  MONACO GRAND PRIX                    ⚙
   🔴 Live • Lap 42/78</code></pre><hr>
<h2 id="프로젝트-구조">프로젝트 구조</h2>
<p>파일을 역할별로 분리하는 것부터 시작했습니다.</p>
<pre><code>practiceApp/
├── Assets.xcassets
├── Components/
│   └── Header.swift       # 재사용 가능한 UI 부품
├── practiceApp.swift      # @main 앱 진입점
└── Screens/
    └── HomeView.swift     # 실제 화면</code></pre><ul>
<li><code>Components/</code> — 여러 화면에서 재사용할 수 있는 작은 UI 단위</li>
<li><code>Screens/</code> — 하나의 완성된 화면 단위</li>
</ul>
<blockquote>
<p><code>ContentView.swift</code>는 필수가 아닙니다. <code>@main</code> 파일에서 원하는 View를 바로 진입점으로 지정할 수 있습니다.</p>
</blockquote>
<pre><code class="language-swift">@main
struct practiceApp: App {
    var body: some Scene {
        WindowGroup {
            HomeView() // ContentView 없이 바로 연결
        }
    }
}</code></pre>
<hr>
<h2 id="레이아웃-기초--hstack--vstack--zstack">레이아웃 기초 — HStack / VStack / ZStack</h2>
<p>SwiftUI의 레이아웃은 세 가지 Stack으로 구성됩니다.</p>
<table>
<thead>
<tr>
<th>Stack</th>
<th>방향</th>
<th>주요 용도</th>
</tr>
</thead>
<tbody><tr>
<td><code>HStack</code></td>
<td>가로 (←→)</td>
<td>아이콘, 버튼 나열</td>
</tr>
<tr>
<td><code>VStack</code></td>
<td>세로 (↑↓)</td>
<td>제목 + 부제목 배치</td>
</tr>
<tr>
<td><code>ZStack</code></td>
<td>앞뒤 겹치기</td>
<td>배경 위에 컨텐츠 올리기</td>
</tr>
</tbody></table>
<p>각 Stack은 <code>alignment</code> 파라미터로 정렬 방향을 지정할 수 있습니다.</p>
<pre><code class="language-swift">VStack(alignment: .leading) { }  // 왼쪽 정렬
ZStack(alignment: .top) { }      // 위쪽 정렬</code></pre>
<hr>
<h2 id="spacer--공간을-차지하는-투명한-뷰">Spacer() — 공간을 차지하는 투명한 뷰</h2>
<p><code>Spacer()</code>는 <strong>남은 공간을 전부 차지하는 투명한 뷰</strong>입니다. 직접 무언가를 밀어내는 것이 아니라, 공간을 먼저 차지함으로써 다른 뷰들이 상대적으로 밀려나는 효과를 만듭니다.</p>
<pre><code class="language-swift">// HStack — 양쪽 끝으로 분리
HStack {
    Text(&quot;왼쪽&quot;)
    Spacer()
    Text(&quot;오른쪽&quot;)
}

// VStack — 헤더를 상단에 고정
VStack {
    Header()
    Spacer()
}</code></pre>
<hr>
<h2 id="전체-화면-검은-배경-적용">전체 화면 검은 배경 적용</h2>
<p><code>ZStack</code>으로 <code>Color.black</code>을 배경으로 먼저 깔고, 그 위에 컨텐츠를 올립니다. <code>.ignoresSafeArea()</code>를 적용하면 노치/홈바 영역까지 색상이 채워집니다.</p>
<pre><code class="language-swift">struct HomeView: View {
    var body: some View {
        ZStack(alignment: .top) {
            Color.black
                .ignoresSafeArea()  // Safe Area 영역까지 검은색

            VStack(alignment: .leading) {
                Header()
                Spacer()
            }
        }
    }
}</code></pre>
<hr>
<h2 id="sf-symbols">SF Symbols</h2>
<p>Apple이 제공하는 <strong>6,000개 이상의 무료 아이콘 라이브러리</strong>입니다. 별도 설치 없이 <code>Image(systemName:)</code>으로 바로 사용할 수 있습니다.</p>
<pre><code class="language-swift">Image(systemName: &quot;line.3.horizontal&quot;)  // 햄버거 메뉴
Image(systemName: &quot;gearshape&quot;)          // 설정
Image(systemName: &quot;magnifyingglass&quot;)    // 검색</code></pre>
<blockquote>
<p>Mac App Store에서 <strong>SF Symbols</strong> 앱을 설치하면 아이콘을 카테고리별로 검색하고 이름을 바로 복사할 수 있어 개발 속도가 훨씬 빨라집니다.</p>
</blockquote>
<hr>
<h2 id="커스텀-컬러--color-extension">커스텀 컬러 — Color Extension</h2>
<p><code>extension</code>으로 Color를 확장하면 프로젝트 전반에서 <code>.f1Red</code>처럼 기본 색상처럼 사용할 수 있습니다.</p>
<pre><code class="language-swift">extension Color {
    static let f1Red = Color(red: 225/255, green: 6/255, blue: 0/255)
}

// 사용
.foregroundColor(.f1Red)
.foregroundColor(.f1Red.opacity(0.3))  // 투명도 조절</code></pre>
<hr>
<h2 id="배경-있는-버튼--zstack-활용">배경 있는 버튼 — ZStack 활용</h2>
<p><code>ZStack</code>으로 도형과 버튼을 겹쳐서 배경이 있는 버튼을 만들 수 있습니다.</p>
<pre><code class="language-swift">ZStack {
    RoundedRectangle(cornerRadius: 10)
        .frame(width: 50, height: 50)
        .foregroundColor(.f1Red.opacity(0.3))
    Button(action: {}) {
        Image(systemName: &quot;gearshape&quot;)
            .foregroundColor(.red)
            .font(.system(size: 30))
    }
}</code></pre>
<hr>
<h2 id="최종-코드">최종 코드</h2>
<pre><code class="language-swift">// Header.swift
import SwiftUI

extension Color {
    static let f1Red = Color(red: 225/255, green: 6/255, blue: 0/255)
}

struct Header: View {
    var body: some View {
        HStack {
            Button(action: {}) {
                Image(systemName: &quot;line.3.horizontal&quot;)
                    .foregroundColor(.red)
                    .font(.system(size: 30))
            }

            VStack(alignment: .leading) {
                Text(&quot;Monaco Grand Prix&quot;)
                    .font(.title3)
                    .bold()
                    .foregroundColor(.white)
                HStack {
                    Circle()
                        .frame(width: 8, height: 8)
                        .foregroundColor(.red)
                    Text(&quot;Live • Lap 42/78&quot;)
                        .font(.title3)
                        .bold()
                        .foregroundColor(.red)
                }
            }
            .padding()

            Spacer()

            ZStack {
                RoundedRectangle(cornerRadius: 10)
                    .frame(width: 50, height: 50)
                    .foregroundColor(.f1Red.opacity(0.3))
                Button(action: {}) {
                    Image(systemName: &quot;gearshape&quot;)
                        .foregroundColor(.red)
                        .font(.system(size: 30))
                }
            }
        }
        .padding(.horizontal)
    }
}</code></pre>
<hr>
<h2 id="마치며">마치며</h2>
<p>첫 SwiftUI 컴포넌트를 만들면서 느낀 것은, UIKit과 달리 레이아웃을 <strong>선언적으로 구성</strong>한다는 점이 매우 직관적이라는 것입니다. Stack을 조합하고 Spacer로 공간을 배분하는 방식에 익숙해지면 복잡한 레이아웃도 명확하게 구조화할 수 있을 것 같습니다.</p>
<p>다음에는 <code>@State</code>와 <code>@Binding</code>을 활용해 버튼에 실제 동작을 연결하고, 바텀 네비게이션바를 통한 화면 전환을 구현할 예정입니다.</p>
<hr>
<blockquote>
<p>학습 기록은 GitHub <a href="https://github.com/TheSoftBelly/AppleDeveloperAcademy_TIL">AppleDeveloperAcademy_TIL</a>에서 확인할 수 있습니다.</p>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>