<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>bono.log</title>
        <link>https://velog.io/</link>
        <description>iOS 개발자 보노</description>
        <lastBuildDate>Sat, 01 Jun 2024 11:49:53 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>bono.log</title>
            <url>https://velog.velcdn.com/images/haeun_/profile/1eb5b12f-d0ff-4920-a91e-2647b36ba083/image.JPEG</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. bono.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/haeun_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[iOS 개발자가 서비스에 가지는 책임]]></title>
            <link>https://velog.io/@haeun_/%ED%99%94%EB%A9%B4-%EA%B5%AC%ED%98%84%EC%9D%B4-%EA%B0%9C%EB%B0%9C%EC%9E%90%EC%9D%98-%EC%A0%84%EB%B6%80%EC%9D%BC%EA%B9%8C</link>
            <guid>https://velog.io/@haeun_/%ED%99%94%EB%A9%B4-%EA%B5%AC%ED%98%84%EC%9D%B4-%EA%B0%9C%EB%B0%9C%EC%9E%90%EC%9D%98-%EC%A0%84%EB%B6%80%EC%9D%BC%EA%B9%8C</guid>
            <pubDate>Sat, 01 Jun 2024 11:49:53 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요! 보노입니다. </p>
<p>아주 오랜만에 포스팅을 하게 되었습니다. </p>
<p>본 포스팅은 최근 진행한 미니 프로젝트에서 <code>기획자</code>, <code>디자이너</code>, <code>개발자</code>를 <strong>대상</strong>으로 
<strong>1) 제가 이해한 개발자의 책임</strong>에 대해 나누고 
이를 토대로 <strong>2) 프로젝트 구조 설계를 제시</strong>한 <code>발표</code> 내용 입니다.</p>
<p>발표 당시의 제목은 </p>
<blockquote>
<p>모두가 이해한 프로젝트</p>
</blockquote>
<p>였습니다.</p>
<p>포스팅을 작성하며 보다 내용을 명확히 하기 위해 <strong>iOS 개발자가 서비스에 가지는 책임</strong>으로 제목을 변경하였습니다. </p>
<p>본문에 들어가기 앞서, iOS 개발자의 책임은 무엇일까요? 
개인적인 깨달음을 담은 글이니 가볍게 읽어주시면 좋을 것 같습니다.
(본 포스팅은 본문도 길지만 서론도 몹시 긴 글이 될 것 같습니다..!)</p>
<h3 id="개발자는-왜-이렇게-아는-게-많아야-할까">개발자는 왜 이렇게 아는 게 많아야 할까</h3>
<p>과거에 저는 <strong>개발자가 가지는 책임에 대한 이해가 없어</strong> 방대한 지식을 무작위로 접하였습니다. </p>
<p>Swift 문법, CS, 알고리즘,
UIKit, SwiftUI, MVC, MVVM-C, Ribs, TCA 등의 아키텍처 방법론들, 모듈화, Tuist, RxSwift, Combine, 객체지향 프로그래밍, 함수형 프로그래밍과 같은 것들 말입니다.</p>
<p>수많은 지식에 대한 학습은 추가적인 학습을 필요로 했고, 어디까지 공부해야 하는 건지 몰라 스스로를 절망과 혼란에 빠뜨렸습니다. </p>
<p>점점 <strong>서비스 구현</strong>과 <strong>학습</strong>은 별개의 영역이 되어갔습니다. </p>
<p>그러니 </p>
<blockquote>
<p>아키텍처에 대한 학습은 3년 차 이하가 필요를 느끼기 어려운 영역인 것 같다</p>
</blockquote>
<p>며 한탄하기도 했습니다. 
(이에 대한 생각이 어떻게 바뀌었는 지는 별도의 포스팅으로 마련하고자 합니다)</p>
<p>그렇게 헛도는 느낌을 안고 개발을 지속하던 어느 날, <code>클린 아키텍처</code>를 읽다가 제가 개발자의 역할에 대해 오해하고 있던 지점을 벼락치듯 깨닫게 되었습니다. </p>
<p>깨달음을 준 문장은 아래와 같습니다. </p>
<blockquote>
<p><strong>소프트웨어 개발자인 당신도 이해관계자임을 명심하라.</strong> 당신은 소프트웨어를 안전하게 보호해야 할 책임이 있으므로 당신 역시도 이해관계가 있다. 당신의 역할 중 하나며, 당신의 책무 중 하나다.</p>
</blockquote>
<p>소프트웨어를 안전하게 지킬 책임이 있음을 강조하는 문장이었으나, 제게 주는 울림은 좀 더 본질적인 것이었습니다.</p>
<p>왜 개발자가 <strong>서비스를 위하는 입장</strong>임을 놓치고 있었지?</p>
<p>스스로 질문하자 답답하게 느꼈던 모든 것들이 엮이고 풀리는 것을 느꼈습니다. </p>
<p>여러 기술적인 논의, 아키텍처, 학습의 필요를 진정으로 느낌과 동시에 협업, 커뮤니케이션을 강조하는 이유에 대해서도 단숨에 이해할 수 있었습니다. </p>
<p>지난 협업 과정에서 개발자의 책임을 다하고 있지 못했구나 반성하며 학습 방향을 점검하기도 했습니다.</p>
<p>구현과 학습을 별개로 가져가는 것에는 변함이 없지만, 
무엇을 위해 학습하는가에 대한 이해가 생기니 더 이상 모든 학습이 막연하지 않았습니다. 
더 나아가 협업에 임하는 태도도 바뀐 것을 느낄 수 있었습니다. </p>
<p><strong>디자인에 공부를 시작한 것</strong>도 개발자의 책임에 대해 고민한 이후의 변화라고 볼 수 있을 것 같습니다. </p>
<h3 id="발표의-목적">발표의 목적</h3>
<p>본 발표는 팀원들에게 <strong>개발자의 책임</strong>에 대해 전하는 것이 가장 큰 목적이었습니다.
그를 통해,
<strong>1) 공동의 목표를 점검하여 개발 단계에서 각 파트간 건강한 커뮤니케이션 문화를 구축</strong>하고 
(어려워서 안돼요. 와 같은 개발자 갑질 방지)
<strong>2) 프로젝트 설계의 필요를 설득 하여
3) 서비스를 위하는 개발 논의</strong>를 이끌고자 했습니다.</p>
<p>더 나아가 <strong>최종적이고 개인적인 목표</strong>는
개발자의 고민이 그리 막연한 것이 아님을 전하여
파트에 관계 없이 <strong>모든 팀원들이 개발에 참여하도록</strong> 하는 것 이었습니다.
(개발 직군이 아닌 팀원들도 개발에 대한 열정이 있는 상황이었기 때문에 생긴 욕심이었습니다)</p>
<h3 id="내용-요약">내용 요약</h3>
<p>발표의 내용을 요약하자면 아래와 같습니다.</p>
<ul>
<li><strong>개발자는 기획자, 디자이너가 그러하듯 서비스를 위하는 입장</strong> : 핵심</li>
<li>뷰 짜기는 개발자의 목표를 이루기 위한 수단으로, 목표가 될 수 없음</li>
<li>개발자는 서비스를 잘 만들기 위해 도메인을 구성하는 구체적 수단을 선택하게 됨</li>
<li>뷰도, 데이터도 도메인을 구성하기 위한 수단일 뿐임 </li>
<li>수단이 목적을 덮치면 당연하게도 프로젝트가 망가짐. 개발자는 그를 경계하게 됨.</li>
<li>그러한 노력의 일환으로, 통제 가능한, 유연한 코드를 만들어 보자 (설계 제시)</li>
</ul>
<h3 id="개인적인-도전">개인적인 도전</h3>
<p>과거 <strong>배움을 얻을 수 있는 곳에서 일하고 싶다</strong> 생각했습니다. 
제가 가진 논의점들에 해답을 제시하는 집단에 소속되고 싶어 열심히 공부했습니다. </p>
<p>하지만 이제는 어딜 가도 건강하게 교류하는 사람이 되고 싶습니다. 
습득한 지식이 있다면 그 필요를 모르는 팀원에게 그를 <strong>설득할 수 있는 팀원</strong>이 되고 싶어 공부하게 됩니다. </p>
<p>본 발표는 제가 가진 개발자에 대한 이해를 전달하고, 개발 경험이 없는 팀원에게도 설계의 필요 전달 합니다.</p>
<p><strong>제가 가진 지식의 필요를 설득시킬 수 있는 지 검증</strong>하는 자리였습니다.</p>
<p><strong>결과적으로</strong>, 해당 발표를 들은 팀원들은 전부 개발에 참여하였습니다.</p>
<p>모든 팀원들이 개발에 참여하여 개발자로서 고민하고 개발자로서 개발의 재미를 느끼고 논의하여 MVP 개발을 끝마쳤습니다. </p>
<blockquote>
<p>이 포스팅을 열정적인 팀원들, 개발자로서 걷고자 하는 길이 옳음을 확신하게 만들어준,
<code>고스트</code>, <code>구름</code>, <code>로셸</code>, <code>브리</code>, <code>에디</code>에게 바칩니다! </p>
</blockquote>
<p>피드백이나 궁금한 점 남겨주시면 감사히 읽겠습니다! </p>
<p>감사합니다! </p>
<p>(아래부터의 본문은 팀원들에게 발표한 발표 내용에 해당합니다)</p>
<h1 id="모두가-함께하는-프로젝트">모두가 함께하는 프로젝트</h1>
<p><img src="https://velog.velcdn.com/images/haeun_/post/e92a50f3-183b-4571-a4e6-ba67a718a112/image.png" alt=""></p>
<p>우리 팀은 <code>기획자 2</code>, <code>디자이너 2</code>, <code>개발자 2</code>로 이루어졌다. 
그리고 모두가 파트에 대한 엄밀한 구분 없이 기획, 디자인 과정에 참여했다. </p>
<p>기획을 시작으로 도메인이 결정되자 디자인을 시작했고, High-Fi가 완성되자 MVP를 목표로 개발에 들어섰다. </p>
<p>수시로 우리의 논의에 사용자가 고려되고 있는 지, 해당 디자인이 우리 앱의 도메인을 해치지는 않는 지 점검했다.</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/40692f79-6fa3-4032-9658-93a02d9fdea8/image.png" alt=""></p>
<p>하지만 모든 팀원들이 개발을 앞두곤 전혀 다른 영역에 들어선 듯 막연함을 느꼈다.
더 나아가 기획자와 디자이너가 <code>서비스를 잘 만들기 위해 하는 노력</code>을 이해하던 것과 달리 
개발자가 <code>서비스를 잘 만들기 위해 하는 노력</code>은 가늠하지 못했다.</p>
<p>그러니 <strong>개발이 도메인에서 벗어나고 있지는 않은 지 질문하고 논의할 힘을 잃었다</strong>. </p>
<h1 id="왜-개발자의-역할에-이입하지-못할까">왜 개발자의 역할에 이입하지 못할까?</h1>
<p><img src="https://velog.velcdn.com/images/haeun_/post/b88e8702-a5a8-4390-97a6-add65c1f5726/image.png" alt="">
기획자나 디자이너, 개발자 모두 서비스를 <code>잘 만들고자</code> 노력한다는 점에서 같은 선상에 있다고 생각한다. </p>
<blockquote>
<p>그렇다면 개발자가 서비스를 <code>잘 만들고자</code> 하는 시도란 무엇일까?</p>
</blockquote>
<h2 id="개발자가-서비스를-잘-만들고자-하는-노력">개발자가 서비스를 잘 만들고자 하는 노력</h2>
<p><img src="https://velog.velcdn.com/images/haeun_/post/92be3105-0a39-4f1b-a33a-f7917f22a98f/image.png" alt=""></p>
<blockquote>
<p>기획자, 디자이너, 개발자는 서비스의 이해 관계자이다. </p>
</blockquote>
<p>서비스를 이해하며, 잘 되게 하기 위해 노력한다. </p>
<p>하지만 개발자가 서비스를 위하는 방식은 종종 기획자 디자이너에게 서비스에서 동떨어진 것으로 추측되곤 한다다. <strong>때론 개발자 또한</strong> 서비스를 위하는 노력과 개발, 구현을 분리하여 이해한다. </p>
<p>코드를 통해 서비스를 잘 만들고자 한 시도를 직관적으로 이해하기 어렵기 때문이다. </p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/dd317c25-7585-470b-92dd-328c6feb98b2/image.png" alt=""></p>
<p>특히나 <strong>iOS 개발자</strong>라면 <strong>디자인을 토대로 화면을 구현하는 일</strong>이 중요하게 여겨지니, 
개발자가 디자인의 후발 주자로서 디자인을 구현하는 역할이라 판단될 여지가 많은 것 같다. </p>
<p>바로 이러한 점이 개발자의 역할을 뷰에 한정시켜 전체적인 프로젝트 코드가 무엇을 위하는 지 혼동되게 만든다. </p>
<p>물론, iOS 개발자에게 화면을 만들고 매끄러운 사용감을 제공하는 일은 거의 전부라고 느껴질 만큼 업무의 대부분을 차지하는 일이다. </p>
<blockquote>
<p>하지만 뷰 구현이 무엇을 위해 진행되는 일인지 이해하여야 한다. </p>
</blockquote>
<p>그렇지 않으면 개발자는 곧 뷰 구현자가 되어 서비스의 이해 관계자로서 역할을 이해할 수 없게 된다. </p>
<blockquote>
<p>마치 iOS 개발자가 뷰를 위한 개발자라고 착각하게 만든다.</p>
</blockquote>
<h1 id="개발자도-서비스를-위한다">개발자도 서비스를 위한다</h1>
<p><img src="https://velog.velcdn.com/images/haeun_/post/086dba43-affd-4844-a862-922a00719f8e/image.png" alt="">
서비스 이해 관계자라는 표현을 자주 언급하는 이유는, 기획, 디자인, 개발 전부가 공동의 목표를 가진다는 점을 강조하기 위함이다. </p>
<p>서비스를 위하는 목표, 서비스의 정체성인 도메인을 <code>잘</code> 만들고자 하는 시도가 바로 그것이다. </p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/2a23c0f0-e879-403f-93fd-252de2bfd282/image.png" alt=""></p>
<p>모든 팀원들은 서비스를 <code>잘</code>  만들기 위해 고민한다. 서비스를 <code>잘</code> 만들기 위해 결정한 뷰에 개발자의 역할을 한정지으면 개발자는 디자이너의 후속주자로 이해되기 쉽다.</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/93ccfb17-3b40-454b-a234-474304c53ace/image.png" alt=""></p>
<p>개발자는 디자인의 후속 주자가 아닌, 도메인을 잘 만들기 위해 노력하는 서비스의 이해 관계자이다. </p>
<p>개발자는 기획자, 디자이너와 공통의 도메인을 공유하고 그를 잘 만들기 위해 여러가지 합리적 판단을 거친다. 더 나아가 서비스 과정에서 변하기도 하는 유즈케이스, 화면 등에 대비하기 위해 코드 생태를 유연하게 만들고자 노력한다. 기능 추가 상황에 대비하는 것 또한 마찬가지다.</p>
<p>개발자는 빈번하게 변하는 구체적 결정들 속에 도메인을 지키고 갈아끼우는 역할을 수행한다. </p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/143da2bd-3788-4c76-8fc0-755e8c8d800c/image.png" alt=""></p>
<p>개발자의 책임은 <strong>도메인을 <code>잘</code> 만들자</strong> 이다.</p>
<p>자유로운 시도를 위해선 기초가 필요하다. <code>선택할 수 있는 도구의 수</code>를 늘리기 위해선 새로운 기술 학습이 필요하다.</p>
<p>하지만 여전히 <strong>도메인을 <code>잘</code> 만들자</strong>란 말 만으론 개발자가 실제로 고민하는 부분을 상상하기 어렵다. </p>
<p>개발자의 입장에서 <strong>도메인</strong>을 잘 만든다는 것은 어떤 고민을 의미할까?</p>
<h1 id="도메인이란">도메인이란?</h1>
<p><img src="https://velog.velcdn.com/images/haeun_/post/3b9335c0-b4af-48b2-9008-666e106f3ae2/image.png" alt=""></p>
<p>우선 <strong>Domain</strong>이란 무엇일까. 해당 키워드는 서술 상황에 따라 묘사되는 범위에 차이가 있는 것 같다. </p>
<p>그러니 현재의 발표에서는 Domain을 </p>
<blockquote>
<p>서비스 이해 관계자 모두가 공유하고 있는 앱의 컨셉, 정체성</p>
</blockquote>
<p>이라고 정의하자. </p>
<hr>
<p>본 발표에서 등장하는 키워드가 타 자료에서 언급되는 키워드와 다른 맥락을 지닐 수 있음을 경고하고 싶다.</p>
<p>하지만 대게 개발자 월드 속 키워드가 그런 것 같다. </p>
<p>어떤 방식으로든 이해했다면, 앞으로 구체화 해 나가면 된다 생각한다. 그러니 너그러운 마음으로, 가볍게 봐 주었으면 한다.</p>
<hr>
<p><img src="https://velog.velcdn.com/images/haeun_/post/31b3abef-2cf6-427a-a79c-ed31c296af22/image.png" alt=""></p>
<p><strong>Domain</strong>에는 사용자가 서비스와 어떻게 교류하길 바라는 지에 대한 <code>UseCase</code>와, 시스템에서 다루는 주요 개체나 개념을 의미하는 <code>Entity</code>가 존재한다. </p>
<p>서비스 이해관계자라면 <strong>모두가 이해하고 공유하는 내용</strong>이다.</p>
<p>즉, 개발자만 아는 키워드로 묘사되는 것이 아니라, 모두가 알고 있는 추상적인 내용들로 구성된 것들을 말한다. </p>
<p>구체적인 예시로 알아보자. </p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/6dce92e8-d2d4-4dce-9b98-bb54d0964883/image.png" alt=""></p>
<p>우리 서비스의 경우 세 가지의 <strong>UseCase</strong>로 <strong>Domain</strong>을 분류할 수 있다. 
이 중 <code>기록UseCase</code>란 무엇이고 이를 만들기 위해 어떠한 결정이 필요될까.</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/2ae4362b-4eb5-40a6-8446-71edc09c66ff/image.png" alt=""></p>
<p><code>기록UseCase</code>는 사용자가 야구 경기의 다양한 기록을 입력하고 저장하며, 이를 실시간으로 화면에 표시하는 과정을 말한다. 이러한 과정은 사용자에게 최신 경기 정보를 제공하고, <code>기록</code>을 체계적으로 관리할 수 있도록 도와준다. </p>
<p><code>기록</code> 과정에는 어떠한 정보가 필요한 지 알아야 한다. (<strong>Entity</strong>)</p>
<p><strong>Entity</strong>는 서비스 이해 관계자 <strong>모두가 알고있는 앱의 다양한 주제 정보</strong>를 말한다.  </p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/a73946ff-a9a6-4fa8-8ff5-578b2a4d014d/image.png" alt=""></p>
<p>여기까지는 기획자, 디자인, 개발자 모두가 공유하고 있는 절대적인 앱의 정체성 이자 Domain 영역에 해당하는 내용이다.</p>
<p>구체적인 방법은 없고, 추상적인 내용이라 코드 상에선 <code>추상적 계층</code>, 또는 <code>고수준</code>이라 부른다. </p>
<p>개발자는 해당 도메인을 구현하기 위해 <code>구체적인 수단</code>, 즉 <strong>구현 계층, 저수준</strong>을 구현해야 한다. </p>
<p>무엇이 우리 앱에 적합하고 합리적인 선택일까? </p>
<p>이러한 선택 사항에 대한 적절한 고민은 코드에 대한 이해를 필요로 하고, 이 시점부터는 기획자와 디자이너에게는 낯선 용어가 등장한다. </p>
<p>*<em>그러나 여전히 Domain은 모두가 알고 있는, 공통적인 내용이다. *</em></p>
<h2 id="구체적인-결정을-통한-domain-구현-viewdata">구체적인 결정을 통한 Domain 구현 (View/Data)</h2>
<p><img src="https://velog.velcdn.com/images/haeun_/post/7c8fda03-bf4a-40b4-9675-bf253922fcfe/image.png" alt=""></p>
<p><code>기록UseCase</code>에서 선택해야 하는 <strong>저수준 결정은 두 가지</strong>이다.
바로 이를 표시할 <strong>화면 구성 방식</strong>(View)과 <strong>저장하는 방식</strong>(Data)의 결정이다. </p>
<p>개발자는 Domain을 구현할 수단으로 구체적인 방법을 선택해야 한다. </p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/594b83d8-47c7-4c69-b5d8-6231d15d5f0f/image.png" alt="">
iOS 개발에서는 View와 Data 저장 방식을 구현할 다양한 수단이 있다. </p>
<p>대표적인 <strong>화면 구현 수단</strong>으로는 <code>UIKit</code>과 <code>SwiftUI</code>
<strong>Data 저장 수단</strong>으로는 <code>CoreData</code>, <code>SwiftData</code>, <code>Server 통신</code> 등이 있다.</p>
<p>중요한 것은, 이 모든 것들이 도메인을 구현하기 위한 구체적인 수단에 해당한다는 점이다.</p>
<p>우리 팀은 View(입력 인지, 표시)와 Data(저장, 불러오기)를 구현하기 위해 SwiftUI와 CoreData를 선택하였다. (이유에 대한 논의는 해당 포스팅에서 생략한다. 각각은 학습 접근성과 팀원들의 학습 의지에 따라 선택되었다)</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/31beba2c-cb93-4ec5-b8d6-708d8bbfb9cc/image.png" alt=""></p>
<p><strong>View</strong>는 Domain에서 하드웨어와 사용자의 인터렉션 상황을 알기 위해 필요한 <strong>구현체</strong>다. </p>
<p><code>UseCase</code>에게 <strong>사용자 액션을 전달</strong>하고, <code>UseCase</code>의 의도를 따라 변경된 <code>Entity</code> 값을 <strong>보여주기 위해</strong> 서 필요한 존재이다. </p>
<p>우리는 View에서 어떻게 전달받고 응답할 것인지에 대해 구체적으로 명시한다. </p>
<blockquote>
<p><code>View</code>는 <code>Domain</code>의 필요에 의해 선택됐다. 그러니 <strong>View</strong>가 <strong>Domain</strong>에 <strong>직접적으로 영향을 행사하는 상황</strong>은 논리적으로 이상하게 느껴진다. </p>
</blockquote>
<p>그러나 코드는 유기적으로 맞물려 돌아가기에 <strong>View에서의 변화가 Domain 코드에 영향을 미칠 수도 있다</strong>. </p>
<p>디자이너가 잘못된 디자인이 사용자에게 Domain의 내용을 착각하게 만들지 않도록 경계하듯이, 
*<em>개발자는 View의 구현이 Domain을 변경시키는 일을 경계해야 한다. *</em></p>
<blockquote>
<p>즉, 개발자는 코드 수준에서 <strong>구체적 구현 계층</strong>이 <strong>앱의 도메인을 침범하지 못하도록</strong> 관리할 필요를 느낀다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/haeun_/post/2e9bc365-c81e-4d1d-9810-8c6b447bbc7c/image.png" alt=""></p>
<p><strong>데이터 저장, 갱신 수단</strong> 또한 마찬가지다. </p>
<p>기획자, 디자이너는 개발자가 어떤 방식으로 Data를 저장하고 불러올 건지에 대해 정확히 알지 못한다. </p>
<p><strong>CoreData</strong>라는 명칭도, <strong>SwiftData</strong>라는 명칭도 모른다. </p>
<p>(하지만 여전히 저장한 뒤 불러오자는 논의를 나눈다. 
<code>저장한다</code>는 말 자체는 몹시도 추상적인, <strong>도메인 영역에서의 논의</strong>이기 때문이다)</p>
<p>개발자는 우리 서비스에 적합한 데이터 저장 수단이 무엇일지 고민하게 된다.</p>
<p>더 나아가, <strong>CoreData를 통해 데이터를 저장하겠다는 결정</strong>이 <strong>Domain을 침범하여 변경할 수 없도록 의식</strong>한다. 
(이를 해결하는 대표적인 방법으로, 인터페이스를 두어 의존을 분리하는 방법이 있다)
(Domain-&gt;Data는 대표적인 고-&gt;저 의존이다)
(그러나 개발자는 때때로 View-&gt;Data와 같은 저-&gt;고의 상황에서도 <strong>의존을 분리하고자 시도</strong>한다.) (설계를 제시하는 본문에서 추가적으로 언급될 내용이다)</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/2e34f0f2-f54d-45b1-9c2e-9c86a2961882/image.png" alt="">
개발자는 여러가지 사항을 염두에 두며 궁극적으로 <code>Usecase</code>가 가진 시나리오에 해당하는 <strong>구현체를 작성</strong>하고 <strong>적절히 연결</strong>한다.</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/1ba8bfa9-7e99-4b0c-a89a-2000dca1deaa/image.png" alt=""></p>
<p>모든 노력은 <strong>소프트웨어가 변경에 유연</strong>하도록, <strong>Domain을 잘 만들기 위해 노력하는 개발자의 방식</strong>이다. </p>
<p>해당 내용을 통해 Domain이라는 폴더 구조가 필요하다고 말하고 싶은 것은 아니다. </p>
<p>이러한 내용이 <strong>.swift</strong> 파일 내부에 전부 들어있을 수도 있다. 하나의 <strong>ContentView</strong> 위에 들어있고 이러한 코드가 이해하기 쉽다고 주장하는 것이 <strong>타당</strong>할 수도 있다. </p>
<p>위의 구분(Domain, View, Data)은 서비스에 따라, 개발자에 따라 어떠한 방식으로 분류되고 구현될 지 모른다.</p>
<blockquote>
<p>전하고 싶었던 것은 그저 <strong>서비스의 Domain에 대한 구체적 구현</strong>으로서 <code>View</code>와 <code>Data</code>가 결정된다는 것 뿐이다. </p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/haeun_/post/1e2e33c5-7a7f-4d83-8c8a-26735e4ce475/image.png" alt=""></p>
<blockquote>
<p>이제 코드를 작성할 때 <strong>View</strong>를 작성하고, <strong>Domain</strong>을 작성하고, 
<strong>Data</strong>를 작성하는 상황이 <strong>프로젝트 구조의 어디쯤에 위치하고 있는 지 상상할 수</strong> 있다.</p>
</blockquote>
<p>코드 속에서 길을 잃지 않을 수 있다. 무엇을 위한 코드인 지 알고 작성할 수 있다.</p>
<p>물론, 알고 있더라도 여전히 길을 잃기도 한다. 
그를 방지하기 위해선 <strong>더 많은 논의와 뒷받침할 지식들</strong>이 필요하다.</p>
<p><strong>길을 잃지 않기 위한 고려들</strong>은 <code>경험적</code>으로 부딪히며 늘기도 하고, <code>꾸준한 학습</code>을 통해서 늘기도 한다. </p>
<p>어떠한 방식이 되었건, 모든 학습은 서비스를 위한 일이다. </p>
<h1 id="그래서-우리-서비스는-어떻게-설계할까">그래서, 우리 서비스는 어떻게 설계할까?</h1>
<p><strong>View</strong>와 <strong>Data</strong> 계층이 <strong>Domain</strong> 계층의 필요에 의해 생성되었음을 명확히 하기 위해 <code>UseCase</code>가 <code>View</code>에 반영될 <strong>상태값</strong>(<code>State</code>)과 <strong>액션</strong>(<code>Action</code>)을 전부 가진 프로젝트 <strong>설계를 제안</strong>하고 싶다. </p>
<p>더불어 <code>UseCase</code>가 <strong>Data</strong> 계층을 직접적으로 의존하지 못하도록 그 사이에 <strong>인터페이스</strong>를 두어 <strong>Domain의 독립성</strong>을 보장하도록 만들자.</p>
<p>이러한 설계를 통해 아래를 기대할 수 있다. </p>
<p><strong>1)</strong> View에 변동을 야기하는 상태의 출처를 UseCase 내부로 고정하여 복잡한 로직 속에서 예기치 않는 상태 변동을 방지할 수 있음
<strong>2)</strong> Data 구현체가 만들어지기 전에도 UseCase 내부 로직을 완성할 수 있음</p>
<p>=&gt; 내 코드로 인해 망가지지 않는 프로젝트 구현 
=&gt; 원인 추론이 용이한 프로젝트 구조 </p>
<p>(아키텍처 방법론 중 MVI와 TCA의 지향점을 일부 참고하였다)
(앞선 논의를 따르기 위해 앞으로 설명할 구조를 반드시 따라야 한다는 뜻은 아니다!)</p>
<p>해당 설계가 어떤식으로 동작하는 지, 구체적인 이해를 돕기 위해 이미지를 살펴보자. </p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/2035f387-27b0-4424-af85-b5680c2f36f1/image.png" alt=""></p>
<h3 id="view">View</h3>
<p><code>View</code>는 <code>UseCase</code>가 필요로 하는 <strong>사용자 액션</strong>을 <code>UseCase</code>의 <strong>메서드</strong>를 통해 전달한다. (실행한다)
<code>UseCase</code>는 해당 상황, <strong>메서드</strong>가 실행될 상황에 적절히 <strong>상태를 변경</strong>한다. 그러한 <strong>상태 변경</strong>은 <code>View</code>에 적용된다. </p>
<p>우리의 설계에서 <code>View</code>는 본인이 실행한 <strong>UseCase</strong>의 <code>메서드</code>가 어떠한 상태 변동을 만들어 내는 지 알지 못한다. 메서드를 실행하고, 상태가 변동된다면 반응할 뿐이다.</p>
<blockquote>
<p><code>View</code>를 <strong>바보 만든다-</strong>는 표현은 이러한 상황에서 쓰인다</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/haeun_/post/702c1e44-0ab7-4f4c-865d-4fc76986f861/image.png" alt=""></p>
<h3 id="data">Data</h3>
<p><code>Domain</code>은 때때로 <code>View</code>에 의해 실행되는 <strong>Action(메서드)</strong>의 동작으로 <code>Data</code>에게 <strong>CRUD</strong>를 요청해야 한다. </p>
<p>이를 요청하기 위해서는 <code>UseCase 내부</code>에서 <code>Data</code>를 <strong>저장하는 객체</strong>(구체적인 구현체)에 대한 정보를 알고 있어야 한다.</p>
<p>우리는 고수준이 저수준에 의존하는 것이 불안정함을 이해하였에, 
<code>Domain</code>과 <code>Data</code> 사이에 <code>Interface</code>를 두고자 한다.</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/4ebe87a1-1325-438e-809b-56feac9e9c4a/image.png" alt=""></p>
<p><code>Domain(UseCase)</code>은 <code>Data</code>의 <strong>구체적인 타입</strong>에 의존하지 않고 <code>필요한 메서드</code>만 바라볼 수 있게 된다. 
(<code>인터페이스</code>는 <code>프로토콜</code> 또는 <code>정적 메서드</code>로 구현할 수 있다)</p>
<p>그러니 코드작성에 실질적 구현 객체를 몰라도 된다. 
(해당 설계를 토대로 실제로 우리 프로젝트는 도메인이 먼저 작성되고, 구체적인 Data 저장 객체가 후에 개발되었다)</p>
<p>그저 <code>UseCase</code>는 <code>DataInterface타입</code>을 가지고 필요한 로직을 작성한다.</p>
<p>해당 로직의 구체적 구현체가 결정되는 순간은 UseCase가 생성되는 상황으로, 주입하면 된다. </p>
<p>실제 프로그램이 실행될 때의 의존과 (런타임 의존) 코드가 바라보고 있는 대상이 (컴파일 의존) 서로 다른 방향을 향한다. </p>
<p>각 계층 간의 분리를 이루기 위해 사용하는 해당 방식을 <code>의존성 역전</code>이라 한다. 고수준 코드가 저수준 코드를 직접적으로 알지 못하게 하기 위해 사용되는 방식이다. </p>
<p>(우리 팀 프로젝트는 단일 타겟으로 개발되어 고려하지 않았지만, <code>모듈화</code>를 이루려면 (독립 빌드) 소스코드 간 의존성을 끊어내기 위해 의존성 역전을 필수적으로 고려해야 한다)</p>
<h3 id="의존성-역전에-대해-덧붙이자면">의존성 역전에 대해 덧붙이자면,</h3>
<p><code>의존</code>이라는 말 뜻 그대로 바라보면 의존성에 대한 이해가 편한 것 같다. 
기존에 <code>UseCase</code>는 구체적인 <code>Data 코드</code>를 <code>의존</code>하고 있기에, <code>Data 코드</code>로 부터 <strong>자유로울 수가 없다</strong>.
<code>UseCase</code>가 동작하기 위해선 <code>Data 코드</code>를 <strong>사용해야하기, 필요하기</strong> 때문이다.</p>
<p>하지만 <code>Interface</code>를 둔 순간 <code>UseCase</code>는 더 이상 실제 <code>Data 구현체</code>를 직접적으로 알지 않아도 된다.</p>
<p>의존하지 않으니, 영향을 받지도 않는다.</p>
<p>의존하고 있는 인터페이스의 메서드가 누구로부터 구현되었는 지는 <strong>프로젝트가 실행될 때</strong> (런타임) 알게 될 것이다. </p>
<h3 id="저수준이-고수준을-아는-상황은-당연한가">저수준이 고수준을 아는 상황은 당연한가?</h3>
<p><code>저수준</code>(<strong>구체적 구현체</strong>)는 <code>고수준</code>의 <strong>필요에 의해</strong> 탄생하였으니 <code>저수준</code>이 <code>고수준</code>을 알고 있는 상황이 <strong>이상하게 느껴지지 않는다</strong>. (<code>View</code> -&gt; <code>Domain</code> 상황을 말한다)</p>
<p>하지만 <strong>실제 프로젝트 설계</strong>에는 <code>View</code>가 <code>Domain</code>을 소유하여 접근하는 상황 자체가 종종 불편하게 여겨진다. </p>
<p>사실 실제 프로젝트 중에는 서비스 이해관계자의 논의를 통해 <strong>Usecase가 변경되기도 하기 때문</strong>이다. </p>
<p>자연히 개발자는 <code>UseCase</code>의 변경 또한 <code>View</code>에 큰 영향을 미치지 않길 바란다. 
이러한 필요로 인해 개발자는 <code>저</code> -&gt; <code>고</code>로 가는 길목에도 <code>인터페이스</code>를 둘 수 있다. </p>
<p>이를 통해 <code>저수준</code>이 <code>고수준</code>을 의존하는 상황도 소스코드 상으로 완전히 끊어낼 수 있게 된다. </p>
<p>우리의 서비스에서는 <code>View</code>와 <code>Domain</code> 사이 <strong>인터페이스 계층</strong>의 <strong>필요를 직접적으로 느끼기 어렵다고 판단</strong>되어 <strong>고려하지 않았다</strong></p>
<p>즉, <code>Domain</code> -&gt; <code>Data</code> 사이에만 <code>Interface</code>가 존재한다.</p>
<h1 id="실제-프로젝트-폴더-분류">실제 프로젝트 폴더 분류</h1>
<p><img src="https://velog.velcdn.com/images/haeun_/post/3ff3eff6-b579-4066-a112-5b930aa88ede/image.png" alt=""></p>
<p>앞선 구조를 위해 위와 같은 폴더링을 가져가고자 한다. </p>
<p><code>App</code>은 우리 앱이 구동될 때 실행될 앱의 첫 시작 뷰를 담고 있다. 
<code>DesignSystem</code>은 공통으로 사용될 우리 앱의 디자인 설정 또는 컴포넌트를 담는다. 
<code>Data</code>, <code>Domain</code>, <code>Screens</code>는 앞단에서 나눈 세 개의 계층을 의미한다. </p>
<p>View가 아닌 Screens라 표기한 이유는 View가 아닌 조합된 하나의 화면을 의미하기 위해서 이다.</p>
<h2 id="추가-코드-예시">추가) 코드 예시</h2>
<hr>
<p>구체적으로 어떻게 작성할 지에 대한 <strong>발표 내용은 생략</strong>하고자 한다. 
혹시 몰라 아래엔 이해를 돕기 위해 MVP 작업 이후의 코드를 간략히 첨부한다.</p>
<hr>
<h3 id="mvp-작업-이후의-내용">MVP 작업 이후의 내용</h3>
<p>(* 폴더 분류 및 코드 관련 내용은 MVP 개발이 끝난 시점 붙여 놓은 이미지 입니다)</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/f328a2e4-e0fa-4045-aa5d-992fb9c3be4d/image.png" alt=""></p>
<ul>
<li>위 계층 구조가 적용된 실제 앱 서비스 코드 중 UseCase</li>
</ul>
<p><img src="https://velog.velcdn.com/images/haeun_/post/1e5a098a-f73a-482d-b5f1-03f4a0cd4275/image.png" alt=""></p>
<ul>
<li>위 계층 구조가 적용된 실제 앱 서비스 코드 중 View</li>
</ul>
<p><img src="https://velog.velcdn.com/images/haeun_/post/c47a9087-a261-4c25-96e5-971f96ad7652/image.png" alt=""></p>
<ul>
<li>위 계층 구조가 적용된 실제 앱 서비스 코드 중 DataInterface</li>
</ul>
<p><img src="https://velog.velcdn.com/images/haeun_/post/9556c9d4-d088-482a-8458-f6f52b5ba203/image.png" alt=""></p>
<ul>
<li>위 계층 구조가 적용된 실제 앱 서비스 코드 중 Data</li>
</ul>
<h1 id="ios-개발자가-서비스에-가지는-책임">iOS 개발자가 서비스에 가지는 책임</h1>
<h3 id="소프트웨어를-소프트웨어-답게">소프트웨어를 소프트웨어 답게,</h3>
<p><img src="https://velog.velcdn.com/images/haeun_/post/3c276b1e-aa50-48d6-8987-5d9ea5c9c68b/image.png" alt=""></p>
<p>때때로 아키텍처 구조에 대해 고민하고 설계하는 일, 
프로그램을 유연하게 만들기 위해 다양한 방법론에 대해 접하고 학습하는 일은 <code>유지보수</code>에서만 유용한 일로 치부되곤 한다. </p>
<p>*<em>하지만 우리는 단순 구현 상황에서도 수많은 변경과 고민을 거친다. *</em></p>
<p>앞선 아키텍처 설계처럼 <code>커다란 컴포넌트 간 구조건</code>, <code>객체와 객체간의 관계건</code>, <code>함수와 함수간의 관계건</code>, 
<strong>계층 간 중요도를 가늠하고 변경에 대해 대비하는 자세</strong>는 언제 적용해도 가장 빠른 길을 제시한다. </p>
<blockquote>
<p>개발자의 노력이란 결국 <strong>서비스를 위한 노력</strong>이고 <strong>서비스를 안전하고 빠르게 발전시키기 위한 책임이다.</strong></p>
</blockquote>
<p>다양한 기술 학습 또한 더 나은 방식을 위해 고민할 선택지를 넓히기 위함이다. </p>
<p><strong>본문</strong>에선 개발자의 책임을 <strong>아키텍처에 대한 논의</strong>로 엮어냈다. 
이 외에도 <strong>개발자</strong>가 <code>서비스를 잘</code> 만들기 위해선 수 많은 고려사항들이 존재한다.</p>
<h3 id="개발자는-곧-서비스를-위한다">개발자는 곧 서비스를 위한다</h3>
<p><strong>개발자는 기획자, 디자이너와 계속해서 대화</strong>해야 한다. 
타 파트의 의견을 <strong>적극적으로 수용</strong>하고 때로는 타 파트의 의견으로부터 <strong>우리 서비스를 지켜야</strong> 한다. 
그것이 <strong>서비스를 위하는 태도</strong>이기 때문이다.</p>
<p><strong>화면 구현</strong>은 개발자가 <strong>서비스를 잘 만들기 위해 책임지는 영역 중 하나</strong>일 뿐이다. </p>
<blockquote>
<p>본질적인 책임은 서비스 <code>잘</code> 만들고 운영해 나가는 것에 있다. </p>
</blockquote>
<h2 id="마치며">마치며</h2>
<p><img src="https://velog.velcdn.com/images/haeun_/post/8b5319ad-e055-47c1-a087-b5dd1587272c/image.png" alt="">
(개발을 처음하는 팀원의 즐거운 PR!)</p>
<p>약 2주라는 짧은 개발 기간임에도 팀원들은 <strong>MVP</strong> 제작을 성공적으로 끝마쳤습니다. </p>
<p>6명의 모두 <strong>개발 경험에 관계 없이</strong> 참여하였으며 모두가 컨트리뷰터로 이름을 올렸습니다. </p>
<p>가장 기뻤던 피드백 중 하나는 </p>
<blockquote>
<p>왜 문법공부를 해야하는 지 알겠다-</p>
</blockquote>
<p>였습니다.
저의 팀원은 제가 과거에 겪었던 왜 해야하는가에 관한 고난을 겪지 않고 이정표를 지닌 채 학습해 나갈 수 있다면 좋겠습니다!</p>
<p>가진 지식을 타인에게 이해시킬 수 없다면 그 지식의 효용 가치를 재고해야 한다 생각합니다. 개발 경험이 없는 분들께서도 필요를 이해할 수 있도록 전달하는 것은 제게 있어 도전이었습니다. </p>
<p>코드 속에서 길을 잃지 않으려면, 내가 작성하는 코드가 어떠한 이유로 존재하는 지 머릿 속에 그려볼 수 있어야 한다 생각합니다. 그를 그리는 힘을 얻는 포스팅이었다면 좋겠습니다.</p>
<p>우리 팀 최고! </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[propertyWrapper: 눈 떠보니 Source of Truth가 내 앞에 ]]></title>
            <link>https://velog.io/@haeun_/PropertyWrapper-%EB%88%88-%EB%96%A0%EB%B3%B4%EB%8B%88-Source-of-Truth%EA%B0%80-%EB%82%B4-%EC%95%9E%EC%97%90</link>
            <guid>https://velog.io/@haeun_/PropertyWrapper-%EB%88%88-%EB%96%A0%EB%B3%B4%EB%8B%88-Source-of-Truth%EA%B0%80-%EB%82%B4-%EC%95%9E%EC%97%90</guid>
            <pubDate>Sun, 24 Mar 2024 09:58:06 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요 보노입니다! 🦦</p>
