<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>hyunn</title>
    <link>https://dev-hyunn.tistory.com/</link>
    <description>hyunn의 개발 블로그 입니다.</description>
    <language>ko</language>
    <pubDate>Thu, 14 May 2026 05:51:53 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>hyunn383</managingEditor>
    <image>
      <title>hyunn</title>
      <url>https://tistory1.daumcdn.net/tistory/7091658/attach/0de442e2fdde4db48a4a3ef0aee585dd</url>
      <link>https://dev-hyunn.tistory.com</link>
    </image>
    <item>
      <title>CocoaPods version update 과정에서 Error 해결</title>
      <link>https://dev-hyunn.tistory.com/10</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;현재 코코아팟 버전 확인&lt;/p&gt;
&lt;pre id=&quot;code_1747804605077&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pod --version&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버전 업데이트가 필요하다면, 코코아팟 설치 진행&lt;/p&gt;
&lt;pre id=&quot;code_1747804636466&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 가장 최신 버전으로 설치
sudo gem install cocoapods

# or

# 특정 버전으로 설치
sudo gem install cocoapods -v 1.16.2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;❌ Error 발생&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(터미널 에러 로그를 캡쳐를 안해놔서 정확한 로그가 기억나진 않는다...ㅠㅠ)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 에러 발생 시 체크해야 될 건 크게 두가지 인 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;1. Xcode Command Line Tools 버전&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;2. Ruby 버전&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘 다 업데이트 해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;  Error 해결&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;1.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Xcode Command Line Tools&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Xcode Command Line Tools 삭제 후 재 설치&lt;/p&gt;
&lt;pre id=&quot;code_1747805203479&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 삭제
sudo rm -rf /Library/Developer/CommandLineTools

# 설치
sudo xcode-select --install&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같은 창이 뜨면 설치를 진행해준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1800&quot; data-origin-height=&quot;616&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c8K7Gi/btsN7CikS50/iwdu6QclQ4lhIAMmjldKCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c8K7Gi/btsN7CikS50/iwdu6QclQ4lhIAMmjldKCk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c8K7Gi/btsN7CikS50/iwdu6QclQ4lhIAMmjldKCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc8K7Gi%2FbtsN7CikS50%2Fiwdu6QclQ4lhIAMmjldKCk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;410&quot; height=&quot;140&quot; data-origin-width=&quot;1800&quot; data-origin-height=&quot;616&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;2.&lt;span&gt; Ruby&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;Ruby 버전 관리자 rvm 설치&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1747805441447&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;curl -L https://get.rvm.io | bash -s stable&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666; text-align: left;&quot;&gt;Ruby 설치&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1747805475751&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 가장 최신 버전으로 설치
rvm install ruby

# or

