<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>ieunsoo.log</title>
        <link>https://velog.io/</link>
        <description>iOS Developer, 천 리 길도 한 걸음부터</description>
        <lastBuildDate>Sun, 04 May 2025 02:10:04 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>ieunsoo.log</title>
            <url>https://velog.velcdn.com/images/fox_hunter/profile/56177a07-87e6-463b-901c-67acdcda6e9a/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. ieunsoo.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/fox_hunter" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[iOS] Render Loop]]></title>
            <link>https://velog.io/@fox_hunter/SwiftUI-Render-Loop</link>
            <guid>https://velog.io/@fox_hunter/SwiftUI-Render-Loop</guid>
            <pubDate>Sun, 04 May 2025 02:10:04 GMT</pubDate>
            <description><![CDATA[<h1 id="들어가며">들어가며</h1>
<p>iOS로 앱을 만들면서 앱의 화면이 어떤 구조로 그려지는지 궁금한적이 있으셨나요?</p>
<p>저는 너무 궁금했습니다. </p>
<p>그래서 여러가지 공부하던 와중에 Render Loop이라는것을 알기 되었고 공부한 내용을 공유하고자 합니다.</p>
<h1 id="refresh-rate--frame-duration">Refresh Rate &amp; Frame Duration</h1>
<p>Render Loop에 대해 이야기 하기전에 알고 가야할 개념이 있습니다.</p>
<p><strong>Refresh Rate</strong>와 <strong>Frame Duration</strong>인데요 </p>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/f7494e96-9dc2-42f1-a072-6f1bb995c4ad/image.png" alt=""></p>
<p>아이폰, 아이패드, 맥북의 화면은 <strong>Refresh Rate(주사율)</strong> 로 구분하여 60Hz, 120Hz 이렇게 크게 두가지로 나뉩니다. </p>
<p>애플은 120Hz Refresh Rate(주사율)을 가진 디스플레이를 ProMotion 디스플레이라고 부릅니다.</p>
<blockquote>
</blockquote>
<p><strong>Refresh Rate(주사율)</strong>는 초당 그리는 화면의 개수를 나타내고
<strong>Frame Duration</strong>는 1개의 화면을 그리는 시간을 나타냅니다.</p>
<p>Refresh Rate와 Frame Duration는 서로 별개의 개념이 아닙니다.
서로 기준이 다를 뿐이죠 </p>
<blockquote>
</blockquote>
<p>60Hz 디스플레이의 경우 16.67ms, 즉 0.001667초에 한번 화면을 그리고
120Hz 디스플레이의 경우 8.33ms, 즉 0.000833초에 화면을 한번 그린다고 생각하면 됩니다.</p>
<p>120Hz 디스플레이의 경우 같은 시간에 그려야 하는 화면의 개수가 60Hz디스플레이에 비해 2배 많죠? 그래서 애플에서는 120Hz 디스플레이를 탑재하는 경우 상대적으로 고 사양인 프로라인 (아이폰 프로, 아이패드 프로, 맥북 프로)에 적용합니다. </p>
<h3 id="가변-주사율">가변 주사율</h3>
<p>물론 모든 화면을 0.001667초 ~ 0.000833초에 한번씩 그리는게 아닙니다. </p>
<p>애플 기기의 디스플레이는 가변 주사율이라고 해서 최대 60 ~ 120Hz 출력하는 것이지 많은 연산이 필요한 작업은 Refresh Rate를 낮춰서 가변적으로 동작합니다. </p>
<p>항상 120Hz로 동작하면 너무 많은 연산이 이루어져서 기기 배터리가 빨리 소모되기에 이런 방식을 사용합니다.</p>
<h1 id="render-loop">Render Loop</h1>
<p>그럼 이제 Render Loop에 대해서 알아볼까요?</p>
<blockquote>
</blockquote>
<p>*<em>Render Loop는 iOS앱의 화면을 그리는 흐름입니다.
*</em></p>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/9f8f9dd1-c059-4aa1-affe-9cddb06d8a87/image.png" alt=""></p>
<p>앞서 이야기 했듯이 애플의 모든 화면이 있는 기기는 초당 최대 60 ~ 120번 화면을 새로고침합니다.</p>
<blockquote>
</blockquote>
<p><strong>1개의 화면을 frame</strong>이라고 하고 *<em>frame을 그리는것을 렌더링 *</em>한다고 합니다.</p>
<p>60Hz 디스플레이의 경우 0.001667초에 한번 frame을 그리고 
120Hz 디스플레이의 경우 0.000833초에 한번 frame을 그린다고 했죠?</p>
<p><strong>iOS에서 이 한개의 frame을 렌더링 할 때 마다 Render Loop가 발생합니다.</strong></p>
<blockquote>
<p>Render Loop는 원칙상 <strong>해당 디스플레이의 Frame Duration안에 실행</strong>되어야 합니다. </p>
</blockquote>
<h2 id="vsync">VSYNC</h2>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/9f8f9dd1-c059-4aa1-affe-9cddb06d8a87/image.png" alt=""></p>
<p>컴퓨터 게임을 좋아하시는 분들이라면 VSYNC를 한번이라도 들어보셨을겁니다.
게임에서 계단현상을 줄여주는 수직동기화 기능을 VSYNC라고 하곤 하죠</p>
<p>*<em>하지만 iOS에서는 다른 의미로 사용됩니다. *</em></p>
<p>새로운 frame의 render loop를 시작하는 이벤트로 이용됩니다. </p>
<h2 id="loop">Loop</h2>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/371e2e30-8d3b-42f1-a77c-87ed86433cb3/image.png" alt=""></p>
<p>Render Loop는 위 이미지와 같은 형태로 진행됩니다. </p>
<p>총 3개의 라인으로 구성된 파이프라인 형태로 병렬구조로 진행됩니다.</p>
<p>3개의 라인을 App, Render Server, On The Display단계로 분리 합니다</p>
<blockquote>
<p><strong>App</strong>, 이벤트 받기, 수정할 데이터 수집/정리,Render 트리거
<strong>Render Server</strong>, Rendering 준비 및 실행
<strong>On The Display</strong>, 화면 출력 별도의 연산 존재하지 않음</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/442eecda-ec8f-4f53-8c2d-5369f4afcb1f/image.png" alt=""></p>
<p>위 이미지 처럼 연산 단계를 세분화할 수 있습니다.</p>
<h4 id="app">App</h4>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/b4f70d9f-9d26-4c16-9ece-ec871c057315/image.png" alt=""></p>
<blockquote>
</blockquote>
<ol>
<li><strong>Event Phase</strong> <ul>
<li>앱이 화면 변경을 요구하는 <u>이벤트를 받고</u>, 업데이트가  필요한 <u>요소를 수집, 정리함</u> (ex. 화면 터치, 키보드 입력, 네트워킹)<blockquote>
</blockquote>
</li>
</ul>
</li>
<li><strong>Commit Phase</strong><ul>
<li>재연산이 필요한 레이어를 Tree형태로 <u>정리</u>하고 <u>Render Server에 전송</u></li>
</ul>
</li>
</ol>
<h4 id="render-server">Render Server</h4>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/86ef9144-a640-4a64-940c-b284cf3bcb73/image.png" alt=""></p>
<blockquote>
</blockquote>
<ol start="3">
<li><strong>Render Prepare</strong><ul>
<li>넘겨받은 <u>Tree를 분석</u>해서 그래픽 연산에 용이하게  선형 파이프라인 형태로 변환<blockquote>
</blockquote>
</li>
</ul>
</li>
<li><strong>Render Execute</strong><ul>
<li>준비를 마친뒤 <u>컴포넌트들을 렌더링</u>, 각각의 레이어는  최종 Texture로 합쳐진다.</li>
</ul>
</li>
</ol>
<h4 id="on-the-display">On The Display</h4>
<blockquote>
</blockquote>
<ol start="5">
<li><strong>Display</strong><ul>
<li>렌더링 끝난 <u>결과를 화면에 보여주는 단계</u> (별도의 연산 없음)</li>
</ul>
</li>
</ol>
<p>이렇게 총 5단계를 거쳐서 1개의 화면이 그려집니다</p>
<h1 id="hitch">Hitch</h1>
<p>앞선 내용들을 읽으면서 어떤 생각이 드셨나요? </p>
<p>저는 공부하면서 이걸 최대 0.000833초 안에 한번해야 한다고? 라는 생각이 들었습니다. </p>
<p>기기가 오래되거나 앱이 연산을 많이 필요로 하는 경우 우리는 흔히 렉걸린다 버벅인다라는 경험을 한번씩 해보셨을겁니다. </p>
<p>이런 현상을 Hitch라고 합니다,</p>
<blockquote>
</blockquote>
<p>Render Loop의 동작이 다음 VSYNC 이벤트 발생 전까지 완료되지 못하면 이런현상이 발생합니다. </p>
<p>앞서 각 단계별로 작업이 분리 되어 있다고 말했었죠?</p>
<p>hitch도 어떤 작업이 완료되지 못했는지를 기준으로 두가지로 분류됩니다.</p>
<p>Commit Hitch, Render Hitch이렇게 두가지로 분류됩니다.</p>
<h2 id="commit-hitch">Commit Hitch</h2>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/fe20f569-2b54-4004-bc21-cfb440fceed0/image.png" alt=""></p>
<p>App 단계의 Commit이 다음 VSYNC이전에 끝나지 않은 경우 발생하는 Hitch를 Commit Hitch라고 합니다. </p>
<h2 id="render-hitch">Render Hitch</h2>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/3fe84461-5f8e-4c2a-b9cc-6a07d3cd2d43/image.png" alt=""></p>
<p>Render Server 단계의 Render Execute에서 렌더링이 다음 VSYNC전에 마무리 되지 못한 경우 발생하는 Hitch를 Render Hitch라고 합니다. </p>
<h1 id="마무리">마무리</h1>
<p>iOS에서는 화면을 최대 60~120번 새로고침 합니다.</p>
<p>1개의 화면, frame을 그릴때 한번의 RenderLoop가 발생합니다. </p>
<p>Render Loop는 파이프라인 형태로 병렬로 동작하고 3가지 단계로 분류됩니다.</p>
<p>각 연산 단계에서 작업이 다음 VSYNC이전에 끝나지 않는경우 Hitch가 발생합니다.
(이를 사용자는 렉걸린다, 버벅인다라고 느낍니다.)</p>
<h1 id="출처">출처</h1>
<p><a href="https://developer.apple.com/videos/play/tech-talks/10855/">https://developer.apple.com/videos/play/tech-talks/10855/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SwiftUI] Animation 동작과정에 대해서 ]]></title>
            <link>https://velog.io/@fox_hunter/SwiftUI-Animation2</link>
            <guid>https://velog.io/@fox_hunter/SwiftUI-Animation2</guid>
            <pubDate>Sun, 04 May 2025 00:21:15 GMT</pubDate>
            <description><![CDATA[<h1 id="들어가며">들어가며</h1>
<p>먼저 제 애니메이션 게시글을 보지 않으셨다거나 SwiftUI의 애니메이션에 대해 생소하다면 <strong><a href="https://velog.io/@fox_hunter/SwiftUI-Animation">이전 글</a></strong>을 보는것을 추천드려요!</p>
<p>이전 글에서 애니메이션에 대해서 이야기 했었죠 </p>
<p>이번 시간에는 SwiftUI에서 애니메이션이 어떤 구조로 적용되는지만 간단하게 알아 볼게요!</p>
<h1 id="보간법">보간법?</h1>
<blockquote>
<p>컴포넌트의 위치나 크기의 차이값을  <strong>보간법</strong>으로 계산해서 변화를 시각적으로 표현한다.</p>
</blockquote>
<p>애플에서 제공하는 WWDC영상에서 나온 내용입니다.</p>
<p>SwiftUI는 보간법을 이용해서 애니메이션을 적용한다고 합니다.</p>
<p>*<em>그렇다면 보간법이란 무엇일까요? *</em></p>
<p>일반적으로 보간법은 수학에서 주로 사용하는 용어입니다.</p>
<p>우리가 중고등학교 수학시간에 그래프를 그릴때 점 두개를 주고 공식에 일치하는 선을 그리는 일을 해본적이 다들 한번쯤은 있으시겠죠?</p>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/f345c6fd-656e-47dd-a086-c1d5f58aa922/image.png" alt=""></p>
<p>이처럼 보간법은 두 점을 연결하는 방법을 의미합니다다.  
여기서 말하는 연결은 궤적(Trajectory)를 생성한다는 의미를 가집니다.</p>
<p>여기서 선을 연결하는 방법은 개발자가 직접 지정할 수 있고 SwiftUI가 자동으로 그리는것을 이용하는 방법도 있죠 </p>
<p>이전 시간에 작성한 방법이 SwiftUI가 자동으로 제공하는 방식을 사용한 것입니다.</p>
<blockquote>
<p>SwiftUI는 굉장히 똑똑한 친구라서 뷰의 상태가 변경되면 그 차이값을 자동으로 계산해서 부드럽게 시각적으로 표현해줍니다.</p>
</blockquote>
<h1 id="animatable">Animatable</h1>
<p>그렇다면 SwiftUI는 해당 뷰가 애니메이션을 적용해야하는 존재인지 어떻게 구별할 수 있을까요?</p>
<p>애플에서 말하길</p>
<blockquote>
<p><strong>View를 렌더링 하다가 Animatable한 Effect를  렌더링하는 순간이 오면 애니메이션을 적용한다</strong></p>
</blockquote>
<p>라고 합니다.</p>
<p>그렇다면 SwiftUI는 Animatable한 존재를 어떻게 구별할까요? </p>
<p>우리는 이미  Animatable한 존재를 지정하는 방법을 알고 있습니다.</p>
<p>바로 애니메이션 지정하는 과정이 바로 그것입니다.</p>
<p>이전에 애니메이션을 적용하기위해서 .animation모디파이어를 사용하거나 withAnimation클로저를 사용한다고 했었죠? </p>
<p>바로 이 과정이 애니메이션을 발생시켜라고 하는 일종의 <strong>Flag</strong>나 <strong>Trigger</strong> 역할을 한다고 보시면 됩니다.</p>
<h2 id="예시">예시</h2>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/370dd113-64d4-44a2-ac6a-c59639e8429a/image.png" alt="">
예를 들어 다음 과 같은 원의 위치를 변경하는 뷰가 있다고 합시다. </p>
<p>이 뷰의 렌더링 과정은 아래 이미지와 같습니다.</p>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/2da06e5e-78fd-4994-84b2-5a91578597d5/image.png" alt=""></p>
<ol>
<li>모디파이어도 적용해서 Circle을 그립니다.</li>
<li>Button을 그립니다. </li>
<li>버튼이 실행되고 상태변수의 값이 변경됩니다.</li>
<li>상태변수의 값이 변경되면 SwiftUI는 자동으로 해당부분의 화면을 다시 렌더링합니다.</li>
<li>상태변수와 연관된 컴포넌트인 원을 다시 그립니다.</li>
<li>이때 원에는 .animation모디파이어가 적용되어있으니 이때 원에 애니메이션을 적용해 줍니다.</li>
</ol>
<h1 id="마무리">마무리</h1>
<p>SwiftUI에너 화면을 그리는 방법을 알아보았습니다.</p>
<p>내부적으로 보간법이라는것을 이용해서 애니메이션 화면을 적용합니다.</p>
<p>이전에 이야기 했던 .animation모디파이어나 withAnimation클로저가 일종의 애니메이션 동작 플래그 역할을 한다는것을 알고 갑시다!</p>
<h1 id="출처">출처</h1>
<p><a href="https://www.youtube.com/watch?v=IuSuHJs5-KE&amp;t=22s">https://www.youtube.com/watch?v=IuSuHJs5-KE&amp;t=22s</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SwiftUI] Animation]]></title>
            <link>https://velog.io/@fox_hunter/SwiftUI-Animation</link>
            <guid>https://velog.io/@fox_hunter/SwiftUI-Animation</guid>
            <pubDate>Sat, 03 May 2025 08:00:12 GMT</pubDate>
            <description><![CDATA[<h1 id="애니메이션">애니메이션?</h1>
<p>애니메이션이란 뭘까요? </p>
<p><img src="https://www.ujeil.com/news/photo/202103/272821_111750_2151.jpg" alt=""></p>
<p>사실 애니메이션이라고 하면 위 이미지 처럼 만화같은것을 상상하겠지만 SwiftUI에서 애니메이션은 우리가 자주사용하는 파워포인트나 키노트의 전환 애니메이션에 가깝습니다.
<br>
<img src="https://i.namu.wiki/i/gEExxz7l0YL3c5x3shDmvWGpnNetqLhvALo0Jrb4aGClzVZA2rDCYWTu8kPPQx7KvxqVEngc59wUEsC3F2_HbA.webp" alt=""></p>
<blockquote>
<p>애니메이션은 발표용 프로그램에서 사용하는 화면 전환 효과에 가깝습니다.</p>
</blockquote>
<p>실제로 SwiftUI에서 사용하는 애니메이션 효과에도 컴포넌트의 크기변화, 위치변화, 색변화 등등 간단한 효과만 지정할 수 있습니다.</p>
<p>물론 우리가 아는 애니메이션같은 형태도 만들 수 있겠지만 그러면 엄청난 노력과 시간이 들어가겠죠?</p>
<p>그런 어려운 효과는 보통 gif로 넣거나 lottie같은 외부 라이브러리를 사용하는게 일반적입니다.</p>
<h1 id="주관적-정의">주관적 정의</h1>
<p>제 나름대로 SwiftUI에서의 애니메이션을 한마디로 정의하자면 다음과 같이 말할 수 있을거 같습니다.</p>
<blockquote>
<p><strong>상태 변화에 대해 UI를 업데이트(렌더링) 할때 그 변화를 부드럽게 시각화 한 것을 Animation 이라고 한다.</strong></p>
</blockquote>
<p>실제로 SwiftUI에서 애니메이션을 적용하기는 매우 쉽습니다. 컴포넌트에 모디파이어로 애니메이션을 사용하겠다고 표시해주기만 하면 됩니다.</p>
<p>그러면 SwiftUI에서는 자동으로 상태 변화에 대한 차이를 계산해서 부드러운 전환효과를 적용해 줍니다.</p>
<h1 id="애니메이션의-사용법">애니메이션의 사용법</h1>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/9d059af3-b983-4a61-b0c9-6ac6523980aa/image.png" alt=""></p>
<p>애니메이션을 적용한 간단한 코드입니다.</p>
<p>애니메이션을 적용하고자 하는 컴포넌트 코드 맨아래에 .animation() 모디파이어를 적용해주기만 하면 됩니다.</p>
<blockquote>
<p>위 코드에서 애니메이션이 적용되는 컴포넌트는 Image만 적용되고 Button은 적용되지 않는 형태의 View가 될것입니다.</p>
</blockquote>
<h2 id="암묵적-애니메이션과-명시적-애니메이션">암묵적 애니메이션과 명시적 애니메이션</h2>
<p>SwiftUI에서 애니메이션을 사용하는 방법은 크게 두가지 입니다.</p>
<blockquote>
<p><strong>암묵적인 방법</strong>과 <strong>명시적인 방법</strong>으로 나뉩니다.</p>
</blockquote>
<h3 id="암묵적인-애니메이션">암묵적인 애니메이션</h3>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/9d059af3-b983-4a61-b0c9-6ac6523980aa/image.png" alt="">
암묵적인 방법은 위 코드에서 처럼 컴포넌트아래에 .animation모디파이어를 사용하는 것 입니다.</p>
<blockquote>
<p>( ) 괄호안에는 <strong>애니메이션 옵션</strong>을 적용할 수 있습니다.</p>
</blockquote>
<p><strong>타이밍커브, 실행시간, 반복횟수</strong>를 애니메이션 옵션 이라고 하는데 아래에서 따로 다루겠습니다, 어떻게 사용하는지만 보고 넘어가세요 ㅎㅎ</p>
<h3 id="명시적인-애니메이션">명시적인 애니메이션</h3>
<p>앞서 암묵적인 애니메이션코드를 살펴보고 한가지 문제점을 느끼셨나요?</p>
<p>바로 암목적인 방식을 사용하면 해당 컴포넌트에 상태가 여러개 변하는 상황에서 각각상태 변화에 대해서 애니메이션이 적용되지 않고 모두 동일한 애니메이션 효과가 적용됩니다.</p>
<p>특정 변화에 대해서는 애니메이션을 적용하고 싶지 않을 수도 있는데 암묵적으로 애니메이션을 사용하면 모든변화에 대해서 효과가 적용되겠죠?</p>
<h4 id="예시">예시</h4>
<pre><code class="language-swift">import SwiftUI
import Foundation

struct AnimationEx01: View {
    @State var circlePos: CGPoint = CGPoint(x: UIScreen.main.bounds.width / 2, y: UIScreen.main.bounds.height / 2)
    @State var isSmall: Bool = false

    var body: some View {
        Circle()
            .frame(width: 100, height: 100)
            .position(circlePos)
            .scaleEffect(isSmall ? 1 : 2)
            .animation(Animation.linear(duration: 0.5))

        Button(&quot;move circle&quot;){
            isSmall.toggle()
            circlePos = CGPoint(x: Int.random(in: 100...300),y: Int.random(in: 100...200))
        }
    }
}

</code></pre>
<p>예를들어 다음과 같이 원의 크기와 위치가 변화하는 코드가 있을때 위치변화하는 속도와 크기변화하는 속도를 따로 적용하고 싶다면 암묵적인 애니메이션은 도움이 되지 못할것입니다.</p>
<p>그럴 때를 위해서 명시적 애니메이션이 존재합니다.</p>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/2cb3d4be-769a-42b3-b8a0-8703fef14fc8/image.png" alt=""></p>
<p>다음과 같이 상태변화는 적용하는 코드에 작성하면 됩니다.</p>
<p>Button처럼 동작 코드를 클로저로 받는경우에 <strong><u>적용하고싶은 코드에  withAnimation클로저로 한번 더 묶어주기만 하면됩니다. </u></strong></p>
<p>위 코드처럼 작성하면 <strong>위치변화에는 애니메이션이 적용</strong>되지만 색변화에 대해서는 효과가 적용되지 않겠죠?</p>
<p>애니메이션 옵션은 암묵적인 방법과 동일하게 적용하면 됩니다.</p>
<h3 id="암묵적-애니메이션과-명시적-애니메이션의-비교">암묵적 애니메이션과 명시적 애니메이션의 비교</h3>
<table>
<thead>
<tr>
<th align="center"><center>종류</center></th>
<th align="center"><center>암묵적 애니메이션</center></th>
<th align="center"><center>명시적 애니메이션</center></th>
</tr>
</thead>
<tbody><tr>
<td align="center">사용성</td>
<td align="center">간단함</td>
<td align="center">비교적 복잡함</td>
</tr>
<tr>
<td align="center">형태</td>
<td align="center">모디파이어</td>
<td align="center">클로저</td>
</tr>
</tbody></table>
<h1 id="애니메이션-오버라이딩">애니메이션 오버라이딩</h1>
<p>앞서 명시적 애니메이션을 사용하는 이유가 애니메이션을 상태별로 다르게 적용하고 싶을때 사용한다고 했었죠?</p>
<p>그렇다면 예를들어 <strong>프로그래머가 실수로 명시적애니메이션을 적용했는데 모디파이어를 이용해서 암묵적으로도 다시한번 애니메이션을 적용하면 어떻게 될까요??</strong></p>
<blockquote>
<p>정답은 <strong>암묵적 애니메이션이 선택</strong>됩니다.</p>
</blockquote>
<p>코드로 한번 살펴볼까요?</p>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/54c123c8-fc0c-4071-ac81-a16a8d0f648f/image.png" alt=""></p>
<p>heart.fill이미지의 위치를 변화시키는 코드입니다. 여기서 다른건 애니메이션 전환속도입니다. </p>
<p>명시적애니메이션은 1, 암묵적 애니메이션은 0.3으로 설정되어있는데
<strong>결과는 0.3의 속도로 애니메이션이 적용</strong>될 것입니다.</p>
<p>실수로 이렇게 사용할 수도 있겠지만 만약 다른 컴포넌트의 duration은 1로 설정하고 하나만 빠르게 하고 싶다면 이렇게 사용하는것도 할 수 있겠죠</p>
<p>하지만 <strong>이런식으로 애니메이션을 오버라이딩 하면 코드의 가독성이 너무 떨어져</strong> 코드를 관리하기 어려워 질겁니다.</p>
<p>그럴때를 위해서 중첩 애니메이션이 존재합니다.</p>
<h2 id="중첩-애니메이션">중첩 애니메이션</h2>
<p>중첩애니메이션은 각 상태변화별로 애니메이션을 적용할때 권장되는 형태입니다.</p>
<p>위에서 처럼 오버라이딩을 하면 상황에 따라 다른 애니메이션이 적용되는 <strong>돌발성 애니메이션</strong>이 발생할 수 있습니다.</p>
<p>간단하게만 알아봅시다 형태는 아래와 같이 두가지 형태로 나뉠 수 있습니다.</p>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/fox_hunter/post/de7941f9-ca8b-4971-bcfb-a07046a606c7/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/fox_hunter/post/3b2e3951-db56-4663-a293-1e0ddf663638/image.png" alt=""></th>
</tr>
</thead>
</table>
<p>(자세한 코드는 아래에서 다시 한번 보여드릴게요 😎)</p>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/637135c1-a0e2-4e3e-ba12-e3b07dfa622e/image.png" alt="">
위 코드처럼 모디파이어의 위치를 이용해서 적용할 효과 바로 아래에 .animation모디파이어를 작성할 수 있고, </p>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/2892af94-b24e-4e58-9c22-73394ec9c204/image.png" alt="">
또는 .animation모디파이어에 후행클로저를 이용해서 적용할 효과만 작성할 수 있습니다.</p>
<p>(애플에서 권장하는 형태는 이 형태이니 참고하세요 ㅎㅎ)</p>
<h1 id="애니메이션-옵션">애니메이션 옵션</h1>
<p>앞서 SwiftUI에서 애니메이션을 적용할때 타이밍커브, 실행시간, 반복횟수를 지정할 수 있다고 했었죠?</p>
<p>이 3가지 옵션이 무엇을 결정하는지 알아봅시다</p>
<table>
<thead>
<tr>
<th align="center"><center>타이밍 커브<br>(Timing Curve)</center></th>
<th align="center"><center>반복<br>(Repeat)</center></th>
<th align="center"><center>지연<br>(Duration)</center></th>
</tr>
</thead>
<tbody><tr>
<td align="center">애니메이션 속도변화</td>
<td align="center">애니메이션 반복횟수</td>
<td align="center">애니메이션 실행시간</td>
</tr>
</tbody></table>
<p>반복이랑 지연은 어떤 옵션인지 아시겠죠? </p>
<blockquote>
</blockquote>
<p><strong>반복</strong>에는 몇번 반복할것인지를 지정할 수 있고
<strong>지연</strong>에는 애니메이션이 동작하는 시간을 지정할 수 있습니다
<strong>타이밍커브</strong>에는 애니메이션 속도변화를 조절할 수 있습니다.</p>
<p>지연은 적은 값이 들어갈수록 빠르게 보이게 되겠죠? </p>
<p>여기서 <strong>타이밍 커브</strong>는 조금 생소할 수 있을거 같아 더 설명할게요</p>
<h2 id="timing-curve">Timing Curve</h2>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/91a2b0d4-023b-4340-bd18-5c20c625d058/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/43d6eef5-b50f-46e5-bae6-18d269ec0cfa/image.png" alt=""></p>
<p>타이밍커브는 애니메이션속도 변화를 조절할 수 있습니다.</p>
<blockquote>
</blockquote>
<p><strong>EaseOut</strong> 는 일정하게 진행되다 끝에서 느려지는 효과
<strong>EaseIn</strong> 은 천천히 시작해서 일정하게 진행되는 효과
<strong>EaseInOut</strong> 는 처음과 끝부분이 느리게 진행되는 효과
<strong>Linear</strong> 는 애니메이션 도중에 속도 변화가 없는 속성입니다.</p>
<p>Ease효과를 이용하면 보다 안정적인 느낌의 애니메이션을 제공할 수 있습니다. 
(코드로 적용하면서 어떤느낌인지 살펴보세요!)</p>
<h1 id="transition">Transition</h1>
<p>애니메이션 효과를 트랜지션이라고 합니다.</p>
<p>트랜지션은 크게 3가지로 분리 할 수 있습니다.</p>
<blockquote>
</blockquote>
<ol>
<li>형태/크기 변화</li>
<li>위치 변화</li>
<li>색/배경 변화 </li>
</ol>
<p>이렇게 3가지 효과를 적용할 수 있고 종류는 아래와 같습니다.</p>
<blockquote>
</blockquote>
<ul>
<li>rotationEffect: 뷰를 2D 평면에서 회전시킵니다. </li>
<li>scaleEffect: 뷰의 크기를 확대하거나 축소합니다. </li>
<li>rotation3DEffect: 뷰를 3D 공간에서 회전시킵니다.</li>
<li>offset: 뷰를 특정 x, y 축 방향으로 이동시킵니다. </li>
<li>transformEffect: CGAffineTransform을 사용하여 뷰에 복합적인 변형을 적용합니다.<blockquote>
</blockquote>
</li>
<li>opacity: 뷰의 투명도를 조정합니다. </li>
<li>foregroundColor: 텍스트나 아이콘의 색상을 변경합니다. </li>
<li>background: 뷰의 배경색을 변경합니다.</li>
</ul>
<blockquote>
</blockquote>
<ul>
<li>cornerRadius: 둥근 모서리를 애니메이션으로 조정할 수 있습니다. </li>
<li>clipShape: 뷰를 특정 모양으로 자릅니다. </li>
<li>border: 테두리 두께와 색상을 변경합니다.</li>
</ul>
<blockquote>
</blockquote>
<ul>
<li>frame: 뷰의 크기와 위치를 변경합니다. </li>
<li>padding: 내부 여백을 조정합니다.</li>
</ul>
<blockquote>
</blockquote>
<ul>
<li>shadow: 그림자의 크기와 투명도를 조정합니다. </li>
<li>blur: 블러 효과를 추가합니다. </li>
<li>hueRotation: 색조를 회전시킵니다. </li>
<li>brightness: 밝기를 조정합니다. </li>
<li>contrast: 대비를 조정합니다. </li>
<li>saturation: 채도를 조정합니다.</li>
</ul>
<p>모든 모디파이어를 소개할수는 없으니 종류만 살펴보고 애플 공식문서나 gpt를 활용하세요!</p>
<h1 id="마무리">마무리</h1>
<p>SwiftUI에서는 뷰의 전환효과를 줄 수 있는데 이를 <strong>Animation</strong>이라고 한다.</p>
<p>애니메이션을 적용하는 방법은 <strong>명시적인 방법과 암묵적인 방법</strong> 두가지로 나눌 수 있다.</p>
<p>명시적인 방법과 암묵적인 방법을 동시에 사용할 수 있고 상태별로 다른 애니메이션을 적용할 수 있는데 이를 <strong>애니메이션 오버라이딩</strong>이라고 한다. </p>
<p>애니메이션 효과를 <strong>transition</strong>이라고 하고 형태/크기 변화, 위치 변화, 색/배경 변화에 대해서 다양한 효과를 적용할 수 있다.</p>
<h1 id="출처">출처</h1>
<p><a href="https://www.youtube.com/watch?v=IuSuHJs5-KE&amp;t=22s">https://www.youtube.com/watch?v=IuSuHJs5-KE&amp;t=22s</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자료구조] Tree 트리]]></title>
            <link>https://velog.io/@fox_hunter/DataStructure-Tree</link>
            <guid>https://velog.io/@fox_hunter/DataStructure-Tree</guid>
            <pubDate>Mon, 17 Mar 2025 09:43:19 GMT</pubDate>
            <description><![CDATA[<h1 id="개요">개요</h1>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/582fdd21-1dfd-4946-aed9-460c603a2bcd/image.jpg" alt=""></p>
<p>tree자료구조는 그 이름처럼 트리의 형태를 하는 자료구조이다.</p>
<h1 id="구조">구조</h1>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/b2ea4827-a27d-42e6-9fe2-76b7c9b4f4cf/image.jpeg" alt=""></p>
<p>트리의 형태는 위 이미지와 같은데 각 동그라미 부분을 <strong>노드</strong>라고 하고 여기에 데이터가 저장된다. </p>
<p>이때 데이터 식별을 위해서 key와 데이터 저장소인 value를 설정할 수 있는데 이를 <strong>key-value구조</strong>라고 한다.</p>
<p>그리고 Tree를 탐색하는 과정을 <strong>순회한다.</strong> 라고 한다.</p>
<blockquote>
</blockquote>
<p>맨위 노드를 <strong>Root node</strong>, 맨아래에 있는 노드를 <strong>leaf node</strong>
하위 노드가 연결된 노드를 <strong>자식 node</strong>, 연결된 상위 node를 <strong>부모 node</strong>라고 한다.</p>
<h1 id="여러가지-tree">여러가지 Tree</h1>
<h2 id="binary-tree">Binary Tree</h2>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/964a2c72-99cc-4488-ad01-c7d20a316110/image.jpeg" alt="">
한글로는 이진 트리라고 한다.</p>
<p>tree의 가장 기본적인 형태로 각 부모노드가 항상 2개 이하의 자식노드를 가져야 하는 트리형태이다.</p>
<hr>

<h2 id="binary-search-tree">Binary Search Tree</h2>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/105bf3e0-a2a3-41ca-a316-9f9aadf17779/image.jpeg" alt="">
이진트리의 가장 일반적인 형태로 노드의 키를 기준으로 이미 정렬된 상태의 이진트리이다.</p>
<p>모든 노드의 키는 왼쪽 서브트리보다 크고 오른쪽 서브트리보다 작은 형태를 띈다.</p>
<p>가장 작은 키는 제일 왼쪽 부분 리프노드가 제일작고, 
가장 큰 키는 제일 오른쪽 리프노드가 제일 큰 값을 갖는다.</p>
<hr>

<h2 id="avl-tree">AVL Tree</h2>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/44c8d7a4-0ab7-45b9-a505-3413bfe5ef8b/image.jpeg" alt="">
AVL트리는 위 이미지의 왼쪽트리처럼 불완전 트리 형태가 되면 균형조정을 통해 완전트리의 형태로 변환되고 정렬된다. 이를 트리회전이라고 한다.</p>
<p>시간 복잡도는 O(log n)이다.</p>
<p>DB데이터 검색에서 일부 사용되곤 하는 형태이다.</p>
<hr>

<h2 id="rb-tree">RB Tree</h2>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/b2087e67-2d6b-456f-8d74-d3fdb985364b/image.jpeg" alt=""></p>
<p>RB트리는 AVL트리와 동일하게 불균형을 감지하면 균형을 조정한다는 점에서 ㅂ슷하다.</p>
<p>하지만 RB트리가 트리 회전의 좀더 효율적이다.</p>
<p>RB트리는 노드마다 빨강또는 검정의 비트를 포함한다.</p>
<p>시간 복잡도는 AVL트리와 동일하게 O(log n)이다.</p>
<hr>

<h2 id="b-tree">B Tree</h2>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/82a3f429-91ff-4a7a-85bc-caeb3da56a6e/image.jpeg" alt="">
B트리는 이진트리와는 다르게 자식노드를 3개 이상 가지는것이 가능한 트리이다.</p>
<p>B트리 또한 AVL, RB트리와 동일하게 자체적인 균형조정 기능을 갖춘 트리이다.</p>
<p>주로 파일 시스템에서 이러한 구조를 채택해서 사용한다.</p>
<hr>

<h2 id="heap">Heap</h2>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/7e91c038-a8c7-4830-87f7-cad0b9e593a9/image.jpeg" alt="">
이진트리의 한 종류이며 최대 값과 최소 값을 빠르게 접근해야 하는 경우 주로 사용된다.</p>
<p>힙에는 두가지 형태가 있다.</p>
<blockquote>
</blockquote>
<p><strong>최대 힙:</strong> 루트 노드가 힙에서 제일 큰 값, 노드 각각의 값이 부모 노드보다 작거나 같도록 구성 됨
<strong>최소 힙:</strong> 루트 노드가 힙에서 제일 작은 값, 노드 각각의 값이 부모 노드보다 크거나 같도록 구성 됨</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자료구조] Linked List 링크드 리스트]]></title>
            <link>https://velog.io/@fox_hunter/DataStructure-LinkedList</link>
            <guid>https://velog.io/@fox_hunter/DataStructure-LinkedList</guid>
            <pubDate>Thu, 13 Mar 2025 06:15:32 GMT</pubDate>
            <description><![CDATA[<h1 id="개요">개요</h1>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/3e85ad26-9107-40e2-b53a-65e54fd0c4e1/image.png" alt=""></p>
<p>링크드 리스트는 기본적으로 노드로 구성되어있고 그 노드가 연결될 형태로 구성되어있다. 
일종의 체인같은 구조로 구성되어있다고 보면 된다.</p>
<p>가장 Singly Linked List를 기준으로 노드에는 data부분과 next부분이 있다</p>
<p>이번에는 Singly Linked List를 기준으로 설명할 것이다.</p>
<h1 id="설명">설명</h1>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/2086ae78-2dd4-4458-a6f4-d8a41c059c49/image.jpeg" alt=""></p>
<p>위 이미지는 Singly Linked List로 LinkedList라고 하면 가장 쉽게 떠오르는 형태의 이미지이다. </p>
<p>각 노드에는 <strong>data부분과 next부분</strong>이 있다</p>
<p>그리고 <u>리스트의 진입지점</u>을 가리키는 <strong>head</strong>가 존재한다.</p>
<p>Data는 일반적으로 저장하고 싶은 데이터가 들어가고 Next에는 다음 노드를 가리키는 포인터가 들어간다.</p>
<p>Singly Linked List를 이용하면 마지막 노드의 next는 null이 된다.</p>
<hr>

<p>Linked List에는 여러가지 형태가 있는데 종류는 다음과 같다.</p>
<blockquote>
</blockquote>
<p><strong>Circuler Linked List</strong>: 끝 노드의 next부분이 맨앞 노드를 가리키게 하는 형태</p>
<ul>
<li>이 경우 첫노드의 구별은 head라는 노드 관리 포인터를 따로 만들어서 관리한다.</li>
</ul>
<blockquote>
</blockquote>
<p><strong>Doubly Linked List</strong>: 각 노드의 previus와 next를 동시에 가지는 형태 </p>
<ul>
<li>여기서 Circuler형식이 합쳐진 Doubly Circuler Linked List도 있다.</li>
</ul>
<h2 id="구성요소">구성요소</h2>
<blockquote>
</blockquote>
<p><strong>Data</strong> : 실제 저장할 값
<strong>Next</strong> : 다음 노드에 대한 포인터
<strong>isEmpty</strong> : LinkedList가 비어있는지에 대한 정보를 확인하는 메소드</p>
<h2 id="장단점">장단점</h2>
<p>✅ 장점:</p>
<ul>
<li>중간에 노드를 삽입하거나 삭제할 때 속도가 빠름(O(1)).</li>
<li>크기가 동적으로 변할 수 있으며, 미리 크기를 정할 필요 없음.</li>
<li>메모리가 분산되어 사용 가능.</li>
</ul>
<p>❌ 단점:</p>
<ul>
<li>특정 인덱스의 데이터에 접근하기 위해 순차적으로 접근해야 하므로 접근 속도가 느림(O(n)).</li>
<li>포인터를 저장하기 위한 추가 메모리 공간이 필요함.</li>
</ul>
<h2 id="in-swift">in Swift</h2>
<pre><code class="language-swift">import Foundation

class Node&lt;T: Equatable&gt;{
    let value: T?
    var next: Node?

    init(_ value: T) {
        self.value = value
    }
    init(){
        self.value = nil
        self.next = nil
    }
}


class SingleLinkedList&lt;T: Equatable&gt; {
    private var head: Node&lt;T&gt;?

    func append(_ value: T) {
        let node = Node(value)
        if let head = head {
            head.next = node
        } else {
            self.head = node
        }
    }
    func insert(value: T, at: Int){
        var tmpNode = Node&lt;T&gt;(value)

        if let head = head {
            if at == 0 {
                tmpNode.next = head
                self.head = tmpNode
            }else{

            }
        }else{
            self.head = tmpNode
        }

    }
    func removeFirst(){
        print(&quot;\(head?.value) removed&quot;)
        head = head?.next
    }
    func removeLast(){
        var cursor: Node&lt;T&gt;? = head

        while cursor?.next != nil {
            if cursor?.next?.next == nil {
                print(&quot;\(cursor?.next?.value) removed&quot;)
                cursor?.next = nil
                break
            }
            cursor = cursor?.next
        }
    }
    func remove(at: Int){}

    //void타입으로 하고 결과를 print하는걸로
    func find(value: T){
        var cursor: Node&lt;T&gt;? = head

        while cursor?.next != nil {
            if cursor?.next?.value == value{
                if let next = cursor?.next { print(next.value) }
            }
            cursor = cursor?.next
        }

        print(&quot;data not found&quot;)

    }

    func get(at: Int) -&gt; T?{
        var cursor: Node&lt;T&gt;? = head
        var cnt: Int = 0


        if at &gt;= size() {
            return nil
        }
        else{
            while cnt &gt; at {
                if cnt == at {
                    return cursor!.value
                }
                cnt += 1
                cursor = cursor?.next
            }

        }
        return nil
    }
    func isEmpty() -&gt; Bool{
        if let head = head {
            return false
        }else{
            return true
        }
    }
    func size() -&gt; Int{
        var cursor: Node&lt;T&gt;? = head
        var count: Int = 0
        while cursor?.next != nil {
            count += 1
            cursor = cursor?.next
        }
        return count
    }
    func printList(){
        var cursor: Node&lt;T&gt;? = head

        while cursor?.next != nil {
            print(cursor?.value)
            cursor = cursor?.next
        }


    }

}
</code></pre>
<h1 id="정리">정리</h1>
<blockquote>
<p>Singly Linked List를 기준으로 노드에는 <strong>data</strong>부분과 <strong>next</strong>부분이 있다
노드의 마지막과 처음이 연결된 <strong>Ciruler Linked List</strong>가 있고, 
next와 previus값을 가지는  <strong>Doubly Linked List</strong>, 
이와 동시에 Ciruler 형태를 띄는 <strong>Ciruler Doubly Linked List</strong> 도 있다.</p>
</blockquote>
<p> +&nbsp; 그리고 대부분의 데이터구조를 linked list를 이용해서 구현하는것이 가능하다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자료구조] Queue 큐]]></title>
            <link>https://velog.io/@fox_hunter/DataStructure-Queue</link>
            <guid>https://velog.io/@fox_hunter/DataStructure-Queue</guid>
            <pubDate>Wed, 12 Mar 2025 11:13:37 GMT</pubDate>
            <description><![CDATA[<h1 id="개요">개요</h1>
<blockquote>
<p><strong>First In First Out (FIFO)</strong>
<em>먼저 입력된 데이터가 먼저 나간다.</em></p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/50c33010-b722-4372-bd5d-a6b6f348e742/image.jpeg" alt=""></p>
<p>큐는 이러한 형태로 가장 먼저 입력된 값이 가장먼저 출력된다.</p>
<p>어렵게 생각할것 없이 우리가 식당이나 버스를 기다릴때 서는 줄과 같은 구조라고 생각하면 된다.</p>
<h1 id="큐의-동작">큐의 동작</h1>
<blockquote>
</blockquote>
<ul>
<li>Queue에 데이터를 입출력하는 경우 enQueue/deQueue라는 키워드를 사용한다.</li>
<li>enQueue: 데이터 맨뒤에 데이터를 추가 </li>
<li>deQueue: 맨 앞에 데이터를 반환 후 제거</li>
<li>isEmpty: 큐가 비어있는지 확인한다.</li>
<li>size: 전체 사이즈를 반환한다.</li>
<li>peek: queue 맨앞의 데이터를 조회한다.</li>
</ul>
<br>


<h2 id="in-swift">in Swift</h2>
<pre><code class="language-swift">import Foundation

//가장 간단한 queue
struct Queue&lt;T&gt;{
    var queue:[T] = []

    var isEmpty: Bool{
        return queue.isEmpty
    }
    var size: Int{
        return queue.count
    }

    public mutating func enQueue(_ data: T){
        queue.append(data)
    }

    public mutating func deQueue() -&gt; T?{
        var tmp:T? = queue.first
        queue.removeFirst()

        return queue.isEmpty ? nil : tmp
    }

    public func peek() -&gt; T?{
        return queue.first
    }
}


//head를 이용gkdu 큐를 만들고 메모리 오버헤드 줄이기
struct Queue2&lt;T&gt;{
    var queue:[T?] = []
    var head:Int = 0

    var isEmpty: Bool{
        return queue.isEmpty
    }
    var count: Int{
        return queue.count
    }

    public mutating func enQueue(_ data: T){
        queue.append(data)
    }

    public mutating func deQueue() -&gt; T?{
        guard head &lt; queue.count, let element = queue[head] else { return nil }
        queue[head] = nil
        head += 1

        if head &gt; 50 {
            queue.removeFirst(head)
            head = 0
        }
        return element
    }

}</code></pre>
<h1 id="활용">활용</h1>
<p>Queue는 주로 프로그램 내부의 스케쥴링등 순서가 필요한 작업에 주로 사용된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자료구조] Stack 스택]]></title>
            <link>https://velog.io/@fox_hunter/DataStructure-Stack</link>
            <guid>https://velog.io/@fox_hunter/DataStructure-Stack</guid>
            <pubDate>Wed, 12 Mar 2025 08:19:03 GMT</pubDate>
            <description><![CDATA[<h1 id="개요">개요</h1>
<blockquote>
<p><strong>Last in First out (LIFO)</strong></p>
</blockquote>
<p>후입선출 스택하면 가장 먼저 떠올라야 하는 개념이다 Stack은 바구니에 차곡차곡 쌓아서 꺼내쓰는 데이터 형태를 생각하면 된다.</p>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/f7c84060-e440-46f8-9266-ccbf8537c4f4/image.webp" alt="">
이러한 Stacking Tower를 어릴적에 갖고 놀았던 기억이 있는 사람이 있을 수 있다.</p>
<p>Stack은 이러한 형태를 띄는 데이터 구조이다.</p>
<h1 id="동작">동작</h1>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/a5881e46-32f0-4525-91ab-e57cf0251523/image.jpeg" alt=""></p>
<blockquote>
<p>Stack의 데이터를 입력하는것을 PUSH, 데이터를 가져오는것을 POP이라고 한다.</p>
</blockquote>
<p>Stack은 배열같은 다른 데이터들과 다르게 중간의 데이터만 쏙 가져오는것이 불가능하다.</p>
<p>무조건 입력의 역순으로 한개씩 데이터를 가지고 나와야만 한다.</p>
<p>예를들어 12345순으로 5개의 데이터가 들어갔다면 데이터의 출력은 54321 순서로 가능하다.</p>
<h3 id="stack의-동작">Stack의 동작</h3>
<blockquote>
</blockquote>
<ul>
<li>push: 스택 맨뒤에 데이터를 추가한다.</li>
<li>pop: 스택 맨 뒤 항목을 반환하는 동시에 제거한다.</li>
<li>top/peek: 맨 뒤의 데이터를 조회한다.</li>
<li>empty: 스택이 비어있는지 조회한다.</li>
<li>size: 스택 크기를 확인한다.</li>
</ul>
<h2 id="swift-code">Swift Code</h2>
<pre><code class="language-swift">struct Stack&lt;T&gt;{
    var stack:[T] = []

    public var count: Int {
        return stack.count
    }

    public var isEmpty: Bool{
        return stack.isEmpty
    }

    public mutating func push(_ data: T){
        stack.append(data)
    }

    //pop하는 값이 없을 수 도 있음, 옵셔널 타입으로 반환
    public mutating func pop() -&gt; T?{
        return isEmpty ? nil : stack.popLast()
    }

    public func peek() -&gt; T{
        return stack.last()
    }

    public func peek() -&gt; T?{
        return stack.last
    }

    public func size() -&gt; Int{
        return stack.count
    }

}
</code></pre>
<p>그리고 Swift에서는 직접적으로는 Stack을 제공하지는 않지만 배열의 마지막 요소를 삭제하는 부분에서는 .popLast() 라는 기능과 키워드를 사용하는것으로 보아 간접적으로라도 배열을 이용해서 stack을 이용하라는 애플의 의도가 보인다. </p>
<blockquote>
<p>push는 .append(), pop은 .popLast()프로퍼티를 이용하는 형태로 구현가능하다.</p>
</blockquote>
<h1 id="활용">활용</h1>
<ol>
<li>재귀 알고리즘</li>
<li>웹사이트의 뒤로가기</li>
<li>애플리케이션의 실행취소기능 (undo)</li>
</ol>
<p>등등 다양한 곳에서 사용된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[애플 디벨로퍼 아카데미 4기 늦은 합격 후기]]></title>
            <link>https://velog.io/@fox_hunter/ADA-Review</link>
            <guid>https://velog.io/@fox_hunter/ADA-Review</guid>
            <pubDate>Wed, 05 Mar 2025 07:35:23 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><em>선발과정의 구체적인 내용이나 사용하는 프로그램, 아카데미의 구성이나 컨텐츠는 내부 정책으로 인해 소개 못드리는 점 양해 부탁드립니다.</em> 😅</p>
</blockquote>
<h1 id="🎬-개요">🎬 개요</h1>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/d421a842-bed0-4dd3-9a58-8f24e66d4569/image.jpg" alt=""></p>
<p>본인은 2025년도 진행하는 애플 디벨로퍼 아카데미 4기에 지원하게 되었고 최종적으로 합격하게 되었다 🥳</p>
<p>개인적으로 조금 부족했지만 정말 운이 좋았다고 생각하고 애플 디벨로퍼 아카데미를 준비하며 느낀점과 준비한 내용을 공유하고자 한다!</p>
<h1 id="🏃-기본적인-준비">🏃 기본적인 준비</h1>
<h2 id="🧑💻-인재상-파악하기">🧑‍💻 인재상 파악하기</h2>
<blockquote>
<p>애플 디벨로퍼 아카데미 한국 홈페이지 FAQ</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/8f229a2e-72de-41a5-b8bc-112eac2b1d97/image.png" alt=""></p>
<p>애플 디벨로퍼 아카데미가 아니더라도 어딘가에 지원한다면 해당 기업혹은 기관에서 원하는 인재상이 매우 중요하다고 생각하고 이를 조사하고 서류작성과 면접준비를 했다.</p>
<hr>
<p><a href="https://developeracademy.postech.ac.kr/">애플 디벨로퍼 아카데미 홈페이지</a>를 자세히 읽어보자 인재상은 어떠한지 이전 기수들의 작품들은 어떤느낌인지 내부 분위기는 어떠한지 말이다.</p>
<p>나는 이러한 과정을 통해서 애플디벨로퍼 아카데미 내부의 스타일을 나와 맞추기 위해서 많이 노력했다.</p>
<blockquote>
<p>아카데미 멘토님에게 들은 내용인데 어차피 애플 디벨로퍼 아카데미는 교육기관이기에 뛰어난 능력을 가진 사람 보다 애플 디벨로퍼 아카데미에 스타일에 맞는 사람이 합격할 가능성이 높다는 조언도 해주셨다.</p>
</blockquote>
<h2 id="🎉-공식행사-참여해보기">🎉 공식행사 참여해보기</h2>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/96b9ee2d-5a84-407f-8b10-b3987d321bcd/image.jpeg" alt=""></p>
<p>나는 애플 디벨로퍼 아카데미 오픈데이 행사에 참여 했다. 
오픈데이는 애플 디벨로퍼 아카데미를 지원한 사람이라면 누구나 선착순으로 참여 가능한 행사로 사실 내부 분위기가 궁금해서 참여했지만 지금와서 보면 합격에 적지않은 도움을 받았던 경험이었던것 같다. ( 맛있는 간식도 챙겨준다 😏)</p>
<p>오픈데이에서는 아카데미 멘토분들과 이전 기수 러너 분들과 함께 러너들이 어떤 교육을 받는지, 합격하기 위해서 어떤 준비를 하면 좋을지, 애플 디벨로퍼 아카데미가 지향하는 목표가 무엇인지 그리고 개인적인 준비상황에 대한 간단한 피드백등을 주고 받을 수 있었다.</p>
<hr>
<p>굳이 오픈데이에 고집할 필요는 없다고 생각한다. </p>
<p>애플 디벨로퍼 아카데미에서는 오픈데이, 성과발표회, 파운데이션 프로그램 등등 아카데미 소속이 아니더라도 참여할 수 있는 기회가 많다. </p>
<p>애플 디벨로퍼 아카데미에 들어가고 싶다면 적어도 한번은 이러한 공식행사를 참여 하는것이 정말 많은 도움이 된다고 생각한다.</p>
<h1 id="📑-라이프저니-포트폴리오">📑 라이프저니, 포트폴리오</h1>
<p>애플 디벨로퍼 아카데미 러너 선발에는 크게 3가지 과정으로 나뉜다.</p>
<blockquote>
</blockquote>
<p>라이프저니와 포트폴리오를 제출하는 <strong>서류 제출과정</strong>,
온라인 테스트로 문제해결 능력을 파악하는 <strong>온라인 테스트 *<em>,
마지막으로 멘토님들과의 *</em>최종 인터뷰</strong></p>
<p>일단 서류 합격을 위해서 인터넷에 있는 애플 디벨로퍼 아카데미 지원 서류들을 있는대로 다 읽어봤다. 내용을 공개하는건 안되기에 다들 형태만 공개했지만 그래도 왠만한 블로그 게시글은 다 읽어 본것 같다 </p>
<p>그리고 기본적인 초안을 작성하고 애플 디벨로퍼 아카데미 오픈데이에 참여해서 멘토님들의 조언을 듣고 수정해서 제출했다.</p>
<hr>
<h2 id="⚜️-서류의-디자인이-중요한가">⚜️ 서류의 디자인이 중요한가?</h2>
<p>나는 테크 분야로 지원했기에 사실 디자인은 크게 뛰어나게 하지 않았다. 라이프저니는 제공하는 공식 포맷에서 살짝 변형한 형태였고 포트폴리오는 인터넷에 있는 개발자 포트폴리오 파일 형식을 많이 참조해서 제출했다. </p>
<p>디자인으로 지원한다면 문서의 디자인이 중요할수 있고 나의 역량과 경험을 소개하기 위해서 중요할 수 있지만 이외의 분야의 지원자들은 디자인이 크게 중요한것 같지는 않다.</p>
<hr>
<h2 id="🧐-그럼-뭣이-중헌디">🧐 그럼 뭣이 중헌디?</h2>
<p>나도 처음엔 애플이니까 디자인이 진짜 중요하겠지? 나의 감각을 표현하는게 중요하겠지? 이렇게 생각했지만 </p>
<p>개인적인 느낀점은 정말 중요한건 나의 라이프 스타일이 아카데미에 맞는지, 배울 열정이 있는지, 가독성 이렇게 크게 3가지라고 생각한다.</p>
<h3 id="👓-가독성">👓 가독성</h3>
<blockquote>
<p>개인적으로 가독성이 생각보다 중요하다고 생각한다. </p>
</blockquote>
<p>객관적으로 보면 애플 디벨로퍼 아카데미 지원자는 상반기, 하반기 별로 100명 내외로 선발하는것으로 알고 있는데
<img src="https://velog.velcdn.com/images/fox_hunter/post/ee4b177d-d51a-4977-80d2-6e620b84bbbc/image.png" alt=""></p>
<p>1:10의 비율이라고 가정하고 (한번 뉴스기사에서 경쟁률이 1: 10~20이라고 나온적 있음) 한번의 지원에 서류만 인당 2개 그리고 각각 10장 안으로 작성할텐데 간단하게 인당 20장 X 100 X 10 으로 생각하면 선발하는 입장으로 보면 선발 과정마다 20000장에 이르는 문서를 읽어 보게 될텐데 선발하는 입장에서 보면 가독성이 중요하지 않을 수 없다고 생각한다.</p>
<blockquote>
<p>그래서 나는 최대한 많은 내용을 작성하고 최대한 깎아내려고 노력했다 그리고 강조하고 싶은 내용을 <strong><em>볼드 + 이탤릭</em></strong> 처리해서 부분부분 강조하는 식으로 표현했다. </p>
</blockquote>
<p>그리고 마지막으로 라이프 저니에서 나만의 독특한 경험, 혹은 실패한 경험 같은 나만의 내용이 적어도 하나의 파트는 있는게 좋다고 생각한다. </p>
<blockquote>
<p>선발하는 사람이 기억할만한 나만의 킥?이 중요하다고 생각한다.</p>
</blockquote>
<hr>
<h3 id="🖥️-서류의-해상도">🖥️ 서류의 해상도</h3>
<p>가독성과도 이어지는 내용인데 </p>
<p>추상적인 내용보다는 내가 아카데미 러너로 참여해서 어떤일을 해내고 싶은지 어떤 모습으로 성장하고 싶은지 자세하게 작성하는것이 중요하다고 생각한다.</p>
<h1 id="📝-온라인-테스트">📝 온라인 테스트</h1>
<p>대다수의 블로그 후기글을 읽어 봤을때 거의 모든 지원자들이 서류제출하고 온라인 테스트까지는 무난하게 간다는것을 보았다.</p>
<p>개인적으로 그런것 같기도 하다.</p>
<p>하지만 준비는 사실상 어렵다고 생각한다. 기출문제가 있는것도 아니고 문제를 소개하는것을 애플 디벨로퍼 아카데미 내부 정책에 위반하는 것이니...</p>
<hr>
<p>나는 블로그를 찾아보다 보니 삼성같은 대기업 입사 논리문제나 IQ테스트 문제, 멘사문제 같은 논리 문제를 풀고 가면 도움이 된다기에 몇개 보고 테스트를 진행했다.</p>
<blockquote>
<p>그냥 너무 긴장하지 말고 릴렉스한 상태에서 간단한 문제풀이로 머리만 조금 풀어주고 진행하면 도움이 될것이다.</p>
</blockquote>
<h1 id="💬-온라인-인터뷰">💬 온라인 인터뷰</h1>
<p>오픈데이에서 멘토님께 들은 내용이다.</p>
<blockquote>
<p><em>&quot;인터뷰를 준비하지 않는것이 준비하는것 입니다.&quot;</em></p>
</blockquote>
<p>다른 블로그에서 공통적으로 들은 말이다.</p>
<blockquote>
<p>&quot; 준비할수도 없고 준비해도 도움이 되지 않을것이다. &quot;
&quot; 처음 본 형태의 인터뷰이다. &quot;
&quot; 인싸 라면 좋아할 인터뷰이다.&quot;</p>
</blockquote>
<p>외향적이고 밝은 분위기에서 진행되는 예상불가능한 인터뷰일것으로 생각했다.</p>
<p>극 I인 나는 긴장했지만 실제론 열심히 인터뷰에 참여한것만으로도 충분하다고 생각한다.</p>
<hr>
<h2 id="인터뷰-준비">인터뷰 준비</h2>
<p>내용적인 준비는 내가 이전에 작성한 라이프저니와 포트폴리오를 꼼꼼하게 읽어보는것으로 충분할것 같고</p>
<p>외적으로는 온라인으로 인터뷰하는 것이므로 인터뷰 환경을 깔끔하게 하고(가능하면 스튜디오나 뒷배경이 깔끔한 장소를 대여하는것을 추천)
마이크나 카메라의 상태를 미리 점검해서 깔끔한 이미지를 내는것만으로 충분하다고 생각한다. </p>
<p>만약 본인처럼 내향적이라면 chatGPT를 이용해서 음성으로 ai와 잡담이라도 하면서 입을 풀고 인터뷰를 진행하는것도 도움이 될 수 있을것이라 생각한다.</p>
<blockquote>
<p>긴장하지 않고, 밝은 모습과 열정적인 모습 보여주기 !</p>
</blockquote>
<h1 id="📓-합격-후기">📓 합격 후기</h1>
<p>개인적으로 애플 디벨로퍼 아카데미를 지원하며 합격하지 않았더라도 많은 것을 느꼈다.</p>
<p>라이프저니를 작성하면서 나의 인생을 돌아보고 정리하며 내가 어떤 사람인지 파악하며 성장하는 계기가 되어서 너무 좋았다.</p>
<p>어쩌면 애플 디벨로퍼 아카데미 선발이라는 하나의 큰 챌린지를 수행하면서 내가 어떤 점이 부족한지 어떤 점을 더 공부하고 성장해 나가고 싶은지 정확하게 파악하게 된 계기가 되었다고 생각한다.</p>
<p>물론 애플 디벨로퍼 아카데미의 합격이 중요하지만 개인적으로는 개인의 성장과 회고를 중심으로 선발을 준비하는것이 중요하다고 생각한다. </p>
<blockquote>
<p>모든 지원자들의 큰 목표는 개인의 성장이니까 👍</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SwiftUI] Button에 대해서]]></title>
            <link>https://velog.io/@fox_hunter/SwiftUI-Button</link>
            <guid>https://velog.io/@fox_hunter/SwiftUI-Button</guid>
            <pubDate>Wed, 05 Feb 2025 07:25:21 GMT</pubDate>
            <description><![CDATA[<h1 id="개요">개요</h1>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/fox_hunter/post/47707d18-22bd-4d26-b804-2d2af7cd8bb7/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/fox_hunter/post/8740ca74-112c-4d0a-8a68-f0704f83a213/image.png" alt=""></th>
</tr>
</thead>
</table>
<p>-&gt; 기본적인 형태는 다음과 같다</p>
<p>버튼이란 SwiftUI(이후 스유) 많이 사용되는 컴포넌트 중 하나이다.</p>
<p>뷰에서 사용자가 클릭함으로 특정 이벤트를 발생시키는 역할을 수행한다.</p>
<h1 id="설명">설명</h1>
<h2 id="구조">구조</h2>
<p>버튼은 두개의 label, action 클로저들을 인수로 받고 두개 모두 들어와야 정상적으로 뷰에 그려진다.</p>
<p>뷰에 나타나는 컴포넌트를 넣는 label과 코드상의 동작을 작성하는 action 두개의 파라메터를 요구한다.</p>
<h3 id="label">Label</h3>
<blockquote>
<p>() -&gt; some View 타입의 클로저를 인수로 받는다.</p>
</blockquote>
<p>말이 어렵지만 알고보면 간단하다</p>
<p>처음으로 스유 프로젝트를 생성하면 ContentView의 기본적인 구조를 기억하는가?</p>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/fb42bf29-991a-4ddc-b9cb-7b064aebc643/image.png" alt=""></p>
<p>여기서 내부에 여러 컴포넌트를 작성하는 body프로퍼티의 타입이 바로 ()-&gt;View 타입이다.</p>
<blockquote>
<p>즉 많이 사용하는 <u>Text(), Image() 컴포넌트를 안에 사용이 가능하다.</u></p>
</blockquote>
<pre><code class="language-swift">Button(action: {
            // () -&gt; Void 타입의 클로저가 들어와야 함
            print(&quot;hello world&quot;)
        }, label: {
            // () -&gt; some View타입의 클로저가 와야함
            Image(&quot;earth1&quot;)
                .resizable()
                .frame(width: 150, height: 150)
                .cornerRadius(100)

            Text(&quot;Click Me&quot;)
        })</code></pre>
<p>그리고 VStack이나 HStack으로 묶는것도 가능하다.</p>
<pre><code class="language-swift"> Button(action: {
            // () -&gt; Void 타입의 클로저가 들어와야 함
            print(&quot;hello world&quot;)
        }, label: {
            // () -&gt; some View타입의 클로저가 와야함
            VStack{
                Image(&quot;earth1&quot;)
                    .resizable()
                    .frame(width: 150, height: 150)
                    .cornerRadius(100)

                Text(&quot;Click Me&quot;)
            }
        })</code></pre>
<h3 id="action">action</h3>
<blockquote>
<p>action에는 () -&gt; Void타입의 클로저가 들어와야 한다.</p>
</blockquote>
<blockquote>
<p>즉 이곳에는 인수가 없고 반환값이 존재하지 않는 함수나 클로저가 와야한다는 말이다. </p>
</blockquote>
<p>외부에서 따로 함수를 만들어서 작성해도 되고 그냥 내부에서 동작을 정의 해도 된다.</p>
<pre><code class="language-swift">import SwiftUI

struct ButtonTest: View {

    func someFunc() -&gt; Void{
        print(&quot;hello world&quot;)
    }

    var body: some View {

        let someClosure = { () -&gt; Void in
            print(&quot;hello world&quot;)
        }

        Button(action: {
            // () -&gt; Void 타입의 클로저가 들어와야 함
            print(&quot;hello world&quot;)

            someClosure()

            someFunc()

        }, label: {
            // () -&gt; some View타입의 클로저가 와야함
            VStack{
                Image(&quot;earth1&quot;)
                    .resizable()
                    .frame(width: 150, height: 150)
                    .cornerRadius(100)

                Text(&quot;Click Me&quot;)
            }
        })

    }


}</code></pre>
<h2 id="생략형태">생략형태</h2>
<p><a href="https://velog.io/@fox_hunter/swift-Closures-%ED%81%B4%EB%A1%9C%EC%A0%80%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C">클로저</a> 게시글에서 설명했듯 인수로 클로저를 받는 경우 { }괄호를 뒤로 빼서 따로 작성하는게 가능하다 (=후행클로저)</p>
<pre><code class="language-swift">Button(action: {
    print(&quot;hello world&quot;)
}){
    Text(&quot;Click&quot;)
}</code></pre>
<blockquote>
<p>label의 경우 글자만 넣는 형태가 많다 보니 Text()컴포넌트를 생략하고 그냥 String의 값만 넣는 형태를 지원한다.</p>
</blockquote>
<pre><code class="language-swift">
Button(&quot;Click Me&quot;){
    print(&quot;hello world&quot;)
}</code></pre>
<p>이 경우 action라벨의 경우 후행클로저로 빠지게 되니 생략이 가능하다</p>
<p>이 형태가 제일 작은 형태라고 생각하면 된다.</p>
<h1 id="정리">정리</h1>
<ol>
<li>SwiftUI에서 동작을 실행시키는 컴포넌트중에 Button 컴포넌트가 있다.</li>
<li>버튼 컴포넌트에는 label과 action두개의 파라메터를 필수로 넣어야 한다.</li>
<li>버튼 자체의 생략방법과 후행클로저 문법을 이용하면 다양한 형태로 버튼의 코드 작성이 가능하다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SwiftUI] @State, @Binding 프로퍼티]]></title>
            <link>https://velog.io/@fox_hunter/SwiftUI-StateBinding</link>
            <guid>https://velog.io/@fox_hunter/SwiftUI-StateBinding</guid>
            <pubDate>Sat, 23 Nov 2024 07:30:26 GMT</pubDate>
            <description><![CDATA[<h1 id="개요">개요</h1>
<p>SwiftUI에서 Int나 String같은 데이터를 저장하기 위해서 사용하는게 상태 프로퍼티이다.</p>
<p>사용은 View 구조체 안에 저장 프로퍼티 앞에 @State 라는 프로퍼티 래퍼를 붙여서 표현해 준다.</p>
<p>더 정확히 알고자 한다면 <a href="https://velog.io/@fox_hunter/SwiftBasic-properties2">프로퍼티</a>에 대해서 알아보고 오자.</p>
<h1 id="설명">설명</h1>
<h2 id="state-상태-프로퍼티">@State, 상태 프로퍼티</h2>
<p>앞서 말한것 처럼 상태 프로퍼티는 뷰 구조체에서 내부 데이터를 표시 하기 위한 프로퍼티를 말한다.</p>
<p>SwiftUI에서 이 상태 프로퍼티가 변경되면 그 프로퍼티가 소속된 뷰 계층을 다시 렌더링해야 한다는 신호를 나타낸다.</p>
<p>코드를 살펴보자</p>
<pre><code class="language-swift">import SwiftUI

struct ContentView: View {
    @State private var hello: String = &quot;Hello, World!&quot;

    var body: some View {
        VStack {
            Image(systemName: &quot;globe&quot;)
                .imageScale(.large)
                .foregroundStyle(.tint)
            Text(hello)

        }
        .padding()
    }
}

#Preview {
    ContentView()
}</code></pre>
<blockquote>
</blockquote>
<img src="https://velog.velcdn.com/images/fox_hunter/post/2a25b104-4249-45d4-b4d6-8e47c7d3e21e/image.png" width="300">

<p>여기서 상태 프로퍼티의 경우 해당 뷰에 속한 값이기에 기본적으로 private로 선언하는 것을 권장한다.(private 아니어도 동작은 함)</p>
<p>상태 프로퍼티는 그 이름에 걸맞게 뷰 내부에 상태를 저장하는 기능을 수행하므로 당연히 수정도 가능하다. </p>
<blockquote>
</blockquote>
<p>상태 프로퍼티를 수정하는것을 <strong><em>바인딩</em></strong> 이라고 한다. 이때 변수 이름앞에 $을 붙여서 프로퍼티에 접근해야 한다.</p>
<pre><code class="language-swift">
import SwiftUI

struct ContentView: View {
    @State private var hello: String = &quot;Hello, World!&quot;

    var body: some View {
        VStack {
            Image(systemName: &quot;globe&quot;)
                .imageScale(.large)
                .foregroundStyle(.tint)
            Text(hello)

            Divider()

            //상태 프로퍼티 수정
            TextField(&quot;inesrt text&quot;, text: $hello)

        }
        .padding()
    }
}

#Preview {
    ContentView()
}</code></pre>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/fox_hunter/post/2a25b104-4249-45d4-b4d6-8e47c7d3e21e/image.png" alt="원본"></th>
<th><img src="https://velog.velcdn.com/images/fox_hunter/post/6b7ea34d-7af6-4ae6-ada2-32c1fb77263c/image.png" alt=""></th>
</tr>
</thead>
</table>
<blockquote>
<p>그리고 상태 프로퍼티에 대해서 주의 해야 될 점은 상태 프로퍼티는 단방향 프로세스 라는것이다. </p>
</blockquote>
<p>즉 상위 뷰의 상태 프로퍼티가 변화되면 상위 뷰의 상태 프로퍼티를 사용하는 하위 뷰또한 다시 렌더링 된다.</p>
<h2 id="binding-state바인딩">@Binding, State바인딩</h2>
<p>상태 프로퍼티를 사용해서 하위 뷰에서도 참조하고 접근하고 싶을때 사용한다.</p>
<p>바인딩 하기 위해서 하위 뷰에서 @Binding을 프로퍼티 앞에 붙여주고 상위뷰에선 하위 뷰를 호출할때 ()괄호 안에 상태 프로퍼티를 넣어서 호출하면 된다.</p>
<p>말로 하면 복잡한데 코드로 보면 간단하다 다음 코드를 보자</p>
<pre><code class="language-swift">import SwiftUI