<p>근 나흘 간 애정을 들여 쓴 글이 벨로그에서 날아가고... 멘붕이 왔지만,</p>
<p>돌아볼 기회라 생각하고 다시금 앉아 차근히 포스팅을 시작합니다. </p>
<p>(이제는 사라진 제 포스팅의 흔적 입니다. 수정하는 과정에서 덮어쓰기 당해 사라졌답니다...)</p>
<p>그러니 이번이 진짜 진짜 진짜 최종이 되겠군요...</p>
<p>하지만 제 머릿속에 남아 있기 때문에 괜찮습니다. </p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/3281ab81-daf2-4be0-ae33-790c021de1d9/image.png" alt=""></p>
<p>저는 <strong>SwiftUI</strong>를 사용하며, </p>
<p>SwiftUI가 제공하는 상태 관리자 @State, @Binding ... 등등을 통해 <strong>PropertyWrapper</strong>를 처음 접했습니다. </p>
<p><code>struct</code>인 <code>View</code> 내부에 선언하여 <code>View</code> 간에 상태 값을 관리할 수 있다니, <strong>SwiftUI</strong>가 제공하는 도구들은 마법같이 느껴졌습니다. </p>
<p>하지만 안타깝게도 <strong>SwiftUI</strong>를 사용하는 짧지 않은 기간 동안 그 이상의 이해는 없었습니다. </p>
<p>제가 뜯어서 이해할 수 있는 영역이 아니라 판단했기 때문입니다. </p>
<p><strong>propertyWrapper</strong>에 대한 학습할 시기가 있기는 했지만, 이를 SwiftUI 가 제공하는 propertyWrapper <strong>타입</strong>과 연관지어 떠올리진 못했습니다.</p>
<p>그런 제가 다시금 <strong>PropertyWrapper</strong>를 열어 보게 된 것은 <strong>TCA</strong>에서 곧 Release 될 <a href="https://github.com/pointfreeco/episode-code-samples/blob/e0616b5fd9a69f61e39f00fd0aef7c916bb9bc24/0270-shared-state-pt3/swift-composable-architecture/Examples/CaseStudies/SwiftUICaseStudies/01-GettingStarted-SharedState.swift#L22">SharedState</a> 때문 입니다. </p>
<p>이슈(<strong>공유상태</strong>)를 해결하기 위해 제시한 해법이 예상치 못한 것 이었고, 그 형태가 까다롭지 않음에 가벼운 절망감이 들었습니다. </p>
<p><strong>propertyWrapper</strong>라는 <strong>도구</strong>를 도구로서 바라보지 못했음을 반성하는 계기가 되기도 했습니다. </p>
<p>더불어 학습이 늦었지만, <strong>propertyWrapper</strong>란 무엇인가 찾아가는 과정 속에서 SwiftUI가 말하는 <code>Source of Truth</code>가 무엇인 지 느낄 수 있었습니다.  </p>
<p>그러니 본 포스팅의 끝에는 아래 질문의 논점을 이해할 수 있을 것입니다. </p>
<blockquote>
<p><strong><code>@Binding var someState: Bool</code></strong>의 타입은 <strong><code>Binding&lt;Bool&gt;</code></strong> 인가?</p>
</blockquote>
<p>본문 내에는 주관적인 의견이 다수 들어가 있습니다. </p>
<p>제가 많이 헤맸던 만큼, 꼭 공식문서와 소스를 통해 개념을 먼저 접하고 오개념을 흡수하지 않도록 유의하여 읽으셨으면 합니다. </p>
<p>늘 그랬듯 누군가에겐 도움이 되는 글이길 바랍니다.</p>
<h1 id="propertywrapper란">PropertyWrapper란?</h1>
<p><strong>propertyWrapper</strong>는 <strong><code>Attributes</code></strong>의 하위 항목으로 분류되기도, 
<strong><code>Properties</code></strong>의 하위 항목으로 분류 되기도 한다. </p>
<h2 id="attributes에서의-propertywrapper">Attributes에서의 PropertyWrapper</h2>
<p><img src="https://velog.velcdn.com/images/haeun_/post/07a10d54-de53-4fec-8d6d-a5191e694f8f/image.png" alt=""></p>
<p><a href="https://docs.swift.org/swift-book/documentation/the-swift-programming-language/attributes/">링크</a></p>
<h2 id="properties에서의-propertywrapper">Properties에서의 PropertyWrapper</h2>
<p><img src="https://velog.velcdn.com/images/haeun_/post/358152d2-4a54-4d56-acab-c6eae6ada04a/image.png" alt=""></p>
<p><a href="https://docs.swift.org/swift-book/documentation/the-swift-programming-language/properties/">링크</a></p>
<p>내게 더 와닿는 문장은 <strong>properties</strong> 하위의 정의에 해당했다. </p>
<p>핵심은 아래와 같다 느꼈다. </p>
<blockquote>
<p>프로퍼티 래퍼는 <strong>프로퍼티의 저장 방식을 관리하는 코드</strong>를 <strong>프로퍼티의 선언부와 분리</strong>하여, 특정 로직이나 관리 코드를 <strong>프로퍼티에 적용</strong>할 수 있게 해준다.</p>
</blockquote>
<p>좀 더 와닿는 형태로 바꾸어 말하자면,</p>
<blockquote>
<p><code>프로퍼티 래퍼</code>는 저장 방식을 관리하는 <strong>인스턴스</strong>를 생성하고, 이를 독립된 <strong>선언부</strong>와 <strong>연결</strong>한다. </p>
</blockquote>
<p>어쩌다 이런 역할을 수행하는 도구(<strong>propertyWrapper</strong>)의 필요를 느꼈을까?</p>
<h1 id="propertywrapper의-필요">PropertyWrapper의 필요</h1>
<p>PropertyWrapper의 존재 의의는 </p>
<blockquote>
<p>독립된 속성 값에 반복적으로 적용되는 로직을 인스턴스로 빼내어, 원할 때 마다 입혀줄 수 있음</p>
</blockquote>
<p>에 있다 생각한다. </p>
<p>이에 대한 이해로 공식소스의 예제를 들어보자. </p>
<p>아래는 Swift Evolution의 가장 간단한 예제 <strong>@Lazy</strong>이다. </p>
<p>이는 propertyWrapper를 구성하는 요소에 익숙해 지기 위해 가져온 예시로, @Lazy 코드의 쓰임에 대해 이해하는 것을 목표하지 않는다.</p>
<p>누군가는 이러한 필요성으로 propertyWrapper의 필요를 느끼는 구나! 이러이러한 방식으로 동작하기에 원하는 구현이 가능하구나! </p>
<p>를 이해하는 것이 전부다. </p>
<h2 id="propertywrapper가-개선-가능한-상황">propertyWrapper가 개선 가능한 상황</h2>
<h3 id="lazy-지연-할당-상황">lazy 지연 할당 상황</h3>
<pre><code class="language-swift">struct Foo {
  // lazy var foo = 1738
  private var _foo: Int?
  var foo: Int {
    get {
      if let value = _foo { return value }
      let initialValue = 1738
      _foo = initialValue
      return initialValue
    }
    set {
      _foo = newValue
    }
  }
}</code></pre>
<p>종종 optional한 값을 노출시키지 않기 위해 lazy 할당하고 싶은 상황을 마주한다. </p>
<p>이를 위해 swift는 lazy 라는 지연할당 키워드를 제공 하지만 해당 키워드는 타 언어와 호환성이 떨어진다.</p>
<p>그렇다고 lazy 라는 언어 지원 없이 같은 효과를 원한다면 위와 같이 많은 보일러 플레이트가 필요하다.</p>
<pre><code class="language-swift">class Foo {
  let immediatelyInitialized = &quot;foo&quot;
  var _initializedLater: String?

  // We want initializedLater to present like a non-optional &#39;let&#39; to user code;
  // it can only be assigned once, and can&#39;t be accessed before being assigned.
  var initializedLater: String {
    get { return _initializedLater! }
    set {
      assert(_initializedLater == nil)
      _initializedLater = newValue
    }
  }
}</code></pre>
<p>더불어 우리는 지연 할당 되기를 바라는 optional 값이 할당 된 이후엔 불변해도 상관 없는 상황에 자주 놓인다.</p>
<p>var는 let 에 비해 많은 안정성을 포기하는 것도 아쉬운 부분이다.</p>
<p>이를 propertyWrapper를 통해 개선할 수 있다. </p>
<p>매번 선언해야 하는 불필요한 코드 중복을 줄이고, 원하는 값 만을 노출한다. </p>
<blockquote>
<p>@Lazy var foo = 1738</p>
</blockquote>
<p>이런 식으로 말이다. </p>
<p> 위 코드는 foo가 내보내는 임시값이 1738이고, 외부에 의해 할당될 때 (lazy의 지연 할당 처럼) 그 값으로 다시금 할당 됨을 의미한다. </p>
<p>(위에서 언급한 한 번 할당된 값이 불변하도록 하는 작업은 Lazy 내부 코드에서 일어나고 propertyWrapper의 특성과는 무관하다) </p>
<p>Lazy가 어떤 propertyWrapper인지 말하기에 앞서, 나는 위 코드 형태에 익숙함을 느낄 수 있다. </p>
<blockquote>
<p>@State var state: String = &quot;하이&quot;</p>
</blockquote>
<p>라던가, </p>
<blockquote>
<p>@Binding var bindingValue: String </p>
</blockquote>
<p>라던가 말이다. </p>
<p>결국 한 방향으로 흐르는 개념이니 길을 잃지 않는 것이 중요하다. </p>
<h2 id="propertywrapper-lazy">propertyWrapper @Lazy</h2>
<p>다시 lazy를 대신하여 지연 속성을 구현해 줄 Lazy를 살펴보자</p>
<p>코드는 아래와 같다. </p>
<pre><code class="language-swift">@propertyWrapper
enum Lazy&lt;Value&gt; {
  case uninitialized(() -&gt; Value)
  case initialized(Value)