# 특정 버전으로 설치
rvm install ruby-3.3.0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;❌ 또 한번의 Error 발생&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;ERROR:&amp;nbsp;&amp;nbsp;While executing gem ... (Gem::Exception)&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;OpenSSL is not available. Install OpenSSL and rebuild Ruby or use non-HTTPS sources (Gem::Exception)&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222; text-align: start;&quot;&gt;-&amp;gt; Ruby를 설치하는 과정에서 OpenSSL 의존성을 정확하게 참조할 수 있도록 해야한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222; text-align: start;&quot;&gt;만약 SSL이 없다면 SSL을 우선 설치하고, 설치를 했다면 ruby 설치 시 &lt;span style=&quot;background-color: #ffffff; color: #222222; text-align: start;&quot;&gt;SSL&lt;/span&gt; 경로를 참조할 수 있게 해준다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1747805786409&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;rvm install ruby-3.3.0 -C --with-openssl-dir=`brew --prefix openssl`&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;모든 에러 해결 후, 코코아팟 설치&lt;/p&gt;
&lt;pre id=&quot;code_1747805855831&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo gem install cocoapods&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치된 코코아팟 버전 확인&lt;/p&gt;
&lt;pre id=&quot;code_1747805871593&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pod --version&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Error</category>
      <author>hyunn383</author>
      <guid isPermaLink="true">https://dev-hyunn.tistory.com/10</guid>
      <comments>https://dev-hyunn.tistory.com/10#entry10comment</comments>
      <pubDate>Wed, 21 May 2025 14:38:43 +0900</pubDate>
    </item>
    <item>
      <title>면접을 위한 CS 전공지식 노트 - 자료 구조</title>
      <link>https://dev-hyunn.tistory.com/9</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;자료 구조란?&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;효율적으로 데이터를 관리하고 수정, 삭제, 탐색, 저장할 수 있는 데이터 집합이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;시간 복잡도&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;문제를 해결하는 데 걸리는 시간과 입력의 함수 관계이다.&lt;br /&gt;&lt;br /&gt;- 빅오 표기법&lt;br /&gt;입력 크기 n의 모든 입력에 대한 알고리즘에 필요한 시간이다.&lt;br /&gt;ex. O(n), O(logn) 등&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;공간 복잡도&lt;/h3&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;프로그램을 실행시켰을 때 필요로 하는 자원 공간의 양이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;선형 자료 구조&lt;/h3&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;요소가 일렬로 나열되어 있는 자료 구조이다.&lt;br /&gt;&lt;br /&gt;1. 연결 리스트&lt;br /&gt;데이터를 감싼 노드를 포인터로 연결해서 공간적인 효율성을 극대화시킨 자료 구조이다.&lt;br /&gt;삽입과 삭제가 O(1) 걸리며 탐색에는 O(n) 걸린다.&lt;br /&gt;&lt;br /&gt;2. 배열&lt;br /&gt;같은 타입의 변수들로 이루어져 있고, 크기가 정해져 있으며, 인접한 메모리 위치에 있는 데이터를 모아놓은 집합이다.&lt;br /&gt;중복을 허용하고 순서가 있다.&lt;br /&gt;탐색에 O(1)이 되어 랜덤 접근이 가능하고, 삽입과 삭제에는 O(n)이 걸린다.&lt;br /&gt;&lt;br /&gt;3. 벡터&lt;br /&gt;동적으로 요소를 할당할 수 있는 동적 배열이다.&lt;br /&gt;컴파일 시점에 개수를 모른다면 벡터를 써야한다.&lt;br /&gt;중복을 허용하고 순서가 있고 랜덤 접근이 가능하다.&lt;br /&gt;맨 뒤의 요소를 삽입/삭제에는 O(1)이 걸리며, 그 외의 요소를 삽입/삭제에는 O(n)이 걸린다.&lt;br /&gt;벡터의 크기가 증가되는 시간 복잡도는 상수 시간 복잡도 O(1)과 유사한 시간 복잡도를 갖는다.&lt;br /&gt;&lt;br /&gt;4. 스택&lt;br /&gt;가장 마지막으로 들어간 데이터가 가장 첫 번째로 나오는 성질(LIFO)을 가진 자료 구조이다.&lt;br /&gt;삽입 및 삭제에 O(1), 탐색에 O(n) 걸린다.&lt;br /&gt;&lt;br /&gt;5. 큐&lt;br /&gt;먼저 집어넣은 데이터가 먼저 나오는 성질(FIFO)을 지닌 자료 구조이다.&lt;br /&gt;삽입 및 삭제에 O(1), 탐색에 O(n)이 걸린다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;비선형 자료 구조&lt;/h3&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;일렬로 나열하지 않고 자료 순서나 관계가 복잡한 구조이다.&lt;br /&gt;&lt;br /&gt;1. 그래프&lt;br /&gt;정점과 간선으로 이루어진 자료 구조이다.&lt;br /&gt;&lt;br /&gt;2. 트리&lt;br /&gt;정점과 간선으로 이루어져 있고, 트리 구조로 배열된 일종의 계층적 데이터의 집합이다.&lt;br /&gt;루트 노드, 내부 노드, 리프 노트 등으로 구성된다.&lt;br /&gt;&lt;br /&gt;* 이진 트리 : 자식의 노드 수가 두 개 이하인 트리이다.&lt;br /&gt;* 이진 탐색 트리 : 노드의 오른쪽 하위 트리에는 '노드 값보다 큰 값'이 있는 노드만 포함되고, 왼쪽 하위 트리에는 '노드 값보다 작은 값'이 들어 있는 트리이다.&lt;br /&gt;* AVL 트리 : 선형적인 트리가 되는 것을 방지하고 스스로 균형을 잡는 이진 탐색 트리이다. 두 자식 서브트리의 높이는 항상 최대 1만큼 차이 난다.&lt;br /&gt;* 레드 블래 트리 : 균형 이진 탐색 트리로 탐색, 삽입, 삭제 모두 시간 복잡도가 O(logn) 이다. 각 노드는 빨간색 또는 검은색의 색상을 나타내는 추가 비트를 저장하며, 삽입 및 삭제 중에 트리가 균형을 유지하도록 하는 데 사용된다.&lt;br /&gt;&lt;br /&gt;3. 힙&lt;br /&gt;완전 이진 트리 기반의 자료 구조이며, 최소힙과 최대힙 두 가지가 있고 해당 힙에 따라 특징을 지킨 트리이다.&lt;br /&gt;&lt;br /&gt;* 최대힙 : 루트 노드에 있는 키는 모든 자식에 있는 키 중에서 가장 커야 한다. 각 노드의 자식 노드와의 관계도 이와 같은 특징이 재귀적으로 이루어져야 한다.&lt;br /&gt;* 최소힙 : 루트 노드에 있는 키는 모든 자식에 있는 키 중에서 최솟값이어야 한다. 각 노드의 자식 노드와의 관계도 이와 같은 특징이 재귀적으로 이루어져야 한다.&lt;br /&gt;&lt;br /&gt;4. 우선순위 큐&lt;br /&gt;대기열에서 우선순위가 높은 요소가 우선 순위가 낮은 요소보다 먼저 제공되는 자료 구조이다.&lt;br /&gt;(힙을 기반으로 구현된다.)&lt;br /&gt;&lt;br /&gt;5. 맵&lt;br /&gt;특정 순서에 따라 키와 매핑된 값의 조합으로 형성된 자료 구조이다.&lt;br /&gt;(map&amp;lt;string, int&amp;gt; 형태로 구현한다. ex. &quot;김지현&quot;: 1, &quot;hyunn&quot;: 2)&lt;br /&gt;&lt;br /&gt;6. 셋&lt;br /&gt;특정 순서에 따라 고유한 요소를 저장하는 컨테이너이다. &lt;br /&gt;중복되는 요소는 없고, 오로지 unique 값만 저장하는 자료 구조이다.&lt;br /&gt;&lt;br /&gt;7. 해시 테이블&lt;br /&gt;무한에 가까운 데이터들을 유한한 개수의 해시 값으로 매핑한 테이블이다.&lt;br /&gt;삽입, 삭제, 탑색 시 평균적으로 O(1)의 시간 복잡도를 가지며 unordered_map으로 구현한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;XL.jpeg&quot; data-origin-width=&quot;934&quot; data-origin-height=&quot;1200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pxsQf/btsMAHSKeYd/TJuKp2gSwlxHp26Zqp52B0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pxsQf/btsMAHSKeYd/TJuKp2gSwlxHp26Zqp52B0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pxsQf/btsMAHSKeYd/TJuKp2gSwlxHp26Zqp52B0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpxsQf%2FbtsMAHSKeYd%2FTJuKp2gSwlxHp26Zqp52B0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;514&quot; data-filename=&quot;XL.jpeg&quot; data-origin-width=&quot;934&quot; data-origin-height=&quot;1200&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Book</category>
      <author>hyunn383</author>
      <guid isPermaLink="true">https://dev-hyunn.tistory.com/9</guid>
      <comments>https://dev-hyunn.tistory.com/9#entry9comment</comments>
      <pubDate>Fri, 28 Feb 2025 13:12:39 +0900</pubDate>
    </item>
    <item>
      <title>면접을 위한 CS 전공지식 노트 - 운영체제</title>
      <link>https://dev-hyunn.tistory.com/8</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;운영체제란?&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;사용자가 컴퓨터를 쉽게 다루게 해주는 인터페이스이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;운영체제의 역할&lt;/h3&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;한정된 메모리나 시스템 자원을 효율적으로 분배한다.&lt;br /&gt;&lt;br /&gt;1. CPU 스케줄링과 프로세스 관리&lt;br /&gt;2. 메모리 관리&lt;br /&gt;3. 디스크 파일 관리&lt;br /&gt;4. I/O 디바이스(마우스, 키보드 등) 관리&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;컴퓨터의 요소&lt;/h3&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;컴퓨터는 CPU, DMA 컨트롤러, 메모리, 타이머, 디바이스 컨트롤러 등으로 이루어져 있다.&lt;br /&gt;&lt;br /&gt;- CPU&lt;br /&gt;산술논리연산장치, 제어장치, 레지스터로 구성되어 있는 컴퓨터 장치이며, 인터럽트에 의해 단순히 메모리에 존재하는 명령어를 해석해서 실행한다.&lt;br /&gt;&lt;br /&gt;* 제어장치 : 프로세스 조작을 지시하는 CPU의 한 부품이다. 입출력장치 간 통신을 제어하고 명령어들을 읽고 해석하며 데이터 처리를 위한 순서를 결정한다.&lt;br /&gt;* 레지스터 : CPU 안에 있는 매우 빠른 임시기억장치이다. CPU는 자체적으로 데이터를 저장할 방법이 없기 때문에 레지스터를 거쳐 데이터를 전달한다.&lt;br /&gt;* 산술논리연산장치 : 산술 연산과 논리 연산을 계산하는 디지털 회로이다.&lt;br /&gt;* 인터럽트 : 어떤 신호가 들어왔을 때 CPU를 잠깐 정지시키는 것이다.&lt;br /&gt;&lt;br /&gt;- DMA 컨트롤러&lt;br /&gt;I/O 디바이스가 메모리에 직접 접근할 수 있도록 하는 하드웨어 장치이다.&lt;br /&gt;&lt;br /&gt;- 메모리&lt;br /&gt;전자회로에서 데이터나 상태, 명령어 등을 기록하는 장치이다. 메모리가 크면 클수록 CPU가 많은 일을 동시에 할 수 있다.&lt;br /&gt;&lt;br /&gt;- 타이머&lt;br /&gt;특정 프로그램에 시간 제한을 다는 역할이다. 시간이 많이 걸리는 프로그램이 작동할 때 제한을 걸기 위함이다.&lt;br /&gt;&lt;br /&gt;- 디바이스 컨트롤러&lt;br /&gt;컴퓨터와 연결되어 있는 IO 디바이스들의 작은 CPU를 말한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;메모리 계층&lt;/h3&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;- 레지스터 : CPU 안에 있는 작은 메모리이다. 휘발성, 속도 가장 빠름, 기억 용량 가장 적음.&lt;br /&gt;- 캐시 : L1, L2 캐시를 지칭한다. 휘발성, 속도 빠름, 기억 용량 적음.&lt;br /&gt;- 주기억장치 : RAM을 지칭한다. 휘발성, 속도 보통, 기억 용량 보통.&lt;br /&gt;- 보조기억장치 : HDD, SSD를 지칭한다. 비휘발성, 속도 낮음, 기억 용량 많음.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;캐시&lt;/h3&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;- 캐시는 데이터를 미리 복사해 놓는 임시 저장소이다.&lt;br /&gt;- 빠른 장치와 느린 장치에서 속도 차이에 따른 병목 현상을 줄이기 위한 메모리이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;웹 부라우저의 캐시&lt;/h3&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;- 쿠키 : 만료기한이 있는 키-값 저장소이다.&lt;br /&gt;- 로컬 스토리지 : 만료기한이 없는 키-값 저장소이며, 브라우저를 닫아도 유지되고 도메인 단위로 저장, 생성된다.&lt;br /&gt;- 세션 스토리지 : 만료기한이 없는 키-값 저장소이며, 탭 단위로 세션 스토리지를 생성하며 탭을 닫을 때 해당 데이터가 삭제된다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;가상 메모리&lt;/h3&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;컴퓨터가 실제로 이용 가능한 메모리 자원을 추상화하여 이를 사용하는 사용자들에게 매우 큰 메모리로 보이게 만드는 것&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;프로세스&lt;/h3&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;컴퓨터에서 실행되고 있는 프로그램을 말하며 CPU 스케줄링 대상이 되는 작업(task)라는 용어와 거의 같은 의미로 쓰인다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;프로세스의 컴파일 과정&lt;/h3&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;1. 전처리&lt;br /&gt;소스 코드의 주석을 제거하고 #include 등 헤더 파일을 병합하여 매크로를 치환한다.&lt;br /&gt;&lt;br /&gt;2. 컴파일러&lt;br /&gt;오류 처리, 코드 최적화 작업을 하며 어셈블리어로 변환한다.&lt;br /&gt;&lt;br /&gt;3. 어셈블러&lt;br /&gt;어셈블리어를 목적 코드로 변환한다.&lt;br /&gt;&lt;br /&gt;4. 링커&lt;br /&gt;라이브러리 함수 또는 다른 파일들과 목적 코드를 결합하여 실행 파일을 만든다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;프로세스의 메모리 구조&lt;/h3&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;- 스택과 힙&lt;br /&gt;런타임 단계에서 메모리를 동적으로 할당받는 동적 영역이다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;* 스택 : 지역 변수, 매개변수, 실행되는 함수에 의해 늘어나거나 줄어드는 메모리 영역이다. 함수가 호출될 때마다 특정 정보가 스택에 계속해서 저장된다.&lt;br /&gt;* 힙 : 동적으로 할당되는 변수들을 담는다.&lt;br /&gt;&lt;br /&gt;- 데이터 영역과 코드 영역&lt;br /&gt;컴파일 단계에서 메모리를 할당하는 정적 영역이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;멀티프로세싱&lt;/h3&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;여러 개의 프로세스를 통해 동시에 두 가지 이상의 일을 수행할 수 있는 것이다. 하나 이상의 일을 병렬로 처리할 수 있으며, 프로세스 중 일부에 문제가 발생되더라도 다른 프로세스를 이용해서 처리할 수 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;스레드&lt;/h3&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;프로세스의 실행 가능한 가장 작은 단위&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;멀티스레딩&lt;/h3&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;-  프로세스 내 작업을 여러 개의 스레드로 처리하는 기법이다.&lt;br /&gt;- 스레드끼리는 서로 자원을 공유한다.&lt;br /&gt;&lt;br /&gt;- 장점&lt;br /&gt;한 스레드가 중단 되어도 다른 스레드는 실행 상태일 수 있기 때문에 중단되지 않은 빠른 처리가 가능하고, 동시성에도 큰 장점이 있다. (동시성은 서로 독립적인 작업들을 작은 단위로 나누고 동시에 실행되는 것처럼 보여주는 것이다.)&lt;br /&gt;&lt;br /&gt;- 단점&lt;br /&gt;한 스레드에 문제가 생기면 다른 스레드에도 영향을 끼쳐 스레드로 이루어져 있는 프로세스에 영향을 줄 수 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;공유 자원&lt;/h3&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;시스템 안에서 각 프로세스, 스레드가 함께 접근할 수 있는 모니터, 프린터, 메모리, 파일, 데이터 등의 자원이나 변수 등을 의미한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;임계 영역&lt;/h3&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;둘 이상의 프로세스, 스레드가 공유 자원에 접근할 때 순서 등의 이유로 결과가 달라지는 코드 영역이다.&lt;br /&gt;&lt;br /&gt;- 해결 방법&lt;br /&gt;lock 매커니즘을 토대로 한 뮤텍스, 세마포어, 모니터를 사용하여 해결한다.&lt;br /&gt;&lt;br /&gt;* 뮤텍스 : lock()을 통해 공유 자원을 잠금 설정하고, 사용한 후에는 unlock()을 통해 잠금 해제한다.&lt;br /&gt;* 세마포어 : wait()을 통해 자신의 차례가 올 때까지 기다리고, signal()을 통해 다음 프로세스로 순서를 넘겨준다.&lt;br /&gt;* 모니터 : 둘 이상의 스레드나 프로세스가 공유 자원에 안전하게 접근할 수 있도록 공유 자원을 숨기고 해당 접근에 대해 인터페이스만 제공한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;교착 상태&lt;/h3&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;두 개 이상의 프로세스들이 서로가 가진 자원을 기다리며 중단된 상태이다.&lt;br /&gt;&lt;br /&gt;- 원인&amp;nbsp;&lt;br /&gt;상호 배제 : 한 프로세스가 자원을 독점하고 있으며 다른 프로세스들은 접근이 불가능하다.&lt;br /&gt;점유 대기 : 특정 프로세스가 점유한 자원을 다른 프로세스가 요청하는 상태이다.&lt;br /&gt;비선점 : 다른 프로세스의 자원을 강제적으로 가져올 수 없다.&lt;br /&gt;환형 대기 : 두 개의 프로세스가 서로가 서로의 자원을 요구하는 상황이다.&lt;br /&gt;&lt;br /&gt;- 해결&lt;br /&gt;1. 자원을 할당할 때 애초에 조건이 성립되지 않도록 설계한다.&lt;br /&gt;2. 은행원 알고리즘 (총 자원의 양과 현재 할당한 자원의 양을 기준으로 안정 또는 불안정 상태로 나누고 안정 상태로 가도록 자원을 할당하는 알고리즘) 을 사용한다.&lt;br /&gt;3. 교착 상태가 발생하면 사이클이 있는지 찾아보고 이에 관련된 프로세스를 지운다.&lt;br /&gt;4. 사용자가 작업을 종료할 수 있게끔 '응답 없음' 문구 등으로 알려준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;스케줄러&lt;/h3&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;CPU 스케줄러는 스케줄링 알고리즘에 따라 프로세스에서 해야 하는 일을 스레드 단위로 CPU에 할당한다.&lt;br /&gt;&lt;br /&gt;- 비선점형 방식&lt;br /&gt;프로세스가 스스로 CPU 소유권을 포기하는 방식이며, 강제로 프로세스를 중지하지 않는다.&lt;br /&gt;&lt;br /&gt;1. FCFS (First Come, First Served) :&amp;nbsp; 가장 먼저 온 것을 가장 먼저 처리한다. 준비 큐에서 오래 기다리는 현상이 발생하는 단점이 있다.&lt;br /&gt;2. SJF (Shortest Job First) : 가장 짧은 프로세스를 가장 먼저 실행하는 알고리즘이다. 긴 시간을 가진 프로세스가 실행되지 않는 현상이 생길 수 있는 단점이 있다.&lt;br /&gt;3. 우선순위 : 우선순위를 높이는 방법(aging)을 통해 SJF의 단점을 보완한 알고리즘이다.&lt;br /&gt;&lt;br /&gt;- 선점형 방식&lt;br /&gt;지금 사용하고 있는 프로세스를 알고리즘에 의해 중단시켜 버리고 강제로 다른 프로세스에 CPU 소유권을 할당하는 방식이다.&lt;br /&gt;&lt;br /&gt;1. 라운드 로빈 : 우선순위 스케줄링의 일종으로 각 프로세스는 동일한 할당 시간을 주고 그 시간 안에 끝나지 않으면 다시 준비 큐의 뒤로 가는 알고리즘이다.&lt;br /&gt;2. SRF (Shortest Remaining Time First) : 중간에 더 짧은 작업이 들어오면 수행하던 프로세스를 중지하고 해당 프로세스를 수행한다.&lt;br /&gt;3. 다단계 큐 : 우선순위에 따른 준비 큐를 여러 개 사용하고, 큐마다 라운드 로빈이나 FCFS 등 다른 스케줄링 알고리즘을 적용한다. 큐 간의 프로세스 이동이 안되므로 스케줄링 부담이 적지만 유연성이 떨어지는 특징이 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;XL.jpeg&quot; data-origin-width=&quot;934&quot; data-origin-height=&quot;1200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TNBLP/btsMxJK8XX3/mZVkbCxX5sQ9rdWYmFJsNK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TNBLP/btsMxJK8XX3/mZVkbCxX5sQ9rdWYmFJsNK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TNBLP/btsMxJK8XX3/mZVkbCxX5sQ9rdWYmFJsNK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTNBLP%2FbtsMxJK8XX3%2FmZVkbCxX5sQ9rdWYmFJsNK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;514&quot; data-filename=&quot;XL.jpeg&quot; data-origin-width=&quot;934&quot; data-origin-height=&quot;1200&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Book</category>
      <author>hyunn383</author>
      <guid isPermaLink="true">https://dev-hyunn.tistory.com/8</guid>
      <comments>https://dev-hyunn.tistory.com/8#entry8comment</comments>
      <pubDate>Thu, 27 Feb 2025 12:04:20 +0900</pubDate>
    </item>
    <item>
      <title>면접을 위한 CS 전공지식 노트 - 네트워크</title>
      <link>https://dev-hyunn.tistory.com/7</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;네트워크란?&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;노드와 링크가 서로 연결되어 있으며 리소스를 공유하는 집합&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;좋은 네트워크란?&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;많은 처리량을 처리할 수 있으며, 지연 시간이 짧고, 장애 빈도가 적으며, 좋은 보안을 갖춘 네트워크이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;네트워크 토폴로지&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;노드와 링크가 어떻게 배치되어 있는지에 대한 방식이자 연결 형태&lt;br /&gt;&lt;br /&gt;- 트리 토폴로지 : 계층형&lt;br /&gt;- 버스 토폴로지 : 중앙 통신 회선 하나에 여러 개의 노드가 연결&lt;br /&gt;- 스타 토폴로지 : 중앙에 있는 노드에 모두 연결된 네트워크 구성&lt;br /&gt;- 링형 토폴로지 : 각각의 노드가 양 옆의 두 노드와 연결&lt;br /&gt;- 메시 토폴로지 : 그물망 형태&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;병목 현상&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;전체 시스템의 성능이나 용량이 하나의 구성 요소로 인해 제한 받는 현상&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;네트워크 계층 구조&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;- TCP/IP 4계층&lt;br /&gt;애플리케이션 계층 - 전송 계층 - 인터넷 계층 - 링크 계층&lt;br /&gt;&lt;br /&gt;- OSI 7계층&lt;br /&gt;애플리케이션 계층 - 프레즌테이션 계층 - 세션 계층 - 전송 계층 - 네트워크 계층 - 데이터 링크 계층 - 물리 계층&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;- 애플리케이션 계층 : 프로토콜 계층 (FTP, HTTP, SSH, SMTP, DNS)&lt;br /&gt;FTP : 파일 전송 통신 프로토콜&lt;br /&gt;SSH : 암호화 네트워크 프로토콜&lt;br /&gt;HTTP : 웹을 위한 데이터 통신 프로토콜&lt;br /&gt;SMTP : 전자 메일 전송 통신 프로토콜&lt;br /&gt;DNS : 도메인 이름과 IP 주소를 매핑해주는 서버&lt;br /&gt;&lt;br /&gt;- 전송 계층 : 송신자와 수신자 연결 (TCP, UDP)&lt;br /&gt;TCP : 패킷 사이의 순서 보장, 연결지향, 가상회선 패킷 교환 방식 사용&lt;br /&gt;UDP : 패킷 사이의 순서 보장 x, 수신 여부 확인 x, 단순히 데이터만 주는 데이터그램 패킷 교환 방식 사용&lt;br /&gt;&lt;br /&gt;- 인터넷 계층 : 장치로부터 받은 네트워크 패킷을 IP 주소로 지정된 목적지로 전송하기 위해 사용되는 계층 (IP, ARP, ICMP)&lt;br /&gt;&lt;br /&gt;- 링크 계층 : 전선, 광섬유, 무선 등으로 실질적으로 데이터 전달&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HTTP&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;HTTP는 애플리케이션 계층으로서 웹 서비스 통신에 사용된다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HTTP/1.0&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;한 연결당 하나의 요청을 처리하도록 설계되어 있다.&lt;br /&gt;&lt;br /&gt;- 단점&lt;br /&gt;RTT 증가 (RTT란? 패킷 왕복 시간)&lt;br /&gt;&lt;br /&gt;- 단점 극복 방법&lt;br /&gt;이미지 스플리팅, 코드 압충, 이미지 Base64 dlszheld&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;HTTP/1.1&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;한번 TCP 초기화를 한 이후에 keep-alive 옵션으로 여러 개의 파일을 송수신할 수 있다.&lt;br /&gt;&lt;br /&gt;- 단점&lt;br /&gt;HOL Blocking (같은 큐에 있는 첫번째 패킷이 지연될 때 발생하는 성능 저하 현상), 무거운 헤더 구조 (압축이 되지 않았음)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;HTTP/2&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;멀티플렉싱, 헤더 압축, 서버 푸시, 요청의 우선순위 처리를 지원하는 프로토콜&lt;br /&gt;&lt;br /&gt;- 멀티플랙싱 : 여러 개의 스트림을 사용하여 송수신&lt;br /&gt;- 서버 푸시 : 클라이언트 요청 없이 서버가 바로 리소스를 푸시할 수 있음&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HTTP/3&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;TCP가 아닌 QUIC라는 계층 위에서 돌아가며, UDP 기반으로 돌아간다.&lt;br /&gt;&lt;br /&gt;- 장점&lt;br /&gt;3-웨이 핸드셰이크 과정을 거치지 않으므로 초기 연결 설정 시 지연 시간이 감소한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HTTPS&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;애플리케이션 계층과 전송 계층 사이에 SSL/TLS 계층을 넣은 신뢰할 수 있는 HTTP 요청&lt;br /&gt;&lt;br /&gt;- SSL/TLS : 보안을 제공하는 프로토콜, 보안 세션을 기반으로 데이터를 암호화하며 인증 메커니즘, 키 교환 암호화 알고리즘, 해싱 알고리즘이 사용된다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해싱 알고리즘&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;데이터를 추정하기 힘든 더 작고, 섞여 있는 조각으로 만드는 알고리즘&lt;br /&gt;ex. SHA-256, SHA-384&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SEO&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;검색엔진 최적화를 뜻하며, 사용자들이 검색엔진으로 웹사이트를 검색했을 때, 그 결과를 상단에 노출시킬 수 있도록 최적화하는 방법&lt;br /&gt;&lt;br /&gt;- 방법&lt;br /&gt;캐노니컬 설정, 메타 설정, 페이지 속도 개선, 사이트맵 관리&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 참고 용어&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 브로드캐스트 : 송신 호스트가 전송한 데이터가 네트워크에 연결된 모든 호스트에 전송되는 방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 유니캐스트 : 고유 주소로 하나의 네트워크 목적지에 1:1로 데이터를 전송하는 방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 라우팅 : IP 주소를 찾아가는 과정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 게이트웨이 : 서로 다른 통신망, 프로토콜을 사용하는 네트워크 간의 통신을 가능하게 하는 관문 역할을 하는 컴퓨터나 소프트웨어&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 해시 : 다양한 길이를 가진 데이터를 고정된 길이를 가진 데이터로 매핑한 값&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 해싱 : 임의의 데이터를 해시로 바꿔주는 일&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 해시 함수 : 임의의 데이터를 입력으로 받아 일정한 길이의 데이터로 바꿔주는 함수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;XL.jpeg&quot; data-origin-width=&quot;934&quot; data-origin-height=&quot;1200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/F7k9R/btsMq1RNv75/mbefWF6t89vp9KPv80d5pK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/F7k9R/btsMq1RNv75/mbefWF6t89vp9KPv80d5pK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/F7k9R/btsMq1RNv75/mbefWF6t89vp9KPv80d5pK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FF7k9R%2FbtsMq1RNv75%2FmbefWF6t89vp9KPv80d5pK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;410&quot; height=&quot;527&quot; data-filename=&quot;XL.jpeg&quot; data-origin-width=&quot;934&quot; data-origin-height=&quot;1200&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Book</category>
      <author>hyunn383</author>
      <guid isPermaLink="true">https://dev-hyunn.tistory.com/7</guid>
      <comments>https://dev-hyunn.tistory.com/7#entry7comment</comments>
      <pubDate>Thu, 20 Feb 2025 19:08:52 +0900</pubDate>
    </item>
    <item>
      <title>면접을 위한 CS 전공지식 노트 - 디자인 패턴과 프로그래밍 패러다임</title>
      <link>https://dev-hyunn.tistory.com/6</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;디자인 패턴&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;싱글톤 패턴&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;하나의 클래스에 오직 하나의 인스턴스만 가지는 패턴&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;- 하나의 인스턴스를 다른 모듈들이 공유하며 사용한다.&lt;br /&gt;&lt;br /&gt;- 장점&lt;br /&gt;인스턴스를 생성할 때 드는 비용이 줄어든다.&lt;br /&gt;&lt;br /&gt;- 단점&lt;br /&gt;의존성이 높아진다.&lt;br /&gt;&lt;br /&gt;- 단점 해결법&lt;br /&gt;의존성 주입 (Dpendency Injection)&lt;br /&gt;&amp;rarr; 상위 모듈은 하위 모듈에서 어떠한 것도 가져오지 않고, 추상화에 의존해야 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;팩토리 패턴&lt;/h4&gt;