struct ContentView: View {
    @State private var hello: String = &quot;Hello, World!&quot;

    var body: some View {
        VStack {
            Text(&quot;MainView: \(hello)&quot;)
            Divider()
            TextField(&quot;inesrt text&quot;, text: $hello)
        }
        .padding()

        SubView(hello: $hello)  //하위 뷰 호출
    }
}

struct SubView: View{
    @Binding var hello: String  //state 바인딩

    var body: some View{
        VStack{
            Text(&quot;SubView: \(hello)&quot;)
            Divider()
            TextField(&quot;inesrt text&quot;, text: $hello)
        }
        .padding()
    }
}

#Preview {
    ContentView()
}

</code></pre>
<img src="https://velog.velcdn.com/images/fox_hunter/post/cce0f3d2-b650-4e6a-8c5f-b31c554d690e/image.png" width= "300">

<p>다음과 같이 사용하면 된다.</p>
<p>주의 할점은 하위 뷰를 호출할때도 SubView(hello: $hello) 처럼 $달러기호를 이용해서 바인딩 해줘야 한다는 점이다.</p>
<h1 id="정리">정리</h1>
<p>SwiftUI에서 상태를 저장하고 참조 할 수 있는 수단인 상태 프로퍼티와 상태 프로퍼티에 접근하는 방법인 바인딩에 대해서 다뤘다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Swift] Swift 프로퍼티 String으로 출력하기]]></title>
            <link>https://velog.io/@fox_hunter/Swift-CustomStringConvertible</link>
            <guid>https://velog.io/@fox_hunter/Swift-CustomStringConvertible</guid>
            <pubDate>Thu, 21 Nov 2024 08:29:03 GMT</pubDate>
            <description><![CDATA[<h1 id="개요">개요</h1>
<p>Java로 코딩을 하다보면 toString()을 사용하는 경우가 많다.</p>
<p>필자의 경우 GoF 디자인 패턴을 공부하고 있는데 서적의 예제코드가 전부 자바로 작성되어있어서 toString을 이용한 부분을 Swift로 변경하는데 어려움을 겪었다.</p>
<p>swift에서 toString같은 로직을 사용하게 도와주는게 CustomStringConvertible 프로토콜이다.</p>
<p>CustomStringConvertible이란? 타입의 프로퍼티를 원하는 형태의 String으로 변환해주는 것을 도와주는 프로토콜이다.</p>
<h3 id="descrption과의-차이점">descrption과의 차이점</h3>
<p>.description은 해당 프로퍼티의 이름 그 자체를 스위프트에 정의된 형태로 출력해준다.</p>
<pre><code class="language-swift">struct Point{
    var posX: Int = 10
    var posY: Int = 20
}

var point: Point = Point()

print(point)</code></pre>
<blockquote>
<p>출력결과: Point(posX: 10, posY: 20)</p>
</blockquote>
<p>만약 이 결과를 내가 원하느 형태로 출력하고 싶다면 </p>
<p>미리 원하는 클래스나 구조체에 CustomStringConvertible을 구현하도록 하거나 extension을 이용해서 해당 타입이 CustomStringConvertible을 따르도록 구현해 주면 된다.</p>
<h2 id="customstringconvertible의-사용법">CustomStringConvertible의 사용법</h2>
<pre><code class="language-swift">struct Point{
    var posX: Int = 10
    var posY: Int = 20
}

extension Point: CustomStringConvertible{
    var description: String{
        return &quot;X좌표는 \(posX), Y좌표는 \(posY)&quot;
    }
}

var point: Point = Point()

print(point)</code></pre>
<blockquote>
<p>출력결과: X좌표는 10, Y좌표는 20</p>
</blockquote>
<p>위 코드처럼 CustomStringConvertible을 적용하고 description프로퍼티를 정의해서 원하는 형태로 출력되도록 구현하면 된다.</p>
<p>CustomStringConvertible을 이용하서 사용자 정의 형태의 출력을 원하는 경우 사용자정의 클래스나 구조체의 경우에는 위 코드처럼 사용하면 된다.</p>
<p>하지만 Int나 Double, String같은 swift에 기본적으로 정의된 구조체나 클래스에 CustomStringConvertible을 사용하길 원한다면 아래의 extension을 이용하는 방법만 가능하다.</p>
<h2 id="extension을-이용하는-방법">extension을 이용하는 방법</h2>
<pre><code class="language-swift">
extension Int {
    var description: String {
        return &quot;내 번호는 \(self)&quot;
    }
}

print(10.description)
</code></pre>
<blockquote>
<p>출력 결과: 내 번호는 10</p>
</blockquote>
<p>이처럼 Int나 String같은 기본으로 정의된 구조체나 클래스들은 보통 이미 CustomStringConvertible를 따르면서 기본적으로 정의된 description이 존재하기 때문에 사용자 정의 형태로 변경하려면 extension을 이용해서 기존의 description을 재정의 해줘야 한다. </p>
<p>그래서 CustomStringConvertible를 그냥 적용해서는 사용이 불가능하다. </p>
<h1 id="정리">정리</h1>
<blockquote>
</blockquote>
<ol>
<li>사용자 정의 클래스/구조체의 경우 CustomStringConvertible를 구현하는것으로 사용가능</li>
<li>Int/Double/String같은 Swift에 기본적으로 정의된 타입의 경우 extension을 이용해서 기존의 정의된 description을 재정의 하는것으로 커스텀이 가능하다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SwiftUI] 프로젝트 생성하기]]></title>
            <link>https://velog.io/@fox_hunter/SwiftUICreateProject</link>
            <guid>https://velog.io/@fox_hunter/SwiftUICreateProject</guid>
            <pubDate>Tue, 19 Nov 2024 08:16:19 GMT</pubDate>
            <description><![CDATA[<h1 id="개요">개요</h1>
<p>SwiftUI로 앱을 만들기 위해서는 xcode라는 apple에서 제공하는 프로그램을 사용해야 한다.</p>
<p>이번 시간에는 Hello World!만 출력하는 기본적인 앱을 SwiftUI로 생성하는 법을 알아 보고자 한다.</p>
<h1 id="설명">설명</h1>
<p>먼저 xcode를 맥의 AppStore에서 다운받아준다.</p>
<p>이후 실행시키면 아래의 이미지와 동일한 창이 나오는데 처음 xcode를 사용한다면 우측에 리스트는 나오지 않는다. </p>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/9c6cede3-7e3f-4f7a-9263-5fc8afc438fe/image.png" alt=""></p>
<p>Create New Project...를 눌러보자</p>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/b007827e-c9cd-49cb-8828-73ded85c24ae/image.png" alt=""></p>
<p>다음과 같이 어떤 앱을 만들것인지 정하라는 화면이 나온다.</p>
<p>여기서 최상단에 Mutiplatform, iOS, macOS, tvOS등등이 있는데 아이폰앱을 개발하려면 iOS를 선택하고 App아이콘을 클릭하면 된다.</p>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/a6b59561-e2b4-43d2-a38a-ccba593be41c/image.png" alt=""></p>
<p>제대로 선택을 했다면 다음과 같은 화면이 나온다.</p>
<p>맨위에 프로젝트 이름을 임의로 지어주고 Organization Identifier는 프로젝트를 개발한 사람이름 정도를 표시하는 정보로 사용하고 싶은 이름을 작성하면 된다.</p>
<blockquote>
</blockquote>
<p>Interface는 SwiftUI
Language는 Swift
Testing System과 Storage는 None을 해주면 된다.</p>
<p>설명을 간단하게 하자면 Swift는 사용하고자 하는 언어이다.</p>
<p>iOS로 앱을 개발하는데에는 SwiftUI와 UIKit 두가지 선택지가 있는데 지금은 SwiftUI로 개발할 것이니 SwiftUI로 입력해주면 된다. 
(참고로 UIKit은 Storyboard라는 옵션을 선택해 주면 된다.)</p>
<blockquote>
<p>그리고 Next를 해주면 파일 저장위치를 선택하라는 창이 나오는데 원하는 위치에 선택해주면 된다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/5a6311fd-a7b1-4b75-b2ae-371c7308bd94/image.png" alt=""></p>
<p>파일 저장위치까지 지정해주면 다음과 같은 기본 화면이 나온다. </p>
<p>좌측에는 프로젝트 구성과 파일의 코드부분, 우측에는 코드에 대한 preview가 나온다.</p>
<h1 id="정리">정리</h1>
<p>xcode를 이용해서 SwiftUI 프로젝트를 생성하였다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Design Pattern] 추상 팩토리 패턴(Abstract Factory Pattern)]]></title>
            <link>https://velog.io/@fox_hunter/Design-Pattern-Abstract-Factory-Pattern</link>
            <guid>https://velog.io/@fox_hunter/Design-Pattern-Abstract-Factory-Pattern</guid>
            <pubDate>Mon, 11 Nov 2024 04:44:39 GMT</pubDate>
            <description><![CDATA[<h1 id="개요">개요</h1>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/7d19db0e-7a23-49dc-a5a7-0b39ed5097b3/image.png" alt="">
이 글을 더 잘 이해하고 싶다면 <a href="https://velog.io/@fox_hunter/Design-Pattern-Template-Method-%ED%85%9C%ED%94%8C%EB%A6%BF%EB%A9%94%EC%86%8C%EB%93%9C-%ED%8C%A8%ED%84%B4"><u>템플릿메소드 패턴</u></a>, <a href="https://velog.io/@fox_hunter/DesignPattern-FactoryMethod"><u>팩토리메소드 패턴</u></a> 두가지 패턴을 알고 보는것을 추천한다.</p>
<p><u><strong>템플릿 메소드 패턴</strong></u>은 상속혹은 구현하는 클래스의 흐름을 미리 추상화해서 흐름을 설계하고 구현하도록 하는 패턴이고 </p>
<p><u><strong>팩토리 메소드 패턴</strong></u>은 템플릿 메소드 패턴에서 더 나아가 클래스의 인스턴스 생성흐름까지 설계하고 고정하는 패턴이었다. </p>
<p><u><strong>추상 팩토리 패턴</strong></u>은 여기서 조금 더 확장된 개념이라고 볼 수 있다. 
추상 팩토리 패턴은 여러개의 클래스를 임의의 단위로 묶어서 추상화하는 방식으로 여러개의 객체의 흐름과 형태를 고정하는 방식이다.</p>
<p>예를들면 <strong>모니터+키보드+메인보드+배터리+운영체제</strong>가 합쳐진 노트북이라는 패키지가 있을때 이를 애플이라는 클라이언트가 구현하면 맥북, 삼성이 구현하면 갤럭시북, 레노버가 구현하면 씽크패드 등등 이러한 SW구조에 적용되는 패턴이라고 생각하면 이해가 쉬울것이다.</p>
<p>정확한 설명은 아니지만 <strong>패키지 단위로 추상화하는 패턴</strong>이라고 생각하면 편할 수 있다.</p>
<h1 id="설명">설명</h1>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/2df91a51-a4df-49bd-a990-cd2870029593/image.png" alt=""></p>
<p>기본적인 형태는 팩토리 메소드 패턴과 비슷하다고 볼 수 있다.
다만 클래스 한개의 흐름을 지정하는게 아니라 앞서 말했듯이 여러개의 클래스의 흐름을 결정한다. </p>
<p>이러한 형태의 장점은 여러개의 클래스가 하나의 특정 클래스에 의존하지 않게 해준다는 장점이 있고</p>
<p>기능 확장이 용이하며 형태가 정해져 있어서 유지보수가 용이하다는 점이 장점이 있다.</p>
<h3 id="의도">의도</h3>
<blockquote>
<p>클래스 이름을 사용하지 않고 관련 객체들의 패밀리를 생성하기 위한 인터페이스 제공하기 위함</p>
</blockquote>
<h3 id="적용시점">적용시점</h3>
<blockquote>
<ul>
<li>객체들이 다른 부류의 객체들과 섞이지 않고 관련 객체들의 패밀리들만 사용될 수 있도록 할 때</li>
</ul>
</blockquote>
<ul>
<li>시스템이 여러 패밀리들 중에서 하나만 사용하여 형상화되어야 할 때</li>
</ul>
<h3 id="역할-설명">역할 설명</h3>
<blockquote>
</blockquote>
<ul>
<li>AbstractProduct<ul>
<li>AbstractFactory에서 생성할 제품들에 대한 인터페이스를 선언함</li>
</ul>
</li>
<li>ConcreteProduct<ul>
<li>ConcreteProduct1, 2, 3는 각각 AbstractProduct1, 2, 3의 인터페이스를 구현함</li>
</ul>
</li>
<li>AbstractFactory<ul>
<li>AbstractProduct 타입의 인스턴스를 생성하기 위한 인터페이스를 선언함</li>
</ul>
</li>
<li>ConcreteFactory<ul>
<li>AbstractFactory의 인터페이스를 구현함</li>
</ul>
</li>
<li>Client<ul>
<li>예제에서는, Main 클래스가 해당됨</li>
<li>AbstractFactory와  AbstractProduct의 인터페이스만 사용함</li>
<li>AbstractProduct 타입의 객체를 생성하려면 우선 적절한 ConcreteFactory 객체를 얻어야 함</li>
</ul>
</li>
</ul>
<h2 id="예제코드">예제코드</h2>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/c46bc362-e347-4bde-b637-c33bae064f3a/image.png" alt=""></p>
<p>예제코드의 클래스 다이어그램</p>
<h3 id="java">JAVA</h3>
<h4 id="추상-클래스들">추상 클래스들</h4>
<pre><code class="language-java">public abstract class Factory {
    public static Factory getFactory(String classname) {
        Factory factory = null;
        try {
            factory = (Factory)Class.forName(classname).getDeclaredConstructor().newInstance();
        } catch (ClassNotFoundException e) {
            System.out.println(classname + &quot; 클래스가 발견되지 않았습니다.&quot;);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return factory;
    }

    public abstract Link createLink(String caption, String url);
    public abstract Tray createTray(String caption);
    public abstract Page createPage(String title, String author);

    public Page createNaverPage() {
        Link link = createLink(&quot;Naver&quot;, &quot;https://www.naver.com/&quot;);
        Page page = createPage(&quot;Naver&quot;, &quot;Naver&quot;);
        page.add(link);
        return page;
    }
}</code></pre>
<pre><code class="language-java">public abstract class Item {
    protected String caption;

    public Item(String caption) {
        this.caption = caption;
    }

    public abstract String makeHTML();
}</code></pre>
<pre><code class="language-java">public abstract class Link extends Item {
    protected String url;

    public Link(String caption, String url) {
        super(caption);
        this.url = url;
    }
}</code></pre>
<pre><code class="language-java">public abstract class Tray extends Item {
    protected List&lt;Item&gt; tray = new ArrayList&lt;&gt;();

    public Tray(String caption) {
        super(caption);
    }

    public void add(Item item) {
        tray.add(item);
    }
}</code></pre>
<pre><code class="language-java">public abstract class Page {
    protected String title;
    protected String author;
    protected List&lt;Item&gt; content = new ArrayList&lt;&gt;();

    public Page(String title, String author) {
        this.title = title;
        this.author = author;
    }

    public void add(Item item) {
        content.add(item);
    }

    public void output(String filename) {
        try {
            Files.writeString(Path.of(filename), makeHTML(),
                    StandardOpenOption.CREATE,
                    StandardOpenOption.TRUNCATE_EXISTING,
                    StandardOpenOption.WRITE);
            System.out.println(filename + &quot; 파일을 작성했습니다.&quot;);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public abstract String makeHTML();
}</code></pre>
<h4 id="구현-클래스들">구현 클래스들</h4>
<pre><code class="language-java">public class ListFactory extends Factory {
    @Override
    public Link createLink(String caption, String url) {
        return new ListLink(caption, url);
    }

    @Override
    public Tray createTray(String caption) {
        return new ListTray(caption);
    }

    @Override
    public Page createPage(String title, String author) {
        return new ListPage(title, author);
    }
}</code></pre>
<pre><code class="language-java">public class ListLink extends Link {
    public ListLink(String caption, String url) {
        super(caption, url);
    }

    @Override
    public String makeHTML() {
        return &quot;  &lt;li&gt;&lt;a href=\&quot;&quot; + url + &quot;\&quot;&gt;&quot; + caption + &quot;&lt;/a&gt;&lt;/li&gt;\n&quot;;
    }
}</code></pre>
<pre><code class="language-java">public class ListPage extends Page {
    public ListPage(String title, String author) {
        super(title, author);
    }

    @Override
    public String makeHTML() {
        StringBuilder sb = new StringBuilder();
        sb.append(&quot;&lt;!DOCTYPE html&gt;\n&quot;);
        sb.append(&quot;&lt;html&gt;&lt;head&gt;&lt;title&gt;&quot;);
        sb.append(title);
        sb.append(&quot;&lt;/title&gt;&lt;/head&gt;\n&quot;);
        sb.append(&quot;&lt;body&gt;\n&quot;);
        sb.append(&quot;&lt;h1&gt;&quot;);
        sb.append(title);
        sb.append(&quot;&lt;/h1&gt;\n&quot;);
        sb.append(&quot;&lt;ul&gt;\n&quot;);
        for (Item item: content) {
            sb.append(item.makeHTML());
        }
        sb.append(&quot;&lt;/ul&gt;\n&quot;);
        sb.append(&quot;&lt;hr&gt;&lt;address&gt;&quot;);
        sb.append(author);
        sb.append(&quot;&lt;/address&gt;\n&quot;);
        sb.append(&quot;&lt;/body&gt;&lt;/html&gt;\n&quot;);
        return sb.toString();
    }
}</code></pre>
<pre><code class="language-java">public class ListTray extends Tray {
    public ListTray(String caption) {
        super(caption);
    }

    @Override
    public String makeHTML() {
        StringBuilder sb = new StringBuilder();
        sb.append(&quot;&lt;li&gt;\n&quot;);
        sb.append(caption);
        sb.append(&quot;\n&lt;ul&gt;\n&quot;);
        for (Item item: tray) {
            sb.append(item.makeHTML());
        }
        sb.append(&quot;&lt;/ul&gt;\n&quot;);
        sb.append(&quot;&lt;/li&gt;\n&quot;);
        return sb.toString();
    }
}</code></pre>
<pre><code class="language-java">public class Main {
    public static void main(String[] args) {
        if (args.length != 2) {
            System.out.println(&quot;Usage: java Main filename.html class.name.of.ConcreteFactory&quot;);
            System.out.println(&quot;Example 1: java Main listNaver.html listfactory.ListFactory&quot;);
            System.out.println(&quot;Example 2: java Main divNaver.html divfactory.DivFactory&quot;);
            System.exit(0);
        }

        String filename = args[0];
        String classname = args[1];

        Factory factory = Factory.getFactory(classname);

        // Page
        Page page = factory.createNaverPage();

        page.output(filename);
    }
}
</code></pre>
<h3 id="swift">Swift</h3>
<p>swift에서는 추상클래스가 없어서 java의 추상클래스를 따라하려면 프로토콜이나 일반 클래스로 구현후 접근 제한을 걸어야 한다.</p>
<p>(코드 구현중임...)</p>
<pre><code class="language-swift">class AbstractFactory{
    //Main에서 호출되는 클래스 이름에 따라 하위에서 생성된 concreateFactory클래스 인스턴스를 연결해주는 기능을 수행함
    //클래스 동적할당
    static func getFactory(_ classname: String) -&gt; AbstractFactory{
        if classname == &quot;ListFactory&quot;{
            return ListFactory()
        }

        return AbstractFactory()
    }
    func createLink(_ caption: String, _ url: String) -&gt; AbstractLink {
        return AbstractLink(caption: caption, url: url)
    }
    func createTray(_ caption: String) -&gt; AbstractTray{
        return AbstractTray(caption: caption)
    }
    func createPage(_ title: String, _ author: String) -&gt; AbstractPage{
        return AbstractPage(title: title, author: author)
    }

    func createNaverPage() -&gt; AbstractPage{
        let link = createLink(&quot;Naver&quot;, &quot;https://www.naver.com/&quot;)
        let page = createPage(&quot;Naver&quot;, &quot;Naver&quot;)
        page.add(item: link)
        return page
    }

}
class AbstractTray: Item{
    var tray: [Item] = []

    init(caption: String, _ tmp: String = &quot;&quot;) {
        super.init(caption: caption)
    }

    func add(item: Item) {
        tray.append(item)
    }

}
class AbstractPage {
    var title:String
    var author:String
    var content: [Item] = Array&lt;Item&gt;()

    init(title: String, author: String) {
        self.title = title
        self.author = author
    }

    func add(item: Item){
        content.append(item)
    }
    func output(filename: String){
        print(&quot;file을 생성했습니다.&quot;)
    }
    func makeHTML() -&gt; String{
        return &quot;&quot;
    }    
}

class AbstractLink: Item{
    var url:String

    init(caption: String, url: String) {
        self.url = url
        super.init(caption: caption)
    }
}
class Item{
    var caption: String
    init(caption: String) {
        self.caption = caption
    }
    func makeHTML() -&gt; String {
        return &quot;&quot;
    }
}
class ListFactory : AbstractFactory{
    override func createLink(_ caption: String, _ url: String) -&gt; AbstractLink {
        return ListLink(caption: caption, url: url)
    }
    override func createTray(_ caption: String) -&gt; AbstractTray {
        return ListTray(caption: caption)
    }
    override func createPage(_ title: String, _ author: String) -&gt; AbstractPage {
        return ListPage(title: title, author: author)
    }
}

class ListTray: AbstractTray{
    init(caption: String){
        super.init(caption: caption)
    }

    override func makeHTML() -&gt; String {
        var result:String = &quot;&quot;
        result += &quot;&lt;li&gt;\n&quot;
        result += caption
        result += &quot;\n&lt;ul&gt;\n&quot;
        for item in tray{
            result += item.makeHTML()
        }
        result += &quot;&lt;/ul&gt;\n&quot;
        result += &quot;&lt;/li&gt;\n&quot;


        return result
    }
}
class ListLink: AbstractLink {
    init(caption: String, url: String, _ tmp: Int = 0) {
        super.init(caption: caption, url: url)
    }
    override func makeHTML() -&gt; String {
        return &quot;  &lt;li&gt;&lt;a href=\&quot;&quot; + url + &quot;\&quot;&gt;&quot; + caption + &quot;&lt;/a&gt;&lt;/li&gt;\n&quot;
    }
}
class ListPage: AbstractPage {
    init(title: String, author: String, _ tmp: Int = 0) {
        super.init(title: title, author: author)
    }
    override func makeHTML() -&gt; String {
        var result:String = &quot;&quot;
        result += &quot;&lt;!DOCTYPE html&gt;\n&quot;
        result += &quot;&lt;html&gt;&lt;head&gt;&lt;title&gt;&quot;
        result += title
        result += &quot;&lt;/title&gt;&lt;/head&gt;\n&quot;
        result += &quot;&lt;body&gt;\n&quot;
        result += &quot;&lt;h1&gt;&quot;
        result += title
        result += &quot;&lt;/h1&gt;\n&quot;
        for item in content {
            result += item.makeHTML()
        }
        result += &quot;&lt;/ul&gt;\n&quot;
        result += &quot;&lt;hr&gt;&lt;address&gt;&quot;
        result += author
        result += &quot;&lt;/address&gt;\n&quot;
        result += &quot;&lt;/body&gt;&lt;/html&gt;\n&quot;
        return result
    }
    @main
struct Main {
    static func main() {
        let args:[String] = readLine()!.split(separator: &quot; &quot;).map{ String($0) }

        if args.count != 2 {
            print(&quot;Usage: java Main filename.html class.name.of.ConcreteFactory&quot;)
            print(&quot;Example 1: java Main listNaver.html listfactory.ListFactory&quot;)
            print(&quot;Example 2: java Main divNaver.html divfactory.DivFactory&quot;)
            exit(0)
        }

        var filename:String = args[0]
        var classname:String = args[1]

        var factory:AbstractFactory = AbstractFactory.getFactory(classname)

         var page:AbstractPage = factory.createNaverPage()
        page.output(filename: filename)

    }
}


// AbstractFactory클래스의 파일 출력 부분 구현 안됨.
// AbstractFactory클래스의 동적 클래스 생성부 if로 구현
</code></pre>
<h1 id="정리">정리</h1>
<p>템플릿 메소트 패턴과 팩토리 메소드 패턴에서 발전한 추상 팩토리 패턴이 있다.</p>
<p>하나의 클래스의 동작 흐름이나 인스턴스 생성흐름의 구조를 설계하는 두 패턴과는 달리 여러개의 클래스 흘름을 전체적으로 설계하는 패턴이 추상 팩토리 메소드 패턴이다.</p>
<p>여러개의 단위 객체들을 묶어서 추상화하고 기능 확장에 용이한 패턴이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Swift] 진법 변환]]></title>
            <link>https://velog.io/@fox_hunter/change-base</link>
            <guid>https://velog.io/@fox_hunter/change-base</guid>
            <pubDate>Wed, 30 Oct 2024 04:38:23 GMT</pubDate>
            <description><![CDATA[<h1 id="개요">개요</h1>
<p>코딩테스트같은 특수한 상황에서 기존의 10진수에서 값을 변경해야 할대가 있다. </p>
<p>그럴때 사용하면 좋을 듯한 진수 변환 방법이다.</p>
<h1 id="설명">설명</h1>
<h2 id="10진법에서-n진법으로">10진법에서 n진법으로</h2>
<p>방법은 어렵지 않다. 바로 String생성자를 이용하는것이다. radix 파라메터에 변환할 진법을 작성해주기만 하면된다.</p>
<p>정수의 표현방법은 10진법을 지원하므로 결과를 String으로 처리 해줘야 한다.</p>
<pre><code class="language-swift">let decimalNumber = 42
let binaryString = String(decimalNumber, radix: 2)
print(&quot;2진수로 변환: \(binaryString)&quot;)

let decimalNumber = 42
let octaString = String(decimalNumber, radix: 8)
print(&quot;8진수로 변환: \(octaString)&quot;)

let decimalNumber = 42
let hexaString = String(decimalNumber, radix: 16)
print(&quot;8진수로 변환: \(hexaString)&quot;)</code></pre>
<pre><code>2진수로 변환: 101010
8진수로 변환: 52
8진수로 변환: 2a    </code></pre><p>2, 8, 16진수만 적용가능한게 아니다, radix에 어떤 정수를 넣냐에 따라 다양한 진법으로의 변환이 가능하다.</p>
<p>혹시 진법으로 변환후 숫자에 알파벳이 들어가는데 소문자이면 안되는 경우는 그냥 문자열 뒤에 .uppercased()를 붙여주자</p>
<pre><code class="language-swift">let hexaString = String(decimalNumber, radix: 16).uppercaesd()
</code></pre>
<h2 id="n진법에서-10진법으로-변환">n진법에서 10진법으로 변환</h2>
<p>이전에 10진법의 변환을 위해서 String으로 사용했다면 다시 10진법으로 되돌리려면 Int 생성자를 이용한다. 이번에도 동일하게 radix라는 파라메터 이름을 사용한다.</p>
<pre><code class="language-swift">let binaryString = &quot;101010&quot;  // 2진수
if let decimalFromBinary = Int(binaryString, radix: 2) {
    print(&quot;2진수를 10진수로 변환: \(decimalFromBinary)&quot;)
}

let octalString = &quot;52&quot;  // 8진수
if let decimalFromOctal = Int(octalString, radix: 8) {
    print(&quot;8진수를 10진수로 변환: \(decimalFromOctal)&quot;)
}

let hexaString = &quot;4A&quot;  // 8진수
if let decimalFromHexa = Int(hexaString, radix: 16) {
    print(&quot;16진수를 10진수로 변환: \(decimalFromHexa)&quot;)
}</code></pre>
<pre><code>2진수를 10진수로 변환: 42
8진수를 10진수로 변환: 42
16진수를 10진수로 변환: 74</code></pre><h2 id="n진법에서-m진법은">n진법에서 m진법은?</h2>
<p>10진법이 아닌 n진법을 m진법으로 변경하려면 어떻게 해야 할까?????</p>
<p>그럴땐 n진법을 10진수로 바꾼 다음에 이 10진수를 m진법으로 변경해줘야 한다.</p>
<p>예를 들어 2진수를 16진수로 바꾼다고 하면</p>
<pre><code class="language-swift">let bnum: String = &quot;101010&quot;
let dnum = Int(bnum, radix: 2)
let hnum = String(dnum, 16) //2a</code></pre>
<pre><code>101010 (2) --&gt; 42 (10) --&gt; 2a (16)</code></pre><p>다음과 같이 2진법에서 10진법으로 변환후 10진수를 16진수로 변환하면된다.</p>
<h1 id="정리">정리</h1>
<p>10진법에서 n진법으로의 변환과 n진법에서 10진법으로의 변환을 다루어 보았다.</p>
<blockquote>
<ul>
<li>10진법에서 다른 진법으로 변환은 _<strong>String()생성자</strong>_를 </li>
</ul>
</blockquote>
<ul>
<li>n진법에서 10진법으로 변환은 _<strong>Int()생성자</strong>_를 이용한다.</li>
<li>10진법이 아닌 서로 다른 진법 변환의 경우 <strong><em>중간에 10진수로 한번 변환</em></strong>해줘야 한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Swift] String에 대해서]]></title>
            <link>https://velog.io/@fox_hunter/SwiftBasic-String</link>
            <guid>https://velog.io/@fox_hunter/SwiftBasic-String</guid>
            <pubDate>Tue, 29 Oct 2024 03:32:05 GMT</pubDate>
            <description><![CDATA[<h1 id="개요">개요</h1>
<p>swift를 사용하다보면 string을 사용할 일이 정말 많다.</p>
<p>Swift에서 readLine()으로 문자열을 입력받으면 옵셔널String타입으로 데이터를 받고, </p>
<p>SwiftUI에서 textfiled로 데이터를 받아와도 옵셔널String으로 받는등 입력을 받는경우에는 string이 많이 사용된다.</p>
<p>이런 중요한 string 이번에 한번 제대로 알아보자
(그리고 본 게시글에서 String과 문자열은 동일한 의미로 사용되니 유의할것)</p>
<h1 id="설명">설명</h1>
<h2 id="string의-기본-사용법">String의 기본 사용법</h2>
<pre><code class="language-swift">var greeting:String = &quot;hello, world!&quot;</code></pre>
<p>다음과 같이 &quot;&quot;쌍 따옴표 안에 저장하고 싶은 문자열을 입력한다.</p>
<h3 id="문자열-보간법">문자열 보간법</h3>
<p>만약 문자열 사이에 상황에 따라 다른 데이터를 넣고 싶다면 쌍따옴표 안에 ()를 쓰고 안에 데이터를 넣으면 된다.</p>
<pre><code class="language-swift">var name = &quot;gildong&quot;
var greeting:String = &quot;hello, \(name)!&quot;</code></pre>
<h3 id="문자열의-결합">문자열의 결합</h3>
<blockquote>
<p>+나 +=을 사용한다.</p>
</blockquote>
<pre><code class="language-swift">var str1 = &quot;hello&quot;
var str2 = &quot; world!&quot;</code></pre>
<p>이렇게 두개의 string이 있을때 결합은 다음과 같다.</p>
<pre><code class="language-swift">str1 += &quot; world!&quot;
//or
str1 += str2
//or
var str1 = &quot;hello&quot; + &quot; world!&quot;//처음부터 이렇게 선언</code></pre>
<h3 id="여러줄의-문자열">여러줄의 문자열</h3>
<pre><code class="language-swift">let banner = &quot;&quot;&quot;
          __,
         (           o  /) _/_
          `.  , , , ,  //  /
        (___)(_(_/_(_ //_ (__
                     /)
                    (/
        &quot;&quot;&quot;</code></pre>
<blockquote>
<p>&quot;&quot;&quot; 3개의 쌍따옴표를 사용해서 작성하면 된다.</p>
</blockquote>
<h2 id="raw-string">raw string</h2>
<p>(swift 5부터 사용가능하다.)</p>
<p>&quot;&quot;쌍따옴표 안에 문자를 작성할때 \같은 문자는 사용할 수 없다, \가 문자열의 종료나 ()같은 곳에 사용되면서 일종의 예약어 처럼 작동하고 있기 때문이다 이 불편함을 해소하기 위해서 #을 이용해서 Raw String이라는 것을 사용가능하다.</p>
<pre><code class="language-swift">// 일반 문자열에서는 백슬래시와 따옴표를 이스케이프해야 함
let regularString = &quot;This is a \&quot;normal\&quot; string with escape sequences like \\n.&quot;

// Raw String에서는 이스케이프 없이 그대로 작성 가능
let rawString = #&quot;&quot;&quot;
This is a &quot;raw&quot; string with no need for escape sequences like \n.
&quot;&quot;&quot;#</code></pre>
<pre><code>This is a &quot;raw&quot; string with no need for escape sequences like \n</code></pre><p>사용방법은 단순하다 &quot;&quot;쌍따옴표 맨앞과 맨뒤에 #을 붙여준다.</p>
<p>여기서 이전처럼 ()같이 \을 이용하고 싶으면 #을 붙여 주면 된다.</p>
<pre><code class="language-swift">let rstr1 = #&quot;raw string with escape line: \#n&quot;#
let rstr2 = #&quot;This is rstr1: &quot;\#(rstr1)!&quot; &quot;#</code></pre>
<pre><code>raw string with escape line:

This is rstr1: raw string with escape line: </code></pre><p>이렇게 /바로 뒤에 #을 붙여 사용한다. </p>
<p>그리고 여기서 #의 개수는 임의의 개수를 사용하면 된다.
하지만 #을 사용할때 개수가 동일해야 한다.</p>
<h2 id="string-접근">String 접근</h2>
<p>String은 일종의 Character타입의 배열이라고 봐도 무방하다 </p>
<pre><code class="language-swift">var greeting:String = &quot;hello&quot;</code></pre>
<p>문자열 greeting은 h, e, l, l, o 이렇게 5개의 문자가 합쳐진 배열처럼 취급이 가능하다.</p>
<p>완전히 배열과 동일하지는 않지만 이렇게 데이터를 처리하도록 도와주는 기능들이 여럿 존재한다.</p>
<h3 id="count">.count</h3>
<pre><code class="language-swift">var greeting = &quot;hello world&quot;
print(greeting.count)</code></pre>
<pre><code>5</code></pre><p>다음과 같이 count를 사용하면 문자열의 길이를 반환한다.</p>
<h3 id="firstindex">.firstIndex</h3>
<pre><code>let name = &quot;Marie Curie&quot;
let firstSpace = name.firstIndex(of: &quot; &quot;) ?? name.endIndex
let firstName = name[..&lt;firstSpace]</code></pre><p>name.firstIndex(of: &quot; &quot;)는</p>
<pre><code>Index(_rawBits: 262407)?</code></pre><p>이런 옵셔널 타입의 인덱스 값을 반환한다.
그래서 뒤에 ?? 를 이용해서 옵셔널 처리를 해준것이다.</p>
<p>이를 이용해서 string을 서브스크립트[ ]를 사용해서 내부 데이터를 접근 할 수 있다. </p>
<p>그러나 .firstIndex(of: &quot; &quot;)에서 &quot; &quot;문자열을 나누는 조건인데 여기에는 &quot;&quot;이 불가능하다. </p>
<p>즉 hello라는 문자열의 문자 하나하나씩(h,e,l,l,o) 나누는 것이 불가능하고 일련의 단위로 분리된 문자열로만 추출이 가능하다는 것이다.</p>
<p>문자단위로 추출하고 싶다면 배열로 만들어서 처리해야 한다.</p>
<h2 id="string-to-array">String to Array</h2>
<h3 id="array생성자를-이용">Array생성자를 이용</h3>
<pre><code>var name = &quot;gil dong&quot;
var names = Array&lt;Character&gt;(name)
print(type(of: names), names)</code></pre><pre><code>Array&lt;Character&gt; [&quot;g&quot;, &quot;i&quot;, &quot;l&quot;, &quot; &quot;, &quot;d&quot;, &quot;o&quot;, &quot;n&quot;, &quot;g&quot;]</code></pre><p>우선 가장 간단한 방법은 Array()생성자를 이용하는 방법이다. 타입지정은 &lt;&gt;각 괄호를 이용해 지정하면 되고 ()괄호안에 배열로 바꾸고 싶은 문자열을 넣으면 된다.</p>
<h3 id="split">.split()</h3>
<pre><code class="language-swift">var name = &quot;gil dong&quot;
var names = name.split(separator: &quot;&quot;)
print(names)</code></pre>
<pre><code>[&quot;g&quot;, &quot;i&quot;, &quot;l&quot;, &quot; &quot;, &quot;d&quot;, &quot;o&quot;, &quot;n&quot;, &quot;g&quot;]</code></pre><p>다음과 같이 .split(separator: &quot;&quot;)를 사용하면 문자 추출이 가능하다. (separator:) 옵션에서 다양한 분할 조건 삽입 가능</p>
<p>그러나 이 names배열이 Character타입이면 좋겠으나 SubString 타입이라는 것이다.</p>
<pre><code class="language-swift">var names1:[String] = names //error
var names1:[Character] = names</code></pre>
<p><a href="">SubString</a>타입은 쉽게 말하면 Swift에서 자른  메모리 낭비 적게 다루기 위한 타입이라고 생각하면 되는데, 이대로는 사용이 안되기 때문에 string이나 Character타입으로 사용하기 위해선 형변환이 필요하다.</p>
<p>for문 같은 반복문을 이용해서 원소 하나하나씩 형변환해줘도 되지만 배열의 .map{ }을 사용하면 한줄에 바로 변환이 가능하다.</p>
<pre><code class="language-swift">var names1:[String] = names.map{ String($0) }

//String타입이 아닌 배열로 변환하는 경우 한번 더 형변환 필요
var names2:[Character] = names.map{ Character(String($0))! }</code></pre>
<p>(여기서 $0의 형태가 생소하다면 <a href="https://velog.io/@fox_hunter/swift-Closures-%ED%81%B4%EB%A1%9C%EC%A0%80%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C">클로저</a>를 다시 공부하자)</p>
<p>여기서 Character(), String()은 각 타입의 생성자를 이용해서 <a href="https://velog.io/@fox_hunter/SharpSwift-StringToNumber">형변환</a> 해준것이다.</p>
<h2 id="유니코드">유니코드</h2>
<p>swift에서는 문자를 유니코드로 다룬다.
(기본값은 UTF-32, = String.unicodeScalars)</p>
<p>그래서 swift로 코딩을 할땐 한글 문자열 저장은 물론이고 변수의 이름도 한글로 하는 것이 가능하다. 또한 유니코드 이모지 🌍 를 글자처럼 사용하는것도 가능하다.</p>
<pre><code class="language-swift">var 인삿말 = &quot;안녕하세요&quot;
print(인삿말)</code></pre>
<pre><code>안녕하세요</code></pre><h3 id="유니코드로-출력">유니코드로 출력</h3>
<pre><code class="language-swift">let text = &quot;Hello, 🌍!&quot;
for scalar in text.unicodeScalars {
    print(&quot;\(scalar.value) &quot;, terminator: &quot; &quot;)
}</code></pre>
<pre><code>72  101  108  108  111  44  32  127757  33 </code></pre><p>.unicodeScalars를 붙여주면 앞서 설명했던 utf-32값으로 나타내 준다.</p>
<h3 id="utf-8-utf-16">UTF-8, UTF-16</h3>
<pre><code class="language-swift">let text = &quot;Hello, 🌍!&quot;
for scalar in text.utf8 {
    print(&quot;\(scalar) &quot;, terminator: &quot; &quot;)
}</code></pre>
<pre><code>72  101  108  108  111  44  32  240  159  140  141  33</code></pre><pre><code class="language-swift">let text = &quot;Hello, 🌍!&quot;
for scalar in text.utf16 {
    print(&quot;\(scalar) &quot;, terminator: &quot; &quot;)
}</code></pre>
<pre><code>72  101  108  108  111  44  32  55356  57101  33</code></pre><p>이처럼 같은 문자열이라 하더라도 유니코드의 형태가 달라지면 값이 달라지므로 주의할 것</p>
<h2 id="string-to-ascii">String to ASCII</h2>
<p>Swift는 C계열인 Objective-C를 뒤를 잇는 애플의 언어라서 그런지 아스키코드로 변환하는 기능을 지원한다.</p>
<pre><code>let text = &quot;Hello, World!&quot;

for character in text {
    if let ascii = character.asciiValue {
        print(&quot;\(character)의 ASCII 값: \(ascii)&quot;)
    } else {
        print(&quot;\(character)는 ASCII 범위에 없습니다.&quot;)
    }
}</code></pre><pre><code>H의 ASCII 값: 72
e의 ASCII 값: 101
l의 ASCII 값: 108
l의 ASCII 값: 108
o의 ASCII 값: 111
,의 ASCII 값: 44
  의 ASCII 값: 32
W의 ASCII 값: 87
o의 ASCII 값: 111
r의 ASCII 값: 114
l의 ASCII 값: 108
d의 ASCII 값: 100
!의 ASCII 값: 33    </code></pre><p>다만 이 경우 유니코드를 사용하는 swift의 문자열중에 ascii를 지원하지 않는 문자의 경우 변환이 불가능 하기 때문에 반환타입은 옵셔널이 된다. 
(아스키코드로 변환이 불가능한 것은 nil을 반환한다.)</p>
<h2 id="swift의-string은-값타입">Swift의 String은 값타입</h2>
<p>마지막으로 swift의 String은 다른언어의 String과는 다를 수 있다는 것을 설명하고 싶다.</p>
<p>예를 들어 java의 경우 string은 불변이고 인스턴스타입이기 때문에 아래 와 같은 경우 새로운 인스턴스가 새로 생긴다.</p>
<pre><code class="language-java">String hello = &quot;hello&quot;;
hello += &quot;world&quot;;</code></pre>
<p>겉으로 보기에는 hello변수 하나만 있는것처럼 보이지만 실제로는 &quot;world&quot;라는 문자열을 추가 하는 과정에서 새로운 인스턴스가 생성되고 hello가 &quot;world&quot;인스턴스를 가리키는 형태로 만들어 진다. 이러한 구조는 많은 문자열 추가가 일어나게 되면 성능에 영향을 미칠수 있다. 그래서 java에서는 StringBuilder를 사용하기도 한다.</p>
<hr>
<p>하지만 swift의 경우 string은 값타입이다.</p>
<p>그래서 string의 인스턴스 생성은 초기에 생성될때 한번만 생성된다, 문자열의 추가가 일어나더라도 무조건 새로운 인스턴스를 만들지 않는다.
(String을 Character타입의 배열이라고 생각하면 된다.)</p>
<pre><code class="language-swift">var str1 = &quot;Hello&quot;
str1 += &quot; World&quot;</code></pre>
<p>위 코드 처럼 동일한 상황이 일어난다면 swift는 새로운 인스턴스를 만들어서 가리키는게 아닌 기존의 str1의 메모리 공간을 확장해서 데이터를 삽입한다.</p>
<p>그래서 java에서 StringBuilder같은 존재를 swift에서는 따로 사용할 필요가 없다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Design Pattern] Strategy Pattern]]></title>
            <link>https://velog.io/@fox_hunter/Design-Pattern-Strategy-Pattern</link>
            <guid>https://velog.io/@fox_hunter/Design-Pattern-Strategy-Pattern</guid>
            <pubDate>Sat, 19 Oct 2024 03:45:43 GMT</pubDate>
            <description><![CDATA[<h1 id="1-개요">1. 개요</h1>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/154f6bfb-8459-4159-8696-85a0cc8e0b6f/image.png" alt=""></p>
<p>프로그램을 만들다 보면 여러가지의 알고리즘을 설계하고 상황에 따라 알고리즘을 동적으로 적용해야하는 경우가 있다. 그럴때 쓰면 좋은 패턴이 있다. 바로 스트래티지(strategy) 패턴이다.</p>
<p>심지어 잘만 설계하면 런타임에서 동적으로 변경하도록 만드는것도 가능하다.</p>
<h1 id="2-설명">2. 설명</h1>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/154f6bfb-8459-4159-8696-85a0cc8e0b6f/image.png" alt=""></p>
<h2 id="21-특징">2.1. 특징</h2>
<ul>
<li>client코드는 사용하고자 하는 알고리즘을 모두 알고 있어야 한다.</li>
<li>알고리즘이 변경될 때 마다 client코드가 변경됨을 해결하고자 함<h3 id="211-의도">2.1.1. 의도</h3>
다양한 알고리즘들을 캡슐화 해서 서로 교환 가능하도록 하게 구현하기 위함<h3 id="212-역할">2.1.2 역할</h3>
</li>
<li>Stretegy<ul>
<li>알고리즘들이 공통적으로 가져야 하는 인터페이스를 정의한다.</li>
<li>stretegyMethod()<ul>
<li>context가 알고리즘을 이용할 때 호출되는 메소드</li>
</ul>
</li>
</ul>
</li>
<li>Concreate Stretegy<ul>
<li>stretegyMethod를 구현한다.</li>
<li>구체적인 하나의 알고리즘을 의미한다.</li>
</ul>
</li>
<li>Context<ul>
<li>Stretegy를 이용하는 존재</li>
<li>contextMethod<ul>
<li>Strategy가 Context의 데이터를 접근할 때 사용하는 인터페이스</li>
<li>예제에서는 사용 안됨</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="22-예제코드">2.2. 예제코드</h2>
<h3 id="221-다이어그램">2.2.1. 다이어그램</h3>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/a92a0086-d365-4eb0-9ead-d8237b388551/image.png" alt=""></p>
<h3 id="222-java">2.2.2. Java</h3>
<pre><code class="language-java">public enum Hand {
    // 가위 바위 보를 나타내는 세 개의 enum 상수
    ROCK(&quot;바위&quot;, 0),
    SCISSORS(&quot;가위&quot;, 1),
    PAPER(&quot;보&quot;, 2);

    // enum이 가진 필드 
    private String name; // 가위 바위 보 손의 이름
    private int handvalue; // 가위 바위 보 손의 값 

    // 손의 값으로 상수를 얻기 위한 배열
    private static Hand[] hands = {
        ROCK, SCISSORS, PAPER
    };

    // 생성자 
    private Hand(String name, int handvalue) {
        this.name = name;
        this.handvalue = handvalue;
    }

    // 손의 값으로 enum 상수를 가져온다 
    public static Hand getHand(int handvalue) {
        return hands[handvalue];
    }

    // this가 h보다 강할 때 true
    public boolean isStrongerThan(Hand h) {
        return fight(h) == 1;
    }

    // this가 h보다 약할 때 true
    public boolean isWeakerThan(Hand h) {
        return fight(h) == -1;
    }

    // 무승부는 0, this가 이기면 1, h가 이기면 -1
    private int fight(Hand h) {
        if (this == h) {
            return 0;
        } else if ((this.handvalue + 1) % 3 == h.handvalue) {
            return 1;
        } else {
            return -1;
        }
    }

    // 가위 바위 보의 문자열 표현
    @Override
    public String toString() {
        return name;
    }
}</code></pre>
<hr>
<pre><code class="language-java">public class Player {
    private String name;
    private Strategy strategy;
    private int wincount;
    private int losecount;
    private int gamecount;

    // 이름과 전략을 받아서 플레이어를 만든다 
    public Player(String name, Strategy strategy) {
        this.name = name;
        this.strategy = strategy;
    }

    // 전략에 따라 다음 손을 결정한다
    public Hand nextHand() {
        return strategy.nextHand();
    }

    // 승리
    public void win() {
        strategy.study(true);
        wincount++;
        gamecount++;
    }

    // 패배
    public void lose() {
        strategy.study(false);
        losecount++;
        gamecount++;
    }

    // 무승부 
    public void even() {
        gamecount++;
    }

    @Override
    public String toString() {
        return &quot;[&quot;
            + name + &quot;:&quot;
            + gamecount + &quot; games, &quot;
            + wincount + &quot; win, &quot;
            + losecount + &quot; lose&quot;
            + &quot;]&quot;;
    }
}
</code></pre>
<hr>
<pre><code class="language-java">public interface Strategy {
    public abstract Hand nextHand();
    public abstract void study(boolean win);
}</code></pre>
<hr>
<pre><code class="language-java">import java.util.Random;

public class ProbStrategy implements Strategy {
    private Random random;
    private int prevHandValue = 0;
    private int currentHandValue = 0;
    private int[][] history = {
        { 1, 1, 1, },
        { 1, 1, 1, },
        { 1, 1, 1, },
    };

    public ProbStrategy(int seed) {
        random = new Random(seed);
    }

    @Override
    public Hand nextHand() {
        int bet = random.nextInt(getSum(currentHandValue));
        int handvalue = 0;
        if (bet &lt; history[currentHandValue][0]) {
            handvalue = 0;
        } else if (bet &lt; history[currentHandValue][0] + history[currentHandValue][1]) {
            handvalue = 1;
        } else {
            handvalue = 2;
        }
        prevHandValue = currentHandValue;
        currentHandValue = handvalue;
        return Hand.getHand(handvalue);
    }

    private int getSum(int handvalue) {
        int sum = 0;
        for (int i = 0; i &lt; 3; i++) {
            sum += history[handvalue][i];
        }
        return sum;
    }

    @Override
    public void study(boolean win) {
        if (win) {
            history[prevHandValue][currentHandValue]++;
        } else {
            history[prevHandValue][(currentHandValue + 1) % 3]++;
            history[prevHandValue][(currentHandValue + 2) % 3]++;
        }
    }
}
</code></pre>
<hr>
<pre><code class="language-java">import java.util.Random;

public class WinningStrategy implements Strategy {
    private Random random;
    private boolean won = false;
    private Hand prevHand;

    public WinningStrategy(int seed) {
        random = new Random(seed);
    }

    @Override
    public Hand nextHand() {
        if (!won) {
            prevHand = Hand.getHand(random.nextInt(3));
        }
        return prevHand;
    }

    @Override
    public void study(boolean win) {
        won = win;
    }
}</code></pre>
<hr>
<pre><code class="language-java">import java.util.Random;

public class RandomStrategy implements Strategy {
    private Random random;

    public RandomStrategy(int seed) {
        random = new Random(seed);
    }

    @Override
    public void study(boolean win) {
    }

    @Override
    public Hand nextHand() {
        return Hand.getHand(random.nextInt(3));
    }
}</code></pre>
<hr>
<pre><code class="language-java">public class Main {
    public static void main(String[] args) {
        if (args.length != 2) {
            System.out.println(&quot;Usage: java Main randomseed1 randomseed2&quot;);
            System.out.println(&quot;Example: java Main 314 15&quot;);
            System.exit(0);
        }
        int seed1 = Integer.parseInt(args[0]);
        int seed2 = Integer.parseInt(args[1]);
        Player player1 = new Player(&quot;Taro&quot;, new ProbStrategy(seed1));
        Player player2 = new Player(&quot;Hana&quot;, new RandomStrategy(seed2));
        for (int i = 0; i &lt; 10000; i++) {
            Hand nextHand1 = player1.nextHand();
            Hand nextHand2 = player2.nextHand();
            if (nextHand1.isStrongerThan(nextHand2)) {
                System.out.println(&quot;Winner:&quot; + player1);
                player1.win();
                player2.lose();
            } else if (nextHand2.isStrongerThan(nextHand1)) {
                System.out.println(&quot;Winner:&quot; + player2);
                player1.lose();
                player2.win();
            } else {
                System.out.println(&quot;Even...&quot;);
                player1.even();
                player2.even();
            }
        }
        System.out.println(&quot;Total result:&quot;);
        System.out.println(player1.toString());
        System.out.println(player2.toString());
    }
}
</code></pre>
<p>이 코드에서는 특이하게 열거형이 사용된다.</p>
<h3 id="223-swift">2.2.3. Swift</h3>
<pre><code class="language-swift">enum Hand: Int, CustomStringConvertible{
    case Rock = 0
    case Scissors = 1
    case Paper = 2

    // 열거형을 배열로 저장하여 값을 가져오기 쉽게 함
    private static var hands: [Hand] = [.Rock, .Scissors, .Paper]

    private var name: String {
        switch self {
        case .Rock: return &quot;ROCK&quot;
        case .Scissors: return &quot;SCISSORS&quot;
        case .Paper: return &quot;PAPER&quot;
        }
    }

    private var handValue: Int {
        switch self {
        case .Rock: return 0
        case .Scissors: return 1
        case .Paper: return 2
        }
    }

    static func getHand(_ handValue: Int) -&gt; Hand {
        return hands[handValue]
    }

    func isStrongerThan(_ h: Hand) -&gt; Bool {
        return fight(h) == 1
    }

    func isWeakerThan(_ h: Hand) -&gt; Bool {
        return fight(h) == -1
    }

    func fight(_ h: Hand) -&gt; Int{
        if self == h {
            return 0
        }
        else if ((self.handValue + 1) % 3 == h.handValue){
            return 1
        }
        else{
            return -1
        }
    }

    var description: String {
        switch self {
        case .Rock: return &quot;Rock&quot;
        case .Scissors: return &quot;Scissors&quot;
        case .Paper: return &quot;Paper&quot;
        }
    }
}</code></pre>
<hr>
<pre><code class="language-swift">protocol Strategy {
    func nextHand() -&gt; Hand
    func study(_ win: Bool)
}
</code></pre>
<hr>
<pre><code class="language-swift">class ProbStrategy: Strategy {
//    private var random: Int
    private var seed: Int

    private var prevHandValue: Int = 0
    private var currentHandValue: Int = 0

    private var history = [
        [1,1,1, ],
        [1,1,1, ],
        [1,1,1, ]
    ]

    init(_ seed: Int) {
        self.seed = seed
    }

    func nextHand() -&gt; Hand {
        let bet: Int = Int.random(in: 0...getSum(prevHandValue)-1)
        var handvalue: Int = 0

        if (bet &lt; history[currentHandValue][0]){
            handvalue = 0
        }else if(bet &lt; history[currentHandValue][0] + history[currentHandValue][1]){
            handvalue = 1
        }else{
            handvalue = 2
        }

        prevHandValue = currentHandValue
        currentHandValue = handvalue
        return Hand.getHand(handvalue)
    }

    private func getSum(_ handvalue: Int) -&gt; Int{
        var sum: Int = 0
        for i in 0..&lt;3{
            sum += history[handvalue][i]
        }
        return sum
    }


    func study(_ win: Bool) {
        if(win){
            history[prevHandValue][currentHandValue] += 1
        }else{
            history[prevHandValue][(currentHandValue + 1) % 3] += 1
            history[prevHandValue][(currentHandValue + 2) % 3] += 1
        }
    }
}
</code></pre>
<hr>
<pre><code class="language-swift">class RandomStrategy: Strategy {
    private let seed: Int

    init(_ seed: Int) {
        self.seed = seed
    }

    func study(_ win: Bool) {
    }
    func nextHand() -&gt; Hand {
        return Hand.getHand(Int.random(in: 0..&lt;3))
    }
}</code></pre>
<hr>
<pre><code class="language-swift">class WinningStrategy : Strategy{
    private var won : Bool = false
    private var prevHand: Hand?
    var seed: Int = 0

    init(_ seed: Int){
        self.seed = seed
    }

    func nextHand() -&gt; Hand {
        if(!won){
            prevHand = Hand.getHand(Int.random(in: 0..&lt;3))
        }
        return prevHand!
    }
    func study(_ win: Bool){
        won = win
    }
}
</code></pre>
<hr>
<pre><code class="language-swift">class Player: CustomStringConvertible{
    private var name: String
    private var strategy: Strategy
    var wincount: Int = 0
    var losecount: Int = 0
    var gamecount: Int = 0

    init(_ name: String,_ strategy: Strategy){
        self.name = name
        self.strategy = strategy
    }

    func nextHand() -&gt; Hand{
        return strategy.nextHand()
    }

    func win(){
        strategy.study(true)
        wincount += 1
        gamecount += 1
    }

    func lose(){
        strategy.study(false)
        losecount += 1
        gamecount += 1
    }
    func even(){
        gamecount += 1
    }

    var description: String{
        return &quot;[\(name): \(gamecount) games, \(wincount) wins, \(losecount) lose]&quot;
    }
}
</code></pre>
<hr>
<pre><code class="language-swift">@main
struct Main {
    static func main() {
        var seed1: Int = 10
        var seed2: Int = 100

        print(&quot;insert seed 1&gt;&gt; &quot;)
        seed1 = Int(readLine()!)!
        print(&quot;insert seed 2&gt;&gt; &quot;)
        seed2 = Int(readLine()!)!

        var player1 = Player(&quot;Taro&quot;, ProbStrategy(seed1))
        var player2 = Player(&quot;Hana&quot;, RandomStrategy(seed2))

        for i in 0...1000{
            var nextHand1 = player1.nextHand()
            var nextHand2 = player2.nextHand()

            if(nextHand1.isStrongerThan(nextHand2)){
                print(&quot;Winner: \(player1)&quot;)
                player1.win()
                player2.lose()
            }else if(nextHand2.isStrongerThan(nextHand1)){
                print(&quot;Winner: \(player2)&quot;)
                player1.lose()
                player2.win()
            }else{
                print(&quot;Even...&quot;)
                player1.even()
                player2.even()
            }
        }
        print(&quot;Total Result:\n\(player1)\n\(player2)&quot;)
    }
}
</code></pre>
<ul>
<li><p>swift의 열거형의 경우</p>
<ul>
<li>저장 프로퍼티를 사용하는것이 불가능해서 조금 다른형태로 구현하였다.</li>
</ul>
</li>
<li><p>toString의 경우</p>
<ul>
<li>다른 게시글에서 그러한것처럼 CustomStringConvertible프로토콜을 이용해서 description 프로퍼티를 이용해 동일한 형태를 이루도록 만들었다.</li>
</ul>
</li>
<li><p>random의 경우</p>
<ul>
<li>swift와 자바의 랜덤사용방법이 조금 다른점 참고</li>
</ul>
</li>
</ul>
<h1 id="3-정리">3. 정리</h1>
<p>스트래티지패턴은 여러가지 알고리즘을 사용하는 프로그램에서 알고리즘의 추가와 동적인 알고리즘 교체에 유용한 디자인 패턴이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Design Pattern] Composite Pattern]]></title>
            <link>https://velog.io/@fox_hunter/Design-Pattern-Composite-Pattern</link>
            <guid>https://velog.io/@fox_hunter/Design-Pattern-Composite-Pattern</guid>
            <pubDate>Fri, 18 Oct 2024 15:44:30 GMT</pubDate>
            <description><![CDATA[<h1 id="개요">개요</h1>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/aa77e743-9779-4cc1-bdfb-12657a230935/image.png" alt=""></p>
<p>Composite패턴은 우리가 많이 사용하는 컴퓨터의 파일관리자처럼 객체들을 트리 형태로 구조화 해서 데이터를 처리하는 패턴이다.</p>
<h1 id="설명">설명</h1>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/aa77e743-9779-4cc1-bdfb-12657a230935/image.png" alt=""></p>
<h3 id="의도">의도</h3>
<p>객체들을 트리 형태로 구조화 하여 그릇객체와 내용물객체를 동일하게 취급하기 위한 패턴</p>
<h2 id="예제코드">예제코드</h2>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/55934517-50c1-4055-937b-2a5b6481a3d0/image.png" alt=""></p>
<h3 id="역할">역할</h3>
<ul>
<li>Component<ul>
<li>Leaf와 Composite의 상위 클래스, 이들을 동일하게 취급 할 수 있도록 공통 인터페이스를 선언한다.</li>
</ul>
</li>
<li>Composite<ul>
<li>그릇을 나타내는 역할</li>
</ul>
</li>
<li>Leaf<ul>
<li>내용물에 해당하는 역할</li>
<li>여기에는 자식 component가 올 수 없음</li>
</ul>
</li>
<li>Client<ul>
<li>Composite패턴의 이용자 역할, Main클래스</li>
</ul>
</li>
</ul>
<h2 id="java">Java</h2>
<pre><code class="language-java">public abstract class Entry {
    private Entry parent;

    // 부모를 설정한다
    protected void setParent(Entry parent) {
        this.parent = parent;
    }

    // 이름을 가져온다 
    public abstract String getName();

    // 크기를 가져온다 
    public abstract int getSize();

    // 목록을 표시한다 
    public void printList() {
        printList(&quot;&quot;);
    }

    // prefix를 앞에 붙여 목록을 표시한다
    protected abstract void printList(String prefix);

    // 문자열 표시
    @Override
    public String toString() {
        return getName() + &quot; (&quot; + getSize() + &quot;)&quot;;
    }

    // 전체 경로를 가져온다 
    public String getFullName() {
        StringBuilder fullname = new StringBuilder();
        Entry entry = this;
        do {
            fullname.insert(0, entry.getName());
            fullname.insert(0, &quot;/&quot;);
            entry = entry.parent;
        } while (entry != null);
        return fullname.toString();
    }
}</code></pre>
<hr>
<pre><code class="language-java">import java.util.ArrayList;
import java.util.List;

public class Directory extends Entry {
    private String name;
    private List&lt;Entry&gt; directory = new ArrayList&lt;&gt;();

    public Directory(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public int getSize() {
        int size = 0;
        for (Entry entry: directory) {
            size += entry.getSize();
        }
        return size;
    }

    @Override
    protected void printList(String prefix) {
        System.out.println(prefix + &quot;/&quot; + this);
        for (Entry entry: directory) {
            entry.printList(prefix + &quot;/&quot; + name);
        }
    }

    public Entry add(Entry entry) {
        directory.add(entry);
        entry.setParent(this);
        return this;
    }
}</code></pre>
<hr>
<pre><code class="language-java">public class File extends Entry {
    private String name;
    private int size;

    public File(String name, int size) {
        this.name = name;
        this.size = size;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public int getSize() {
        return size;
    }

    @Override
    protected void printList(String prefix) {
        System.out.println(prefix + &quot;/&quot; + this);
    }
}</code></pre>
<hr>
<pre><code class="language-java">public class Main {
    public static void main(String[] args) {
        Directory rootdir = new Directory(&quot;root&quot;);

        Directory usrdir = new Directory(&quot;usr&quot;);
        rootdir.add(usrdir);

        Directory youngjin = new Directory(&quot;youngjin&quot;);
        usrdir.add(youngjin);

        File file = new File(&quot;Composite.java&quot;, 100);
        youngjin.add(file);
        rootdir.printList();

        System.out.println();
        System.out.println(&quot;file = &quot; + file.getFullName());
        System.out.println(&quot;youngjin = &quot; + youngjin.getFullName());
    }
}</code></pre>
<h3 id="실행-결과">실행 결과</h3>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/ab8020bb-3f17-4dd2-a3f2-2f6cff72ab4b/image.png" alt=""></p>
<h2 id="swift">Swift</h2>
<pre><code class="language-swift">class Entry: CustomStringConvertible{
    private var parent: Entry?

    init(){}

    func getName() -&gt; String{return &quot;&quot;}
    func getSize() -&gt; Int{return 0}
    func printList(_ prefix: String){}

    func setParent(_ parent: Entry){
        self.parent = parent
    }
    func printList(){
        printList(&quot;&quot;)
    }

    //toString
    var description: String {
        return &quot;\(getName()) (\(getSize()))&quot;
    }

    func getFullName() -&gt; String{
        var tempStr: String = &quot;&quot;
        var tmpEntry: Entry? = self
        repeat {
            if let e = tmpEntry {
                tempStr = &quot;/&quot; + e.getName() + tempStr
                tmpEntry = e.parent
            }
        } while tmpEntry != nil
        return tempStr
    }
}
</code></pre>
<p>CustomStringConvertible 프로토콜</p>
<ul>
<li>java의 toString처럼 print할때 출력되는 설정을 하기 위한 프로토콜</li>
</ul>
<hr>
<pre><code class="language-swift">class Directory: Entry{
    private var name: String
    private var directory = Array&lt;Entry&gt;()

    init(_ name: String){
        self.name = name
    }

    override func getName() -&gt; String {
        return name
    }
    override func getSize() -&gt; Int {
        var size: Int = 0

        for i in directory{
            size = size + i.getSize()
        }
        return size
    }
    override func printList(_ prefix: String){
        print(prefix + &quot;/&quot; + self.name)
        for entry in directory{
            entry.printList(prefix + &quot;/&quot; + name)
        }
    }

    func add(_ entry: Entry) -&gt; Entry{
        directory.append(entry)
        entry.setParent(self)
        return self
    }


}
</code></pre>
<hr>
<pre><code class="language-swift">class File: Entry {
    private var name: String
    private var size: Int

    init(_ name: String,_ size: Int) {
        self.name = name
        self.size = size
    }

    override func getName() -&gt; String {
        return name
    }

    override func getSize() -&gt; Int {
        return size
    }

    override func printList(_ prefix: String) {
        print(prefix + &quot;/&quot; + self.name)
    }
}
</code></pre>
<hr>
<pre><code class="language-swift">@main
struct Main {
    static func main() {
        let rootdir: Directory = Directory(&quot;root&quot;)
        let usrdir: Directory = Directory(&quot;usr&quot;)
        let youngjin: Directory = Directory(&quot;youngjin&quot;)

        rootdir.add(usrdir)
        usrdir.add(youngjin)

        let file: File = File(&quot;Composite.swift&quot;, 100)

        youngjin.add(file)
        rootdir.printList()

        print()
        print(&quot;file = \(file.getFullName())&quot;)
        print(&quot;youngjin = \(youngjin.getFullName())&quot;)

    }
}</code></pre>
<h3 id="결과">결과</h3>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/5cb1f903-3394-4898-b334-0756fd547f8c/image.png" alt=""></p>
<h4 id="entry클래스의-형태-고민">Entry클래스의 형태 고민</h4>
<p>자바의 경우 추상클래스 였으나 swift로 구현하면서 프로토콜과 일반 클래스 사이에서 고민했는데 swift의 프로토콜의 경우 저장프로퍼티를 허용하지 않고 구현또한 extension이라는 확장기능을 이용해야 해서 그냥 클래스로 구현하였다.</p>
<p>오버 라이딩 되어야 되는 부분은 그냥 0 또는 공백문자를 반환하는 형태로 만들었다.</p>
<h1 id="정리">정리</h1>
<p>tree형태로 객체를 다루어야 하는 상황이 온다면 나름 유용한 데이터 형태가 될 수 있다고 생각된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Design Pattern] Bridge 패턴]]></title>
            <link>https://velog.io/@fox_hunter/Design-Pattern-Bridge</link>
            <guid>https://velog.io/@fox_hunter/Design-Pattern-Bridge</guid>
            <pubDate>Fri, 18 Oct 2024 14:33:07 GMT</pubDate>
            <description><![CDATA[<h1 id="개요">개요</h1>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/e616ef04-3a15-408c-b11d-41c54e5d9c93/image.png" alt="">
프로그램의 내부 분류를 기능부분과 구현부분으로 나눠서 만든 형태를 브릿지 패턴이라고 한다.</p>
<p>이 두 종류의 클래스 계층이 혼합된다면 client코드가 플랫폼 의족적인 형태가 될 수 있다.</p>
<h1 id="설명">설명</h1>
<h3 id="의도">의도</h3>
<p>프로그램의 기능과 구현을 분리하여 둘의 구조가 독립적으로 변화될 수 있도록 한 패턴</p>
<h3 id="특징">특징</h3>
<ol>
<li>Bridge패턴의 특징은 기능 계층과 구현 계층을 분리하여 각각을 독립적으로 확장할 수 있다는 것<ol>
<li>기능의 추가는 기능 계층에서만 이루어 지고 구현계층에는 영향을 미치지 않는다.</li>
</ol>
</li>
<li>상속은 강한결합, 위임은 약한 결합<ol>
<li>상속을 사용하면 클래스 간의 관계가 고정되어 소스코드를 다시 쓰지 않는 한 변경할 수 없다.</li>
<li>위임을 사용한다면 교체가 용이하다.</li>
</ol>
</li>
<li>적용 시점<ol>
<li>프로그램 실행시 특정한 implementation이 선택되도록 할 때</li>
<li>Abstraction과 implementation이 별도의 계층구조를 가질 때</li>
<li>implementation부분을 수정하여도 client코드에는 영향이 없을 때</li>
</ol>
</li>
</ol>
<hr>
<h2 id="예제코드">예제코드</h2>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/e616ef04-3a15-408c-b11d-41c54e5d9c93/image.png" alt=""></p>
<h3 id="역할-설명">역할 설명</h3>
<ul>
<li>Abstraction<ul>
<li>기능 계층의 최상위 클래스</li>
</ul>
</li>
<li>Refined Abstraction<ul>
<li>Abstraction의 기본적인 기능외에 새로운 기능을 추가하여 확장한 클래스</li>
</ul>
</li>
<li>Implementor<ul>
<li>구현 계층의 최상위 클래스</li>
</ul>
</li>
<li>Concreate Implementor<ul>
<li>Implementor인터페이스를 구현한다.</li>
</ul>
</li>
</ul>
<h3 id="예제-코드-클래스-다이어그램">예제 코드 클래스 다이어그램</h3>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/9987d0a5-6092-4354-aa40-ad067fdfebe4/image.png" alt=""></p>
<h3 id="java">Java</h3>
<pre><code class="language-java">public class Display {
    private DisplayImpl impl;

    public Display(DisplayImpl impl) {
        this.impl = impl;
    }

    public void open() {
        impl.rawOpen();
    }

    public void print() {
        impl.rawPrint();
    }

    public void close() {
        impl.rawClose();
    }

    public final void display() {
        open();
        print();
        close();
    }
}</code></pre>
<hr>
<pre><code class="language-java">public abstract class DisplayImpl {
    public abstract void rawOpen();
    public abstract void rawPrint();
    public abstract void rawClose();
}</code></pre>
<hr>
<pre><code class="language-java">public class CountDisplay extends Display {
    public CountDisplay(DisplayImpl impl) {
        super(impl);
    }

    public void multiDisplay(int times) {
        open();
        for (int i = 0; i &lt; times; i++) {
            print();
        }
        close();
    }
}</code></pre>
<hr>
<pre><code class="language-java">public class StringDisplayImpl extends DisplayImpl {
    private String string;
    private int width;

    public StringDisplayImpl(String string) {
        this.string = string;
        this.width = string.length();
    }

    @Override
    public void rawOpen() {
        printLine();
    }

    @Override
    public void rawPrint() {
        System.out.println(&quot;|&quot; + string + &quot;|&quot;);
    }

    @Override
    public void rawClose() {
        printLine();
    }

    private void printLine() {
        System.out.print(&quot;+&quot;);
        for (int i = 0; i &lt; width; i++) {
            System.out.print(&quot;-&quot;);
        }
        System.out.println(&quot;+&quot;);
    }
}</code></pre>
<hr>
<pre><code class="language-java">public class Main {
    public static void main(String[] args) {
        CountDisplay d = new CountDisplay(new StringDisplayImpl(&quot;Hello, Korea.&quot;));
        d.multiDisplay(10);
    }
}

</code></pre>
<h3 id="swift">Swift</h3>
<pre><code class="language-swift">class Display{
    private var impl: DisplayImpl

    init(_ impl: DisplayImpl){
        self.impl = impl
    }

    func open(){
        impl.rawOpen()
    }
    func print(){
        impl.rawPrint()
    }
    func close(){
        impl.rawClose()
    }
    final func display(){
        open()
        print()
        close()
    }
}</code></pre>
<hr>
<pre><code class="language-swift">protocol DisplayImpl{
    func rawOpen()
    func rawPrint()
    func rawClose()
}</code></pre>
<hr>
<pre><code class="language-swift">class CountDisplay: Display {
    override init(_ impl: DisplayImpl) {
        super.init(impl)
    }

    func multiDisplay(_ times: Int){
        open()
        for i in 0...times {
            print()
        }
        close()
    }
}</code></pre>
<hr>
<pre><code class="language-swift">class StringDisplayImpl: DisplayImpl{
    private var string: String
    private var width: Int

    init(_ string: String) {
        self.string = string
        self.width = string.count
    }

    func rawOpen() {
        printLine()
    }
    func rawPrint() {
        print(&quot;| \(string) |&quot;)
    }
    func rawClose() {
        printLine()
    }
    func printLine() {
        print(&quot;+ \(String(repeating: &quot;-&quot;, count: width)) +&quot;)
    }
}
</code></pre>
<hr>
<pre><code class="language-swift">@main
struct Main {
    static func main() {
        var d: CountDisplay = CountDisplay(StringDisplayImpl(&quot;Hello Korea&quot;))
        d.multiDisplay(11)
    }
}</code></pre>
<h1 id="정리">정리</h1>
<p>브릿지 패턴은 프로그램의 구조를 기능부분과 구현부분으로 나눠서 그사이에 브릿지(다리)를 두고 이용하는 형태의 패턴이다. </p>
<p>브릿지 패턴을 이용하면 기능추가를 하기 위해서 기능계층에 코드 추가만 이루어 지면 되기 때문에 유지보수, 기능추가 부분에서 이점을 갖는 패턴이다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[카메라 이론]  카메라의 3요소]]></title>
            <link>https://velog.io/@fox_hunter/%EC%B9%B4%EB%A9%94%EB%9D%BC-%EC%9D%B4%EB%A1%A0-%EC%B9%B4%EB%A9%94%EB%9D%BC%EC%9D%98-3%EC%9A%94%EC%86%8C</link>
            <guid>https://velog.io/@fox_hunter/%EC%B9%B4%EB%A9%94%EB%9D%BC-%EC%9D%B4%EB%A1%A0-%EC%B9%B4%EB%A9%94%EB%9D%BC%EC%9D%98-3%EC%9A%94%EC%86%8C</guid>
            <pubDate>Wed, 16 Oct 2024 02:03:16 GMT</pubDate>
            <description><![CDATA[<h1 id="개요">개요</h1>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/9d4eed86-405d-42fb-865e-4311711d40a0/image.webp" alt=""></p>
<p>최근 카메라를 하나 구매하려고 찾아보던중 문득 카메라 이론에 대해서 대강만 알고 있지 잘 모른다고 생각되어서 최근에 공부한 아주 기본적인 카메라 이론에 대해서 정리해 보고자 한다.</p>
<h1 id="카메라의-촬영-원리">카메라의 촬영 원리</h1>
<p>카메라는 크게 바디와 렌즈로 나뉜다.</p>
<p>렌즈를 통해 들어온 빛이 카메라 이미지 센서에 인식이 되게 되고 이것을 촬영이라고 한다.</p>
<p>이후 설명할 3요소를 통해서 받아들이는 빛의 양이나 센서의 민감도등을 설정할 수 있다.</p>
<h1 id="노출">노출</h1>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/1efa52b2-99c9-4534-9938-c622413299ef/image.webp" alt=""></p>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/13ef888f-599b-4f28-997b-51a5b76dcf10/image.jpg" alt=""></p>
<p>아이폰의 카메라던 소니A7의 카메라가 되었던 모든 디지털 카메라에는 일정한 노출값을 유지하려고 노력한다.
위 이미지처럼 우리가 일반적으로 적당하다고 느끼는 적정값을 0으로 두고 -로 갈수록 어둡게, +로 갈수록 밝게 찍힌다.</p>
<p>이 노출값은 아래에서 설명할 3요소들에 연관이 있는데 아래 3요소들이 카메라의 빛의 양을 조절하는 존재들이기 때문이다.</p>
<h2 id="노출을-밝게-만드는-조건">노출을 밝게 만드는 조건</h2>
<ol>
<li>낮은 조리개 값
1.1. 조리개 값이 낮을 수록 카메라에서는 렌즈의 구멍(조리개)을 많이 열어서 빛을 많이 받아 들인다. </li>
<li>느린 셔터스피드
2.1 셔터스피트가 느릴수록 셔터가 열리고 닫히는 시간이 늘어나기에 빛을 많이 받아들일 수 있다.</li>
<li>높은 iso값
3.1 iso값이 높을수록 센서가 빛에 민감해 지므로 더 밝게 찍을 수 있다.</li>
</ol>
<h1 id="조리개">조리개</h1>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/14e77eff-bfac-400e-83b6-97c496c6bab2/image.jpeg" alt=""></p>
<p>조리개는 카메라 렌즈에 보통 기재되어있다. 보통의 렌즈는 f1.8~2.8사이의 값으로 많이 나온다.</p>
<p>위 이미지 처럼 조리개의 값은 f뒤에 숫자를 붙여서 사용하며 숫자가 클수록 빛을 적게 받아들인다.</p>
<hr>
<h2 id="피사계-심도">피사계 심도</h2>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/360260b7-4fd9-4cbe-a06b-10c15e5879ef/image.webp" alt=""></p>
<p>조리개값에 따라 사진에서 아웃포커싱의 강도 즉 피사계심도를 조절할 수 있는데</p>
<p>심도가 깊은수록 뒷배경이 선명하게 나오고 심도가 얕을수록 뒷 배경이 블러처리즉 흐리게 나온다.</p>
<p>사진이나 영상을 찍을때 심도를 고려해서 적정한 상황에 맞는 심도를 골라서 찍으면 좋다.</p>
<p>풍경을 찍을 때 -&gt; 심도를 깊게
인물을 찍을 때 -&gt; 심도를 얕게</p>
<p>위 사진은 아이폰의 인물사진 모드인데 이는 조리개를 이용하는것이 아닌 일반사진을 알고리즘을 통해 사람의 테두리를 인식해서 배경으로 인식되는 부분을 자연스럽게 블러처리하는 기술이다. (심도가 얕은 느낌만 보자)</p>
<hr>
<p>조리개는 심도를 조정하기 위해서 건드는경우가 많다. 조리개 값이 낮으면 심도가 얕고 조리개 값이 높으면 심도가 깊게 나온다.</p>
<p>하지만 f8이상의 값으로 올리는것은 비추이다. 카메라 별로 다르겠지만 f8을 넘어가면 자동으로 셔터스피트가 엄청 느려지거나 iso값이 엄청 올라가서 노이즈 많고 흔들리는 완성도 떨어지는 사진을 얻을 가능성이 높기때문이다.</p>
<h1 id="셔터스피드">셔터스피드</h1>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/88d60868-d9b3-48cc-8f4f-662f15fb338b/image.png" alt="">
셔터스피드는 이미지 센서가 빛은 받아들이는 시간을 제어한다.</p>
<p>이 값은 분수로 나타내는데 값이 클수록 셔터가 열려있는 시간이 늘어나 밝은 사진을 찍게 도와준다.</p>
<p>하지만 스피트값이 크다면(스피드가 느리다면) 그만큼 흔들림에 민감해 진다.&#39;</p>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/5ba42982-d8dd-44b6-b65d-ebca8784f40e/image.jpg" alt=""></p>
<p>이런 사진을 찍는 것은 셔터스피트값을 높게 하고 삼각대등을 이용해서 카메라를 고정해서 촬영한다.</p>
<p>밤에는 어둡기 때문에 셔터스피드를 느리게 해도 위와 같은 사진이 잘 나오지만 낮의 경우 속도를 느리게 하면 사진이 굉장히 밝게 (형체가 구분이 가지 않을 정도로)찍히는경우가 많아서 ND필터라는 일종의 카메라용 선글라스를 씌워서 사진을 찍는 경우도 있다.</p>
<h2 id="one-more-thing">one more thing</h2>
<p>우리가 밤에 폰으로 사진을 찍을때 사진이 많이 흔들리게 찍히는 이유가 바로 셔터스피드와 연관이 있는데
앞서 말했듯 스마트폰은 고정조리개로 이미 최대로 밝게 만들고 iso또한 이미지 품질이 저하되지 않을 정도로 최대한의 값으로 설정되어 있기에 폰카메라가 건들수 있는것은 셔터스피드가 제일 만만하기 때문이다.</p>
<p>그래서 스마트폰카메라가 빛을 많이 확보하기 위해서 셔터스피드를 느리게 가져가기 때문에 어두운곳에서의 사진이 많이 흔들린다. </p>
<p>따라서 어두운 곳에서 촬영할때 양질의 사진을 촬영하고자 한다면 삼각대는 필수라고 생각하면 된다.</p>
<h1 id="iso">iso</h1>
<p>iso는 센서의 민감도이다.
센서가 빛에 얼마나 민감하게 반응하느냐에 대한 값이다.</p>
<p><img src="https://velog.velcdn.com/images/fox_hunter/post/f6b3c87c-a5bb-40ad-9865-8e7a3000d1b8/image.jpg" alt="">
iso값의 단위는 카메라 별로 다르지만 보통은 위와 같은 단위로 존재한다고 보면된다.</p>
<p>iso값이 높으면 사진의 노이즈 값이 많아 지는데 적은 빛에 일반적인 노출값의 결과물을 내려면 센서가 그만큼 민감해지고 그만큼 피사체의 빛뿐만 아니라 방해되는 빛까지 많이 들어오기때문에 노이즈가 많아진다.</p>
<p>대부분 iso값은 조리개나 셔터스피드와는 다르게 밝기 외의 의도를 위해서 건들지는 않는다.
iso값이 높아지면 노이즈가 많아지는데 이러한 노이즈가 많은 사진을 원하는 경우 라이트룸 같은 후보정 프로그램을 이용해서 노이즈를 임의로 넣으면 되지 굳이 원본 자체를 노이즈 많게 찍을 필요가 없기 때문이다.</p>
<p>iso값은 보통 12800이상은 가져가지 않는다, 빛이 많은 낮이라면 100으로 가는게 제일 좋다. 
카메라 별로 노이즈가 많아 지는 iso값은 다르겠지만 요즘 카메라 기준으로 12800 전후로 노이즈가 생기는게 눈에 보일것이다.</p>
<p>본인이 iso조절이 가능한 카메라를 사용한다면 노이즈가 보기 싫을정도의 값이 어느정도의 iso값에서부터 나타나는지 미리 파악을 하는게 좋다.</p>
<p>구형카메라의 경우 iso가 3200만 넘어가도 노이즈가 많이 생기는 경우가 있기에 자신의 카메라의 한계는 어디가지인지 알고있는것이 좋다.</p>
<h1 id="정리">정리</h1>
<p>카메라의 3요소에는 조리개, 셔터스피드, iso값이 있다.</p>
<p>iso값은 무조건 낮을수록 좋고, 이외의 값은 본인이 원하는 사진에 맞춰 값을 설정해서 찍으면 된다.</p>
<p>요즘에 스마트폰에도 프로 모드라고 해서 iso나 셔터스피드, 심지어 조리개도 가변조리개를 지원하는 스마트폰도 나온다. 보통의 스마트폰은 iso나 셔터스피드를 자동으로 조절하기 때문에 삼각대같은 추가 장비를 사용한고 위의 이론들을 잘 적용한다면 스마트폰으로도 충분히 멋진 작품을 찍을 수 있을것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[swift] while let]]></title>
            <link>https://velog.io/@fox_hunter/while-let</link>
            <guid>https://velog.io/@fox_hunter/while-let</guid>
            <pubDate>Sat, 12 Oct 2024 08:48:11 GMT</pubDate>
            <description><![CDATA[<h1 id="개요">개요</h1>
<p>옵셔널바인딩과 반복문을 동시에 사용하는 방법이 있다. 바로 <strong><em>while-let</em></strong> 이다. </p>
<p>while-let은 옵셔널 바인딩에서 사용한 if-let과 반복문이 합쳐진 형태라고 보면 된다.</p>
<h1 id="소스코드">소스코드</h1>
<h2 id="기본적인-형태">기본적인 형태</h2>
<pre><code class="language-swift">var numbers: [Int?] = [1, 2, nil, 4, nil, 6]

while let number = numbers.popLast() {
    print(&quot;Number: \(number)&quot;)
}</code></pre>
<pre><code>Number: Optional(6)
Number: nil
Number: Optional(4)
Number: nil
Number: Optional(2)
Number: Optional(1)    </code></pre><p>기능으로 놓고 보면 ForEach문과 if-let이 합쳐진 기능에 가깝다. </p>
<h2 id="여러개의-옵셔널바인딩">여러개의 옵셔널바인딩</h2>
<pre><code class="language-swift">var numberStrings = [&quot;123&quot;, &quot;456&quot;, &quot;789&quot;, &quot;101112&quot;, &quot;131415&quot;, &quot;banana&quot;]
var index = 0

while let first = Int(numberStrings[index]),
      let second = Int(numberStrings[index + 1]),
      index &lt; numberStrings.count - 1 {
    print(&quot;첫 번째: \(first), 두 번째: \(second)&quot;)
    index += 2
}</code></pre>
<pre><code>첫 번째: 123, 두 번째: 456
첫 번째: 789, 두 번째: 101112</code></pre><p>위 코드는 배열의 원소를 2개씩 묶어서 출력하는 코드이다. </p>
<p>옵셔널 바인딩으로 인해 int로 변환 불가능해서 nil을 반환하는 원소의 쌍인 경우에는 출력이 안된다.</p>
<p>그리고 let의 바인딩 뒤에 붙은 조건을 보면 알수 있듯 while-let에는 while처럼 조건을 적용하는것이 가능하다.</p>
<h2 id="조건이-붙은-while-let">조건이 붙은 while-let</h2>
<pre><code class="language-swift">var numbers: [Int?] = [1, 2, nil, 4, nil, 6]
var index = 0

while let number = numbers[index], number &lt; 4 {
    print(&quot;값: \(number)&quot;)
    index += 1
}</code></pre>
<pre><code>값: 1
값: 2</code></pre><p>이처럼 바인딩의 뒤에 ,로 구분해서 조건을 입력해 준다.</p>
<p>무조건 바인딩 구문 뒤에 와야된다는 점을 유의하자.</p>
<h3 id="응용-여러줄의-문자열-입력">응용) 여러줄의 문자열 입력</h3>
<pre><code class="language-swift">var inputs: [String] = []
while let command = readLine(), !command.isEmpty {
    inputs.append(command)
}
print(inputs)</code></pre>
<p>백준같은 문제들을 풀다보면 문자열을 여러개 입력받아야 하는 경우가 자주 생긴다. </p>
<p>그럴때 사용하면 유용한 코드인데 readLine()으로 문자열을 입력받되 데이터가 빈 경우에서의 줄바꿈 (=엔터) 가 오면 입력 while-let을 중단하는 구조이다.</p>
<h1 id="정리">정리</h1>
<p>옵셔널 바인딩과 반복문이 합쳐진 while-let이 있다.</p>
<p>while-let의 기능은 Foreach와 옵셔널 바인딩이 합쳐진 형태에 가깝다.</p>
<p>동시에 여러개의 바인딩을 하는것도 가능하고, while처럼 조건을 하나 거는것도 가능하다.</p>
<p>위 특징을 이용해서 여러개의 문자열을 입력받아야 하는 경우에도 활용 할 수 있다.</p>
]]></description>
        </item>
    </channel>
</rss>