  init(wrappedValue: @autoclosure @escaping () -&gt; Value) {
    self = .uninitialized(wrappedValue)
  }

  var wrappedValue: Value {
    mutating get {
      switch self {
      case .uninitialized(let initializer):
        let value = initializer()
        self = .initialized(value)
        return value
      case .initialized(let value):
        return value
      }
    }
    set {
      self = .initialized(newValue)
    }
  }
}</code></pre>
<p>(case의 연관값을 이용하여 initialized 이후의 값 유지를 강제하다니...!)</p>
<p>위 코드에서 가져가야 할 부분은 @propertyWrapper가 wrappedValue라는 속성을 강제한다는 점이다. </p>
<p>@propertyWrapper가 꾸며주는 타입 <code>Lazy</code>는 아직 결정되지 않은 어딘가의 property를 wrap할 주체인 <code>Wrapper</code>로서의 의미를 가진다. </p>
<p>그리고 Lazy에 의해 wrap 당할 프로퍼티는 선언값으로서 Wrapper 내부속성인 <code>wrappedValue</code>와 엮이게 될 것이다. </p>
<p>명칭이 직관적이다. </p>
<p><strong>Wrapper</strong>(<code>Lazy</code>)가 가지는 <code>wrappedValue</code> 란 <strong>Wrapper</strong>가 <strong>wrap</strong>할 <strong>property</strong>, 즉 <code>속성값</code>을 의미한다. </p>
<p>코드로 보자면, 아래 Lazy 인스턴스 속성 wrappedValue와 엮인 property가 바로 foo다. </p>
<blockquote>
<p>@Lazy var foo = 1738</p>
</blockquote>
<p>어떤 타입 내부 속성인 foo는 위 코드로 생성될 Lazy 인스턴스 내부의 wrappedValue를 대변하게 된다. wrapper에 의해 한 번 Wrap 되는 것이다. </p>
<p>이를 가능하게 하는 것이 바로 어트리뷰츠인 @propertyWrapper이다. </p>
<p>그러니 foo의 타입은 Lazy 내부 wrappedValue 타입과 같고, 해당 접근은 Lazy 인스턴스 속성인 wrappedValue로의 접근과 같다. </p>
<p>그런데, Lazy 인스턴스는 어디에, 언제 생성되는 걸까? </p>
<p>우선 생성된 Lazy 인스턴스는 @propertyWrapper에 의해 보이지 않는다.</p>
<p>보이지는 않지만, @propertyWrapper에 의해 wrap 할 속성과 <strong>같은 scope</strong>에 위치된다.</p>
<p>더불어 <code>wrappedValue</code>인 <code>foo</code>를 통해서 <strong>인스턴스 본체</strong>에 접근할 방법이 존재하는데, 이것이 바로 <code>_</code>(언더바)를 통하는 것이다. </p>
<p>즉, foo라는 Lazy 인스턴스 속성 값 앞에 언더바를 붙이면 그게 foo가 속한 인스턴스 본체를 가르키는 값이 된다. </p>
<p>이를 명확히 하기 위해서 짚고 넘어가야 할 부분이 있는데,  <code>@propertyWrapper</code> 타입의 특별한 상황에 대해서 제공되는 초기화 방식이다.</p>
<blockquote>
<p>@Lazy var foo = 1738</p>
</blockquote>
<p>@propertyWrapper의 타입을 생성할 때에는 이런 식으로 <code>=</code> 할당 연산자를 사용하는 걸까? </p>
<p>맞는 말이기도 틀린 말이기도 하다. </p>
<p><strong>@propertyWrapper</strong>는 <code>특정 상황</code>을 충족하면 개발자에게 생성 편의를 돕기 위해 <strong>propertyWrapper 타입 생성 경로</strong>를 <strong>wrappedValue 경로</strong>와 엮어준다. </p>
<p>특정 상황이란 아래와 같다. </p>
<ol>
<li>@propertyWrapper로 선언된 타입 (Lazy)의 생성자 (init)의 <strong>파라미터명</strong>이 <strong>wrappedValue</strong> 일 것</li>
<li>그 <strong>파라미터 wrappedValue의 타입</strong>이 내부 속성 <strong>wrappedValue 타입</strong>과 같을 것 </li>
</ol>
<p>그러니 만약 파라미터 명이 아래와 같이 수정하여 특정 상황에서 벗어난다면</p>
<pre><code class="language-swift">init(wrapped: @autoclosure @escaping () -&gt; Value) {
    self = .uninitialized(wrapped)
  }</code></pre>
<p>컴파일러는 에러를 띄운다.</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/df943286-94d3-47ed-be5b-8b72ed360e16/image.png" alt=""></p>
<p>왜 에러가 날까? </p>
<p>우선 특별한 조건을 충족하여 <strong>Lazy</strong>의 <strong>wrappedValue</strong>(foo)로의 접근이 Lazy 생성자와 연결되지 못했을 뿐더러, </p>
<p>아직 인스턴스 Lazy가 생성되지도 않았기에 접근할 <code>foo</code>(<code>_foo.wrappedValue</code>)도 없다. </p>
<p>여기서 꼭 짚고 싶은 부분이 이것이다. </p>
<p><strong>보이지 않지만</strong>, @propertyWrapper를 통해 관리하고자 하는 코드 로직이 존재하는 공간은 <strong>Lazy 인스턴스</strong>이다. </p>
<p>그러니 이를 사용하고자 한다면, <strong>Lazy 인스턴스를 생성</strong>해야 한다. </p>
<blockquote>
<p>@Lazy var foo = 1738</p>
</blockquote>
<p>위의 코드는 마치 <code>foo</code>(<code>_foo.wrappedValue</code>)의 생성이 곧 @propertyWrapper의 시작점으로 보이게 한다. </p>
<p>위는 특별한 상황에 대한 특별한 이니셜라이저를 이용하여 Lazy 인스턴스를 만든 상황이다. 즉 위 코드가 <strong>Lazy 인스턴스</strong>를 생성하는 코드임을 이해해야 한다. </p>
<p>방심하면 foo를 생성하는 것이 @propertyWrapper를 사용하는 조건 처럼 보이게 하므로 꼭 짚고 넘어가고 싶었다. </p>
<p>만약 특별한 상황 (생성자 파라미터명이 wrappedValue, 타입도 일치)을 충족하지 않았다면 어떨까.</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/bbff4b74-a443-4990-ae9b-219b18f25fde/image.png" alt=""></p>
<p>바로 이렇게 Lazy 생성자를 이용하여 인스턴스를 생성해 주어야 한다. </p>
<p>위 형태는 마치 </p>
<pre><code class="language-swift">@Binding var bindingValue: String</code></pre>
<p>과 일부 닮아 아직 그 자체로는 속성 값을 가지는 뷰(또는 값)의 초기화가 완료되지 않았다 생각하게 만들지만, </p>
<p>두 코드는 상황이 다르다. </p>
<p>Lazy로 작성된 속성이 존재하는 뷰는 생성자로 아무것도 받지 않더라도 에러가 발생하지 않는다. </p>
<pre><code class="language-swift">init() {} // 에러 없음</code></pre>
<p>이미 선언된 내부 속성의 초기값이 충족 되었기 때문이다. </p>
<h2 id="_의-의미"><code>_</code>의 의미</h2>
<p><img src="https://velog.velcdn.com/images/haeun_/post/04986f89-65ed-44c7-864f-3da3785ffa95/image.png" alt=""></p>
<blockquote>
<p>저장 속성 이름에 밑줄(_)을 접두사로 사용하는 것은 의도적입니다. 이는 비공개 저장 속성에 대한 기존 규칙에 부합하는 예측 가능한 이름을 합성된 저장 속성에 제공합니다. </p>
</blockquote>
<p>앞선 설명에서 <code>_</code>를 미리 접할 수 있었듯, _는 wrappedValue를 가진, 인스턴스를 의미한다. </p>
<p>@propertyWrapper가 의도적으로 감춘(private, <code>_</code>의 의미) 인스턴스가 존재하는 공간으로 접근하는 경로이기에 <code>_</code>를 사용한다고 한다. </p>
<p>위의 예시 코드가 아니더라도, 우리는 익숙히 <code>_</code> 를 사용해 왔다. </p>
<pre><code class="language-swift">struct TestView: View {
    @Binding var bindingValue: String
    init(bindingValue: Binding&lt;String&gt;) {
        _bindingValue = bindingValue
    }
}</code></pre>
<p>바로 위의 상황에서 말이다. </p>
<p>왜 <code>bindingValue = bindingValue</code>는 불가능한 지, 이제는 알고 있다. </p>
<p><code>bindingValue</code>가 나타내는 것은 <code>_bindingValue.wrappedValue</code>와 같다. </p>
<p>생성자의 파라미터와는 타입도 다르고, 초기화 되지 않은 Lazy 인스턴스를 초기화 시켜주는 역할도 수행하지 못한다. </p>
<h2 id="의-의미"><code>$</code>의 의미</h2>
<p><code>$</code>는 <strong>propertyWrapper 타입 인스턴스</strong>의 <code>projectedValue</code>에 접근하는 경로로 사용된다. </p>
<p>wrappedValue와 다르게, 그 구현 여부가 <strong>선택적</strong>이다. </p>
<p>wrappedValue 외에 더 필요한 정보가 있다면 해당 속성을 사용하여 <code>$</code>로 접근할 수 있다. </p>
<p>즉 위 Lazy 타입 내부 속성으로 <strong>이름</strong>을 <strong>projectedValue</strong> 로 추가한다면</p>
<p>해당 속성값에 접근 시 <code>$foo</code> (_foo.projectedValue) 로의 접근이 가능하게 된다. </p>
<p>물론 <code>$</code>는 SwiftUI를 사용하는 이라면 <strong>@State</strong>를 사용할 때 숨쉬듯 사용하는 키워드이기도 하다. </p>
<pre><code class="language-swift">@State var state: Int = 1</code></pre>
<p>Binding을 인자로 받는 뷰에 state를 넘기려면 $state로 넘겨야 하기 때문이다. </p>
<p>여기서 <strong>propertyWrapper 타입</strong>인 <strong>State</strong>가 갖는 의미는, <code>State&lt;Value&gt;</code> 타입의 projectedValue가 반환하는 값이 <code>Binding&lt;Value&gt;</code>라는 점이다. </p>
<h2 id="정리--property-wrapper란">정리 : Property Wrapper란?</h2>
<pre><code class="language-swift">@Field(name: &quot;first_name&quot;) public var firstName: String

// 아래 형태로 확장

private var _firstName: Field&lt;String&gt; = Field(name: &quot;first_name&quot;)

public var firstName: String {
  get { _firstName.wrappedValue }
  set { _firstName.wrappedValue = newValue }
}

public var $firstName: Field&lt;String&gt; {
  get { _firstName.projectedValue }
  set { _firstName.projectedValue = newValue }
}</code></pre>
<p>이제 포스팅 서두에 놓인 질문에 나름의 답을 해보자.</p>
<blockquote>
<p><strong><code>@Binding var someState: Bool</code></strong>의 타입은 <strong><code>Binding&lt;Bool&gt;</code></strong> 인가?</p>
</blockquote>
<p>그렇다. </p>
<p>위 문장의 타입이 있냐는 질문은 모호한 구석이 있어, 위 코드를 사용하기 위해서 어떤 타입이 필요하느냐고 질문을 해석하였다. </p>
<p><strong>@propertyWrapper</strong>를 동작시키는, <code>Binding</code>을 생성하기 위한 타입이 무엇이냐 묻는다면 해당 <strong>propertyWrapper</strong>인 <code>Binding</code>을 생성하기 위해 <code>Binding&lt;Bool&gt;</code> <strong>타입 인스턴스 생성</strong>이 필요하다.</p>
<h1 id="swiftui에서-source-of-truth-란">SwiftUI에서 source of Truth 란?</h1>
<p>이제껏 <strong>@State</strong>나 <strong>@Binding</strong>을 사용하면서도,</p>
<p>이 <code>wrappedValue</code>를 통한 변경이 body 내부에서 가능한 이유를 일찍이 고민하지 못했음이 참 아쉽다. 이를 알았더라면 뷰를 다루는 시각이 확연히 넓었을 것 같다. </p>
<p><strong>State</strong>나 <strong>Binding</strong>이나, 각각의 propertyWrapper 타입은 <strong>wrappedValue</strong>로 <code>{get nonmutating set }</code> <strong>계산속성</strong>을 가진다. </p>
<p>이 말은 즉슨 State와 Binding 인스턴스가 wrappValue의 set 통해 변경시키는 값은 해당 인스턴스 <strong>내부에 없다</strong>는 뜻으로 풀이된다. </p>
<p>그 값이 어디에 보관되고, 어떠한 방식으로 변화를 읽어 View를 갱신시키는 지는 알 수 없다. </p>
<p>그저 추측하는 것은 <code>wrappedValue</code>의 <strong>nonmutating set</strong>의 실행이 <strong>접근하는 곳</strong>에 뷰의 <strong>source or truth</strong>가 있을 것이라는 것이다. </p>
<p>뷰가 상태값을 안고 있는 구조가 아니라 추측하면 View가 struct여도 일관된 상태 연결을 유지하는 이유를 납득할 수 있다. </p>
<p>이전부터 <code>View</code>가 <code>struct</code>인 이유에 대하여 다양한 가설을 세워왔다. </p>
<p>나름 답을 내렸다 생각했는데, 한참 부족했음을 깨닫는다. </p>
<p>다양한 글들을 읽다보면, View에 변동을 야기하는 상태값들을 다양한 <strong>propertyWrapper</strong>를 통해 <strong>안전하게 처리하라</strong>는 말을 보았다. </p>
<blockquote>
<p>상태 값은 위치는 뷰에게 있지 않다. 뷰는 누군가에 의해 갱신된다. </p>
</blockquote>
<p>뷰가 갱신될 때 내부 속성 또한 초기화 될 수 있으므로 값을 안전하게 보관하라는 말의 뜻을 이제야 곱씹어 본다. </p>
<h1 id="마치며">마치며,</h1>
<p>새로운 깨달음에 기쁘기도 하지만 상태 관리를 SwiftUI에 맡겨 버렸던, 시야가 좁아져 더 많은 것을 깨닫지 못한 지난날이 아쉽습니다. </p>
<p>어쩌면 잘 모르고도 쓸 수 있게 만든 SwiftUI에 감탄을 해야하는 지도 모르겠습니다.</p>
<p>그리고 블로그를 꾸준히 쓰기 위해 글쓰기 모임에 가입했는데, 제게 주 1회 업로드는 생각보다 더 어려운 일이었습니다. 가벼운 주제를 택하고 싶었지만, 도저히 찾을 수 없었습니다 ... </p>
<p>이번주로 2주치 벌금이 누적되는 걸 보면서... 이러지 말아야지 다짐을 해 봅니다...</p>
<p><del>포스팅 글은 다른 곳에 작성 뒤 옮겨와야겠다는 것도요...</del></p>
<p>nonmutating을 사용한 @UserDefaults 라던가, 접근 경로에 따른 비교라던가,</p>
<p>떠나보낸 내용이 아쉽기도 하지만 이만 글을 마무리 하려합니다. </p>
<p>무엇이던 혼자만의 이야기로 간직하지 않게</p>
<p>늘 든든한 말벗이 되어주는 메이슨과 마이노 감사합니다. </p>
<p>다시 쓰는 것에 대한 압박으로 검토가 부족하였기에, 키워드 오류가 많을 것으로 예상됩니다. 혹시 발견하신다면 댓글 부탁드립니다! </p>
<p>감사합니다. 즐거운 개발 되세요! 🫶</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Swift) 2016 WWDC Understanding Swift Performance를 공부하며, ]]></title>
            <link>https://velog.io/@haeun_/Swift-2016-WWDC-Understanding-Swift-Performance-Allocation%ED%95%A0%EB%8B%B9%EC%97%90-%EB%8C%80%ED%95%9C-%EC%9D%B4%ED%95%B4</link>
            <guid>https://velog.io/@haeun_/Swift-2016-WWDC-Understanding-Swift-Performance-Allocation%ED%95%A0%EB%8B%B9%EC%97%90-%EB%8C%80%ED%95%9C-%EC%9D%B4%ED%95%B4</guid>
            <pubDate>Fri, 08 Mar 2024 08:33:05 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요, 보노입니다! </p>