&lt;blockquote style=&quot;color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;객체를 사용하는 코드에서 객체 생성 로직을 분리하여 추상화한 패턴&lt;/blockquote&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;- 상위 클래스에서 뼈대를 결정하고, 하위 클래스에서 객체 생성에 관한 구체적인 내용을 결정한다.&lt;br /&gt;- 의존성 주입이라고도 볼 수 있다.&lt;br /&gt;&lt;br /&gt;- 장점&lt;br /&gt;코드 리팩토링 용이, 유지 보수성 증가&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;옵저버 패턴&lt;/h4&gt;
&lt;blockquote style=&quot;color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;주체가 객체의 상태 변화를 관찰하다가 상태 변화가 있을 때마다 옵저버들에게 변화를 알려주는 패턴&lt;/blockquote&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;- 주체 : 상태 변화를 보고 있는 관찰자&lt;br /&gt;- 옵저버 : 변화 사항이 생기는 객체들&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;프록시 패턴&lt;/h4&gt;
&lt;blockquote style=&quot;color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;객체에 접근하기 전 그 접근에 대한 흐름을 가로채 객체 앞단의 인터페이스 역할을 하는 패턴&lt;/blockquote&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;- 객체의 속성, 변환 등을 보완한다.&lt;br /&gt;- 보안, 데이터 검증, 캐싱, 로깅에 사용한다.&lt;br /&gt;&lt;br /&gt;* 프록시 서버&lt;br /&gt;- 서버와 클라이언트 사이에서 클라이언트가 프록시 서버를 통해 다른 네트워크 서비스에 간접적으로 접속할 수 있게 해준다.&lt;br /&gt;- 프록시 서버의 캐시에 정보를 담아두고, 캐시 안에 있는 정보를 요구하는 요청에 대해서는 원격 서버에 요청하는 것이 아닌 프록시 서버의 캐시 안에 있는 데이터를 활용한다. 이를 통해 불필요하게 외부와 연결하지 않기 때문에 트래픽을 줄일 수 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;MVC 패턴&lt;/h4&gt;
&lt;blockquote style=&quot;color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;모델, 뷰, 컨트롤러로 이루어진 디자인 패턴&lt;/blockquote&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;- 모델 : 애플리케이션의 데이터 (데이터베이스, 상수, 변수)&lt;br /&gt;- 뷰 : 사용자 인터페이스 요소 (inputbox, checkbox, textarea)&lt;br /&gt;- 컨트롤러 : 하나 이상의 모델과 하나 이상의 뷰를 잇는 다리역할을 하며 이벤트 등 메인 로직을 담당하고, 모델과 뷰의 생명주기도 관리한다.&lt;br /&gt;&lt;br /&gt;- 장점&lt;br /&gt;개발하기 편리하며 재사용성과 확장성이 용이하다.&lt;br /&gt;&lt;br /&gt;- 단점&amp;nbsp;&lt;br /&gt;애플리케이션이 복잡해질수록 모델과 뷰의 관계 또한 복잡해진다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;MVP 패턴&lt;/h4&gt;
&lt;blockquote style=&quot;color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;MVC의 C에 해당하는 컨트롤러가 프리젠터로 교체된 디자인 패턴&lt;/blockquote&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;뷰와 프리젠터는 일대일 관계이기 때문에 MVC 패턴보다 더 강한 결합을 지닌 디자인 패턴이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;MVVM 패턴&lt;/h4&gt;
&lt;blockquote style=&quot;color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;MVC의 C에 해당하는 컨트롤러가 뷰모델로 바뀐 디자인 패턴&lt;/blockquote&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;- 뷰모델 : 뷰를 더 추상화한 계층이며, 커맨드와 데이터 바인딩을 가진다.&lt;br /&gt;&lt;br /&gt;- 장점&lt;br /&gt;UI를 별도의 코드 수정 없이 재사용할 수 있고 단위 테스팅하기 쉽다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;프로그래밍 패러다임&lt;/h2&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;선언형/함수형 프로그래밍&lt;/h4&gt;
&lt;blockquote style=&quot;color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;프로그램은 함수로 이루어진 것이다. '무엇을' 풀어내는가에 집중하는 패러다임&lt;/blockquote&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;- 작은 순수 함수들을 블록처럼 쌓아 로직을 구현하고, 고차 함수를 통해 재사용성을 높인다.&lt;br /&gt;&lt;br /&gt;* 순수 함수&lt;br /&gt;출력이 입력에만 의존하는 함수&lt;br /&gt;&lt;br /&gt;* 고차 함수&lt;br /&gt;함수가 함수를 값처럼 매개변수로 받아 로직을 생성할 수 있는 함수&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;객체지향 프로그래밍&lt;/h4&gt;
&lt;blockquote style=&quot;color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;객체들의 집합으로 프로그램의 상호 작용을 표현하는 패러다임&lt;/blockquote&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;-  데이터를 객체로 취급하여 객체 내부에 선언된 메서드를 활용한다.&lt;br /&gt;&lt;br /&gt;* 특징&lt;br /&gt;- 추상화 : 핵심 개념과 기능을 간추려내는 것&lt;br /&gt;- 캡슐화 : 객체의 속성과 메서드를 하나로 묶고 일부를 외부에 감추어 은닉하는 것&lt;br /&gt;- 상속성 : 상위 클래스의 특성을 하위 클래스가 이어받아서 재사용하거나 추가, 확장 하는 것&lt;br /&gt;- 다형성 : 하나의 메서드나 클래스가 다양한 방법으로 동작 하는 것&lt;br /&gt;(오버로딩 : 같은 이름을 가진 여러개의 메서드, 오버라이딩 : 상위 클래스로부터 상속받은 메서드를 하위 클래스가 재정의)&lt;br /&gt;&lt;br /&gt;* 설계 원칙&lt;br /&gt;SOLID 원칙&lt;br /&gt;- 단일 책임 원칙 : 모든 클래스는 각각 하나의 책임만 갖는다.&lt;br /&gt;- 개방/폐쇄 원칙 : 기존의 코드는 잘 변경하지 않으면서 확장은 쉽게 할 수 있어야 한다.&lt;br /&gt;- 리스코프 치환 원칙 : 부모 객체와 자식 객체를 바꿔도 문제가 없어야 한다.&lt;br /&gt;- 인터페이스 분리 원칙 : 구체적인 여러 개의 인터페이스를 만들어야 한다.&lt;br /&gt;- 의존 역전 원칙 : 상위 계층은 하위 계층의 변화에 영향 받지 않게 독립적이여야 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;절차형 프로그래밍&lt;/h4&gt;
&lt;blockquote style=&quot;color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;로직이 수행되어야 할 연속적인 계산 과정으로 이루어진 프로그래밍 패러다임&lt;/blockquote&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;- 장점&lt;br /&gt;코드의 가독성이 좋고 실행 속도가 빠르다.&lt;br /&gt;&lt;br /&gt;- 단점&amp;nbsp;&lt;br /&gt;모듈화하기 어렵고 유지 보수성이 떨어진다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;XL.jpeg&quot; data-origin-width=&quot;934&quot; data-origin-height=&quot;1200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l5A7n/btsMkaW58QT/UkJkOcBeyWxGPBzakleBV0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l5A7n/btsMkaW58QT/UkJkOcBeyWxGPBzakleBV0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l5A7n/btsMkaW58QT/UkJkOcBeyWxGPBzakleBV0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl5A7n%2FbtsMkaW58QT%2FUkJkOcBeyWxGPBzakleBV0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;578&quot; data-filename=&quot;XL.jpeg&quot; data-origin-width=&quot;934&quot; data-origin-height=&quot;1200&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Book</category>
      <author>hyunn383</author>
      <guid isPermaLink="true">https://dev-hyunn.tistory.com/6</guid>
      <comments>https://dev-hyunn.tistory.com/6#entry6comment</comments>
      <pubDate>Mon, 17 Feb 2025 16:52:09 +0900</pubDate>
    </item>
    <item>
      <title>[iOS] 딥링크(URI Scheme, Universal Link)와 Deferred DeepLink</title>
      <link>https://dev-hyunn.tistory.com/5</link>
      <description>&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;딥링크(Deep Link)&lt;/h2&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;정의&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;DeepLink는 앱의 특정 화면, 기능, 또는 콘텐츠로 직접 이동할 수 있게 해주는 링크이다.&lt;/blockquote&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;종류&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;1. 표준 URL Scheme&lt;br /&gt;&amp;nbsp; &amp;nbsp; - 'myapp://' 같은 형식으로 앱 고유의 스킴을 사용&lt;br /&gt;2. Universal Links (iOS), App Links (Android)&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp;- 웹 URL을 사용하여 앱으로 연결&lt;/blockquote&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;URL Shceme vs. Universal Links&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;1. 작동 방식&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;- URL Scheme: 커스텀 프로토콜을 사용하여 앱을 실행 (예: myapp://)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Universal Links: 표준 HTTP/HTTPS URL을 사용 (예: https://www.myapp.com)&lt;br /&gt;&lt;br /&gt;2. 플랫폼 지원&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;URL&amp;nbsp;Scheme:&amp;nbsp;iOS,&amp;nbsp;Android&amp;nbsp;등&amp;nbsp;대부분의&amp;nbsp;모바일&amp;nbsp;플랫폼에서&amp;nbsp;지원&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Universal&amp;nbsp;Links:&amp;nbsp;iOS와&amp;nbsp;macOS에서만&amp;nbsp;지원&amp;nbsp;(Android는&amp;nbsp;App&amp;nbsp;Links라는&amp;nbsp;유사한&amp;nbsp;기능&amp;nbsp;사용)&lt;br /&gt;&lt;br /&gt;3. 폴백(Fallback) 동작&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;URL&amp;nbsp;Scheme:&amp;nbsp;앱이&amp;nbsp;설치되지&amp;nbsp;않은&amp;nbsp;경우&amp;nbsp;오류&amp;nbsp;발생&amp;nbsp;또는&amp;nbsp;아무&amp;nbsp;동작&amp;nbsp;없음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Universal&amp;nbsp;Links:&amp;nbsp;앱이&amp;nbsp;없으면&amp;nbsp;자동으로&amp;nbsp;웹&amp;nbsp;브라우저에서&amp;nbsp;해당&amp;nbsp;URL&amp;nbsp;열림&lt;br /&gt;&lt;br /&gt;4. 보안&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;URL&amp;nbsp;Scheme:&amp;nbsp;다른&amp;nbsp;앱에서&amp;nbsp;스키마를&amp;nbsp;가로채거나&amp;nbsp;악용할&amp;nbsp;수&amp;nbsp;있어&amp;nbsp;보안에&amp;nbsp;취약&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Universal&amp;nbsp;Links:&amp;nbsp;HTTPS를&amp;nbsp;사용하고&amp;nbsp;Apple에서&amp;nbsp;인증하므로&amp;nbsp;더&amp;nbsp;안전함&lt;br /&gt;&lt;br /&gt;5. SEO&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;URL&amp;nbsp;Scheme:&amp;nbsp;검색&amp;nbsp;엔진이&amp;nbsp;인식하지&amp;nbsp;못해&amp;nbsp;SEO에&amp;nbsp;도움&amp;nbsp;안&amp;nbsp;됨&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Universal&amp;nbsp;Links:&amp;nbsp;일반&amp;nbsp;웹&amp;nbsp;URL이므로&amp;nbsp;SEO에&amp;nbsp;유리&lt;br /&gt;&lt;br /&gt;6. 사용자 경험&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;URL&amp;nbsp;Scheme:&amp;nbsp;앱&amp;nbsp;설치&amp;nbsp;여부에&amp;nbsp;따라&amp;nbsp;경험이&amp;nbsp;크게&amp;nbsp;달라질&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Universal&amp;nbsp;Links:&amp;nbsp;앱&amp;nbsp;설치&amp;nbsp;여부와&amp;nbsp;관계없이&amp;nbsp;일관된&amp;nbsp;경험&amp;nbsp;제공&lt;br /&gt;&lt;br /&gt;7. 구현 복잡성&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;URL&amp;nbsp;Scheme:&amp;nbsp;비교적&amp;nbsp;간단하게&amp;nbsp;구현&amp;nbsp;가능&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Universal&amp;nbsp;Links:&amp;nbsp;서버&amp;nbsp;설정과&amp;nbsp;앱&amp;nbsp;구성이&amp;nbsp;더&amp;nbsp;복잡함&lt;br /&gt;&lt;br /&gt;8. 딥링킹&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;URL&amp;nbsp;Scheme:&amp;nbsp;앱&amp;nbsp;내&amp;nbsp;특정&amp;nbsp;화면으로&amp;nbsp;이동&amp;nbsp;가능&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Universal&amp;nbsp;Links:&amp;nbsp;앱과&amp;nbsp;웹&amp;nbsp;모두에서&amp;nbsp;동일한&amp;nbsp;링크로&amp;nbsp;특정&amp;nbsp;콘텐츠에&amp;nbsp;접근&amp;nbsp;가능&lt;br /&gt;&lt;br /&gt;9. 사용자 선택&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;URL&amp;nbsp;Scheme:&amp;nbsp;사용자가&amp;nbsp;선택할&amp;nbsp;수&amp;nbsp;없이&amp;nbsp;항상&amp;nbsp;앱으로&amp;nbsp;연결&amp;nbsp;시도&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Universal&amp;nbsp;Links:&amp;nbsp;사용자가&amp;nbsp;웹과&amp;nbsp;앱&amp;nbsp;중&amp;nbsp;선택&amp;nbsp;가능&lt;/blockquote&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;앱이 설치되어 있지 않은 경우, 앱스토어로 이동하게 하는 방법&lt;/h3&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;URL Shceme&lt;/h4&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size18&quot;&gt;1. JavaScript를 이용&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function openApp() {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var appUrl = &quot;myapp://&quot;;&amp;nbsp;&amp;nbsp;// URL Scheme
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var storeUrl = &quot;https://apps.apple.com/app/idXXXXXXXXXX&quot;;&amp;nbsp;&amp;nbsp;// App Store URL

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var clickedAt = +new Date;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; setTimeout(function() {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (+new Date - clickedAt &amp;lt; 2000) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; window.location = storeUrl;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }, 500);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; window.location = appUrl;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;- 먼저 URL Scheme을 사용해 앱을 열려고 시도&lt;br /&gt;- 2초 이내에 앱이 열리지 않으면, App Store 페이지로 리다이렉트&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;Universal Links&lt;/h4&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size18&quot;&gt;1. 메타 태그를 추가하여 Smart App Banner를 구현&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;&amp;lt;meta name=&quot;apple-itunes-app&quot; content=&quot;app-id=myAppStoreID, affiliate-data=myAffiliateData, app-argument=myURL&quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;- 이 배너는 &lt;span style=&quot;color: #333333;&quot;&gt;iOS에서 제공하는 기능이며,&lt;/span&gt; 앱이 설치되지 않은 경우 사용자에게 App Store로 이동할 수 있는 옵션을 제공함&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;100&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mn8wZ/btsJouWGptd/XfEESo2rxU38rANjsRRyLk/tfile.svg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mn8wZ/btsJouWGptd/XfEESo2rxU38rANjsRRyLk/tfile.svg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mn8wZ/btsJouWGptd/XfEESo2rxU38rANjsRRyLk/tfile.svg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fmn8wZ%2FbtsJouWGptd%2FXfEESo2rxU38rANjsRRyLk%2Ftfile.svg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;100&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;100&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;- 실제 iOS 디바이스에서 Safari 브라우저를 통해 볼 수 있는 Smart App Banner의 모습&lt;/p&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size18&quot;&gt;2. 서버 측 리다이렉션&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;- Universal Link로 요청이 서버에 도달했을 때, 서버에서 User-Agent를 확인하여 앱이 설치되지 않은 것으로 판단되면 App Store URL로 리다이렉트&lt;/p&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size18&quot;&gt;3. 웹페이지에 앱 설치 유도 버튼 추가&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;- Universal Link가 웹페이지로 연결되었을 때, 페이지 내에 눈에 띄는 &quot;앱 설치&quot; 버튼을 배치하여 사용자를 App Store로 유도&lt;/p&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size18&quot;&gt;4. 커스텀 중간 페이지 사용&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;- Universal Link가 웹페이지로 연결되었을 때, 사용자에게 앱 설치 또는 웹 버전 사용을 선택할 수 있는 중간 페이지를 보여주기&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;지연된 딥링크(Deferred DeepLink)&lt;/h2&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;정의&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;앱이 설치되지 않은 상태에서 생성되어, 앱 설치 후 첫 실행 시 특정 콘텐츠나 화면으로 사용자를 안내하는 링크&lt;/blockquote&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;작동 방식&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;1. 사용자가 링크를 클릭하면 앱 스토어로 이동&lt;br /&gt;2. 앱 설치 후 첫 실행 시, 원래 의도된 딥링크 대상으로 사용자를 안내&lt;/blockquote&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;구현 고려사항&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;- 서버 측 저장: 딥링크 정보를 서버에 저장하고 앱 설치 후 조회해야 한다.&lt;br /&gt;- 사용자 식별: 설치 전후 사용자를 식별하는 메커니즘이 필요하다.&lt;br /&gt;- 시간 제한: 딥링크의 유효 기간을 설정하는 것이 좋다.&lt;/blockquote&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;기술적 구현&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;- 핑거프린팅: 기기의 고유한 특성을 이용해 사용자를 식별한다.&lt;br /&gt;- IDFA/GAID: 광고 식별자를 사용할 수 있지만, 개인정보 정책에 주의해야 한다.&lt;br /&gt;- 서드파티 툴: Firebase Dynamic Links, Branch.io, AppsFlyer One Link 등의 서비스를 활용할 수 있다.&lt;/blockquote&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;써드파티 툴 없이 디퍼드 딥링크 구현하는 방법&lt;/h3&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;1. Universal Links 설정&lt;/h4&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size18&quot;&gt;1-1. Associated Domains 설정&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;bull; Signing &amp;amp; Capabilities 탭에서 + Capability 버튼을 클릭하고 Associated Domains를 추가&lt;br /&gt;&amp;bull; applinks:yourdomain.com 형식으로 도메인을 추가&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size18&quot;&gt;1-2. apple-app-site-association 파일 설정&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;bull; 서버의 루트에 .well-known 디렉토리를 생성&lt;br /&gt;&amp;bull; 해당 디렉토리에 apple-app-site-association 파일을 추가&lt;/p&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;2. 딥링크 처리 구현&lt;/h4&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size18&quot;&gt;2-1. AppDelegate에서 URL 처리&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;bull; 앱이 열릴 때 Universal Link를 통해 전달된 URL을 처리한다&lt;/p&gt;
&lt;pre class=&quot;Swift&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;Swift&quot;&gt;&lt;code&gt;func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -&amp;gt; Void) -&amp;gt; Bool {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if userActivity.activityType == NSUserActivityTypeBrowsingWeb, let url = userActivity.webpageURL {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// URL을 처리하는 코드 작성
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;handleDeeplink(url)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return true
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;swift&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;func handleDeeplink(_ url: URL) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// URL의 경로와 매개변수를 분석하여 적절한 화면을 연다.
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let path = url.path
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let queryItems = URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 예시: 특정 화면으로 이동
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if path == &quot;/path/to/content&quot; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 화면으로 이동하는 코드 작성
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;3. 디퍼드 딥링크 처리&lt;/h4&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;- 디퍼드 딥링크는 사용자가 앱을 설치한 후에도 딥링크를 처리할 수 있도록 해야 한다. 이를 위해 서버 측에서 정보를 저장하고 앱이 설치된 후 정보를 가져올 수 있도록 구현한다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size18&quot;&gt;3-1. 서버 측에서 정보 저장&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;bull; 사용자가 특정 링크를 클릭할 때 서버에서 해당 사용자와 관련된 정보를 저장. 예) 사용자의 디바이스 ID, 광고 ID, 쿠키 등&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size18&quot;&gt;3-2. 앱 설치 후 정보 가져오기&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;bull; 앱이 시작될 때 API 요청을 통해 서버에서 사용자의 ID에 매칭되는 딥링크 정보를 가져온다.&lt;/p&gt;
&lt;pre id=&quot;code_1725256576761&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func fetchDeferredDeeplink() {
    // 서버 API 호출을 통해 저장된 딥링크 정보를 가져옴
    let url = URL(string: &quot;https://yourserver.com/get-deferred-deeplink?user_id=example&quot;)!
    let task = URLSession.shared.dataTask(with: url) { data, response, error in
        guard let data = data, error == nil else { return }
        do {
            if let deeplinkInfo = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
               let deeplinkURLString = deeplinkInfo[&quot;deeplink&quot;] as? String,
               let deeplinkURL = URL(string: deeplinkURLString) {
                DispatchQueue.main.async {
                    self.handleDeeplink(deeplinkURL)
                }
            }
        } catch {
            print(&quot;Failed to parse deferred deeplink&quot;)
        }
    }
    task.resume()
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>iOS</category>
      <category>DeepLink</category>
      <category>deferred deeplink</category>
      <category>universal link</category>
      <category>딥링크</category>
      <category>유니버셜링크</category>
      <author>hyunn383</author>
      <guid isPermaLink="true">https://dev-hyunn.tistory.com/5</guid>
      <comments>https://dev-hyunn.tistory.com/5#entry5comment</comments>
      <pubDate>Mon, 2 Sep 2024 14:57:56 +0900</pubDate>
    </item>
    <item>
      <title>Binary Data를 Swift 구조체로 변환하기 (feat. ByteBuffer, swift-nio)</title>
      <link>https://dev-hyunn.tistory.com/4</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;목차&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  Binary Data로 서버와 통신 (feat. 소켓 통신)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  Byte Buffer란?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  Swift에서 ByteBuffer를 사용해서 데이터 읽기 (기본 자료형)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  ByteBuffer를 사용해서 Swift 구조체(Struct)로 변환하기&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;  Binary Data로 서버와 통신 (feat. 소켓 통신)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근에 채팅 기능을 개발하면서, 소켓(Socket) 통신을 구현하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;(소켓 통신에 대해서는 채팅 기능 개발이 끝난 후 포스팅하도록 하겠다.)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 소켓 통신을 구현하면서, 특이점은 서버와 통신할때 주고받는 데이터 형식이 Binary Data였다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론, 소켓 통신도 json 형태로 데이터를 주고받아도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 소켓 통신의 경우 Binary Data로 데이터를 주고받는게 더 적합하다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이유는 여러가지가 있겠지만 두가지만 적겠다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 효율적인 데이터 크기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 우선 json은 문자열이다. json 문자열을 실제로 서버에 전송될때는 바이트로 인코딩 된다. 그렇다면 실제 data만 byte로 보내는것 보다, key값을 포함한 모든 data 문자열을 byte로 인코딩하면 용량이 더 클 수 밖에 없다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 더 빠른 속도와 성능&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Binary data는 컴퓨터가 직접 읽고 쓸 수 있는 원시 형태이므로, 데이터를 처리하는 데 더 빠를 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 그에 반해, JSON 데이터는 문자열로 표현되기 때문에, 전송하기 전에 문자열로 변환하거나, 수신 후 문자열을 파싱해야  한다. 이 과정은 추가적인 CPU와 메모리 자원을 소모하며, 전송 속도를 저하시킬 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;  Byte Buffer란?&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Byte&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;바이트(Byte)는 디지털 정보의 단위 중 하나로, 8비트로 구성된다. 컴퓨터와 정보 통신 분야에서 널리 사용되는 기본적인 데이터 단위로, 문자, 숫자, 이미지, 사운드 등의 다양한 종류의 데이터를 표현하는 데 사용된다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Buffer&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;버퍼(Buffer)는 데이터를 임시로 저장하는 메모리 공간을 의미한다. 버퍼는 주로 서로 다른 속도로 작동하는 시스템 구성 요소 간의 데이터 전송을 조정하고, 데이터를 임시 저장하거나 처리하는 데 사용된다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ByteBuffer&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;ByteBuffer는 Java에서 제공하는 특정 클래스이지만, &quot;바이트 버퍼&quot;라는 개념 자체는 Java에만 국한되지 않고, 다양한 프로그래밍 언어와 시스템에서 사용되는 일반적인 개념이다. 바이트 버퍼는 바이트 단위의 데이터를 임시로 저장하고 처리하는 데 사용되는 메모리 공간을 의미한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Swift에서 ByteBuffer를 사용해서 데이터 읽기 (기본 자료형)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선, ByteBuffer 클래스를 사용하기 위해 Swift-nio 라는 패키지를 설치해줘야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;(cocoapods를 지원 안하니, spm으로 설치해야한다.)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/apple/swift-nio&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/apple/swift-nio&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1722068286219&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - apple/swift-nio: Event-driven network application framework for high performance protocol servers &amp;amp; clients, non-blocki&quot; data-og-description=&quot;Event-driven network application framework for high performance protocol servers &amp;amp; clients, non-blocking. - apple/swift-nio&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/apple/swift-nio&quot; data-og-url=&quot;https://github.com/apple/swift-nio&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/P6iVc/hyWCCvrxNU/cCYH4gw2XcBwHN24OJdQBk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/apple/swift-nio&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/apple/swift-nio&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/P6iVc/hyWCCvrxNU/cCYH4gw2XcBwHN24OJdQBk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - apple/swift-nio: Event-driven network application framework for high performance protocol servers &amp;amp; clients, non-blocki&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Event-driven network application framework for high performance protocol servers &amp;amp; clients, non-blocking. - apple/swift-nio&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;swift-nio는 정말 방대한 라이브러리이다. 정말 많은걸 지원하지만, ByteBuffer를 읽고 쓸 수 있는 기능만 사용하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1722068498091&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import NIO&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1722069166309&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// MARK: - 서버에서 응답 받은 데이터
// WebSocket으로 data를 receive할때 데이터 타입은 Data 타입으로 들어온다.
let data: Data 

// MARK: - Data to byteBuffer
// ByteBuffer는 SwiftNIO 패키지에서 제공해주는 객체이다.
var byteBuffer: ByteBuffer = ByteBuffer(data: data)

// MARK - 바이트 버퍼 읽기
// 바이트 버퍼를 앞에서부터 읽게 된다.
// 바이트 버퍼의 첫 데이터로 Integer가 들어온다고 가정하고, 우선 Integer를 읽어보자.

// MARK: - byteBuffer to INT32 - Mid-Little
let int32 = byteBuffer.readInteger(endianness: .little, as: Int32.self)
// Int로 바꾸기
let int = Int(int32!)

// MARK: - 다음 바이트 버퍼 읽기
// 바이트 버퍼를 또 읽게 되면 앞에 읽은 Integer 다음 부분부터 읽게 된다.
// 다음 데이터는 String이 들어온다고 가정하고, String을 읽어보자.

// MARK: - String을 읽으려면 우선 String의 길이인 Integer부터 읽어야한다.
let strLength = byteBuffer.readInteger(endianness: .little, as: Int32.self)
// MARK: - byteBuffer to Ascii
let str = byteBuffer.readString(length: Int(strLength!), encoding: .ascii) 
// MARK: - 한글도 읽을거면 utf8로 인코딩하기
let str = byteBuffer.readString(length: Int(strLength!), encoding: .utf8)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;  여기서 잠깐! readInteger() 에 endianness와 as 속성은 뭘까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이거.. 중요한거다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜냐하면 이 속성을 어떻게 설정하는지에 따라 다른 데이터가 출력된다...&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Endianness - 바이트 순서&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;엔디안 속성은 메모리 내에서 데이터의 바이트가 어떻게 배열되는지를 정의한다.&lt;br /&gt;1. 빅 엔디안 (Big-endian):&lt;br /&gt;가장 중요한 바이트(가장 높은 바이트)가 메모리의 낮은 주소에 저장된다. 즉, 데이터의 첫 번째 바이트가 가장 먼저 저장된다.&lt;br /&gt;2. 리틀 엔디안 (Little-endian):&lt;br /&gt;가장 중요한 바이트가 메모리의 높은 주소에 저장된다. 즉, 데이터의 마지막 바이트가 가장 먼저 저장된다.&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;As - 형변환&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt; &lt;/b&gt;As 속성은 readInteger() 메서드가 읽은 데이터를 특정 형식으로 변환할 때 사용된다. 데이터가 읽힐 때, 어떻게 해석될지를 결정할 수 있는 옵션이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 응답받은 데이터를 어떤 속성으로 변환해야될지 모른다면, &lt;span style=&quot;color: #9d9d9d;&quot;&gt;(나는 알려주는 사람이 없어서 몰랐다...)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;응답 받은 데이터를 콘솔에서 찍어보고, 찍힌 데이터를 컨버터에서 돌려보고 맞는 속성을 찾는다!!&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;a href=&quot;https://www.scadacore.com/tools/programming-calculators/online-hex-converter/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.scadacore.com/tools/programming-calculators/online-hex-converter/&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1722069829260&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Online Hex Converter - Bytes, Ints, Floats, Significance, Endians - SCADACore&quot; data-og-description=&quot;Convert Hex values into Bytes, Ints, and Floats of different bit significance, Bit Endians, and byte significance for interfacing with unknown field devices&quot; data-og-host=&quot;www.scadacore.com&quot; data-og-source-url=&quot;https://www.scadacore.com/tools/programming-calculators/online-hex-converter/&quot; data-og-url=&quot;https://www.scadacore.com/tools/programming-calculators/online-hex-converter/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/g26RG/hyWCOCEb06/AMp5qCQKcGJQuNKYRH4F8K/img.png?width=220&amp;amp;height=30&amp;amp;face=0_0_220_30&quot;&gt;&lt;a href=&quot;https://www.scadacore.com/tools/programming-calculators/online-hex-converter/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.scadacore.com/tools/programming-calculators/online-hex-converter/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/g26RG/hyWCOCEb06/AMp5qCQKcGJQuNKYRH4F8K/img.png?width=220&amp;amp;height=30&amp;amp;face=0_0_220_30');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Online Hex Converter - Bytes, Ints, Floats, Significance, Endians - SCADACore&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Convert Hex values into Bytes, Ints, and Floats of different bit significance, Bit Endians, and byte significance for interfacing with unknown field devices&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.scadacore.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 사용한 컨버터이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 Hex Converter냐고? Hex String은 Binary Data를 사람이 보기 좋게 변환한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 응답으로 받은 데이터를 콘솔에 찍으면 [01 00 00 00] 이 나왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이건 binary data를 사람이 보기 좋게 변환해놓은 Hex String이었고, 따라서 Hex String Converter 를 사용하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;874&quot; data-origin-height=&quot;1188&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cL6B2Z/btsIOCiFlZT/W2OlkiM9uicJkmdGlHsqSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cL6B2Z/btsIOCiFlZT/W2OlkiM9uicJkmdGlHsqSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cL6B2Z/btsIOCiFlZT/W2OlkiM9uicJkmdGlHsqSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcL6B2Z%2FbtsIOCiFlZT%2FW2OlkiM9uicJkmdGlHsqSK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;874&quot; height=&quot;1188&quot; data-origin-width=&quot;874&quot; data-origin-height=&quot;1188&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 변환했을때 값이 1이여야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1을 찾아보면, Int32 - Little Endian (DCBA) 와 UINT16 - Little Endian (BA) 가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이중에서 나는 Int32 - Little Endian으로 변환했고, 데이터를 잘 읽어올 수 있었다.&lt;/p&gt;
&lt;pre id=&quot;code_1722070342186&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let int32 = byteBuffer.readInteger(endianness: .little, as: Int32.self)&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;ByteBuffer를 사용해서 Swift 구조체(Struct)로 변환하기&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1722070592524&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct BaseResponse&amp;lt;T&amp;gt; {
    var res: Int?
    var code: Int?
    var data: T?
}

struct ChattingRooms {
    var chattingRooms: [ChattingRoom]?
}

struct ChattingRoom {
    var id: String?
    var name: String?
    var groupId: String?
    var channelId: String?
    var imageUrl: String?
    var lastChat: String?
    var lastChatTimeStamp: Int?
    var unreadMessageCount: Int?
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;응답받은 Binary Data를 Swift로 읽기 편하게 위와 같은 구조체(Struct)로 변환해보자!&lt;/p&gt;
&lt;pre id=&quot;code_1722070766785&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let res = byteBuffer.readInteger(endianness: .little, as: Int32.self) 
let code = byteBuffer.readInteger(endianness: .little, as: Int32.self)

// MARK: - data는 object Array로 들어오기 때문에 우선, length부터 구한다.
let objectArrayLength = byteBuffer.readInteger(endianness: .little, as: Int32.self)

var data: [ChattingRoom] = []

// MARK: - 배열 길이만큼 object를 읽는다.
if let objectArrayLength = objectArrayLength {
    for _ in 0..&amp;lt;objectArrayLength {
        if let object = readObject(byteBuffer: byteBuffer) {
            data.append(object)
        }
    }
}

// MARK: - 최종 구조체(Struct)
let response: BaseResponse&amp;lt;ChattingRooms&amp;gt; = BaseResponse(res: Int(res!), code: Int(code!), data: ChattingRooms(chattingRooms: data))

print(response)&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1722071059631&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// MARK: - object를 읽어 ChattingRoom 구조체로 변환
private func readObject(byteBuffer: ByteBuffer) -&amp;gt; ChattingRoom {
    var byteBuffer = byteBuffer

    let idLength = byteBuffer.readInteger(endianness: .little, as: Int32.self)
    let id = byteBuffer.readString(length: Int(idLength!), encoding: .ascii)

    let nameLength = byteBuffer.readInteger(endianness: .little, as: Int32.self)
    let name = byteBuffer.readString(length: Int(nameLength!), encoding: .utf8)

    let groupIdLength = byteBuffer.readInteger(endianness: .little, as: Int32.self)
    let groupId = byteBuffer.readString(length: Int(groupIdLength!), encoding: .ascii)

    let channelIdLength = byteBuffer.readInteger(endianness: .little, as: Int32.self)
    let channelId = byteBuffer.readString(length: Int(channelIdLength!), encoding: .ascii)

    let imageUrlLength = byteBuffer.readInteger(endianness: .little, as: Int32.self)
    let imageUrl = byteBuffer.readString(length: Int(imageUrlLength!), encoding: .ascii)

    let lastChatLength = byteBuffer.readInteger(endianness: .little, as: Int32.self)
    let lastChat = byteBuffer.readString(length: Int(lastChatLength!), encoding: .utf8)

    let lastChatTimeStamp = byteBuffer.readInteger(endianness: .little, as: Int32.self)

    let unreadMessageCount = byteBuffer.readInteger(endianness: .little, as: Int32.self)

    return ChattingRoom(id: id, name: name, groupId: groupId, channelId: channelId, imageUrl: imageUrl, lastChat: lastChat, lastChatTimeStamp: Int(lastChatTimeStamp!), unreadMessageCount: Int(unreadMessageCount!))
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Swift</category>
      <category>binary data to struct</category>
      <category>ByteBuffer</category>
      <category>ios binary data</category>
      <category>SOCKET</category>
      <category>swift bytebuffer</category>
      <category>swift 바이너리 데이터</category>
      <category>swift 바이트 버퍼</category>
      <category>swift 소켓 데이터</category>
      <category>swift 소켓통신</category>
      <category>swift-nio</category>
      <author>hyunn383</author>
      <guid isPermaLink="true">https://dev-hyunn.tistory.com/4</guid>
      <comments>https://dev-hyunn.tistory.com/4#entry4comment</comments>
      <pubDate>Sat, 27 Jul 2024 18:08:53 +0900</pubDate>
    </item>
    <item>
      <title>[iOS] DI(의존성 주입)에 대한 고찰 (feat. Clean Architecture + MVVM)</title>
      <link>https://dev-hyunn.tistory.com/3</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;✨ DI에 대해 글을 쓰게된 계기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Clean Architecture를 공부하면서 항상 어려웠던 개념이 바로 Dependency Injection이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;그래서 오늘은 DI에 대해서 내가 생각하고 고민했던 과정들과 결과를 글로 정리해보려고 한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt; ️ Clean Architecture + MVVM&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;이전 포스팅에서도 언급했듯이, 클린아키텍쳐를 프로젝트에 적용해본 적이 있다. &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;정확히 말하자면 Clean Architecture에 MVVM패턴이 적용된 아키텍쳐이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-07-17 오후 9.33.47.png&quot; data-origin-width=&quot;287&quot; data-origin-height=&quot;243&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/x3l2f/btsIDNXzjSY/EnFEQufKcdkC9hagOuK6m1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/x3l2f/btsIDNXzjSY/EnFEQufKcdkC9hagOuK6m1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/x3l2f/btsIDNXzjSY/EnFEQufKcdkC9hagOuK6m1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fx3l2f%2FbtsIDNXzjSY%2FEnFEQufKcdkC9hagOuK6m1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;287&quot; height=&quot;243&quot; data-filename=&quot;스크린샷 2024-07-17 오후 9.33.47.png&quot; data-origin-width=&quot;287&quot; data-origin-height=&quot;243&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;우선 클린아키텍쳐 컨셉에 맞게 Presentation Layer, Domain Layer, Data Layer로 계층을 나누었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-07-17 오후 9.34.38.png&quot; data-origin-width=&quot;318&quot; data-origin-height=&quot;157&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WyKq8/btsIEdBx1qM/cOlNlDWrBT7rP0vykdqIe0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WyKq8/btsIEdBx1qM/cOlNlDWrBT7rP0vykdqIe0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WyKq8/btsIEdBx1qM/cOlNlDWrBT7rP0vykdqIe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWyKq8%2FbtsIEdBx1qM%2FcOlNlDWrBT7rP0vykdqIe0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;318&quot; height=&quot;157&quot; data-filename=&quot;스크린샷 2024-07-17 오후 9.34.38.png&quot; data-origin-width=&quot;318&quot; data-origin-height=&quot;157&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;Presentation Layer는 UI를 담당하는 계층이다. MVVM패턴을 적용하여 ViewModel을 두었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-07-17 오후 9.35.56.png&quot; data-origin-width=&quot;313&quot; data-origin-height=&quot;244&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUiZJt/btsIDVA8Olx/0ZRxWqNnZKDRM3A3K25dBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUiZJt/btsIDVA8Olx/0ZRxWqNnZKDRM3A3K25dBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUiZJt/btsIDVA8Olx/0ZRxWqNnZKDRM3A3K25dBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUiZJt%2FbtsIDVA8Olx%2F0ZRxWqNnZKDRM3A3K25dBk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;313&quot; height=&quot;244&quot; data-filename=&quot;스크린샷 2024-07-17 오후 9.35.56.png&quot; data-origin-width=&quot;313&quot; data-origin-height=&quot;244&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;Domain Layer는 앱의 비즈니스 로직을 담당한다. 비즈니스 로직이 작성된 UseCase와 앱에서 사용할 데이터 모델인 Entity를 두었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-07-17 오후 10.43.49.png&quot; data-origin-width=&quot;314&quot; data-origin-height=&quot;422&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lGgmf/btsIEsSMBsR/KqhYZ3nTpTa1B37s9Kqqp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lGgmf/btsIEsSMBsR/KqhYZ3nTpTa1B37s9Kqqp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lGgmf/btsIEsSMBsR/KqhYZ3nTpTa1B37s9Kqqp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlGgmf%2FbtsIEsSMBsR%2FKqhYZ3nTpTa1B37s9Kqqp0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;314&quot; height=&quot;422&quot; data-filename=&quot;스크린샷 2024-07-17 오후 10.43.49.png&quot; data-origin-width=&quot;314&quot; data-origin-height=&quot;422&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;Data Layer는 서버와 통신하여 외부에서 데이터를 가져오는 계층이다. 서버와 통신하는 로직이 작성된 Repository와 서버에서 응답해주는 데이터 모델인 DTO를 두었다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;  의존성 규칙을 어기다&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 각각의 레이어들을 다 구현하고 나니, 의문이 들었다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;클린아키텍쳐의 컨셉에는 레이어를 분리하는 것 뿐만 아니라 &quot;의존성 규칙&quot; 이라는 것이 있다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;(유명한 깃허브 레포지토리이다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;해당 레포에 자세히 설명되있다. 참고하면 좋다.)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/kudoleh/iOS-Clean-Architecture-MVVM&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/kudoleh/iOS-Clean-Architecture-MVVM&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1721224602246&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - kudoleh/iOS-Clean-Architecture-MVVM: Template iOS app using Clean Architecture and MVVM. Includes DIContainer, FlowCoor&quot; data-og-description=&quot;Template iOS app using Clean Architecture and MVVM. Includes DIContainer, FlowCoordinator, DTO, Response Caching and one of the views in SwiftUI - GitHub - kudoleh/iOS-Clean-Architecture-MVVM: Tem...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/kudoleh/iOS-Clean-Architecture-MVVM&quot; data-og-url=&quot;https://github.com/kudoleh/iOS-Clean-Architecture-MVVM&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bxdWbL/hyWCJfdRiC/CkKyTQkUbmjjTWt1bfClK0/img.png?width=1200&amp;amp;height=600&amp;amp;face=995_121_1038_167&quot;&gt;&lt;a href=&quot;https://github.com/kudoleh/iOS-Clean-Architecture-MVVM&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/kudoleh/iOS-Clean-Architecture-MVVM&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bxdWbL/hyWCJfdRiC/CkKyTQkUbmjjTWt1bfClK0/img.png?width=1200&amp;amp;height=600&amp;amp;face=995_121_1038_167');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - kudoleh/iOS-Clean-Architecture-MVVM: Template iOS app using Clean Architecture and MVVM. Includes DIContainer, FlowCoor&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Template iOS app using Clean Architecture and MVVM. Includes DIContainer, FlowCoordinator, DTO, Response Caching and one of the views in SwiftUI - GitHub - kudoleh/iOS-Clean-Architecture-MVVM: Tem...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Clean Architecture의 의존성 규칙에 따르자면, Domain Layer는 다른 레이어를 의존하면 안된다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Presentation Layer -&amp;gt; Domain Layer &amp;lt;- Data Layer&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의존성 방향은 이렇게 흘러야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 의존성 규칙 따위는 신경안쓰고 각각의 레이어들을 구현하다보니, Domain Layer가 Data Layer를 참조하고 있었다!!&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;앱의 비즈니스 로직을 구현하려면 어쨋든 서버와의 통신으로 받아온 데이터들을 가지고 로직을 짜야하는데,&lt;br /&gt;그러면 당연히 Domain Layer의 UseCase에서 Data Layer의 Repository의 함수를 호출할 수 밖에 없다.&lt;br /&gt;DI가 뭔지 몰랐던 그 당시의 나는... UseCase에서 Repository객체를 생성해서 객체 내부의 함수를 호출했다.&lt;br /&gt;Domain Layer가 Data Layer를 너무나도 의존하고 있었다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이해하기 쉽게 코드로 예시를 들자면 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1721225685337&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class UserUseCase {
    func getMyProfile(accessToken: String) -&amp;gt; Observable&amp;lt;Execution&amp;lt;BaseResponse&amp;lt;MyProfileData&amp;gt;&amp;gt;&amp;gt; {
        let observable = Observable&amp;lt;Execution&amp;lt;BaseResponse&amp;lt;MyProfileData&amp;gt;&amp;gt;&amp;gt;.create { observer -&amp;gt; Disposable in
            // MARK: - UseCase 내부에서 Repository객체를 생성하여 참조하게 됨
            UserRepository().getMyProfile(accessToken: accessToken).subscribe (onNext: { result in
                switch result {
                case let .success(result):
                    observer.onNext(.success(result))
                    observer.onCompleted()
                case let .failure(error):
                    observer.onNext(.error(error.rawValue))
                    observer.onCompleted()
                }
            })
            .disposed(by: self.disposeBag)
            return Disposables.create()
        }
        return observable
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;  의존성 주입 Dependency Injection&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 의존하면 안되는데 객체를 참조해야 된다면, 객체 내부에서 참조해야될 객체를 생성하는 것이 아니라, 객체 외부에서 참조해야될 객체를 생성하여 해당 객체로 주입해주는 방법이 있다. 이게 바로 &quot;의존성 주입&quot; 이라는 개념이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UseCase와 Repository의 관계에 DI를 적용해보자면,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Repository class를 구현할때, 레포지토리의 인터페이스를 정의하는 protocol을 만들고 해당 프로토콜을 준수하도록 구현한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. UseCase에서는 Repository의 protocol을 선언하고, &lt;span style=&quot;background-color: #ffffff; color: #5c5c5c; text-align: left;&quot;&gt;Initializer 즉 생성자를 통해 외부에서 생성한 객체를 주입시킨다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1721227720021&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;protocol UserRepositoryProtocol {
    func getMyProfile(accessToken: String) -&amp;gt; Observable&amp;lt;Result&amp;lt;BaseResponse&amp;lt;MyProfileData&amp;gt;, Error&amp;gt;&amp;gt;
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1721227037973&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct UserRepository: UserRepositoryProtocol {
    func getMyProfile(accessToken: String) -&amp;gt; Observable&amp;lt;Result&amp;lt;BaseResponse&amp;lt;MyProfileData&amp;gt;, Error&amp;gt;&amp;gt; {
        return Observable.create { observer -&amp;gt; Disposable in
            AF.request(UserAPI.getMyProfile(accessToken: accessToken))
                .responseDecodable(of: BaseResponse&amp;lt;MyProfileData&amp;gt;.self) { response in
                    switch response.result {
                    case .success(let data):
                        observer.onNext(.success(data))
                    case .failure(let error):
                        observer.onNext(.failure(error))
                    }
                }
            
            return Disposables.create()
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1721227473656&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class UserUseCase {
    // MARK: - Repository protocol 선언
    private let userRepositoryProtocol: UserRepositoryProtocol

    // MARK: - 생성자를 통해 의존성 주입
    init(userRepositoryProtocol: UserRepositoryProtocol) {
        self.userRepositoryProtocol = userRepositoryProtocol
    }

    func getMyProfile(accessToken: String) -&amp;gt; Observable&amp;lt;Execution&amp;lt;BaseResponse&amp;lt;MyProfileData&amp;gt;&amp;gt;&amp;gt; {
        let observable = Observable&amp;lt;Execution&amp;lt;BaseResponse&amp;lt;MyProfileData&amp;gt;&amp;gt;&amp;gt;.create { observer -&amp;gt; Disposable in
            // MARK: - Repository 객체가 아니라 protocol에 접근
            self.userRepositoryProtocol.getMyProfile(accessToken: accessToken).subscribe (onNext: { result in
                switch result {
                case let .success(result):
                    observer.onNext(.success(result))
                    observer.onCompleted()
                case let .failure(error):
                    observer.onNext(.error(error))
                    observer.onCompleted()
                }
            })
            .disposed(by: self.disposeBag)
            return Disposables.create()
        }
        return observable
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ViewModel과 UseCase와의 관계도 동일하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Clean Architecture 관점에서만 봤을때는, Presentation Layer는 Domain Layer를 의존하고 반대로 Domain Layer는 Presentation Layer를 의존하면 안되니까 Presentation Layer에서 UseCase를 직접 생성하여 호출해도 된다고 생각할 수 있다. &lt;span style=&quot;color: #9d9d9d;&quot;&gt;(사실 내가 그렇게 생각했다... ㅎㅎ)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, MVVM 패턴이 적용된 Clean Architecture라서 ViewModel과 UseCase는 서로 의존하면 안된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, ViewModel에서도 UseCase를 protocol로 선언하고 initializer를 통해 주입 받아서 사용해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; background-color: #ffffff; color: #9d9d9d; text-align: left;&quot;&gt;혹시나 틀린 내용이나 저와 다른 생각이 있으시다면 댓글로 알려주세요~ :)&lt;/span&gt;&lt;/p&gt;</description>
      <category>iOS</category>
      <category>clean architecture</category>
      <category>Dependency Injection</category>
      <category>iOS 아키텍쳐</category>
      <category>mvvm</category>
      <category>RxSwift</category>
      <category>SWIFT</category>
      <category>의존성 주입</category>
      <category>클린아키텍쳐</category>
      <author>hyunn383</author>
      <guid isPermaLink="true">https://dev-hyunn.tistory.com/3</guid>
      <comments>https://dev-hyunn.tistory.com/3#entry3comment</comments>
      <pubDate>Wed, 17 Jul 2024 23:59:28 +0900</pubDate>
    </item>
    <item>
      <title>&amp;quot;피피&amp;quot; 사이드프로젝트 회고</title>
      <link>https://dev-hyunn.tistory.com/2</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;24년 4월 1일.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사이드프로젝트를 시작한 날짜이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기획과 디자인은 내가 진행했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 기획에 대해 말하기 전에 &quot;피피&quot; 에 대해 먼저 설명하자면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;피피라는 이름은 Private과 Public의 앞글자를 따와서 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱에 대해 설명하자면, &lt;i&gt;&lt;b&gt;일기와 커뮤니티 게시글 &lt;/b&gt;&lt;/i&gt;을 작성할 수 있는 앱이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Private - 개인적인 일기를 작성할 수 있다.&lt;br /&gt;Public - 공개적인 게시글을 작성하여 커뮤니티에 업로드 할 수 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;기획&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀원을 구하기 위해서는 기능명세는 꼭 필요할 것 같아 기능명세를 작성했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_240330 - 기획 1.jpg&quot; data-origin-width=&quot;2457&quot; data-origin-height=&quot;1732&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/u7lCY/btsIswwfqlW/5obM2vQgbvIdAd072szi70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/u7lCY/btsIswwfqlW/5obM2vQgbvIdAd072szi70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/u7lCY/btsIswwfqlW/5obM2vQgbvIdAd072szi70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fu7lCY%2FbtsIswwfqlW%2F5obM2vQgbvIdAd072szi70%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2457&quot; height=&quot;1732&quot; data-filename=&quot;edited_240330 - 기획 1.jpg&quot; data-origin-width=&quot;2457&quot; data-origin-height=&quot;1732&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;디자인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디자인은 피그마에 진행했는데, 중요하지 않으니 넘어가겠다. &lt;span style=&quot;color: #9d9d9d;&quot;&gt;(피그마 툴을 사용할 줄 아는거 뿐이다.. ㅎㅎㅎ)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;개발&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;인원&lt;/u&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;iOS - 2명&lt;/li&gt;
&lt;li&gt;안드로이드 - 2명&lt;/li&gt;
&lt;li&gt;백엔드 - 2명&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;소통 및 기록&lt;/u&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;슬랙&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;협업&lt;/u&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;깃&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;u&gt;나에겐 새로운 도전이었던 SwiftUI&lt;/u&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SwiftUI를 끄적인적은 있다. 무신사 앱의 메인 화면의 UI를 클론코딩해본게 다이긴 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 프로젝트 하나를 완성해본 적은 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런 의미에서 SwiftUI 프로젝트는 내게 새로운 도전이였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&amp;bull;&lt;span&gt; 뷰를 그릴때 정말 편한 Modifier&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Modifer 덕분에 정말 편하고 빠르게 뷰를 그릴 수 있었다.&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;UIKit에서는 뷰의 프로퍼티에 접근하는 반면, SwiftUI에서는 Modifier가 View를 반환하기 때문에 체이닝으로 연결해서 코드를 작성할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;뷰를 변수에 담고, 변수를 여러번 쓸 필요없이 체이닝으로 바로 연결해서 쓸 수 있는 점이 매우 편했다.&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;u&gt;RxSwift와 비슷한 Combine&lt;/u&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 프로젝트에서는 비동기 통신을 도와주는 Combine 프레임워크를 사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RxSwift와 Combine은 둘 다 비동기 이벤트 스트림을 다루고 옵저버블/퍼플리셔 패턴을 사용한다는 공통적인 패러다임을 따르기 때문에, RxSwift 프로젝트를 진행해봤던터라 Combine도 금방 할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 문법과 네이밍이 비슷하나 차이가 있었기 때문에 구글링하면서 하느라 시간이 좀 걸리긴했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&amp;bull;&lt;span&gt;&lt;span&gt; 프로퍼티 래퍼&lt;/span&gt; @State @Published ...&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;UIKit과 RxSwift를 사용했을때는 쓰지 않았던 프로퍼티 래퍼들을 많이 활용하게 되었다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote style=&quot;color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;프로퍼티 래퍼(Property Wrapper)는 Swift 5.1에서 도입된 기능으로, 프로퍼티의 저장 방식을 관리하는 코드를 재사용할 수 있게 해주는 특별한 속성이다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;Property Wrapper 특징&lt;/u&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;구문: '@' 기호로 시작하며, 프로퍼티 선언 앞에 위치한다.&lt;/li&gt;
&lt;li&gt;캡슐화: 프로퍼티의 접근과 설정 로직을 캡슐화한다.&lt;/li&gt;
&lt;li&gt;재사용성: 동일한 프로퍼티 관리 로직을 여러 프로퍼티에 쉽게 적용할 수 있다.&lt;/li&gt;
&lt;li&gt;코드 간소화: 반복적인 보일러플레이트 코드를 줄여준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;u&gt;다른 iOS 개발자와의 협업&lt;/u&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그동안은 iOS파트는 항상 혼자 개발했었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 이번에 사이드프로젝트를 진행하면서 다른 iOS개발자와 함께 작업할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Git에서 각자 피처별로 브렌치를 따서 작업했고, 완료된 작업은 Pull Request를 보내 상대방이 코드를 확인하고 merge해주는 방식으로 협업하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;bull; 좋았던 점&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 사람의 코드를 보면서 생각의 폭을 넓힐 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 서로 해결방안에 대해 의논하면서 더 나은 해결방법으로 코드를 수정할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭐 이런저런거 있지만, 일단 다른 사람의 생각을 듣고 볼 수 있다는게 가장 좋았던 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그동안도 나는 맨날 혼자 생각하고 혼자 개발한다면 우물 안 개구리가 되지 않을까 하고 생각했었기 때문에 이번 사이드프로젝트에서의 협업 경험이 나는 매우 좋았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;배포 및 출시&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;24년 7월 9일.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱스토어에 출시되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&amp;bull;&lt;span&gt;&amp;nbsp;세번의 심사 거절&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;유저 차단을 구현해야된다는 이유와 EULA(&lt;span&gt;최종 사용자 라이센스 계약) 약관이 추가되어야하는 등의 이유로 심사가 거절됬었다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;유저가 직접 게시물을 생성하는 거라 민감한 부분이라서 충족해야될 심사 기준이 많은 것 같았다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;그래도 끝까지 수정하여 심사를 통과하고 앱스토어에 출시할 수 있게 되었다!&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;끝으로 &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;피피 많관부~&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Diary</category>
      <category>IOS앱개발</category>
      <category>사이드프로젝트</category>
      <category>앱출시</category>
      <category>포트폴리오</category>
      <author>hyunn383</author>
      <guid isPermaLink="true">https://dev-hyunn.tistory.com/2</guid>
      <comments>https://dev-hyunn.tistory.com/2#entry2comment</comments>
      <pubDate>Wed, 10 Jul 2024 13:21:33 +0900</pubDate>
    </item>
  </channel>
</rss>