<p><a href="https://developer.apple.com/videos/play/wwdc2016/416/">WWDC 2016 Understanding Swift Performance</a></p>
<p>본 포스팅은 과거 위 세션을 <strong>내 코드 어디에 적용시킬 수 있는 지가</strong> 와닿지 않아 헤매다, 당시 나름대로 내린 답을 짧게 정리한 포스팅입니다. </p>
<p>누군가에겐 당연한 내용이기에 포스팅을 망설였으나,</p>
<p>24년도의 목표를 &#39;<strong>부족함을 직면하고 자주 실패한다</strong>&#39;로 설정하였기 때문에 자신없는 주제 또한 남겨두는 것이 좋겠다 생각이 들었습니다. </p>
<p>일기에 가까운 글이며 부족하지만, 늘 그랬듯 누군가에게는 도움이 되는 글이길 바랍니다.</p>
<h2 id="allocation할당이-성능에-미치는-영향">Allocation(할당)이 성능에 미치는 영향</h2>
<p>우선, <a href="https://developer.apple.com/videos/play/wwdc2016/416/">WWDC 2016 Understanding Swift Performance</a> 에서는 Swift의 <strong>성능 기준</strong>을 아래와 같이 제시한다. </p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/ea64f689-906b-4d52-a676-41ef49ecd455/image.png" alt=""></p>
<p>성능을 평가하는 기준은 총 세 가지이다.</p>
<p>(1) <strong>Allocation</strong> (할당)  </p>
<ul>
<li>_인스턴스_를 어디에 할당? 
=&gt;  <code>Stack</code> / <code>Heap</code>  </li>
</ul>
<p>(2) <strong>Reference Counting</strong> (참조 카운팅)  </p>
<ul>
<li><em>인스턴스</em> 전달 시 참조 카운팅은 얼마나 많이? (오버헤드는 얼마나 많이?) 
=&gt; <code>Less</code> / <code>More</code></li>
</ul>
<p>(3) <strong>Method Dispatch</strong> (메소드 디스패치)</p>
<ul>
<li><em>인스턴스</em> 메서드 호출 시 어떻게 동작? 
=&gt;<code>Static</code> / <code>Dynamic</code></li>
</ul>
<blockquote>
<p>좌측일수록 성능이 좋고, 우측일수록 성능이 나쁘다. </p>
</blockquote>
<p>세 가지의 기준 중 <strong>할당</strong>에 대한 이야기를 이어가 보자. </p>
<p>Swift의 <strong>인스턴스</strong>는 힙 영역 또는 스택 영역에 할당된다. </p>
<p>Struct(<strong>값 타입</strong>)는 스택에 쌓이고 Class(<strong>참조 타입</strong>)은 Heap 영역에 저장된다.</p>
<p>그러니 나는 익숙히 <strong>Stack</strong> / <strong>Heap</strong>의 특성을 <strong>Struct</strong> / <strong>Class</strong>로 대리표현하기도 했다. 
(예외 상황이 있기에 옳은 표현 방법은 아니다)</p>
<h3 id="값싼-점토와-값비싼-점토-struct와-class">값싼 점토와 값비싼 점토, Struct와 Class</h3>
<p>나는 개념을 비유하여 이해하기를 즐기기에,
Struct와 Class를 <strong>점토</strong>에 같이 비유하고자 한다. </p>
<p><strong>Struct</strong>와 <strong>Class</strong>를 점토재료에 비유하자면, <strong>Struct</strong>는 딱딱한 대신 값이 싼 점토, <strong>Class</strong>는 유연하고 부드러운 대신 비싼 점토이다.</p>
<p>값비싼 점토인 <strong>Class</strong>는 그 유연성을 보장하기 위한 재료로 만들어져 구입하는 데 드는 값이 크고, 흐물거리는 모양을 유지하기 위해 들이는 노력 값도 크다. </p>
<p>( 하지만 경우에 따라 값싼 <strong>Struct</strong>가 *<em>Class *</em>사용보다 더 큰 비용을 지불해야 하는 경우가 존재한다. 
해당 WWDC에서는 Allocation 부분을 설명하며 해당 내용을 다룬다 )</p>
<p>그러니 문득 이러한 생각이 들었다. </p>
<blockquote>
<p>&#39;비용&#39;을 낮추려면(성능 높임) 무조건 딱딱한 점토(Struct)를 써야 하는 게 아닌가? </p>
</blockquote>
<h2 id="성능-무조건-좋아야-좋은-게-아닌가">성능? 무조건 좋아야 좋은 게 아닌가?</h2>
<p>애초에 해당 영상을 알아서 무엇이 좋은 지 이해가 되지 않았다. </p>
<p>그러나 위 질문은 *<em>무조건 싼 코드가 좋은가? *</em>로 바꾸어 적으면 이해에 도움이 되는 것 같다. </p>
<p>뭔들 무조건 싼 게 좋은 건 아니었는데...? 란 추측으로 이어지기 때문이다. </p>
<p>해당 세션에서는 언급되지 않지만 (당연해서 그런 걸지도 모르겠다... 하지만 난 몰라서 이해가 어려웠다) <strong>Swift 성능 최적화에는 당연히 전제된 상황</strong>이 존재해 보인다.</p>
<p>(이를 짚고 넘어가지 않으면 <strong>무조건 struct 쓰면 좋은 건가?</strong> 하고 생각하게 된다)</p>
<p>처음으로 돌아가서, </p>
<blockquote>
<p>성능평가에 대한 기준을 알아야 하는 이유는 뭘까. </p>
</blockquote>
<p>내 나름 합리적으로 추측한 바는 아래와 같다. </p>
<p>성능평가는 원하는 상황에 대한 <strong>상대평가</strong>이다. <strong>원하는 상황</strong>이 인스턴스를 힙에 할당하고 참조하는 것이 <code>적합</code>하다면 <strong>struct</strong>로 변환하여 성능을 높이는 일이 <strong>필요치 않다</strong>는 것이다. </p>
<p>예를 들어보자. </p>
<p>이곳저곳에서 접근해도 <strong>유연하게 움직이는 인형</strong>을 만들고자 한다. </p>
<p>이는 <strong>비싼 기능</strong>이기 때문에 원재료값으로 비싼 값을 지불해야함이 당연하다. 그러나 싸다는 이유로 딱딱한 점토를 구입하여 <strong>만들고자 한 인형</strong>을 만들지 못하게 된다면?</p>
<p>애초에 기획 의도를 따르지 않았으니 결과물이 산으로 간 셈이다. </p>
<p>그러니 코드의 성능을 고려하고자 한다면, </p>
<p><strong>1)</strong> 내 코드에서 위의 세 기준 중
<strong>2)</strong> 불필요한 값이 지불되고 있는 지 확인하고 
<strong>3)</strong> <strong><em>합의가 가능한 부분</em></strong>을 최대한 줄이면 </p>
<p>코드의 성능을 높일 수 있을 것이다. </p>
<p>정리하자면, </p>
<blockquote>
<p>내 코드가 얼마나 많은 비용을 사용하고 있는 지, 
그게 타당한 지출인 지를 알고자 한다면 값을 치루는 기준, 위의 세 기준을 알 필요가 있다.</p>
</blockquote>
<p>이러한 생각을 거치고 났더니 (다행히) <a href="https://developer.apple.com/videos/play/wwdc2016/416/">WWDC 2016 Understanding Swift Performance</a> 세션을 재미있게 볼 수 있었다. </p>
<blockquote>
<p><strong>필요한 동작을 충족</strong>하는 코드라면 싸면 쌀 수록 좋다 ! </p>
</blockquote>
<hr>
<p>애초에 왜 해당 세션이 편성되었는가-- 에 대해 이해하기 위한 과정이었습니다.</p>
<p>벨로그에 팔로우 기능이 생겨서 신기합니다! </p>
<p>댓글은 늘 감사히 읽고 있으니 문제 되는 부분이 있다면 언제든 편히 알려주세요! </p>
<p>감사합니다! 아요쓰 화이팅 ! </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SwiftUI : cornerSize와 cornerRadius의 차이 ]]></title>
            <link>https://velog.io/@haeun_/SwiftUI-cornerSize%EC%99%80-cornerRadius%EC%9D%98-%EC%B0%A8%EC%9D%B4</link>
            <guid>https://velog.io/@haeun_/SwiftUI-cornerSize%EC%99%80-cornerRadius%EC%9D%98-%EC%B0%A8%EC%9D%B4</guid>
            <pubDate>Thu, 31 Aug 2023 19:27:18 GMT</pubDate>
            <description><![CDATA[<p>SwiftUI로 둥근 사각형을 만들기 위해 값을 찾다가 문득 의문이 생겼다.</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/a6f010c4-0dd6-4c6a-86a9-7694af139828/image.png" alt=""></p>
<p>바로 cornerSize와 cornerRadius의 차이가 뭐냐는 것이다.</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/3f911309-88bf-47be-8a27-ccf5b7917bc6/image.png" alt=""></p>
<p>우선 둥근 사각형을 만들기 위한 cornerSize는 <code>CGSize</code>를 받는다.</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/c51e7ee1-aafc-4ec6-be3b-5c0b1dd23430/image.png" alt=""></p>
<p>cornerRadius는 <code>CGFloat</code>을 받는다.</p>
<p>CGFloat은 단일 값이고 CGSize는 <code>가로</code>와 <code>세로</code> 두 가지 값을 받기 때문에</p>
<p>곡선을 (round) 만들기 위해
원 반지름만 필요로 하냐(<code>CGFloat</code>)  
아니면 가로 세로를 조절하여 좀 더 디테일한 설정을 잡고 싶냐(<code>CGSize</code>) 의 차이일 것이라 추측한다. </p>
<h1 id="cornerradius">cornerRadius</h1>
<p><img src="https://velog.velcdn.com/images/haeun_/post/a653536a-dfbd-4f89-8bf7-d94d5b8cba4d/image.png" alt=""></p>
<p>Radius라는 표현을 썼으니 cornerRadius는 반지름 길이를 뜻함을 추측할 수 있다.</p>
<p>만약 반지름 길이로 모서리를 자른다는 게 무슨 뜻인지 헷갈린다면, 
반지름 길이로 원을 만들어 그를 각 모서리에 갖다 대고, 튀어나간 부분을 모조리 잘라낸다고 생각하면 된다. </p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/5495ab47-abf2-4cb8-8d70-04daf2d45e4f/image.png" alt=""></p>
<p>빨간 실선이 바로 결과값이 된다. </p>
<p>코드로 확인해보자.</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/2f3d1441-3e93-4c09-8f0b-befe2c8a8a54/image.png" alt=""></p>
<p>흡사하게 잘 그린 것 같다. </p>
<p>그렇다면 size로는 어떻게 만드는 걸까.</p>
<h1 id="cornersize">cornerSize</h1>
<p><img src="https://velog.velcdn.com/images/haeun_/post/ac259f52-afb1-42c4-8bd5-644676238896/image.png" alt=""></p>
<p>검은색이 radius, 파란색이 cornerSize를 파라미터로 받는 도형이다.</p>
<p>우선 size를 20씩 주었을 때 radius와 같은 형태를 보임을 확인할 수 있다.</p>
<p>이러한 결과를 통해 각 모서리에 수직인 width와 heigth가 만나는 점의 위치에 모서리를 찍고 원을 그리고 있는 게 아닐까 추측해 본다. 
(검증 상 반은 맞고 반은 틀림)</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/715a40c4-e871-43b3-ba83-637eeba0e729/image.png" alt=""></p>
<p>각 모서리로부터의 거리를 통해 하나의 원을 특정할 수 있기 때문이다.</p>
<p>(애플의 공식 문서 내용이 상당히 심플하여 나름의 추측을 해본다)</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/e1603802-336f-459e-a483-4bdab33f4e29/image.png" alt=""></p>
<p>확인을 위해 만약 가로가 60이고 세로가 10이라고 생각해 보자.</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/5fc59039-f89b-47b8-8035-872fc9ed2dcc/image.png" alt=""></p>
<p>위 추측대로라면 위아래가 뾰족한 둥근 육각형이 나올 것이다. </p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/cf23f1e4-d9e2-41ca-ad56-f9f8d96027f8/image.png" alt=""></p>
<p>실제론 뾰족하지 않고 둥근 도형이 나왔다.</p>
<p>아마 Swift 내부적으로 사각형의 가로 / 2 를 넘어가는 길이는 가로 / 2를 넘어가지 못하도록 처리하고 있는 게 아닐까 싶다. (그래야 뾰족하지 않고 둥근 모양이 되기 때문에)</p>
<p>radius가 그랬듯이 말이다.</p>
<p>확인해 보기 위해 코드를 작성한다.</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/bc1fa4da-a92d-42cf-871c-621c84e953f3/image.png" alt=""></p>
<p>위의 노란색 네모는 width가 20으로 사각형 가로 길이의 절반(40)보다 작고, 나머지는 전부 cornerSize의 width가 사각형 가로길이의 절반(40)보다 크다</p>
<p>우선 40보다 큰 사각형을 겹쳐서 튀어나오는 점이 있는 지 보자.</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/d4b58745-a473-495c-9a10-bd3fc4fe8c0f/image.png" alt=""></p>
<p>혹시몰라 최하단에 빨간색을 칠했지만 튀어나오는 부분이 없다.</p>
<p>그렇다면 cornerSize width가 40보다 작은 사각형을 겹치면 어떨까</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/f7297b65-b64e-4465-9c35-eb2b10c3151b/image.png" alt=""></p>
<p>노란색이 튀어나옴을 확인할 수 있다. </p>
<p>그리자면 이런 상황일 것이다</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/3e14f3a5-f910-49af-96b9-3f1353c44ba5/image.png" alt=""></p>
<p>더 자세한 내용은 그래픽에 대한 내용을 다루며 깊게 생각해 보아야 할 것 같다.</p>
<blockquote>
<p>중요한 건 일관적인 형태의 둥근 사각형을 필요로한다면 cornerRadius를 사용하면 된다는 점이다!</p>
</blockquote>
<hr>
<p>단순한 내용이지만 궁금증이 생겨 천천히 생각하는 시간을 가졌다. </p>
<p>보다 정확한 정보를 습득한다면 포스팅을 수정하려 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Class, Struct 사용 판단]]></title>
            <link>https://velog.io/@haeun_/Tuist-ResourceSynthesizers-%ED%95%A9%EB%A6%AC%EC%A0%81%EC%9D%B8-Class-Struct-%EC%82%AC%EC%9A%A9-%ED%8C%90%EB%8B%A8</link>
            <guid>https://velog.io/@haeun_/Tuist-ResourceSynthesizers-%ED%95%A9%EB%A6%AC%EC%A0%81%EC%9D%B8-Class-Struct-%EC%82%AC%EC%9A%A9-%ED%8C%90%EB%8B%A8</guid>
            <pubDate>Tue, 29 Aug 2023 10:41:07 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요! 보노입니다.</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/849f48c6-13e1-4307-9445-cc12fb7035e1/image.png" alt=""></p>
<p>오늘은 Tuist를 사용하며, 자동으로 생성해주는 <strong>Resource 파일 코드</strong>를 보다 느낀 의문과 이해 과정에 대해 기록해 보려 합니다.</p>
<p>기록에 의미를 두고 편한 말투를 사용하나 모르는 것이 많습니다.</p>
<p>꼭꼭 질문 사항이나 피드백 사항이 있다면 댓글 부탁드립니다! </p>
<hr>
<h1 id="서론--xcode-instruments">서론 : Xcode Instruments</h1>
<p>최근 Xcode Instruments에 관심이 많아서 이것저것 눌러보고 있다.</p>
<p>가장 관심있게 보고 있는 것은 leak 이다. </p>
<p>최근 진행 중인 프로젝트에 Instruments leak을 돌리자 특정 class 인스턴스가 여럿 생성되는 걸 발견했다.</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/8e36030d-4cc7-44f6-b172-39781096dc24/image.png" alt=""></p>
<p><code>WineyLit.WineyKitColors</code></p>
<p>WineyKit은 내가 진행 중인 프로젝트의 <strong>디자인모듈</strong>(프레임워크) 명이다. 이 곳에 내가 넣어둔 <strong>Font</strong>와 <strong>Color</strong>가 있다. </p>
<p>그리고 이 모듈에서 11번 이나 생성되고 단 한 번도 메모리 해제되지 않은 class, <code>WineyKitColors</code>가 있었다</p>
<p>코드를 찾아보자.</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/be206ee9-a374-405f-8f14-8cbcc9700d63/image.png" alt="">
Tuist가 자동 생성해 준 Asset 파일들 </p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/daa1e3c7-9991-425f-98bf-1de90b5abb4c/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/b259a78b-809a-47d2-86ae-752f91313a78/image.png" alt=""></p>
<p>위의 코드는 <strong>TuistAssets+WineyKit</strong>의 일부로, 내가 넣어둔 Color에 대해 Tuist가 자동 생성해 준 코드이다. </p>
<p>Tuist에서 Resource를 넣으면 자동으로 관리 파일을 생성해 주기에 이제껏 별 생각 없이 사용하고 있었다.</p>
<p>(우선 나는 <strong>SwiftGen</strong> 사용 경험이 없다)</p>
<p>그런데 지금 보니, <strong>WineyKitColors</strong>에 해당하는 코드가 <strong>class</strong>로 생성되어 있음을 알게 되었다.</p>
<p>그러니 색상에 접근할 때 <strong>인스턴스</strong>가 생성되어 <strong>leak</strong> 상에 찍혔던 것이다. (누수가 발생한 것은 아니다)</p>
<hr>
<ul>
<li><p>인스턴스 내부에 <strong>여러 색상</strong>이 전부 들어 있는 게 더 좋지 않나/ 더 일반적이지 않나? </p>
</li>
<li><p>왜 색상을 <strong>struct</strong>가 아닌 <strong>class</strong>로 만들어 <strong>타입변수</strong>로 두는 거지?</p>
</li>
</ul>
<hr>
<p>우선 <strong>첫 번째 의문</strong>은 추가하는 Assets(Color)을 정해진 양식으로 추가해 주기 위해 객체화 한 것으로 판단하고 해결하였다. </p>
<p>하지만 <strong>class</strong> 사용 판단은 여전히 의문이다. </p>
<blockquote>
<p>생각해 보면 나는 class와 sturct를 자의로 판단해 사용해 본 적이 없었다.</p>
</blockquote>
<p>그저 커다란 묶음이면 <strong>class</strong> (UIKit의 ViewController나 ViewModel)</p>
<p>작은 조각이면 <strong>struct</strong>를 사용해 왔다. </p>
<p>통상 대부분의 코드가 그렇게 구성되어 있길래, 체득한 것에 가깝다. 그러니 이론적으로 배운 지식과 실제 개발의 불일치 영역이기도 했다.</p>
<p>class의 상속을 프로젝트에 적용해 본 건 반복적인 작업을 구조화한 BaseViewController 뿐이다. (그 마저도 ViewController를 위한 class)</p>
<p>또한 만일 기능 정의가 필요하다면 대게 protocol과 extension을 사용하였다.</p>
<p>무의식 중에 더 작은 단위가 컨트롤하기 편하다는 생각이 있었기 때문이다.</p>
<h1 id="왜-class-사용">왜 class 사용?</h1>
<p><img src="https://velog.velcdn.com/images/haeun_/post/2cea14c2-9098-447c-a35f-1af6f1e0b4ba/image.png" alt=""></p>
<p>다시 본론으로 돌아와서, 내게 위 코드가 어색하게 느껴지는 이유는 이런 식으로 많은 인스턴스 생성을 필요로 하는 class를 사용해 본 적이 없기 때문이다. </p>
<p>만약 WineyKitColors가 <strong>class</strong>라는 것을 몰랐더라면, 나는 위의 코드를 보고 WineyKitColors가 당연히 <strong>struct</strong> 일 것이라 예상하였을 것이다.</p>
<p>내가 사용한 class는 대게 누군가에게 특정 부분 자유도(확장)을 제공하기 위해 제작된 framework나 library 속 기능, 특정 주제에 대한 코드 묶음 (ViewModel) 이었다.</p>
<blockquote>
<p>확장도, 상속도 일어날 일 없는 기능을 왜 class로 만들었을까.</p>
</blockquote>
<p>여기서 확장과 상속을 고려하지 않았으리라 추측하는 건 해당 class의 init이 fileprivate이기 때문이다. </p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/aae8e9dd-ec52-4887-b755-2e6b4874f057/image.png" alt=""></p>
<p>위 클래스는 <code>TuistAssets+WineyKit.swift</code> 내부에서 외부에 보일 아래 코드의 color들을 규격화된 양식으로 찍어내기 위한 틀의 역할을 수행한다.</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/885aabdf-7791-4658-80fe-420e5b21a827/image.png" alt=""></p>
<p>그래서 왜 class로 만들었을까. </p>
<p>나름의 답을 내리기 위해 WineyKitColors의 코드를 다시 살펴보자.</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/3ee317cb-303f-477f-99e1-4b232e34f809/image.png" alt=""></p>
<p>사실 잘 모르겠다.</p>
<p>그냥 struct로 바꾸어보자.</p>
<h1 id="struct로-바꿔보기">struct로 바꿔보기</h1>
<p><img src="https://velog.velcdn.com/images/haeun_/post/75502a1c-a4f1-48da-9059-38b3d64a225e/image.png" alt=""></p>
<p>struct는 상속을 수행하지 않으므로 <code>final</code>을 지워주고 기존 <code>get</code> 함수가 내부 변수를 변경하므로 <code>mutating</code> 키워드를 추가한다. </p>
<p>아무런 이상 없는데? 라고 생각하는 순간 에러가 하나 발생했다.</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/aafd1f9b-f1e5-4604-82c8-4a6abffc891f/image.png" alt=""></p>
<p>코드 사용 단에서 발생한 문제였다.</p>
<p>에러 내용은 아래와 같다.</p>
<blockquote>
<p>Cannot use mutating getter on immutable value: &#39;gray950&#39; is a &#39;let&#39; constant</p>
</blockquote>
<p>아.</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/f0d3e75f-4d7b-4ff7-bf07-334cbc0e4798/image.png" alt=""></p>
<p>static let으로 선언되어 있던 내부 변수를 static var로 변경해 주니 문제가 사라졌다. 
왜냐하면 </p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/062943ed-7399-43b7-8935-407c1cf9cc3f/image.png" alt=""></p>
<p>현재 Color 값에 접근시 그때야 _swiftUIColor에 값을 할당하기 때문이다.</p>
<blockquote>
<p>let 으로 선언된 <strong>struct</strong>는 속성 변경을 허용하지 않는다.
let 선언 시 인스턴스의 주소값이 고정되고 속성 변경은 허용되는 <strong>class</strong>와는 개념이 다르다. </p>
</blockquote>
<p>이후 build 하면 에러가 사라진다. </p>
<p>또한 정적 변수로 생성된 struct는 메모리에 존재하므로 메모리 leak (누수)를 검사해도 찍히지 않는다.</p>
<p>그래도 실제 앱 사용공간을 차지하기는 마찬가지다. </p>
<p>무엇이 더 실용적인가? </p>
<h1 id="깨달음과-결론">깨달음과 결론</h1>
<p>코드를 이리저리 바꿔보며 <strong>class 사용이 더 이로울 것 같다</strong>는 깨달음을 얻었다.</p>
<p>이유는 아래와 같다.</p>
<blockquote>
<p><strong>class</strong>가 <code>lazy</code> 사용에 더 적합하다.</p>
</blockquote>
<p>위 코드에서 사용된 지연 속성은 static과 lazy이다.</p>
<p>둘 다 접근 시에 활성화되도록 동작한다.</p>
<p>(차이가 있다면 static이 더 thread-safe 하다고 한다)</p>
<p>class 내부 lazy는 접근 시에 값을 활성화 한다는 의미가 있다.</p>
<p>물론 <strong>struct</strong>도 <code>mutating</code>과 함께 <code>lazy</code>를 사용할 수 있지만, &#39;값이 없다&#39;는 상황 자체가 struct 인스턴스의 특징이 되므로 사용상 어색하다.</p>
<p>또한 <code>lazy</code> 상황을 적용하기 위해 <code>static let</code> 이 아닌 <code>static var</code> 라고 표현하는 것도 <code>nameSpace</code> 용도로 사용되는 <strong>enum</strong> 내의 타입 변수로 사용하기엔 어색한 느낌이다. </p>
<pre><code class="language-swift">public enum WineyKitAsset { 
    public static let background1 = WineyKitColors(name: &quot;background_1&quot;)
}</code></pre>
<p><code>static let</code>이 <strong>WineyKitAsset</strong>의 역할을 더 잘 설명한다.</p>
<h1 id="추가--좋아보이는-코드-센스">추가 : 좋아보이는 코드 센스</h1>
<p>또한 위 코드를 뜯어 보며 유익한 코드 습관을 두 가지 습득하였는데 </p>
<p><strong>첫 번째</strong>는 바로 <code>enum</code>과 <code>static let</code> 의 조합이다.</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/27d06486-c705-4982-b334-b602be587888/image.png" alt=""></p>
<p>익숙한 형태의 사용이긴 한데, 이제야 왜 이렇게 자주 쓰는 지 알 것 같다.</p>
<p>enum은 생성자가 없다. </p>
<blockquote>
<p>즉, enum + static 조합은 선택지가 여러 개인 <strong>싱글톤</strong>처럼 보인다.</p>
</blockquote>
<p><strong>두 번째</strong> 유익한 깨달음은 public으로 노출할 코드와, public 내부에서 사용되나 노출하고 싶지 않은 코드를 분리하기 위해 <strong>init</strong>을 <strong>fileprivate</strong>로 설정하는 것이다.</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/f7d56e3a-975a-43d5-b572-f253d3669550/image.png" alt=""></p>
<p>아마 공유 라이브러리를 만든 경험이 있었다면 일찍이 체득했겠지만, 이제야 알았다.</p>
<p>아마 이걸 몰랐다면 숨기고픈 코드도 public으로 열어두고 피눈물만 흘리지 않았을까 싶다.</p>
<h1 id="마치며">마치며,</h1>
<p>왜 이걸 class로 쓸까에 대해 공부하다가 이것저것 많은 생각을 해 보았습니다.</p>
<p>공부가 다 이런거겠죠? 유익했습니다! </p>
<p>혹시 class 사용 이유에 대해 조언해주실 것이 있다면 꼭 댓글 부탁드립니다.</p>
<p>다들 즐거운 개발 되세요! </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[RxSwift / Combine 입문 : 옵저버 패턴의 메커니즘]]></title>
            <link>https://velog.io/@haeun_/%EC%98%B5%EC%A0%80%EB%B2%84-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80-%EB%8B%A4</link>
            <guid>https://velog.io/@haeun_/%EC%98%B5%EC%A0%80%EB%B2%84-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80-%EB%8B%A4</guid>
            <pubDate>Sun, 13 Aug 2023 14:58:55 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요 보노(해언)입니다! </p>
<p>최근 겪은 몇몇 상황과 책을 읽던 중 얻은 깨달음을 토대로 포스팅을 작성합니다. 본래는 고차함수에 대한 포스팅을 기획했으나, 개념을 확장하여 옵저버 패턴을 다루려 합니다.</p>
<ol>
<li>옵저버 패턴의 기초</li>
<li>고차함수를 통한 옵저버 패턴의 응용 </li>
</ol>
<p>로 진행되는 서술입니다. </p>
<p>개인적인 생각이지만 옵저버 패턴에 <code>고차함수</code>가 적용된 구간을 이해하고나면 옵저버 패턴의 구성을 이해할 수 있으리라 생각합니다.</p>
<p>본 포스팅을 통해 아래와 같은 코드의 작동 메커니즘을 추측할 수 있습니다</p>
<pre><code class="language-swift">let subject = PublishSubject&lt;String&gt;()

// 구독
subject
    .subscribe(onNext: { message in
        print(&quot;Received event with message: \(message)&quot;)
    })
    .disposed(by: disposeBag)

// 이벤트 발행
subject.onNext(&quot;Hello, RxSwift!&quot;)</code></pre>
<p>실질적인 코드나 사용법을 다루기 보다는 그를 이해하여 풀어놓은 해설에 가까운 글입니다. (옵저버 패턴의 사용 예를 다루지는 않습니다)</p>
<p><strong>RxSwift</strong>, <strong>Combine</strong> 등 <code>Reactive programing</code>을 접하며 <strong>옵저버 패턴</strong>에 대해 관심이 생겼으나, 아직 이해가 부족한 분들께 도움이 되는 글이길 바랍니다 </p>
<hr>
<h1 id="옵저버-패턴">옵저버 패턴?</h1>
<blockquote>
<p>옵서버 패턴(observer pattern)은 <code>객체</code>의 상태 변화를 관찰하는 <code>관찰자</code>들, 즉 옵저버들의 목록을 객체에 등록하여 상태 변화가 있을 때마다 메서드 등을 통해 객체가 직접 목록의 각 옵저버에게 통지하도록 하는 디자인 패턴이다.</p>
</blockquote>
<blockquote>
<p>주로 분산 이벤트 핸들링 시스템을 구현하는 데 사용된다. 발행/구독 모델로 알려져 있기도 하다.
<a href="https://ko.wikipedia.org/wiki/%EC%98%B5%EC%84%9C%EB%B2%84_%ED%8C%A8%ED%84%B4">출처 위키백과</a></p>
</blockquote>
<p>(<del>역시 근본 개념은 눈에 잘 안들어온다</del>)
간단히 말해, 옵저버 패턴은 <strong>1:N으로 상태 변화 전달이 가능한 디자인 패턴</strong>이다</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/eaf9cc3c-f588-4179-b03f-3c10e3633580/image.png" alt=""></p>
<p>위의 그림은 옵저버 패턴에 대한 이해를 돕기위한 그림이다. </p>
<p><code>신문</code> 이라는 <strong>이벤트</strong>가 <strong>Publisher</strong>에 의해 발행되고, 그걸 Pubilser를 구독한 <strong>Observer</strong>들이 수신하는 상황이다. </p>
<p>이처럼 발행할 이벤트(신문)을 가진 객체를 <code>발행자</code>라고 부르고, 해당 객체의 변화를 전달 받기를 희망하는 객체들을 <code>구독자</code>라고 부른다. (꽤나 직관적인 명명이라고 생각한다)</p>
<p>더불어 <code>발행자</code>와 <code>구독자</code>를 부르는 명칭은 예제 코드마다 다르다. </p>
<p>본 포스팅에서는 <strong>발행자</strong>를 <code>Publisher</code>, <strong>구독자</strong>를 <code>Observer</code> 라 부르려 한다. 포스팅 마다 발행자를 Obserable, 구독자를 Subsriber 라 부르는 등 용어에 차이가 있다. </p>
<hr>
<p>+) </p>
<p>RxSwift / Combine에는 발행자와 구독자의 역할을 세분화하거나, 혼합하여 만든 추가적인 개념이 있다. (이벤트 또한 발행 형태가 다양하다)</p>
<p>그러나 근간은 <code>발행자</code>와 <code>구독자</code>이다. </p>
<p>무엇이 되었건 <code>발행자</code>와 <code>구독자</code>를 지칭하는 명칭이 무엇인지 확인하고 읽으면 코드를 읽을 수 있다.</p>
<hr>
<p>옵저버 패턴에 대한 서론은 끝났다. </p>
<p>그래서 옵저버 패턴은 어떻게 구성되어 있길래 1:N 관계를 어떻게 성립시킨 걸까? </p>
<p>뜯어 볼 예시 코드를 <strong>ChatGPT</strong>에게 요청해 보자</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/5ddf8b7d-bf45-4f0d-b6c7-13066110b347/image.png" alt=""></p>
<p>아래는 ChatGPT의 답변에 해당한다 </p>
<p>당장은 이해보다 등장하는 키워드와 구성에 집중해 보자 </p>
<h3 id="예제-코드-1">예제 코드 (1)</h3>
<pre><code class="language-swift">// Observer 프로토콜 정의
protocol Observer: AnyObject {
    func update(_ message: String)
}

// Publisher 클래스
class Publisher {
    private var observers: [Observer] = []
    private var state: String = &quot;&quot; {
        didSet {
            notify()
        }
    }

    // 상태를 변경하고 관찰자들에게 알림
    func changeState(_ newState: String) {
        self.state = newState
    }

    func add(observer: Observer) {
        observers.append(observer)
    }

    func remove(observer: Observer) {
        if let index = observers.firstIndex(where: { $0 === observer }) {
            observers.remove(at: index)
        }
    }

    private func notify() {
        observers.forEach { $0.update(state) }
    }
}

// Observer를 구현한 ConcreteObserver 클래스
class ConcreteObserver: Observer {
    let name: String

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

    func update(_ message: String) {
        print(&quot;\(name) received state update: \(message)&quot;)
    }
}</code></pre>
<h3 id="실행-코드-2">실행 코드 (2)</h3>
<pre><code class="language-swift">
// 예제 실행
let publisher = Publisher()

let observer1 = ConcreteObserver(name: &quot;Observer1&quot;)
let observer2 = ConcreteObserver(name: &quot;Observer2&quot;)

publisher.add(observer: observer1)
publisher.add(observer: observer2)

publisher.changeState(&quot;New State!&quot;)

// 출력:
// Observer1 received state update: New State!
// Observer2 received state update: New State!</code></pre>
<p><strong>ConcreteObserver</strong>이란 구독자 클래스는 <code>Observer</code> <strong>프로토콜</strong>을 채택하여 생성되었다.</p>
<p>Observer 프로토콜이 Publisher와 ConcreteObserver 인스턴스의 연결다리(추상화)인 셈이다 </p>
<p><strong>Publisher</strong>는 <strong>add</strong> 메서드를 통해 자신이 발행할 이벤트 수신을 원하는 <strong>Observer 인스턴스</strong>를 저장해 두었다가 <strong>이벤트</strong>가 변경되면 (<strong>changeState를 통해 이벤트가 수신</strong>되면) 해당 이벤트를 observers의 <strong>update 메서드</strong>를 통해 <strong>구독자들</strong>(<strong>observers</strong>)에게 알린다. </p>
<p>순서를 그림으로 그리자면 아래와 같다 </p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/4d281394-d0be-419b-84ca-96a55d10f723/image.png" alt=""></p>
<p>코드와 함께 보면 아래와 같다 
<img src="https://velog.velcdn.com/images/haeun_/post/ab897711-46ac-4d33-ae85-380eb1e89ca6/image.png" alt="">
<img src="https://velog.velcdn.com/images/haeun_/post/d2dbff61-6db6-4a7b-9a92-6b5098f4e801/image.png" alt=""></p>
<p>그럼 발행자인 Publisher는 언제 observers를 가지게 되었을까?  </p>
<p>Publisher는 add 메서드를 통해 observer를 전달받아 자신의 observers 배열에 추가하였다 </p>
<pre><code class="language-swift">func add(observer: Observer) {
        observers.append(observer)
    }</code></pre>
<p>실제 구현에 해당하는 구간을 다시한번 살펴보자</p>
<pre><code class="language-swift">let publisher = Publisher() // 나는 발행자

let observer1 = ConcreteObserver(name: &quot;Observer1&quot;) // 나는 옵저버1
let observer2 = ConcreteObserver(name: &quot;Observer2&quot;) // 나는 옵저버2

publisher.add(observer: observer1) // 옵저버1은 발행자의 observers에 추가될게
publisher.add(observer: observer2) // 옵저버2는 발행자의 observers에 추가될게

publisher.changeState(&quot;New State!&quot;) // 상태가 변경된다면 ...</code></pre>
<p>1) Publisher는 Observer 타입의 배열에 구독자들을 가지고, 
2) 이벤트 변경(수신) 시 
3) Observer들의 update 메서드를 호출하여 이벤트를 전달한다</p>
<pre><code class="language-swift">let publisher = Publisher() // 나는 발행자</code></pre>
<p>중요한 점은 발행자(Publisher)는 자기 자신만으로는 이벤트 수신 시 실행시킬 update 메서드를 알지 못한다는 것이다.</p>
<p>실행할 update 메서드를 알게 되는 시점은 </p>
<pre><code class="language-swift">publisher.add(observer: observer1)
publisher.add(observer: observer2)</code></pre>
<p>구독자가 생긴 시점 이후이다. </p>
<p>즉, 옵저버 패턴의 핵심은 아래와 같다. </p>
<blockquote>
<p><strong>⭐️ Publisher</strong>가 자신의 <strong>observers(구독자들)</strong>에게 <strong>state(이벤트)</strong>를 전달해 주기 위해 실행하는 <strong>update 함수</strong>의 구현 내용을 모른다 **</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/haeun_/post/a157851d-8c45-40b6-9ad0-976d2bd16bcc/image.png" alt=""></p>
<p>Publisher가 실행시킬 update 메서드는 Observer가 정의한 것이다.</p>
<p>왜 Publisher가 Observer의 메서드를 실행해야 할까?</p>
<p>그건 Publisher가 이벤트의 수신 시점(생성 시점)을 알고, 해당 이벤트를 원하는 Observer는 그 시점을 모르기 때문이다.</p>
<p>그러니 옵저버 패턴의 작동 메커니즘은 </p>
<blockquote>
<p><strong>이벤트 발생 시점</strong>을 아는 <strong>Publisher</strong>가 Observer들이 자신의 이벤트를 전달 받아 수행하고자 하는 <strong>행동</strong>을 가지고 있다가, 이벤트 생성 시점에 <strong>대신 실행</strong>해주는 것이다. </p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/haeun_/post/044e73df-35a3-418f-920c-d7c47d5dbf69/image.png" alt=""></p>
<p>좀 더 구체적인 이해를 위해 다른 코드를 들어보자 </p>
<h1 id="구독자observer가-하고-싶은-일">구독자(Observer)가 하고 싶은 일</h1>
<p>이전에 설명한 코드는 모든 옵저버들이 이벤트를 전달 받았을 때 </p>
<pre><code class="language-swift">func update(_ message: String) {
        print(&quot;\(name) received state update: \(message)&quot;)
    }</code></pre>
<blockquote>
<p>(내 이름) received state update: (전달 받은 이벤트)</p>
</blockquote>
<p>지정된 양식으로만 행동할 수 있었다</p>
<p>하지만 아래와 같은 상황을 고려해 보자. </p>
<blockquote>
<p>Observer1 : 너 신문 발행해 주는 애지? 나 너 구독할테니까 신문 나오면 줘.
나 신문 나오면 <strong>부모님께 드려야 하거든</strong></p>
</blockquote>
<blockquote>
<p>Observer2 : 너 신문 발행해 주는 애지? 나 너 구독할테니까 신문 나오면 줘.
나 신문 보고 <strong>학교 숙제해야 하거든</strong></p>
</blockquote>
<p>두 옵저버가 같은 이벤트를 전달받았을 때 해주고 싶은 일이 다르다.</p>
<p>그러니 지금처럼 update 함수 내용을 ConcreteObserver class 내부에 고정할 수가 없다. </p>
<p>그렇다면 어떻게 해야할까.</p>
<p>우선 발행자와 구독자의 관계를 이해해보자. </p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/75ddd07a-f5cc-458e-816b-83cb676261a5/image.png" alt=""></p>
<blockquote>
<p>Publisher : 데이터 생성 시점을 앎. 그거 가지고 뭐 할 지를 모름 
Observer : 데이터를 가지고 하고 싶은 게 있음. 그 데이터 생성 시점을 모름</p>
</blockquote>
<p>역시나 중요한 것은 데이터(이벤트)의 수신 시점을 아는 것이 <strong>Publisher</strong>란 것이다.</p>
<p>그러니 Publisher는 Observer에게 행동을 전달받아, 적절한 시점에 대신 실행해 주고자 한다.</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/4546572d-46a2-460f-a396-35ce58a2137e/image.png" alt=""></p>
<p>Publisher는 이벤트 생성 시점에 빈 칸을 뚫어 옵저버가 원하는 행동을 실행시킬 공간을 마련해 둔다. </p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/c4279cd0-8cec-4962-aa7d-6610ba8f6527/image.png" alt=""></p>
<p>위 내용을 코드로 살펴보자. </p>
<h3 id="예제-코드-2">예제 코드 (2)</h3>
<pre><code class="language-swift">class Publisher {
    private var observers: [(String) -&gt; Void] = []
    private var state: String = &quot;&quot; {
        didSet {
            notify()
        }
    }

    func changeState(_ newState: String) {
        self.state = newState
    }

    func subscribe(_ closure: @escaping (String) -&gt; Void) {
        observers.append(closure)
    }

    private func notify() {
        observers.forEach { $0(state) } //여기가 빈 칸. 
        //Publisher 입장에서는 외부에서 주입 받을 옵저버의 행동 실행 공간
    }
}</code></pre>
<p>Publisher는 <code>subscribe</code> 메서드를 통해 이벤트 생성 시 처리해 줄 구독자의 행동을 외부로부터 주입받는다. </p>
<p>Publisher는 이벤트가 생성 될 때 <code>notify()</code>를 실행하여 저장해 둔 구독자들의 행동에 이벤트를 넣어 실행시킨다 </p>
<h3 id="실행-코드-2-1">실행 코드 (2)</h3>
<pre><code class="language-swift">
// 예제 실행
let publisher = Publisher()

publisher.subscribe { message in
    print(&quot;Observer1 received state update: \(message)&quot;)
}

publisher.subscribe { message in
    print(&quot;Observer2 received state update: \(message)&quot;)
}

publisher.changeState(&quot;New State!&quot;)

// 출력:
// Observer1 received state update: New State!
// Observer2 received state update: New State!</code></pre>
<p><strong>Publisher</strong>는 <code>func subscribe(_ closure: @escaping (String) -&gt; Void)</code> 함수를 통해 이벤트 수신 시 실행되길 원하는 동작을 <code>클로저</code> 형태로 전달 받는다.</p>
<p>이 <strong>클로저</strong>는 <strong>Publisher</strong> 내부에 배열 형태로 저장되며, 이벤트 발생 시 실행된다.</p>
<p><strong>클로저</strong>를 함수의 <code>인자</code>로 전달하고, 배열에 <code>저장</code>한 후, 필요한 시점에 <code>실행</code>하는 이러한 접근법은 <strong>클로저</strong>의 <code>일급 객체</code> 특성과 함수가 클로저 형태의 파라미터를 받는 <strong>고차 함수</strong>의 장점을 잘 반영한다.</p>
<h1 id="그래서-발행자의-역할은">그래서 발행자의 역할은,</h1>
<p><img src="https://velog.velcdn.com/images/haeun_/post/15a77d09-72b6-49c7-bd8a-f79757d9b007/image.png" alt=""></p>
<p>위 그림은 내 나름의 유머로, 저 자체가 정답이다. </p>
<p>발행자가 이벤트 수신 시 실행할 메서드의 내용을 모른 채 정의되었다는 점을 강조하고 싶었다.</p>
<p>굳이 정답을 적는다면 이렇게 적을 수 있을 것 같다 </p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/83a7fa69-7aac-48bd-953c-ca5e39cdb379/image.png" alt=""></p>
<h1 id="객체지향적-특징">객체지향적 특징</h1>
<p>옵저버 패턴은 <strong>객체지향 디자인 패턴</strong> 중 하나로, 객체 간의 의존성을 줄이면서 하나의 객체의 상태 변경에 대해 다른 객체들에게 알릴 수 있는 방식을 제공한다. </p>
<p>나는 아직 <code>객체지향적</code> 이라는 말이 와닿지 않아 <strong>옵저버 패턴</strong>이 객체지향적인 특징을 갖는 부분을 정리하며 글을 마무리하려 한다.</p>
<p><code>캡슐화</code> : 옵저버 패턴에서 <strong>Publisher</strong>는 자신의 상태와 상태 변경 로직을 캡슐화한다. <strong>Observer</strong>는 이벤트에 반응하는 로직만을 캡슐화한다. </p>
<p>즉, 객체 간 <strong>책임</strong>이 명확해 진다. </p>
<p><code>다형성</code> : Observer 인터페이스 (또는 추상 클래스)를 구현한 여러 구체적인 클래스들은 다양한 방식으로 이벤트를 처리할 수 있다. </p>
<p>이로 인해 여러 타입의 옵저버가 하나의 주제에 대해 다양한 동작을 수행할 수 있다. (첫번째 예시에서 Observer 프로토콜을 채택하는 또 다른 클래스를 생성해주면 된다)</p>
<p><code>의존성 역전</code> : <strong>Publisher</strong>는 <strong>Observer</strong> 인터페이스에만 의존하며, 구체적인 <strong>Observer</strong> 구현에는 의존하지 않는다. 
즉 고수준의 모듈 <strong>Publisher</strong>와 저수준의 모듈 <strong>Observer</strong> 간의 직접적인 의존성이 제거된다. </p>
<p><code>재사용성</code> : 옵저버 패턴을 사용하면, <strong>Publisher</strong>와 <strong>Observer</strong> 사이의 결합도가 낮아져 각각 독립적으로 재사용될 수 있다.</p>
<p><code>확장성</code> : <strong>Observer</strong>를 추가하거나 기존의 Observer를 삭제하는 게 <strong>Publisher</strong>에 영향을 주지 않는다. </p>
<p>이러한 특징들은 객체지향 프로그래밍의 핵심 원칙들을 잘 반영하며, 객체 간의 의존성을 최소화하고 변경에 유연하게 대응할 수 있게 해준다.</p>
<h1 id="정리">정리</h1>
<p>Publisher는 배열 형태로 Observer의 행동을 관리하므로 발신, 수신 관계를 <code>1:N</code>으로 형성할 수 있습니다.</p>
<p>참고로, RxSwift와 Combine에서 각각을 지칭하는 명칭은 아래와 같습니다. </p>
<blockquote>
<p><strong>RxSwift</strong>
발행자 : <code>Obserable</code> (구독가능한) 
구독자 : <code>Observer</code></p>
</blockquote>
<blockquote>
<p><strong>Combine</strong>
발행자 : <code>Publisher</code>
구독자 : <code>Subscriber</code></p>
</blockquote>
<p>이번엔 정말 짧게 쓰고 싶었는데 마음처럼 잘 되지 않아 아쉽습니다...</p>
<p>글이 길어질 수록 간단히 자주 쓰자는 다짐과는 점점 더 멀어지는 기분이 들지만... 누군가에게 도움이 되는 글이길 바랍니다.</p>
<p>댓글 다 읽고 있으니 궁금한 점이나 오류에 대한 피드백이 있다면 남겨주세요! </p>
<p>감사합니다! 다들 즐거운 개발 되세요! </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[UIKit] iOS 화면 간 데이터 전달방식: Delegate와 Closure]]></title>
            <link>https://velog.io/@haeun_/UIKit-%ED%99%94%EB%A9%B4-%EA%B0%84-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%84%EB%8B%AC%EB%B0%A9%EC%8B%9D-Delegate%EC%99%80-Closure</link>
            <guid>https://velog.io/@haeun_/UIKit-%ED%99%94%EB%A9%B4-%EA%B0%84-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%84%EB%8B%AC%EB%B0%A9%EC%8B%9D-Delegate%EC%99%80-Closure</guid>
            <pubDate>Fri, 21 Jul 2023 13:23:01 GMT</pubDate>
            <description><![CDATA[<h3 id="서론">서론</h3>
<p>안녕하세요. 보노(해언)입니다.</p>
<p><strong>RxSwift</strong>와 <strong>Combine</strong>에 대한 포스팅을 작성하기 앞서 <strong>기본</strong>이 되는 두 가지 데이터 전달 방식에 대해 포스팅을 하려 합니다. </p>
<p><strong>전송 화면</strong> / <strong>수신 화면</strong> 에 따른 코드 처리가 늘 헷갈렸는데 라이브 코딩으로 끝장나는 데이터 전달 설명을 해준 <code>메이슨</code>에게 감사 인사를 전합니다. </p>
<p>요즘 푸바오가 핫하기 때문에🐼 관련된 소소한 비유로 풀어보았습니다. 나름 적절한 상황 묘사를 하고 싶었던 것인데... 다 쓰고보니 간결하게 쓰지 못한 것에 대한 아쉬움이 남습니다. </p>
<p>코드 읽기가 어렵다면 마지막 정리 글만 보셔도 좋을 것 같습니다.</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/b476b886-c745-4e67-a3c5-263f47823af2/image.jpeg" alt=""></p>
<h1 id="양방향-데이터-전달-상황">양방향 데이터 전달 상황</h1>
<blockquote>
</blockquote>
<p><strong>사육사VC</strong>가 <strong>푸바오VC</strong>에게 <code>먹이</code>를 전달하려 합니다. 
<strong>푸바오VC</strong>는 자신이 사라질 때 <code>배부름(Bool)</code> 상태를 <strong>사육사VC</strong>에게 전달하려 합니다.</p>
<blockquote>
<p>사육사VC : 
1️⃣ 먹이를 가지고 있음 
2️⃣ <strong>푸바오VC에게 먹이를 주고 싶음</strong> 
데이터 전달 : <code>사육사 -&gt; 푸바오</code></p>
</blockquote>
<blockquote>
</blockquote>
<p>푸바오 VC : 
1️⃣ 먹이를 손에 쥘 수 있음 
2️⃣ 손에 쥔 먹이를 먹을 수도, 먹지 않을 수도 있음
3️⃣ <strong>배 부른지 아닌지 사라지기 전에 사육사에게 알려주고 싶음</strong> 
데이터 전달 : <code>푸바오 -&gt; 사육사</code></p>
<p>(단순 학습용으로 작성하여서... 세세히 읽지 않는 것을 권합니다!)
(필요한 코드엔 이모지를 달아두었습니다)</p>
<p><strong>기본 구성</strong></p>
<pre><code class="language-swift">import UIKit

enum Food {
  case 죽순
  case 고구마
}

// MARK: - 사육사
final class 사육사ViewController: UIViewController {
  override func viewDidLoad() {
    super.viewDidLoad()
    self.view.backgroundColor = .white

    DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
      let 푸바오VC = 푸바오ViewController()
      let food: Food = .죽순
      print(&quot;1️⃣ 📦 먹이 전송 \(food)&quot;)
      푸바오VC.손에쥐어줌(food: food)
      self.present(푸바오VC, animated: true)
    }
  }

  func 푸바오밥먹었니(state: Bool) {
    print(&quot;4️⃣ 📮 전달받은 배부름 상태 \(state)&quot;)
  }
}

// MARK: - 푸바오
final class 푸바오ViewController: UIViewController {
  private var 손에쥐고있는: Food?
  private var 배부름: Bool = false

  override func viewDidLoad() {
    super.viewDidLoad()
    self.view.backgroundColor = .black
  }

  deinit {
    print(&quot;3️⃣ 📦 배부름 상태 \(배부름) 전송&quot;) //이 부분이 문제
  }

  func 손에쥐어줌(food: Food) {
    print(&quot;2️⃣ 📮 먹이 전달 받음 \(food)&quot;)
    self.손에쥐고있는 = food
    DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
      self.식사()
    }
  }

  func 식사() {
    guard let food = self.손에쥐고있는 else { return }

    switch food {
    case .죽순:
      배부름 = true //죽순이면 먹고
    case .고구마:
      배부름 = false //고구마면 안 먹음
    }
    self.dismiss(animated: true)
  }
}
</code></pre>
<h3 id="문제-상황--푸바오가-사육사에-접근할-방법이-없음-">*<em>문제 상황 : 푸바오가 사육사에 접근할 방법이 없음 *</em></h3>
<pre><code class="language-swift">//푸바오ViewController
  deinit {
      //해제될 때 사육사VC에게 접근할 방법 필요
  }</code></pre>
<h1 id="delegate">Delegate</h1>
<p>해당 방법에서 교류하는 두 VC는 </p>
<ul>
<li>Delegate 채택 VC : 사육사 </li>
<li>Delegate 사용 VC : 푸바오</li>
</ul>
<p>로 나뉜다. </p>
<p>네 가지 단계만 기억하면 된다 (코드상의 숫자는 실제 실행 순서로 단계와 무관)
1️⃣ 접근하고 싶은 타 객체의 메서드를 protocol로 생성 </p>
<pre><code class="language-swift">protocol 사육사Delegate {
  func 푸바오밥먹었니(state: Bool)
}</code></pre>
<p>2️⃣ 데이터 전송 객체는(푸바오VC) delegate 변수를 생성하여 원하는 시점에 메서드 실행 </p>
<pre><code class="language-swift">final class 푸바오ViewController: UIViewController {
    var delegate: 사육사Delegate?
  ...

  deinit {
    print(&quot;2️⃣-1 📦 배부름 상태 \(self.배부름) 전송&quot;)
    delegate?.푸바오밥먹었니(state: self.배부름)
  }

  ...
}
</code></pre>
<p>3️⃣ 수신 객체는(사육사VC) protocol을 채택하여 해당 메서드 구현 </p>
<pre><code class="language-swift">final class 사육사ViewController: UIViewController, 사육사Delegate {
...
func 푸바오밥먹었니(state: Bool) {
    print(&quot;2️⃣-2 📮 전달받은 배부름 상태 \(state)&quot;)
  }
...
}</code></pre>
<p>4️⃣ ⭐️ 데이터 수신 객체는(사육사VC) 전송 객체(푸바오VC)가 사용할 delegate의 구현자가 &#39;나&#39; 임을 밝힘 ⭐️</p>
<pre><code class="language-swift">override func viewDidLoad() {
    super.viewDidLoad()
    ...

    let 푸바오VC = 푸바오ViewController()
    let food: Food = .죽순

    print(&quot;1️⃣-1 📦 먹이 전송 \(food)&quot;)
    푸바오VC.손에쥐어줌(food: food)
    푸바오VC.delegate = self ⭐️ //(푸바오VC가 delegate 메서드를 통해 나를 쓸 거임 = 데이터를 넣어줄 거임)
    self.present(푸바오VC, animated: true)

  }</code></pre>
<p><img src="https://velog.velcdn.com/images/haeun_/post/397420d0-69e7-452d-ab90-4592d0b229e4/image.png" alt=""></p>
<h3 id="델리게이트-채택-vc와-델리게이트-소유-vc의-구분법">델리게이트 채택 VC와 델리게이트 소유 VC의 구분법</h3>
<p>체득할 정도로 자주 사용한 게 아니라면 delegate의 사용은 각 위치에 뭐가 들어가야 하는 지 헷갈리기 쉽다.</p>
<p>천천히 원리를 생각해보자.</p>
<p>함수는 <code>input</code>과 <code>output</code>으로 구성된다. 
또한 함수를 통해 데이터를 전송한다는 건 <strong>전달자</strong>가 <strong>수신자</strong> 메서드 파라미터에 접근하여 데이터를 넣어 준다는 뜻이다. </p>
<p>그렇다면 누가 메서드를 구현하여 가지고 있어야할까?
즉, 누가 데이터를 필요로 하는가?</p>
<p>그렇다. 데이터를 <strong>원하는 쪽</strong>이 상세 메서드를 <code>구현</code>해야 한다. </p>
<p><code>발송자</code>는 실행할 delegate 메서드가 어떤식으로 동작하는 지 알 필요가 없다. 짜장면을 주문하는 데 요리법을 알 필요 없는 것과 같다. </p>
<p>그러나 요리사와 주문을 엮어주는 과정은 반드시 필요하다. </p>
<blockquote>
<p>생성자.delegate = 수신자</p>
</blockquote>
<h3 id="🛑-주의-순환참조">🛑 주의! 순환참조</h3>
<pre><code class="language-swift">final class 푸바오ViewController: UIViewController {
  var delegate: 사육사Delegate?
  ...
}</code></pre>
<p>은근슬쩍 넘어갔지만, 이러면 순환 참조의 위험이 있으므로 <code>weak</code> 처리를 해줘야 한다. </p>
<pre><code class="language-swift">final class 푸바오ViewController: UIViewController {
    weak var delegate: 사육사Delegate?
  ...
}</code></pre>
<p>현재 코드상으론 weak 처리를 해주지 않아도 메모리 누수가 발생하지 않는다. 왜냐하면 사육사VC가 푸바오VC를 ViewDidLoad 내에서 생성하므로 강하게 참조하지 않는 덕분이다. </p>
<p>그러나 아래와 같은 코드라면 🚨순환 참조 문제🚨가 발생하게 된다.</p>
<pre><code class="language-swift">final class 사육사ViewController: UIViewController, 사육사Delegate {
  var 푸바오VC: 푸바오ViewController? //🚨문제

  override func viewDidLoad() {
    super.viewDidLoad()
    DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {

      self.푸바오VC = 푸바오ViewController() //🚨문제
      let food: Food = .죽순
      print(&quot;1️⃣ 📦 먹이 전송 \(food)&quot;)
      self.푸바오VC?.손에쥐어줌(food: food)
      self.푸바오VC?.delegate = self  //(푸바오VC가 delegate 메서드를 통해 나를 쓸 거임 = 데이터를 넣어줄 거임)
      self.present(self.푸바오VC!, animated: true)
    }
  }
  ...
}</code></pre>
<pre><code class="language-swift">final class 푸바오ViewController: UIViewController {
    var delegate: 사육사Delegate?
}</code></pre>
<blockquote>
<p>*<em>사육사 -&gt; &lt;- 푸바오 *</em></p>
</blockquote>
<p>서로가 서로를 가르키므로 순환 참조가 발생한다. </p>
<p>푸바오가 가진 사육사 <strong>Delegate</strong>와 <strong>사육사ViewController</strong>가 연결되면, 푸바오는 <strong>Delgate</strong>를 통해 사육사의 메서드를 참조하게된다. </p>
<p>그러니 푸바오가 가진 사육사를 향한 참조 (화살표)를 약하게 (weak) 만들지 않으면 푸바오가 화면에서 내려지고도 메모리 해제 되지 않는 문제가 발생한다. </p>
<p>식사 여부 확인 없이 먹이만 전달하고 푸바오VC를 종료하는 코드를 작성해 보자.</p>
<p>화면 제거 시 <code>푸바오VC 사라짐</code>을 출력하고 메모리 해제 시 <code>메모리 해제</code>를 출력하도록 하였다. </p>
<pre><code class="language-swift">// MARK: - 푸바오
final class 푸바오ViewController: UIViewController {
  private var 손에쥐고있는: Food?
  private var 배부름: Bool = false
  var delegate: 사육사Delegate?

  override func viewDidLoad() {
    super.viewDidLoad()
    self.view.backgroundColor = .black
  }

  deinit {
    print(&quot;메모리 해제&quot;)
  }

  func 손에쥐어줌(food: Food) {
    print(&quot;2️⃣ 📮 먹이 전달 받음 \(food)&quot;)
    self.손에쥐고있는 = food
    DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
      self.식사()
    }
  }

  func 식사() {
    guard let food = self.손에쥐고있는 else { return }

    switch food {
    case .죽순:
      배부름 = true //죽순이면 먹고
    case .고구마:
      배부름 = false //고구마면 안 먹음
    }
    self.dismiss(animated: true)
    print(&quot;푸바오VC 사라짐&quot;)
  }
}</code></pre>
<p>실행하면 콘솔 창에는 아래와 같이 출력된다.</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/ffd1a123-5817-495a-8061-da6db4e518e1/image.png" alt=""></p>
<p><code>메모리 해제</code>가 출력되지 않는다. </p>
<p>즉, 화면 상에는 더 이상 푸바오VC가 없음에도 메모리가 해제되고 있지 못하고 누수가 발생하였다. </p>
<p>해결법은 간단하다. </p>
<pre><code class="language-swift">final class 푸바오ViewController: UIViewController {
  weak var delegate: 사육사Delegate?
  ...
}</code></pre>
<p>delegate 변수 선언 시 순환참조가 일어나지 않도록 weak 키워드를 작성하는 것이다. </p>
<h3 id="🛑-weak-키워드-에러-뜨는데">🛑 weak 키워드 에러 뜨는데?</h3>
<p><img src="https://velog.velcdn.com/images/haeun_/post/876872e9-f4c5-4f08-abd5-459d3f661a11/image.png" alt=""></p>
<p><strong>weak</strong>는 <code>약한참조</code>! 참조! 참조! 를 의미한다. </p>
<p>해당 경고는 사육사Delegate를 채택한 것이 클래스(참조 타입)이 아닌 구조체(값 타입)일 수 있으므로 weak 타입이 적절하지 않다고 말하고 있다 (구조체도 프로토콜을 채택할 수 있으므로)</p>
<p>해당 문제는 해당 delegate가 반드시 클래스만을 채택할 것이라고 특정해주면 해결된다. </p>
<pre><code class="language-swift">protocol 사육사Delegate: AnyObject {
  func 푸바오밥먹었니(state: Bool)
}</code></pre>
<p>에러가 사라졌다. </p>
<p> +) AnyObject와 AnyClass의 차이는 무엇일까?</p>
<p>AnyObject는 클래스 인스턴스를 의미하고 AnyClass는 class라는 타입 자체, 메타타입을 의미한다</p>
<h1 id="closure">Closure</h1>
<p>클로저는 헤더가 없는, 이름이 없는 메서드다. 
즉 <code>델리게이트</code>나 <code>클로저</code>나 input으로 데이터를 이동시킨다는 대주제는 같다. </p>
<p>클로저는 <strong>일급 객체</strong>로서 변수로도, 함수의 인자로도 사용될 수 있다. 
클로저를 이용한 데이터 전달은 <strong>파라미터</strong>로 원하는 데이터를 <code>받았다치고!</code> 작업을 서술하는 데 있다. </p>
<p>1️⃣ : 데이터를 <code>수신</code>하는 <strong>VC</strong>가 데이터를 <code>전송</code>하는 <strong>VC의 클로저</strong>를 <strong>정의</strong>한다.
2️⃣ : 데이터를 <code>전송</code>하는 <strong>VC</strong>는 <strong>클로저를 속성</strong>으로 가지고, 실행한다.</p>
<p>delegate와 클로저 방식의 가장 큰 차이점은 클로저는 수신VC가 전송VC의 저장속성인 클로저에 접근하여, 상세 내용을 직접 주입한다는 점에 있다. </p>
<p>delegate는 함수의 구현부와 사용 헤더를 Delegate를 통해 연결해 주는 느낌이었다면, 클로저는 상세 구현을 냅다 꽂아주는 느낌이다. </p>
<p>코드를 보면 이해가 빠를 것 같다. 코드를 보자. </p>
<pre><code class="language-swift">// MARK: - 사육사
final class 사육사ViewController: UIViewController {
  override func viewDidLoad() {
    super.viewDidLoad()
    self.view.backgroundColor = .white

    DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
      let 푸바오VC = 푸바오ViewController()
      let food: Food = .죽순
      print(&quot;1️⃣ 📦 먹이 전송 \(food)&quot;)
      푸바오VC.손에쥐어줌(food: food)

      푸바오VC.밥먹었는지안먹었는지 = { state in
        print(&quot;4️⃣ 📮 전달받은 배부름 상태 \(state)&quot;)
      }
      self.present(푸바오VC, animated: true)
    }
  }
}</code></pre>
<p>사육사VC 내부 코드이다. </p>
<p>핵심 코드는 아래와 같다. </p>
<pre><code class="language-swift">푸바오VC.밥먹었는지안먹었는지 = { state in
    print(&quot;4️⃣ 📮 전달받은 배부름 상태 \(state)&quot;)
}</code></pre>
<p>푸바오VC에 접근하여, 내부에 클로저인 것으로 추정되는 (넣어주는 값이 {} 꼴이므로) <code>밥먹었는지안먹었는지</code> 속성에 세부 구현을 넣어주고 있다. </p>
<p>앞선 delegate 방식이 그러했듯이 데이터를 전달받는 쪽이 함수(클로저)를 구현하고 있다. </p>
<p>그러니 푸바오VC 내부 코드를 보기전에도 푸바오VC에서 해당 클로저를 원하는 타이밍에 실행하겠구나 추측할 수 있다. </p>
<pre><code class="language-swift">// MARK: - 푸바오
final class 푸바오ViewController: UIViewController {
  private var 손에쥐고있는: Food?
  private var 배부름: Bool = false
  var 밥먹었는지안먹었는지: ((Bool) -&gt; Void)? //🐼

  override func viewDidLoad() {
    super.viewDidLoad()
    self.view.backgroundColor = .black
  }

  deinit {
    print(&quot;3️⃣ 📦 배부름 상태 \(배부름) 전송&quot;)
    밥먹었는지안먹었는지?(배부름) //🐼
  }

  func 손에쥐어줌(food: Food) {
    print(&quot;2️⃣ 📮 먹이 전달 받음 \(food)&quot;)
    self.손에쥐고있는 = food
    DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
      self.식사()
    }
  }

  func 식사() {
    guard let food = self.손에쥐고있는 else { return }

    switch food {
    case .죽순:
      배부름 = true //죽순이면 먹고
    case .고구마:
      배부름 = false //고구마면 안 먹음
    }
    self.dismiss(animated: true)
  }
}
</code></pre>
<p>핵심적인 코드는 아래와 같다.</p>
<pre><code class="language-swift">final class 푸바오ViewController: UIViewController {
var 밥먹었는지안먹었는지: ((Bool) -&gt; Void)? //🐼
...
deinit { //원하는 타이밍 
    print(&quot;3️⃣ 📦 배부름 상태 \(배부름) 전송&quot;)
    밥먹었는지안먹었는지?(배부름) //🐼
  }
}
</code></pre>
<p>값을 전달하는 푸바오는 <code>밥먹었는지안먹었는지</code> 클로저에 자신이 전달하고자 하는 input 값인 자신의 <code>배부름</code> 상태를 넣어 실행한다. </p>
<p>그리고 해당 클로저 실행으로 인해 <strong>input</strong> 값을 받게 되는 곳이 바로 해당 클로저의 내용을 대입해준 <strong>사육사VC</strong>가 되는 것이다. </p>
<h3 id="🛑-주의-캡쳐-현상">🛑 주의! 캡쳐 현상</h3>
<p>클로저 사용시 캡쳐현상이 발생할 수 있다. </p>
<pre><code class="language-swift">  푸바오VC.밥먹었는지안먹었는지 = { state in
    print(&quot;4️⃣ 📮 전달받은 배부름 상태 \(state)&quot;)
  }</code></pre>
<p>그러나 현재 코드는 자기 자신을 참조할 일이 없어 캡처 현상을 일으키지 않는다. </p>
<p>만약 아래와 같이 자신을 참조하는 코드가 존재한다면 weak 처리를 해주어 캡처 현상을 피해야 한다. </p>
<pre><code class="language-swift">//사육사VC
    DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
      푸바오VC.밥먹었는지안먹었는지 = { [weak self] state in
        guard let self = self else { return }
        self.test = &quot;weak을 써야 VC 화면에서 내려갈 때 test가 캡처되는 일을 막을 수 있다&quot;
      }
    }</code></pre>
<h3 id="참고-클로저-사용-시-메모리-누수">참고) 클로저 사용 시 메모리 누수</h3>
<p>아래 코드도 메모리 누수의 위험이 있다. </p>
<pre><code class="language-swift">// MARK: - 푸바오
final class 푸바오ViewController: UIViewController {
  ...
  deinit {
    print(&quot;메모리 해제&quot;)
  }

  func 식사() {
    ...
    self.dismiss(animated: true)


    DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
      print(&quot;3초 뒤&quot;)
      print(&quot;3️⃣ 📦 배부름 상태 \(self.배부름) 전송&quot;)
      self.밥먹었는지안먹었는지?(self.배부름) //🐼
    }
  }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/haeun_/post/eff3a7d3-592c-4494-8d83-07b739ee38ed/image.png" alt=""></p>
<p>화면이 사라졌음에도 메모리가 해제되지 않는 것은 자기 자신을 참조하는 클로저 탓이다. 그러니 class 내 클로저 사용 시에는 반드시 weak 키워드를 습관화 해야 한다.</p>
<p>*<em>개선 코드 *</em></p>
<pre><code class="language-swift">DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) { [weak self] in
      guard let self = self else { return }
      print(&quot;3초 뒤&quot;)
      print(&quot;3️⃣ 📦 배부름 상태 \(self.배부름) 전송&quot;)
      self.밥먹었는지안먹었는지?(self.배부름) //🐼
    }
</code></pre>
<p><strong>실행 결과</strong></p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/fbd7d6c7-cd7a-4716-a1aa-8d9e0316a7f8/image.png" alt=""></p>
<p>화면이 사라짐에 따라 메모리도 해제되었음을 확인할 수 있다. </p>
<hr>
<h1 id="공통점">공통점</h1>
<h2 id="발행자-수신자-간의-11-매칭">발행자, 수신자 간의 1:1 매칭</h2>
<p>아무래도 <code>전송자(발행자)</code>, <code>수신자</code>와 같은 표현은 Rx를 공부하며 버릇이 된 것 같다. 
그래도 VC의 관계에 대해 직관적으로 이해하기 편리한 이름이라 사용해 보자면,
<strong>Delegate</strong>와 <strong>Closure</strong>은 데이터 전송 시 모두 아래와 같은 관계를 가진다. </p>
<blockquote>
<p>전송자 : 수신자 = 1 : 1</p>
</blockquote>
<p><strong>Delegate 먼저 생각해 보자.</strong></p>
<p>우선 Delegate는 <strong>protocol</strong>이다.</p>
<p>사실 이전의 나는 프로토콜이 재사용성을 확보하는 추상화 요소로 사용되기에 재사용! 자유롭게! 라는 단어의 어감에서 오는 이미지로 수신자와 1:N 관계를 맺고 있다고 생각했다. </p>
<p>하지만 이는 오해였다.</p>
<p>프로토콜이 가진 추상화는 규격화된 틀을 사용하기에 *<em>교체가 편리하다는 거지, 연결 자체를 여러개 할 수 있다는 말이 아니다. *</em></p>
<p><strong>콘센트</strong>처럼 말이다. 규격화된 콘센트 양식은 다양한 전자기기가 같은 충전포트에 꽂힐 수 있도록하지만, <strong>한 개의 포트</strong>에 꽂히는 것은 결국 <strong>하나의 전자기기</strong>다. </p>
<p><strong>Closure</strong> 도 마찬가지로 <code>1:1</code> 관계이다.</p>
<p>앞선 코드에서, closure는 수신자에 의해 상세 클로저를 주입 받았다. </p>
<p>만일 다른 객체가 접근하여 해당 변수에 다른 클로저를 주입 한다면 내용이 교체된다. </p>
<h2 id="코드-작성-시-공통점">코드 작성 시 공통점</h2>
<p>(일반적으로)</p>
<ul>
<li>메서드(클로저)의 상세 내용을 서술하는 것은 수신화면 (수신자)</li>
<li>메서드(클로저)를 실행하는 것은 전달할 데이터를 가진 전송화면 (발행자)</li>
</ul>
<h1 id="차이점">차이점</h1>
<ul>
<li>델리게이트 : 객체간의 규격화된 소통 </li>
<li>클로저 : 코드 블럭을 전달하기 위함</li>
</ul>
<hr>
<h2 id="마치며">마치며,</h2>
<p><strong>NotificationCenter</strong> 은 다룰까 하다가 다루지 않았다.
기본적인 데이터 전송 방식 중 <strong>NotificationCenter</strong>가 대표적인 <code>1:N</code> 매칭 방식이라 앞선 두 방식과 비교하면 좋았겠으나, 익숙하게 사용한 경험이 없기도 하고 싱글턴과 같은 방식으로 동작함을 알게되어 앞으로도 썩 사용하지 않을 것 같았다. </p>
<p>차차 포스팅하고자 하는 <strong>RxSwift</strong>와 <strong>Combine</strong>은 발행자와 수신자간의 관계가 <code>1:1</code>도, <code>1:N</code>도 가능하다. 더 나아가 이벤트 발생과 수신에 대해 세밀한 분류를 가진다. 아직은 아득하지만 다룰 날이 온다면 좋겠다. </p>
<blockquote>
<p>포스팅이 길어짐에 따라 세밀한 검토를 하지 못하였습니다. 혹시 문제가 있다면 언제든 댓글로 알려주세요! 
다들 즐거운 개발 되세요 🐼</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[CMC 12기 iOS 파트 회고]]></title>
            <link>https://velog.io/@haeun_/CMC-12%EA%B8%B0-iOS-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@haeun_/CMC-12%EA%B8%B0-iOS-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Thu, 30 Mar 2023 12:27:34 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>개인 회고에 집중된 글입니다. 정보 전달로는 적합하지 않습니다. 프로젝트 협업 경험이 없던 개발자의 첫 협업 경험 후기와 앱 출시 후기, 앞으로의 공부 계획을 담았습니다. </p>
</blockquote>
<p>포스팅 하기 앞서, 멀리서 찾아와 데모데이날 축하의 인사를 건네준 <strong>엘리엇</strong>과 정신적 지주 <strong>앨런 12기 동료들</strong>에게 글로는 다 표현 못할 감사의 인사를 전한다. 그리고 나의 영원한 iOS 개발 근본 <strong>앨런</strong>과 블로그를 쓸 의지가 되어준 동경하는 <strong>에디</strong>에게도!</p>
<p>개인 회고에 집중된 글로 팀원에 대한 언급은 크게 없지만, 긍정적이고 적극적이며 열정적이던 우리 <strong>하나팀 팀원</strong>들에게 다시 한 번 감사를 전한다. 앞으로도 이어질 인연을 기대하며 아주아주 큰 애정을 보내고 싶다.</p>
<h2 id="첫-it-동아리-cmc-12기-회고">첫 IT 동아리, CMC 12기 회고</h2>
<p><img src="https://velog.velcdn.com/images/haeun_/post/7aae3d75-4075-4203-9a4c-eced925a31a8/image.JPG" alt=""></p>
<p><strong>CMC</strong>는 <code>기획자 1</code> <code>디자이너 1</code> <code>프론트엔드 2</code> <code>백엔드 1</code> 총 다섯 명이 한 팀을 이루어 주어진 세 달 간 앱을 런칭하는 동아리이다. 현직자분들도, 학생분들도 많다. </p>
<p>3개월 간이라고 표기하긴 했지만 개발자의 관점에서는 개발 기간이 약 <strong>한 달 반</strong> 정도에 해당한다. </p>
<p>이전의 나는 프로젝트 경험도, 협업 경험도 없었다. 
(8주 간 유료로 진행되는 <code>라이징캠프</code> iOS 수료자로서 테스트 기간에 협업한 경험이 있긴 하나 당시 class와 struct가 무엇인지도 모르는 상태로 박치기 하듯 수료하였으니 제대로된 협업이었다 보긴 어려워 스스로 제하고 있다) </p>
<p>그리고 덧붙이자면 지난 CMC 11기 지원에서 떨어진 경험도 있었다. </p>
<p>그럼에도 CMC 12기 iOS 파트의 여섯 명 중 한 명으로 합격했으니 정말이지 감사한 일이다. 물론 기쁨은 잠시였고, 시작한 직후에는 큰 부담감이 있었다. </p>
<p>CMC는 한 팀을 구성하기 위한 두 번의 매칭이 있는데, 프론트 기준 
<strong>안드로이드-iOS</strong> 매칭과 최종적으로 팀이 결정되는 <strong>기획,디자인-프론트엔드-백앤드</strong> 매칭이 있다. </p>
<p>챌린저는 매칭에 앞서 자기 자신을 소개하기 위해 소개페이지를 작성한다. 그를 읽으며 깜짝 놀란 게 현직인 분들이 꽤나 많았다. 더불어 앱 런칭 경험이나 그에 준하는 프로젝트 협업 경험 정도는 모두 기본인 것 같았다. </p>
<p>그러니 주눅이 좀 들었다. 하지만 누군가는 반드시 나와 팀이 되어야 하고, 팀원들이 나의 부족함을 감내할 이유는 없었다. 정신 차리고 함께 하고 싶은 팀원이란 어떤 모습일까를 고심하며 자기소개서를 작성했다. </p>
<p>그렇게 작성한 자기소개서로 다행히 좋은 팀원들과 멋진 프로젝트를 완수할 수 있었다. 더불어 프로젝트가 끝나고서는 몇몇 군데서 자기소개서를 인상 깊게 읽었다는 말을 들었다. 민망하고 부끄러웠지만 그때 열심히 쓰길 잘했노라는 생각이 들어 행복했다. </p>
<p>아쉽지만 CMC 기간 동안에 iOS 개발자 분들과 많은 대화를 나누지는 못했다. 네이티브 앱 개발 팀의 경우 한 팀당 iOS 개발자가 한 명 배정되니 말이다. 추가적인 스터디도 없었다. </p>
<p>개인적으로 이러한 점이 아쉬워 추후 다른 팀들의 iOS 개발자 분들께 궁금한 점을 연락드리려 한다. 멋진 iOS 개발자 분들이 정말 많았고, 그분들이 구현한 기능과 그에 대한 개발 방향, 의견이 궁금하다. </p>
<p>혼자 하는 공부는 정말이지 외롭다. &#39;많은 개발자들이 이렇게 느낍니다&#39; 라는 글보다 바로 옆 사람의 끄덕거림이 더 큰 깨달음을 준다. </p>
<p>더불어 이러한 점을 들어 IT 동아리에 대해 많이 알지는 못하지만 CMC의 두드러지는 특징을 정리해 볼 수 있을 것 같다. </p>
<ul>
<li>앱 기획이어야 한다 </li>
<li>네이티브 개발 팀과 웹 개발 팀으로 나뉜다 </li>
<li>네이티브 개발 팀은 <code>안드로이드</code>, <code>iOS</code> 개발자가 팀당 <strong>한 명</strong>씩 배정된다. 
즉 네이티브 개발 팀의 경우, 안드로이드와 iOS 개발자가 혼자서 모든 구현을 해내야 한다. </li>
<li>앱 개발 팀은 한 팀당 <code>웹</code> 개발자가 <strong>두 명</strong> 배정된다. 즉 프론트앤드 개발자가 두 명이다. </li>
</ul>
<p>추후에 바뀔 수도 있다곤 생각하지만, 12기는 이런 식으로 운영되었다. </p>
<p>처음에는 네이티브 기획 팀의 진도가 느릴 수 밖에 없으므로 조금 불리하지 않나 생각했다. 하지만 나는 웹 기획을 잘 모르기도 하고, 웹 팀도 웹 팀만의 고난과 문제가 있을 것이다.</p>
<p>네이티브 팀의 입장에서만 이야기 하자면, 혼자 일하는 게 좋은 분들의 경우 정말 좋은 경험이 될 것 같다. </p>
<p>나또한 프로젝트 전체를 내 손으로 개발하는 귀중한 경험을 얻었다. 물론 iOS 개발자 간의 협업 경험 부재는 아쉬움으로 남는다. </p>
<p>iOS 개발 팀원이 나 뿐이니 개발 기간 내내 같은 코드를 공유하며 의논할 팀원이 없었다. 더불어 iOS 개발 파트인 내가 iOS 출시에 실패한다면 이는 곧 우리 팀의 iOS 앱 출시가 불발된다는 뜻과 같다. </p>
<p>기획의 볼륨이 작은 편도 아니기에, 한 달 반 동안 정말이지 전쟁같은 시간을 보냈다. 내가 개발하지 못하면 이제껏 고군분투 한 <code>기획자</code> <code>디자이너</code> <code>백엔드</code>의 노고가 세상에 드러날 수 없다는 막중한 책임감이 느껴졌다. 이래서 <code>&#39;프론트&#39;엔드</code> 구나 새삼 깨달았다. </p>
<p>기획안에 따라 다르겠지만, CMC 기간 내에 개발을 완수하기란 일정이 굉장히 빠듯하다. 적어도 나는 그랬다. 기획이 늘 고정적인 것도 아니며, 디자인의 변동사항도 생기기 때문에 유연한 대처도 필요하다. </p>
<p>그러니 가장 몰입해서 개발한 기간, 데모데이 전주에는 하루에 18시간 이상을 작업하였다. (개발자로서의 장기적 수명을 생각하면 좀 더 효율적으로 작업해야겠지만.. 당장은 최선이었던 것 같다)</p>
<p>개인적으로 첫 협업 경험을 앞두고 가장 두려웠던 것은 서버와의 소통이었다. </p>
<p>그러니 모르면 묻고, 불확실해도 묻고, 특정 코드에 의도가 궁금해도 물었다. 질문이 너무 많아도 안돼. 너무 가벼운 질문은 안돼. 그렇다고 내 마음대로 추측하고 넘어가선 안돼. 많은 잣대를 들이밀어 질문을 정제했다. </p>
<p>진땀 빼며 정리해놓은 질문이지만 소통은 상상 이상으로 평화로웠다. 머쓱할 정도로 평화롭게 흘러가는 대화와 피드백에 어쩌면 이제껏 스스로 만들어낸 가상의 무언가와 싸우고 있었던 게 아닐까 하는 생각도 들었다. </p>
<p>프로젝트가 끝난 뒤 술자리에서 이것저것 물어보았을 때 별 의문 없이 설명해 주어 고마웠노라고 서버 팀원에게 감사를 표했다. 팀원은 뭐가? 하고 맹한 표정을 지었다. 그 반응도 왠지 감사하고 안심스러워서 웃음이 났다. </p>
<p>CMC 기간 내내 적극적인 태도로 최선을 다했다고 생각한다. 좋은 인연을 많이 얻었고, 감사한 성장 기회를 받았다. </p>
<h2 id="230325_cmc-끝-데모데이">23.03.25_CMC 끝, 데모데이</h2>
<p>오늘은 23.03.30로, 장장 3개월의 CMC 12기가 데모데이를 끝으로 막을 내린 지 닷새가 되었다.</p>
<p>CMC에서 진행된 총 11개 앱에 대한 설명회 또는 시연회를 데모데이라 한다. 
모든 팀들의 최종 출시 마감 기한이기도 하다. </p>
<p>우리 팀은 다행히도 데모데이까지 <code>안드로이드</code> / <code>iOS</code> 모두 <code>플레이스토어</code> / <code>앱스토어</code>에 출시 및 두 번 이상의 업데이트까지 마쳤다. 시연에 필요한 모든 기능에 대한 구동이 가능했다. </p>
<p>더불어 아쉬운 점에 대해서는 추가적인 업데이트를 계속해서 진행할 생각이다. (유지보수할 나의 앱이 생겨서 정말 기쁘다)</p>
<p>너무 피곤한 상태라 즐기지 못할 것이라 생각했는데, 정말 행복하고 재밌는 시간이었다. 더불어 우리 팀원들 모두 기대치 않았던 수상의 영광도 안을 수 있었다. </p>
<p>개발자로서는 여러 팀들의 훌륭한 결과물을 보고, 또 많은 개발자 분들과 대화를 나눌 수 있어 기뻤다. </p>
<h2 id="ios-출시--field-mate">iOS 출시 : Field Mate</h2>
<p><img src="https://velog.velcdn.com/images/haeun_/post/13ab080e-18a2-453d-89ec-83c725c5d1b6/image.png" alt=""></p>
<pre><code>최고의 디자이너 티나가 만들어준 소개 화면</code></pre><p><a href="https://apps.apple.com/kr/app/fieldmate/id6446427396">FieldMate</a>를 클릭하면 CMC 기간 내에 출시된 앱의 앱스토어 링크로 이동한다. </p>
<blockquote>
<p><strong>간략한 기술스택</strong> </p>
</blockquote>
<ul>
<li><code>SwiftUI</code> <code>UIKit</code></li>
<li><code>MVVM</code></li>
<li><code>Alamofire</code> <code>FsCalendar</code> <code>KeychainSwift</code></li>
</ul>
<h3 id="swiftui와-uikit">SwiftUI와 UIKit</h3>
<p><strong>SwiftUI</strong>를 메인으로 두고 작업하였다. 
<strong>UIKit</strong>은 필요한 구간만 SwiftUI로 변환해서 사용하였다. </p>
<p>UIKit은 딱 두 파트에 사용되었는데,
<strong>1)</strong>  UIKit 라이브러리인 <code>FsCalendar</code>를 SwiftUI로 변환하여 사용할 때와 
<strong>2)</strong> 멀티라인이 지원되지 않는 SwiftUI의 <code>TextField</code> 특성상 UIKit을 변환해서 사용할 때 이다. </p>
<p>또한 본 프로젝트는 UIKit으로 전체적인 리팩토링을 진행할 예정이다. 
그에 대한 이유는 아래 추후 업데이트 사항에 적어두었다. </p>
<hr>
<h3 id="mvvm-아키텍쳐">MVVM 아키텍쳐</h3>
<p>곰튀김님의 유튜브 강의로 MVVM의 근간을 다졌다. 
하지만 내가 만들어낸 구조는 민망할 정도로 엉성하다.
그 시작은 UIKit 라이브러리를 SwiftUI로 변환해서 사용하는 것이었는데, 어쩌다 파일 분류가 길을 잃었는 지에 대해서는 따로 포스팅을 작성하려 한다. 
더불어 프로젝트를 진행하며 느낀 전체적인 의문에 대한 개선점을 찾고 있다. </p>
<hr>
<h3 id="라이브러리">라이브러리</h3>
<p>우선, 모든 라이브러리는 SwiftPackages로 다운 받았다. 
단순한 이유였는데 Xcode 파일이 프로젝트 파일과 워크 스페이스 파일로 분리되는 것을 원하지 않았기 때문이다. </p>
<p><strong>Alamofire</strong>
API 연결을 위해 사용하였다. 
이제껏 이론만 접했을 뿐 제대로 사용법을 인지하여(왜 편리한지) 사용하는 것은 처음이었다. 
<strong>FsCalendar</strong>
골칫덩이 캘린더를 그리기 위해 사용하였다. 
하지만 이러한 선택이 SwiftUI로의 변환 과정에서 더 큰 골치를 불러올 줄은 몰랐다.
<strong>KeychainSwift</strong>
사용자의 토큰 / 리프레쉬 토큰 / 서비스 접근 권한 등의 데이터를 저장하기 위해 사용하였다. 하지만 이러한 로컬 DB 저장 방식은 의문을 남겼다. 아래 작성된 업데이트 사항에 그 이유를 적어 두었다. </p>
<hr>
<h2 id="앞으로의-업데이트-사항">앞으로의 업데이트 사항</h2>
<h3 id="업데이트-사항-추리기-과정--모든-작업-내용은-기록으로">업데이트 사항 추리기 과정 : 모든 작업 내용은 기록으로</h3>
<p>CMC 기간 내내 특별한 사정이 없다면 매일매일 노션에 기록을 남겼다. 
아래는 API 연결로 한창 정신 없던 때의 기록 일부이다. 
<img src="https://velog.velcdn.com/images/haeun_/post/d7f06bba-3ef2-4305-84ca-a23b1415d3c0/image.png" alt="">
오늘 어떤 진도를 나갔는지, 어떤 부분을 공부했는지, 어떤 문제를 겪었는 지에 대한 내용을 담았다. (비공개 포스팅도 두 세 개 작성하였다)</p>
<p>나중에 보완+회고하기 위해 시작한 기록이었지만 뜻밖의 재미도 컸다. 
그때의 내가 어떤 식으로 고군분투 했는 지가 고스란히 남아 있어 짠하기도 하고, 장하기도 하고 그렇다. 
네가 그만큼 했으니 이젠 내가 이만큼 해줄게, 이런 생각도 드는 것 같다. </p>
<blockquote>
<p>위의 작업 기록을 토대로 작성한 업데이트 사항은 아래와 같다.</p>
</blockquote>
<h3 id="1-swiftui로-작성된-프로젝트를-uikit으로의-리팩토링"><strong>1) SwiftUI로 작성된 프로젝트를 UIKit으로의 리팩토링</strong></h3>
<p>이번 프로젝트에서 나는 SwiftUI를 사용하였다. 정말 아무것도 모르는 상태로 시작하여 이것저것 적용해 보며 사용법을 익혔다. 그러니 의문사항도 많았다. </p>
<p>SwiftUI를 다듬음과 동시에, UIKit으로의 리팩토링을 진행하려 한다. 별 이유는 없고 차이가 궁금해서이다. </p>
<p>늘 누군가 내게 <strong>SwiftUI</strong>가 좋았냐 <strong>UIKit</strong>이 좋냐 묻는다면 둘 다 같은 상황에서 동등하게 써 본 경험이 없어 잘 모르겠다고 답해 왔다.</p>
<p>그러니 그에 대한 호불호를 밝히려면 둘 다 사용해본 경험이 있어야 할 것 같다. 나는 개발 전문가라고 스스로를 소개할 만큼 개발을 잘 하고 싶다. 그리고 전문가라면 응당 본인 취향에 대해 답할 수 있는 사람이어야 한다고 생각한다. (바리스타의 원두 취향이라던가, 바텐더의 술 취향이라던가)</p>
<p>더불어 아직 많은 회사에서 UIKit을 사용 중이므로, 보다 소스가 많은 도구의 사용법을 익히기 위한 데에 목적이 있다.</p>
<p>+) UIKit(FsCalendar)을 SwiftUI로 변환하는 과정에서 View를 다시 그리는 시점이 굉장히 잦다는 것을 알게되었다. 
이로인해 사용자가 FsCalendard와는 무관한 액션을 취할 때 갑자기 FsCalendar의 PickDate가 선택되지 않는 날짜 위에서 우다다다 나타났다 사라지는 (View를 아주 잘게 다시 그리는 과정에서 생기는 문제로 추측) 현상이 발생했다.
여러 조건문을 써서 이러한 문제를 막아주려 했지만 쉽지 않았다. 당장 생각하는 방법은 Combine을 사용하여 특정 데이터 변동 시점에만 View를 다시 그려주는 방법이다 &lt;- 시도 중에 있다 </p>
<hr>
<h3 id="2-combine"><strong>2) Combine</strong></h3>
<p>Combine에 대한 보다 심도 깊은 이해가 필요하다. 로그인 화면의 유효성 검사에서 Combine을 사용했다. 하지만 원하는 동작이 유도되지 않았고, (동시에 다섯가지를 작업 불가능한가?) 이에 대한 해결 방법이 궁금하다. 
<img src="https://velog.velcdn.com/images/haeun_/post/7e6736ae-82f8-437f-a03d-57e8a756a9eb/image.png" alt="">
다섯 개를 한꺼번에 처리해 주지 못해 터져나온 문제를 당장 꿰매듯 수습한 모습이다. 이렇게 되면 회원가입 화면에서 사용자가 인증코드를 가장 마지막에 확인할 경우 문제가 생기게 된다. </p>
<hr>
<h3 id="3-사용감-개선"><strong>3) 사용감 개선</strong></h3>
<p>프로젝트를 진행하며, <code>토스</code>와 <code>뱅크샐러드</code>를 정말 많이 떠올렸다. 내 스스로 많이 비교했고, 그렇게 매끄러운 사용감을 이끌어 내려면 어떻게 해야하지? 하는 답답함과 궁금증이 일었다. </p>
<p>정말, 너무 궁금하다! 그러니 내가 원하는 사용감을 얻을 수 있을 만큼 도전해 보고 싶다. 
지금 당장 떠오르는 개선사항으로는 1)텍스트 필드를 눌렀을 때 키보드 위치 및 2)사용자 눈 높이에 포커싱 하기, 3)텍스트 필드 위치 눌렀을 때 곧장 포커스 처리 해주기, 4)네비게이션 뷰의 뒤로가기 버튼 삭제하지 않고 커스텀 해서 사용하기 (삭제하면 스와프로 뒤로 가기 기능도 삭제됨) 등등이 떠오른다. </p>
<p>더 많은 사용감 개선에 대한 내용을 추후 블로그 글로 포스팅 할 예정이다. </p>
<hr>
<h3 id="4-암호화-처리--보안-강화-">*<em>4) 암호화 처리 / 보안 강화 *</em></h3>
<p>이를 암호화 처리라도 명명해도 되는 지 의문이지만, 우리 팀의 어플은 사내 업무처리를 간편하게 함에 목적이 있는 어플이다. 따라서 보안성에도 큰 신경을 써야 한다. 하지만 이러한 부분에 대한 심도 깊은 고려 없이 구현 진행되었다. </p>
<p>물론 서버에서도 고려해 주어야 할 부분이지만, 프론트에서 처리해줄 부분에 대해 고려하는 시간을 가지고 싶다. 
당장 찝찝한 구간은 로컬에 사용자의 데이터를 저장해주는 부분이다. 나는 <strong>KeychainSwift</strong> 라이브러리를 사용하여 사용자의 데이터를 로컬에 저장해 주었다. 사용자의 데이터를 로컬에 저장해 주어야 할 이유로는 앱 도메인 상 <code>리더</code>와 <code>일반</code> 사원과의 화면 차이가 존재하기 때문이었다. 그러나 이러한 데이터 저장은 앱을 삭제한 이후에도 삭제되지 않음을 알게되었다. 
내가 사용자였더라면, 만약 앱을 깔고 다시 로딩했는데 이전 데이터가 남아 있는 듯한 알림창을 보았더라면, 이거 뭐야. 내 개인정보! 라는 생각을 했을 것 같아서 개선 필요성을 느꼈다. <strong>KeychainSwift</strong>을 사용한 의도는 <strong>UserDefault</strong>보다는 깊숙한 곳에, 안전히 보관하고 싶었기 때문이다. 하지만 정말 타당한 필요성에 의해 사용했노라 묻는다면 그러지 못했다. </p>
<hr>
<h3 id="5-ui-테스트-코드-작성"><strong>5) UI 테스트 코드 작성</strong></h3>
<p><code>뱅크샐러드</code>의 <a href="https://blog.banksalad.com/tech/test-in-banksalad-ios-1/UI">iOS팀이 숨쉬듯이 테스트코드 짜는 방식</a> 기술 블로그 글을 읽으며 두근두근 했던 기억이 있다. 아직은 내가 이러한 기술을 이해 + 적용할 수준이 안된다 생각하고 넣어두었던 것인데, 이번 UIKit으로의 리팩토링 과정에 적용해 보려 한다.</p>
<hr>
<h3 id="6-ci--cd-의-이해"><strong>6) CI / CD 의 이해</strong></h3>
<p>출시할 수 있는 프로젝트만 있다면 &lt;- 하고 고대하던 주제 중 하나이다. 마찬가지로 현재 앱에 적용하며 학습한 내용을 블로그에 남기려 한다. </p>
<hr>
<h3 id="7-mvvm-패턴에-대한-회고와-더-나은-아키텍쳐-패턴에-대한-가능성"><strong>7) MVVM 패턴에 대한 회고와 더 나은 아키텍쳐 패턴에 대한 가능성</strong></h3>
<p>기존 프로젝트는 MVVM 패턴을 목표로 진행하였으나, 과연 엄격히 지켰냐고 묻는다면 그렇지 않다. 나 혼자 프로젝트를 진행하였기에 기간이 급박해질 수록 스스로 세워둔 규칙이 무너기도 했고, 이제껏 공부한 이론에 의존하여 내 나름의 MVVM를 좇았기 때문에 프로젝트 진행 과정에서 보편적인 형태로부터 얼마나 변형되었는 지 알 수 없다. 
더불어 이 앱에게 MVVM 패턴보다 더 용이한 아키텍쳐 패턴은 없는 지 고찰 하려 한다. 
회사에 들어가면 대부분 고정적인 아키텍쳐 패턴을 사용한다고 알고 있다. 손에 익은 도구가 생기기 전에 이것저것 손에 쥐어보고 싶다. 오른손 잡이나 왼손 잡이보다는 양손잡이가 되고 싶은 욕심이다. </p>
<hr>
<h3 id="8-클린코드"><strong>8) 클린코드</strong></h3>
<p>전체적인 코드 개선이 시급하다. 부끄럽지만 변수명이 통일되지 않은 것은 예삿일이다. 같은 주제에 대하여 customer, client 라는 명칭이 혼용된 것이 그 예시이다. 동사가 앞에 붙기도, 명사가 앞에 붙기도 했다. 한창 개발에 몰두할 때엔 파일 위치를 머리에 익혀버려 문제가 되지 않았지만 반드시 문제가 될 사안임을 모르지 않는다. 코드가 깔끔하지 않아서, 결합도가 너무 높아서, 나열 순서가 뒤죽 박죽이라서 등, 사소하고도 중대한 문제들을 잡아가고자 한다.</p>
<hr>
<h3 id="9-깃-관리-">*<em>9) 깃 관리 *</em></h3>
<p>그저 부끄럽기만 한 주제이다. 
나름 프로젝트 초반에는 이슈관리를 시도 했으나 습관화 되지 않아 커밋 메세지도 뭉터기로 날렸다. (feat: &lt;- 이라고 적혀 있긴 하지만 사실상 그날 한 작업 과정을 저장하는 개념이었다)
규칙을 정해 공유할 팀원이 없었다는 핑계가 초라하다. </p>
<p>프로젝트를 진행하며 깃 관리에 대한 필요성을 정말 많이 느꼈다. 
타 개발자들의 작업 과정을 참고하여 커밋 메세지에 규칙을 세우고 이슈 관리를 하고자 한다. </p>
<hr>
<h3 id="10-사진-다루기-">*<em>10) 사진 다루기 *</em></h3>
<p>앱 디자인상 커스텀 갤러리를 만들어야 했다. 그리고 이 과정에서 애를 먹었다. 
전체적인 흐름은 아래와 같다. </p>
<ol>
<li>사용자의 갤러리 권한을 확인한다 </li>
<li>사용자의 갤러리에서 사진을 50개씩 가져와 리사이즈 후 배치한다 </li>
<li>선택된 사진과 선택되지 않은 사진은 identifier로 구분한다 </li>
<li><code>저장</code> 시, 사용자가 선택한 사진의 identifier로 사진 원본을 불러온 뒤 리사이즈(서버 통신을 위해 화질을 낮추긴 하지만 갤러리 배치 때 보다는 좋은 화질로) 후 API 통신 </li>
</ol>
<p>이 과정에서 가장 큰 문제는 2번과 4번이다. 
2번에서 많은 부분 개선되긴 하였지만 여전히 사용감이 매끄럽지 않다. 로딩까지 딜레이가 큰 점도 그러하다. 
4번에서는 <code>저장</code> 버튼을 누를 때 마치 앱이 멈춘 것 처럼 버벅거리는 현상을 겪었다. 문제가 없는 사용자도 있었으나 문제를 겪는 사용자도 있었다 (데모데이 시연 때 확인) 
당장은 통신 시 progressView가 돌아가도록 만들어 사용자의 당혹감을 줄이긴 하였지만 근본적인 문제가 해결된 것은 아니다. 
기능 구현에 있어, 사진 다루기가 정말 까다롭다는 생각이 들었다. 이 부분에 대해서 더 많은 개선을 이루고 싶다. </p>
<hr>
<h2 id="-책-읽기-스터디-재개">+) 책 읽기 스터디 재개</h2>
<blockquote>
<p>프로젝트 기간 중 백엔드 현직자인 선배와 둘이서 진행하던 책 스터디를 중단했었다. 이를 재개하며 선택한 도서는 아래와 같다. 
나의 관심사와 선배의 관심사를 고려하였으며 도서 선택에는 전적으로 선배의 도움이 있었다. </p>
</blockquote>
<h3 id="1-함수형-코딩">1) 함수형 코딩</h3>
<blockquote>
<p>함수형 프로그래밍을 알고 싶다 + 추상화를 온전히 이해하고 싶다</p>
</blockquote>
<p>같은 주제로 묶어 표시해도 되는건가 걱정스럽지만 나는 추상화와 함수형 프로그래밍에서 비슷한 결을 느꼈다. (이 둘에 대해 잘 아는 누군가의 입장에서는 읽었다면 기겁할 말일지도 모르겠다...) 아직 잘 모르는 상태로 구구절절 써 봐야 혼란만 가중시킬 뿐이다. 머리에 둥실둥실 떠다니는 개념을 내 식으로 학습한 이후 정말 두 개 사이에 연관성이 있었는 지 판단하고자 한다. 또한 그 후 위 개념을 프로젝트에 적용하고, 그 과정을 블로그 포스팅으로 작성해야겠다. <img src="https://velog.velcdn.com/images/haeun_/post/53bc8361-771c-4cae-bf6e-40e7030a019b/image.png" alt=""></p>
<h3 id="2-위대한-it-영어">2) 위대한 IT 영어</h3>
<blockquote>
<p>원활한 소통은 결국 언어에서 온다. </p>
</blockquote>
<p>일단 책 내용이 가볍고 알차다. 아 이게 이거였구나 하는 감격스런 내용이 많고 글을 읽을 때 뇌의 피로도 없이 재밌기만해서 (재밌는거에 비해 너무 알차서) 쉬어가는 책으로 선택했다.</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/dbdb51f0-a41a-4e62-abf0-d35fdf289e9d/image.png" alt=""></p>
<h2 id="마치며">마치며,</h2>
<p>유지 보수할 수 있는 앱이 생겼음에 너무나도 기쁘다. 후기 글을 남기는 날이 오다니! 벅차다. </p>
<p>이렇게 멋진 사람들과 프로젝트를 함께하는 경험을 하고 또 싶고, 더 나아가 지금 내가 겪은 경험을 모두와 나눌 수 있을만큼 성장하고 싶다. </p>
<p>시작할 때 내가 가장 가진 게 없었고 내가 제일 부족했다. 
그러니 감사하게도 이 기간 동안에는 내가 얻은 것이 가장 많다.</p>
<blockquote>
<p>개인 회고 글로, 두서 없이 작성되었으나 모든 피드백과 질문을 환영합니다. 
읽어 주셔서 감사합니다! </p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[소설가의 입장에서 이해하는 아키텍쳐 패턴 / 디자인 패턴]]></title>
            <link>https://velog.io/@haeun_/%EC%86%8C%EC%84%A4%EA%B0%80%EC%9D%98-%EC%9E%85%EC%9E%A5%EC%97%90%EC%84%9C-%EC%9D%B4%ED%95%B4%ED%95%98%EB%8A%94-%EC%95%84%ED%82%A4%ED%85%8D%EC%B3%90-%ED%8C%A8%ED%84%B4-%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@haeun_/%EC%86%8C%EC%84%A4%EA%B0%80%EC%9D%98-%EC%9E%85%EC%9E%A5%EC%97%90%EC%84%9C-%EC%9D%B4%ED%95%B4%ED%95%98%EB%8A%94-%EC%95%84%ED%82%A4%ED%85%8D%EC%B3%90-%ED%8C%A8%ED%84%B4-%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Sun, 05 Feb 2023 09:09:36 GMT</pubDate>
            <description><![CDATA[<h3 id="들어가기에-앞서">들어가기에 앞서,</h3>
<blockquote>
<p>본 포스팅은 막 <code>디자인패턴</code>이라는 주제에 입문하여, 
<code>아키텍쳐 설계</code> / <code>아키텍쳐 패턴</code> / <code>디자인패턴</code>의 구분이 모호한 이를 위한 글이다. 그러니 바로 어제의 나를 위해 남기는 글이기도 하다. </p>
</blockquote>
<p>구글에 <code>디자인패턴</code>이라 검색하면 다양한 주제가 보인다. <code>DDD</code>, <code>MVVM</code>, <code>싱글톤</code>, <code>옵저버</code> 등등 이 그 예시이다. </p>
<p>iOS 개발 공부 중이던 나는 <code>디자인패턴</code>이라 검색하면 당연히 <code>MVC</code>, <code>MVP</code>, <code>MVVM</code>에 관한 내용만 나올 줄 알았다. 그러니 예상치 못한 수많은 키워드 앞에 눈을 끔뻑였다. </p>
<p>그래서 <code>iOS MVVM</code>으로 검색해서 공부하다가, 맛보기를 끝내고 다시 <code>디자인 패턴</code>을 검색했다. 다시보면 좀 이해할 줄 알았는데 혼란스러운 것은 마찬가지였다. </p>
<p>그러니 차분히 공부하며, <code>디자인패턴</code>이라는 주제 앞에 마주할 수 있는 개념들을 정리하였다. (MVVM은 디자인 패턴이 아니었음을...)</p>
<p>더불어 <code>아키텍쳐 설계</code>시 필요한 다양한 <code>패턴들의 공통점</code>이 와닿지 않는다면, 본 포스팅이 도움이 될 것이라 생각한다. (MVVM과 DDD의 연관이라던가)</p>
<p>그렇게 정리한 개념들을 소설가의 입장에 빗대어 이야기 해 보고자 한다. </p>
<p>어디에도 없는 예시로, 나의 해석을 곁들여 내용을 구성하였다. </p>
<p>아주 샅샅이 파헤치면 엉성할 수 있지만, 큰 맥락을 이해하는 데에는 도움이 될 것이라 믿는다. </p>
<h1 id="디자인-패턴">디자인 패턴?</h1>
<p><img src="https://velog.velcdn.com/images/haeun_/post/b714b0a3-080b-4042-bb46-b9faef8dc233/image.png" alt="">
디자인패턴이라 하면 뭐가 떠오르는가? </p>
<p>얼마전까지의 나는 곧바로 <code>MVVM</code>을 떠올렸다.
하지만 <code>MVVM</code>은 <code>아키텍쳐 패턴</code>으로, <code>디자인 패턴</code>과는 분류가 다르다.</p>
<p>물론 엄밀히 말하면 그렇고, 대부분의 자료에서는 <code>MVVM</code>도 <code>디자인 패턴</code>이라 뭉뚱그려 부르곤 한다.</p>
<p>&#39;밥 먹으러 가자.&#39; 고 해서 간 곳이 칼국수 집이어도 괜찮은 것 처럼, <code>아키텍쳐 패턴</code>을 <code>디자인 패턴</code>이라 부르더라도, 대부분 맥락상 유연하게 해석하여 이를 받아들인다. </p>
<p>그러나 이 포스팅에서 만큼은 <code>아키텍쳐 패턴</code>과 <code>디자인 패턴</code>을 엄밀히 구분해보자. </p>
<blockquote>
<p>우선, <code>MVVM</code>은 <code>디자인 패턴</code>이 아니다.</p>
</blockquote>
<p>누군가에겐 당연한 이야기겠으나, 디자인패턴을 처음 접하는 나에겐 그렇지 않았다. </p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/e5c8a917-71aa-45c9-94e4-298ec1b61c49/image.png" alt=""></p>
<p>하지만 방대한 데이터를 기반으로한 chatGPT마저 <code>MVC</code>를 <code>디자인 패턴</code>이라 표기하였으니 헷갈리는 게 부끄러운 일은 아닐지도 모르겠다. </p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/76575b2f-e699-42a0-a2be-b857e5899770/image.png" alt=""></p>
<p><strong>패턴</strong>이란 특정 상황에 필요한 일반화된 <code>풀이</code>, <code>도구</code>, <code>방법</code>에 해당한다. </p>
<ul>
<li><p><code>아키텍쳐 패턴</code>은 <code>아키텍쳐 설계</code>단계에서 <code>정보를 분류하는 기준과 각 정보간의 연결 방법</code>을 제시한다. </p>
</li>
<li><p><code>디자인 패턴</code>은 <code>실제 코드 작성</code>에서 마주치는 문제들에 대한 <code>일반적인 풀이</code>를 제시한다.</p>
</li>
</ul>
<p>이러한 이론은 아직 잘 와닿지 않는다. </p>
<p><strong>패턴</strong>은 <code>도구</code>이며, 도구는 <code>필요</code>에 의해 사용된다는 점을 상기해 보자. </p>
<p><strong>도구</strong>의 쓰임을 정확히 이해하기 위해선 그들이 <code>필요한 상황</code>을 이해해야 한다.</p>
<p><code>아키텍쳐 패턴</code>은 <code>아키텍쳐 설계</code>를 돕는 도구이다. </p>
<p>지금부터 <strong>소설가</strong>의 입장에서 <code>두 패턴이 적용되는 상황</code>을 이해해 보자.  </p>
<h1 id="아키텍쳐-설계--시나리오-작성">아키텍쳐 설계 = 시나리오 작성</h1>
<h3 id="작은-프로젝트에서는-느껴지지-않는-시나리오-필요성">작은 프로젝트에서는 느껴지지 않는 시나리오 필요성</h3>
<p><img src="https://velog.velcdn.com/images/haeun_/post/e1960465-7253-4a58-be7b-340626b617b3/image.png" alt=""></p>
<p><strong>소설</strong>을 프로젝트에 비유한다면 <strong>일기</strong>는 작은 프로젝트에 비유할 수 있다. </p>
<p>내용이 짧고, 목적이 확고하니 글쓴이는 일기를 작성할 때 시나리오 구성의 필요를 느끼지 못한다. 그러니 <code>시나리오 구성을 생략</code>하고 글을 쓴다. </p>
<p>문제가 될 것이 없기 때문이다. </p>
<h3 id="큰-프로젝트에서-시나리오-부재가-미치는-영향">큰 프로젝트에서 시나리오 부재가 미치는 영향</h3>
<p><img src="https://velog.velcdn.com/images/haeun_/post/acbb74df-fa16-41a0-b7dd-4938fd457b35/image.png" alt=""></p>
<p>그렇게 <code>일기</code>를 쓰며 실력을 갈고 닦던 어느 날, 아마추어 소설가는 <strong>조앤롤링</strong>으로부터 <code>해리포터 외전</code> 작성을 권유 받는다.</p>
<p>데뷔하기에는 더 없이 황금같은 기회였다. 의욕에 가득 차 곧장 이를 수락한다.</p>
<p>한 번도 큰 프로젝트를 맡아본 적 없는 소설가는 늘 그랬듯이 <code>시작</code>과 <code>끝</code>만을 정해둔 채 글을 써내린다.</p>
<p>그러나 <code>결말</code>에 다다를 때쯤 예상치 못한 <code>문제</code>가 발생한다. </p>
<p>바로 <strong>조앤롤링</strong>이 전개 내용 속 특정 사건을 <code>수정</code>해 달라 요청한 것이다. </p>
<p>소설가는 결말이 임박한 시점에 <strong>사건6</strong>을 수정해야 한다.</p>
<p>소설가는 번거롭지만 충분히 가능한 요구라 생각했다. </p>
<p>그러나 얼마 못가, 소설가는 이제와서 깔끔히 <strong>사건6</strong>만을 수정하는 게 쉽지 않은 일임을 깨닫게 된다. </p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/5e74af3b-8bc9-4009-9d15-ca88b9d36a3e/image.png" alt=""></p>
<p><strong>사건6</strong>을 구성하고 있는 내용이 <strong>사건2</strong>와 연관 있음을 알게 된 것이다. </p>
<p>그래서 <strong>사건2</strong>도 고치자니 <strong>사건2</strong>는 또 <strong>사건3</strong>에게 영향을 주고 있다.</p>
<p>끽 해야 바로 전 사건인 <strong>사건5</strong>만 수정하면 될 줄 알았는데, 그게 아니었다. </p>
<p>소설가는 혹시 잘못 건드렸다간 연쇄적으로 모든 전개가 엉망이 될지 몰라 이도저도 못하는 상황에 놓이고 만다. </p>
<blockquote>
<p>소설가는 그제야 사건과 사건이 <code>비순차적</code>으로 엮여 의존하고 있음을 알게 된다.</p>
</blockquote>
<p>중요한 정보와 중요하지 않은 정보가 그물처럼 뒤섞여 있으니 이를 구분하는 것 조차 쉽지 않다. </p>
<p>그러니 소설가는 결국 수정을 포기하고 처음부터 다시 소설을 쓰기로 한다. </p>
<blockquote>
<p>유지보수의 유연성 확보.
이것이 바로 소설가가 느낀 <strong>시나리오 구성</strong> 필요성이다. 
개발자에겐 <strong>아키텍쳐 설계</strong>의 필요성에 해당한다. </p>
</blockquote>
<h2 id="시나리오-작성의-뼈대">시나리오 작성의 뼈대</h2>
<p><img src="https://velog.velcdn.com/images/haeun_/post/c96b0cec-7905-4caa-9089-668b06366ba1/image.png" alt=""></p>
<p>졸지에 작품을 다시 쓰게된 소설가는 이번에는 어떤 <strong>수정 요청</strong>이 들어와도 내용을 쉽게 <strong>교체</strong>할 수 있도록 <strong>시나리오</strong>를 작성하려 한다.</p>
<p>시나리오를 작성하기 위한 방법은 아래와 같다. </p>
<p><strong>각 장면</strong>마다 <code>1)중요도에 따라 정보를 분류</code>하고, 이의 <code>2)의존관계</code>를 명확히 한다. </p>
<p>각 항목을 따르는 구체적인 방법은 아래와 같다.</p>
<p><strong>1) 중요도에 따라 정보를 분류</strong> 
중요도에 따라 정보를 <code>전개 내용</code> / <code>핵심 전개</code> / <code>불변 설정</code>으로 분류한다. 왼쪽에서 오른쪽으로 갈수록 중요한 정보이다. 
예를 들어, 가장 오른쪽 정보인 <code>불변 설정</code>은 이 책이 <code>해리포터 외전</code>이라는 점이 바뀌지 않는 한 변하지 않을 정보를 담고 있다. </p>
<p><strong>2) 의존관계 표시</strong><br><code>의존 방향</code>에 있어 <code>하위 정보</code>(덜 중요한 정보)는 그 내용이 변하더라도 <code>상위 정보</code>(더 중요한 정보)에 <code>영향을 미치지 않아야 한다</code>. 
즉, 그림 속 오른쪽에 위치한 정보는 왼쪽에 위치한 정보가 수정된다 해서 영향을 받지 않는다. </p>
<p>이런 분류를 통해 정리한 <code>수 많은 장면에 대한 시나리오</code>가 모여 </p>
<p>하나의 커다란     <code>소설 시나리오</code>를 구성하게 될 것이다.</p>
<h3 id="한-장면에-대한-시나리오-작성">한 장면에 대한 시나리오 작성</h3>
<p><img src="https://velog.velcdn.com/images/haeun_/post/bfa7ff15-148f-4b73-841a-8ce85ad910d0/image.png" alt=""></p>
<p>위의 뼈대를 토대로 소설을 구성하는 수 많은 장면 중 한 장면에 대한 시나리오를 확인해 보면 아래와 같다. </p>
<ul>
<li><p><code>전개 내용</code>은 해리포터가 넘어졌다는 것이다. </p>
</li>
<li><p><code>핵심 전개</code>는 해리포터의 다리 아래 사소한 상처가 발생했다는 것이다. </p>
</li>
<li><p><code>불변 설정</code>은 장르가 판타지이고, 주인공은 남자이며, 특징으로 번개흉터를 가졌다는 것이다.</p>
</li>
</ul>
<h3 id="시나리오-작성으로-인한-수정-유연성-확보">시나리오 작성으로 인한 수정 유연성 확보</h3>
<p>이러한 분류로 시나리오를 작성하던 때, 
이번에도 <strong>조앤롤링</strong>이 전개 내용 <code>수정</code>해 달라고 요청하게 된다.</p>
<p>해리포터가 넘어진 <strong>기존 내용</strong>을 <code>삭제</code>하고 <strong>다른 상황</strong>으로 <code>교체</code>해 달라는 요청이었다.</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/e073d29f-ffe0-4ff9-ba7c-a4543c09ff6d/image.png" alt=""></p>
<p>시나리오를 작성해 둔 소설가는 <code>수정 요청</code> 받은 정보가 <code>전개 내용</code>에 해당함을 확인하고 <code>필요한 내용만 수정</code>할 수 있게 된다. </p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/2e571608-c6e9-4273-b841-8931a3e5f33c/image.png" alt=""></p>
<p><code>전개 내용</code>이 바뀌었음에도 사소한 상처가 발생한다는 <code>핵심 전개</code>는 변하지 않았다. <code>불변 설정</code> 또한 바뀌지 않았음은 마찬가지다. </p>
<p>이렇듯 미리 작성해 둔 시나리오를 토대로 수정하고자 하는 정보의 <code>중요도</code>를 파악하여 <code>수정 유연성</code>을 확보할 수 있었다. </p>
<p>만약, <code>핵심 전개</code> <code>불변 설정</code>에 대한 수정 요청을 받은 것이라면, 해당 정보가 <code>해리포터 외전</code>이라는 정체성과 밀접하게 연결되어 있으며, 그를 수정하면 그 좌측에 놓인 정보를 전부 수정해야 할 수도 있음을 통해 <strong>조앤롤링</strong>을 설득시킬 수 있을지도 모른다. </p>
<h3 id="시나리오-구성이-없었다면">시나리오 구성이 없었다면,</h3>
<p><img src="https://velog.velcdn.com/images/haeun_/post/1c1c3bda-f11f-466e-87b8-f8610d7dbfea/image.png" alt=""></p>
<p>만약 이런 시나리오 구성 작업이 없었더라면 유기적으로 얽혀있는 사건에서 <code>삭제하면 안될 정보</code>와 <code>삭제해도 될 정보</code>의 구분이 어려웠을 것이다. </p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/562caa7c-d774-4780-83d9-369685e45a23/image.png" alt=""></p>
<p>또한 이러한 정보 분류는 수정을 용이하게 할 뿐만 아니라, 중요한 전개 내용을 놓치거나, 풀었던 떡밥을 회수하지 않는 일을 막아 하여 소설의 <code>완성도</code>에도 기여한다. </p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/d880268d-d1e6-460c-80c7-d7789fdfb5da/image.png" alt=""></p>
<p>이렇게 구성한 시나리오들이 모여 전체 시나리오를 구성한다. </p>
<p>아마 느꼈겠지만, 위의 분류는 무언가와 매우 흡사한 모양이다. </p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/592a7101-b826-4a63-adf8-34ad95ca6876/image.png" alt=""></p>
<p>그렇다. 소설가의 시나리오 작성은 바로 개발자의 아키텍쳐 설계와 흡사하다. </p>
<ul>
<li><code>전개 내용</code>은 <code>View</code></li>
<li><code>핵심 전개</code>는 <code>Adaptor</code> </li>
<li><code>불변 설정</code>은 <code>Model</code></li>
</ul>
<p>iOS에 비유하자면, </p>
<p>이렇게 구성된 각 장면에 대한 시나리오가 바로 특정 역할을 수행하는 
<code>View</code>-<code>Adaptor</code>-<code>Model</code> 이며, 이들이 모여 
<code>하나의 프로젝트</code>(앱)을 구성하게 된다. </p>
<p>대부분의 아키텍쳐 패턴들이 위와 동일한 구성을 가진다. </p>
<blockquote>
<p>그렇다면 각 <code>아키텍쳐 패턴</code> 간의 차이를 결정짓는 것은 무엇일까? 
바로 <code>정보</code>의 <code>중요도를 분류하는 기준</code>과 
그렇게 분류된 정보의 <code>의존방식(화살표 수와 방향)</code> 차이이다. </p>
</blockquote>
<h1 id="아키텍쳐-패턴--아키텍쳐-설계-도구">아키텍쳐 패턴 = 아키텍쳐 설계 도구</h1>
<p><code>아키텍쳐 패턴</code> 마다 정보를 분류하는 기준이 다르며, 그렇게 분류된 각각의 정보를 일컫는 <code>용어</code>에 차이가 있다. 또한 그 정보 간의 <code>의존 방식</code>에도 조금씩 차이가 있다. </p>
<blockquote>
<p>하지만 모든 아키텍쳐 패턴이 정보를 중요도에 따라 나누고 각 정보간의 의존성을 낮추길 원한다는 점은 일치한다. </p>
</blockquote>
<p>요즘 뜨거운 감자로 떠오르는 <code>MVVM</code>을 떠올려 보자. </p>
<p>지금 iOS에서 <code>MVVM</code>이 핫한 이유는 <code>MVVM</code>이 <code>이전의 패턴들</code> 보다 더 나은 <code>아키텍쳐 설계 방식</code>임에 많은 이들이 동의하기 때문이다. </p>
<p>그러나 <code>MVVM</code>은 어디까지나 아키텍쳐 설계를 돕는 도구이다. </p>
<p>개발자는 손에 익은 도구(아키텍쳐 패턴)보다 더 좋은 도구가 나오면 그 사용법을 습득하여 설계에 적용할 수 있어야 한다.</p>
<p>사실 나는 MVVM을 공부하며 꽤나 허덕였다. 이 패턴에 정착하고 나면 다른 패턴에는 눈길도 주지 않고 싶다는 생각을 하기도 했다. </p>
<p>그러니 위의 말은 그때의 내 생각을 경계하기 위해 적은 말이다. </p>
<p>손에 익은 폴더폰을 놓치기 싫어 고집하면 평생 스마트폰의 편리함를 모를 수도 있기 때문이다. </p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/31bc3a96-b19b-4114-9590-36ad9970ed9a/image.png" alt=""></p>
<p>다시 얘기로 돌아와서, iOS 개발자라면 익숙할 아키텍쳐 패턴 MVVM을 시나리오 작성에 빗대어 보자.</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/913d9275-a4fc-4685-9422-9f1054931d6d/image.png" alt=""></p>
<p>MVVM의 정보 간 <code>의존관계</code>(화살표)을 그림에 적용해 보았다. 정보 분류 명칭은 아래와 같이 비교할 수 있다.</p>
<ul>
<li><code>전개 내용</code>은 <code>View</code></li>
<li><code>핵심 전개</code>는 <code>ViewModel</code> </li>
<li><code>불변 설정</code>은 <code>Model</code></li>
</ul>
<p>당연하게도, 시나리오 작성과 같이 오른쪽으로 갈수록 중요한 정보에 해당한다. </p>
<p>(MVVM이 무엇인지에 대해 이야기 하는 포스팅이 아니므로 더한 설명을 생략하고자 한다)</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/78121293-c3ff-41bc-a2f4-cf632e182535/image.png" alt=""></p>
<p>이 포스팅에서 더 다루진 않겠지만, iOS 아키텍쳐 패턴 분류는 <code>핵심 전개</code>에 해당하는 내용이 무엇이냐에 따라 <code>명칭</code>이 결정된다. </p>
<ul>
<li><p><code>핵심 전개</code> -&gt; <code>ViewModel</code> : MVVM</p>
</li>
<li><p><code>핵심 전개</code> -&gt; <code>Presentor</code> : MVP</p>
</li>
<li><p><code>핵심 전개</code> -&gt; <code>Controller</code> : MVC</p>
</li>
</ul>
<p>물론 각 정보간의 의존방향(화살표)에도 차이가 생기지만, 그 부분에 대해서는 다루지 않겠다. </p>
<p>더불어 시나리오 작성에 빗댄 위의 설명을 조금 변형하면 <strong>아키텍쳐 설계 패턴</strong> 중 하나인 <code>DDD</code>도 쉽게 이해할 수 있다. </p>
<blockquote>
<p><strong>소설가</strong>의 입장에서 <code>시나리오 작성</code>이 필요하듯이 <strong>개발자</strong>에겐 <code>아키텍쳐 설계</code>가 필요하다.
그리고 <code>아키텍쳐 패턴</code>은 <code>아키텍쳐 설계</code>를 위한 보다 구체적인 방법을 제시한다. </p>
</blockquote>
<p>이제는 <code>디자인 패턴</code>을 이해해 보자!</p>
<h1 id="디자인-패턴--클리셰">디자인 패턴 = 클리셰</h1>
<p><img src="https://velog.velcdn.com/images/haeun_/post/8679f39b-33a7-4a1b-8324-9f8e9f763831/image.png" alt=""></p>
<p>시나리오가 완성되었으니, 소설가는 이제 &#39;진짜&#39; 서술을 해야 한다.</p>
<p>시나리오 구성을 위한 도구가 <code>아키텍쳐 패턴</code>이었다면, 실제 서술과정에 필요한 도구가 바로 <code>디자인 패턴</code>이다. </p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/d1e3e520-b984-4bdf-8544-5dace34dd441/image.png" alt=""></p>
<blockquote>
<p>실제 서술 과정에서 만난 특정 문제에 대한 일반적인 해결법.</p>
</blockquote>
<p>개발자의 <code>디자인 패턴</code>은 소설가에겐 <code>클리셰</code>와 같다.  </p>
<h2 id="소설가의-클리셰와-개발자의-디자인패턴">소설가의 클리셰와 개발자의 디자인패턴</h2>
<p><img src="https://velog.velcdn.com/images/haeun_/post/655ce314-d240-4d1f-8aea-6a7bd7afd5ed/image.png" alt=""></p>
<p><code>클리셰</code>는 <strong>소설가</strong>에게 <code>뻔한 상황</code>의 <code>일반적인 해결책</code>을 제시한다. </p>
<p>위기가 필요한 위치에서 자동차 사고를 내어 기억 상실을 만든다던가, </p>
<p>위험하지만 죽지는 않아야 하는 상황에서 주인공이 빠져나온 직후 자동차를 폭발시키거나 하는 장면이 그렇다. </p>
<p>뻔하지만, 작가가 필요로 하는 장면에 대한 <code>일반적이고 확실한 해결책</code>이다.</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/1f558741-6b5f-4675-9b36-a910e7a4d095/image.png" alt=""></p>
<p><strong>개발자</strong>에게는 <code>디자인 패턴</code>이 <code>클리셰</code>와 같은 의미를 가진다.</p>
<p>숙련된 개발자가 프로그래밍을 진행하다보면, 자주 익숙한 문제를 마주치게 된다. 다른 개발자들도 마찬가지로 그러한 경험을 한다. </p>
<p>그러니 모두가, 동일하게 겪는 뻔한 문제에 대한 일반적인 해결책을 정의하고 그에 명칭을 붙여 필요할 때마다 사용하는 것의 필요성을 느끼게 된다. </p>
<p>그런 필요에 의해 정의된 일반적인 해결책들이 바로 <code>디자인 패턴</code>이다.</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/35755697-bc3e-4844-96f9-48f92842a3d8/image.png" alt=""></p>
<p><a href="https://refactoring.guru/ko/design-patterns">위의 디자인 패턴이 정리된 매우 유용한 사이트</a></p>
<p><code>디자인 패턴</code>의 종류는 스무가지 안팎으로 꽤나 많은 편이다. </p>
<p>물론 이를 몰라도 프로젝트를 진행함에는 아무런 문제가 없다. </p>
<p>그러나 만일 디자인 패턴을 알고 있다면 이미 앞선 개발자들이 겪어 온 뻔한 문제에 대한 일반화된 해답을 얻을 수 있게 된다. </p>
<p><code>디자인 패턴</code>은 앞선 개발자들이 겪었던 실패를 건너뛰어 <code>작업 시간을 단축</code>시켜주는 <code>편리한 도구</code>이다. </p>
<p>개발자들이 정의한 뻔한 상황에 무엇이 있는 지, 디자인 패턴의 종류를 대략적으로 알기만 하더라도 우리는 프로젝트 진행 중 이를 떠올리고 <code>디자인 패턴</code>의 도움을 받을 수 있다. </p>
<p><strong>하지만</strong>, 클리셰 범벅인 소설은 작품성을 잃듯이, 개발자도 디자인 패턴 사용에 주의를 기울여야 한다.</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/fea5cf5f-d13f-4179-ad20-897f2b584c89/image.png" alt="">
<code>디자인 패턴</code>이라는 도구에 심취하면 더 나은 해결법을 찾지 못하고 마구잡이로 휘두르게 될지 모른다. </p>
<p><code>확고한 목표</code>를 가지고 스스로 세운 <code>원칙</code>들을 지키다 보면 일반화된 풀이인 디자인 패턴보다 더 나은 풀이를 마주할 수도 있다.</p>
<p>디자인 패턴은 도구일 뿐, <code>만능 해결사가 아님</code>을 깊이 유념해야 한다. </p>
<h1 id="마치며">마치며,</h1>
<p><img src="https://velog.velcdn.com/images/haeun_/post/b843855d-0f77-4eb7-91f3-b2c4832cffa6/image.png" alt=""></p>
<p><strong>소설가</strong>에겐 <code>완벽한 시나리오</code>가 작업 능률을 향상시키고 극의 완성도를 보장한다.</p>
<p><code>클리셰</code>를 활용한 전개는 독자의 평판을 결정한다.</p>
<p>당연하게도, 클리셰는 우리 주인공의 성별이나 성격이 어떤 지는 고려해 주지 않기 때문에, 적절한 위치에, 상황에 맞게 변형하여 사용해야 한다.</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/edebb787-3c53-446f-adf7-281fca7e05e0/image.png" alt=""></p>
<p><strong>개발자</strong>에게는 <code>아키텍쳐 패턴</code>을 사용한 <code>아키텍쳐 설계</code>가 작업 능률을 향상시키고, 수정 유연성을 확보하며 완성도를 보장해준다. </p>
<p><code>디자인 패턴</code> 사용은 개발 속도를 단축해주고 빠른 시간 내에 앞선 이들의 지혜를 습득시켜 준다. </p>
<p>하지만 <code>클리셰</code>가 그러했듯이, <code>디자인 패턴</code>은 우리의 코드가 어떤 도메인을 가지고 어떤 문제를 풀이하고 있는 지 고려해주지 않음을 유념해야 한다.</p>
<p><img src="https://velog.velcdn.com/images/haeun_/post/c09f95c4-ab28-4916-8257-c46e07090358/image.png" alt=""></p>
<p>지금까지 일반적으로 <code>디자인 패턴</code>이라 뭉뚱그려져 불리우는 <code>아키텍쳐 패턴</code>과 <code>디자인 패턴</code>의 개념을 소설가의 입장으로 이해해 보았다. </p>
<p>본 포스팅을 작성하며 뒤죽박죽 섞여 있던 개념을 정리하고 이해하는 시간을 가질 수 있었다. </p>
<blockquote>
<p>오개념이 있을 수 있으니 혹시 문제되는 부분이 있다면 지적 부탁드립니다. 
감사합니다! </p>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>