<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>애플사이다의 iOS 개발 일지</title>
    <link>https://applecider2020.tistory.com/</link>
    <description>#지속가능한성장 #iOS개발자</description>
    <language>ko</language>
    <pubDate>Wed, 15 Apr 2026 13:48:42 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Applecider</managingEditor>
    <image>
      <title>애플사이다의 iOS 개발 일지</title>
      <url>https://tistory1.daumcdn.net/tistory/4942276/attach/bed97b1d5ec3472f8f9540a5cdd07132</url>
      <link>https://applecider2020.tistory.com</link>
    </image>
    <item>
      <title>[iOS] Bottom Sheet 버그 - viewDidLoad에서 layout을 설정하면 안되는 이유</title>
      <link>https://applecider2020.tistory.com/87</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;서비스에서 진행 중인 굵직한 이벤트가 있다면&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;앱을 실행하고 Home 화면에 진입했을 때, 애니메이션과 함께&amp;nbsp;&lt;b&gt;Bottom Sheet (이하 바텀시트)&lt;/b&gt;를 띄워준다.&amp;nbsp;&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;바텀시트를 노출하는 앱은 매우 흔하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;dim 영역이나 x 버튼을 탭하면 Sheet가 내려가도록 구현되어 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.2857%;&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;29CM&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%;&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;무신사&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%;&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;ABLY&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%;&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;오늘의집&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%;&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Shein&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%;&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;쿠팡&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.2857%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;2532&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxgK4R/btsGCwXMnms/MWOTRGyOZM4N16lqCkkZDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxgK4R/btsGCwXMnms/MWOTRGyOZM4N16lqCkkZDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxgK4R/btsGCwXMnms/MWOTRGyOZM4N16lqCkkZDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcxgK4R%2FbtsGCwXMnms%2FMWOTRGyOZM4N16lqCkkZDk%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;1170&quot; height=&quot;2532&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;2532&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;2532&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nbsve/btsGEkbg1NI/L5jK02ec8F8wdb66cUoav0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nbsve/btsGEkbg1NI/L5jK02ec8F8wdb66cUoav0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nbsve/btsGEkbg1NI/L5jK02ec8F8wdb66cUoav0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fnbsve%2FbtsGEkbg1NI%2FL5jK02ec8F8wdb66cUoav0%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;1170&quot; height=&quot;2532&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;2532&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;2532&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dFLdAw/btsGCo6MfsE/B2fg7LslL6pH5n5kF7l4Kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dFLdAw/btsGCo6MfsE/B2fg7LslL6pH5n5kF7l4Kk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dFLdAw/btsGCo6MfsE/B2fg7LslL6pH5n5kF7l4Kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdFLdAw%2FbtsGCo6MfsE%2FB2fg7LslL6pH5n5kF7l4Kk%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;1170&quot; height=&quot;2532&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;2532&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;2532&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uLAbH/btsGCWPuaNI/nlz9fsUQ7cKjqzDLxkdAs0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uLAbH/btsGCWPuaNI/nlz9fsUQ7cKjqzDLxkdAs0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uLAbH/btsGCWPuaNI/nlz9fsUQ7cKjqzDLxkdAs0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuLAbH%2FbtsGCWPuaNI%2Fnlz9fsUQ7cKjqzDLxkdAs0%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;1170&quot; height=&quot;2532&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;2532&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;2532&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XfiQq/btsGCYzMHic/wdrs1tnJDvfpjblJftEUg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XfiQq/btsGCYzMHic/wdrs1tnJDvfpjblJftEUg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XfiQq/btsGCYzMHic/wdrs1tnJDvfpjblJftEUg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXfiQq%2FbtsGCYzMHic%2Fwdrs1tnJDvfpjblJftEUg1%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;1170&quot; height=&quot;2532&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;2532&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;2532&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2Ny7o/btsGCzAb5dN/JjE5KbtzOrneMlXdHqwbJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2Ny7o/btsGCzAb5dN/JjE5KbtzOrneMlXdHqwbJK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2Ny7o/btsGCzAb5dN/JjE5KbtzOrneMlXdHqwbJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2Ny7o%2FbtsGCzAb5dN%2FJjE5KbtzOrneMlXdHqwbJK%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;1170&quot; height=&quot;2532&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;2532&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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; text-align: start; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;개인적으로 귀찮아서 습관적으로 무조건 닫기 버튼을 누르긴 하지만... ㅋㅋㅋ (솔직히 사용성 너무 안 좋다고 생각함)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그래서인지 아마존, Etsy, Temu 같은 글로벌 앱에서는 이러한 시트를 띄우지 않고 있었다.&lt;/span&gt;&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 data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아무튼 이번에는 이러한 바텀시트를 구현하는 과정에서 겪었던 버그를 파헤쳐보려 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;제목에서 벌써 스포하고 있듯이 &lt;b&gt;ViewDidLoad 시점에 Bottom Sheet의 layout을 잡아줬던 게&lt;/b&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;먼저, 버그는 이랬다&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;viewController 위에 바텀시트를 subview로 올리고&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;바텀시트의 &lt;b&gt;imageView의 width/height&lt;/b&gt;를 &lt;b&gt;viewController&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;b&gt; width&lt;/b&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 style=&quot;color: #333333; text-align: start; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그리고 아래에서 위로 올라오는 애니메이션 효과를 주기 위해&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;초기에는 &lt;b&gt;imageView의 top&lt;/b&gt;을 바텀시트의 bottom으로 맞춰서 바닥에 숨겨두고,&lt;br /&gt;바텀시트를 띄울 때는 imageView의 top을 바텀시트의 height 만큼 올려줬다.&amp;nbsp;&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1) HomeBottomSheetView 내부적으로 layout을 잡아주는 코드&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1713092614384&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// layout 초기 설정
addSubview(imageView)
imageView.snp.makeConstraints { make in
    make.leading.trailing.equalToSuperview()
    make.top.equalTo(snp.bottom)
    make.height.equalTo(snp.width) // ✅ 바텀시트 height == width
}

// 바텀시트를 show/close하는 로직
func show() {
    layoutIfNeeded()

    UIView.animate(withDuration: 0.25) {
        self.dimView.alpha = 1
        // ✅ imageView top == 바텀시트 height 만큼 올려주기
        self.setImageViewOffsetY(to: -self.imageView.bounds.size.height)
    }
}

func close() {
    UIView.animate(withDuration: 0.25) {
        self.dimView.alpha = 0
        // ✅ imageView top == 바텀시트 bottom으로 내려서 숨기기 
        self.setImageViewOffsetY(to: 0)
    } completion: { [weak self] _ in
        self?.removeFromSuperview()
    }
}

private func setImageViewOffsetY(to offsetY: CGFloat) {
    imageView.snp.updateConstraints { make in
        make.top.equalTo(snp.bottom).offset(offsetY)
    }
    layoutIfNeeded()
}&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2) ViewController에서 바텀시트를 올려주는 코드&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1713092847881&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;override func viewDidLoad() {
    super.viewDidLoad()

    showBottomSheet()
}

private func showBottomSheet() {
    let bottomSheetView = HomeBottomSheetView()

    // ✅ dim 영역이 있으므로 전체화면 크기로 잡아줌 
    view.addSubview(bottomSheetView)
    bottomSheetView.snp.makeConstraints { make in
        make.edges.equalToSuperview()
    }
    bottomSheetView.show()
}&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그런데..! 아래와 같이 &lt;b&gt;기기마다 바텀시트의 bottom inset이 다르게 나타났다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(기기는 왼쪽부터 iPhone 15 / 13 mini / SE)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mSz2I/btsGAPdrGOb/nGK3DbDXQW1lgDMv0o0qa0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mSz2I/btsGAPdrGOb/nGK3DbDXQW1lgDMv0o0qa0/img.png&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;2556&quot; data-is-animation=&quot;false&quot; style=&quot;width: 30.3389%; margin-right: 10px;&quot; data-widthpercent=&quot;31.06&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mSz2I/btsGAPdrGOb/nGK3DbDXQW1lgDMv0o0qa0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmSz2I%2FbtsGAPdrGOb%2FnGK3DbDXQW1lgDMv0o0qa0%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;1179&quot; height=&quot;2556&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t5u16/btsGBY8jIO4/zB2ADdG6MCVqIBbN0RDje1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t5u16/btsGBY8jIO4/zB2ADdG6MCVqIBbN0RDje1/img.png&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;2340&quot; data-is-animation=&quot;false&quot; style=&quot;width: 30.3567%; margin-right: 10px;&quot; data-widthpercent=&quot;31.08&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t5u16/btsGBY8jIO4/zB2ADdG6MCVqIBbN0RDje1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft5u16%2FbtsGBY8jIO4%2FzB2ADdG6MCVqIBbN0RDje1%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;1080&quot; height=&quot;2340&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bx1QDh/btsGDCDfcYL/LjwIsFzgYuvI94kVv71ncK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bx1QDh/btsGDCDfcYL/LjwIsFzgYuvI94kVv71ncK/img.png&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;1334&quot; data-is-animation=&quot;false&quot; style=&quot;width: 36.9788%;&quot; data-widthpercent=&quot;37.86&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bx1QDh/btsGDCDfcYL/LjwIsFzgYuvI94kVv71ncK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbx1QDh%2FbtsGDCDfcYL%2FLjwIsFzgYuvI94kVv71ncK%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;750&quot; height=&quot;1334&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;삼체 너무 재밌음.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;버그 원인은 뭘까?&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;결론부터 말하자면 원인은&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1) ViewController와 연결된 &lt;b&gt;storyboard&lt;/b&gt;가 존재했고&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2) 바텀시트의 layout을 &lt;b&gt;viewDidLoad 시점에 설정했기 때문&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;conflict을 피하기 위해 storyboard 없이 layout을 그리는 데에 익숙해지다보니&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이처럼 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;storyboard&lt;/b&gt;를 사용하고 있는 ViewController의 특성&lt;/span&gt;을 고려하지 못했다...!&amp;nbsp;&lt;/span&gt;&lt;/p&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  ViewController의 lifecycle method로 실험해 보자&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;view를 띄울 때 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;viewDidLoad, viewWillAppear, viewDidLayoutSubviews가 차례로 호출되는데&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;각 시점에서 &lt;b&gt;view의 width&lt;/b&gt;를 찍어봤다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1713093278738&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;override func viewDidLoad() {
    super.viewDidLoad()
    print(&quot;viewDidLoad ...&quot;, view.frame.size.width)
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    print(&quot;viewWillAppear ...&quot;, view.frame.size.width)
}

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    print(&quot;viewDidLayoutSubviews ...&quot;, view.frame.size.width)
}&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그랬더니...!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;실험했던 iPhone SE의 Screen width는 &lt;b&gt;375&lt;/b&gt;인데&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아래처럼 &lt;b&gt;viewDidLoad에서만 393&lt;/b&gt;으로 잘못된 값이 찍히는 것을 확인할 수 있었다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;466&quot; data-origin-height=&quot;98&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbf0Vb/btsGAYH2mP9/sSJ4zGOPXLM7qUa3KKNdTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbf0Vb/btsGAYH2mP9/sSJ4zGOPXLM7qUa3KKNdTk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbf0Vb/btsGAYH2mP9/sSJ4zGOPXLM7qUa3KKNdTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbbf0Vb%2FbtsGAYH2mP9%2FsSJ4zGOPXLM7qUa3KKNdTk%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;400&quot; height=&quot;84&quot; data-origin-width=&quot;466&quot; data-origin-height=&quot;98&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;그럼 393은 어디에서 온 숫자일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/44500340/view-frame-width-is-correct-in-viewdidload-with-storyboard-why&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;스오플&lt;/a&gt;을 뒤져보니&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;... 놀랍게도...! &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;storyboard&lt;/span&gt;&lt;/b&gt;에서 설정한 &lt;b&gt;기기의 Screen width&lt;/b&gt;였다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2030&quot; data-origin-height=&quot;1196&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wac9j/btsGAXvBDJz/hPWCdPeuo2Z130eP33oEh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wac9j/btsGAXvBDJz/hPWCdPeuo2Z130eP33oEh1/img.png&quot; data-alt=&quot;세상의 모든 Storyboard 없애고 싶다..&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wac9j/btsGAXvBDJz/hPWCdPeuo2Z130eP33oEh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fwac9j%2FbtsGAXvBDJz%2FhPWCdPeuo2Z130eP33oEh1%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;2030&quot; height=&quot;1196&quot; data-origin-width=&quot;2030&quot; data-origin-height=&quot;1196&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;세상의 모든 Storyboard 없애고 싶다..&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;ViewDidLoad에서 Layout을 잡으면 안 되는 이유는?&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그렇다면 왜 viewDidLoad에서는 잘못된 너비가 찍히는 걸까?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이 시점에는 &lt;b&gt;view frame이 정확히 계산되지 않은 상태&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;공식 문서에서 확인해보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/uiviewcontroller/1621495-viewdidload&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;viewDidLoad()&lt;/a&gt; 를 다시 보게 될 날이 올 줄이야..&lt;/span&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-origin-width=&quot;1848&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qCdl0/btsGEhFDBOE/YFCgTdCkXBXByIUbX2kOg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qCdl0/btsGEhFDBOE/YFCgTdCkXBXByIUbX2kOg0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qCdl0/btsGEhFDBOE/YFCgTdCkXBXByIUbX2kOg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqCdl0%2FbtsGEhFDBOE%2FYFCgTdCkXBXByIUbX2kOg0%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;1848&quot; height=&quot;240&quot; data-origin-width=&quot;1848&quot; data-origin-height=&quot;240&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;공식문서를 읽어보면... &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&quot;viewDidLoad가 호출되는 시점은 view hierarchy를 &lt;b&gt;메모리에 로드&lt;/b&gt;한 직후&quot;라고 말한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(view를 view hierarchy에 추가했다는 뜻이 아니다.)&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;공식문서가 좀 불친절하긴 하지만&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;마지막 문장 &quot;viewDidLoad를 override하여 nib 파일에서 로드한 &lt;b&gt;view에 대한 추가적인 초기화&lt;/b&gt;를 할 수 있다&quot;에서 볼 수 있듯이&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;view를 초기화까지만 하고, &lt;b&gt;layout은 하면 안 되는 걸 유추&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그럼 찾아본 김에&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/uiviewcontroller/1621510-viewwillappear&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;viewWillAppear(_:)&lt;/a&gt;도 살펴보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;&quot;&lt;/span&gt;&lt;b&gt;view를 표시&lt;/b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;하는 것과 관련된 작업&quot;을 할 때 viewWillAppear를 override하라고 명시되어 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1870&quot; data-origin-height=&quot;348&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l04fO/btsGC0j3mxm/mYDu8Qe44EZLcDZyRWjXf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l04fO/btsGC0j3mxm/mYDu8Qe44EZLcDZyRWjXf0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l04fO/btsGC0j3mxm/mYDu8Qe44EZLcDZyRWjXf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl04fO%2FbtsGC0j3mxm%2FmYDu8Qe44EZLcDZyRWjXf0%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;1870&quot; height=&quot;348&quot; data-origin-width=&quot;1870&quot; data-origin-height=&quot;348&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;iOS13+부터 쓸 수 있는&amp;nbsp;&lt;a style=&quot;color: #0070d1; text-align: left;&quot; href=&quot;https://developer.apple.com/documentation/uikit/uiviewcontroller/4195485-viewisappearing&quot;&gt;viewIsAppearing(_:)&lt;/a&gt;&amp;nbsp;도 보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;viewWillAppear가 호출된 이후&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;view가 실제로 hierarchy에 추가된 뒤,&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&amp;nbsp;viewIsAppearing이 호출되는 것을 알 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(view가 hierarchy에 추가됐다는 것은 parentView or window에 올라갔음을 의미한다.)&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;왠지 &lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;hierarchy에 추가된 이후 시점인 viewIsAppearing에서&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;544&quot; data-origin-height=&quot;1058&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xpJb6/btsGBtHI8Yl/rfCDbdwKtjji32WwVZkyGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xpJb6/btsGBtHI8Yl/rfCDbdwKtjji32WwVZkyGK/img.png&quot; data-alt=&quot;참고 - 호출 순서&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xpJb6/btsGBtHI8Yl/rfCDbdwKtjji32WwVZkyGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxpJb6%2FbtsGBtHI8Yl%2FrfCDbdwKtjji32WwVZkyGK%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;300&quot; height=&quot;583&quot; data-origin-width=&quot;544&quot; data-origin-height=&quot;1058&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;참고 - 호출 순서&lt;/figcaption&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아래에 viewWillAppear에 대한 자세한 내용이 들어있었다.&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;구체적인 메커니즘은 다른 문서를 뒤져봐야 더 정확히 알겠지만..&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;일단 공식문서를 바탕으로 각 lifecycle 메서드에서 바텀시트가 어떻게 나타나는지 테스트해봤다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1794&quot; data-origin-height=&quot;250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kQbgA/btsGBY1Bq50/SknXp0JmK8oPsBk4CqynYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kQbgA/btsGBY1Bq50/SknXp0JmK8oPsBk4CqynYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kQbgA/btsGBY1Bq50/SknXp0JmK8oPsBk4CqynYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkQbgA%2FbtsGBY1Bq50%2FSknXp0JmK8oPsBk4CqynYK%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;1794&quot; height=&quot;250&quot; data-origin-width=&quot;1794&quot; data-origin-height=&quot;250&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;&lt;span&gt;위 내용을 보면&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;viewWillAppear 시점에 설정한 애니메이션은&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;viewController 전환 애니메이션&lt;/b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;과 &lt;b&gt;동시에&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예를 들어, &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;홈 바텀시트가 올라오는 애니메이션을 viewWillAppear 시점에 설정했다면&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이건 &lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;NavigationController에 의해 push 된 homeViewController가 &lt;b&gt;오른쪽에서 밀려 들어오는 애니메이션&lt;/b&gt;과&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;동시에 보여진다는 거다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아래처럼 말이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;slow.gif&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;889&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxiQDl/btsGBgazAhg/S1BEUEr6M4Pn0mKSfbXTT1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxiQDl/btsGBgazAhg/S1BEUEr6M4Pn0mKSfbXTT1/img.gif&quot; data-alt=&quot;vc은 오른쪽에서 왼쪽으로, 바텀시트는 아래에서 위로&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxiQDl/btsGBgazAhg/S1BEUEr6M4Pn0mKSfbXTT1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cxiQDl/btsGBgazAhg/S1BEUEr6M4Pn0mKSfbXTT1/img.gif&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;533&quot; data-filename=&quot;slow.gif&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;889&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;vc은 오른쪽에서 왼쪽으로, 바텀시트는 아래에서 위로&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p 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;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;viewController 전환 애니메이션 없이, 바텀시트 애니메이션만 나타낼 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;(만약 홈 화면이 첫번째 화면이 아니라면 &lt;b&gt;viewDidAppear&lt;/b&gt; 시점에 띄우는 게 차선책이다.)&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;공식문서 내용처럼&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;viewWillAppear 직후에 viewIsAppearing이 호출되는데&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;&lt;b&gt;viewIsAppearing&lt;/b&gt;에서&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;바텀시트를 띄우면, &lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;위로 올라오는 애니메이션이 보이지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;slow2.gif&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;889&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yr1rc/btsGBbN8WMa/q7xIC7VfETKID3HqBzfrbk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yr1rc/btsGBbN8WMa/q7xIC7VfETKID3HqBzfrbk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yr1rc/btsGBbN8WMa/q7xIC7VfETKID3HqBzfrbk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/yr1rc/btsGBbN8WMa/q7xIC7VfETKID3HqBzfrbk/img.gif&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;533&quot; data-filename=&quot;slow2.gif&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;889&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://developer.apple.com/documentation/uikit/uiviewcontroller/1621398-viewdidlayoutsubviews&quot;&gt;viewDidLayoutSubviews()&lt;/a&gt;는 어떨까?&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&quot;뷰의 subviews가 모두 layout 완료된 이후에 변화를 주고 싶을 때 override 한다&quot;고 되어있다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이것만 봤을 때는 이 시점에 바텀시트를 띄워도 될 것 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1848&quot; data-origin-height=&quot;366&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCBfTV/btsGAPLlUhN/OqjMPyA8RvMLlJEcQjb091/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCBfTV/btsGAPLlUhN/OqjMPyA8RvMLlJEcQjb091/img.png&quot; data-alt=&quot;viewDidLayoutSubviews 문서&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCBfTV/btsGAPLlUhN/OqjMPyA8RvMLlJEcQjb091/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCBfTV%2FbtsGAPLlUhN%2FOqjMPyA8RvMLlJEcQjb091%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;1848&quot; height=&quot;366&quot; data-origin-width=&quot;1848&quot; data-origin-height=&quot;366&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;viewDidLayoutSubviews 문서&lt;/figcaption&gt;
&lt;/figure&gt;
&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;하지만 viewIsAppearing 문서에 이런 내용이 있다.&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;&quot;viewWill/DidLayoutSubviews는 transition이 진행되는 동안 &lt;b&gt;여러 번 호출될 수 있다.&lt;/b&gt;&quot;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;따라서 &lt;b&gt;바텀시트는 한 번만 띄워야 하므로 viewDidLayoutSubviews는 적절하지 않다&lt;/b&gt;는 것을 알 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1878&quot; data-origin-height=&quot;238&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qhWfk/btsGBPKzHH3/Ge37dzA0paS8AHg1cf1J80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qhWfk/btsGBPKzHH3/Ge37dzA0paS8AHg1cf1J80/img.png&quot; data-alt=&quot;viewIsAppearing 문서&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qhWfk/btsGBPKzHH3/Ge37dzA0paS8AHg1cf1J80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqhWfk%2FbtsGBPKzHH3%2FGe37dzA0paS8AHg1cf1J80%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;1878&quot; height=&quot;238&quot; data-origin-width=&quot;1878&quot; data-origin-height=&quot;238&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;viewIsAppearing 문서&lt;/figcaption&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;실제로 아래처럼 바텀시트가 여러 번 띄워지는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(바텀시트가 숨겨지는 transaction에 의해 layoutSubviews, viewDidLayoutSubviews가 다시 호출된 듯)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;slow3.gif&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;889&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PeVwV/btsGBb8o5yw/t5kzBWXJeLSRWKaa38ZEE1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PeVwV/btsGBb8o5yw/t5kzBWXJeLSRWKaa38ZEE1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PeVwV/btsGBb8o5yw/t5kzBWXJeLSRWKaa38ZEE1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/PeVwV/btsGBb8o5yw/t5kzBWXJeLSRWKaa38ZEE1/img.gif&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;533&quot; data-filename=&quot;slow3.gif&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;889&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결론&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;storyboard를 연결한 viewController에만 발생하는 버그이지만&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;협업하는 팀원이 storyboard를 추가할 수도 있으니까&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;subview layout&lt;/b&gt;은 viewDidLoad 대신 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;viewWillAppear&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;에서 잡는 것이 안전하다.&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;(다만, view constraints에 따라 &lt;b&gt;layoutIfNeeded&lt;/b&gt;를 호출해야 할 수 있고,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;viewController 전환 애니메이션과 분리해야 하는 경우 &lt;b&gt;viewDidAppear&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;그리고 당연한 거지만 viewWillAppear는 다른 화면으로 이동했다가 home으로 되돌아왔을 때 재호출되므로&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;앱을 실행했을 때 한 번만 노출하기 위해&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;&lt;b&gt;바텀시트가&lt;/b&gt;&lt;span&gt;&lt;b&gt; 띄워졌는지 여부&lt;/b&gt;를 homeVC&lt;/span&gt;&lt;/span&gt;의 프로퍼티로 저장하여 flag로 활용해야 한다.&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알고 보니 storyboard에서 &lt;b&gt;is initial view controller&lt;/b&gt;로 체크한 경우에는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;viewDidLoad에서도 view width가 잘 잡히는 거였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 구현해보고 싶다면 SecondViewController를 push 해서 테스트해야 함을 잊지 말자!&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;a href=&quot;https://github.com/just1103/Samples/tree/main/ViewDidLoadWidth&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;GitHub Repo&lt;/a&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;Apple Developer Documentation &amp;gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/uiviewcontroller/4195485-viewisappearing&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;viewIsAppearing(_:)&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple Developer Documentation &amp;gt; &lt;a href=&quot;https://developer.apple.com/documentation/uikit/uiviewcontroller/1621495-viewdidload&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;viewDidLoad()&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;StackOverFlow &amp;gt; &lt;a href=&quot;https://stackoverflow.com/questions/44500340/view-frame-width-is-correct-in-viewdidload-with-storyboard-why&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;view.frame.width IS correct in viewDidLoad with storyboard&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: left;&quot;&gt;StackOverFlow &amp;gt;&lt;a href=&quot;https://stackoverflow.com/questions/65352880/loading-view-controller-from-xib-return-wrong-frame&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; Loading&amp;nbsp;View&amp;nbsp;Controller&amp;nbsp;from&amp;nbsp;xib&amp;nbsp;return&amp;nbsp;wrong&amp;nbsp;frame&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;바텀시트 예제 코드 &amp;gt; &lt;a href=&quot;https://github.com/just1103/Samples/tree/main/ViewDidLoadWidth&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;GitHub Repo&lt;/a&gt;&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>iOS</category>
      <category>bottomsheet</category>
      <category>IOS</category>
      <category>Layout</category>
      <category>UIKit</category>
      <category>ViewDidLoad</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/87</guid>
      <comments>https://applecider2020.tistory.com/87#entry87comment</comments>
      <pubDate>Sun, 14 Apr 2024 21:48:19 +0900</pubDate>
    </item>
    <item>
      <title>[Combine] 기본 원리 - Publisher/Subscriber/Cancellable Protocol, Subscription 메커니즘 (2/3)</title>
      <link>https://applecider2020.tistory.com/86</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;지난번에는 RxSwift와 Combine을 비교하고,&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;5가지 중요한 개념인 &lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;Publishers, Operators, Subscribers, &lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;Subscriptions, Cancellable을 간단히 알아봤다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;지난 포스팅 &lt;a href=&quot;https://applecider2020.tistory.com/85#2.-combine-basics&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Combine] 기본 원리 - Apple이 만든 RxSwift를 이해해 보자 (1/3)&lt;/a&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;계속해서 Combine 기본 원리를 알아보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이번에는 &lt;b&gt;Publisher / Subscriber / Cancellable Protocol&lt;/b&gt;의 개념과&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;Subscription &lt;/b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;&lt;b&gt;메커니즘&lt;/b&gt;을&lt;/span&gt;&amp;nbsp;자세히 살펴봤다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. Publisher Protocol&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;AnyPublisher&lt;/b&gt;를 이해하려면,&amp;nbsp;&lt;b&gt;Publisher Protocol&lt;/b&gt;을 알아야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Publisher는&amp;nbsp;&lt;b&gt;이벤트를 방출 (publish) 한다.&lt;/b&gt; 기존의 NotificationCenter와&amp;nbsp;비슷하다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Publisher Protocol의 정의는 이렇다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;i&gt;defines the requirements for a type to be able to transmit a sequence of values.&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;i&gt;(값을 방출할 수 있는 타입에 대한 요구사항을 정의한다.)&lt;/i&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;즉, 값을 방출하는 Publisher를 만들고 싶다면&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Publisher Protocol에서 만들어 둔 특정 요구사항을 따르게 해야 한다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;요구사항을 간단하게 요약하면 아래와 같다. &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;대략적인 구성만 보자.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Protocol의 연관타입으로 &lt;b&gt;방출할 값의 타입 (Output), 방출할 수도 있는 에러 타입 (Failure)&lt;/b&gt;을 정해뒀다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;에러를 절대 방출하지 않는다면 &lt;b&gt;Never&lt;/b&gt;를 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1707045206802&quot; class=&quot;swift&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;struct AnyPublisher&amp;lt;Output, Failure&amp;gt; where Failure : Error

protocol Publisher {
    associatedtype Output
    associatedtype Failure: Error
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style3&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2.&amp;nbsp;Subscriber&amp;nbsp;Protocol&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;마찬가지로 Subscriber Protocol의 정의도 살펴보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;i&gt;defines the requirements for a type to be able to receive input from a publisher.&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;i&gt;(publisher로부터 값을 받을 수 있는 타입에 대한 요구사항을 정의한다.)&lt;/i&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Subscriber는 Publisher로부터 어떻게 값을 받아올 수 있을까?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;sink 메서드, assign 메서드를 사용하는 2가지 방법이 있다.&lt;/span&gt;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;sink 메서드&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;sink 메서드의 형태를 뜯어보자.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1707046026428&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func sink(receiveValue: @escaping ((Self.Output) -&amp;gt; Void)) -&amp;gt; AnyCancellable&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Publisher로부터 받은 값의 타입인 Output,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;escaping closure를 넘기게 될 receiveValue 매개변수,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그리고&amp;nbsp;AnyCancellable이 반환된다는 것을 알 수 있다.&lt;/span&gt;&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;즉,&amp;nbsp;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;subscriber는&amp;nbsp;&lt;/span&gt;sink 메서드에 &lt;b&gt;클로저&lt;/b&gt;를 전달해 publisher의 output을 처리 하도록 한다.&lt;/span&gt;&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예시는 이렇다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1707046493028&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let myNotification = Notification.Name(&quot;MyNotification&quot;)
let center = NotificationCenter.default

// ✅ NotificationCenter.Publisher 타입을 반환
let publisher = center.publisher(for: myNotification, object: nil)

// ✅ publisher로부터 받은 값을 처리
let subscription = publisher.sink { _ in
    print(&quot;Notification received from a publisher!&quot;)
}

// ✅ 값을 방출
center.post(name: myNotification, object: nil)

// ✅ 구독 취소
subscription.cancel()&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;assign(to:on:) 메서드&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;다음으로 assign(to:on:) 메서드를 뜯어보자.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1707046360328&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func assign&amp;lt;Root&amp;gt;(
    to keyPath: ReferenceWritableKeyPath&amp;lt;Root, Self.Output&amp;gt;,
    on object: Root
) -&amp;gt; AnyCancellable&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;keyPath가 눈에 띈다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;publisher로부터 전달받은 값을 &lt;b&gt;객체의 KVO 호환되는 프로퍼티에 할당&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예시를 보면 쉽다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;assign 메서드를 사용하면 &lt;b&gt;label, textView&lt;/b&gt; 등에 직접 할당이 가능하므로 &lt;b&gt;UIKit&lt;/b&gt;과 함께 사용할 때 특히 더 유용하다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1707046532110&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class SomeObject {
    var value: String = &quot;&quot; {
        didSet {
            print(value)
        }
    }
}
let object = SomeObject()

// ✅ 총 2번 방출됨
let publisher = [&quot;Hello&quot;, &quot;world!&quot;].publisher 

// ✅ 객체의 프로퍼티에 바로 반영시킴
_ = publisher.assign(to: \.value, on: object)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;assign(to:) 메서드&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;참고로 assign(to:)을 통한 &lt;b&gt;re-publish&lt;/b&gt;도 가능하다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;publisher에서&amp;nbsp;방출된&amp;nbsp;값을&amp;nbsp;@Published&amp;nbsp;프로퍼티에다가&amp;nbsp;다시&amp;nbsp;&lt;b&gt;republish &lt;/b&gt;할&amp;nbsp;수&amp;nbsp;있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1707046848075&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class SomeObject {
  // ✅ Property wrapper @Published 
  @Published var value = 0
}
let object = SomeObject()

// ✅ $
object.$value.sink {
    print(&quot;@@@&quot;, $0)
}

// ✅ 새롭운 값 할당함 -&amp;gt; sink 호출됨
(0..&amp;lt;5).publisher.assign(to: &amp;amp;object.$value)

// 출력
@@@ 0
@@@ 0
@@@ 1
@@@ 2
@@@ 3
@@@ 4&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;@Published&lt;/b&gt;를 붙이면, 해당 프로퍼티의 value의 변화를 추적하는 publisher가 생성된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;object.$value 형태로 &lt;b&gt;$&lt;/b&gt;를 붙이면, value에 대한 publisher에 접근하여 subscribe 할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;assign(to:) 메서드에 &amp;amp;object.$value가 전달되었다. 이때 &lt;b&gt;&amp;amp;&lt;/b&gt;는 value 프로퍼티에 대한 inout 참조를 의미한다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;즉, 방출된 값 0~4는 object의 value publisher에다가 할당된다는 뜻이다.&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;여기서 한 가지 특이한 점은&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;assign(to:)는 &lt;b&gt;Cancellables를 반환하지 않는다&lt;/b&gt;는 것이다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;@Published 프로퍼티가 deinit될 때 subscription이 자동으로 cancel 되도록 만들어져있다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;따라서 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;assign(&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;to:on:) 메서드와 달리, &lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;assign(to:)에서는&lt;/span&gt; retain cycle이 생성되지 않아서&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;메모리 관리 측면에서 보다 안전하다는 장점이 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1707047287397&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class MyObject {
    @Published var word: String = &quot;&quot;
    var subscriptions = Set&amp;lt;AnyCancellable&amp;gt;()
	
    init() {
        [&quot;A&quot;, &quot;B&quot;, &quot;C&quot;].publisher
        //   retain cycle 형성됨!!! 
        .assign(to: \.word, on: self)  
        .store(in: &amp;amp;subscriptions)

        // ✅ retain cycle 안생김
        .assign(to: &amp;amp;$word) 
	  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3. Cancellable&amp;nbsp;protocol&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;AnyCancellable 타입이 채택하며, cancel()을 구현해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;더 이상 값을 받을 필요가 없으면 subscription을 cancel&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;더 자세히 보자면,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;subscription은 cancellation &lt;b&gt;token 역할&lt;/b&gt;을 하는 &lt;b&gt;anyCancellable 인스턴스&lt;/b&gt;를 반환하는데, &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이 토큰을 통해 원할 때 subscription cancel을 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;subscription.cancel()이 가능한 것은 Subscription Protocol이 &lt;b&gt;Cancellable을 상속&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;cancel을 호출하지 않으면, subscription이 아래 상황까지 유지된다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1) publisher가 완료될 때까지 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2) subscription을 저장한 프로퍼티가 deinit 될 때까지&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span&gt;4. Subscriber가 Publisher를 구독하는 과정 (feat. 다이어그램)&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span&gt;Publisher와 Subscriber가 구독을 통해 연결되고 값이 전달할 때,&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span&gt;어떤 과정을 거치는지 자세히 알아보자.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/S1zBk/btsEkQ6KJ5Q/ZfeA4GZZduCcQlldnnmE01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/S1zBk/btsEkQ6KJ5Q/ZfeA4GZZduCcQlldnnmE01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/S1zBk/btsEkQ6KJ5Q/ZfeA4GZZduCcQlldnnmE01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FS1zBk%2FbtsEkQ6KJ5Q%2FZfeA4GZZduCcQlldnnmE01%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;600&quot; height=&quot;393&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1258&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. Subscriber가 Publisher를 구독한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. Publisher가 Subscription (구독)을 생성하고, Subscriber에게 준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3. Subscriber가 값을 요청한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;4. Publisher는 값을 보낸다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;5. Publisher가 completion을 보낸다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이 관점에서 다시 위에서 봤던 Protocol을 뜯어보자.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Publisher Protocol+&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;먼저 Publisher Protocol이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1707047661088&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public protocol Publisher {
  associatedtype Output 
  associatedtype Failure: Error 

  // 2 - 1에서 내부적으로 이걸 호출함 (subscriber를 publisher한테 붙여서 subscription 생성)
  func receive&amp;lt;S&amp;gt;(subscriber: S)
    where S: Subscriber,
    Self.Failure == S.Failure,
    Self.Output == S.Input
}

extension Publisher {
  // 1 - subscribe가 이걸 호출해서 구독을 시작함
  public func subscribe&amp;lt;S&amp;gt;(_ subscriber: S)
    where S : Subscriber,
    Self.Failure == S.Failure,
    Self.Output == S.Input
}&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Subscriber는 Publisher를 구독하기 위해 해당 Publisher의 &lt;b&gt;subscribe 메서드&lt;/b&gt;를 호출한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;subscribe 메서드는 내부적으로 &lt;b&gt;receive 메서드&lt;/b&gt;를 호출하는데, 이때 &lt;b&gt;Subscription이 생성된다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(Subscriber가 Publisher와 연결된다.)&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Subscriber protocol+&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1707048252234&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public protocol Subscriber: CustomCombineIdentifierConvertible {
  associatedtype Input  
  associatedtype Failure: Error  

  // 1 - subscriber에게 subscription을 전달 (publisher가 이걸 호출)
  func receive(subscription: Subscription)

  // 2 - subscriber에게 새로운 값을 전달 (publisher가 이걸 호출)
  func receive(_ input: Self.Input) -&amp;gt; Subscribers.Demand

  // 3 - subscriber에게 subscription 끝났음을 전달 (publisher가 이걸 호출)
  func receive(completion: Subscribers.Completion&amp;lt;Self.Failure&amp;gt;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Publisher는 Subscriber의 &lt;b&gt;receive(subscription:) 메서드&lt;/b&gt;를 호출해서&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Subscriber에게 생성된 &lt;b&gt;Subscription을 전달한다.&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: left;&quot;&gt;Publisher는 Subscriber의 &lt;b&gt;receive(_:) 메서드&lt;/b&gt;를 호출해서 &lt;b&gt;방출한 값을 보낸다.&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: left;&quot;&gt;Publisher는 Subscriber의&amp;nbsp;&lt;b&gt;receive(completion:) 메서드&lt;/b&gt;를 호출해서&lt;br /&gt;더 이상 값을 방출할 필요가 없는 상황이라서&amp;nbsp;&lt;b&gt;구독을 취소한다고 알려준다.&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Subscription protocol+&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그런데 위에서 계속 언급한 &lt;b&gt;Subscription&lt;/b&gt;이란 뭘까?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;단순히 &lt;b&gt;Publisher와 Subscriber의 연결&lt;/b&gt;이라고 봐도 무방하다.&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_1707048641235&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public protocol Subscription: Cancellable, CustomCombineIdentifierConvertible {
    // subscriber가 이걸 호출해서 값을 받겠다고 전달함
    // subscriber가 받을 새로운 값의 최대개수를 지정 가능
    func request(_ demand: Subscribers.Demand)
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Subscriber는 &lt;b&gt;request(_:) 메서드&lt;/b&gt;를 호출해서 앞으로 계속 &lt;b&gt;값을 받을 의향이 있다고 알려준다.&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이때 &lt;b&gt;Demand&lt;/b&gt;를 통해 subscriber가 &lt;b&gt;받아올&amp;nbsp;값의&amp;nbsp;최대&amp;nbsp;개수를&amp;nbsp;지정&lt;/b&gt;할&amp;nbsp;수&amp;nbsp;있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #9d9d9d;&quot;&gt;*참고로 Subscription은 커스텀이 가능해서 꽤 복잡한 내용인데,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #9d9d9d;&quot;&gt;사실상 개발자가 직접 커스텀할 일이 없을 것 같아서 내용을 매우 많이 생략했다.&lt;/span&gt;&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #9d9d9d;&quot;&gt;예를 들면 Demand에 받아올 값의 최대 개수를 지정할 때,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;새로운 max값이 기존 max값에 더해진다. (새로운+기존 max값 &amp;gt;= subscriber가 지금까지 받은 총 값의 개수) &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;그래서 negative값이 전달되면 fatalError 호출된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;이런 거... 아마 평생 몰라도 될듯&lt;/span&gt;&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;RxSwift를 처음 접했을 때, Binding이라는 개념 자체가 너무 생소해서 메커니즘을 찾아볼 여유가 없었는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 Combine을 분석하면서 내부적인 Flow를 이해할 수 있게 되어 의미있었다.&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;그리고 개인적으로 Combine을 파헤쳐 본 소감을 말하자면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최대한 공식문서를 통해 스터디를 하는 게 바람직하다고 생각하고, 그러려고 노력하는 편이지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;솔직히 Combine 같은 주제는 공식 문서만으로는 이해하기 어려운 게 맞는 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;Publisher가 Subscriber에게 Subscription을 전달한다&quot;는 것을 알기 위해&amp;nbsp;&lt;br /&gt;얼마나 많은 선행 지식을 이해해야 하는지... 절레절레...&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일에 치이고, 기술 부채에 허덕이는 개발자라면 이럴 때는 현실적으로 Kodeco의 도움을 받는 것도 좋은 것 같다.&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;관련 포스팅&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://applecider2020.tistory.com/85#2.-combine-basics&quot;&gt;[Combine] 기본 원리 - Apple이 만든 RxSwift를 이해해보자 (1/3)&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple Developer Documentation &amp;gt;&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/combine&quot;&gt;Combine&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple &amp;gt; WWDC 2019 &amp;gt; &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2019/722/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Introducing Combine&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple &amp;gt; WWDC 2019 &amp;gt; &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2019/721/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Combine in Practice&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #5c5c5c; text-align: left;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Kodeco &amp;gt;&lt;/span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: left;&quot; href=&quot;https://www.kodeco.com/books/combine-asynchronous-programming-with-swift&quot;&gt;Combine: Asynchronous Programming with Swift&lt;/a&gt;&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>iOS</category>
      <category>Combine</category>
      <category>Combine+UIKit</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/86</guid>
      <comments>https://applecider2020.tistory.com/86#entry86comment</comments>
      <pubDate>Sun, 4 Feb 2024 21:34:06 +0900</pubDate>
    </item>
    <item>
      <title>[Combine] 기본 원리 - Apple이 만든 RxSwift를 이해해보자 (1/3)</title>
      <link>https://applecider2020.tistory.com/85</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;최근 RxSwift를 Combine으로 전환하면서&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Combine의 원리에 대해 자세히 알아봤다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/combine&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Combine 공식문서&lt;/a&gt;와 &lt;a href=&quot;https://www.kodeco.com/books/combine-asynchronous-programming-with-swift&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Kodeco의 Combine&lt;/a&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;개인적으로 &lt;b&gt;Combine+UIKit&lt;/b&gt; 조합은 좋지 않은지, &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;꼭 SwiftUI와 함께 써야 하는지&lt;/span&gt;&amp;nbsp;궁금했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;결론적으로 Combine이 first-party라서 더 안전하고, 성능이 좋기 때문에&lt;br /&gt;그리고 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;UIKit에서 사용할 수 있는 API가 많이 나왔기 때문에 (assign 등등)&lt;br /&gt;&lt;b&gt;Combine+UIKit&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;b&gt;&amp;nbsp;조합도 괜찮다!!&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Combine은 비동기 처리를 쉽게 해주는 도구이므로&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span&gt;네트워크 layer, 새로 구현하는 화면 등 프로젝트 일부에만 사용해도 괜찮으니 &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Combine+UIKit 사용 예시도 다음에 포스팅할 예정이다.&lt;/span&gt;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;먼저 간단히 표로 RxSwift와 Combine을 비교해보자.&lt;br /&gt;&lt;a href=&quot;https://github.com/CombineCommunity/rxswift-to-combine-cheatsheet&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Rxswift/Combine Cheatsheet&lt;/a&gt;을 자주 참고했다.&lt;br /&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;RxSwift에서 제공하지 않는 기능은 &lt;a href=&quot;https://github.com/CombineCommunity/CombineExt&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;CombineExt&lt;/a&gt; 라이브러리를 활용한다. (ex. CurrentValueRelay 등)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이제 Combine 관련 커뮤니티도 제법 커졌다.&lt;/span&gt;&lt;/p&gt;
&lt;table id=&quot;86640e09-fd2a-4bf6-9307-80e53b9beead&quot; style=&quot;border-collapse: collapse; width: 100%; height: 569px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr id=&quot;4136e339-d8e2-4de2-a48f-409275f84fe0&quot; style=&quot;height: 17px;&quot;&gt;
&lt;td id=&quot;&amp;#96;oCi&quot; style=&quot;height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td id=&quot;jU&amp;gt;&amp;#96;&quot; style=&quot;height: 17px;&quot;&gt;RxSwift&lt;/td&gt;
&lt;td id=&quot;qZ~K&quot; style=&quot;height: 17px;&quot;&gt;Combine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;Framework&amp;nbsp;Consumption&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&lt;b&gt;Third-party&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&lt;b&gt;First-party&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;Maintained&amp;nbsp;by&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;Open-Source&amp;nbsp;/&amp;nbsp;Community&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;Apple / Community (커뮤니티 커짐)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;16cb1e1b-15d2-49c7-b287-b5f7d49a216e&quot; style=&quot;height: 19px;&quot;&gt;
&lt;td id=&quot;&amp;#96;oCi&quot; style=&quot;height: 19px;&quot;&gt;던지는 애&lt;/td&gt;
&lt;td id=&quot;jU&amp;gt;&amp;#96;&quot; style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Observable&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;qZ~K&quot; style=&quot;height: 19px;&quot;&gt;&lt;b&gt;AnyPublisher&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;ac3a29e7-486e-4239-8a18-3989b6bab7ce&quot; style=&quot;height: 19px;&quot;&gt;
&lt;td id=&quot;&amp;#96;oCi&quot; style=&quot;height: 19px;&quot;&gt;받는 애&lt;/td&gt;
&lt;td id=&quot;jU&amp;gt;&amp;#96;&quot; style=&quot;height: 19px;&quot;&gt;&lt;b&gt;Observer&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;qZ~K&quot; style=&quot;height: 19px;&quot;&gt;&lt;b&gt;AnySubscriber&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;5d041a2c-16f6-4184-b6c3-444f58a67699&quot; style=&quot;height: 38px;&quot;&gt;
&lt;td id=&quot;&amp;#96;oCi&quot; style=&quot;height: 38px;&quot;&gt;둘다 하는 애 &lt;br /&gt;(초기값 없음)&lt;/td&gt;
&lt;td id=&quot;jU&amp;gt;&amp;#96;&quot; style=&quot;height: 38px;&quot;&gt;&lt;b&gt;PublishSubject&lt;/b&gt;&lt;br /&gt;- value, error, completed 던짐&lt;/td&gt;
&lt;td id=&quot;qZ~K&quot; style=&quot;height: 38px;&quot;&gt;&lt;b&gt;PassthroughSubject&lt;/b&gt;&lt;br /&gt;- value, error, finish 던짐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;26378975-547f-4dad-9ed8-709ec7460144&quot; style=&quot;height: 19px;&quot;&gt;
&lt;td id=&quot;&amp;#96;oCi&quot; style=&quot;height: 19px;&quot;&gt;둘다 하는 애 (초기값 있음)&lt;/td&gt;
&lt;td id=&quot;jU&amp;gt;&amp;#96;&quot; style=&quot;height: 19px;&quot;&gt;&lt;b&gt;BehaviorSubject&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;qZ~K&quot; style=&quot;height: 19px;&quot;&gt;&lt;b&gt;CurrentValueSubject&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;82eac9a8-8b02-48ea-9649-4f4d75b5ea49&quot; style=&quot;height: 17px;&quot;&gt;
&lt;td id=&quot;&amp;#96;oCi&quot; style=&quot;height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td id=&quot;jU&amp;gt;&amp;#96;&quot; style=&quot;height: 17px;&quot;&gt;Single&lt;/td&gt;
&lt;td id=&quot;qZ~K&quot; style=&quot;height: 17px;&quot;&gt;*Future (Deffered)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;aea1e80b-4951-4ec1-bc97-091390f69cef&quot; style=&quot;height: 17px;&quot;&gt;
&lt;td id=&quot;&amp;#96;oCi&quot; style=&quot;height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td id=&quot;jU&amp;gt;&amp;#96;&quot; style=&quot;height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td id=&quot;qZ~K&quot; style=&quot;height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;f58abde6-5de5-44bc-912b-e88fc186111a&quot; style=&quot;height: 19px;&quot;&gt;
&lt;td id=&quot;&amp;#96;oCi&quot; style=&quot;height: 19px;&quot;&gt;구독&lt;/td&gt;
&lt;td id=&quot;jU&amp;gt;&amp;#96;&quot; style=&quot;height: 19px;&quot;&gt;&lt;b&gt;.subscribe&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;qZ~K&quot; style=&quot;height: 19px;&quot;&gt;&lt;b&gt;.sink&lt;/b&gt; or &lt;b&gt;.assign&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;22988ae3-d8cd-4495-8ab9-07f1bc5b0e95&quot; style=&quot;height: 19px;&quot;&gt;
&lt;td id=&quot;&amp;#96;oCi&quot; style=&quot;height: 19px;&quot;&gt;버리기&lt;/td&gt;
&lt;td id=&quot;jU&amp;gt;&amp;#96;&quot; style=&quot;height: 19px;&quot;&gt;.disposed(by: &lt;b&gt;disposeBag&lt;/b&gt;)&lt;/td&gt;
&lt;td id=&quot;qZ~K&quot; style=&quot;height: 19px;&quot;&gt;.store(in:&lt;b&gt; &amp;amp;cancellables&lt;/b&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;ac77f131-2e51-4cb1-b307-b8ace9f4e109&quot; style=&quot;height: 17px;&quot;&gt;
&lt;td id=&quot;&amp;#96;oCi&quot; style=&quot;height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td id=&quot;jU&amp;gt;&amp;#96;&quot; style=&quot;height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td id=&quot;qZ~K&quot; style=&quot;height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;a5850814-08f0-497b-87be-298008235ae6&quot; style=&quot;height: 36px;&quot;&gt;
&lt;td id=&quot;&amp;#96;oCi&quot; style=&quot;height: 36px;&quot;&gt;스레드 전환&lt;br /&gt;(down only)&lt;/td&gt;
&lt;td id=&quot;jU&amp;gt;&amp;#96;&quot; style=&quot;height: 36px;&quot;&gt;observeOn&lt;/td&gt;
&lt;td id=&quot;qZ~K&quot; style=&quot;height: 36px;&quot;&gt;receiveOn(on:)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;18470aca-0b6d-4bc8-956b-c6ec073200f2&quot; style=&quot;height: 35px;&quot;&gt;
&lt;td id=&quot;&amp;#96;oCi&quot; style=&quot;height: 35px;&quot;&gt;스레드 전환&lt;/td&gt;
&lt;td id=&quot;jU&amp;gt;&amp;#96;&quot; style=&quot;height: 35px;&quot;&gt;SubscribeOn&lt;br /&gt;(up &amp;amp; down)&lt;/td&gt;
&lt;td id=&quot;qZ~K&quot; style=&quot;height: 35px;&quot;&gt;*Subscribe(on:)&lt;br /&gt;(down only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;b972c491-0112-4423-ae8a-f497baab744c&quot; style=&quot;height: 19px;&quot;&gt;
&lt;td id=&quot;&amp;#96;oCi&quot; style=&quot;height: 19px;&quot;&gt;기타 공통 Operator&lt;/td&gt;
&lt;td id=&quot;jU&amp;gt;&amp;#96;&quot; style=&quot;height: 19px;&quot;&gt;just, combineLatest, zip, debounce &amp;hellip;&lt;/td&gt;
&lt;td id=&quot;qZ~K&quot; style=&quot;height: 19px;&quot;&gt;- 대부분 동일하게 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;46d20e92-1356-4b61-a3ea-21c838ac2d32&quot; style=&quot;height: 17px;&quot;&gt;
&lt;td id=&quot;&amp;#96;oCi&quot; style=&quot;height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td id=&quot;jU&amp;gt;&amp;#96;&quot; style=&quot;height: 17px;&quot;&gt;merge(A,B,C)&lt;/td&gt;
&lt;td id=&quot;qZ~K&quot; style=&quot;height: 17px;&quot;&gt;A.merge(with: B,C)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;9eafe5a7-500b-46f6-8b38-e23a4a166389&quot; style=&quot;height: 17px;&quot;&gt;
&lt;td id=&quot;&amp;#96;oCi&quot; style=&quot;height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td id=&quot;jU&amp;gt;&amp;#96;&quot; style=&quot;height: 17px;&quot;&gt;do&lt;/td&gt;
&lt;td id=&quot;qZ~K&quot; style=&quot;height: 17px;&quot;&gt;handleEvents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;02595bcc-f67b-48df-b861-356b94592577&quot; style=&quot;height: 17px;&quot;&gt;
&lt;td id=&quot;&amp;#96;oCi&quot; style=&quot;height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td id=&quot;jU&amp;gt;&amp;#96;&quot; style=&quot;height: 17px;&quot;&gt;skip&lt;/td&gt;
&lt;td id=&quot;qZ~K&quot; style=&quot;height: 17px;&quot;&gt;dropFirst&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;f3c59b76-1ac0-4cef-a650-441832aaa086&quot; style=&quot;height: 17px;&quot;&gt;
&lt;td id=&quot;&amp;#96;oCi&quot; style=&quot;height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td id=&quot;jU&amp;gt;&amp;#96;&quot; style=&quot;height: 17px;&quot;&gt;take&lt;/td&gt;
&lt;td id=&quot;qZ~K&quot; style=&quot;height: 17px;&quot;&gt;prefix&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;87bcfc69-0974-465e-a9f8-7d051b546848&quot; style=&quot;height: 17px;&quot;&gt;
&lt;td id=&quot;&amp;#96;oCi&quot; style=&quot;height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td id=&quot;jU&amp;gt;&amp;#96;&quot; style=&quot;height: 17px;&quot;&gt;valueChanged&lt;/td&gt;
&lt;td id=&quot;qZ~K&quot; style=&quot;height: 17px;&quot;&gt;removeDuplicates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;c7b784d9-8c16-4250-850f-5e6778f21362&quot; style=&quot;height: 17px;&quot;&gt;
&lt;td id=&quot;&amp;#96;oCi&quot; style=&quot;height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td id=&quot;jU&amp;gt;&amp;#96;&quot; style=&quot;height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td id=&quot;qZ~K&quot; style=&quot;height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;a9ec9d5e-fe1d-4724-9178-1199b10a73fc&quot; style=&quot;height: 72px;&quot;&gt;
&lt;td id=&quot;&amp;#96;oCi&quot; style=&quot;height: 72px;&quot;&gt;없는거&lt;/td&gt;
&lt;td id=&quot;jU&amp;gt;&amp;#96;&quot; style=&quot;height: 72px;&quot;&gt;BehaviorRelay&lt;br /&gt;/PublishRelay&lt;/td&gt;
&lt;td id=&quot;qZ~K&quot; style=&quot;height: 72px;&quot;&gt;❌&lt;br /&gt;&lt;br /&gt;*CombineExt&lt;br /&gt;CurrentValueRelay&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;b3c682ca-ef12-4cac-bcb2-afcb6e80280c&quot; style=&quot;height: 36px;&quot;&gt;
&lt;td id=&quot;&amp;#96;oCi&quot; style=&quot;height: 36px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td id=&quot;jU&amp;gt;&amp;#96;&quot; style=&quot;height: 36px;&quot;&gt;.asObservable&lt;/td&gt;
&lt;td id=&quot;qZ~K&quot; style=&quot;height: 36px;&quot;&gt;*직접 구현&lt;br /&gt;ReadOnly/CurrentValuePublisher&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. Introduction&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Combine이 등장하기 전에 비동기 처리를 어떻게 했을까?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;NotificationCenter, delegate pattern, Grand Central Dispatch (or Operations), Closures, Timer 등이 있다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이제 위의 모든 기능은 Combine으로 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;통합하여&amp;nbsp;구현할 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. Combine Basics&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/combine&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Combine framework 공식문서&lt;/a&gt;를 열면 가장 먼저 보이는 문장이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;비동기 이벤트, 선언적, publishers, subscribers라는 단어가 키워드임을 알 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2100&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dSxch1/btsDHm5PPl5/9KZcYKEirBpkeqAB9JmZB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dSxch1/btsDHm5PPl5/9KZcYKEirBpkeqAB9JmZB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dSxch1/btsDHm5PPl5/9KZcYKEirBpkeqAB9JmZB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdSxch1%2FbtsDHm5PPl5%2F9KZcYKEirBpkeqAB9JmZB1%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;2100&quot; height=&quot;500&quot; data-origin-width=&quot;2100&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;The Combine framework provides a &lt;b&gt;declarative&lt;/b&gt; Swift API for processing values over time. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;These values can represent many kinds of &lt;b&gt;asynchronous events.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Combine declares &lt;b&gt;publishers&lt;/b&gt;&amp;nbsp;to expose values that can change over time, and&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;subscribers&lt;/b&gt;&amp;nbsp;to receive those values from the publishers.&lt;/span&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;&lt;span style=&quot;color: #333333; text-align: start; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이제 Combine의 3개 주요 요소인 Publishers, Operators, Subscribers를 보자.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1) Publishers&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Subscribers에게 값을 방출한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3개 종류의 이벤트를 방출할 수 있다. 그리고 한 번 complete 되면 (성공이든 실패든) 더 이상 emit하지 않는다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(1) Output&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(2) successful completion&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(3) Failure&amp;nbsp; 안 함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;error handling 기능을 기본 제공한다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Publisher protocol은 2개 타입에 대한 Generic 타입이다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;(1) Publisher.Output&lt;/b&gt; : output 값의 타입 (ex. Int)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;(2) Publisher.Failure&lt;/b&gt; : 실패 시 publisher가 throw 하는 error 타입&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;*만약 publisher가 절대 실패하지 않으면 Never 타입을 사용한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2) Operators&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;Publisher protocol에 선언된 메서드이며, 동일하거나 새로운 publisher를 반환한다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;highly decoupled/composable하다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;항상 input 및 output을 가지며, 흔히 &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;upstream/downstream&lt;/b&gt;&lt;/span&gt;라고 불린다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3) Subscribers&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;방출된 output 또는 completion events를 받아서 뭔가를 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;기본 제공되는 2개 종류의 subscribers가 있다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;(1) sink subscriber&lt;/b&gt; : provide closures with your code that will receive output values and completions.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;(2) assign subscriber&lt;/b&gt; : without the need of custom code, &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;i&gt;bind&lt;/i&gt; the resulting output to some property on your data model or on a UI control &lt;/span&gt;to display the data directly on-screen &lt;span style=&quot;color: #ee2323;&quot;&gt;via a key path.&lt;/span&gt;&lt;/span&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;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그리고 문서를 읽다 보면 Subscriptions, Cancellable에 대한 언급이 많다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;4) Subscriptions&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;When you add a subscriber at the end of a subscription, &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;it &lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;ldquo;activates&amp;rdquo; the publisher&lt;/span&gt;&lt;/b&gt; all the way at the beginning of the chain.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Subscriptions (pub sub mechanism)의 장점 : allow you to&amp;nbsp;&lt;i&gt;declare&lt;/i&gt;&amp;nbsp;a chain of asynchronous events with their own custom code and error handling&amp;nbsp;&lt;b&gt;only once.&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;5) Cancellable&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Cancellable을 통해 memory 관리가 자동으로 된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;ex. ViewController의 프로퍼티로 Cancellable 또는 [AnyCancellable]을 들고 있도록 하는데, &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이 때문에 subscription의 lifespan을 쉽게 bind 할 수 있다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(VC dismiss/deinit &amp;rarr; properties deinit &amp;rarr; subscription cancel)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;다음 포스팅에서는 Publishers, Subscribers, Cancellable Protocol에 대해 알아보고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Subscriptions 과정을 뜯어볼 예정이다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;- Reference&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #5c5c5c; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple Developer Documentation &amp;gt; &lt;a href=&quot;https://developer.apple.com/documentation/combine&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Combine&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; background-color: #ffffff; color: #5c5c5c; text-align: left;&quot;&gt;Kodeco &amp;gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; background-color: #ffffff; color: #0070d1; text-align: left;&quot; href=&quot;https://www.kodeco.com/books/combine-asynchronous-programming-with-swift&quot;&gt;Combine: Asynchronous Programming with Swift&lt;/a&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;GitHub &amp;gt; &lt;a href=&quot;https://github.com/CombineCommunity/rxswift-to-combine-cheatsheet&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Rxswift to Combine Cheatsheet&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;GitHub &amp;gt; &lt;a style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; background-color: #e6f5ff; color: #0070d1; text-align: start;&quot; href=&quot;https://github.com/CombineCommunity/CombineExt&quot;&gt;CombineExt&lt;/a&gt;&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>iOS</category>
      <category>Combine</category>
      <category>Combine+UIKit</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/85</guid>
      <comments>https://applecider2020.tistory.com/85#entry85comment</comments>
      <pubDate>Sun, 21 Jan 2024 15:06:55 +0900</pubDate>
    </item>
    <item>
      <title>[2023년 회고] 개발자로 커리어 전환, 1년 후기 - 더 이상 일하는 게 괴롭지 않다</title>
      <link>https://applecider2020.tistory.com/84</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;연말 크리스마스 시즌을 맞아 2023년 회고를 작성했다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;약 1년 4개월 동안 개발자로 근무해 본 시점에서 소감을 남겼고 (주관 가득 주의)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://applecider2020.tistory.com/71&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;작년 회고&lt;/a&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;목차는 아래와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. IT회사에서 개발자로 일하기&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. 올해 목표였던 것&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3. 내년 목표&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;+)&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;646&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcNB8C/btsCy6Jntif/raeQF2Rao3xoHcOPRsgJX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcNB8C/btsCy6Jntif/raeQF2Rao3xoHcOPRsgJX0/img.png&quot; data-alt=&quot;회사 이벤트였던 '인간 크리스마스 Day' - 크리스마스 트리 인간이 올 줄은 몰랐다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcNB8C/btsCy6Jntif/raeQF2Rao3xoHcOPRsgJX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcNB8C%2FbtsCy6Jntif%2FraeQF2Rao3xoHcOPRsgJX0%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;800&quot; height=&quot;442&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;646&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;회사 이벤트였던 '인간 크리스마스 Day' - 크리스마스 트리 인간이 올 줄은 몰랐다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. IT회사에서 개발자로 일하기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;IT와는 사뭇 동떨어진 일터에서 근무하다가 &lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;개발자로 취업한 뒤 여러 방면에서 문화 충격을 받았다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #9d9d9d;&quot;&gt;사무실에서 카페처럼 BGM을 틀어주는 것만으로도 충격이었다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;대부분 긍정적인 충격이었기 때문에 그 경험을 털어놓고 싶어서 입이 근질근질했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그런데 입사 초반에는 단점보다는 장점만 말하게 될 것 같아서 해가 바뀔 때까지 기다렸다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;말하자면 제품을 구매한 직후에 쓴 후기보다 1개월 사용 후기가 더 믿을 만한 것처럼 1년 근무 후기를 올려야 할 것 같았다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;지난 회사에서 느꼈던 갈증&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;커리어 전환을 하기 전에는 환경공학을 전공하고, 2021년까지 발전소에서 &lt;b&gt;환경 엔지니어&lt;/b&gt;로 근무했었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;발전소를 포함해서 대부분의 발전/제조업의 주요 업무는 전화기 (전자전기, 화공, 기계) 전공자에게 주어지고&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그 외 직접적인 성과를 낼 수 없는 staff 부서는 &lt;b&gt;회사 내 입지와 대우에도, 개인적인 성장에도 한계가 많았다.&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;먼저 회사 내 입지가 중요했던 이유는 근무했던 곳이 &lt;b&gt;협업이 많은 팀&lt;/b&gt;이었기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;전공 관련 기사 자격증을 4개 따고, 업무 역량을 기르려고 노력했지만&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;직접 공사를 주관하거나 설비를 운영하는 &lt;b&gt;주요 부서의 협조가 없으면 일을 할 수 없었다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;업무 우선순위에서 밀리다 보니 우리 팀은 그냥 귀찮은 일 시키는 부서일 뿐이었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;자세히 말하자면 10 페이지 정도 쓸 수 있지만.. 이 정도로 줄였다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그리고 이미 staff 조직의 한계에 대해 감안하고 입사를 했지만&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;일을 하면 할수록 루틴 업무를 기계적으로 수행하는 것이 적성에 맞지 않는다는 것을 여실히 알게 되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그리고 동시에 작년에 했던 업무를 똑같이 반복하며 안정적으로 월급을 받는 환경을 선호하는 성향의 사람들이 있다는 것도 인정하게 됐다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;결론적으로는 &lt;b&gt;꾸준히 성과를 내고 성장할 수 있는 환경에서, 배울 점이 많은 동료들과 일하고 싶다&lt;/b&gt;는 꿈을 안고 개발 공부를 시작했었다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그래서 지금은?&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;약 1년 4개월 정도 개발자로 근무해 본 소감을 말하자면 &lt;b&gt;대만족&lt;/b&gt;이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;우선 IT회사에서 개발팀은 &lt;b&gt;직접적인 성과를 만들어내는 핵심 부서&lt;/b&gt;에 속한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;새로운 서비스를 만들 때 관여하는 부서가 많지만 개발팀을 통해서 기획을 실현시킬 수 있어 기여도가 높은 편이라고 생각한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;특히 모바일 앱은 사용자가 서비스를 이용하는 &lt;b&gt;접점&lt;/b&gt;이 되기 때문에 서비스 지표가 오를 때 직접적인 성취감을 느낄 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이렇게 성과를 만드는 부서이기 때문에 회사 내에서 대우도 좋다.&amp;nbsp;&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그리고 이러한 &lt;b&gt;주요 업무를 컴공 전공자가 아님에도 맡을 수 있다&lt;/b&gt;는 게 굉장히 큰 매력이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;물론 전공자가 아닌 만큼 열심히 해야 한다는 압박감도 크지만...&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;어차피 공부가 일상이어야 하는 개발자에게 좋은 동기부여가 된다고 생각하고 있다. &lt;span style=&quot;letter-spacing: 0px; color: #9d9d9d;&quot;&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;또 무엇보다 같이 일하는 동료들이 좋다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;일주일 동안 가장 많은 시간을 보내는 사무실에&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;억지로 노력하지 않아도 &lt;b&gt;자연스레 마음이 가는 동료들&lt;/b&gt;이 있다는 게 &lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;회사 생활을 얼마나 즐겁게 만드는지 배우는 중이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;지난 회사와 분위기 자체가 많이 달라서 비교하는 게 무의미할 수도 있지만&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그래서 현재 수직적인 회사에서 위와 비슷한 갈증을 갖고 있는 분이라면..&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;일이 인생에서 차지하는 비중이 큰 편인데 직장 만족도가 낮아서 괴롭다면  &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;개발자로 전향하는 것이 좋은 탈출구가 될 수 있다,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;마치 다른 나라에 와서 근무하는 것처럼 환경을 완전히 바꾸는 것이 어느 정도는 가능하다는 말을 꼭 드리고 싶었다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;힘든 점&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그렇지만 당연히 개발자로서 겪는 어려움도 있는데 그중 하나는&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;24시간 Slack을 모니터링&lt;/b&gt;하면서... 버그가 발생했을 때 즉시 대응해야 한다는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이상하게 퇴근시간 직전에 버그 제보가 올라와서 집에 못가고 디버깅을 했던 때가 적지 않았다. &lt;span style=&quot;color: #9d9d9d;&quot;&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;생각해보니 그래도 서버 개발자의 고충에 비해서는 좀 나은 것 같다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;앱 개발자는 당장 새벽에 버그를 고치더라도 &lt;b&gt;AppStore 심사&lt;/b&gt;를 거쳐야 배포할 수 있기 때문에&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;치명적인 에러가 발생했을 때도 다음 날 오전에 해결하는 게 일반적이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; 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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그리고 다른 하나는&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;12시간 Slack을 모니터링&lt;/b&gt;하면서... 개발 중인 피처 의 정책이나 디자인이 바뀌었을 때 즉시 대응해야 한다는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;생각보다 개발 도중에 큼직한 변경사항이 생기는 경우가 있는데&amp;nbsp;메시지를 놓쳤다가 나중에 일을 두 번 해야 할 때도 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그래서 &lt;b&gt;로직을 짜면서 몰입력을 유지하면서도 Slack을 수시로 확인해야 한다&lt;/b&gt;는 점에서&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;일반 기업처럼 책상 위에 전화기가 놓여있지는 않지만 그럼에도 꽤 많은 컨텍스트 스위칭이 발생한다고 느꼈다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;요즘은 업무가 많은 시기에는 사무실이 조용한 오전 8시에 일찍 출근해서 몰입할 수 있는 시간을 확보하고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. 올해 목표였던 것&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;작년 회고를 보며 올해 목표를 얼마나 실행했는지 정량적으로 평가해봤다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;10개 목표의 중요도가 다르지만 단순 평균을 내면 &lt;b&gt;87%&lt;/b&gt;이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;완벽하지는 않지만 꽤 만족스러운 결과인 것 같다.&lt;/span&gt;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1) 회사에서 진행 중인 글로벌앱 출시 프로젝트를 완수하고, 안정적인 서비스 운영에 기여하기&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #1b711d;&quot;&gt;100%&lt;/span&gt; &lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #1b711d;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;당연한 걸 수도 있지만 &lt;span style=&quot;background-color: #ffffff; text-align: left;&quot;&gt;개발자로는 첫 회사였기 때문에&amp;nbsp;더더욱&amp;nbsp;&lt;/span&gt;회사 업무에 가장 시간과 노력을 많이 들였다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;글로벌 서비스 론칭을 앞둔 시점에 투입되어 업무량이 적지 않았지만... &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그래도 그만큼 채팅 기능을 포함해 &lt;b&gt;여러 복잡한 view를 원 없이 그려볼 수 있어서 좋았다.&lt;/b&gt;&lt;br /&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 style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;또 동일한 피처가 &lt;b&gt;국내앱&lt;/b&gt;에 구현되어 있는 경우가 있는데, &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이때 기존 코드를 구경하는 것, 그리고 개선하는 것 모두 흥미로운 작업이었다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;국내/해외 서비스를 모두 경험할 수 있는 환경에 감사하다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;글로벌앱을 론칭하고 1년 정도 되었는데 아직은 작고 소중한 수준이긴 하지만 매출이 꾸준히 늘고 있어서 뿌듯하다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2) &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;업무&amp;nbsp;중에&amp;nbsp;접한&amp;nbsp;키워드&amp;nbsp;꾸준히&amp;nbsp;스터디하기&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;70%&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;지식이 미천해서 &lt;b&gt;새롭게 알게 된 개념이 너무 많았기 때문에&lt;/b&gt; 모든 키워드를 스터디하지는 못했다.&amp;nbsp;(고 변명해본다...)&lt;br /&gt;하지만 기록충답게 근무일지에 모두 메모해뒀으므로 시간이 날 때마다 틈틈이 공부할 계획이다.&amp;nbsp;&lt;br /&gt;예를&amp;nbsp;들면&amp;nbsp;Deferred&amp;nbsp;deeplink,&amp;nbsp;App&amp;nbsp;Scheme,&amp;nbsp;Kinesis&amp;nbsp;logging,&amp;nbsp;Web&amp;nbsp;socket,&amp;nbsp;Localization,&amp;nbsp;Combine,&amp;nbsp;Push&amp;nbsp;notification,&amp;nbsp;Server-driven&amp;nbsp;view&amp;nbsp;등을&amp;nbsp;구현해봤고,&amp;nbsp;그&amp;nbsp;외&amp;nbsp;Charles,&amp;nbsp;Kibana,&amp;nbsp;Tableau&amp;nbsp;등의&amp;nbsp;툴을&amp;nbsp;다뤄봤다.&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;3) &lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: left;&quot;&gt;팀원들과&amp;nbsp;더&amp;nbsp;잘&amp;nbsp;협업하기&amp;nbsp;(29cm의&amp;nbsp;김우성&amp;nbsp;개발자님의&amp;nbsp;특강으로&amp;nbsp;들었던&amp;nbsp;&amp;ldquo;PR로&amp;nbsp;협업하기&amp;rdquo;를&amp;nbsp;회사&amp;nbsp;코드리뷰에&amp;nbsp;적용해&amp;nbsp;볼&amp;nbsp;계획이다.)&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #1b711d;&quot;&gt;90%&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;b&gt;팀 내 발표&lt;/b&gt;를 통해 PR 활용 방법을 공유하고, 실제로 팀에서 쓰는 &lt;b&gt;PR template을 개선&lt;/b&gt;했다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;문제해결에 참고했던 Reference, AS-IS/TO-BE를 공유하는 것이 협업에 도움이 되었다는 평가를 들어서 뿌듯했다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;새로운 시도에 열려있는 팀원들 덕분에 가능했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;br /&gt;이외에도 기록 덕후로서 Onboarding 과정에서 배운 것을 모두 문서로 남겨서 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;팀 내 Confluence 문서를 기준으로 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;Most active &lt;b&gt;reader&lt;/b&gt; 1위 (912 views), Most active &lt;b&gt;contributors&lt;/b&gt; 1위 (21 created, 84 updated)를 했다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;그중에서도&lt;span&gt;&amp;nbsp;&lt;/span&gt;Charles/Tableau/딥링크 테스트 관련 튜토리얼 문서를 작성했었는데 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;취준할 때 이력서에 &lt;b&gt;문서화 역량&lt;/b&gt;을 엄청 강조하면서도 이게 정말 도움이 될까 의구심을 가졌었는데, &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;실제로 긍정적인 평가를 받아서 혼자서 조용히 기뻐하고 있다. &lt;span style=&quot;color: #9d9d9d;&quot;&gt;세상의 모든 기록덕후 만세!&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4) &lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: left;&quot;&gt;팀원들과 진행 중인&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: left;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: left;&quot;&gt;디자인 패턴&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: left;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: left;&quot;&gt;스터디 1회/주 참여하기&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;70%&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;이 스터디를 했었다는 사실을 방금 깨달았다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;후반으로 갈수록 아무도 참여하지 않아서 끝을 보지 못했는데, &lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;연초에 혼자서라도 마무리를 지어야 할 것 같다. &lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;업무에 적용할 수 있는 부분이 생각보다 많지 않았지만, 패턴을 공부하는 것 자체는 재밌었다.&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;5) &lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: left;&quot;&gt;노수진님의&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: left;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://fastcampus.co.kr/dev_red_rsj&quot;&gt;슈퍼앱 운영을 위한 확장성 높은 앱 아키텍처 구축&lt;/a&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: left;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #5c5c5c; text-align: left;&quot;&gt;강의 듣기&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #1b711d;&quot;&gt;100%&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;요즘 '모듈화'가 유행인만큼 핫한 주제인 &lt;b&gt;RIBs 아키텍처&lt;/b&gt;에 대해 배웠다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;배울수록 슈퍼앱이 아니면 RIBs의 장점을 활용할 수 없을 것이라는 생각이 들었고, 지금 당장 실무에는 적용할 수 없겠다고 결론 내렸다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;하지만 &lt;b&gt;의존성 역전, 테스트 코드&lt;/b&gt;에 대해 이해하는 데 굉장히 큰 도움을 받았다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;6) &lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: left;&quot;&gt;사이드 프로젝트로 &amp;lsquo;우리뭐먹지&amp;rsquo; 앱&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: left;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: left;&quot;&gt;고도화하기 (지도, 즐겨찾기, 로그인 기능)&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;0%&lt;/span&gt;&lt;/b&gt;&lt;span&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사이드 프로젝트는 &lt;b&gt;전혀 못했다. &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게다가 믿기 어렵지만 서버 비용이 청구되기 시작해서 서버를 껐더니 AppStore에서 앱이 내려갔다.&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;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;7) &lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: left;&quot;&gt;글또 구성원과 소통하고, 블로깅 1회/2주 하기&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #1b711d;&quot;&gt;80%&lt;/span&gt;&lt;/b&gt;&lt;span&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패스를 2번 썼고, 휴가 시즌에 푹 쉬다가 제출 시기를 잊어버린 적이 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 &lt;b&gt;글또&lt;/b&gt; 덕분에 꾸준히 블로깅을 할 수 있었고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무엇보다 글쓰기를 좋아하는 다른 개발자분들을 통해 &lt;b&gt;많은 영감을 받을 수 있었던 소중한 창구&lt;/b&gt;였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;글또를 소개해주신 &lt;a href=&quot;https://medium.com/@Jager-yoo&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;예거&lt;/a&gt;에게 다시 한번 감사를!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;블로그 포스팅을 작년에 비해 많이 하지는 못했지만 예전에 썼던 글이 종종 역주행하면서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7월, 9월, 10월에는 월간 방문수가 &lt;b&gt;5천명을 돌파&lt;/b&gt;해서 뿌듯했다. &lt;b&gt;컨텐츠의 위력&lt;/b&gt;을 다시 한번 느꼈다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;8) &lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: left;&quot;&gt;WWDC 1회/주 보고 정리하기&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;70%&lt;/span&gt;&lt;/b&gt;&lt;span&gt; &lt;/span&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~2번 목표에서 볼 수 있듯이 업무에 관련된 내용만 공부해도 너무 벅찼고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UIKit 관련 업데이트가 많이 줄어서 더욱 손이 가지 않았다. 그래서 &lt;b&gt;Localization&lt;/b&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;그래도 Localization은 모조리 (약 10개) 훓었으니 이렇게라도 본 게 어디인가 싶기도 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내년에는 &lt;b&gt;performance&lt;/b&gt; 관련된 영상을 미리 찾아두고,&lt;span&gt;&amp;nbsp;&lt;/span&gt;1회/월 보는 것을 목표로 삼아야겠다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;9) &lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: left;&quot;&gt;iOS 관련 컨퍼런스 1회/년 참석하기&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #1b711d;&quot;&gt;200%&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대문자 I인 인간이라서 이 목표는 솔직히 스스로 세우면서도 안지킬지도 모른다고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 웬걸, 우연한 기회로 처음 참석했던 개발자 컨퍼런스 &lt;b&gt;KWDC23&lt;/b&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;span style=&quot;color: #9d9d9d;&quot;&gt;이때 살짝 번아웃와서 이후로 블로그 포스팅을 4개월 동안 쉬었다... ㅎㅎㅎ&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 내용은&amp;nbsp;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://applecider2020.tistory.com/82&quot;&gt;KWDC23 연사 참여 후기&lt;/a&gt;를 참고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;그리고 &lt;b&gt;렛어스고&lt;/b&gt; 가서 야곰이랑 오프라인으로는 처음 인사했다. &lt;span style=&quot;color: #9d9d9d;&quot;&gt;떨려 하면서 갔는데 야곰은 모르실 듯 ㅎㅎㅎ &lt;/span&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;10) &lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: left;&quot;&gt;단어장 만들어서 영어&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: left;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: left;&quot;&gt;공부하기&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #1b711d;&quot;&gt;90%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/b&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;b&gt;말해보카&lt;/b&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;br /&gt;궁금하시다면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://applecider2020.tistory.com/77&quot;&gt;말해보카 앱 리뷰&lt;/a&gt;를 참고&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3. 내년 목표&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;목표에는 긴 설명이 필요 없으니 간단히 남겨봤다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;지난 해에 이어서 글로벌앱이 개발 측면에서 안정적으로 운영되고, 디자인/운영 측면에서 성과를 낼 수 있도록 기여하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;국내앱 프로젝트에 참여할 때 기여할 수 있는 방법 고민하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;업무 중에 접한 키워드 꾸준히 스터디하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Combine, SwiftUI 1회/주 스터디하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;새롭게 기획하고 있는 사이드 프로젝트에서 앱 출시하고, 1회 이상 업데이트하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;블로깅 1회/2주 하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;글또 구성원과 글에 대해 논의하기. iOS 이외 분야의 글도 읽기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;멘토 활동을 통해 지식 전파하고, 신선한 시도에서 영감받기&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;performance 관련 WWDC 1회/월 정리하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;iOS 관련 컨퍼런스 1회/년 참석하기&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;30분/일 영어공부. ChatGPT 활용해서 하루 일과 영어로 정리하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;4회/주 &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;아침 운동, 명상하기&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;+) &lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;올해를 추억할 이미지로 마무리&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1734&quot; data-origin-height=&quot;696&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqdZQ3/btsCxaMFlnf/4GZnIjGuMAnkkFQtKrJqp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqdZQ3/btsCxaMFlnf/4GZnIjGuMAnkkFQtKrJqp1/img.png&quot; data-alt=&quot;올해 블로그 방문수&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqdZQ3/btsCxaMFlnf/4GZnIjGuMAnkkFQtKrJqp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqdZQ3%2FbtsCxaMFlnf%2F4GZnIjGuMAnkkFQtKrJqp1%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;800&quot; height=&quot;321&quot; data-origin-width=&quot;1734&quot; data-origin-height=&quot;696&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;올해 블로그 방문수&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1946&quot; data-origin-height=&quot;1002&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bO8puK/btsCEJfcIKO/KzglvIewc6goKEnaGKf3yK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bO8puK/btsCEJfcIKO/KzglvIewc6goKEnaGKf3yK/img.png&quot; data-alt=&quot;올해 그렸던 view들... 자식 같고 소중하다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bO8puK/btsCEJfcIKO/KzglvIewc6goKEnaGKf3yK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbO8puK%2FbtsCEJfcIKO%2FKzglvIewc6goKEnaGKf3yK%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;1946&quot; height=&quot;1002&quot; data-origin-width=&quot;1946&quot; data-origin-height=&quot;1002&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;올해 그렸던 view들... 자식 같고 소중하다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1946&quot; data-origin-height=&quot;994&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dEnY2j/btsCF62mLRD/uWtXRK9GkweEPe4fksRsZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dEnY2j/btsCF62mLRD/uWtXRK9GkweEPe4fksRsZ0/img.png&quot; data-alt=&quot;즐거웠던 순간들&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dEnY2j/btsCF62mLRD/uWtXRK9GkweEPe4fksRsZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdEnY2j%2FbtsCF62mLRD%2FuWtXRK9GkweEPe4fksRsZ0%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;1946&quot; height=&quot;994&quot; data-origin-width=&quot;1946&quot; data-origin-height=&quot;994&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;즐거웠던 순간들&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>비전공자용 노력</category>
      <category>2023</category>
      <category>2024</category>
      <category>회고</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/84</guid>
      <comments>https://applecider2020.tistory.com/84#entry84comment</comments>
      <pubDate>Sun, 24 Dec 2023 23:08:26 +0900</pubDate>
    </item>
    <item>
      <title>Count down 타이머 만들기 - 2개 날짜 비교 (feat. Localization)</title>
      <link>https://applecider2020.tistory.com/83</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;앱으로 쇼핑을 하다 보면 구매를 부추기는 장치가 여럿 마련되어 있는데&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;그중 하나가 &lt;b&gt;Time Deal&lt;/b&gt; (특정 기간 동안 할인), &lt;b&gt;Flash Sale&lt;/b&gt; (짧은 기간동안 한정 수량을 할인) 등의 이벤트이고&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;일반적으로 1초마다 시간을 업데이트하는 &lt;b&gt;카운트 다운&amp;nbsp;타이머&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;할인이 곧 끝나니 얼른 지르라는 은근한 압박을 주는데&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;이런 할인에 전혀 영향을 받지 않는 사용자도 있겠지만&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;UI/UX 관점에서 &lt;b&gt;초 단위의 숫자가 계속 바뀌어서 시선이 집중되는 효과&lt;/b&gt;가 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;그래서 Home, PDP, 할인전 화면 등에서 주로 노출한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 355px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Amazon&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;SHEIN&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;쿠팡&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;오늘의집&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 338px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 338px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Amazon.gif&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;1084&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkcm5Q/btsBC31Sn6r/oNW0hAo8S3ZRxckl5LQsg1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkcm5Q/btsBC31Sn6r/oNW0hAo8S3ZRxckl5LQsg1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkcm5Q/btsBC31Sn6r/oNW0hAo8S3ZRxckl5LQsg1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bkcm5Q/btsBC31Sn6r/oNW0hAo8S3ZRxckl5LQsg1/img.gif&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;500&quot; height=&quot;1084&quot; data-filename=&quot;Amazon.gif&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;1084&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 338px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;SHEIN.gif&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;1084&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjz2fe/btsBF1aTCga/WLKiVl5K6HV76HNIS9yRFK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjz2fe/btsBF1aTCga/WLKiVl5K6HV76HNIS9yRFK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjz2fe/btsBF1aTCga/WLKiVl5K6HV76HNIS9yRFK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bjz2fe/btsBF1aTCga/WLKiVl5K6HV76HNIS9yRFK/img.gif&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;500&quot; height=&quot;1084&quot; data-filename=&quot;SHEIN.gif&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;1084&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 338px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;쿠팡.gif&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;1084&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Aw0Ip/btsBHsZRKFD/tKKJxrgOkqS3lc2cXeSAN0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Aw0Ip/btsBHsZRKFD/tKKJxrgOkqS3lc2cXeSAN0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Aw0Ip/btsBHsZRKFD/tKKJxrgOkqS3lc2cXeSAN0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/Aw0Ip/btsBHsZRKFD/tKKJxrgOkqS3lc2cXeSAN0/img.gif&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;500&quot; height=&quot;1084&quot; data-filename=&quot;쿠팡.gif&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;1084&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 338px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;오늘의집.gif&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;1084&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uq0GT/btsBCn0vwgN/55X8hlHiZMhQKxDTrOFnDK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uq0GT/btsBCn0vwgN/55X8hlHiZMhQKxDTrOFnDK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uq0GT/btsBCn0vwgN/55X8hlHiZMhQKxDTrOFnDK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/uq0GT/btsBCn0vwgN/55X8hlHiZMhQKxDTrOFnDK/img.gif&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;500&quot; height=&quot;1084&quot; data-filename=&quot;오늘의집.gif&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;1084&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이번 포스팅에서는 이러한 Count down 타이머를 구현해보고자 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;먼저 요구사항을 정리해보고, 단계별로 만들어 보자.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;TODO:&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. 남은 할인기간 계산하기&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. Label에 원하는 format으로 시간 표시하기&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3. Timer를 통해 1초마다 Label 업데이트하기&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;4. 추가적인 개선사항&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. 남은 할인기간 계산하기&amp;nbsp;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;Time Deal은 할인 종료시점이 정해져 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;이번 예제에서는 &lt;b&gt;2023년 12월 31일 24시 &lt;/b&gt;(&lt;span style=&quot;text-align: start;&quot;&gt;한국 timeZone 기준&lt;/span&gt;)에 할인이 끝나고, &lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;글로벌 서비스라면 종료시점을&lt;/span&gt; &lt;a href=&quot;https://ko.wikipedia.org/wiki/ISO_8601&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;ISO8601&lt;/a&gt; &lt;span style=&quot;color: #333333;&quot;&gt;format이 적용된&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;&lt;b&gt;2024-01-01T00:00:00+09:00&lt;/b&gt;&amp;nbsp;문자열을 서버로부터 받게 될 것이다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;TimeManager 싱글턴 객체를 만들고,&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;현재 시각에서 할인 종료시점까지 남은 시간을 구해보자.&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;3가지 키 포인트만 알면 쉽다!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;기본적으로 2개의 시 간을 비교할 때 &lt;/span&gt;&lt;b&gt;Calendar&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;의 dateComponents 메서드를 활용한다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;그러려면 시간 문자열을&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Date&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; 타입으로 변환해야 한다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;이때 변환을 위해 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;b&gt;ISO8601DateFormatter&lt;/b&gt;이 필요하다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1702085145111&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;final class FlashSaleManager { 
    // ✅ calendar를 사용하여 시간 비교
    private let calendar = Calendar.autoupdatingCurrent
    
    // ✅ 서버에서 받아온 시간 문자열을 Date 타입으로 변환하기 위한 formatter
    private let iso8601Formatter: ISO8601DateFormatter = {
        let formatter = ISO8601DateFormatter()
        // &quot;yyyy-MM-dd'T'HH:mm:ssZZZZZ&quot; (ex. 2023-12-31T15:00:00+00:00)
        formatter.formatOptions = [.withInternetDateTime]
        return formatter
    }()

    private func date(from dateText: String) -&amp;gt; Date? {
        return iso8601Formatter.date(from: dateText)
    }

    // ✅ 남은 시간을 구하기 (매개변수에 서버 문자열 전달)
    func remainTime(to dateText: String) -&amp;gt; DateComponents? {
        guard let date2 = date(from: dateText) else { return nil }
        
        let date1 = Date() // 현재 시각
        return calendar.dateComponents([.day, .hour, .minute, .second], from: date1, to: date2)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;시간을 표현할 format을 설정할 때 dateFormatter를 만드는데 (ex. &quot;AM 3:04&quot;, &quot;Apr 28&quot;, &quot;Friday, Apr 28, 2023&quot; 등) &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;이와 별개로 서버에서 받은 시간을 변환하는 용도로&amp;nbsp;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;ISO8601DateFormatter&lt;/span&gt;를 만들어두면 편리하다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;서버에서 받은 종료시점 (&lt;span style=&quot;background-color: #dddddd;&quot;&gt;date2&lt;/span&gt;)을 I&lt;span style=&quot;text-align: start;&quot;&gt;SO8601DateFormatter를 활용하여 &lt;/span&gt;Date 타입으로 변환한다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;현재 시각 (&lt;span style=&quot;background-color: #dddddd;&quot;&gt;date1&lt;/span&gt;)은 Date()로 구한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;마지막으로 calendar의 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;dateComponents&lt;/span&gt; 메서드를 활용하여 2개 시간을 비교한다.&lt;br /&gt;이때 비교할 시간 단위를 설정할 수 있는데, 이번에는 '일/시/분/초' 단위로 지정했다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. Label에 원하는 format으로 시간 표시하기&amp;nbsp;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2개 시간을 비교한 결과를 Label에 표시해보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이번 예시에서는 아래처럼 &quot;Ends in &lt;b&gt;dd:hh:mm:ss&lt;/b&gt;&quot; 형태로 표현해봤다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;타이머1.gif&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;1385&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dMzwQw/btsBDOwu7Fo/CZj2I09Kqj236eCx1fRWb1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dMzwQw/btsBDOwu7Fo/CZj2I09Kqj236eCx1fRWb1/img.gif&quot; data-alt=&quot;참 쉽죠&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dMzwQw/btsBDOwu7Fo/CZj2I09Kqj236eCx1fRWb1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/dMzwQw/btsBDOwu7Fo/CZj2I09Kqj236eCx1fRWb1/img.gif&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;866&quot; data-filename=&quot;타이머1.gif&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;1385&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;참 쉽죠&lt;/figcaption&gt;
&lt;/figure&gt;
&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;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;방금 위에서 만들었던 remainTime 메서드에 로직을 추가했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;일/시/분/초&lt;/span&gt;&amp;nbsp;단위의 시간 차이를 dateComponents 타입으로 만들었으니&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;각각을 &lt;b&gt;두 자리의 숫자&lt;/b&gt;로 표시해주면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(최근 개발된 API를 활용할 수 있겠지만 귀찮아서.. 일단.. 이렇게..)&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1702089811450&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func formattedRemainTime(to dateText: String) -&amp;gt; String? {
    guard let date2 = date(from: dateText) else { return nil }

    let date1 = Date()
    let countdownComponents = calendar.dateComponents([.day, .hour, .minute, .second], from: date1, to: date2)

    guard let days = countdownComponents.day,
          let hours = countdownComponents.hour,
          let minutes = countdownComponents.minute,
          let seconds = countdownComponents.second else { return nil }

    // ✅ 두 자리 숫자로 표시
    return String(format: &quot;%02d:%02d:%02d:%02d&quot;, days, hours, minutes, seconds)
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3. Timer를 통해 1초마다 Label 업데이트하기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이제 카운트 다운을 하기 위해 Timer를 설정해주자.&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1702090315903&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;final class FlashSaleUnitView: UIView {
    private var timer: Timer?
    private lazy var timeBadgeView: TimeBadgeView = createTimeBadgeView()
    
    // ✅ 타이머 설정
    func startTimer() {
        if timer?.isValid != true {
            let timer = Timer(timeInterval: 1, repeats: true) { [weak self] _ in
                self?.changeTimeText()
            }
            self.timer = timer
            RunLoop.main.add(timer, forMode: .common)
        }
    }
    
    // ✅ Label에 남은 시간 텍스트 반영
    private func changeTimeText() {
        // 서버에서 받은 종료시점 &quot;2024-01-01T00:00:00+09:00&quot;
        guard let dueDateText = viewModel.saleDueDate, 
              let remainTimeText = FlashSaleManager.shared.formattedRemainTime(to: dueDateText) else { return }
        
        timeBadgeView.setTime(remainTimeText)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;타이머를 통해 1초마다 텍스트를 바꿔주면 된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;유저가 화면 스크롤할 때도 텍스트가 바뀌도록 main run loop에서 timer가 실행되도록 설정했다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;서버에서 받은 할인종료 시점 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;saleDueDate&lt;/span&gt;을 위에서 만든 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;formattedRemainTime(to:)&lt;/span&gt;에 전달해서 남은 기간을 알아내고,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;Label에 반영하면 끝이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;다른 화면으로 이동했을 때 timer를 정지시키고, 다시 재진입했을 때 실행시키는 로직도 잊지 말고 넣어주자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;timerDelegate를 통해 viewController의 viewWillDisappear/viewWillAppear 시점에 timerStop/timerStart를 호출하도록 했다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1702091064744&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;deinit { // view deinit 시점에 호출
    stopTimer()
}

func stopTimer() { // viewController viewWillDisappear 시점에 호출
    if let timer,
       timer.isValid {
        timer.invalidate()
    }
}&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  &lt;span style=&quot;color: #333333;&quot;&gt;참고) 여기까지 따라왔다면 한 가지 의문이 들 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;'최초 한 번만 남은 시간을 계산하고, 이후에는 Timer로 -1초씩 해주면 되는 거 아닌가?'&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;그래서 찾아봤는데 카운트 다운할 때 &lt;b&gt;Timer는 부정확하기 때문에 Date를 써야 한다고&lt;/b&gt; 한다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;SoF &amp;gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://stackoverflow.com/questions/3519562/how-do-i-write-a-timer-in-objective-c/3519913#3519913&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Don't use NSTimer that way. NSTimer is normally used to fire a selector at some time interval. It isn't high precision (...)&lt;/a&gt;&amp;nbsp;&lt;/span&gt;&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;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;GCD Timer 등 정확도 높은 timer를 만드는 방법도 찾았지만.. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;Apple Technical Note에서도 &lt;b&gt;비용이 크고, 단 1개 timer만 정확도를 높일 수 있기 때문에 지양&lt;/b&gt;하는 방식이라고 명시하고 있어서 패스했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(정확도 높은 timer 만들려면 Real time thread라는 녀석한테 일을 시켜야 하는데, 이거 과부하되면 다 망한다는 내용.. 아마도..)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;SoF &amp;gt; &lt;a href=&quot;https://stackoverflow.com/questions/43746336/high-precision-timer-in-swift&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;high precision timer&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple/TN2169 &amp;gt; &lt;a href=&quot;https://developer.apple.com/library/archive/technotes/tn2169/_index.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Don't&amp;nbsp;use&amp;nbsp;a&amp;nbsp;high&amp;nbsp;precision&amp;nbsp;timer&amp;nbsp;unless&amp;nbsp;you&amp;nbsp;really&amp;nbsp;need&amp;nbsp;it.&amp;nbsp;They&amp;nbsp;consume&amp;nbsp;compute&amp;nbsp;cycles&amp;nbsp;and&amp;nbsp;battery. There&amp;nbsp;can&amp;nbsp;only&amp;nbsp;be&amp;nbsp;a&amp;nbsp;limited&amp;nbsp;number&amp;nbsp;of&amp;nbsp;high&amp;nbsp;precsion&amp;nbsp;timers&amp;nbsp;active&amp;nbsp;at&amp;nbsp;once.&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple/Dispatch Source &amp;gt; &lt;a href=&quot;https://developer.apple.com/documentation/dispatch/1385606-dispatch_source_set_timer&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Note&amp;nbsp;that&amp;nbsp;some&amp;nbsp;latency&amp;nbsp;is&amp;nbsp;to&amp;nbsp;be&amp;nbsp;expected&amp;nbsp;for&amp;nbsp;all&amp;nbsp;timers,&amp;nbsp;even&amp;nbsp;when&amp;nbsp;a&amp;nbsp;leeway&amp;nbsp;value&amp;nbsp;of&amp;nbsp;zero&amp;nbsp;is&amp;nbsp;specified.&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;4. 추가적인 개선사항&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;1) Label 너비 고정시키기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;이 상태로도 좋지만 개선점을 찾아낸 분들이 있을 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;개인적으로 숫자가 바뀔 때마다 Label 너비가 늘었다 줄었다하는 게... 눈에 너무 거슬렸다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;늘줄늘줄.gif&quot; data-origin-width=&quot;608&quot; data-origin-height=&quot;102&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lzi7M/btsBJaxRCvQ/0gAgclf60ELcs59Sty8tv1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lzi7M/btsBJaxRCvQ/0gAgclf60ELcs59Sty8tv1/img.gif&quot; data-alt=&quot;늘었다가 줄었다가 하는 Label&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lzi7M/btsBJaxRCvQ/0gAgclf60ELcs59Sty8tv1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/lzi7M/btsBJaxRCvQ/0gAgclf60ELcs59Sty8tv1/img.gif&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;67&quot; data-filename=&quot;늘줄늘줄.gif&quot; data-origin-width=&quot;608&quot; data-origin-height=&quot;102&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;늘었다가 줄었다가 하는 Label&lt;/figcaption&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;아래처럼 숫자마다 텍스트 너비가 다르기 때문에 발생하는 문제이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;518&quot; data-origin-height=&quot;158&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yjR58/btsBGjoWdCI/57KHTql3LFLrmWgt1Ykfk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yjR58/btsBGjoWdCI/57KHTql3LFLrmWgt1Ykfk0/img.png&quot; data-alt=&quot;최대 8 / 최소 1&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yjR58/btsBGjoWdCI/57KHTql3LFLrmWgt1Ykfk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyjR58%2FbtsBGjoWdCI%2F57KHTql3LFLrmWgt1Ykfk0%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;400&quot; height=&quot;122&quot; data-origin-width=&quot;518&quot; data-origin-height=&quot;158&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;최대 8 / 최소 1&lt;/figcaption&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;다른 앱을 벤치마킹해봤을 때&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;이번 예시처럼 backgroundColor를 적용한 경우에는 너비를 고정하는 게 디자인상 깔끔하다고 판단해서 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;최대 너비를 지정하는 방법으로 개선해봤다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1702092104361&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// view 그릴 때
// ✅ 최대 너비인 숫자 텍스트를 활용
let maxWidthText = &quot;88:48:48:48&quot;
let fixedWidth = maxWidthText.width(font: .systemFont(ofSize: 13, weight: .bold)).ceiling(to: 0)

addSubview(timeLabel)
timeLabel.snp.makeConstraints { make in
    make.width.equalTo(fixedWidth)
}

// extension String
func width(font: UIFont) -&amp;gt; CGFloat {
    let fontAttributes = [NSAttributedString.Key.font: font]
    let size = self.size(withAttributes: fontAttributes)
    return size.width
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;숫자가&amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&quot;88:48:48:48&quot;&lt;/span&gt;일 때 텍스트 너비가 최대가 되기 때문에 이 값을 활용했다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;텍스트 중에는 88이 최대너비이고, 시간을 표현하는 숫자 중에서는 48이 가장 너비가 크므로 &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;일에는 88, 시/분/초에는 각각 48을 넣어서 텍스트 너비를 구했다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;이제 아래처럼 깔끔하다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;완성2.gif&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;1385&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPmc81/btsBDQA7ibH/NXNH7mLP82XGm2PhKdLyqK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPmc81/btsBDQA7ibH/NXNH7mLP82XGm2PhKdLyqK/img.gif&quot; data-alt=&quot;너비 고정 버전&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPmc81/btsBDQA7ibH/NXNH7mLP82XGm2PhKdLyqK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bPmc81/btsBDQA7ibH/NXNH7mLP82XGm2PhKdLyqK/img.gif&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;866&quot; data-filename=&quot;완성2.gif&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;1385&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;너비 고정 버전&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt; &lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2) 시간 초과된 케이스 처리하기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그리고 현재 시각이 할인 종료기간을 초과했을 때 어떻게 대응할지 고려해보면 좋다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;아마 할인기간이 종료되면 해당 Unit이 노출되지 않도록 서버에서 처리하겠지만&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;서버에서 잘못 내려준다면 숫자에 -가 붙게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;143&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQxIzD/btsBGjJid1O/4q8O8bTL4QkPVkiBkxYi1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQxIzD/btsBGjJid1O/4q8O8bTL4QkPVkiBkxYi1K/img.png&quot; data-alt=&quot;서버 고장났을 때&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQxIzD/btsBGjJid1O/4q8O8bTL4QkPVkiBkxYi1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQxIzD%2FbtsBGjJid1O%2F4q8O8bTL4QkPVkiBkxYi1K%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;400&quot; height=&quot;49&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;143&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;서버 고장났을 때&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;아래처럼 클라이언트 로직에도 방어코드를 추가하면 기부니가 조커든요. ☺️&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;141&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHH1IS/btsBF2APfjq/MoJRUbTS6mOdgD3krzbGD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHH1IS/btsBF2APfjq/MoJRUbTS6mOdgD3krzbGD1/img.png&quot; data-alt=&quot;방어코드 있을 때&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHH1IS/btsBF2APfjq/MoJRUbTS6mOdgD3krzbGD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHH1IS%2FbtsBF2APfjq%2FMoJRUbTS6mOdgD3krzbGD1%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;400&quot; height=&quot;48&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;141&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;방어코드 있을 때&lt;/figcaption&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;위에서 만든 formattedRemainTime(to:) 메서드에서 isExpired 여부를 bool 타입으로 반환하도록 개선해주면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1702092960877&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    func formattedRemainTime(to dateText: String) -&amp;gt; (Bool, String)? {
        guard let date2 = date(from: dateText) else { return nil }
        
        let date1 = Date()
        // ✅ 종료시점 초과 여부를 판단
        let isExpired = calendar.compare(date1, to: date2, toGranularity: .second) == .orderedDescending
        let countdownComponents = calendar.dateComponents([.day, .hour, .minute, .second], from: date1, to: date2)
        
        guard let days = countdownComponents.day,
              let hours = countdownComponents.hour,
              let minutes = countdownComponents.minute,
              let seconds = countdownComponents.second else { return nil }
        
        // ✅ 종료된 경우 보여줄 텍스트 지정
        let remainTimeText = isExpired ? &quot;00:00:00:00&quot; : String(format: &quot;%02d:%02d:%02d:%02d&quot;, days, hours, minutes, seconds)
        
        return (isExpired, remainTimeText)
    }&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;그리고 formattedRemainTime(to:) 메서드를 호출하는 시점에서&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;isExpired인 경우 timer를 종료하도록 추가해주자.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1702093104174&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private func changeTimeText() {
    // 서버에서 받은 종료시점 &quot;2024-01-01T00:00:00+09:00&quot;
    guard let dueDateText = viewModel.saleDueDate, 
          let remainTimeText = FlashSaleManager.shared.formattedRemainTime(to: dueDateText) else { return }

    timeBadgeView.setTime(remainTimeText)

    // ✅ isExpired 이면 타이머 종료
    if isExpired {
        stopTimer()
    } 
}&lt;/code&gt;&lt;/pre&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;2개의 날짜를 비교하고, 원하는 형태로 formatting 하고&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;timer를 사용하여 Label을 업데이트하는 과정을 거쳐서 Countdown Timer를 만들어봤다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;로직 자체는 복잡하지 않으니 여러 가지 다른 디자인으로 만들어보면 재미있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;카운트 다운할 때 애니메이션 효과를 넣는 코드도 구경해보기를 추천한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;GitHub &amp;gt; &lt;a href=&quot;https://github.com/lexrus/LTMorphingLabel&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;LTMorphingLabel&lt;/a&gt; &lt;span style=&quot;color: #9d9d9d;&quot;&gt;(Custom Label 장인..)&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Blog &amp;gt; NamS &amp;gt; &lt;a href=&quot;https://nsios.tistory.com/123&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;CountDown&amp;nbsp;Animation&amp;nbsp;Label&amp;nbsp;구현&lt;/a&gt;&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;- Reference &lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple Developer Documentation &amp;gt; Calendar &amp;gt; &lt;a href=&quot;https://developer.apple.com/documentation/foundation/calendar/2293166-compare&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;compare(_:to:toGranularity:)&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple Developer Documentation &amp;gt; Calendar &amp;gt; &lt;a href=&quot;https://developer.apple.com/documentation/foundation/calendar/2293176-datecomponents&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;dateComponents(_:from:to:)&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;Apple Archive &amp;gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;Technical Note TN2169 /&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;text-align: left;&quot; href=&quot;https://developer.apple.com/library/archive/technotes/tn2169/_index.html&quot;&gt;High Precision Timers in iOS / OS X&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;SoF &amp;gt; &lt;a href=&quot;https://stackoverflow.com/questions/29374553/how-can-i-make-a-countdown-with-nstimer&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;How&amp;nbsp;can&amp;nbsp;I&amp;nbsp;make&amp;nbsp;a&amp;nbsp;countdown&amp;nbsp;with&amp;nbsp;NSTimer?&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;SoF &amp;gt; &lt;a href=&quot;https://stackoverflow.com/questions/30582967/how-to-make-a-countdown-to-date-swift&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;How to make a countdown to date&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;SoF &amp;gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;text-align: start;&quot; href=&quot;https://stackoverflow.com/questions/43746336/high-precision-timer-in-swift&quot;&gt;high precision timer&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;SoF &amp;gt; &lt;a href=&quot;https://stackoverflow.com/questions/45120492/how-do-i-achieve-very-accurate-timing-in-swift&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;How&amp;nbsp;do&amp;nbsp;I&amp;nbsp;achieve&amp;nbsp;very&amp;nbsp;accurate&amp;nbsp;timing&amp;nbsp;in&amp;nbsp;Swift?&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Blog&amp;nbsp;&amp;gt;&amp;nbsp;&lt;a href=&quot;https://medium.com/ios-os-x-development/build-an-stopwatch-with-swift-3-0-c7040818a10f&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Build&amp;nbsp;a&amp;nbsp;count&amp;nbsp;down&amp;nbsp;timer&amp;nbsp;with&amp;nbsp;Swift&amp;nbsp;3.0&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>iOS</category>
      <category>calendar</category>
      <category>countdown</category>
      <category>FlashSale</category>
      <category>TimeDeal</category>
      <category>timer</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/83</guid>
      <comments>https://applecider2020.tistory.com/83#entry83comment</comments>
      <pubDate>Sat, 9 Dec 2023 14:12:11 +0900</pubDate>
    </item>
    <item>
      <title>[컨퍼런스] KWDC23 연사 참여 후기: 자료조사는 시작일 뿐</title>
      <link>https://applecider2020.tistory.com/82</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;최근 코엑스에서 1,300여 명이 참석한 국내 최대 규모의 &lt;b&gt;애플 생태계 컨퍼런스 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://kwdc.dev/&quot;&gt;KWDC23&lt;/a&gt;&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;가 개최됐다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Apple WWDC의 공식 후속행사 (&lt;a href=&quot;https://developer.apple.com/kr/wwdc23/beyond-wwdc/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;WWDC Extra Event&lt;/a&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 style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;이번에 감사하게도 좋은 기회가 주어져서&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;Localization을 주제로 발표를 했는데,&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;b&gt;발표를 하게 된 계기와 발표과정에서 배운 것들&lt;/b&gt;을 남겨봤다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;나중에 발표를 준비하시는 분들에게 참고가 되었으면 한다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;1. 발표 계기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;5월의 어느 날 KWDC 준비위원회로부터 &lt;b&gt;스피커 제안&lt;/b&gt;을 받았다.&amp;nbsp;&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;입사 후 10개월 간 &lt;b&gt;글로벌 앱을 개발&lt;/b&gt;하면서 배운 게 정말 많았지만,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;s&gt;쪼렙&lt;/s&gt; 1년차 개발자인데.. 내 &lt;b&gt;미천한 지식&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;그런데 제안서를 살펴보니&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;개발자뿐만 아니라 디자이너, 기획자 등 &lt;/span&gt;Apple 생태계의 구성원이 참여하는 행사이고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;기술/스킬에 포커스된 세션, 경험 공유 세션 등 &lt;b&gt;발표 영역/주제가 다양하다&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;몇 가지 &lt;b&gt;희망 발표 주제&lt;/b&gt;를 정해서 스피커 신청서를 제출했고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;아래는 당시의 내적 갈등&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;  운좋게 발표할 기회가 오다니. 한번 해보자!&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;  발표 망할거면 안 하는 게 낫다. 조용히 살려고 개발자로 전직한 거 아니었어?&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;  만약 망하더라도 이번 발표를 통해 성장할 수 있을거야&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;  컨퍼런스 처음인데 발표 망한 개발자로 낙인찍힐래?&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;  발표 내용이 한 사람에게라도 도움이 된다면 뿌듯할 거야&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;  글로벌 앱 만들려는 사람들은 이미 다 알고 있는 내용이지 않을까?&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt; &amp;nbsp;시간 많으니까 열심히 준비하면 돼&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;2. 준비과정&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;발표 주제 탐색&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;글로벌 프로젝트를 하면서 많은 걸 경험했었다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;처음에는 &lt;b&gt;채팅 기능&lt;/b&gt;을 개발하면서 배웠던 Up scroll pagination, 소켓 통신 등을 다루려고 했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;그런데 관련 내용이 WWDC23에서 거의 언급되지 않았다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;(KWDC23이라는 네이밍에 걸맞게 WWDC23의 내용을 커버해야 한다는 조건이 있었다.)&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;그래서 결국&amp;nbsp;&lt;b&gt;Localization&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;글로벌 앱을 개발하면서 가장 심혈을 기울였던 부분은 아래와 같았다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;사용자의 Locale 및 TimeZone에 맞게 서비스 (배송가능 상품, 배송비 등)와 데이터 (날짜/시간, 번역어 등)를 제공하고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;Locale에 따라 변경이 잦은 데이터를 Server-driven으로 관리하고,&lt;br /&gt;사용자의 Region은 싱가포르이지만, 베트남 친구에게 선물을 보내는 케이스에 대응하는 등등&lt;br /&gt;그래서&amp;nbsp;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;처음에는&amp;nbsp;&lt;b&gt;개발자를 타겟으로&lt;/b&gt;&lt;/span&gt;&amp;nbsp;&lt;b&gt;글로벌 비즈니스에서 발생할 수 있는 문제 상황과 해결 방법&lt;/b&gt;을 소개하려고 계획했다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;고민한 점&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그런데 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;예상과 달리...&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;WWDC 20-23&lt;/b&gt;을 공부해 보니&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Localization 관련 기술들이 너무 다양했고,&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;프로젝트에 활용한 건 아주 일부였음을 알게 됐다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;그리고 해당 기술을 설명하기 위해서는 &lt;/span&gt;&lt;b&gt;여러 언어에 대한 배경지식&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예를 들어 WWDC23 (&lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2023/10153/&quot; data-token-index=&quot;0&quot;&gt;Unlock the power of grammatical agreement&lt;/a&gt;)에서 &lt;b&gt;Term of address&lt;/b&gt;라는 개념이 언급됐는데, &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;스페인어&lt;/b&gt;처럼 성별을 구분하는 언어에 대한 이해도가 약간 있어야 했고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;WWDC21 (&lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2021/10109/&quot; data-token-index=&quot;0&quot;&gt;What&amp;rsquo;s new in Foundation&lt;/a&gt;)을 봐야 왜 Automatic grammar agreement API가 도입됐는지 제대로 이해할 수 있었다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;또 마찬가지로 애플이 왜 stringdict 파일을 만들었는지, &lt;b&gt;Pluralization&lt;/b&gt;이 뭔지 설명하려면 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;우크라이나어&lt;/b&gt;의 특성을 이해해야 했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이런 게 한두 가지가 아니었다. 기절할 만큼 많았다. ㅇ&amp;lt;-&amp;lt;&lt;/span&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-origin-width=&quot;2136&quot; data-origin-height=&quot;936&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJTUFU/btsoDIXeigF/p6QVdGCW5V8BAzP3fk6bCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJTUFU/btsoDIXeigF/p6QVdGCW5V8BAzP3fk6bCk/img.png&quot; data-alt=&quot;자료조사하면서 올렸던 인스타 스토리&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJTUFU/btsoDIXeigF/p6QVdGCW5V8BAzP3fk6bCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJTUFU%2FbtsoDIXeigF%2Fp6QVdGCW5V8BAzP3fk6bCk%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;2136&quot; height=&quot;936&quot; data-origin-width=&quot;2136&quot; data-origin-height=&quot;936&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;자료조사하면서 올렸던 인스타 스토리&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그래서...&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;Localization을 처음 접해본 사람도 알아들을 수 있는 수준&lt;/b&gt;으로 타겟을 변경했고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이 발표를 보고 난 뒤에 &lt;b&gt;WWDC를 들었을 때 쉽게 이해할 수 있도록 도움을 주는 것&lt;/b&gt;을 목표로 삼게 됐다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그래서 발표 초반부에 Locale, TimeZone, Calendar에 대한 &lt;b&gt;기초적인 개념 설명&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;7월 초에는 &lt;b&gt;온라인 리허설&lt;/b&gt;을 진행했는데&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;KWDC 준비위원회분들이 구체적이고 따뜻한 피드백을 주셔서 멘탈 관리에 큰 힘이 됐다. ㅎㅎ&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그렇게 탄생한 발표 목차&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2030&quot; data-origin-height=&quot;1136&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lVMkf/btsoRElhHhd/ifiHBPSTH4OERyoD1rQ7MK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lVMkf/btsoRElhHhd/ifiHBPSTH4OERyoD1rQ7MK/img.png&quot; data-alt=&quot;Localization 발표 목차&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lVMkf/btsoRElhHhd/ifiHBPSTH4OERyoD1rQ7MK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlVMkf%2FbtsoRElhHhd%2FifiHBPSTH4OERyoD1rQ7MK%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;600&quot; height=&quot;336&quot; data-origin-width=&quot;2030&quot; data-origin-height=&quot;1136&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Localization 발표 목차&lt;/figcaption&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;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그리고 발표 제목은 &lt;b&gt;글로벌&amp;nbsp;앱을&amp;nbsp;위한&amp;nbsp;Localization&lt;/b&gt;이었는데&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;다른 연사자분들이 제목을 힙하게 지으시길래 ㅎㅎ&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;뒤에 &lt;b&gt;부제&lt;/b&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 style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그렇게 탄생한 발표 제목&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2034&quot; data-origin-height=&quot;1140&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l61oY/btsoTqNBd5i/PCBIUbxmkwoavaBBrOYL6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l61oY/btsoTqNBd5i/PCBIUbxmkwoavaBBrOYL6k/img.png&quot; data-alt=&quot;Localization 발표 제목&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l61oY/btsoTqNBd5i/PCBIUbxmkwoavaBBrOYL6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl61oY%2FbtsoTqNBd5i%2FPCBIUbxmkwoavaBBrOYL6k%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;600&quot; height=&quot;336&quot; data-origin-width=&quot;2034&quot; data-origin-height=&quot;1140&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Localization 발표 제목&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;후회하는 점&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;발표를 끝낸 시점에서 아쉬움이 남는 건 &lt;b&gt;자료 조사에 너무 많은 시간을 쏟았다&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;초반에는 개발자를 타겟으로 했기 때문에 &lt;b&gt;기술적인 내용을 철저히 준비하려 했고&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Localization과 관련된 거라면 뭐든 탈탈 털어서 거의 모든 걸 다 봤다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;WWDC20/21/22/23 15편 이상, 관련 공식문서를 전부 (Archive까지 싹 다)...&lt;/b&gt; 봤다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;주말과 저녁시간을 모두 발표 준비에 쏟을 수밖에 없었다. &lt;span style=&quot;color: #333333; text-align: start;&quot;&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1021&quot; data-origin-height=&quot;570&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d1AI59/btso9gKCFLN/WcVHieUP3u9BDQCikAnKd1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d1AI59/btso9gKCFLN/WcVHieUP3u9BDQCikAnKd1/img.png&quot; data-alt=&quot;참고자료의 일부...&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d1AI59/btso9gKCFLN/WcVHieUP3u9BDQCikAnKd1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd1AI59%2Fbtso9gKCFLN%2FWcVHieUP3u9BDQCikAnKd1%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;1021&quot; height=&quot;570&quot; data-origin-width=&quot;1021&quot; data-origin-height=&quot;570&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;참고자료의 일부...&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;발표 방향을 제대로 설정한 뒤에 자료 조사를 했으면 좋았을 텐데&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;중간에 방향을 한 번 틀게 되면서&lt;/b&gt; 후반부에 시간이 좀 부족했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;결과적으로 준비했던 기술적인 내용의 25% 정도 밖에 발표자료에 담지 못했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;시간이 더 있었다면... 내용 구성 자체를 더 고민해보거나, 글로벌 앱 사례를 보완할 수 있었을 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3. D-1 리허설&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;발표 전 날에는 코엑스에 가서&lt;b&gt; 현장 리허설&lt;/b&gt;을 했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;강당 규모가 가장 컸던 명동 홀에서 연습을 했는데, &lt;/span&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: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;강렬한 핀 조명을 받으니 눈이 부셔서 객석이 잘 안보였고... 그래서 오히려 좋기도 했다. (?)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/J1nrb/btsoQtkikzH/1wexXlTXWuYbnnHevCBjtk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/J1nrb/btsoQtkikzH/1wexXlTXWuYbnnHevCBjtk/img.png&quot; data-origin-width=&quot;3861&quot; data-origin-height=&quot;2895&quot; data-is-animation=&quot;false&quot; style=&quot;width: 56.4847%; margin-right: 10px;&quot; data-widthpercent=&quot;57.15&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/J1nrb/btsoQtkikzH/1wexXlTXWuYbnnHevCBjtk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJ1nrb%2FbtsoQtkikzH%2F1wexXlTXWuYbnnHevCBjtk%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;3861&quot; height=&quot;2895&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BpYUj/btsoULjHkgt/hGqwvJHOv8DnZkupGgXZH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BpYUj/btsoULjHkgt/hGqwvJHOv8DnZkupGgXZH1/img.png&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;3024&quot; data-is-animation=&quot;false&quot; style=&quot;width: 42.3525%;&quot; data-widthpercent=&quot;42.85&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BpYUj/btsoULjHkgt/hGqwvJHOv8DnZkupGgXZH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBpYUj%2FbtsoULjHkgt%2FhGqwvJHOv8DnZkupGgXZH1%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;3024&quot; height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;아렉스가 찍어주신 리허설 현장&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;4. D-DAY&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;발표 시간은 &lt;b&gt;오전 11시&lt;/b&gt;였는데, 그래서 정말 다행이었다고 생각한다.&lt;/span&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: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;리허설을 할 때까지도 '국내에 글로벌 비즈니스를 하려는 회사가 많지 않을텐데 몇 명 안 오면 어쩌지?' 하며 전전긍긍했는데&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그런 걱정이 무색하게 &lt;b&gt;현장의 열기&lt;/b&gt;는 뜨거웠다.  &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;발표 20분 전부터 강당 앞에 줄을 서는 분들이 생겼고, 발표를 시작할 때 이미 객석이 꽉 차있었다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이때부터 긴장이 엄청 되기 시작했고, &lt;/span&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: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;근데 강단에 섰을 때 머리가 하얘져서 KWDC23 연사자로 참여하게 되어 영광이고 감사하다는 인사말도 빼먹어버렸다. &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt; &amp;lt;-&amp;lt;&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;발표를 시작한 뒤에는 스크린에 띄운&amp;nbsp;&lt;/span&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: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;'이걸 왜 찍으시지? 이 내용이 도움이 되긴 하는구나?'&lt;/b&gt; 싶어서 긴장이 풀렸다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bw155x/btsoTreJ1iP/lMEOi1UL7WMEKHP2pg4hMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bw155x/btsoTreJ1iP/lMEOi1UL7WMEKHP2pg4hMK/img.png&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;654&quot; data-is-animation=&quot;false&quot; style=&quot;width: 66.7342%; margin-right: 10px;&quot; data-widthpercent=&quot;67.52&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bw155x/btsoTreJ1iP/lMEOi1UL7WMEKHP2pg4hMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbw155x%2FbtsoTreJ1iP%2FlMEOi1UL7WMEKHP2pg4hMK%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;1170&quot; height=&quot;654&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xGjCt/btsoReApg5A/L4jNJHaH0ynwpEKtZ008w0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xGjCt/btsoReApg5A/L4jNJHaH0ynwpEKtZ008w0/img.png&quot; data-origin-width=&quot;2982&quot; data-origin-height=&quot;3465&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.103%;&quot; data-widthpercent=&quot;32.48&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xGjCt/btsoReApg5A/L4jNJHaH0ynwpEKtZ008w0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxGjCt%2FbtsoReApg5A%2FL4jNJHaH0ynwpEKtZ008w0%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;2982&quot; height=&quot;3465&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;핀 조명에 사라진 이목구비 / 전리품&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;발표를 끝낸 뒤에는 부트캠프 동기들, 일하며 알게 된 분들, 선생님들을 만나며 반갑고 기쁜 시간을 보냈다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;특히 연사자 대기실에서 평소 팬이었던 멋진 블로거분들을 많이 뵈었는데&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;연예인 목격한 것처럼 설레고 신기했고 ㅎㅎ 이번 기회로 함께 할 수 있어서 즐거웠다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;행사장에서 마주쳤을 때&amp;nbsp;&lt;b&gt;내 블로그 글을 잘 읽고 있다고..!&lt;/b&gt; 얘기해주는 분들이 계셔서 약간 부끄럽고 많이 뿌듯했다.  &lt;/span&gt;&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 data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;하지만 &lt;b&gt;슈퍼파워 I&lt;/b&gt;인 나는 너무 많은 사람을 한꺼번에 보게 되어 정신이 없었다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;진이 빠져서 부스를 한 군데도 못 갔고, 발표자 뒤풀이에도 참석하지 못해서 아쉬움이 남는다. ㅎㅎ&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;5. Thanks to&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이 자리를 빌어 고마움을 전하고 싶은 분들이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;먼저&amp;nbsp;&lt;a href=&quot;https://medium.com/@Jager-yoo&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;예거&lt;/a&gt;와 &lt;a href=&quot;https://ho8487.tistory.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;호댕&lt;/a&gt;, 작고 소중한 주말시간을 내서 꼼꼼히 발표 피드백을 해주셨고, 자료를 갈아엎으며 방황할 때 많은 도움을 주셨다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예거는 키노트 전문가여서 애니메이션 특강까지 해주셔서 큰 도움이 됐다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이외에도 행사장에서 만나거나 따로 연락해서 응원해준 부캠 동기들 덕분에 뭉클했고 많은 힘을 받았다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;또 &lt;a href=&quot;https://babbab2.tistory.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;소들님&lt;/a&gt;&amp;nbsp;&lt;span style=&quot;color: #9d9d9d;&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: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그리고 발표자료에 아이디어스 글로벌 앱 사례를 어떻게 녹일지 함께 고민해 주신 셀 리더님,&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;휴가 중에 말없이 행사장에 와서 발표를 들으시고, 주변에서 들은 호평을 전해주며 응원해주신 전-셀 리더님,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예상 못했는데 맨 앞줄에 ㅋㅋㅋ 달려와 앉아서 발표 내내 큰 대답과 호응을 해준 따뜻하고 든든한 우리 iOS셀원들,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;공부하느라 바쁜 시기에 초대에 응해주고 맨 앞에서 응원해준 빠키,&amp;nbsp;&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;큰 규모의 애플 생태계 컨퍼런스를 만들기 위해 &lt;/span&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;다른 모든 스피커분들과 준비위원회, 자원봉사자분들께도 정말 감사하고 &lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;함께 할 수 있어서 영광이었다는 말씀을 전하고 싶다.  &amp;nbsp;&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 style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;마지막으로 이 모든 것을 &lt;span style=&quot;color: #333333; text-align: start;&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: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;애플 생태계에 발들일 수 있도록 노베이스 비전공자를 iOS 개발자로 키워주신 야곰에게 다시 무한히 감사함을 느꼈다.  &lt;/span&gt;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;준비과정이 수월하지만은 않아서 멘탈이 나갈 때도 있었지만&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;특정 일자에 특정 지식을 공유한다는 명확한 목표&lt;/b&gt;가 있어서 스터디를 하는데 제대로 &lt;b&gt;동기부여&lt;/b&gt;가 됐기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그리고 무엇보다, 부족한 점이 많은 발표였음에도 발표를 들어주신 분들의 &lt;b&gt;지지와 응원&lt;/b&gt;을 들을 수 있어서 좋았다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;발표를 준비하며 정리한 자료와 QnA 내용은 블로그에 천천히 포스팅할 계획이다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://kwdc.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;KWDC23 공식 웹&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple - &lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://developer.apple.com/kr/wwdc23/beyond-wwdc/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;WWDC Extra Event&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple - WWDC23 &amp;gt; &lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://developer.apple.com/videos/play/wwdc2023/10153/&quot; data-token-index=&quot;0&quot;&gt;Unlock the power of grammatical agreement&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple - WWDC21 &amp;gt; &lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://developer.apple.com/videos/play/wwdc2021/10109/&quot; data-token-index=&quot;0&quot;&gt;What&amp;rsquo;s new in Foundation&lt;/a&gt;&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>iOS</category>
      <category>Conference</category>
      <category>KWDC</category>
      <category>KWDC23</category>
      <category>WWDC23</category>
      <category>발표</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/82</guid>
      <comments>https://applecider2020.tistory.com/82#entry82comment</comments>
      <pubDate>Thu, 27 Jul 2023 00:59:52 +0900</pubDate>
    </item>
    <item>
      <title>[디자인 패턴] Prototype - 의존성 없이 객체를 복사할 때</title>
      <link>https://applecider2020.tistory.com/81</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;GoF 디자인 패턴 중 하나인 Prototype 패턴을 정리했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-pm-slice=&quot;0 0 []&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Ref :&amp;nbsp;&lt;a href=&quot;https://www.goodreads.com/book/show/43125355-dive-into-design-patterns&quot;&gt;도서 &amp;lt;Dive into Design Patterns&amp;gt;,&amp;nbsp;Alexander Shvets 저&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 id=&quot;문제-상황&quot; data-pm-slice=&quot;0 0 []&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;문제 상황&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체 A가 있고, 해당 객체의 복사본을 만들고 싶은 상황이다.&lt;/li&gt;
&lt;li&gt;이때 새로운 객체 인스턴스를 생성하고, 원본 (객체 A)의 프로퍼티 값들을 새 객체에 모두 &lt;b&gt;복사&lt;/b&gt;하는 방법이 있다.&lt;/li&gt;
&lt;li&gt;문제점 : 객체의 프로퍼티 중 일부가 &lt;b&gt;비공개&lt;/b&gt;라면 외부에서 접근할 수 없다. 또한 복사하려면 원본을 알아야 하므로 해당 &lt;b&gt;객체에 대한 의존성&lt;/b&gt;이 생긴다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;패턴-설명&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;패턴 설명&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;요약 : &lt;/span&gt;&lt;u data-renderer-mark=&quot;true&quot;&gt;의존성 없이 객체를 복사&lt;/u&gt;하는 생성 패턴이다.&lt;/li&gt;
&lt;li&gt;Prototype = 시제품, 시험용으로 만들어 본 샘플 제품
&lt;ul style=&quot;list-style-type: circle;&quot; data-indent-level=&quot;2&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;실제 산업의 프로토타입 : 대량 생산 이전에 테스트용으로 만드는 샘플 제품&lt;/li&gt;
&lt;li&gt;프로그래밍의 프로토타입 : 세포 분열과 비슷함. &amp;lsquo;원본 세포&amp;rsquo;는 복사본을 만들어내기 위한 &amp;lsquo;프로토타입 역할&amp;rsquo;을 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;복제를 지원하는 객체&lt;/b&gt;를 &amp;lsquo;프로토타입&amp;rsquo;이라고 부른다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-indent-level=&quot;2&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;매번 객체를 처음부터 새로 만들지 않는다.&lt;/li&gt;
&lt;li&gt;&amp;lsquo;복제되는 객체&amp;rsquo;에게 복제 프로세스를 위임한다.&lt;b&gt; &lt;/b&gt;(설정값을 지정하여 미리 만들어둔 프로토타입을 복제해서 쓴다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;830&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cf4lOn/btsnGOppL9m/iHLwSw5qQ3Q1tYkyEAUjhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cf4lOn/btsnGOppL9m/iHLwSw5qQ3Q1tYkyEAUjhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cf4lOn/btsnGOppL9m/iHLwSw5qQ3Q1tYkyEAUjhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcf4lOn%2FbtsnGOppL9m%2FiHLwSw5qQ3Q1tYkyEAUjhk%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;700&quot; height=&quot;415&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;830&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-indent-level=&quot;1&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Prototype Protocol : clone 메서드를 정의한다. (반환타입은 Prototype Protocol 자기 자신이다.)&lt;/li&gt;
&lt;li&gt;ConcretePrototype : clone 메서드를 구현한다. (원본 객체의 프로퍼티값을 복사한다.)&lt;/li&gt;
&lt;li&gt;Client : Prototype Protocol을 채택한 모든 객체의 복사본을 생성 가능하다.&lt;/li&gt;
&lt;/ol&gt;
&lt;div style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-layout=&quot;center&quot; data-width=&quot;81&quot; data-node-type=&quot;mediaSingle&quot;&gt;
&lt;div&gt;
&lt;div data-context-id=&quot;1333004173&quot; data-type=&quot;file&quot; data-node-type=&quot;media&quot; data-width=&quot;778&quot; data-height=&quot;461&quot; data-id=&quot;26faa2bb-8235-40e6-acf2-fc81165cc032&quot; data-collection=&quot;contentId-1333004173&quot; data-file-name=&quot;image-20230213-121357.png&quot; data-file-size=&quot;131556&quot; data-file-mime-type=&quot;image/png&quot; data-alt=&quot;&quot;&gt;
&lt;div id=&quot;newFileExperienceWrapper&quot; data-testid=&quot;media-card-view&quot;&gt;
&lt;div data-testid=&quot;media-file-card-view&quot; data-test-status=&quot;complete&quot; data-test-media-name=&quot;image-20230213-121357.png&quot; data-test-progress=&quot;1&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예시 코드&lt;/span&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1689511285450&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// MARK: - Prototype Protocol
protocol Prototype {
    init(prototype: Self)
}
extension Prototype {
    func clone() -&amp;gt; Self {
        return type(of: self).init(prototype: self)
    }
}

// MARK: - Concrete Prototype
class ConcretePrototype1: Prototype {
    var name: String
    var weight: Double
    
    init(name: String, weight: Double) {
        self.name = name
        self.weight = weight
    }
    
    required convenience init(prototype: ConcretePrototype1) {
        self.init(name: prototype.name, weight: prototype.weight)
    }

    func printStatus() {
        print(&quot;ConcretePrototype1 - name \(name) / weight \(weight)&quot;)
    }
}

class ConcretePrototype2: Prototype {
    var name: String
    var weight: Double
    
    init(name: String, weight: Double) {
        self.name = name
        self.weight = weight
    }
    
    required convenience init(prototype: ConcretePrototype2) {
        self.init(name: prototype.name, weight: prototype.weight)
    }

    func printStatus() {
        print(&quot;ConcretePrototype2 - name \(name) / weight \(weight)&quot;)
    }
}

// MARK: - Client
class Client {
    func main() {
        let concrete1 = ConcretePrototype1(name: &quot;con1&quot;, weight: 1.0)
        let concrete2 = ConcretePrototype2(name: &quot;con2&quot;, weight: 2.0)
        
        print(&quot;Original&quot;)
        concrete1.printStatus()
        concrete2.printStatus()
        
        let copied1 = concrete1.clone() // 완전히 새로운 객체 (원본의 참조가 복사된 게 아님)
        let copied2 = concrete2.clone()
        
        print(&quot;Clone&quot;)
        copied1.printStatus()
        copied2.printStatus()
        
        print(&quot;---&quot;)
        copied1.weight += 10.0  // 원본에 영향을 미치지 않음
        concrete1.printStatus() // 1
        copied1.printStatus()   // 11
    }
}

//Original
//ConcretePrototype1 - name con1 / weight 1.0
//ConcretePrototype2 - name con2 / weight 2.0

//Clone
//ConcretePrototype1 - name con1 / weight 1.0
//ConcretePrototype2 - name con2 / weight 2.0
//---
//ConcretePrototype1 - name con1 / weight 1.0
//ConcretePrototype1 - name con1 / weight 11.0&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;장점&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;장점&lt;/span&gt;&lt;/h2&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;중복되는 initializer 코드를 제거할 수 있다. (만들어 둔 프로토타입을 복제해서 쓰면 되므로)&lt;/li&gt;
&lt;li&gt;복잡한 객체를 쉽게 생성한다.&lt;/li&gt;
&lt;li&gt;복잡한 객체에 대한 설정값을 처리할 때 상속 대신 사용 가능한 방법이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;단점&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;단점&lt;/span&gt;&lt;/h2&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;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference&lt;/span&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;a style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot; href=&quot;https://www.goodreads.com/book/show/43125355-dive-into-design-patterns&quot;&gt;도서 &amp;lt;Dive into Design Patterns&amp;gt;, Alexander Shvets 저&lt;/a&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Blog &amp;gt; &lt;a href=&quot;https://icksw.tistory.com/238&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;dev_pingu - Prototype Pattern&lt;/a&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;&lt;span style=&quot;background-color: #ffffff; color: #555555; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>프로그래밍 철학</category>
      <category>Prototype</category>
      <category>디자인패턴</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/81</guid>
      <comments>https://applecider2020.tistory.com/81#entry81comment</comments>
      <pubDate>Sun, 16 Jul 2023 21:42:35 +0900</pubDate>
    </item>
    <item>
      <title>[디자인 패턴] Command - 실행할 작업을 덩어리로 관리할 때</title>
      <link>https://applecider2020.tistory.com/80</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;GoF 디자인 패턴 중 하나인 Command&amp;nbsp;패턴을 정리했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-pm-slice=&quot;0 0 []&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Ref :&amp;nbsp;&lt;a href=&quot;https://www.goodreads.com/book/show/43125355-dive-into-design-patterns&quot;&gt;도서 &amp;lt;Dive into Design Patterns&amp;gt;,&amp;nbsp;Alexander Shvets 저&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 id=&quot;문제-상황&quot; style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-renderer-start-pos=&quot;305&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;문제 상황&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-indent-level=&quot;1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Text Editor 프로그램을 개발하는 중이며, 텍스트를 저장할 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;Save Button&lt;/span&gt;을 만들었다. &lt;br /&gt;Button을 상속받아 자식클래스로 Save Button을 구현한 뒤, save 기능을 추가했다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그런데 Save Button 뿐만 아니라 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;Ctrl+C 단축키&lt;/span&gt;로도 복사를 하고 싶고, &lt;br /&gt;텍스트와 서식을 동시에 복사할 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;Super Save Button&lt;/span&gt;을 추가하고 싶어졌다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;기존 방식으로 하면 버튼/단축키에 &lt;b&gt;중복 코드&lt;/b&gt;가 생기고, &lt;b&gt;자식클래스 종류가 너무 많아질 수 있다&lt;/b&gt;는 문제점이 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;패턴-설명&quot; style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;패턴 설명&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-indent-level=&quot;1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Command의 사전적 정의는 &amp;ldquo;명령, 명령어&amp;rdquo;이다. &lt;br /&gt;Command 패턴에서는 &lt;b&gt;실행할 작업을 캡슐화&lt;/b&gt;하여 하나의 덩어리로 취급한다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;즉, 실행할 명령 (ex. SaveCommand)을 별도 타입으로 정의해 두고,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;필요한 곳 (ex. SaveButton, SaveShortcut)에 해당 &lt;b&gt;명령 덩어리를 주입&lt;/b&gt;시켜서 활용한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;관심사 분리의 원칙에 따라 &lt;b&gt;UI 로직&lt;/b&gt;과 &lt;b&gt;비즈니스 로직&lt;/b&gt;을 분리하는게 좋다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Command 객체는 UI 객체와 비즈니스 로직 객체 사이의 &lt;b&gt;연결고리&lt;/b&gt; 역할을 한다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;덕분에 UI 객체가 비즈니스 로직에게 직접 작업을 요청하지 않는다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Command 패턴은 아래 5가지 역할로 구성된다. &lt;br /&gt;언뜻 복잡해보이지만 예시코드를 먼저 보면 쉽게 이해할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;670&quot; data-origin-height=&quot;377&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kIFGd/btsl0UMDSfN/Ojc1RTMSJgjtk3CjzfD8h0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kIFGd/btsl0UMDSfN/Ojc1RTMSJgjtk3CjzfD8h0/img.png&quot; data-alt=&quot;Command 패턴의 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kIFGd/btsl0UMDSfN/Ojc1RTMSJgjtk3CjzfD8h0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkIFGd%2Fbtsl0UMDSfN%2FOjc1RTMSJgjtk3CjzfD8h0%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;660&quot; height=&quot;371&quot; data-origin-width=&quot;670&quot; data-origin-height=&quot;377&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Command 패턴의 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-indent-level=&quot;1&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;Invoker&lt;/b&gt; (발송자) : 커맨드에게 일을 시킨다. 커맨드 객체의 참조를 저장할 프로퍼티를 가진다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;Command 인터페이스&lt;/b&gt; : 단일 메서드 execute()를 선언한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;Concrete Command&lt;/b&gt; : 일을 구현한다. Concrete Command를 초기화할 때, &lt;br /&gt;Receiver 자체 (또는 Receiver의 메서드를 실행하는 데 필요한 매개변수)를 전달받아 프로퍼티로 가진다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;Receiver&lt;/b&gt; (수신자) : 실제로 작업이 실행되는 곳이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;Client&lt;/b&gt; : Concrete Command를 초기화한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;예시-코드&quot; style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-renderer-start-pos=&quot;1080&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;예시 코드&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1688305546041&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Command
protocol Command {
    func execute()
}

// Concrete Command
class SaveCommand: Command {
    var textEditor: TextEditor
    
    init(textEditor: TextEditor) {
        self.textEditor = textEditor
    }
    
    func execute() {
        // 복사 기능을 구현
        let currentText = textEditor.text
        textEditor.clipboard = currentText
        print(&quot;Text Copied!&quot;)
    }
}

// Invoker - 커맨드에게 일을 시킴
class SaveButton {
    var command: Command? // 커맨드를 저장
    
    func setCommand(_ command: Command) {
        self.command = command
    }
    
    func executeCommand() {
        command?.execute()
    }
}
class SaveShortcut {
    var command: Command?

    func setCommand(_ command: Command) {
        self.command = command
    }

    func executeCommand() {
        command?.execute()
    }
}

// Receiver - 실제로 작업이 실행되는 곳
class TextEditor {
    // 가정 : 복사 기능이 실행되면 -&amp;gt; text를 clipboard에 저장함
    var text: String = &quot;&quot;
    var clipboard: String = &quot;&quot;
}

// Client
let textEditor = TextEditor() // receiver

let saveCommand = SaveCommand(textEditor: textEditor)

let saveButton = SaveButton() // invoker-1
saveButton.setCommand(saveCommand)
saveButton.executeCommand() // Text Copied! 출력

let saveShortcut = SaveShortcut() // invoker-2
saveShortcut.setCommand(saveCommand) // 재사용 가능
saveShortcut.executeCommand() // Text Copied! 출력&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-renderer-start-pos=&quot;1087&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;장점&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-indent-level=&quot;1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;단일책임 원칙을 준수한다. Receiver (TextEditor)와 Invoker (SaveButton)의 결합도가 줄어든다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;개방폐쇄 원칙을 준수한다. client 코드를 변경하지 않고도 새로운 Command를 추가할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Redo/Undo 기능, 작업 예약 기능을 쉽게 구현할 수 있다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-indent-level=&quot;2&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;SaveCommand 외에도 PasteCommand, WriteCommand 등을 만들고, &lt;br /&gt;작업을 실행할 때마다 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;Command Stack&lt;/span&gt;에 쌓아서 히스토리를 관리하면 된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;일부 데이터의 접근이 제한된 경우 (private) Memento 패턴으로 대체 가능하다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;간단한 Command를 조합해서 복잡한 Command를 만들어낼 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;단점&quot; style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-renderer-start-pos=&quot;3773&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;단점&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-indent-level=&quot;1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;코드 복잡도가 높아진다.&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference&lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/43125355-dive-into-design-patterns&quot;&gt;도서 &amp;lt;Dive into Design Patterns&amp;gt;, Alexander Shvets 저&lt;/a&gt;&lt;/span&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;&lt;span style=&quot;background-color: #ffffff; color: #555555; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>프로그래밍 철학</category>
      <category>command</category>
      <category>GOF</category>
      <category>디자인패턴</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/80</guid>
      <comments>https://applecider2020.tistory.com/80#entry80comment</comments>
      <pubDate>Sun, 2 Jul 2023 22:51:59 +0900</pubDate>
    </item>
    <item>
      <title>[toss] iOS 개발자를 위한 SLASH23 리뷰</title>
      <link>https://applecider2020.tistory.com/79</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;toss의 디자인 컨퍼런스 &lt;b&gt;SIMPLICITY23&lt;/b&gt;에 이어 개발자 컨퍼런스 &lt;a href=&quot;https://toss.im/slash-23/session&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;b&gt;SLASH23&lt;/b&gt;&lt;/a&gt;가 진행됐다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;SLASH23은 토스 개발자들의 &lt;b&gt;기술적 고민과 성취&lt;/b&gt;를 공유하는 자리이며, 총 24개 세션으로 구성되어 있다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그중 &lt;b&gt;클라이언트 개발자&lt;/b&gt;가 눈여겨 봐야 할 세션은 3가지가 있다.&lt;/span&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-origin-width=&quot;1446&quot; data-origin-height=&quot;576&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Mdxhw/btsjR76uL2D/8Drb6drW3X15ITCZSz00Uk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Mdxhw/btsjR76uL2D/8Drb6drW3X15ITCZSz00Uk/img.png&quot; data-alt=&quot;클라이언트 관련 세션 - SLASH23&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Mdxhw/btsjR76uL2D/8Drb6drW3X15ITCZSz00Uk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMdxhw%2FbtsjR76uL2D%2F8Drb6drW3X15ITCZSz00Uk%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;1446&quot; height=&quot;576&quot; data-origin-width=&quot;1446&quot; data-origin-height=&quot;576&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;클라이언트 관련 세션 - SLASH23&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote style=&quot;color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. Rally로 3분 만에 애니메이션 완성하기&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. 레고처럼 조립하는 토스 앱&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3. 누구나 쓸 수 있는 접근성 높은 토스 만들기&lt;/span&gt;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;하나씩 간단히 정리해 봤다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;나중에 언젠가... 시간이 나면 다른 세션도 정리해보고 싶다.  &lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. &lt;a href=&quot;https://www.youtube.com/watch?v=Jq4MzgzSDEE&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Rally로 3분 만에 애니메이션 완성하기&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;저번에 포스팅했던 &lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://applecider2020.tistory.com/78&quot;&gt;SIMPLICITY23 리뷰&lt;/a&gt;에서&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;첫 번째로 다뤘던&amp;nbsp;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://simplicity-23.toss.im/sessions/21&quot;&gt;인터렉션, 꼭 넣어야 해요?&lt;/a&gt;&amp;nbsp;세션과 연결되는 내용이다.&lt;/span&gt;&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;사실 대부분의 내용을 SIMPLICITY23에서 다뤘기에 크게 새로운 정보는 없었다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;디자이너 세션을 안 본 분들을 위해 요약하자면,&lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;배경 : 제품에 인터렉션을 추가하면서 생겨난 커뮤니케이션 비용을 줄이기 위해 &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Andriod/iOS/Web에서 동일한 개발 언어와 로직을 사용하는 &lt;b&gt;Rally라는&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;애니메이션 라이브러리&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;를 만들게 됐다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;작업 방법 : 디자이너가 프레이머로 인터랙션 컴포넌트를 사용해서 Motion을 만들면,&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;프레이머 &lt;b&gt;inspect에서 Rally 코드를 확인&lt;/b&gt;할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;개발자 관점에서 와닿았던 내용은 이랬다.&lt;/span&gt;&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;화면의 여러 UI가 유기적으로 움직이는 애니메이션은 개발 난이도가 높고, 가독성이 낮으므로 유지보수 비용이 증가한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;애니메이션을 관리하려면 &lt;b&gt;애니메이션을 자유롭게 컨트롤&lt;/b&gt;할 수 있어야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예를 들면 애니메이션의 &lt;b&gt;재생/역재생, 일시정지, 특정 시점으로 이동 등이 간편해야 한다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;이 부분이 대부분의 회사에서 마주하는 가장 큰 허들인 것 같다.&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;animation.gif&quot; data-origin-width=&quot;1012&quot; data-origin-height=&quot;558&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d9rIzs/btsjPIzKh7s/0jOwYD1f3oMnEq5yqWndJk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d9rIzs/btsjPIzKh7s/0jOwYD1f3oMnEq5yqWndJk/img.gif&quot; data-alt=&quot; 애니메이션 다루기 - SLASH23&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d9rIzs/btsjPIzKh7s/0jOwYD1f3oMnEq5yqWndJk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/d9rIzs/btsjPIzKh7s/0jOwYD1f3oMnEq5yqWndJk/img.gif&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;800&quot; height=&quot;441&quot; data-filename=&quot;animation.gif&quot; data-origin-width=&quot;1012&quot; data-origin-height=&quot;558&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt; 애니메이션 다루기 - SLASH23&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Rally는 &lt;b&gt;pseudo code&lt;/b&gt; 형태로 소통하고, 개발 시에는 각 플랫폼별 언어로 번역해서 사용한다고 한다.&lt;/span&gt;&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1243&quot; data-origin-height=&quot;416&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mOMzJ/btsjKTblYrS/k7FE0XkQOWdKG9LAsM4Qh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mOMzJ/btsjKTblYrS/k7FE0XkQOWdKG9LAsM4Qh1/img.png&quot; data-alt=&quot;Rally 예시 - SLASH23&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mOMzJ/btsjKTblYrS/k7FE0XkQOWdKG9LAsM4Qh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmOMzJ%2FbtsjKTblYrS%2Fk7FE0XkQOWdKG9LAsM4Qh1%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;1243&quot; height=&quot;416&quot; data-origin-width=&quot;1243&quot; data-origin-height=&quot;416&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Rally 예시 - SLASH23&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;Rally는 이렇게 구성된다.&lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;먼저 Easing (값이 시간에 따라 어떻게 변화할지)을 바탕으로 Rally의 최소 단위인 Motion (어떻게 움직일지)을 만들고,&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;재생의 최소 단위인 &lt;b&gt;Rally&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;를 통해 애니메이션을 적용할 target에 연결한 뒤,&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;Timeline&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;을 통해 여러 개의 Rally를 스케줄링하는 구조이다. &lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;자주 쓰는 설정값을 모아둔&amp;nbsp;&lt;b&gt;프리셋&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;도 있다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;애니메이션을 &lt;b&gt;컨트롤&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;하기 위한 API (play, backward.play 등)도 정의되어 있다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&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 data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이러한 &lt;b&gt;Rally의 구성요소&lt;/b&gt;는 아래처럼 저번 세션에 더 명확히 정리되어 있다.&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byf10S/btsjO01VK6W/TiOpmtRDKPkKo7z3pDDgY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byf10S/btsjO01VK6W/TiOpmtRDKPkKo7z3pDDgY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9UJUT%2FbtshCgFW3Bj%2Fcjrgzq2XKiMfVUhSdwLyk1%2Fimg.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;998&quot; data-is-animation=&quot;false&quot; style=&quot;width: 26.4024%; margin-right: 10px;&quot; data-widthpercent=&quot;27.03&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byf10S/btsjO01VK6W/TiOpmtRDKPkKo7z3pDDgY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbyf10S%2FbtsjO01VK6W%2FTiOpmtRDKPkKo7z3pDDgY0%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;1280&quot; height=&quot;998&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQFxSF/btsjO7UkiL9/GgKs0cwDkgVLBndD7JDKe0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQFxSF/btsjO7UkiL9/GgKs0cwDkgVLBndD7JDKe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdkJlta%2FbtshAwvQXmw%2FppFKyejUTIGa0BzWyfd8ek%2Fimg.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1145&quot; data-is-animation=&quot;false&quot; style=&quot;width: 23.0127%; margin-right: 10px;&quot; data-widthpercent=&quot;23.56&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQFxSF/btsjO7UkiL9/GgKs0cwDkgVLBndD7JDKe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQFxSF%2FbtsjO7UkiL9%2FGgKs0cwDkgVLBndD7JDKe0%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;1280&quot; height=&quot;1145&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Q5x1J/btsjPGokped/ksyQrjy69eRNYfvj0vIgXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Q5x1J/btsjPGokped/ksyQrjy69eRNYfvj0vIgXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLfTmh%2FbtshRxTVoKX%2FC3AEa43HJBhSNOybg588mk%2Fimg.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;546&quot; data-is-animation=&quot;false&quot; style=&quot;width: 48.2593%;&quot; data-widthpercent=&quot;49.41&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Q5x1J/btsjPGokped/ksyQrjy69eRNYfvj0vIgXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQ5x1J%2FbtsjPGokped%2FksyQrjy69eRNYfvj0vIgXk%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;1280&quot; height=&quot;546&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;Rally 구성요소 - SIMPLICITY23&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;인터렉션 디자인 시스템이 있다는 것만으로 놀라운데, 크로스 플랫폼 inspect 기능까지 있다니 너무 멋지다...&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(근데 &lt;span style=&quot;color: #9d9d9d;&quot;&gt;하나만 봐야 한다면 설명이 더 짜임새 있는&amp;nbsp;&lt;/span&gt;SIMPLICITY23의 세션을 보는 것을 추천한다.)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; 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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;*SIMPLICITY23의 해당 세션 내용이 궁금하다면?&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt; &amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://applecider2020.tistory.com/78#1.%C2%A0%EC%9D%B8%ED%84%B0%EB%A0%89%EC%85%98,-%EA%BC%AD-%EB%84%A3%EC%96%B4%EC%95%BC-%ED%95%B4%EC%9A%94?&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[toss] SIMPLICITY23 리뷰 &amp;gt; #1. 인터렉션 꼭 넣어야 해요?&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. 레고처럼 조립하는 토스 앱&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #9d9d9d;&quot;&gt;레고라는 단어를 보자마자 'RIBs' 얘기를 하겠구나! 직감했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #9d9d9d;&quot;&gt;근데 아니었음...&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;문제상황&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;앱의 규모가 커지면 모듈 분리 (앱을 여러 모듈로 나누고, 모듈 간의 구조를 설계하는 작업)이 중요하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;기존에는 아래처럼 &lt;b&gt;기능별 Layer로&lt;/b&gt;&amp;nbsp;분리되어 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Feature Layer의 모듈은 각자 하위 의&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Service Layer&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt; 모듈만을 의존하게 된다.&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;321&quot; data-origin-height=&quot;303&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cRM4jQ/btsknvTGucS/PVlbf7nCkCXZJGsfrJYuu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cRM4jQ/btsknvTGucS/PVlbf7nCkCXZJGsfrJYuu1/img.png&quot; data-alt=&quot;기존 - 기능별 Layer로 분리됨&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cRM4jQ/btsknvTGucS/PVlbf7nCkCXZJGsfrJYuu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcRM4jQ%2FbtsknvTGucS%2FPVlbf7nCkCXZJGsfrJYuu1%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;250&quot; height=&quot;236&quot; data-origin-width=&quot;321&quot; data-origin-height=&quot;303&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;기존 - 기능별 Layer로 분리됨&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;하지만 토스에 여러 기능이 추가되면서 Feature Layer에 많은 모듈이 생기고,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;모듈 간의 의존성이 복잡해지는 문제&lt;/b&gt;가 발생했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;빌드 속도가 느려지고, 순환 참조가 자주 발생&lt;/b&gt;하기도 했다.&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예를 들어 &lt;b&gt;홈에서 사용자를 인증하기 위해 홈 &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Feature가 인증&lt;/span&gt;&amp;nbsp;Feature를 의존&lt;/b&gt;하게 되어&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;동일한 Layer의 모듈 간 의존 관계가 꼬이게 된 것이다.&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;따라서 홈 Feature 및 인증 &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Feature를 사용하는 &lt;b&gt;홈 인증 Service&lt;/b&gt;를 따로 만들고,&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;두 Feature가 홈 인증 Service를 의존하도록 개선&lt;/b&gt;했지만...&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;또 다시 이로 인해 &lt;b&gt;지나치게 많은 Layer&lt;/b&gt;가 생기는 문제가 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dF7ZKw/btskrmhQioG/CKWCSIdbGobWIJnC62Kv2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dF7ZKw/btskrmhQioG/CKWCSIdbGobWIJnC62Kv2k/img.png&quot; data-origin-width=&quot;410&quot; data-origin-height=&quot;284&quot; data-is-animation=&quot;false&quot; style=&quot;width: 37.1994%; margin-right: 10px;&quot; data-widthpercent=&quot;37.64&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dF7ZKw/btskrmhQioG/CKWCSIdbGobWIJnC62Kv2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdF7ZKw%2FbtskrmhQioG%2FCKWCSIdbGobWIJnC62Kv2k%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;284&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dP817n/btsklnaz5MN/EYh0ACx0NmnR3gGUo20C5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dP817n/btsklnaz5MN/EYh0ACx0NmnR3gGUo20C5K/img.png&quot; data-origin-width=&quot;665&quot; data-origin-height=&quot;278&quot; data-is-animation=&quot;false&quot; style=&quot;width: 61.6378%;&quot; data-widthpercent=&quot;62.36&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dP817n/btsklnaz5MN/EYh0ACx0NmnR3gGUo20C5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdP817n%2Fbtsklnaz5MN%2FEYh0ACx0NmnR3gGUo20C5K%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;665&quot; height=&quot;278&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt; Feature Layer에 여전히 존재하는 의존성 문제&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Microfeatures&amp;nbsp;Architecture로의 전환&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;결론적으로 Tuist에서 제작한 모듈 구조인 &lt;b&gt;Microfeatures Architecture&lt;/b&gt;를 도입했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;모듈의 역할과 상호 관계&lt;/b&gt;는 아래와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/swHtJ/btskllX6CWl/5cTHXQxfjj8a28da4y3oh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/swHtJ/btskllX6CWl/5cTHXQxfjj8a28da4y3oh0/img.png&quot; data-origin-width=&quot;647&quot; data-origin-height=&quot;283&quot; data-is-animation=&quot;false&quot; style=&quot;width: 51.876%; margin-right: 10px;&quot; data-widthpercent=&quot;52.49&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/swHtJ/btskllX6CWl/5cTHXQxfjj8a28da4y3oh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FswHtJ%2FbtskllX6CWl%2F5cTHXQxfjj8a28da4y3oh0%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;647&quot; height=&quot;283&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6tsF8/btskhDrHVB4/bpkd52FQxZO0SONJ5vKod1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6tsF8/btskhDrHVB4/bpkd52FQxZO0SONJ5vKod1/img.png&quot; data-origin-width=&quot;981&quot; data-origin-height=&quot;474&quot; data-is-animation=&quot;false&quot; style=&quot;width: 46.9612%;&quot; data-widthpercent=&quot;47.51&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6tsF8/btskhDrHVB4/bpkd52FQxZO0SONJ5vKod1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6tsF8%2FbtskhDrHVB4%2Fbpkd52FQxZO0SONJ5vKod1%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;981&quot; height=&quot;474&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;Microfeatures Architecture의 구조 / 모듈 간 관계&lt;/figcaption&gt;
&lt;/figure&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Feature : 실제 기능을 구현한 모듈&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Interface&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&amp;nbsp;: feature 기능 중 외부에 공개할 interface&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Testing&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&amp;nbsp;: interface 모듈의 Mocking을 제공&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Tests&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&amp;nbsp;: unit test&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Example : 기능을 테스트할 수 있는 작은 단위의 앱 target (빠르게 일부만 빌드/배포 가능)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;전체 코드에 대해 새로운 Architecture로 전환하는 과정에서 &lt;b&gt;작업량에 대한 부담과 수많은 conflict&lt;/b&gt;이 걸림돌이 됐다고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;따라서 반복 작업은 Tuist의 &lt;b&gt;Template/Scaffold&lt;/b&gt; 기능을 활용했고 (template으로 쉽게 모듈을 생성),&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;iOS 전체 인원이 전환 작업에 대한 필요성을 공감하고 적극적으로 참여할 수 있도록 했다고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Microfeatures&amp;nbsp;Architecture의 장점&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;기자에서 개발자로 커리어를 전환한 과정을 담은 글 &lt;a href=&quot;https://velog.io/@eddy_song/retrospective&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;'노베이스에서 토스 합격까지'&lt;/a&gt;로 유명한&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;송범근님의 장점 소개가 이어졌다. &lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;흥미진진&lt;/i&gt;&lt;/span&gt;&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;토스 앱은 거대하지만, &lt;b&gt;피처를 개발할 때는 마치 작은 앱을 만드는 것처럼&lt;/b&gt; 작업이 가능하다고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;각 피처별 &lt;b&gt;Example 앱&lt;/b&gt;을 예로 들자면,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;첫째, Example&amp;nbsp;앱을&amp;nbsp;통해&amp;nbsp;&lt;b&gt;특정 기능의 결과 케이스&lt;/b&gt;를 모두 확인 가능하다.&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;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;개인적으로 담당한 피처에 대해 스스로 개발자 테스트를 할 때,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Charles의 Map local, Rewrite 기능을 활용해서 모든 성공/실패 케이스를 Mock 데이터를 적용해 확인해야 했는데&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이렇게&amp;nbsp;&lt;b&gt;결과 케이스를 모두를 한눈에 확인 가능하다니&lt;/b&gt;&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;297&quot; data-origin-height=&quot;502&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6knPl/btskk9pOW1L/s1ztEEnJewcSapqz9KuZck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6knPl/btskk9pOW1L/s1ztEEnJewcSapqz9KuZck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6knPl/btskk9pOW1L/s1ztEEnJewcSapqz9KuZck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6knPl%2Fbtskk9pOW1L%2Fs1ztEEnJewcSapqz9KuZck%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;300&quot; height=&quot;507&quot; data-origin-width=&quot;297&quot; data-origin-height=&quot;502&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;둘째, Example 앱은 &lt;b&gt;작은 앱 target&lt;/b&gt; 형태로 만들어지는데,&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;빌드 시간이 짧아서&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;개발 생산성을 높일 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예를 들어 송금 Example 앱을 만든다면&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;b&gt;레고를 조립하듯이&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;송금 Feature/Interface, 인증 Interface/Testing 등 필요한 부분만 가져와서 쓰면 된다.&lt;/span&gt;&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;color: #000000; text-align: start; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이때 인증 Testing에는 Mock 데이터가 들어있어서&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;실제 인증 절차를 거치지 않고 가볍게 결과물을 확인 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZNmy3/btskg8FtF3Q/1grpECKevh2ZxOSBjirsh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZNmy3/btskg8FtF3Q/1grpECKevh2ZxOSBjirsh1/img.png&quot; data-origin-width=&quot;489&quot; data-origin-height=&quot;358&quot; data-is-animation=&quot;false&quot; style=&quot;width: 39.7652%; margin-right: 10px;&quot; data-widthpercent=&quot;40.23&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZNmy3/btskg8FtF3Q/1grpECKevh2ZxOSBjirsh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZNmy3%2Fbtskg8FtF3Q%2F1grpECKevh2ZxOSBjirsh1%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;489&quot; height=&quot;358&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7edgl/btskigXn2cu/k6goiZCgSfBmCvDb5qZlE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7edgl/btskigXn2cu/k6goiZCgSfBmCvDb5qZlE1/img.png&quot; data-origin-width=&quot;976&quot; data-origin-height=&quot;481&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;59.77&quot; style=&quot;width: 59.072%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7edgl/btskigXn2cu/k6goiZCgSfBmCvDb5qZlE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7edgl%2FbtskigXn2cu%2Fk6goiZCgSfBmCvDb5qZlE1%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;976&quot; height=&quot;481&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;송금 Example 앱의 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;셋째, Example 앱을 &lt;b&gt;내부 배포&lt;/b&gt;할 수 있어서 디자이너, QA와의&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;협업 효율이 향상됐다고 한다.&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 style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Example 앱만 봐도 이렇게 많은 장점이 있다니 놀라웠다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Tuist도 듣기만 하고 실제 써보지 않았는데, &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;당장 전체 프로젝트를 개선하지는 못하더라도 하나씩 공부하며 부분 적용해 보자는 목표가 생겼다.&lt;/span&gt;&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3. 누구나 쓸 수 있는 접근성 높은 토스 만들기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://blog.toss.im/article/tinyquestions-disability-5&quot;&gt;토스가&amp;nbsp;모바일&amp;nbsp;접근성에&amp;nbsp;진심인&amp;nbsp;이유는?&lt;/a&gt;라는 글로도 문서화되어 있다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;접근성을 신경 쓰게 된 계기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;토스의 사용자가 늘어나며 접근성 개선에 대한 &lt;b&gt;사용자의 건의&lt;/b&gt;가 지속적으로 발생했는데,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;UX를 개선하고, 개발 생산성을 높이기 위해&lt;/b&gt; TDS (toss Design System) 차원에서 대응하기로 결정했다고 한다.&lt;/span&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-origin-width=&quot;952&quot; data-origin-height=&quot;302&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tIp5G/btskiTnigEM/wilaIZ6vKzMbSosmk5e4Lk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tIp5G/btskiTnigEM/wilaIZ6vKzMbSosmk5e4Lk/img.png&quot; data-alt=&quot;웹페이지 메인 - toss&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tIp5G/btskiTnigEM/wilaIZ6vKzMbSosmk5e4Lk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtIp5G%2FbtskiTnigEM%2FwilaIZ6vKzMbSosmk5e4Lk%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;952&quot; height=&quot;302&quot; data-origin-width=&quot;952&quot; data-origin-height=&quot;302&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;웹페이지 메인 - toss&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;큰 글씨 모드 대응&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;기존에 개발 난이도를 낮추기 위해 사용했던&amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;numberOfLines = 1&lt;/span&gt;을 해제하여&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;큰 글씨 모드일 때 버튼의 title  text, 목록 항목의 cell title text가 잘리지 않도록 방지했고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;UI 요소를 &lt;b&gt;재배치&lt;/b&gt;하여 컨텐츠가 들어갈 공간을 확보했다.&lt;/span&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-origin-width=&quot;1227&quot; data-origin-height=&quot;528&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dtzDlk/btskgeF0jrb/8OhWOg2lxdZev5SZupAw7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dtzDlk/btskgeF0jrb/8OhWOg2lxdZev5SZupAw7k/img.png&quot; data-alt=&quot;numberOfLines 개선 전 -&amp;amp;gt; 후&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dtzDlk/btskgeF0jrb/8OhWOg2lxdZev5SZupAw7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdtzDlk%2FbtskgeF0jrb%2F8OhWOg2lxdZev5SZupAw7k%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;1227&quot; height=&quot;528&quot; data-origin-width=&quot;1227&quot; data-origin-height=&quot;528&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;numberOfLines 개선 전 -&amp;gt; 후&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Tab Bar처럼 공간이 한정되어 있어 &lt;b&gt;Layout 배치 변경&lt;/b&gt;이 어렵다면&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;말 줄임 (&lt;span style=&quot;background-color: #dddddd;&quot;&gt;truncatingTail&lt;/span&gt;) 로직을 유지한 채, &lt;b&gt;특정 항목을 탭하면 Bottom Sheet를 띄워&lt;/b&gt; 추가 정보를 제공하는 방법으로 해결했다고 한다.&lt;/span&gt;&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;734&quot; data-origin-height=&quot;528&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bF3d82/btskgcVMKal/hPAOZKTrljsUKGN7UPucy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bF3d82/btskgcVMKal/hPAOZKTrljsUKGN7UPucy1/img.png&quot; data-alt=&quot;Bottom Sheet로 해결&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bF3d82/btskgcVMKal/hPAOZKTrljsUKGN7UPucy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbF3d82%2FbtskgcVMKal%2FhPAOZKTrljsUKGN7UPucy1%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;500&quot; height=&quot;360&quot; data-origin-width=&quot;734&quot; data-origin-height=&quot;528&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Bottom Sheet로 해결&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;또한 가독성을 위해 의도적으로 개행 (줄 바꿈)을 넣고 있는데&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1.3배 등&amp;nbsp;&lt;b&gt;특정 글자 크기에서 개행이 오히려 가독성을 떨어트리는 경우&lt;/b&gt;가 발생했다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이 경우에는&amp;nbsp;&lt;b&gt;개행 문자 (&lt;/b&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&quot;&lt;/span&gt;&lt;b&gt;)를 공백 (&lt;/b&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;b&gt;)으로 대체&lt;/b&gt;하여 개행이 되지 않게 수정했다고 한다.&lt;/span&gt;&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;733&quot; data-origin-height=&quot;527&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qu1mU/btsknvzrlsw/emgQg0K1i6lh2yvNwNungk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qu1mU/btsknvzrlsw/emgQg0K1i6lh2yvNwNungk/img.png&quot; data-alt=&quot;개행을 공백으로 개선 전 -&amp;amp;gt; 후&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qu1mU/btsknvzrlsw/emgQg0K1i6lh2yvNwNungk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fqu1mU%2Fbtsknvzrlsw%2FemgQg0K1i6lh2yvNwNungk%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;500&quot; height=&quot;359&quot; data-origin-width=&quot;733&quot; data-origin-height=&quot;527&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;개행을 공백으로 개선 전 -&amp;gt; 후&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;스크린 리더 (Voice Over)&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그다음은 Voice Over (안드로이드의 Talk Back) 기능이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아래의 2가지 개념이 있다.&lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;대체 텍스트&lt;/b&gt; : 이미지 등 시각 자료에 대한 추가 정보를 줌&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;초점&lt;/b&gt; : UI를 포커싱함 (Voice Over를 시작하면 Voice로 읽히는 영역이 차례로 하나씩 포커싱됨)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Voice Over 기능을 별도로 구현하지 않는다면, &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;시스템은 자동으로 위에서 아래, 왼쪽에서 오른쪽 방향으로 초점을 이동시키는데&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이때 &lt;b&gt;약관 list&lt;/b&gt;에서 아래와 같은 문제가 발생한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;체크 버튼&lt;/b&gt;이 어떤 항목에 대한 것인지, &lt;b&gt;화살표 버튼&lt;/b&gt;이 어떤 동작을 하는 것인지&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt; Voice만으로는 알 수가 없는 것이다.&lt;/span&gt;&lt;/span&gt;&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;937&quot; data-origin-height=&quot;471&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LLjoz/btsklpfb6s9/rvDV8BCudD9VMoroS3kIe0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LLjoz/btsklpfb6s9/rvDV8BCudD9VMoroS3kIe0/img.png&quot; data-alt=&quot;대체 텍스트 및 초점 개선 전 -&amp;amp;gt; 후&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LLjoz/btsklpfb6s9/rvDV8BCudD9VMoroS3kIe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLLjoz%2Fbtsklpfb6s9%2FrvDV8BCudD9VMoroS3kIe0%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;600&quot; height=&quot;302&quot; data-origin-width=&quot;937&quot; data-origin-height=&quot;471&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;대체 텍스트 및 초점 개선 전 -&amp;gt; 후&lt;/figcaption&gt;
&lt;/figure&gt;
&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;부트캠프 시절에는 야곰의 가르침을 따라&amp;nbsp;&lt;b&gt;Accessibility&lt;/b&gt;에 대해 심층적으로 공부했었다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;큰 글씨 모드 &lt;b&gt;(Larger Text)&lt;/b&gt;에서 StackView &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;axis&lt;/span&gt;를&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;horizontal에서&lt;/span&gt; vertical로 바꾸어&amp;nbsp;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;label 배치를&amp;nbsp;&lt;/span&gt;dynamic하게 조정하고,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;VoiceOver&lt;/b&gt;까지 구현하는 등 실제 프로젝트에 적용해봤었다.&lt;/span&gt;&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;하지만 실무에 와서는 전혀 신경쓰지 못하고 있었다...&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;Accessibility의 중요성은 알지만 비즈니스 임팩트가 큰 작업을 우선시하다보니&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;VoiceOver는 커녕&amp;nbsp;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Dynamic Font,&amp;nbsp;&lt;/span&gt;다크 모드, 가로 모드 등 기초적인 기능도 포기하고 있었다.&lt;/span&gt;&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그런데 주 1회 배포를 하며, 빠르게 피처를 만드는 토스에서 Accessibility를 챙긴다니 놀라웠다.&lt;/span&gt;&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: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;결론 : 건강하고 빠릿한 조직&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Slash의 여러 세션에서 공통적으로 느꼈던 부분인데&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;토스는 &lt;b&gt;구성원이 문제 의식에 공감한다면 빠르게 실행으로 이어지는 좋은 문화&lt;/b&gt;를 가진 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이처럼 똑똑하고 실행력 있는 개발자들의 고민과 문제해결 사례가 궁금하다면&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;toss의 SLASH23 세션을 가볍게 들어보는 것을 추천한다!&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt; 번외&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;왜 컨퍼런스 이름을 Slash로 네이밍했을까 궁금증이 들어서 ChatGPT한테 물어봤다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;답변이 그럴싸했다.&lt;/span&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-origin-width=&quot;888&quot; data-origin-height=&quot;616&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b904GU/btsjR7ZKEja/7tSFYehrzorjO5SVIYiW9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b904GU/btsjR7ZKEja/7tSFYehrzorjO5SVIYiW9K/img.png&quot; data-alt=&quot;SLASH 네이밍의 비밀 - ChatGPT&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b904GU/btsjR7ZKEja/7tSFYehrzorjO5SVIYiW9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb904GU%2FbtsjR7ZKEja%2F7tSFYehrzorjO5SVIYiW9K%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;800&quot; height=&quot;555&quot; data-origin-width=&quot;888&quot; data-origin-height=&quot;616&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SLASH 네이밍의 비밀 - ChatGPT&lt;/figcaption&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; background-color: #ffffff;&quot;&gt;지난 글&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; background-color: #ffffff;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;/span&gt;&lt;a style=&quot;background-color: #ffffff;&quot; href=&quot;https://applecider2020.tistory.com/78&quot;&gt;[toss] SIMPLICITY23&amp;nbsp;리뷰&amp;nbsp;-&amp;nbsp;프로덕트&amp;nbsp;디자이너의&amp;nbsp;고민&amp;nbsp;엿보기&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; background-color: #ffffff;&quot;&gt;toss &amp;gt; &lt;a style=&quot;background-color: #ffffff; text-align: start;&quot; href=&quot;https://toss.im/slash-23/session&quot;&gt;SLASH23&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; background-color: #ffffff;&quot;&gt;&lt;a style=&quot;background-color: #ffffff;&quot; href=&quot;https://tuist.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Tuist.io&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; background-color: #ffffff;&quot;&gt;toss feed &amp;gt; &lt;a style=&quot;background-color: #ffffff; text-align: start;&quot; href=&quot;https://blog.toss.im/article/tinyquestions-disability-5&quot;&gt;토스가&amp;nbsp;모바일&amp;nbsp;접근성에&amp;nbsp;진심인&amp;nbsp;이유는?&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; background-color: #ffffff;&quot;&gt;Blog &amp;gt; &lt;span style=&quot;text-align: start;&quot;&gt;&lt;/span&gt;&lt;a style=&quot;text-align: start; background-color: #ffffff;&quot; href=&quot;https://velog.io/@eddy_song/retrospective&quot;&gt;'노베이스에서 토스 합격까지'&lt;/a&gt;&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>프로그래밍 철학</category>
      <category>Conference</category>
      <category>IOS</category>
      <category>Microfeature Architecture</category>
      <category>slash23</category>
      <category>TOSS</category>
      <category>인터렉션</category>
      <category>접근성</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/79</guid>
      <comments>https://applecider2020.tistory.com/79#entry79comment</comments>
      <pubDate>Sun, 18 Jun 2023 23:57:26 +0900</pubDate>
    </item>
    <item>
      <title>[toss] iOS 개발자를 위한 SIMPLICITY23 리뷰 - 디자이너와 친해지기</title>
      <link>https://applecider2020.tistory.com/78</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;toss &lt;a href=&quot;https://simplicity-23.toss.im/all&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;SIMPLICITY23&lt;/a&gt;의 세션을 온라인으로 구경하고 재밌었던 4개 세션을 가져와 봤다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;SIMPLICITY23은 토스의 2023년 &lt;b&gt;디자인 컨퍼런스&lt;/b&gt;이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;토스가 UX 기획에 가장 중요하게 생각하는 원칙인 &lt;b&gt;단순함&lt;/b&gt; (Simplicity)을 따서 네이밍했다고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;크게 5개 카테고리, 총 23개 세션으로 구성되어 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;333&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FJLrR/btsiFOtzSan/WKQWcbwUzfFyzNwOhZhb2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FJLrR/btsiFOtzSan/WKQWcbwUzfFyzNwOhZhb2K/img.png&quot; data-alt=&quot;SIMPLICITY23 - toss&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FJLrR/btsiFOtzSan/WKQWcbwUzfFyzNwOhZhb2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFJLrR%2FbtsiFOtzSan%2FWKQWcbwUzfFyzNwOhZhb2K%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;1280&quot; height=&quot;333&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;333&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SIMPLICITY23 - toss&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;개발자가 디자인 컨퍼런스에 왜 참여해야 할까?&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;디자인팀의 경험과 노하우를 공유하는 자리인데,&amp;nbsp;개발자가 참여할 필요가 있을까?&lt;/span&gt;&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;개인적으로 개발자는 디자이너와 긴밀하게 협업하는 위치이므로&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;디자이너가 마주하는 어려움과 고민을 이해하는 것&lt;/b&gt;이 중요하다고 생각한다.&lt;/span&gt;&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그리고 주로 프로덕트 디자이너에 대한 이야기지만 개발자에게도 도움되는 내용도 많다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;장애물을 만났을 때 어떤 마인드셋을 가졌는지, 문제해결을 위해 구성원을 어떻게 설득했는지 등&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;일잘러의 태도&lt;/b&gt;도 살펴볼 수 있어서 좋았다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;소감부터 먼저 말하자면&lt;/span&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;토스의 저 세상 문화&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;처음에는 사실 이런 의문을 가졌다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;'토스가 애플도 아닌데 왜 이런 세션을 열까? 왜 토스만의 고급 정보를 굳이 공개할까?'&amp;nbsp;&lt;/span&gt;&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그런데 세션을 보고 나니 알겠다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;토스에서 토스 문화를 누리면서 일해보고 싶다&lt;/b&gt;는 강력한 동기부여를 받았다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&quot;문화가 복지다. 유능한 동료가 복지다.&quot;라는 말이 잘 맞는 회사인 것 같다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이렇게 회사 내부적인 이야기를 밖에다가 해도 되나 싶을 정도로&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;구체적인 문제해결 사례와 토스만의 디자인 원칙&lt;/b&gt;을 볼 수 있어서 좋았다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;심지어 성공 사례뿐만 아니라 &lt;b&gt;실패 사례&lt;/b&gt;까지 들을 수 있었는데, &lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;실패를 허용하고 적극적으로 시도하는 문화가 정말 좋아 보였다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;혁신 그 잡채&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;게다가 컨퍼런스의 내용도 좋았지만,&lt;b&gt; 형식&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;세션은 디자이너를 인터뷰하는 방식으로 진행되는데&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;유튜브의 수많은 인터뷰 영상처럼 두 사람을 투 샷으로 잡고, 아래에 자막을 까는 식으로 만든 게 아니라&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;둘의 대화를 &lt;b&gt;interactive 자막&lt;/b&gt;으로 보여주는&lt;b&gt; 팟캐스트&lt;/b&gt; 형태로 진행됐는데&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;내용에 몰입하기 좋았고, 모바일 화면도 같이 나와서 쉽게 이해할 수 있었다. &lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;기획자 천재 아닌가?&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1915&quot; data-origin-height=&quot;578&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KqCz9/btshMXk1w9H/6OWjvAsIXXsFv78GP3eb3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KqCz9/btshMXk1w9H/6OWjvAsIXXsFv78GP3eb3k/img.png&quot; data-alt=&quot;팟캐스트 형식 - SIMPLICITY23&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KqCz9/btshMXk1w9H/6OWjvAsIXXsFv78GP3eb3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKqCz9%2FbtshMXk1w9H%2F6OWjvAsIXXsFv78GP3eb3k%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;1915&quot; height=&quot;578&quot; data-origin-width=&quot;1915&quot; data-origin-height=&quot;578&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;팟캐스트 형식 - SIMPLICITY23&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&quot;프로젝트를 마친 시점에서&amp;nbsp;&lt;/span&gt;과거의 자신에게 조언을 해준다면?&quot;이라는 질문도 신선했다.&lt;/span&gt;&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1.&amp;nbsp;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://simplicity-23.toss.im/sessions/21&quot;&gt;인터렉션, 꼭 넣어야 해요?&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;문제상황&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;제품에 인터렉션을 넣으면 UX가 좋아진다는 건 모두 알지만, &lt;b&gt;구현 비용&lt;/b&gt; 때문에 엄두가 나지 않는 게 현실이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;토스도 인터렉션 디자이너팀은 사실상 작년에 만들어졌고, 대부분의 화면이 정적이다 보니&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&quot;구현이 어렵다, 모션이 개발 공수 대비 임팩트가 없을 것 같다&quot;... 등의 부정적인 인식이 많았다고 한다.&lt;/span&gt;&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;b&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;*인터렉션 디자인&lt;/span&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;이 궁금하다면?&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;toss 블로그에&lt;/span&gt; &lt;a href=&quot;https://blog.toss.im/article/why-motion-in-finance&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;모션, 금융을 즐겁고 쉬운 경험으로 만드는 방법&lt;/a&gt; &lt;span style=&quot;color: #9d9d9d;&quot;&gt;포스팅으로 구체적인 내용을 더 읽을 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;해결방법&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;인터렉션은 단순히 심미적인 기능 (예뻐 보이기만 하는 것!) 뿐만 아니라&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;애플/구글의 디자인처럼 &lt;b&gt;사용자에게 더 명확한 피드백을 줄 수 있다는 장점&lt;/b&gt;이 있는데,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이것을 디자이너가 구성원들에게 적극 어필했다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;apple.gif&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;327&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7Ie4f/btshWuWLq9F/Isf8rYPKLY2XNMOOWccDN0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7Ie4f/btshWuWLq9F/Isf8rYPKLY2XNMOOWccDN0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7Ie4f/btshWuWLq9F/Isf8rYPKLY2XNMOOWccDN0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/7Ie4f/btshWuWLq9F/Isf8rYPKLY2XNMOOWccDN0/img.gif&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;800&quot; height=&quot;327&quot; data-filename=&quot;apple.gif&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;327&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;첫째로&lt;b&gt; 인터렉션으로 지표를 개선하는 사례&lt;/b&gt;를 만들었고, 실제로 이탈률이 감소한 것을 확인했다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;loan.gif&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;307&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yGZ5l/btshTvVWqGz/4GGh6OPXujVBDVZfUb5PR1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yGZ5l/btshTvVWqGz/4GGh6OPXujVBDVZfUb5PR1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yGZ5l/btshTvVWqGz/4GGh6OPXujVBDVZfUb5PR1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/yGZ5l/btshTvVWqGz/4GGh6OPXujVBDVZfUb5PR1/img.gif&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;800&quot; height=&quot;307&quot; data-filename=&quot;loan.gif&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;307&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그리고 앱 전체에 영향을 미치는 기능인 &lt;b&gt;버튼의 터치 이펙트&lt;/b&gt;를 다듬었다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예를 들어 버튼이 눌리는 느낌이 나도록 모션을 만든 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;interaction2.gif&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;307&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cOXBbb/btshE1BIGyG/8SUkaoGFZa2xb3Ua79TQT0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cOXBbb/btshE1BIGyG/8SUkaoGFZa2xb3Ua79TQT0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cOXBbb/btshE1BIGyG/8SUkaoGFZa2xb3Ua79TQT0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cOXBbb/btshE1BIGyG/8SUkaoGFZa2xb3Ua79TQT0/img.gif&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;800&quot; height=&quot;307&quot; data-filename=&quot;interaction2.gif&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;307&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;둘째로 &lt;b&gt;공수를 최소화&lt;/b&gt;했다.&lt;/span&gt;&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;실제 CollectionView에 모션을 주는 게 아니라 &lt;b&gt;CollectionView 위에 새 레이어를 올리고 모션을 적용&lt;/b&gt;해서 한 화면인 것처럼 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;눈속임 모션&lt;/b&gt;으로 공수를 줄였다.&lt;/span&gt;&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;인터뷰이가 안드로이드의&amp;nbsp;&lt;a style=&quot;color: #9d9d9d;&quot; href=&quot;https://developer.android.com/design&quot;&gt;Material 가이드&lt;/a&gt;&amp;nbsp;(= iOS의 HIG 같은 문서)를 통해 이런 아이디어를 생각하게 됐다는 계기까지 공유해 주셨다.   천재인데 천사임...&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;안드로이드 쪽에는 관심이 없었는데 나중에 Material 가이드와 HIG를 비교하는 글을 써보면 재밌을 것 같다.&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그다음 &lt;b&gt;인터렉션을 재사용&lt;/b&gt;하여 공수를 줄였다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;여기서 놀라운 점은 '디자인 시스템'과 비슷하게 '인터렉션 시스템'을 만들었다는 것이다. &lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;진짜 대단하다...  &lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;인터렉션 시스템.gif&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;333&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wtgLi/btshE9l5tOE/HnwsHBPY3K6XDcLR0BYXKK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wtgLi/btshE9l5tOE/HnwsHBPY3K6XDcLR0BYXKK/img.gif&quot; data-alt=&quot;간지 그 잡채, 인터렉션 시스템 - toss&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wtgLi/btshE9l5tOE/HnwsHBPY3K6XDcLR0BYXKK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/wtgLi/btshE9l5tOE/HnwsHBPY3K6XDcLR0BYXKK/img.gif&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;800&quot; height=&quot;333&quot; data-filename=&quot;인터렉션 시스템.gif&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;333&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;간지 그 잡채, 인터렉션 시스템 - toss&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;근데 또 여기서 문제는 iOS, 안드로이드, 웹의 용어나 동작 방식이 모두 달랐던 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그래서 각 플랫폼별 담당자가 모두 모여서 &lt;b&gt;플랫폼 공통 스펙&lt;/b&gt;을 만들었다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;복잡한 모션을 표현할 수 있는 공통의 언어를 만든 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1915&quot; data-origin-height=&quot;802&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1C6JW/btshMWma11r/AMDkKwU23bE16rs459dKS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1C6JW/btshMWma11r/AMDkKwU23bE16rs459dKS1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1C6JW/btshMWma11r/AMDkKwU23bE16rs459dKS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1C6JW%2FbtshMWma11r%2FAMDkKwU23bE16rs459dKS1%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;800&quot; height=&quot;335&quot; data-origin-width=&quot;1915&quot; data-origin-height=&quot;802&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9UJUT/btshCgFW3Bj/cjrgzq2XKiMfVUhSdwLyk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9UJUT/btshCgFW3Bj/cjrgzq2XKiMfVUhSdwLyk1/img.png&quot; style=&quot;width: 26.4237%; margin-right: 10px;&quot; data-origin-width=&quot;1580&quot; data-origin-height=&quot;1232&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;27.05&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9UJUT/btshCgFW3Bj/cjrgzq2XKiMfVUhSdwLyk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9UJUT%2FbtshCgFW3Bj%2Fcjrgzq2XKiMfVUhSdwLyk1%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;1580&quot; height=&quot;1232&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dkJlta/btshAwvQXmw/ppFKyejUTIGa0BzWyfd8ek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dkJlta/btshAwvQXmw/ppFKyejUTIGa0BzWyfd8ek/img.png&quot; style=&quot;width: 23.0226%; margin-right: 10px;&quot; data-origin-width=&quot;1580&quot; data-origin-height=&quot;1414&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;23.57&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dkJlta/btshAwvQXmw/ppFKyejUTIGa0BzWyfd8ek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdkJlta%2FbtshAwvQXmw%2FppFKyejUTIGa0BzWyfd8ek%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;1580&quot; height=&quot;1414&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LfTmh/btshRxTVoKX/C3AEa43HJBhSNOybg588mk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LfTmh/btshRxTVoKX/C3AEa43HJBhSNOybg588mk/img.png&quot; style=&quot;width: 48.2281%;&quot; data-origin-width=&quot;1580&quot; data-origin-height=&quot;675&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;49.38&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LfTmh/btshRxTVoKX/C3AEa43HJBhSNOybg588mk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLfTmh%2FbtshRxTVoKX%2FC3AEa43HJBhSNOybg588mk%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;1580&quot; height=&quot;675&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;결론적으로 개발자와 디자이너 모두 편해졌다고 한다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Figma에서 inspect를 열면 바로 Rally 코드를 확인 가능하다. &lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;그저 놀랍다... 사실 이거보고 우리회사는 못쓰겠구나 싶었다. &lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&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;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;i&gt;회사의 디자이너분에게 건너들었는데,&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;i&gt;토스는 디자인툴로 Figma 대신, 코드 기반의 &lt;a href=&quot;https://medium.com/harbor-school/%EB%94%94%EC%9E%90%EC%9D%B8-%EC%8B%9C%EC%8A%A4%ED%85%9C%EC%9D%84-%EB%A7%8C%EB%93%A4%EB%A9%B4%EC%84%9C-%EB%B0%B0%EC%9B%A0%EB%8D%98-%EA%B2%83%EB%93%A4-figma-vs-framer-78d83d5337f6&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Framer&lt;/a&gt;라는 프로그램을 쓴다고 한다.&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;커뮤니케이션 측면에서 배운 점&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;인터렉션 시스템을 빠르게 전파시 킨 비결&lt;/b&gt;도 있다.&lt;/span&gt;&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;슬랙에 #animate-noti라는 &lt;b&gt;모니터링 채널&lt;/b&gt;을 만들어서&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;다른 구성원들이 인터렉션, 모션, 랠리 관련 단어를 언급할 때 자동으로 알림이 오도록 하고&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;스레드에 찾아가서 Rally 코드를 쓰면 된다고 직접 안내했다고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. &lt;a href=&quot;https://simplicity-23.toss.im/sessions/6&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;크고 복잡한 제품 과감하게 갈아엎기&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;문제상황&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;기존의 '내 문서함' 기능에 너무 특성이 다른 기능 (ex. 주민등본 발급, 관리비 납부, 세금 납부, 건강검진 안내 등)이 엉켜있어서&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;화면의 복잡도가 높아지고 사용자가 직관적으로 앱을 사용할 수 없게 되는 문제가 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;워크샵만 3번 가고, 몇 개월을 날렸다고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;해결방법&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;결론적으로는 특정 서류를 접했을 때 &lt;b&gt;사용자가 Main Action이 뭘까?&lt;/b&gt; (= 가장 먼저 어떤 행동을 할까?)를 기준으로 3개 종류로 분류했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그리고 진입점이 필요없는 화면은 과감하게 메뉴에서 제거했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예를 들어 보험사 통지서는 특정시점에 가입 유저에게 알림만 주면 되고, 별도의 액션이 없다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그런데 여기서 굉장히&lt;b&gt; 정치적인 문제&lt;/b&gt;가 생겼다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;문서 기능을 3개 종류로 쪼개고 나니, 더 이상 1개의 진입점으로 고정시킬 수 없게 된 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;근데 기존의 '내 문서함' 진입점은 &lt;b&gt;전체 탭의 상단&lt;/b&gt;에 위치하므로 좋은 트래픽을 받고 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;PO 입장에서는 제품을 성공시켜야 하므로 트래픽을 포기하기 힘들었다. ('크로스 액티베이션'이라도 되지 않을까?)&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;근데 결국 단기적인 지표보다는 사용자 중심으로 기능을 만드는 것에 집중하면&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;장기적으로 전체적인 제품 성장에 도움이 될 거라는 믿음을 가졌다고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;결론만 보면 당연한 얘기지만, 고민의 과정을 엿볼 수 있어 좋았다.&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Product Principle 엿보기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;개인적으로 토스는 디자인 원칙이 분명하고, 원칙에 대한 팀원들의 살제 공감도가 높아보였다.&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예를 들면 Product Principal의 One thing per on page (한 화면에서 하나를 말해야 한다)가 지켜지지 않는 예&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1915&quot; data-origin-height=&quot;636&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1ii1G/btshPoit4rw/m2duBcFroH06GFSXD432SK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1ii1G/btshPoit4rw/m2duBcFroH06GFSXD432SK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1ii1G/btshPoit4rw/m2duBcFroH06GFSXD432SK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1ii1G%2FbtshPoit4rw%2Fm2duBcFroH06GFSXD432SK%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;1915&quot; height=&quot;636&quot; data-origin-width=&quot;1915&quot; data-origin-height=&quot;636&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1915&quot; data-origin-height=&quot;435&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ut0tK/btshRxsNGMY/Fn0ThRvYHrXsRsMKujFYjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ut0tK/btshRxsNGMY/Fn0ThRvYHrXsRsMKujFYjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ut0tK/btshRxsNGMY/Fn0ThRvYHrXsRsMKujFYjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fut0tK%2FbtshRxsNGMY%2FFn0ThRvYHrXsRsMKujFYjk%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;1915&quot; height=&quot;435&quot; data-origin-width=&quot;1915&quot; data-origin-height=&quot;435&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1915&quot; data-origin-height=&quot;556&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zdrqy/btshCfmGpGR/kucmmCZcKXjcmuQKLVebjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zdrqy/btshCfmGpGR/kucmmCZcKXjcmuQKLVebjK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zdrqy/btshCfmGpGR/kucmmCZcKXjcmuQKLVebjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fzdrqy%2FbtshCfmGpGR%2FkucmmCZcKXjcmuQKLVebjK%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;1915&quot; height=&quot;556&quot; data-origin-width=&quot;1915&quot; data-origin-height=&quot;556&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;등본 떼기 외에도 성적증명서, 자격증명서 떼기 도 가능한데,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;결론적으로 &lt;b&gt;'증명서 떼기'로 바꾸되 '토스 주민센터'라는 카테고리로 표시&lt;/b&gt;해서 직관성을 높였는데 반응이 좋았다고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1915&quot; data-origin-height=&quot;616&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1s1w4/btshBkPb06u/lZePTOnKtwNk287O8PNRM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1s1w4/btshBkPb06u/lZePTOnKtwNk287O8PNRM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1s1w4/btshBkPb06u/lZePTOnKtwNk287O8PNRM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1s1w4%2FbtshBkPb06u%2FlZePTOnKtwNk287O8PNRM1%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;1915&quot; height=&quot;616&quot; data-origin-width=&quot;1915&quot; data-origin-height=&quot;616&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3. &lt;a href=&quot;https://simplicity-23.toss.im/sessions/4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;초등학생도 쓸 수 있는 제품 만들기&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;문제상황&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;만 14세 이상을 위한 모바일 금융상품이 없었다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;도입 시 장애물&lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;법적 절차가 까다로움&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;유사 프로덕트가 없어서 마켓 핏 이 검증되지 않음&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;비즈니스 임팩트가 작음&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;해결방법&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;마인드셋&lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;어려움을 겪고 있는 유저의 문제를 해결해주는 것이 더 가치있지 않을까?&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(이미 모든 서비스를 누리고 있는 만 14 이상 vs. 서비스가 전혀 없는 만 14세 미만)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;장점 (구성원을 설득한 논리)&lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;나이가 어린 유저를 Lock-in 시키는 효과&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;리스크&lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;자녀의 사생활 보호를 위해 부모에게 사용 내역을 공유하지 않도록 했다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;-&amp;gt; 부모 입장에서는 자녀가 갈취/사기당하는 상황 등이 우려되는 문제가 있다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;-&amp;gt; 토스의 FDS 시스템 (이상 거래를 감지/차단하여 문제 해결)을 고도화한 &lt;b&gt;청소년 전용 FDS 시스템&lt;/b&gt;을 구축해서 해결했다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;용어에 대한 어려움을 느끼지 않도록 &lt;b&gt;최대한 쉬운 용어&lt;/b&gt;를 사용했다. &lt;br /&gt;- 자산 -&amp;gt; 지갑, 송금 -&amp;gt; 돈 보내기, 소비 -&amp;gt; 용돈 기입장&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;MVP 스펙은 어떻게 정했나?&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&quot;만 14세 미만의 유저가 어떤 기능 때문에 가입하고 싶어할까?&quot;를 고민했다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;-&amp;gt; 사용 불가를 알리는 &lt;b&gt;기존 화면을 활용하여 설문&lt;/b&gt;을 진행했다.&lt;b&gt;&lt;br /&gt;&lt;/b&gt;-&amp;gt; 압도적인 답변이었던 송금만 구현했다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;출시 후 &lt;b&gt;'토스에게 알려주세요' 메뉴를 추가하여 추가 설문&lt;/b&gt;을 진행했다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;-&amp;gt; 1~10위 기능을 모두 배포했더니 유저 만족도가 굉장히 높았다. ('토스는 내가 원하는 걸 만들어준다!')&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2058&quot; data-origin-height=&quot;1072&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTSLVz/btsiuLkCisn/1EthEta4ar8xTZPVYeuK9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTSLVz/btsiuLkCisn/1EthEta4ar8xTZPVYeuK9k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTSLVz/btsiuLkCisn/1EthEta4ar8xTZPVYeuK9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTSLVz%2FbtsiuLkCisn%2F1EthEta4ar8xTZPVYeuK9k%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;860&quot; height=&quot;448&quot; data-origin-width=&quot;2058&quot; data-origin-height=&quot;1072&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;디자이너가 배운 점&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;만드는 사람과 유저와의 간극이 클 때는 더 적극적으로 &lt;b&gt;유저 보이스&lt;/b&gt;를 들어야 한다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(제품 곳곳에 맥락에 맞는 설문 넣기, 임직원 자녀 또는 리쿠르팅을 통해 어린이 인터뷰하기 등)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;반대의 목소리가 많은 프로덕트는 &lt;b&gt;유저에게 전달할 수 있는 가치&lt;/b&gt;를 먼저 파악하는 것이 중요하다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;4. &lt;a href=&quot;https://simplicity-23.toss.im/sessions/11&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;UX 라이팅, 혼자가 아닌 함께 잘 쓰기&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;http://icunow.co.kr/uxwriting-series1/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;*UX 라이팅이란?&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #9d9d9d;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;/span&gt;&lt;b&gt;사용자들이 서비스를 사용할 때 접하게 되는 단어, 문구들을 설계하는 일&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; letter-spacing: 0px;&quot;&gt;입니다. &lt;/span&gt;서비스와 사용자 간 커뮤니케이션을 디자인&lt;span style=&quot;background-color: #ffffff; letter-spacing: 0px;&quot;&gt;하는 역할이기도 하죠. (...) 명확성, 일관성, 꼼꼼함, 자기 인식, 사용자 입장에서의 맥락 등을 종합적으로 고려해 결과물을 만들어낼 수 있어야 합니다.&amp;nbsp;&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;토스에는 UX 라이터가 3명 밖에 없어서(?) 토스의 모든 글을 전부 관리하는 것이 불가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;그래서&amp;nbsp;&lt;/span&gt;&lt;b&gt;동료들의 글쓰기 실력을 향상시키기 위한 고민&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예를 들어&amp;nbsp;&lt;b&gt;푸시 메시지&lt;/b&gt;를 개선한 사례가 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;자유롭게 문제점을 제보하는 #problem-of-toss 슬랙 채널이 있는데,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;푸시 메시지에 대해 공통적으로 '다른 푸시들과 조금 느낌이 다르다'는 내용이 많았다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Writing Principle&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;토스에는 텍스트 디자인 규칙을 정의한 &lt;b&gt;8가지 Writing Principle&lt;/b&gt;이 있는데,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이 원칙을 근거해서 글을 개선해나갔다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- &lt;b&gt;Universal Words&lt;/b&gt; : 모든 사람들이 이해하는 단어를 써야한다. 드라마, 영화 소재, 밈은 사용을 지양한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- &lt;b&gt;Suggest than Force&lt;/b&gt; : 너무 강한 어조의 문구로 유저에게 공포감을 주지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- &lt;b&gt;Predictable Hint&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;이 원칙도 이미 토스 기술블로그의&lt;/span&gt; &lt;a href=&quot;https://toss.tech/article/8-writing-principles-of-toss&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;'토스의 8가지 라이팅 원칙들'&lt;/a&gt; &lt;span style=&quot;color: #9d9d9d;&quot;&gt;포스트로 정리되어 있다. &lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;ㄷㄷ.... 이 글 자체도 너무 좋다. ✨&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;✨&lt;/span&gt;&lt;/i&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;✨&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;i&gt;&lt;a href=&quot;https://toss.tech/article/how-to-write-error-message&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;'좋은 에러 메시지를 만드는 6가지 원칙'&lt;/a&gt; &lt;span style=&quot;color: #9d9d9d;&quot;&gt;포스트도 추천한다.&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;문제상황&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;푸시가 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;투자 콘텐츠로 연결되는 중요한 진입점이므로 자극적인 푸시 문구를 작성했다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;특이한 문구나 이모지를 활용해서 반응률을 높이자. vs. UX가 좋아야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;해결방법&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;먼저 유저의 의견을 들었는데, 콘텐츠에 이미 부정적인 댓글이 달려있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그래서 UX 라이팅 원칙을 기반으로 &lt;b&gt;증권 콘텐츠 푸시에 대한 가이드라인&lt;/b&gt;을 제작했다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1568&quot; data-origin-height=&quot;710&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cViI1a/btsiuMYa9cX/rf237qe4cwqSUxlOx7NyO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cViI1a/btsiuMYa9cX/rf237qe4cwqSUxlOx7NyO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cViI1a/btsiuMYa9cX/rf237qe4cwqSUxlOx7NyO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcViI1a%2FbtsiuMYa9cX%2Frf237qe4cwqSUxlOx7NyO0%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;800&quot; height=&quot;362&quot; data-origin-width=&quot;1568&quot; data-origin-height=&quot;710&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;글을 검토하는 포인트&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;정보와 표현 2가지 차원 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;(어떤 정보를 담을지, 어떻게 표한할지)&lt;/span&gt;으로 나눠서 체크&lt;/b&gt;하면 좋다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- ex. 정보 : 공급자 중심의 밸류는 아닐까? (종목 이름보다 수익률이 얼마인지 소개하는 게 유용하다!)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- ex. 표현 : 상투적인 표현이 아닐까? (오름세로 출발했어요. -&amp;gt; 시작이 좋아요.)&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;고민한 점&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;동일한 메시지를 정보/표현/문장 형태가 다르게 &lt;b&gt;푸시 문구 실험&lt;/b&gt;을 진행했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;결과적으로 유저가 '9시'라는 시간에 잘 반응한다는 러닝을 얻었다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이런 식으로 &lt;b&gt;월 단위로 러닝을 정리하고, Winning/Losing 패턴을 공유&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;또한 기존 글의 패턴을 파악하여 카테고리별 &lt;b&gt;템플릿&lt;/b&gt;을 만들고, Winning 패턴의 &lt;b&gt;디스크립션&lt;/b&gt;을 만들어서 둘을 조합해 사용하도록 했다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1718&quot; data-origin-height=&quot;1592&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYvqc3/btsizcBOZZT/Y0Em0Op0kGvr7HdVLlXmKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYvqc3/btsizcBOZZT/Y0Em0Op0kGvr7HdVLlXmKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYvqc3/btsizcBOZZT/Y0Em0Op0kGvr7HdVLlXmKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYvqc3%2FbtsizcBOZZT%2FY0Em0Op0kGvr7HdVLlXmKk%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;600&quot; height=&quot;556&quot; data-origin-width=&quot;1718&quot; data-origin-height=&quot;1592&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;MVP 버전의 글로벌 앱을 만들면서&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;개발자이지만 앱에 들어가는 문구를 함께 고민했었는데,&amp;nbsp;간단한 메시지도 매번 어렵게 느껴져서 오래 고민했었다.&lt;/span&gt;&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;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;특히 국내에서 런칭하는 해외 서비스인 만큼&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;외국인 유저 입장에서는 번역투가 심하거나 맥락에 맞지 않는 단어를 사용하는 등 문장이 매끄럽지 않으면&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;비즈니스 자체에 대한 신뢰도가 떨어질 수 있다는 생각이 들었다.&lt;/span&gt;&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;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;지금 재직중인 회사에는 UX 라이터가 없지만, 필요성에 대해 매우 공감하게 됐다.&lt;/span&gt;&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;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;쓰다보니 필기 노트처럼 됐지만...  &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;UX에 관심이 많은 개발자라면, 또는 디자이너의 고민을 이해하고 싶다면&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;관심 가는 세션을 가벼운 마음으로 들어보는 것을 추천한다. &lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;(각 섹션은 15분 내외이다!)&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;toss &amp;gt; &lt;a href=&quot;https://simplicity-23.toss.im/all&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Simplicity23 전체 세션보기&lt;/a&gt;&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;toss careers &amp;gt; &lt;a href=&quot;https://toss.im/career/article/simplicity23&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Simplicity23, 오늘도 문제를 해결하고 있을 모든 디자이너에게&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;안드로이드의&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;text-align: start;&quot; href=&quot;https://developer.android.com/design&quot;&gt;Material 가이드&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;toss feed &amp;gt; &lt;a style=&quot;text-align: start;&quot; href=&quot;https://blog.toss.im/article/why-motion-in-finance&quot;&gt;모션, 금융을 즐겁고 쉬운 경험으로 만드는 방법&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;toss tech &amp;gt;&amp;nbsp;&lt;a style=&quot;text-align: start;&quot; href=&quot;https://toss.tech/article/8-writing-principles-of-toss&quot;&gt;토스의 8가지 라이팅 원칙들&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;toss tech &amp;gt; &lt;a style=&quot;text-align: start;&quot; href=&quot;https://toss.tech/article/how-to-write-error-message&quot;&gt;좋은 에러 메시지를 만드는 6가지 원칙&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;toss feed &amp;gt; &lt;a href=&quot;https://blog.toss.im/article/uxwriter-interview&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;토스가 금융을 더 쉽게 만드는 또 하나의 방법, UX Writing&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Blog &amp;gt; Lisa - &lt;a href=&quot;https://lmsanchez.medium.com/what-is-ux-writing-1eb71b0f0606&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;What is UX Wrting?&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Article &amp;gt; ICUNOW - &lt;a href=&quot;http://icunow.co.kr/uxwriting-series1/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;UX 라이팅이란?&lt;/a&gt;&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>프로그래밍 철학</category>
      <category>simplicity23</category>
      <category>TOSS</category>
      <category>토스</category>
      <category>프로덕트 디자인</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/78</guid>
      <comments>https://applecider2020.tistory.com/78#entry78comment</comments>
      <pubDate>Sun, 4 Jun 2023 23:56:12 +0900</pubDate>
    </item>
    <item>
      <title>[앱 리뷰] 말해보카 - 스마트폰 중독 활용하여 꾸준히 영어공부하기</title>
      <link>https://applecider2020.tistory.com/77</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;대부분의 개발자는 매일 영어로 된 공식문서와 블로그 글을 읽기 때문에 업무 중에도 영어에 노출이 많이 되는 편이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&lt;span style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;영어 공식문서를 책으로 따지자면 몇 십 권을 봤겠지만, 아직도 속독이 어려워 &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;매번&amp;nbsp;&lt;/span&gt;번역기에 돌리고 싶은 충동이 든다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이번 포스팅은 &lt;a href=&quot;https://sayvoca.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;'말해보카'&lt;/a&gt;라는 앱을 통해 2개월 동안 꾸준히 영어공부를 한 경험을 소개하려고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;나는 영어공부를 왜 하는가? - 언젠가 이민 가려고&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;개발자가 되기로 결심한 이유 중 하나는 개발이 &lt;b&gt;이민&lt;/b&gt;에 유리한 직군이기 때문이었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;20대 때 길지 않지만 호주, 인도, 태국에서 총 1년 6개월 정도 일해본 경험이 있는데, &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&lt;span style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그래서 기회가 주어질 때 언제든 이민을 떠날 수 있도록 영어공부를 꾸준히 하자고 결심했지만... 쉽지 않았다.&lt;/span&gt;&lt;span style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;나는 영어공부를 어떻게 해왔나?&amp;nbsp;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000; text-align: start;&quot;&gt;영어공부는 어떻게 하면 좋을까? 영어공부에 미쳐있는 한국에는 공부방법과 컨텐츠가 수두룩하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000; text-align: start;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;대학생 때는 &lt;b&gt;대학교 영어강의&lt;/b&gt;와 &lt;b&gt;해커스의 토플 자료&lt;/b&gt;를 통해 어휘/Listening/Reading 위주의 공부를,&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;사회초년생 때는 &lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000; text-align: start;&quot;&gt;Input은 많지만 Output이 부족함을 깨닫고 &lt;/span&gt;&lt;b&gt;통번역대학원 입시학원&lt;/b&gt;에 등록해서 Speaking/Writing 공부를 했다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그 후 태국에서 국제기구 인턴으로 9개월간 일하면서 영어 &lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000; text-align: start;&quot;&gt;Speaking이&lt;/span&gt;&amp;nbsp;가장 많이 늘었던 것 같다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;회의, 업무자료 작성, 스몰톡 등 &lt;b&gt;모든 일상에서 영어를 사용해야 하는 환경&lt;/b&gt;&amp;nbsp;덕분에 강도 높은 성장이 가능했다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;24시간 마음 졸이며 영어시험을 치고 있는 것 같은 압박감 때문에 정신적으로 허덕이기도 했다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;한국에 돌아왔을 때는 따로 영어공부를 하지 않아도 (물론 시험용 벼락치기는 했지만) 토익 975점, 오픽 AL을 딸 수 있었다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;내 영어공부는 왜 실패했나? : 꾸준한 복습이 어렵다&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하지만 그 영어 실력은... 별로 오래가지 않았다...&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;한국에 돌아와 5년간 직장생활을 하면서 영어를 거의 사용하지 않았고, 언어 능력은 빠르게 퇴화했다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;퇴사하고 개발공부를 시작했을 때, &lt;b&gt;소화해야만 하는 기술 문서와 영상들&lt;/b&gt;이 쏟아졌다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그런데&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아주 기초적이고 이미 알고 있는 내용임에도 영어로 읽다 보니 시간이 너무 오래 걸렸고,&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000; text-align: start;&quot;&gt;Apple에서 매년 나오는 WWDC 영상은 자막이 있어도 알아듣기가 힘들었다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그래서 다년간의 &lt;b&gt;영어공부 실패 경험&lt;/b&gt;을 살려&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;개발 관련 영어문서에서&lt;b&gt; 모르는 단어나 문장을 기록하고, 매일 읽는 것&lt;/b&gt;을 목표로 삼았다.&lt;/span&gt;&lt;/span&gt;&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;아래처럼 Evernote에 &lt;b&gt;단어장&lt;/b&gt;을 만들었는데,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이동하면서 보기에 폰트 크기가 너무 작고, 꽤 많은 집중력이 필요해서&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2532&quot; data-origin-height=&quot;1170&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bn6kIK/btsgC5eVeyO/1HkzK82FK1r5DczUwGIbO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bn6kIK/btsgC5eVeyO/1HkzK82FK1r5DczUwGIbO1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bn6kIK/btsgC5eVeyO/1HkzK82FK1r5DczUwGIbO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbn6kIK%2FbtsgC5eVeyO%2F1HkzK82FK1r5DczUwGIbO1%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;800&quot; height=&quot;370&quot; data-origin-width=&quot;2532&quot; data-origin-height=&quot;1170&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2532&quot; data-origin-height=&quot;1170&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uSj7m/btsgEgghuj1/obmyiXdcojKjTLD8vRJUAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uSj7m/btsgEgghuj1/obmyiXdcojKjTLD8vRJUAk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uSj7m/btsgEgghuj1/obmyiXdcojKjTLD8vRJUAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuSj7m%2FbtsgEgghuj1%2FobmyiXdcojKjTLD8vRJUAk%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;800&quot; height=&quot;370&quot; data-origin-width=&quot;2532&quot; data-origin-height=&quot;1170&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;그러다가 부트캠프 동기인 &lt;a href=&quot;https://medium.com/@Jager-yoo/%EA%B0%9C%EB%B0%9C%EC%9E%90%EB%8A%94-%EC%98%81%EC%96%B4-%EA%B3%B5%EB%B6%80-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%95%B4%EC%9A%94-b477653847e7&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;예거&lt;/a&gt;를 통해 &lt;b&gt;'말해보카'&lt;/b&gt;라는 앱을 알게 됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2개월 정도 써봤는데 매일 출퇴근길에 15분 이상 공부하는 습관을 만들 수 있었고, &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;무엇보다 이 앱으로&lt;b&gt; smart하게 공부하는 것이 어떤 것인지&lt;/b&gt; 구경하는 재미가 쏠쏠하다.&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #9d9d9d;&quot;&gt;나중에 언젠가... 학습 앱을 개발한다면 이 앱을 많이 참고할 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;말해보카의 영어공부 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;말해보카는 공부 영역을 크게 3가지로 나눈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하단 탭바를 보면 &lt;b&gt;어휘 / 리스닝 / 문법 (쓰기) 탭&lt;/b&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;(2개월 동안 총 1,567 단어나 공부했다!)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wnTEN/btsgTiJSDIn/3BNCeit0OBiVlGaFJmo5kK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wnTEN/btsgTiJSDIn/3BNCeit0OBiVlGaFJmo5kK/img.png&quot; data-origin-width=&quot;756&quot; data-origin-height=&quot;1418&quot; data-is-animation=&quot;false&quot; style=&quot;width: 52.9471%; margin-right: 10px;&quot; data-widthpercent=&quot;53.57&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wnTEN/btsgTiJSDIn/3BNCeit0OBiVlGaFJmo5kK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwnTEN%2FbtsgTiJSDIn%2F3BNCeit0OBiVlGaFJmo5kK%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;756&quot; height=&quot;1418&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dHznTV/btsgE6K0KOc/hvhDsTNnRaVOn2AVf1s2Ck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dHznTV/btsgE6K0KOc/hvhDsTNnRaVOn2AVf1s2Ck/img.png&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;2532&quot; data-is-animation=&quot;false&quot; style=&quot;width: 45.8901%;&quot; data-widthpercent=&quot;46.43&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dHznTV/btsgE6K0KOc/hvhDsTNnRaVOn2AVf1s2Ck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdHznTV%2FbtsgE6K0KOc%2FhvhDsTNnRaVOn2AVf1s2Ck%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;1170&quot; height=&quot;2532&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 어휘 - 영어 문장에 빈칸 채우기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 단어 공부 방법은 이렇다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;한글 해석과 함께 빈칸이 있는 영어 문장&lt;/b&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 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;사용자의 학습 레벨을 측정해서 &lt;b&gt;적합한 난이도&lt;/b&gt;의 단어를 제시하는 것은 물론,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;비슷한 뜻을 가진 단어지만&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;맥락상 맞지 않는 단어&lt;/b&gt;를 입력했을 때 그 차이점을 보여준다.&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;예를 들면 아래에서 &quot;신빙성&quot;이라는 단어를 맞춰야 하는데 &lt;b&gt;사용자가 credibility를 입력할 것 같으면,&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;해당 단어가 왜 적합하지 않은지 친절하게 설명해 준다!&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;말해보카에 푹 빠지게 된 똑똑한 기능이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1522&quot; data-origin-height=&quot;1550&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ORDUo/btsgGmGpyZZ/FlPOWe51TJXYCEiKsoeViK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ORDUo/btsgGmGpyZZ/FlPOWe51TJXYCEiKsoeViK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ORDUo/btsgGmGpyZZ/FlPOWe51TJXYCEiKsoeViK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FORDUo%2FbtsgGmGpyZZ%2FFlPOWe51TJXYCEiKsoeViK%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;750&quot; height=&quot;764&quot; data-origin-width=&quot;1522&quot; data-origin-height=&quot;1550&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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 data-ke-size=&quot;size16&quot;&gt;이 방식의 장점은 영단어를&amp;nbsp;&lt;b&gt;문맥과 함께 학습&lt;/b&gt;하는 것이 가능하다는 점이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다년간의 영어공부 실패 경험을 통해 &quot;authenticity = 전통성&quot; 형태로 영어와 한글을&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;1:1 대응으로 암기하면 안 된다&lt;/b&gt;는 걸 알고 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; 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;b&gt;발음이 특이한 단어&lt;/b&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 alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1218&quot; data-origin-height=&quot;1262&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/E9iMd/btsgD98RCxK/JHkeqOHkCfTEM4LBozEky1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/E9iMd/btsgD98RCxK/JHkeqOHkCfTEM4LBozEky1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/E9iMd/btsgD98RCxK/JHkeqOHkCfTEM4LBozEky1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FE9iMd%2FbtsgD98RCxK%2FJHkeqOHkCfTEM4LBozEky1%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;750&quot; height=&quot;777&quot; data-origin-width=&quot;1218&quot; data-origin-height=&quot;1262&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 리스닝 - 듣고 따라 말하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;리스닝 탭에서는 문장을 듣고, 곧바로 따라 말하는 방식&lt;/b&gt;으로 Listening/Speaking 학습을 한다.&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; (Speaking 공부가 제일 필요하면서 왜...)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;발음 정밀 진단&lt;/b&gt; (베타 기능)도 가능해서 도움이 많이 될 것 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1630&quot; data-origin-height=&quot;1112&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdnJBV/btsgGjCRJKJ/Z2kRKgGtNSAnZ3mUXnKXSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdnJBV/btsgGjCRJKJ/Z2kRKgGtNSAnZ3mUXnKXSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdnJBV/btsgGjCRJKJ/Z2kRKgGtNSAnZ3mUXnKXSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdnJBV%2FbtsgGjCRJKJ%2FZ2kRKgGtNSAnZ3mUXnKXSK%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;750&quot; height=&quot;512&quot; data-origin-width=&quot;1630&quot; data-origin-height=&quot;1112&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;b&gt;유튜브 학습&lt;/b&gt;으로 &lt;b&gt;Ted 강의를 받아쓰기&lt;/b&gt;하는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;WWDC을 한 번에 듣고 이해하는 것&lt;/b&gt;이 목표인 나에게 딱이었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1168&quot; data-origin-height=&quot;1186&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r23u5/btsgFvjtDZF/sBJSEuRHXmUIPSvESczeWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r23u5/btsgFvjtDZF/sBJSEuRHXmUIPSvESczeWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r23u5/btsgFvjtDZF/sBJSEuRHXmUIPSvESczeWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr23u5%2FbtsgFvjtDZF%2FsBJSEuRHXmUIPSvESczeWK%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;670&quot; height=&quot;680&quot; data-origin-width=&quot;1168&quot; data-origin-height=&quot;1186&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 문법 - 영어문장 완성하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;일상적으로 Reading/Listening 할 때 접하는 영어문장은 보통&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;평서문&lt;/b&gt;이라서&amp;nbsp;&lt;b&gt;의문문&lt;/b&gt;을 거의 쓰지 않는다.&lt;/span&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;문법 탭에서는 아래처럼 &lt;b&gt;주어진 단어를 조합해서 문장을 완성&lt;/b&gt;하도록 되어있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 문법 주제에 대해 직접 예문을 만들어보면서&lt;b&gt; 영어 문장구조&lt;/b&gt;에 익숙해지는 연습을 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;덕분에 예전보다 의문문을 만드는 게 훨씬 수월해졌다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2012&quot; data-origin-height=&quot;1372&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FM5AI/btsgFvcHyCA/8qTwKiOpbayeApLItUvRS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FM5AI/btsgFvcHyCA/8qTwKiOpbayeApLItUvRS1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FM5AI/btsgFvcHyCA/8qTwKiOpbayeApLItUvRS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFM5AI%2FbtsgFvcHyCA%2F8qTwKiOpbayeApLItUvRS1%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;850&quot; height=&quot;580&quot; data-origin-width=&quot;2012&quot; data-origin-height=&quot;1372&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 10대 때 &lt;b&gt;은색 표지의 맨투맨 책&lt;/b&gt;에서 배웠던 문법 지식이 nn년의 세월을 거슬러 뇌리를 스치는 재미도 쏠쏠하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 &lt;b&gt;to 부정사의 be to 용법.&lt;/b&gt; 분명히 예전에 배웠던 건데 까맣게 잊고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쓸데없는 문법도 있지만, 아래 문장처럼 문법을 모르면 단어만으로는 &lt;b&gt;문장을 제대로 해석할 수 없는 경우도 있기 때문에 유용하다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;1332&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OzzLc/btsgEhGhGWE/djVvam3F1UHO2VGLLd6Sok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OzzLc/btsgEhGhGWE/djVvam3F1UHO2VGLLd6Sok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OzzLc/btsgEhGhGWE/djVvam3F1UHO2VGLLd6Sok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOzzLc%2FbtsgEhGhGWE%2FdjVvam3F1UHO2VGLLd6Sok%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;680&quot; height=&quot;697&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;1332&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;말해보카의&amp;nbsp;장점&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;개인적으로 생각하는 강력한 장점 3가지를 꼽아봤다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. 단순하고 재미있음&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;한 화면에 1개의 문장/단어가 배치되므로&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;출퇴근길처럼 &lt;b&gt;집중력이 흐트러지기 쉬운 환경&lt;/b&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 style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그리고 틀린 문제의&amp;nbsp;&lt;b&gt;복습 주기&lt;/b&gt;가 딱 알맞다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예를 들어 사용자가 여러 번 틀린 문제는 더 자주 출제되는데,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;내가 얼마 전에 틀렸던 문제가 다시 돌아왔을 때 정답을 맞히면 희열이 있다!&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 커스텀 단어장&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;개인적으로 이민을 목표로 하고 있기 때문에 드라마/영화 같은 사적인 영어보다는 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;업무에 활용 가능한 공적 언어&lt;/b&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 style=&quot;color: #000000;&quot;&gt;말해보카에서는 이렇게 &lt;b&gt;단어 학습에 &lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000; text-align: start;&quot;&gt;&lt;b&gt;출제할 자료&lt;/b&gt;를 직접 선택할 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;내가 선택한 건 상황별 &amp;gt; 비즈니스 단어장, &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;유튜브 &amp;gt; TED, 연설, 강연, 인터뷰, School of Life, Wall Street Journal 등이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2074&quot; data-origin-height=&quot;1452&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byptzM/btsgFvjtTkO/Kz33IFK7RC71EtFlL1B8lk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byptzM/btsgFvjtTkO/Kz33IFK7RC71EtFlL1B8lk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byptzM/btsgFvjtTkO/Kz33IFK7RC71EtFlL1B8lk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbyptzM%2FbtsgFvjtTkO%2FKz33IFK7RC71EtFlL1B8lk%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;850&quot; height=&quot;595&quot; data-origin-width=&quot;2074&quot; data-origin-height=&quot;1452&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 중독을 공부에 활용 가능함&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;나는 &lt;b&gt;스마트폰 중독&lt;/b&gt;이라 &lt;/span&gt;&lt;span style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;하루종일 폰을 놓지 않고 있는데, &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그러다 보니 자연스레 하루에 15분, 많게는 1시간 동안 말해보카를 쓰게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;스마트폰 중독은 보통 생산성 저하를 가져오는데, 아무 생각 없이 &lt;b&gt;유튜브 쇼츠를 보듯이 영어 단어를 외우게 될 때&lt;/b&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 style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;나는 &lt;b&gt;경쟁 중독&lt;/b&gt;이기도 하다. K-교육체계에 익숙한 한국인이라면 대부분 그럴 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;말해보카도 그 점을 알아서 &lt;b&gt;리그 탭&lt;/b&gt;을 만들었는데, &lt;b&gt;주간 학습량으로 순위&lt;/b&gt;를 매겨서 보여준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&lt;span style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;처음에는 신경 쓰지 않았는데, 레벨이 계속 올라가다 보니 &lt;/span&gt;&lt;span style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;학습량이 많은 그룹에 속하게 되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;근데 내가 하위권에 머물러 있다는 사실이 은근히 신경 쓰여서... 더 공부를 하게 되는 효과가 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;2532&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHmPxp/btsgUsMrswR/7C2GkInKWbHknhKHAtFB9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHmPxp/btsgUsMrswR/7C2GkInKWbHknhKHAtFB9K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHmPxp/btsgUsMrswR/7C2GkInKWbHknhKHAtFB9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHmPxp%2FbtsgUsMrswR%2F7C2GkInKWbHknhKHAtFB9K%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;350&quot; height=&quot;757&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;2532&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;말해보카의 단점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;말해보카의 단점을 굳이 말하자면 &lt;b&gt;유료&lt;/b&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;하지만 12개월 구독을 하면 &lt;b&gt;월 8,000원&lt;/b&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;꾸준히 재밌게 영어공부를 하고 싶은 개발자라면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;말해보카에서 제공하는&amp;nbsp;&lt;b&gt;7일 무료체험&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;말해보카 유료 기능을 2개월 사용해 본 경험&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sayvoca.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;말해보카 웹&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Blog &amp;gt; &lt;a href=&quot;https://medium.com/@Jager-yoo/%EA%B0%9C%EB%B0%9C%EC%9E%90%EB%8A%94-%EC%98%81%EC%96%B4-%EA%B3%B5%EB%B6%80-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%95%B4%EC%9A%94-b477653847e7&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Jager-yoo의 개발자는 영어공부 어떻게 해요?&lt;/a&gt;&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>앱 리뷰</category>
      <category>말해보카</category>
      <category>앱리뷰</category>
      <category>영어공부</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/77</guid>
      <comments>https://applecider2020.tistory.com/77#entry77comment</comments>
      <pubDate>Sun, 21 May 2023 23:59:12 +0900</pubDate>
    </item>
    <item>
      <title>[채팅 기능] Stretchable Image로 채팅 버블 구현하기 (1/5)</title>
      <link>https://applecider2020.tistory.com/76</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;최근 9개월간 국내 제품을 해외 45개국에 판매하는 글로벌 프로젝트에 참여해 왔다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2023년 2월에 MVP 버전의 앱을 출시했고, 그 이후 앱을 고도화하는 작업을 진행하고 있다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;최근 가장 전념하고 있는 건 &lt;b&gt;판매자와 구매자 간의&amp;nbsp;채팅 기능을 개발&lt;/b&gt;하는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;Socket 통신, Polling, Stomp&lt;/b&gt; 등 처음 접하게 된 네트워크 개념도 있었고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;글로벌 서비스라서 고민해 볼 수 있는 &lt;b&gt;TimeZone, Locale, DateFormat&lt;/b&gt; 처리,&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;채팅 목록과 채팅 상세 화면의 &lt;b&gt;복잡한 UI 구현,&lt;/b&gt; &lt;b&gt;Up Scrolling&lt;/b&gt; 등&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이렇게 채팅 기능을 구현하면서 학습한 아래의 5개 주제를 시리즈 형식으로 포스팅해보려 한다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. Stretchable Image - 채팅 버블 구현하기&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. ScrollView의 inset, scroll 버그 (부제: Known issue인줄 알았다면 삽질을 안했을텐데)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3. TimeZone/Locale/Calendar - 국가별 시간 표시, 채팅 버블 배치&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;4. Up Scroll Pagination - CGAffineTransform으로 CollectionView와 cell 뒤집기&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;5. Socket/Polling/Stomp 이해하기 - 양방향 네트워크 구현&lt;/span&gt;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이번 포스팅에서는 첫번째 주제인&lt;b&gt; Stretchable Image&lt;/b&gt;를 다룬다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Introduction&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;채팅 상세 화면에서 가장 기본적인 UI는 &quot;채팅 버블&quot;이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;채팅 버블이라는 네이밍에서 유추 가능하듯이 말풍선 모양으로 묶인 1개의 메시지를 의미한다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이 채팅 버블을 구현해보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;평소 카카오톡을 사용할 때 채팅방의 디자인을 눈여겨본 적이 있다면&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;텍스트가 길어질수록 채팅 버블의 크기 (너비/높이)도 커진다&lt;/b&gt;는 것을 알고 있을 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dqpXZ0/btsd9MG70YL/ScpawO30KMtUGvc2qpZ3K0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dqpXZ0/btsd9MG70YL/ScpawO30KMtUGvc2qpZ3K0/img.png&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;2532&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.4624%; margin-right: 10px;&quot; data-widthpercent=&quot;50.04&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dqpXZ0/btsd9MG70YL/ScpawO30KMtUGvc2qpZ3K0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdqpXZ0%2Fbtsd9MG70YL%2FScpawO30KMtUGvc2qpZ3K0%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;1170&quot; height=&quot;2532&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Se2zq/btsd4eYt1Xu/RLKbPbeSquUULO1sbqAhY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Se2zq/btsd4eYt1Xu/RLKbPbeSquUULO1sbqAhY0/img.png&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;2556&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.3748%;&quot; data-widthpercent=&quot;49.96&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Se2zq/btsd4eYt1Xu/RLKbPbeSquUULO1sbqAhY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSe2zq%2Fbtsd4eYt1Xu%2FRLKbPbeSquUULO1sbqAhY0%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;1179&quot; height=&quot;2556&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;채팅 상세화면 (카톡 / 예시)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;문제점&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이 버블 UI를 어떻게 구현할까?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;일단 짐작가는 대로 화면을 그려보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;말풍선을 쌓아나가는 형태이므로 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;CollectionView&lt;/span&gt;를 만들어 말풍선 1개를 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;Cell&lt;/span&gt;로 구성하고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Cell 위에 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;말풍선 imageView&lt;/span&gt;를 올리고, &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;말풍선 imageView의 subView로 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;message Label&lt;/span&gt;, &lt;span style=&quot;background-color: #dddddd;&quot;&gt;translation button&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그리고 빌드해보면?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;언뜻보면 잘 그려진 것 같지만 문제가 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;버블이 커질수록 &lt;b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;말풍선의&amp;nbsp;&lt;/span&gt;모서리가 점점 둥글어져서 못생겨지는 것이다!&lt;/b&gt; (cornerRadius 값이 커지기 때문이다.)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;2556&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/N6LCF/btsedddkA1r/SKa4kseLXGgHDzy2Ky0y0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/N6LCF/btsedddkA1r/SKa4kseLXGgHDzy2Ky0y0k/img.png&quot; data-alt=&quot;이미지가 커질수록 모서리가 둥글어짐&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/N6LCF/btsedddkA1r/SKa4kseLXGgHDzy2Ky0y0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FN6LCF%2FbtsedddkA1r%2FSKa4kseLXGgHDzy2Ky0y0k%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;380&quot; height=&quot;824&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;2556&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이미지가 커질수록 모서리가 둥글어짐&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이때 이미지의 확대 영역을 설정할 수 있는 &lt;b&gt;Stretchable Image&lt;/b&gt;가 필요하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;참고로 안드로이드에서는 &lt;a href=&quot;https://unyongkim.tistory.com/11&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;9-Patch&lt;/a&gt; 이미지라고 부른다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Stretchable Image란?&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Stretchable Image를 직역하면 &quot;늘어날 수 있는 이미지&quot;이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;Stretchable Image를 사용하면 &lt;b&gt;이미지 크기를 키울 때, 이미지의 특정 영역만 늘어나도록 설정할 수 있다.&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아래 그림으로 비교해보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;일반적인 이미지의 크기를 키우면 이미지 전체가 일괄적으로 확대되지만 (왼쪽)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;Stretchable Image&lt;/span&gt;는 &lt;b&gt;모서리를 제외한 중앙 영역만 늘어난다.&lt;/b&gt;&amp;nbsp;&lt;span style=&quot;color: #333333; text-align: start;&quot;&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&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c3fVvd/btsdZM9Y3or/S6ikhUiCihZd3HsK6uOskK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c3fVvd/btsdZM9Y3or/S6ikhUiCihZd3HsK6uOskK/img.png&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;971&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.5961%; margin-right: 10px;&quot; data-widthpercent=&quot;50.18&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c3fVvd/btsdZM9Y3or/S6ikhUiCihZd3HsK6uOskK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc3fVvd%2FbtsdZM9Y3or%2FS6ikhUiCihZd3HsK6uOskK%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;1179&quot; height=&quot;971&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czVeMm/btsedb0SKLA/G64bAER0rKMddVrqdQEFD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czVeMm/btsedb0SKLA/G64bAER0rKMddVrqdQEFD1/img.png&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;978&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.2411%;&quot; data-widthpercent=&quot;49.82&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czVeMm/btsedb0SKLA/G64bAER0rKMddVrqdQEFD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FczVeMm%2Fbtsedb0SKLA%2FG64bAER0rKMddVrqdQEFD1%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;1179&quot; height=&quot;978&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;왼쪽: 일반 이미지 / 오른쪽: Stretchable Image&lt;/figcaption&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아래처럼 꼬리가 있는 말풍선 그림으로 보면 쉽게 이해할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;1162&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5Tj5z/btsd0n3a8tj/Od60FdtosrMN9zTHpIYwk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5Tj5z/btsd0n3a8tj/Od60FdtosrMN9zTHpIYwk1/img.png&quot; data-alt=&quot;1: 이미지 원본 / 2: 일반 이미지 / 3: Stretchable Image&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5Tj5z/btsd0n3a8tj/Od60FdtosrMN9zTHpIYwk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5Tj5z%2Fbtsd0n3a8tj%2FOd60FdtosrMN9zTHpIYwk1%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;300&quot; height=&quot;465&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;1162&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;1: 이미지 원본 / 2: 일반 이미지 / 3: Stretchable Image&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;공식문서 &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: left;&quot; href=&quot;https://developer.apple.com/documentation/uikit/uiimage/#overview&quot;&gt;UIImage - Define a stretchable image&lt;/a&gt;에 그림과 함께 개념이 설명되어 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아래 그림처럼 inset을 설정하면, &lt;b&gt;모서리 영역은 늘어나지 않도록 고정할 수 있다.&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;정확히는 inset으로 인해 이미지가 9개 영역으로 구분되며, 각각 수평/수직 방향으로 다르게 변형된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예를 들어 &lt;b&gt;Top/Bottom inset 영역은 높이는 고정되고, 너비는 늘어난다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;반대로 &lt;b&gt;Left/Right inset 영역은 너비는 고정되고, 높이는 늘어난다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1221&quot; data-origin-height=&quot;416&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RBRJn/btsd6utda9E/aZwACUbbHCz36IGEeYano0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RBRJn/btsd6utda9E/aZwACUbbHCz36IGEeYano0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RBRJn/btsd6utda9E/aZwACUbbHCz36IGEeYano0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRBRJn%2Fbtsd6utda9E%2FaZwACUbbHCz36IGEeYano0%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;760&quot; height=&quot;259&quot; data-origin-width=&quot;1221&quot; data-origin-height=&quot;416&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;972&quot; data-origin-height=&quot;306&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ObIzn/btsd14aGNJB/8HIJYTXmQAT5SHyOWJOKv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ObIzn/btsd14aGNJB/8HIJYTXmQAT5SHyOWJOKv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ObIzn/btsd14aGNJB/8HIJYTXmQAT5SHyOWJOKv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FObIzn%2Fbtsd14aGNJB%2F8HIJYTXmQAT5SHyOWJOKv0%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;560&quot; height=&quot;176&quot; data-origin-width=&quot;972&quot; data-origin-height=&quot;306&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;&lt;span style=&quot;color: #000000; font-size: 1.62em; letter-spacing: -1px; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Stretchable Image 구현하기&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Stretchable Image를 만드는 방법은 2가지가 있다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; font-size: 1.44em; letter-spacing: -1px; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;하나는&amp;nbsp;Asset의&amp;nbsp;Slicing을&amp;nbsp;사용하는&amp;nbsp;것이고,&amp;nbsp;다른&amp;nbsp;하나는&amp;nbsp;코드로&amp;nbsp;구현하는&amp;nbsp;것이다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;방법-1.&amp;nbsp;Asset&amp;nbsp;Slicing&amp;nbsp;활용&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이 방법은 이미지 위에 직접 확대 영역을 표시할 수 있어서 간편하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그리고 코드로 구현하지 않아도 이미지를 재사용 가능하므로&amp;nbsp;&lt;span style=&quot;color: #333333; text-align: start;&quot;&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Asset에서 이미지를 선택하고, 우상단의 &lt;b&gt;Show Slicing&lt;/b&gt;을 클릭한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(이번 예시에서는 2x, 3x png 파일을 사용했다.)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;751&quot; data-origin-height=&quot;207&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vNYn7/btsd6uGKAmj/u8GVrQd600z2w8tYk2bzRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vNYn7/btsd6uGKAmj/u8GVrQd600z2w8tYk2bzRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vNYn7/btsd6uGKAmj/u8GVrQd600z2w8tYk2bzRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvNYn7%2Fbtsd6uGKAmj%2Fu8GVrQd600z2w8tYk2bzRK%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;751&quot; height=&quot;207&quot; data-origin-width=&quot;751&quot; data-origin-height=&quot;207&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그 다음 이미지 위의 &lt;b&gt;Start Slicing&lt;/b&gt;을 클릭하고, 확대 가능 영역의 방향 (수평, 수직&amp;amp;수평, 수직)을 선택한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;채팅 버블은 너비와 높이 모두 늘어나야 하므로 &lt;b&gt;수직&amp;amp;수평&lt;/b&gt;을 선택했다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;990&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dsLe0O/btsdZ3XTIx2/T4S7Tr4OttnFsVRZ4JzYP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dsLe0O/btsdZ3XTIx2/T4S7Tr4OttnFsVRZ4JzYP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dsLe0O/btsdZ3XTIx2/T4S7Tr4OttnFsVRZ4JzYP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdsLe0O%2FbtsdZ3XTIx2%2FT4S7Tr4OttnFsVRZ4JzYP1%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;755&quot; height=&quot;990&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;990&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;우하단의 &lt;b&gt;Slicing 탭&lt;/b&gt;에서 Slices와 Center를 각각 설정한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(위에서 수직&amp;amp;수평을 선택했다면 Slices는 자동으로 입력된다.)&lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;Slices : &lt;b&gt;Horizontal and Vertical&lt;/b&gt; = 너비, 높이 둘다 늘어나게 할거임&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;Horizontal&lt;/b&gt; = 너비만 늘어나게 할거임, &lt;br /&gt;&lt;b&gt;None&lt;/b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; text-align: left;&quot;&gt;&amp;nbsp;= Strechtable Image 안쓸거임&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;Center :&amp;nbsp;&lt;b&gt;Strectches&lt;/b&gt; = 늘린 공간은 이미지를 확대해서 채울거임&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;(&lt;b&gt;Tiles&lt;/b&gt; = 늘린 공간을 타일 형태로 똑같은 이미지를 여러 개 복붙하는 방식으로 채울거임)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그리고 이미지 위에 &lt;b&gt;확대 가능한 영역&lt;/b&gt;을 지정해주면 끝이다!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아래 화살표로 표시한 영역이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;모서리 부분은 선택하지 않았기 때문에 이제 늘어나지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;754&quot; data-origin-height=&quot;1018&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FzoKU/btsd1i1nQpq/DL6jwHol9Iafw4Z9rofJZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FzoKU/btsd1i1nQpq/DL6jwHol9Iafw4Z9rofJZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FzoKU/btsd1i1nQpq/DL6jwHol9Iafw4Z9rofJZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFzoKU%2Fbtsd1i1nQpq%2FDL6jwHol9Iafw4Z9rofJZK%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;754&quot; height=&quot;1018&quot; data-origin-width=&quot;754&quot; data-origin-height=&quot;1018&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;참고로 궁금해서 실험해봤는데, 아래처럼 Slicing을 사용하되 확대 가능 영역을 지정하지 않으면 어떻게 될까?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;버블이 원본-선 형태로 깨진다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Dco42/btsecusFtVq/kD9oPLfk21RoopbibELKYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Dco42/btsecusFtVq/kD9oPLfk21RoopbibELKYk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;263&quot; data-origin-height=&quot;261&quot; data-filename=&quot;스크린샷 2023-05-07 오전 1.09.01.png&quot; style=&quot;width: 24.9605%; margin-right: 10px;&quot; data-widthpercent=&quot;25.55&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Dco42/btsecusFtVq/kD9oPLfk21RoopbibELKYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDco42%2FbtsecusFtVq%2FkD9oPLfk21RoopbibELKYk%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;263&quot; height=&quot;261&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dKO5Ef/btsecue76ih/kLgsRD4EKx1dKeiDJK6Ne0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dKO5Ef/btsecue76ih/kLgsRD4EKx1dKeiDJK6Ne0/img.png&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;790&quot; data-is-animation=&quot;false&quot; width=&quot;400&quot; height=&quot;268&quot; data-widthpercent=&quot;37.85&quot; style=&quot;width: 36.9678%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dKO5Ef/btsecue76ih/kLgsRD4EKx1dKeiDJK6Ne0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdKO5Ef%2Fbtsecue76ih%2FkLgsRD4EKx1dKeiDJK6Ne0%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;1179&quot; height=&quot;790&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkXOic/btsd9NTyWGW/KUuRccvW1uqktYhzLeU6X0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkXOic/btsd9NTyWGW/KUuRccvW1uqktYhzLeU6X0/img.png&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;817&quot; data-is-animation=&quot;false&quot; style=&quot;width: 35.7461%;&quot; data-widthpercent=&quot;36.6&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkXOic/btsd9NTyWGW/KUuRccvW1uqktYhzLeU6X0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkXOic%2Fbtsd9NTyWGW%2FKUuRccvW1uqktYhzLeU6X0%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;1179&quot; height=&quot;817&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;방법-2. resizableImage(withCapInsets:resizingMode:) 활용&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;코드로 구현하는 방법도 구조는 동일하다.&lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;capInsets&lt;/b&gt; = 확대하지 않을 영역 (ex. 모서리)을 지정함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;resizingMode&lt;/b&gt; = 늘어난 영역을 어떻게 채울건지 (Stretches인지 Tiles인지)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;b&gt;Cap insets&lt;/b&gt;에는 어떤 값을 넣어야 할까?&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예를 들어, Asset의 원본 이미지가 72x72 pixel이고, 모서리의 둥글게 처리된 부분이 10 pixel이라면&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;inset은 10으로 설정하면 된다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이때 &lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;2x, 3x png 파일을 모두 사용한다면&amp;nbsp;&lt;/span&gt;inset에 고정값을 넣는 것보다&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;이미지 크기의 특정 비율을 할당&lt;/b&gt;하는 방법이 더 낫다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;예를 들어 left/right inset에는 image.size.width * 0.4, top/bottom inset에는 &lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;image.size.height * 0.4를 할당하면 된다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;참고로 여기서 조금 헷갈릴 수 있는데, inset에 width * &lt;b&gt;0.5&lt;/b&gt;, height * &lt;b&gt;0.5&lt;/b&gt;를 할당하면 이미지가 안 늘어날까?&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;답은 &quot;늘어난다&quot;이다. &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;cap inset은 &lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;이미지의 원본 크기 (ex. 72x72 pixel)를 기준으로 설정한 것이기 &lt;/span&gt;때문에 &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;이미지를 키우면 cap 영역으로 설정되지 않은 &lt;b&gt;중앙 부분이 늘어나게 된다.&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1683391875019&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private let bubbleImageView: UIImageView = {
    let imageView = UIImageView()
    imageView.tintColor = .systemGray6
    imageView.isUserInteractionEnabled = true
    
    // 방법-1. inset에 고정값 할당
    let inset = 10.0
    imageView.image = UIImage(named: &quot;bubble&quot;)?.resizableImage(withCapInsets: UIEdgeInsets(top: inset, left: inset, bottom: inset, right: inset), resizingMode: .stretch).template
    
    // 방법-2. inset에 이미지 비율 할당
    let image = UIImage(named: &quot;bubble&quot;)
    let horizontalInset = (image?.size.width ?? 0.0) * 0.4
    let verticalInset = (image?.size.height ?? 0.0) * 0.4   
    imageView.image = image?.resizableImage(withCapInsets: UIEdgeInsets(top: verticalInset, left: horizontalInset, bottom: verticalInset, right: horizontalInset), resizingMode: .stretch).template

    return imageView
}()&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;주의할 점&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;삽질하면서 하나 알아낸 것이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Asset에 넣을 이미지는 &lt;b&gt;가장 최소 크기&lt;/b&gt;로 준비해야 한다는 점이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Stretchable Image는&amp;nbsp;&lt;b&gt;확대는 가능하지만, 축소는 불가능&lt;/b&gt;하기 때문이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;심지어 공식문서에 &quot;Stretchable images are commonly used to create backgrounds that can &lt;b&gt;grow or shrink&lt;/b&gt; to fill the available space.&quot; 라고 명시되어 있는데 실제로 해보면 줄어들지 않는다... &lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;애플놈들 부들부들  &lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예를 들어 위에서 본 Short one 텍스트 버블이 가장 작은 크기였는데&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;만약 준비한 Asset 이미지의 크기가 더 크다면?&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;버블이 줄어들지 않으므로 버블 모서리가 뾰족하게 보일 수 있다. (의도한 cornerRadius보다 작아진 상태)&lt;/span&gt;&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이렇게 채팅 버블 이미지처럼&amp;nbsp;이미지 크기를 늘릴 때 특정 영역만 확대되도록 설정하고 싶다면&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Stretchable Image를 사용해보자!&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예제 코드&lt;/span&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1683390484747&quot; class=&quot;swift&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private let bubbleImageView: UIImageView = {
    let imageView = UIImageView()
    imageView.tintColor = .systemGray6
    // ✅imageView의 subView로 label/button을 올리므로
    // tap gesture를 처리하려면 isUserInteractionEnabled 설정이 필요함
    imageView.isUserInteractionEnabled = true 

    let image = UIImage(named: &quot;bubble&quot;)
    let horizontalInset = (image?.size.width ?? 0.0) * 0.4
    let verticalInset = (image?.size.height ?? 0.0) * 0.4   
    // ✅resizableImage 활용
    imageView.image = image?.resizableImage(withCapInsets: UIEdgeInsets(top: verticalInset, left: horizontalInset, bottom: verticalInset, right: horizontalInset), resizingMode: .stretch).template
    return imageView
}()
private let msgLabel: UILabel = {
    let label = UILabel()
    label.numberOfLines = 0
    label.setContentCompressionResistancePriority(.required, for: .vertical)
    label.setContentHuggingPriority(.required, for: .vertical)
    return label
}()
private let translatedMsgLabel: UILabel = {
    let label = UILabel()
    label.numberOfLines = 0
    return label
}()
private lazy var translationButton: UIButton = {
    let button = UIButton()
    button.setTitle(&quot;See Translation&quot;.localized, for: .normal)
    button.setTitle(&quot;Hide Translation&quot;.localized, for: .selected)
    button.setTitleColor(UIColor.systemGrey, for: .normal)
    button.setTitleColor(UIColor.systemGrey, for: .selected)
    button.titleLabel?.font = .systemFont(ofSize: 12)
    button.addAction(UIAction { [weak self] _ in
        self?.translationButtonTapped()
    }, for: .touchUpInside)
    return button
}()

// ...

private func makeLayout() {
    addSubview(bubbleImageView)
    let leadingOffset = Const.horizontalPadding + Const.artistThumbnailViewSize + Const.artistThumbnailViewRightMargin
    bubbleImageView.snp.makeConstraints { make in
        make.top.equalToSuperview()
        make.leading.equalToSuperview().offset(leadingOffset)
        make.bottom.equalToSuperview().inset(Const.bottomPadding)
    }

    bubbleImageView.addSubview(msgLabel)
    msgLabel.snp.makeConstraints { make in
        make.top.equalToSuperview().offset(Const.contentPadding.top)
        make.leading.equalToSuperview().offset(Const.contentPadding.left)
        make.trailing.lessThanOrEqualToSuperview().inset(Const.contentPadding.right)
    }

    bubbleImageView.addSubview(translatedMsgLabel)
    originalMsgLabel.snp.makeConstraints { make in
        make.top.equalTo(msgLabel.snp.bottom)
        make.leading.equalToSuperview().offset(Const.contentPadding.left)
        make.trailing.lessThanOrEqualToSuperview().inset(Const.contentPadding.right)
    }

    bubbleImageView.addSubview(translationButton)
    translationButton.snp.makeConstraints { make in
        make.top.equalTo(translatedMsgLabel.snp.bottom)
        make.leading.greaterThanOrEqualTo(msgLabel)
        make.bottom.equalToSuperview().inset(Const.contentPadding.bottom)
        make.height.equalTo(Const.originalButtonHeight)
    }
    translationButton.titleLabel?.snp.makeConstraints { make in
        make.leading.equalTo(msgLabel.snp.leading)
        make.trailing.equalTo(bubbleImageView).inset(Const.contentPadding.right) 
    }   
}&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple Developer Documentation &amp;gt; &lt;a href=&quot;https://developer.apple.com/documentation/uikit/uiimage/#overview&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;UIImage - Define a stretchable image&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple Developer Documentation &amp;gt; &lt;a href=&quot;https://developer.apple.com/documentation/uikit/uiimage/1624127-resizableimage&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;resizableImage(withCapInsets:resizingMode:)&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;StackOverflow &amp;gt; &lt;a href=&quot;https://stackoverflow.com/questions/35607634/set-stretching-parameters-for-images-programmatically-in-swift-for-ios&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Set&amp;nbsp;stretching&amp;nbsp;parameters&amp;nbsp;for&amp;nbsp;images&amp;nbsp;programmatically&amp;nbsp;in&amp;nbsp;swift&amp;nbsp;for&amp;nbsp;iOS&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Blog &amp;gt; &lt;a href=&quot;https://skytitan.tistory.com/476&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Strechable Image를 이용해 이미지 늘리기&lt;/a&gt;&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&amp;nbsp;&lt;/p&gt;</description>
      <category>iOS</category>
      <category>9-patch</category>
      <category>chatting</category>
      <category>messaging</category>
      <category>stretchableImage</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/76</guid>
      <comments>https://applecider2020.tistory.com/76#entry76comment</comments>
      <pubDate>Sun, 7 May 2023 02:10:31 +0900</pubDate>
    </item>
    <item>
      <title>[삽질 방지] UIButton이 Tap Gesture를 인식 못하는 원인 5가지</title>
      <link>https://applecider2020.tistory.com/75</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;버튼을 구현하는 건 굉장히 쉽다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;하지만 모든 작업을 다 한 것 같은데도 버튼이 Tap Gesture를 인식하지 못할 때가 있어서 답답했던 경험이 있다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;너무 간단한 기능이라서 오히려 적절한 검색 키워드를 찾거나 문제 원인을 파악하기가 더 어려웠다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;삽질하면서 찾아낸 원인 5가지를 정리해 봤다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. Storyboard&amp;nbsp;Connection이 잘못된 경우&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;해결하기 가장 간단한 오류이다. Storyboard의 버튼과 @IBAction 메서드의 연결이 잘못된 경우이다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;여러 개의 버튼 또는 action 메서드를 복붙해서 만들었거나, 단순히 드래그를 잘못해서 발생한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아래처럼 Touch Up Inside 이벤트에 연결된 메서드가 1개 여야 정상동작한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;914&quot; data-origin-height=&quot;468&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Vgi7W/btsb1QrzqzS/l3138GTjwIygP8JQnekEe0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Vgi7W/btsb1QrzqzS/l3138GTjwIygP8JQnekEe0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Vgi7W/btsb1QrzqzS/l3138GTjwIygP8JQnekEe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVgi7W%2Fbtsb1QrzqzS%2Fl3138GTjwIygP8JQnekEe0%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;700&quot; height=&quot;358&quot; data-origin-width=&quot;914&quot; data-origin-height=&quot;468&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;코드로 UI를 그리는 작업에 익숙해졌는데, 반년만에 다시 Storyboard를 사용하면서 잠시 헤맸었다.&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 style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;*현업에서도 Storyboard를 쓸까?&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;협업 시 Conflict이 발생해서 현업에서는 UI를 모두 코드로 구현하는 줄 알았는데 아니었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;화면  구성이 단순하거나, 재사용이 필요 없다고 판단되는 화면은 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;빠르게 작업 가능한 Storyboard (정확하게는 Interface builder)를 쓰기도 한다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. 이벤트에 대한 action을 잘못 구현한 경우&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;코드로 UIButton을 구현하는 방법은 아래 코드처럼 세 가지 정도가 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;addTarget(_:action:for:)&lt;/span&gt; 또는 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;addAction(_:for:)&lt;/span&gt; 메서드, &lt;span style=&quot;background-color: #dddddd;&quot;&gt;primaryAction&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이때 이벤트가 발생했을 때 실행될 action을 잘못 구현했을 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이것도 해결이 단순하니 설명은 생략했다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1682231771606&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 방법-1. addTarget 메서드 활용
private lazy var backButton: UIButton = {
    let button = UIButton()
    // ...
    button.addTarget(self, action: #selector(backButtonTapped), for: .touchUpInside)
    return button
}()

@objc 
func backButtonTapped() {
    navigationController?.popViewController(animated: true)
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1682233119912&quot; class=&quot;swift&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 방법-2. addAction 메서드 활용
private lazy var backButton: UIButton = {
    let button = UIButton()
    button.addAction(UIAction { [weak self] _ in
        self?.originalButtonTapped()
    }, for: .touchUpInside)
    // ...
    return button
}()&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1682231916676&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 방법-3. 초기화 시 UIAction을 주입
private lazy var backButton: UIButton = {
    let button = UIButton(primaryAction: UIAction { [weak self] _ in
        self?.navigationController?.popViewController(animated: true)
    })
    // ...
    return button
}()&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3. 버튼이 위치/크기를 정확히 잡지 못한 경우&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;스오플에서 찾지 못한 원인이었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;설정한 layout constraints가 충분하지 않아서 버튼의 position/size가 명확히 잡히지 않은 경우이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;xcode가 임의로 충돌하는 constraints를 제거해서 일단 view를 그리긴 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Debug view hierarchy에서 확인하면&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&quot;Horizontal position is ambiguous for UIButton&quot;, &quot;Width is ambiguous for UIButton&quot; 등의 Layout issues가 뜬다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;109&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cWfEAq/btsbSEsIHeb/PGUon6CNH14Y87zrgzr3uk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cWfEAq/btsbSEsIHeb/PGUon6CNH14Y87zrgzr3uk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cWfEAq/btsbSEsIHeb/PGUon6CNH14Y87zrgzr3uk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcWfEAq%2FbtsbSEsIHeb%2FPGUon6CNH14Y87zrgzr3uk%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;580&quot; height=&quot;109&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;109&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이때 이벤트에 반응하지 않는 진짜 원인이 뭘까?&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;버튼의 위치가 화면상에 보이는 영역을 벗어났는데, &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;clipToBounds&lt;/span&gt;가 false (default)라서&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&quot;눈에는 보이지만 탭 이벤트를 인식할 수 없는 상태&quot;인 것으로 추측해 봤다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #9d9d9d;&quot;&gt;&lt;b&gt;*clipToBounds란?&lt;br /&gt;&lt;/b&gt;true일 때 view bounds의 밖으로 벗어난 subviews를 자른다. (default = false)&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;4. imageView 또는 stackView 위에 버튼을 올린 경우&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;imageView 또는 stackView의 subview로 버튼을 올리는 경우, &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;imageView의 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;isUserInteractionEnabled&lt;/span&gt;이 false (default)여서 탭 제스처를 무시해 버린다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;즉, 버튼의 superview가 제스처를 빼앗아 무시해 버린 것이다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;따라서 이것만 처리해 주면 해결된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1682237457976&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;bubbleImageView.isUserInteractionEnabled = true // 이제 subview로 이벤트를 전달함

bubbleImageView.addSubview(translationButton) // 버튼이 이벤트를 받음&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;UIButton은 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;UIControl&lt;/span&gt;을 상속받으므로 별도 처리를 하지 않아도 이벤트를 인식하지만&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;UIImageView는 아니라는 것을 다시 뼈에 새겨본다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1007&quot; data-origin-height=&quot;763&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KBaVd/btsbTV1KeQ5/5419GMvyefPWHQBkRVxs3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KBaVd/btsbTV1KeQ5/5419GMvyefPWHQBkRVxs3K/img.png&quot; data-alt=&quot;imageView.addSubview(button) 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KBaVd/btsbTV1KeQ5/5419GMvyefPWHQBkRVxs3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKBaVd%2FbtsbTV1KeQ5%2F5419GMvyefPWHQBkRVxs3K%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;1007&quot; height=&quot;763&quot; data-origin-width=&quot;1007&quot; data-origin-height=&quot;763&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;imageView.addSubview(button) 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;5. 버튼을 isUserInteractionEnabled = false인 뭔가가 덮어서&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;4번과 비슷하게 다른 UI에게 제스처를 빼앗긴 경우이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;view hierarchy를 통해 버튼을 덮고 있는 UI가 없는지 확인해 보자.&lt;/span&gt;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;5번과 관련해서 헷갈리는 부분이 있어 정리해 봤다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;위의 스크린샷처럼 collectionView cell 위에 버튼을 올린 구조에서&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;collectionView에 &lt;b&gt;gesture recognizer를 추가한 뒤, &lt;/b&gt;&lt;b&gt;버튼을 탭하면&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;'gesture recognizer&lt;/span&gt;가 제스처를 빼앗아버려서 버튼이 동작하지 않겠지?'라고 예상했는데 아니었다.  &lt;br /&gt;(다시 생각해보면 당연히 버튼이 제스처를 빼앗는다. view hierarchy상 button이 가장 위에 있기 때문이다.)&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;버튼 영역을 탭했을 때&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;gesture recognizer는 동작하지 않고, 버튼에게 touch가 전달된다.&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(단, cell 영역 중에서 버튼이 아닌 곳을 탭하면, &lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;gesture recognizer&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;가 제스처를 빼앗아서&amp;nbsp;&lt;/span&gt;collectionView의 &lt;span style=&quot;color: #ee2323;&quot;&gt;didSelectItemAt 메서드가 호출되지 않는다.&lt;/span&gt; &amp;lt;- 이 때문에 헷갈린다.)&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;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그런데 만약 gesture recognizer가 항상 제스처를 인식하도록 하고 싶다면?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;또는 didSelectItemAt이 호출되도록 하고 싶다면?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;gesture recognizer의&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;cancelsTouchesInView&lt;/span&gt;를 false로 바꾸면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;버튼 영역을 탭했을 때 &lt;b&gt;gesture recognizer와 &lt;/b&gt;&lt;/span&gt;&lt;b&gt;버튼 모두에게 touch가 전달된다.&lt;/b&gt; &lt;br /&gt;(&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;cell 영역 중에서 버튼이 아닌 곳을 탭하면,&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;gesture recognizer와 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;didSelectItemAt 모두 동작한다.&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1682238341519&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let tapGR = UITapGestureRecognizer(target: self, action: #selector(collectionViewDidTapped))
tapGR.cancelsTouchesInView = false  // GR이 항상 동작하게 하려면 필요
collectionView.addGestureRecognizer(tapGR)&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- 검색 키워드 : how&amp;nbsp;to&amp;nbsp;not&amp;nbsp;take&amp;nbsp;way&amp;nbsp;gesture&amp;nbsp;from&amp;nbsp;cell&amp;nbsp;to&amp;nbsp;collectionView,&amp;nbsp;swift&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://stackoverflow.com/questions/40365971/how-to-add-tap-gesture-to-uicollectionview-while-maintaining-cell-selection&quot;&gt;How to add tap gesture to UICollectionView, while maintaining cell selection?&lt;/a&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;*cancelsTouchesInView란?&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;true (default)일 때 gesture recognizer가 제스처를 인식하면, 보류 (pending)된 제스처의 touches가 view로 전달되지 않고, 기존에 전달된 touches는 (touchesCancelled(_:with:) 메시지를 통해) 취소된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;false일 때 multi-touch sequence의 모든 touches를 view가 전달받는다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이때 &lt;b&gt;'보류된 제스처의 touches'&lt;/b&gt;는 무슨 뜻일까?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;사용자가 화면을 터치했을 때, gesture recognizer는 이게&amp;nbsp;&lt;b&gt;Tab, Swipe, Long Press 등 중에서 어떤 건지&lt;/b&gt; 판단이 필요하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;터치가 처음 발생한 시점 (touchesBegan)에는 알 수 없다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;따라서 gesture recognizer가 제스처를 인식하는 동안 touch 객체의 전달이 지연된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;'보류된 제스처의&amp;nbsp;&lt;/span&gt;touches'는 이러한 touch 객체들을 의미한다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/uigesturerecognizer&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;UIGestureRecognizer&lt;/a&gt; 공식문서의 cancelsTouchesInView 설명이 좀 더 친절하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;요약해보면,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(default인 true일 때) gesture recognizer가 제스처를 인식했을 때, 해당 제스처의 touch 객체를 view에게 전달하지 않는다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;If a gesture recognizer recognizes its gesture, &lt;span style=&quot;color: #ee2323;&quot;&gt;it unbinds the remaining touches of that gesture from their view (so the window won&amp;rsquo;t deliver them).&lt;/span&gt; The window cancels the previously delivered touches with a (touchesCancelled(_:with:)) message. If a gesture recognizer doesn&amp;rsquo;t recognize its gesture, the view receives all touches in the multi-touch sequence.&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;즉, 원래는 responder chain에 따라 버튼이 touch event를 처리하게 되는데,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;cancelsTouchesInView&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;를 false로 바꾸면&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;gesture recognizer가 제스처를 인식했을 때 view (gesture recognizer가 장착된 collectionView)에게 touch가 전달되면서&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;버튼 (버튼 영역을 탭했을 때) 또는 didSelectItemAt (cell 중에서 버튼 이외 영역을 탭했을 때) 이 동작하게 된다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(근데 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;cancelsTouchesInView&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;를 false로 바꾼다고 해도&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;버튼이 제스처를 빼앗아가는데, 왜 gesture recognizer가 제스처를 인식하게 되는걸까...? &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;삽질&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple Developer Documentation &amp;gt;&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/uiview/1622577-isuserinteractionenabled&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;isUserInteractionEnabled&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple Developer Documentation &amp;gt; &lt;a href=&quot;https://developer.apple.com/documentation/uikit/uiview/1622415-clipstobounds&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;clipsToBounds&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple Developer Documentation &amp;gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://developer.apple.com/documentation/uikit/uigesturerecognizer/1624218-cancelstouchesinview&quot;&gt;&amp;nbsp;cancelsTouchesInView&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;Apple Developer Documentation &amp;gt; &lt;a href=&quot;https://developer.apple.com/documentation/uikit/uigesturerecognizer&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;UIGestureRecognizer&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;StackOverflow &amp;gt; &lt;a style=&quot;background-color: #e6f5ff; color: #0070d1; text-align: start;&quot; href=&quot;https://stackoverflow.com/questions/40365971/how-to-add-tap-gesture-to-uicollectionview-while-maintaining-cell-selection&quot;&gt;How to add tap gesture to UICollectionView, while maintaining cell selection?&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Blog &amp;gt; Zeddios -&amp;nbsp;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://zeddios.tistory.com/112&quot;&gt;제스쳐(gesture) 사용해보기&lt;/a&gt;&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>iOS</category>
      <category>Action</category>
      <category>UIButton</category>
      <category>삽질</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/75</guid>
      <comments>https://applecider2020.tistory.com/75#entry75comment</comments>
      <pubDate>Sun, 23 Apr 2023 22:12:56 +0900</pubDate>
    </item>
    <item>
      <title>[디자인 패턴] Singleton</title>
      <link>https://applecider2020.tistory.com/74</link>
      <description>&lt;h1 id=&quot;문제-상황&quot; data-renderer-start-pos=&quot;1&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;문제 상황&lt;/span&gt;&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-indent-level=&quot;1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;현재 로그인한 사용자의 id, nickname, email 등의 User Data는 어떻게 관리하는 게 좋을까?&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;앱을 사용하는 동안 로그인 상태가 수시로 변경될 수 있으므로 &lt;b&gt;여러 화면에서 접근&lt;/b&gt;할 수 있어야 한다. 예를 들어 앱을 최초실행 했을 때 로그인화면에서 sign in 할 수 있고, 로그아웃 화면에서 sign out 할 수 있고, 다시 비로그인 상태에서 좋아요 버튼을 눌렀을 때 modal로 띄워지는 로그인 화면에서 sign in 할 수도 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이때 여러 화면에서 어떠한 시점에 접근하더라도 로그인 상태는 동일해야 한다. 따라서 &lt;b&gt;공유 리소스 형태로 관리&lt;/b&gt;하고 싶다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;위 상황에서 Singleton 패턴이 해결책이 될 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;패턴-설명&quot; data-renderer-start-pos=&quot;378&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;패턴 설명&lt;/span&gt;&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-indent-level=&quot;1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;클래스의 인스턴스를 1개만 만들고, 이 인스턴스에 대해 여러 화면에서 접근하도록 한 패턴이다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-indent-level=&quot;2&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;의문 - 전역변수를 쓰면 되지 않나? : 예상치 못한 시점에 인스턴스를 덮어쓰기할 수 있어서 위험하다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(싱글턴은 만들어둔 인스턴스가 덮어쓰기되지 않도록 보호 가능)&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1044&quot; data-origin-height=&quot;582&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfkYUl/btr53DEMxSR/m7mc8Ch4r6frSji4TkOMF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfkYUl/btr53DEMxSR/m7mc8Ch4r6frSji4TkOMF1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfkYUl/btr53DEMxSR/m7mc8Ch4r6frSji4TkOMF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfkYUl%2Fbtr53DEMxSR%2Fm7mc8Ch4r6frSji4TkOMF1%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;600&quot; height=&quot;334&quot; data-origin-width=&quot;1044&quot; data-origin-height=&quot;582&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;div data-layout=&quot;center&quot; data-width=&quot;65&quot; data-node-type=&quot;mediaSingle&quot;&gt;
&lt;div&gt;
&lt;div data-context-id=&quot;1341554722&quot; data-type=&quot;file&quot; data-node-type=&quot;media&quot; data-width=&quot;652&quot; data-height=&quot;364&quot; data-id=&quot;aa5555a8-ffae-416b-a02e-8e4eb34014f2&quot; data-collection=&quot;contentId-1341554722&quot; data-file-name=&quot;image-20230219-092753.png&quot; data-file-size=&quot;69410&quot; data-file-mime-type=&quot;image/png&quot; data-alt=&quot;&quot;&gt;
&lt;div id=&quot;newFileExperienceWrapper&quot; data-testid=&quot;media-card-view&quot;&gt;
&lt;div data-testid=&quot;media-file-card-view&quot; data-test-status=&quot;complete&quot; data-test-media-name=&quot;image-20230219-092753.png&quot; data-test-progress=&quot;1&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-indent-level=&quot;1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;위 그림에서는 getInstance() 메서드를 통해 인스턴스를 반환하도록 했지만,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Swift 문법에 따라 static 프로퍼티를 선언하여 항상 똑같은 인스턴스를 반환하도록 해도 된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;default initializer를 private로 설정하여 외부에서 인스턴스를 추가 생성하지 못하도록 방지한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;장점&quot; data-renderer-start-pos=&quot;722&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;장점&lt;/span&gt;&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-indent-level=&quot;1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;클래스가 단 1개의 인스턴스만 가지는 것을 보장한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;인스턴스에 대한 전역 접근이 가능하다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;메모리 사용 측면에서 효율적일지도 모른다. (아래 단점 참고) Swift에서 static 프로퍼티는 최초 접근할 때 lazy한 방식으로 초기화되기 때문이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;단점&quot; data-renderer-start-pos=&quot;877&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;단점&lt;/span&gt;&lt;/h1&gt;
&lt;p data-renderer-start-pos=&quot;881&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;Singleton은 다른 디자인 패턴에 비해 단점이 많다. &lt;/b&gt;그래서 Singleton은 디자인 패턴이 아니라는 말까지 있다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;따라서 아래의 단점에 유의해서 사용해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-indent-level=&quot;1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;단일책임 원칙을 위반한다. 한 객체가 여러 가지 역할을 담당한다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(1개의 인스턴스만 생성하도록 제어하는 역할을 하면서, 동시에 User Data를 관리하는 역할을 하는 등 두 가지 기능을 동시에 한다는 뜻인듯?)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;코드 간 결합도가 높아진다. 너무 많은 데이터를 공유할 경우, 개방폐쇄 원칙을 위반한다. 추적이 어렵다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;테스트 코드 작성이 어렵다. (공유 리소스이므로 테스트 코드를 위한 목적만으로 사용하는 게 불가능함. 또한 해당 클래스를 상속하는 Mock 객체를 초기화하는 것도 불가능함)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;메모리 사용 측면에서 비효율적이다. static 프로퍼티에 인스턴스를 저장하므로 앱의 lifecycle 동안 메모리에 해당 인스턴스를 올려서 사용하기 때문이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;다중 스레드 환경에서 여러 스레드가 인스턴스를 여러 번 생성하지 않도록 스레드 lock이 필요할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;예시-코드&quot; data-renderer-start-pos=&quot;1422&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예시 코드&lt;/span&gt;&lt;/h1&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1679832420223&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class UserManager {
  static let shared = UserManager() // 단 하나의 인스턴스를 공유하여 사용함
  private var id: Int?

  private init() { } // 외부에서 인스턴스를 추가 생성 못하도록 방지
  
  func saveId(_ id: Int) {
    self.id = id
  }
  
  func deleteId() {
    self.id = nil
  }
}

// Client
// sign in 하는 시점
UserManager.shared.saveId(100)

// sign out 하는 시점
UserManager.shared.deleteId()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-renderer-start-pos=&quot;1789&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;초기화 과정이 복잡하다면 약간 복잡한 아래 방법도 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1679832433658&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class NetworkManager {
    // MARK: - Properties
    // 클로저를 통해 복잡한 형태의 초기화가 가능함
    private static var sharedNetworkManager: NetworkManager = {
        let networkManager = NetworkManager(baseURL: API.baseURL)

        // Configuration
        // ...

        return networkManager
    }()

    let baseURL: URL

    // MARK: - Initializers
    private init(baseURL: URL) {
        self.baseURL = baseURL
    }

    // MARK: - Accessors
    class func shared() -&amp;gt; NetworkManager {
        return sharedNetworkManager
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;Reference&quot; data-renderer-start-pos=&quot;2351&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Reference&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Blog - Cocoacasts &amp;gt; &lt;a href=&quot;https://cocoacasts.com/are-singletons-bad/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-testid=&quot;inline-card-resolved-view&quot;&gt;Are Singletons Bad&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Blog -  Cocoacasts &amp;gt; &lt;a href=&quot;https://cocoacasts.com/what-is-a-singleton-and-how-to-create-one-in-swift&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;What Is a Singleton and How To Create One In Swift&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로그래밍 철학</category>
      <category>Design Pattern</category>
      <category>singleton</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/74</guid>
      <comments>https://applecider2020.tistory.com/74#entry74comment</comments>
      <pubDate>Sun, 26 Mar 2023 21:09:18 +0900</pubDate>
    </item>
    <item>
      <title>[ModernRIBs] Xcode Template 설치하기 (간단) - install-xcode-template.sh</title>
      <link>https://applecider2020.tistory.com/73</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;ModernRIBs README 하단을 보면 &lt;b&gt;Xcode Template&lt;/b&gt;을 설치하라고 나온다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;&quot;&gt;설치 방법을 간단히 알아보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(script라길래 Build Phases의 &lt;a href=&quot;https://developer.apple.com/documentation/xcode/running-custom-scripts-during-a-build?changes=_8&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Run script&lt;/a&gt; 말하는 줄 알았는데 아니었음..)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1348&quot; data-origin-height=&quot;236&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNsYVa/btr1QyBaYrC/CGRB8qg87qIZ4EM6w2Tk8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNsYVa/btr1QyBaYrC/CGRB8qg87qIZ4EM6w2Tk8k/img.png&quot; data-alt=&quot;ModernRIBs README 하단&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNsYVa/btr1QyBaYrC/CGRB8qg87qIZ4EM6w2Tk8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNsYVa%2Fbtr1QyBaYrC%2FCGRB8qg87qIZ4EM6w2Tk8k%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;650&quot; height=&quot;114&quot; data-origin-width=&quot;1348&quot; data-origin-height=&quot;236&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ModernRIBs README 하단&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;참고로 Template을 추가하면 new file을 생성할 때 아래처럼 선택이 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1482&quot; data-origin-height=&quot;1064&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WdyEe/btr1PNrN8YW/hKSUWb9AFgBD3upwtHfexK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WdyEe/btr1PNrN8YW/hKSUWb9AFgBD3upwtHfexK/img.png&quot; data-alt=&quot;Template이 추가된 상태&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WdyEe/btr1PNrN8YW/hKSUWb9AFgBD3upwtHfexK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWdyEe%2Fbtr1PNrN8YW%2FhKSUWb9AFgBD3upwtHfexK%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;600&quot; height=&quot;431&quot; data-origin-width=&quot;1482&quot; data-origin-height=&quot;1064&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Template이 추가된 상태&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;&quot;&gt;ModernRIBs Template 설치 방법&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. 프로젝트 파일을 준비한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. CocoaPod 또는 SPM으로 ModernRIBs 프레임워크를 설치한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3. &lt;a href=&quot;https://github.com/DevYeom/ModernRIBs&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/DevYeom/ModernRIBs&lt;/a&gt;&amp;nbsp;코드를 다운받는다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2072&quot; data-origin-height=&quot;948&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cIoYGr/btr1UuFenWn/2NQmkcJgF0IouAFWJykO61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cIoYGr/btr1UuFenWn/2NQmkcJgF0IouAFWJykO61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cIoYGr/btr1UuFenWn/2NQmkcJgF0IouAFWJykO61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcIoYGr%2Fbtr1UuFenWn%2F2NQmkcJgF0IouAFWJykO61%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;700&quot; height=&quot;320&quot; data-origin-width=&quot;2072&quot; data-origin-height=&quot;948&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;4. 해당 폴더로 이동한 뒤 터미널에 아래를 입력한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1677832316574&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ cd tooling  // Template 파일이 tooling에 위치해있음
$ sh install-xcode-template.sh  // 설치 시작&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아래 문구가 보이면 설치 끝!&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1677832474512&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;==&amp;gt; Copying up ModernRIB Xcode file templates...
==&amp;gt; ... success!
==&amp;gt; ModernRIB have been set up. In Xcode, select 'New File...' to use ModernRIB templates.&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;GitHub &amp;gt; &lt;a href=&quot;https://github.com/DevYeom/ModernRIBs&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;ModernRIBs&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Blog &amp;gt; &lt;a href=&quot;https://ios-development.tistory.com/420&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;RIBs 튜토리얼&lt;/a&gt;&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>iOS</category>
      <category>ModernRIBs</category>
      <category>ribs</category>
      <category>Xcode template</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/73</guid>
      <comments>https://applecider2020.tistory.com/73#entry73comment</comments>
      <pubDate>Fri, 3 Mar 2023 17:40:35 +0900</pubDate>
    </item>
    <item>
      <title>[디자인 패턴] Builder - 초기화 과정이 복잡할 때</title>
      <link>https://applecider2020.tistory.com/72</link>
      <description>&lt;h2 data-pm-slice=&quot;0 0 []&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;문제 상황&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;여러 개의 프로퍼티, 중첩 타입 등을 지정해야 하므로 초기화 과정이 복잡한 객체가 존재한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이때 모든 가능한 매개변수를 포함하는 거대한 생성자를 만들게 되는데, 가독성이 떨어지고, 사용하지 않는 매개변수가 존재하므로 비효율적이라는 문제가 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;패턴 설명&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;u&gt;복잡한 객체를 단계별로 생성하는 패턴이다.&lt;/u&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;객체를 생성하는 과정에서 해당 객체에 접근 불가하도록 막는다.&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;Builder&lt;/b&gt;를 통해 객체를 초기화하고, 외부에서 프로퍼티값을 메서드 주입받아 객체를 완성한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;Director&lt;/b&gt;를 선택적으로 활용할 수 있다. Build 관련 메서드들의 실행 순서를 정의하는 역할을 담당한다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;객체를 종류별로 구분하여 생성 과정을 분기처리하고자 할 때 활용하면 좋다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1156&quot; data-origin-height=&quot;1168&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3lrmc/btrZ0KxDICE/tcTqV17eMSXDjmckjAkbKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3lrmc/btrZ0KxDICE/tcTqV17eMSXDjmckjAkbKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3lrmc/btrZ0KxDICE/tcTqV17eMSXDjmckjAkbKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3lrmc%2FbtrZ0KxDICE%2FtcTqV17eMSXDjmckjAkbKK%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;550&quot; height=&quot;556&quot; data-origin-width=&quot;1156&quot; data-origin-height=&quot;1168&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;장점&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;복잡한 객체를 단계적으로 생성하거나, 생성 단계를 연기할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;다양한 종류의 객체를 생성할 때 코드 재사용성이 좋다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;단일 책임 원칙을 준수한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;단점&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Builder 타입 등 클래스/구조체를 추가해야 하므로 코드의 복잡도가 증가한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예시&amp;nbsp;&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;852&quot; data-origin-height=&quot;534&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmTviZ/btrZ9MtE07z/sDIv0kkKorlMVcYGpsWHYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmTviZ/btrZ9MtE07z/sDIv0kkKorlMVcYGpsWHYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmTviZ/btrZ9MtE07z/sDIv0kkKorlMVcYGpsWHYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmTviZ%2FbtrZ9MtE07z%2FsDIv0kkKorlMVcYGpsWHYk%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;400&quot; height=&quot;251&quot; data-origin-width=&quot;852&quot; data-origin-height=&quot;534&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>프로그래밍 철학</category>
      <category>Builder</category>
      <category>Design Pattern</category>
      <category>디자인패턴</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/72</guid>
      <comments>https://applecider2020.tistory.com/72#entry72comment</comments>
      <pubDate>Mon, 20 Feb 2023 19:45:45 +0900</pubDate>
    </item>
    <item>
      <title>[2022년 회고] 5년의 커리어를 접고, 노베이스에서 iOS개발자가 되기까지</title>
      <link>https://applecider2020.tistory.com/71</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;개발자 글쓰기 모임인 &amp;ldquo;글또&amp;rdquo;의 8기로 참여하게 되었다. 다른 개발자들과 함께 좋은 글을 쓰는 방법에 대해 고민하고 성장하고 싶다는 것이 계기였다. 글또에 공유할 첫 번째 글로 &amp;lt;2022년 회고&amp;gt;를 작성해보려 한다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #9d9d9d;&quot;&gt;(2023년으로 넘어온지 벌써 한 달이 지난 시점이라 많이 늦었지만... 그래도 글또 덕분에 바쁜 와중에 글을 쓸 동기부여를 받았다.)&lt;/span&gt;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2022년을 한 줄 요약하면&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;&amp;ldquo;노베이스로 부트캠프 7개월 과정을 거치고, 5개월 뒤 iOS 개발자로 취직했다.&amp;rdquo;&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이걸 좀 더 풀어보자면&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;&amp;ldquo;5년간 쌓아온 커리어를 접고,&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;야곰아카데미 부트캠프에서 7개월 동안 공부하고,&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;제주 한달살이로 워케이션하면서 개인 앱을 출시하고,&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;수십 번의 탈락 끝에 아이디어스의 iOS 개발자로 취직했다.&amp;rdquo;&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그 과정에서 느낀 것, 배운 것과 앞으로의 다짐을 적어보려 한다.&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;1. 2022년 연간 목표&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;2. 4가지 성취&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&amp;nbsp; &amp;nbsp; 2-1. 부트캠프 수료&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&amp;nbsp; &amp;nbsp; 2-2. 앱 출시 (feat. 제주 한달살이 워케이션)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&amp;nbsp; &amp;nbsp; 2-3. 이커머스 iOS 개발자로 취업&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&amp;nbsp; &amp;nbsp; 2-4. 꾸준한 블로깅&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;3. 재평가&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&amp;nbsp; &amp;nbsp; 3-1. 좋았던 점&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&amp;nbsp; &amp;nbsp; 3-2. 아쉬운 점&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;4. 2023년 연간 목표&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #9d9d9d;&quot;&gt;*&lt;b&gt;회고&lt;/b&gt;란?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;회고는 개발공부를 시작하면서 처음 접한 개념이었다. 부트캠프에서, 그리고 현재 재직 중인 회사에서 회고를 진행해 봤다. 일정 기간을 주기로 내가 지나온 길을 재평가하고, 이를 바탕으로 앞으로 갈 일에 도움이 될만한 포인트를 찾아낸다는 것에 의의가 있다. 또한 무엇보다 초심을 잃지 않도록 스스로 체크리스트를 만들어 현 상태를 점검하는 기회가 된다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. 2022년 연간 목표&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;모바일 앱을 통해 다양한 분야에서&amp;nbsp;사람들의 일상이 바뀌는 것을 목격했고, 각종 앱의 해비 유저였던 나는 자연스레 &lt;b&gt;앱 개발&lt;/b&gt;에 관심을 갖게 됐다. 당시 발전소에서 엔지니어로 근무 중이었는데, 변화와 성장에 갈증을 느껴서 유튜브와 책을 통해 Swift 독학을 시작했다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그러다가 &lt;b&gt;야곰아카데미&lt;/b&gt;를 알게 되었고, 2021년 9월 시작되는 부트캠프에 등록할 계획을 세우고 퇴사를 했다. 당시 대리로 승진도 했고 나름 잘 다니던 회사였기 때문에 고민이 컸지만, 아래의 말을 듣고는 커리어 전환에 더욱 확신을 가지게 되었다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&amp;ldquo;지금 있는 곳에서의 5년 후를 생각해 봐.&amp;rdquo;&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;같은 분야에서 일했고, 10년 동안 알고 지내며 많은 영감과 자극을 주는 선배가 해 준 말이었다. 내 계획과 비슷하게 IT 분야로 커리어를 전환하여 스타트업에 취업했고, 능력을 인정받아서 취업한 지 1개월 만에 연봉을 1천만원 높여 받고 보람차게 일하고 있었다. 이걸 가까이서 지켜보며 &lt;b&gt;수평적인 조직 문화, 그리고 빠르게 결정하고 효율적으로 일하기&lt;/b&gt;를 경험해보고 싶었기에 &lt;b&gt;스타트업에 취업하는 것을 목표&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;2021년 회고는 따로 작성하지 않고, &lt;a href=&quot;https://applecider2020.tistory.com/34&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;부트캠프 후기&lt;/a&gt;로 대체했는데, 개발에 뛰어들게 된 얘기는 나중에 풀어보려 한다.&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;788&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/46Urk/btrYV86iUbW/jPGnSWZY8YpETzR5WBnV61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/46Urk/btrYV86iUbW/jPGnSWZY8YpETzR5WBnV61/img.png&quot; data-alt=&quot;2022년 설정했던 커리어 목표&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/46Urk/btrYV86iUbW/jPGnSWZY8YpETzR5WBnV61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F46Urk%2FbtrYV86iUbW%2FjPGnSWZY8YpETzR5WBnV61%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;700&quot; height=&quot;431&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;788&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;2022년 설정했던 커리어 목표&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;해마다 Notion을 활용해서 연간 목표를 세운다. 월 단위로 Review하는 시간을 통해 목표를 수정하기도 한다. &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;커리어 외에도 건강, 재테크, 문화생활, 관계, 여행 항목으로 나누어 관리한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. 4가지 성취&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2-1. 부트캠프 수료&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;부트캠프 생활은 다시 생각해도 치열했다. 새로운 개념을 배우고, 프로젝트에 적용하고, 토요일에는 복습스터디를 하는 생활의 반복이었다. 야곰아카데미의 교육철학에 따라 &lt;b&gt;끊임없이 근거를 찾으며 문제를 해결하는 연습&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이상적인 동시에 지금 생각해 보면 굉장히 무서운 말이다. 개발자로서 필수적으로 지녀야 할 태도이기도 하지만, 누군가 떠먹여 주는 게 아니기 때문에 온라인상의 방대한 텍스트를 뒤지고, 추론하고, 다시 코드를 수정하고, 새로운 문제를 만나고&amp;hellip;의 과정을 거쳐야 하는데, 끝이 안 보여서 막막하기도 하고 고통스러울 때가 많기 때문이다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;가장 힘들었을 때는 iOS 공부의 &amp;lsquo;3대 장벽&amp;rsquo;을 만났던 시점이었는데, 노베이스라서 당연히 힘든 거라고 위안 삼으며, 부트캠프의 동기/선배들과 함께 머리를 쥐 뜯으며 스터디를 했다. 힘든 만큼 문제를 해결했을 때의 희열도 컸다. 내가 작성한 코드가 시뮬레이터 상에 화면으로 그려지는 것이 재밌었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;*3대 장벽 - TableView, 네트워크, RxSwift&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwmfDZ/btrYUv1VRvN/7sRi76zNLy4KnJonesStP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwmfDZ/btrYUv1VRvN/7sRi76zNLy4KnJonesStP0/img.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1481&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.332%; margin-right: 10px;&quot; data-widthpercent=&quot;32.71&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwmfDZ/btrYUv1VRvN/7sRi76zNLy4KnJonesStP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwmfDZ%2FbtrYUv1VRvN%2F7sRi76zNLy4KnJonesStP0%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;1280&quot; height=&quot;1481&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tuFoS/btrYTVmg5oP/1cHUvA5NiLtcifFx942Cik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tuFoS/btrYTVmg5oP/1cHUvA5NiLtcifFx942Cik/img.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;67.29&quot; style=&quot;width: 66.5052%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tuFoS/btrYTVmg5oP/1cHUvA5NiLtcifFx942Cik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtuFoS%2FbtrYTVmg5oP%2F1cHUvA5NiLtcifFx942Cik%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;1280&quot; height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;큰 좌절을 가져다 준 계산기 프로젝트 (왼쪽) / 처음 네트워크를 구현했던 오픈마켓 프로젝트 (오른쪽)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;너무나 하고 싶었던 개발 공부에 전념할 수 있어서 기쁘기도 했고, 때로는 유능한 동기들과 나를 비교하면서&lt;b&gt; &amp;lsquo;내가 이 친구들처럼 정말 좋은 개발자가 될 수 있을까?&amp;rsquo;&lt;/b&gt; 하는 두려움이 커지기도 했다. 그래도 iOS를 업으로 삼는 좋은 동료들을 만나서 재밌게 공부하고 함께 성장했던 시간이라 소중한 경험이었다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;890&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7iOQ6/btrYY61pEiA/mGi9UsOT2hVUnbEpJkZdB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7iOQ6/btrYY61pEiA/mGi9UsOT2hVUnbEpJkZdB0/img.png&quot; data-alt=&quot;GitHub - 부트캠프에서 진행했던 프로젝트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7iOQ6/btrYY61pEiA/mGi9UsOT2hVUnbEpJkZdB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7iOQ6%2FbtrYY61pEiA%2FmGi9UsOT2hVUnbEpJkZdB0%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;700&quot; height=&quot;487&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;890&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;GitHub - 부트캠프에서 진행했던 프로젝트&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;*자세한 내용은&lt;/span&gt; &lt;a href=&quot;https://applecider2020.tistory.com/34&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;부트캠프 후기&lt;/a&gt; &lt;span style=&quot;color: #9d9d9d;&quot;&gt;포스팅을 참고&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;*링크는&lt;/span&gt; &lt;a href=&quot;https://github.com/just1103&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;GitHub 프로필&lt;/a&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;을 참고&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2-2. 앱 출시 (feat. 제주 한달살이 워케이션)&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;퇴사한 뒤 제대로 휴식할 시간이 없었기 때문에 부트캠프를 수료한 이후 나에게 주는 선물로 &lt;b&gt;제주 한달살이&lt;/b&gt;를 계획했었다. 남들보다 늦게 시작했기 때문에 빨리 취업을 해야 한다는 &lt;b&gt;조급한 마음&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;당시 부트캠프에서 잘 맞았던 동기 &lt;a href=&quot;https://ho8487.tistory.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;호댕&lt;/a&gt;과 함께 &lt;b&gt;앱 출시 프로젝트 '우리뭐먹지'&lt;/b&gt;를 진행하고 있었다. 미니게임을 통해 사용자 취향을 분석해서 다 같이 먹을 메뉴를 추천해 주는 앱이다.&amp;nbsp;수익을 내는 게 아니라 포트폴리오용으로 만들기로 한 것이었지만, 그래도 짜임새 있는 앱을 개발해보고 싶어서 지인을 통해 서버 개발자와 디자이너까지 섭외했다. 이 과정에서 어렵다고 느꼈던 &lt;b&gt;RxSwift, MVVM, URLSession&lt;/b&gt;에 대해 제대로 체화할 수 있었고, 서버 개발자나 디자이너와 소통하면서 자연스레 실무에서 발생할만한 문제도 직접 대응해 보는 소중한 경험이 되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;*AppStore 출시 앱 &amp;lsquo;우리뭐먹지&amp;rsquo;에 대한 자세한 내용은&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://applecider2020.tistory.com/46&quot;&gt;인디 앱 개발 포스팅&lt;/a&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;을 참고&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;614&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7npN8/btrY2nBNI0q/ADAQcrN2xqfZspDVCgJXs0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7npN8/btrY2nBNI0q/ADAQcrN2xqfZspDVCgJXs0/img.png&quot; data-alt=&quot;앱 스크린샷&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7npN8/btrY2nBNI0q/ADAQcrN2xqfZspDVCgJXs0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7npN8%2FbtrY2nBNI0q%2FADAQcrN2xqfZspDVCgJXs0%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;800&quot; height=&quot;384&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;614&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;앱 스크린샷&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;앱의 기능을 설명하는 글과 스크린샷을 준비하고, 새벽까지 작업을 하다가 Apple에 심사 제출을 했는데, 불과 10분 만에 &lt;b&gt;승인 메일&lt;/b&gt;을 받았었다. 이때 너무 기뻐서 같이 여행 중이던 친구를 깨워서 소식을 전했던 기억이 난다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;124&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKzOLN/btrYV95fDtN/pTJRTEyN1ZkfdkS5lX4PX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKzOLN/btrYV95fDtN/pTJRTEyN1ZkfdkS5lX4PX1/img.png&quot; data-alt=&quot;출시 승인을 받은 메일&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKzOLN/btrYV95fDtN/pTJRTEyN1ZkfdkS5lX4PX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKzOLN%2FbtrYV95fDtN%2FpTJRTEyN1ZkfdkS5lX4PX1%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;1280&quot; height=&quot;124&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;124&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출시 승인을 받은 메일&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;사실은 제주도에 가기 전에 앱 출시를 완료하고 편안한 마음으로 떠날 수 있을 줄 알았는데&amp;hellip; 코드 퀄리티에 욕심을 부리다 보니 작업 기간이 늘어지면서 제주도에서도 코딩을 해야했다. 베케이션 (Vacation)이 &lt;b&gt;워케이션(Workcation)&lt;/b&gt;이 됐지만 나름 뜻깊은 경험이었다. 문제해결을 하려면 생각을 해야 하지만, 뇌에 과부하가 오면 머리가 굴러가지 않아서 애를 먹곤 했는데, 주변에 바다와 산이 있으니 언제든 훌쩍 장소를 옮겨 리프레시할 수 있다는 게 큰 장점이었다. 이때 &lt;b&gt;등산과 위스키&lt;/b&gt;에 심취해서 요즘도 즐기고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/camsGx/btrYV86i9Md/CH5BvLbhRUF8VESz9xSMx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/camsGx/btrYV86i9Md/CH5BvLbhRUF8VESz9xSMx0/img.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;958&quot; data-is-animation=&quot;false&quot; style=&quot;width: 46.1677%; margin-right: 10px;&quot; data-widthpercent=&quot;47.27&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/camsGx/btrYV86i9Md/CH5BvLbhRUF8VESz9xSMx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcamsGx%2FbtrYV86i9Md%2FCH5BvLbhRUF8VESz9xSMx0%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;1280&quot; height=&quot;958&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SBXSb/btrY2nu2m9D/YEmJ3kgxpTKJ8Q8LZSeF90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SBXSb/btrY2nu2m9D/YEmJ3kgxpTKJ8Q8LZSeF90/img.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1704&quot; data-is-animation=&quot;false&quot; style=&quot;width: 25.9558%; margin-right: 10px;&quot; data-widthpercent=&quot;26.57&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SBXSb/btrY2nu2m9D/YEmJ3kgxpTKJ8Q8LZSeF90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSBXSb%2FbtrY2nu2m9D%2FYEmJ3kgxpTKJ8Q8LZSeF90%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;1280&quot; height=&quot;1704&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cvp0M4/btrYV9xmgqG/ypbAxCAJkkuyYYIyPp5zKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cvp0M4/btrYV9xmgqG/ypbAxCAJkkuyYYIyPp5zKK/img.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1731&quot; data-is-animation=&quot;false&quot; style=&quot;width: 25.5509%;&quot; data-widthpercent=&quot;26.16&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cvp0M4/btrYV9xmgqG/ypbAxCAJkkuyYYIyPp5zKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcvp0M4%2FbtrYV9xmgqG%2FypbAxCAJkkuyYYIyPp5zKK%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;1280&quot; height=&quot;1731&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;겉으로는 평안해보이지만 끝없는 디버깅과 싸우는 중&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2-3. 이커머스 iOS 개발자로 취업&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;제주도에서 돌아온 직후부터 취업 준비를 시작했다. 이력서를 다듬고, 서류를 제출하고, 채용 과제를 했다. &lt;b&gt;채용 과제&lt;/b&gt;는 생각보다 어렵지 않았지만, &lt;b&gt;면접 준비&lt;/b&gt;가 만만치 않았다. Swift/iOS 지식을 재정비하면서 아직 멀었구나, 하는 생각을 했다. 이때 &lt;b&gt;앨런의 Swift 강의&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;결과적으로 핸드메이드 상품 거래 플랫폼인 &lt;b&gt;&amp;lsquo;아이디어스&amp;rsquo;&lt;/b&gt; (백패커)에서 합격 통보를 받아 입사하게 되었다. 사실 최종면접을 심하게 망했다고 생각해서 속으로 포기하고 새로운 협업 프로젝트를 구상 중이었다. 심지어 회사 인사팀에서 전화가 왔을 때 &amp;lsquo;여기는 왜 탈락자한테도 전화를 주지?&amp;rsquo;하고 생각했는데, 합격 소식을 들어서 굉장히 놀라고 기뻐했던 기억이 난다.  &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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://www.idus.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;아이디어스&lt;/a&gt;는 평소에 자주 사용했던 앱이기도 하고, 핸드메이드 제품을 다루며 국내 수공예 시장을 키우는데 기여할 수 있다는 의미가 있어 개인적으로 애정이 가기 때문에 즐겁게 일할 수 있을 것 같아 설레었다. 또 다른 도메인에 비해 화면이 복잡하고, 새로운 기능이 정기적으로 도입되는 이커머스 업계에서 빠르게 성장할 것이 기대됐다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c9Dy6n/btrYV9qCPBA/HCtd3akI8sDsLYFoFKCWm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c9Dy6n/btrYV9qCPBA/HCtd3akI8sDsLYFoFKCWm0/img.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1714&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.444%; margin-right: 10px;&quot; data-widthpercent=&quot;33.22&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c9Dy6n/btrYV9qCPBA/HCtd3akI8sDsLYFoFKCWm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc9Dy6n%2FbtrYV9qCPBA%2FHCtd3akI8sDsLYFoFKCWm0%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;1280&quot; height=&quot;1714&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q31GU/btrYY6NQOeG/aCJkmitZzZBi3qVVcmHUvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q31GU/btrYY6NQOeG/aCJkmitZzZBi3qVVcmHUvK/img.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1705&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.6152%; margin-right: 10px;&quot; data-widthpercent=&quot;33.39&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q31GU/btrYY6NQOeG/aCJkmitZzZBi3qVVcmHUvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq31GU%2FbtrYY6NQOeG%2FaCJkmitZzZBi3qVVcmHUvK%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;1280&quot; height=&quot;1705&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZJbwA/btrYWapzn5y/8kjw6Nxtz2JHM4C5zTHu71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZJbwA/btrYWapzn5y/8kjw6Nxtz2JHM4C5zTHu71/img.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1705&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.6152%;&quot; data-widthpercent=&quot;33.39&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZJbwA/btrYWapzn5y/8kjw6Nxtz2JHM4C5zTHu71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZJbwA%2FbtrYWapzn5y%2F8kjw6Nxtz2JHM4C5zTHu71%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;1280&quot; height=&quot;1705&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;백패커 강남캠프 사무실&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;입사 직후에 터미널에 git clone을 입력해서 &lt;b&gt;현업 코드&lt;/b&gt;를 확인하던 순간이 기억에 남는다. 예상은 했지만 프로젝트 규모가 매우 크고 복잡해서 순간적으로 눈앞이 캄캄했지만, 기존에 작성된 문서를 참고하기도 하고, 앱 사용자의 Kinesis Log를 관리하는 작은 업무를 담당하기 시작하면서 차츰 코드와 서비스에 대한 이해도를 키워나갈 수 있었다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;회사 생활은 아직까지 &lt;b&gt;대만족&lt;/b&gt;이다. 팀원이 7명인데, 시니어 개발자가 많아서 문제가 생겼을 때 조언을 구할 수 있고, 누군가 질문을 했을 때 누가 먼저랄 것 없이 도움을 주려는 협조적인 팀원들이 많아서 많은 자극을 받았다. 팀원들이 좋아서 일을 더 열심히 하고, 팀에 기여하고 싶은 마음이 생기는&lt;b&gt; 선순환&lt;/b&gt;을 경험했다. &lt;span style=&quot;color: #9d9d9d;&quot;&gt;(전 직장에서는 정반대로&amp;hellip; 읍읍)&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;복지&lt;/b&gt;적인 측면에서는 주 2회 재택근무, 유연 근무제, 휴가 결재 필요 없음, 넉넉한 간식 창고, 반려견과 함께 출근 등 매우 자유도가 높은 환경이라 놀라면서 다니고 있다. &lt;span style=&quot;color: #333333;&quot;&gt;(IT업계에서는 흔할지도 모르지만, 화재/폭발 사고가 발생하는 발전소에 있다가 온 나에게는 그저 신세계&amp;hellip;)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2-4. 꾸준한 블로깅&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;마지막으로 한 가지 성과를 덧붙이자면, &lt;b&gt;기술 블로그&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;특히 12월에는 페북 게시글과 카톡 단톡방에 내가 작성했던 글이 공유되면서 유입량이 급등했었다. 해당 포스트는 일주일 동안 조회수 1천을 넘기기도 했다. 반년 전에 작성했던 글이 현재의 누군가에게 도움이 될 수 있다니, &lt;b&gt;컨텐츠의 위력&lt;/b&gt;을 실감했고 뿌듯함을 느꼈다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;885&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k3buI/btrYVhoOplY/wBfCF77K7ngmlcvTqHN9sK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k3buI/btrYVhoOplY/wBfCF77K7ngmlcvTqHN9sK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k3buI/btrYVhoOplY/wBfCF77K7ngmlcvTqHN9sK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk3buI%2FbtrYVhoOplY%2FwBfCF77K7ngmlcvTqHN9sK%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;700&quot; height=&quot;484&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;885&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;블로그의 베스트셀러 TOP 3&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1) &lt;a href=&quot;https://applecider2020.tistory.com/46&quot;&gt;인디 앱 개발 포스팅&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1676185801673&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;[인디 앱개발] ✨ 앱 기획부터 출시까지 참고한 링크 - 앱 기획, 디자인, 개발, 배포 준비&quot; data-og-description=&quot;최근 2개월 동안 개발했던 앱을 출시하게 됐다. 기획부터 시작해서 디자인, 서버 개발, iOS 개발에 직접 관여하며 진행했던 프로젝트인 만큼 재밌었고 배운 게 많았다. 그 과정에서 단계별로 고려&quot; data-og-host=&quot;applecider2020.tistory.com&quot; data-og-source-url=&quot;https://applecider2020.tistory.com/46&quot; data-og-url=&quot;https://applecider2020.tistory.com/46&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/lTjn2/hyRAptzxws/elQhfWJqmglA7DyV0pdilk/img.png?width=800&amp;amp;height=563&amp;amp;face=0_0_800_563,https://scrap.kakaocdn.net/dn/bN3Xqw/hyRAds7DTq/gCrYlBZV4jled65vhDnxsk/img.png?width=800&amp;amp;height=563&amp;amp;face=0_0_800_563,https://scrap.kakaocdn.net/dn/96XHt/hyRApNU3YZ/mt7mOXnSXQeTO9AL665Hfk/img.jpg?width=1333&amp;amp;height=1764&amp;amp;face=0_0_1333_1764&quot;&gt;&lt;a href=&quot;https://applecider2020.tistory.com/46&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://applecider2020.tistory.com/46&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/lTjn2/hyRAptzxws/elQhfWJqmglA7DyV0pdilk/img.png?width=800&amp;amp;height=563&amp;amp;face=0_0_800_563,https://scrap.kakaocdn.net/dn/bN3Xqw/hyRAds7DTq/gCrYlBZV4jled65vhDnxsk/img.png?width=800&amp;amp;height=563&amp;amp;face=0_0_800_563,https://scrap.kakaocdn.net/dn/96XHt/hyRApNU3YZ/mt7mOXnSXQeTO9AL665Hfk/img.jpg?width=1333&amp;amp;height=1764&amp;amp;face=0_0_1333_1764');&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;[인디 앱개발] ✨ 앱 기획부터 출시까지 참고한 링크 - 앱 기획, 디자인, 개발, 배포 준비&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;최근 2개월 동안 개발했던 앱을 출시하게 됐다. 기획부터 시작해서 디자인, 서버 개발, iOS 개발에 직접 관여하며 진행했던 프로젝트인 만큼 재밌었고 배운 게 많았다. 그 과정에서 단계별로 고려&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;applecider2020.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2) &lt;a href=&quot;https://applecider2020.tistory.com/14&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Hashable 해야 한다?&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1676185804016&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;[Swift] Hashable 해야 한다? 해쉬값이란? (간단 요약)&quot; data-og-description=&quot;안녕하세요. 애플사이다 입니다. Swift Language Guide의 네 번째 챕터 Collection Types에 &amp;quot;Hashable&amp;quot;과 &amp;quot;해쉬값 (Hash Value)&amp;quot;이 등장합니다. 해쉬 개념을 제대로 이해하려면 해쉬 테이블 (Hash Table)이라는 자료구&quot; data-og-host=&quot;applecider2020.tistory.com&quot; data-og-source-url=&quot;https://applecider2020.tistory.com/14&quot; data-og-url=&quot;https://applecider2020.tistory.com/14&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dvVUWt/hyRAoBqzYL/06AotJlYkeTyoymNIgsVrk/img.jpg?width=800&amp;amp;height=757&amp;amp;face=0_0_800_757,https://scrap.kakaocdn.net/dn/pkZKc/hyRAer0SqT/fLh7pXt52bxzIRkiHLh0LK/img.jpg?width=800&amp;amp;height=757&amp;amp;face=0_0_800_757,https://scrap.kakaocdn.net/dn/c0Om2b/hyRAi82nCk/kue5gTLF8SVcR3NbnEyKTK/img.jpg?width=1333&amp;amp;height=1764&amp;amp;face=0_0_1333_1764&quot;&gt;&lt;a href=&quot;https://applecider2020.tistory.com/14&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://applecider2020.tistory.com/14&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dvVUWt/hyRAoBqzYL/06AotJlYkeTyoymNIgsVrk/img.jpg?width=800&amp;amp;height=757&amp;amp;face=0_0_800_757,https://scrap.kakaocdn.net/dn/pkZKc/hyRAer0SqT/fLh7pXt52bxzIRkiHLh0LK/img.jpg?width=800&amp;amp;height=757&amp;amp;face=0_0_800_757,https://scrap.kakaocdn.net/dn/c0Om2b/hyRAi82nCk/kue5gTLF8SVcR3NbnEyKTK/img.jpg?width=1333&amp;amp;height=1764&amp;amp;face=0_0_1333_1764');&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;[Swift] Hashable 해야 한다? 해쉬값이란? (간단 요약)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;안녕하세요. 애플사이다 입니다. Swift Language Guide의 네 번째 챕터 Collection Types에 &quot;Hashable&quot;과 &quot;해쉬값 (Hash Value)&quot;이 등장합니다. 해쉬 개념을 제대로 이해하려면 해쉬 테이블 (Hash Table)이라는 자료구&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;applecider2020.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3) &lt;a href=&quot;https://applecider2020.tistory.com/37&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Diffable DataSource 이해하기&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1676185819633&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;[CollectionView] Diffable DataSource 이해하기 (1/3) - Advances in UI Data Sources (WWDC19)&quot; data-og-description=&quot;CollectionView / TableView와 관련해서 &amp;quot;Diffable DataSource&amp;quot; 개념이 등장했다. 러닝커브가 조금 있는 내용이라 포스팅을 남기려고 한다. ✏️ 새로운 기술을 습득하기 가장 좋은 방법은 Apple이 만든 WWDC 영&quot; data-og-host=&quot;applecider2020.tistory.com&quot; data-og-source-url=&quot;https://applecider2020.tistory.com/37&quot; data-og-url=&quot;https://applecider2020.tistory.com/37&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eZkXG/hyRAiupi3i/5d0fkImK8j07xNCGr1mFn1/img.gif?width=256&amp;amp;height=532&amp;amp;face=0_0_256_532,https://scrap.kakaocdn.net/dn/eQ5pLA/hyRAoaktFQ/Xy0yHTTyuEtTzo6goqDBC0/img.gif?width=256&amp;amp;height=532&amp;amp;face=0_0_256_532,https://scrap.kakaocdn.net/dn/x1Y8r/hyRAjmzuHf/K2lV8vzsG4WQTk3qkIBPn1/img.jpg?width=1333&amp;amp;height=1764&amp;amp;face=0_0_1333_1764&quot;&gt;&lt;a href=&quot;https://applecider2020.tistory.com/37&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://applecider2020.tistory.com/37&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eZkXG/hyRAiupi3i/5d0fkImK8j07xNCGr1mFn1/img.gif?width=256&amp;amp;height=532&amp;amp;face=0_0_256_532,https://scrap.kakaocdn.net/dn/eQ5pLA/hyRAoaktFQ/Xy0yHTTyuEtTzo6goqDBC0/img.gif?width=256&amp;amp;height=532&amp;amp;face=0_0_256_532,https://scrap.kakaocdn.net/dn/x1Y8r/hyRAjmzuHf/K2lV8vzsG4WQTk3qkIBPn1/img.jpg?width=1333&amp;amp;height=1764&amp;amp;face=0_0_1333_1764');&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;[CollectionView] Diffable DataSource 이해하기 (1/3) - Advances in UI Data Sources (WWDC19)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;CollectionView / TableView와 관련해서 &quot;Diffable DataSource&quot; 개념이 등장했다. 러닝커브가 조금 있는 내용이라 포스팅을 남기려고 한다. ✏️ 새로운 기술을 습득하기 가장 좋은 방법은 Apple이 만든 WWDC 영&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;applecider2020.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3. 재평가&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이에 대해 좋았던 점, 아쉬운 점을 간단히 정리해 봤다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3-1. 좋았던 점&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;굵직한&lt;b&gt; 3가지 목표&lt;/b&gt; (부트캠프 수료, 앱 출시, 개발자로 취업)을 모두 달성했다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;역량이 부족하다는 생각에 자책을 할 때가 있는데, &lt;b&gt;스스로에 대한 믿음&lt;/b&gt;이 (조금) 생겼다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;커리어 전환&lt;/b&gt;을 해보니, 해외에서 일을 하는 것처럼 느껴질 정도로 업무 환경이 달랐다. 한글을 쓴다는 공통점만 있고 나머지는 모든 면에서 달랐다. 매우 수평적인 조직문화, 서로를 존중하는 팀원들, 체계적/효율적으로 진행되는 업무를 경험하게 되어 매우 만족하고 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;나는 &lt;b&gt;기록 덕후&lt;/b&gt; (a.k.a. 기록충)인데, 개발 업계에서는 이게 장점으로 취급된다. 새로 배운 내용을 정리하고 누군가와 공유하는 것을 좋아하는 사람들이 많아서 반갑다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;내 MBTI는 INTJ인데 살면서 같은 유형의 사람들을 많이 만나보지 못했다. 그래서 &amp;lsquo;내가 이상한, 유별난 사람인가?&amp;rsquo; 하는 생각을 종종 했었다. 그런데 개발자 세계에서 &lt;b&gt;INT*&lt;/b&gt; 유형은 꽤나 흔하다. 부트캠프에서, 회사에서 고요하게 자신의 일에 몰두하는 비슷한 성향이 동료들이 많아서 은근히 든든하다. (밖에서 만날 수 없었던 이유는 다들 집에서 조용히 뭔가를 하고 있기 때문이었던 것 같다.)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3-2. 아쉬운 점&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;개발 과정에서 문제를 만났을 때 공식문서를 읽는 것에 매몰되는 경우가 있었다. 그래서 &lt;b&gt;구글링 키워드&lt;/b&gt;를 충분히 고민해보지 않았다. (빠르게 문제해결을 못한다고 자책할 시간에 구글링을 하자. 구글은 답을 알고 있다.)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;회사에서 담당하게 된 프로젝트의 작업을 하느라 급급해서, 다른 팀원들이 작성한 코드를 제대로 보지 못했다. 내년에는 점심시간 직후, 회의를 끝낸 직후 등 &lt;b&gt;컨텍스트 스위칭&lt;/b&gt;이 발생한 타이밍을 활용해 볼 계획이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;스타트업에 취업하는 게 목표였으면서, &lt;b&gt;코딩테스트&lt;/b&gt;를 보려고 기웃거렸다. 그래서 부트캠프에서 코딩테스트 스터디에 참여했었는데, (간접적으로는 도움이 됐겠지만) 물리적으로 시간이 부족할 수밖에 없는 상황에서 모든 걸 다하려고 욕심부리느라 에너지를 낭비했던 것 같다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;출시한 앱을 업데이트 하지 않고 방치했다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;영어 공부를 위해 개발 용어를 단어장에 정리하려는 계획이 있었는데 하다가 포기했다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;한달살이를 할 때 매일 아침 조깅을 할 계획이었는데 못했다. ㅎㅎ&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;4. 2023년 연간 목표&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;회사에서 진행 중인 &lt;b&gt;글로벌앱 출시 프로젝트&lt;/b&gt;를 무사히 완수하고, 안정적인 서비스 운영에 기여하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;업무 중에 접한 키워드 꾸준히 스터디하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;팀원들과 더 잘 &lt;b&gt;협업&lt;/b&gt;하기 (29cm의 김우성 개발자님의 특강으로 들었던 &amp;ldquo;PR로 협업하기&amp;rdquo;를 회사 코드리뷰에 적용해 볼 계획이다.)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;팀원들과 진행 중인 &lt;b&gt;디자인 패턴&lt;/b&gt; 스터디 1회/주 참여하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;노수진님의 강의 듣기 &lt;a href=&quot;https://fastcampus.co.kr/dev_red_rsj&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;(슈퍼앱 운영을 위한 확장성 높은 앱 아키텍처 구축)&lt;/a&gt;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;사이드 프로젝트로 &amp;lsquo;우리뭐먹지&amp;rsquo; 앱 &lt;b&gt;고도화&lt;/b&gt;하기 (지도, 즐겨찾기, 로그인 기능)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;글또 구성원과 소통하고, 블로깅 1회/2주 하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;WWDC 1회/주 보고 정리하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;iOS 관련 컨퍼런스 1회/년 참석하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;&quot;&gt;단어장 만들어서 영어 공부하기&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://applecider2020.tistory.com/34&quot;&gt;부트캠프 후기&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://github.com/just1103&quot;&gt;GitHub 프로필&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://applecider2020.tistory.com/46&quot;&gt;인디 앱 개발 포스팅&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://applecider2020.tistory.com/14&quot;&gt;Hashable 해야 한다?&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://applecider2020.tistory.com/37&quot;&gt;Diffable DataSource 이해하기&lt;/a&gt;&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>비전공자용 노력</category>
      <category>2022</category>
      <category>글또</category>
      <category>회고</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/71</guid>
      <comments>https://applecider2020.tistory.com/71#entry71comment</comments>
      <pubDate>Sun, 12 Feb 2023 16:24:17 +0900</pubDate>
    </item>
    <item>
      <title>[RxSwift] Bind, Driver, Relay - Observable/Subject 말고 다른 것도 써보자</title>
      <link>https://applecider2020.tistory.com/70</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;Swift&lt;/b&gt; 문법을 배우는 것과 &lt;b&gt;RxSwift&lt;/b&gt;를 배우는 것은 큰 차이가 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Swift는 처음부터 차근차근 배워야 하지만&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;RxSwift는 활용 방식이나 관련된 Operator가 너무 다양해서 필요한 것을 잘 추려서 써야한다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그래서 Rx를 배우는 데 많은 시행착오가 있었다.&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;ViewModel-ViewController를 binding 하는 기초 예제를 정리해보려고 한다. 언젠가는..&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;겨우 &lt;b&gt;Observable, BehaviorSubject, Publish&lt;/b&gt;&lt;b&gt;Subject&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그런데 Rx의 기능을 충분히 활용하고 있지 못한 것 같아서&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그 다음 단계로 UI를 안전하게 처리하기 위한 &lt;b&gt;Bind, Driver, Relay&lt;/b&gt;를 배웠는데 매우 유용했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이 내용을 간단히 소개해보려고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. bind(to:)&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;binding할 때 &lt;span style=&quot;color: #ee2323;&quot;&gt;순환참조를 방지&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;코드를 줄일 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #9d9d9d;&quot;&gt;RxCocoa의 기능이다. UI에다가 Rx 기능을 붙인 개념이다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;subscribe(onNext:) 대신 &lt;b&gt;bind(to:)&lt;/b&gt;를 쓰면 되고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;self?.labelText.text = $0 형태 대신 &lt;b&gt;label.rx.text&lt;/b&gt; 형태로 쓴다.&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;참고 - &quot;bind를 쓰면 Main thread에서 실행하는 걸 보장한다&quot;는 블로그 글이 있는데 잘못된 정보이다.&lt;br /&gt;bind는 내부적으로 subscribe를 호출하기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1702438122781&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public func bind(onNext: @escaping (Element) -&amp;gt; Void) -&amp;gt; Disposable {
    return self.subscribe(onNext: onNext, onError: { error in
        rxFatalErrorInDebug(&quot;Binding error: \(error)&quot;)
    })
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. Observable 대신 Driver&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;UI 특성상 stream이 끊기면 안된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;서버가 일시적으로 불안정한 등의 예상치 못한 이유로 인해 &lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;stream이 끊길 수 있는데&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;버튼이 동작하지 않거나, 검색창에 검색어를 입력해도 아무 반응이 없는 문제가 발생한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #9d9d9d;&quot;&gt;*Google Books API를 활용해서 검색화면을 구현했을 때 간헐적으로 문제가 발생했다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그래서 &lt;span style=&quot;color: #ee2323;&quot;&gt;Driver는 error가 발생했을 때 임의 값으로 처리해서 stream을 유지해준다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(매개변수 네이밍인 onErrorJustReturn이 매우 적절하다.)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;subscribe(onNext:), bind(to:) 대신 &lt;b&gt;drive()&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그리고 자동으로 &lt;span style=&quot;color: #ee2323;&quot;&gt;Main 스레드에서 처리&lt;/span&gt;하므로 UI 처리에 더욱 안전하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;따라서 이제 &lt;b&gt;observeOn(MainSceduler.instance)가 필요 없다.&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3. Subject 대신 Relay&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Subject도 마찬가지로 error가 발생해도 stream이 끊기지 않도록 해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;span&gt;Relay는 Subject에서 error가 발생하면 무시해서 stream을 유지해준다.&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;Subject와 종류도 똑같다. &lt;/span&gt;&lt;span&gt;BehaviorSubject 대신 &lt;b&gt;BehaviorRelay&lt;/b&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;onError, onComplete이 발생하지 않는 개념이라서 무조건 받아들일 수 밖에 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 onNext() 대신 &lt;b&gt;accept()&lt;/b&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&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;a href=&quot;https://reactivex.io/documentation/operators.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;ReativeX&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;GitHub &amp;gt; &lt;a href=&quot;https://github.com/ReactiveX/RxSwift&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;RxSwift&lt;/a&gt;&amp;nbsp;&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>비전공자용 노력/개발 툴</category>
      <category>bind</category>
      <category>driver</category>
      <category>Relay</category>
      <category>rxswift</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/70</guid>
      <comments>https://applecider2020.tistory.com/70#entry70comment</comments>
      <pubDate>Sun, 29 Jan 2023 22:06:10 +0900</pubDate>
    </item>
    <item>
      <title>[디자인 패턴] Abstract Factory - 미리 정해둔 종류로만 객체를 생성할 때</title>
      <link>https://applecider2020.tistory.com/69</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;GoF 디자인 패턴 중 하나인 Abstract Factory 패턴을 정리했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이 패턴은 &lt;b&gt;예시코드부터&lt;/b&gt; 보는 것을 추천한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-pm-slice=&quot;0 0 []&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Ref :&amp;nbsp;&lt;a href=&quot;https://www.goodreads.com/book/show/43125355-dive-into-design-patterns&quot;&gt;도서 &amp;lt;Dive into Design Patterns&amp;gt;,&amp;nbsp;Alexander Shvets 저&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 id=&quot;문제-상황&quot; data-pm-slice=&quot;0 0 []&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;문제 상황&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;가구 판매 앱 개발과정에서 &lt;b&gt;세트 제품군&lt;/b&gt; (a family of reloated products)이 있다. (ex. 의자, 소파, 커피테이블) &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;또한 해당 세트 제품군에는 여러 &lt;b&gt;스타일의 변형&lt;/b&gt; (variants)이 있다. (ex. 아르데코, 빅토리안, 현대식)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;고객이 가구를 주문하면, &lt;span style=&quot;color: #ee2323;&quot;&gt;동일한 스타일로 가구세트를 통일&lt;/span&gt;해야 하는 상황이다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;또한 &lt;b&gt;새로운 제품 (새로운 스타일)이 자주 추가&lt;/b&gt;되므로 매번 기존 코드를 수정하는 번거로움을 피하고 싶다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;876&quot; data-origin-height=&quot;598&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmbfaK/btrWca07MNS/ZUA1kn3r2yHmZQT6saCvOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmbfaK/btrWca07MNS/ZUA1kn3r2yHmZQT6saCvOK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmbfaK/btrWca07MNS/ZUA1kn3r2yHmZQT6saCvOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmbfaK%2FbtrWca07MNS%2FZUA1kn3r2yHmZQT6saCvOK%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;480&quot; height=&quot;328&quot; data-origin-width=&quot;876&quot; data-origin-height=&quot;598&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id=&quot;패턴-설명&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;패턴 설명&lt;/span&gt;&lt;/h2&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;i&gt;이 패턴은 말로 설명하면 복잡한데, 예시를 보면 매우 쉽다.  &lt;/i&gt;&lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;요약 :&amp;nbsp;Concrete 클래스가 없어도 &lt;u&gt;특정 객체를 생성할 때 항상 미리 지정해 둔 종류로 생성&lt;/u&gt;하는 패턴이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;먼저 세트 제품군 종류별 프로토콜을 생성한다. (ex. &lt;span style=&quot;background-color: #dddddd;&quot;&gt;의자 프로토콜&lt;/span&gt;) &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이후 각 스타일의 제품이 위 프로토콜을 채택하도록 한다. (ex. &lt;span style=&quot;background-color: #dddddd;&quot;&gt;빅토리안 의자, 모던 의자&lt;/span&gt; 등)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그 다음 Abstract Factory 프로토콜을 선언하고, (ex. &lt;span style=&quot;background-color: #dddddd;&quot;&gt;FurnitureFactory 프로토콜&lt;/span&gt;) &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이후 각 스타일마다 위 프로토콜을 채택하도록 한다. (ex. &lt;span style=&quot;background-color: #dddddd;&quot;&gt;빅토리안 FurnitureFactory, 모던 FurnitureFactory&lt;/span&gt; 등)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;클라이언트는 의자 프로토콜을 사용하여&amp;nbsp;모든 의자를 항상 동일한 방식으로 주문하게 된다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(= &lt;span style=&quot;color: #ee2323;&quot;&gt;의자를 주문하기만 하면 항상 정해진 스타일로 온다는 뜻❗️&lt;/span&gt;)&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Abstract Factory 패턴은 Factory Method 패턴의 집합을 기반으로 한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1290&quot; data-origin-height=&quot;766&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zAEh4/btrWeYyqOoN/40mTlZxUs5ji7ZWyhVVjtK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zAEh4/btrWeYyqOoN/40mTlZxUs5ji7ZWyhVVjtK/img.png&quot; data-alt=&quot;Abstract Factory 구조도&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zAEh4/btrWeYyqOoN/40mTlZxUs5ji7ZWyhVVjtK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzAEh4%2FbtrWeYyqOoN%2F40mTlZxUs5ji7ZWyhVVjtK%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;800&quot; height=&quot;475&quot; data-origin-width=&quot;1290&quot; data-origin-height=&quot;766&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Abstract Factory 구조도&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예시 코드&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;핵심은 &lt;b&gt;초기화 시 스타일을 지정하면 &lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;-&amp;gt; 해당 스타일로 모든 제품군을 생성한다&lt;/b&gt;는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1673701935007&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// MARK: - 1️⃣ Chair
protocol Chair {
    func hasLegs()
    func sitOn()
}
class VictoryianChair: Chair {
    func hasLegs() { }
    func sitOn() { }
}
class ModernChair: Chair {
    func hasLegs() { }
    func sitOn() { }
}

// MARK: - Sofa
protocol Sofa { }  // 생략
class VictoryianSofa: Sofa { }
class ModernSofa: Sofa { }

// MARK: - CoffeeTable
protocol CoffeeTable { }  // 생략
class VictoryianCoffeeTable: CoffeeTable { }
class ModernCoffeeTable: CoffeeTable { }

// MARK: - 2️⃣ FurnitureFactory
protocol FurnitureFactory {
    func createChair() -&amp;gt; Chair
    func createSofa() -&amp;gt; Sofa
    func createCoffeeTable() -&amp;gt; CoffeeTable
}
class VictorianFurnitureFactory: FurnitureFactory {
    func createChair() -&amp;gt; Chair {
        print(&quot;VictoryianChair&quot;)
        return VictoryianChair()
    }
    func createSofa() -&amp;gt; Sofa {
        print(&quot;VictoryianSofa&quot;)
        return VictoryianSofa()
    }
    func createCoffeeTable() -&amp;gt; CoffeeTable {
        print(&quot;VictoryianCoffeeTable&quot;)
        return VictoryianCoffeeTable()
    }
}
class ModernFurnitureFactory: FurnitureFactory {
    func createChair() -&amp;gt; Chair {
        print(&quot;ModernChair&quot;)
        return ModernChair()
    }
    func createSofa() -&amp;gt; Sofa {
        print(&quot;ModernSofa&quot;)
        return ModernSofa()
    }
    func createCoffeeTable() -&amp;gt; CoffeeTable {
        print(&quot;ModernCoffeeTable&quot;)
        return ModernCoffeeTable()
    }
}

// MARK: - ✅ Client
class House {
    let factory: FurnitureFactory
    
    init(factory: FurnitureFactory) {  // 1. 초기화 시 스타일을 결정하면
        self.factory = factory
    }
    
    func service() {  // 2. 해당 스타일로 모든 세트 제품을 생성함 ❗️
        let chair = factory.createChair()
        let sofa = factory.createSofa()
        let coffeeTable = factory.createCoffeeTable()
    }
}

let victorianFurnitureFactory = VictorianFurnitureFactory()
let victorianHouse = House(factory: victorianFurnitureFactory)
victorianHouse.service()
//VictoryianChair
//VictoryianSofa
//VictoryianCoffeeTable

let modernFurnitureFactory = ModernFurnitureFactory()
let modernHouse = House(factory: modernFurnitureFactory)
modernHouse.service()
//ModernChair
//ModernSofa
//ModernCoffeeTable&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;활용하는 상황을 상상해보면 아래와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1673701844542&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;switch style {
case .victorian:
    let victorianFurnitureFactory = VictorianFurnitureFactory()
    let victorianHouse = House(factory: victorianFurnitureFactory)
    victorianHouse.service()
case .modern:
    let modernFurnitureFactory = ModernFurnitureFactory()
    let modernHouse = House(factory: modernFurnitureFactory)
    modernHouse.service()
// ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;장점&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;장점&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;항상 특정한 종류의 제품만 생성하는 것을 보장할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Concrete Product와 클라이언트 간의 결합도를 낮춘다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Product 초기화 코드를 특정 객체가 전담하므로 단일책임 원칙을 준수한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;새로운 제품 (스타일)이 추가할 때 기존 클라이언트를 훼손하지 않으므로 &lt;span style=&quot;color: #ee2323;&quot;&gt;개방폐쇄 원칙을 준수&lt;/span&gt;한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;단점&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;단점&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;새로운 프로토콜과 클래스를 추가해야 하므로 코드의 복잡도가 높아진다.&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference&lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://www.goodreads.com/book/show/43125355-dive-into-design-patterns&quot;&gt;도서 &amp;lt;Dive into Design Patterns&amp;gt;, Alexander Shvets 저&lt;/a&gt;&lt;/span&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;&lt;span style=&quot;background-color: #ffffff; color: #555555; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&amp;nbsp;&lt;/p&gt;</description>
      <category>프로그래밍 철학</category>
      <category>Abstract Factory</category>
      <category>GOF</category>
      <category>디자인패턴</category>
      <category>추상 팩토리</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/69</guid>
      <comments>https://applecider2020.tistory.com/69#entry69comment</comments>
      <pubDate>Mon, 16 Jan 2023 07:00:19 +0900</pubDate>
    </item>
    <item>
      <title>[디자인 패턴] Factory Method - 비슷한 종류의 타입을 찍어낼 때</title>
      <link>https://applecider2020.tistory.com/67</link>
      <description>&lt;p data-pm-slice=&quot;0 0 []&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;GoF 디자인 패턴 중 하나인 Factory Method 패턴을 정리했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-pm-slice=&quot;0 0 []&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Ref : &lt;a href=&quot;https://www.goodreads.com/book/show/43125355-dive-into-design-patterns&quot;&gt;도서 &amp;lt;Dive into Design Patterns&amp;gt;,&amp;nbsp;Alexander Shvets 저&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-pm-slice=&quot;0 0 []&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;문제 상황&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;물류관리 앱을 개발하는 과정에서 먼저 Truck (트럭) 운송 처리 로직을 만들었는데, 얼마 뒤 Ship (선박) 등 추가적인 운송수단 처리가 필요하게 됐다. &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;대부분의 코드가 &lt;b&gt;기존의 Truck 클래스에 결합&lt;/b&gt;되어 있어 코드 재사용성이 떨어지는 문제가 있는 상황이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;패턴 설명&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;요약 : 부모클래스에서 &lt;b&gt;객체 생성 인터페이스&lt;/b&gt;를 제공하며, 동시에 자식클래스가 &lt;b&gt;객체의 유형을 변경하여 생성 가능&lt;/b&gt;하도록 하는 패턴이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Product, Creator 역할이 필요하다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;482&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clmSod/btrWbEVVQAq/gFcsnGYpErDIKwyFOmVaNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clmSod/btrWbEVVQAq/gFcsnGYpErDIKwyFOmVaNk/img.png&quot; data-alt=&quot;Factory Method 구조도&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clmSod/btrWbEVVQAq/gFcsnGYpErDIKwyFOmVaNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FclmSod%2FbtrWbEVVQAq%2FgFcsnGYpErDIKwyFOmVaNk%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;800&quot; height=&quot;410&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;482&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Factory Method 구조도&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. Product&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;일반적인 객체 생성 코드를 부모클래스의 팩토리 메서드 내부에 배치한다. &lt;br /&gt;팩토리 메서드는 생성한 객체를 반환하며, 이 객체를 &lt;b&gt;Product (제품)&lt;/b&gt;라고 부른다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;현재 문제상황에서 Product는 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;Transport&lt;/span&gt; (Truck, Ship 등)이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;여러 종류의 Product는 공통적인 프로토콜을 채택해야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;클라이언트 (팩토리 메서드를 사용하는 코드)는 Product 간의 차이를 모른다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. Creator&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;위에서 설명한 부모클래스 역할을 Creator 클래스가 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;현재 문제상황에서 Creator는 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;Logistics&lt;/span&gt; (Road&lt;span&gt;Logistics, Sea&lt;span&gt;Logistics 등)이다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이제&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;자식클래스에서 팩토리 메서드를 override&lt;/span&gt;하여 반환하는 Product 타입을 변경할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;네이밍에서 예상되는 것과 달리, Creator의 책임은 제품을 생성하는 게 아니라 제품과 관련된 핵심 비즈니스 로직을 처리하는 것이다. &lt;/span&gt;&lt;b&gt;팩토리 메서드는 이러한 비즈니스 로직을 Concrete Product 클래스로부터 분리 (디커플링)&lt;/b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;하는 데 도움을 준다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예시 코드&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;부모클래스 Logistics는 팩토리 메서드에서 Truck (default transport 개념)을 생성하도록 했고,&lt;br /&gt;자식클래스인 Road/SeaLogistics는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;팩토리 메서드를 override&lt;/b&gt;하여 각각 Truck/Ship을 자체적으로 생성한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;swift&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;// MARK: - 1️⃣ Product
protocol Transport {
    func deliver()
}
class Truck: Transport {  // Concrete Product-1
    func deliver() {
        print(&quot;Truck - delivery started&quot;)
    }
}
class Ship: Transport {  // Concrete Product-2
    func deliver() {
        print(&quot;Ship - delivery started&quot;)
    }
}

// MARK: - 2️⃣ Creator
class Logistics {
    func createTransport() -&amp;gt; Transport {
        let defaultTransport = Truck()
        print(&quot;Truck - created&quot;)
        return defaultTransport
    }
    
    func service() {
        let transport: Transport = createTransport()
        print(&quot;Packages on board&quot;)
        transport.deliver()
    }
}
class RoadLogistics: Logistics {  // Concrete Creator-1
    override func createTransport() -&amp;gt; Transport {
        print(&quot;Truck - created&quot;)
        return Truck()
    }
}
class SeaLogistics: Logistics {  // Concrete Creator-2
    override func createTransport() -&amp;gt; Transport {
        print(&quot;Ship - created&quot;)
        return Ship()
    }
}

// MARK: - ✅ Client
let roadLogistics = RoadLogistics()
roadLogistics.service()
//Truck - created
//Packages on board
//Truck - delivery started

let seaLogistics = SeaLogistics()
seaLogistics.service()
//Ship - created
//Packages on board
//Ship - delivery started&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;활용안을 상상해보면 아래 형태일듯&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1673699687221&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if destinationType == .sameLand {
    let roadLogistics = RoadLogistics()
    roadLogistics.service()
} else {
    let seaLogistics = SeaLogistics()
    seaLogistics.service()
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;장점&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;데이터베이스 연결, 파일시스템, 네트워크 등 대규모 객체를 처리하는 상황에서 시스템 리소스를 절약할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Creator와 Concrete Product의 결합도를 낮춘다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;제품 초기화 기능을 한 객체에서 전담하므로 &lt;b&gt;단일책임 원칙&lt;/b&gt;을 준수한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;클라이언트 코드에 영향을 주지 않은 채로 새로운 Product를 도입할 수 있으므로&lt;span style=&quot;color: #ee2323;&quot;&gt; &lt;b&gt;개방폐쇄 원칙&lt;/b&gt;&lt;/span&gt;을 준수한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;단점&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;새로운 자식클래스를 생성해야 하므로 코드 복잡도가 높아진다. &lt;br /&gt;따라서 Creator 클래스의 기존 계층구도에 패턴을 도입하는 것이 좋다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference&lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt; &lt;a href=&quot;https://www.goodreads.com/book/show/43125355-dive-into-design-patterns&quot;&gt;도서 &amp;lt;Dive into Design Patterns&amp;gt;, Alexander Shvets 저&lt;/a&gt;&lt;/span&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;&lt;span style=&quot;background-color: #ffffff; color: #555555; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>프로그래밍 철학</category>
      <category>Factory Method</category>
      <category>GOF</category>
      <category>디자인 패턴</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/67</guid>
      <comments>https://applecider2020.tistory.com/67#entry67comment</comments>
      <pubDate>Sat, 14 Jan 2023 18:29:21 +0900</pubDate>
    </item>
    <item>
      <title>[Blackhole] 맥북 소리 포함시켜서 화면녹화 (간단)</title>
      <link>https://applecider2020.tistory.com/66</link>
      <description>&lt;div&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;맥북의 기본 화면녹화 기능을 사용하면, 소리가 포함되지 않는 문제가 있었다.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;iShowUInstatnt는 유료화됐고, SoundFlower는 M1/Big Sur 이후 비정상 작동해서 &lt;b&gt;BlackHole&lt;/b&gt;을 사용하게 됐다.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;다중출력기기&lt;/b&gt; 설정이 필요한데 매우 쉽다.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. BlackHole 설치&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://existential.audio/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Existential Audio&lt;/a&gt;에서 최상단의 BlackHole &lt;b&gt;2ch 버전&lt;/b&gt;을 다운받으면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이메일 인증만 하면&lt;b&gt; 무료&lt;/b&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 style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이때 &lt;b&gt;사운드 설정&lt;/b&gt;에서 &lt;b&gt;출력&lt;/b&gt;을 BlackHole 2ch로 설정하면, 화면녹화 시 소리가 포함된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;하지만 스피커 출력이 되지 않아서 화면녹화 도중에 소리를 들을 수 없다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그래서 아래의 다중출력기기 설정이 필요하다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. 다중출력기기 설정&lt;/span&gt;&lt;/h2&gt;
&lt;div&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;오디오 MIDI 설정&lt;/b&gt;에서 &lt;b&gt;다중출력기기&lt;/b&gt; 설정을 해야 한다.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;*다중출력 = BlackHole과 맥북 스피커에 동시에 소리가 출력되도록 해줌&lt;/span&gt;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;먼저 좌하단의 &lt;b&gt;+ 버튼&lt;/b&gt; &amp;gt; &lt;b&gt;다중출력기기 생성&lt;/b&gt;을 누르고,&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;BlackHole, 맥북 스피커에 &lt;b&gt;체크박스&lt;/b&gt;를 클릭한다.&lt;/span&gt;&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;763&quot; data-origin-height=&quot;580&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FCeTR/btrWbq35H9p/jEWxiQjcqCmwgADQto1bV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FCeTR/btrWbq35H9p/jEWxiQjcqCmwgADQto1bV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FCeTR/btrWbq35H9p/jEWxiQjcqCmwgADQto1bV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFCeTR%2FbtrWbq35H9p%2FjEWxiQjcqCmwgADQto1bV0%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;860&quot; height=&quot;654&quot; data-origin-width=&quot;763&quot; data-origin-height=&quot;580&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;div&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그리고 사운드 설정에서 출력을&lt;b&gt; 다중출력기기&lt;/b&gt;로 바꾼다.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;306&quot; data-origin-height=&quot;302&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEyYhI/btrWbkpjfLW/vodgmUuKMpfFvUoueFvreK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEyYhI/btrWbkpjfLW/vodgmUuKMpfFvUoueFvreK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEyYhI/btrWbkpjfLW/vodgmUuKMpfFvUoueFvreK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEyYhI%2FbtrWbkpjfLW%2FvodgmUuKMpfFvUoueFvreK%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;320&quot; height=&quot;316&quot; data-origin-width=&quot;306&quot; data-origin-height=&quot;302&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3. 화면녹화 옵션 설정&lt;/span&gt;&lt;/h2&gt;
&lt;div&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;마지막으로 화면녹화 시 &lt;b&gt;옵션&lt;/b&gt;에서&lt;b&gt; BlackHole 2ch&lt;/b&gt;를 선택한다.&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;녹화에 필요한 사운드는&amp;nbsp;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;BlackHole 출력만 있으면 되기 때문이다. 이제 끝!&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;*화면녹화 단축키 : Cmd + Shift + 5&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;882&quot; data-origin-height=&quot;952&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d9EpIu/btrWdffWgCW/djthLS9OoIZHbX70v4qaM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d9EpIu/btrWdffWgCW/djthLS9OoIZHbX70v4qaM1/img.png&quot; data-alt=&quot;화면녹화 시 보이는 옵션 창&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d9EpIu/btrWdffWgCW/djthLS9OoIZHbX70v4qaM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd9EpIu%2FbtrWdffWgCW%2FdjthLS9OoIZHbX70v4qaM1%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;600&quot; height=&quot;648&quot; data-origin-width=&quot;882&quot; data-origin-height=&quot;952&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;화면녹화 시 보이는 옵션 창&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&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: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;BlackHole 다운로드 - &lt;a href=&quot;https://existential.audio/&quot;&gt;Existential Audio&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;YouTube &amp;gt; &lt;a href=&quot;https://www.youtube.com/watch?v=0Ce0HEwpC48&amp;amp;t=65s&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;다중출력기기 추가 설명&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Blog &amp;gt; &lt;a href=&quot;https://creavart.tistory.com/392&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;맥 화면 녹화 소리 포함 시키는 방법&lt;/a&gt;&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>비전공자용 노력/개발 툴</category>
      <category>blackhole</category>
      <category>MIDI</category>
      <category>다중출력기기</category>
      <category>소리 포함 화면녹화</category>
      <category>화면녹화</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/66</guid>
      <comments>https://applecider2020.tistory.com/66#entry66comment</comments>
      <pubDate>Fri, 13 Jan 2023 20:24:01 +0900</pubDate>
    </item>
    <item>
      <title>[레거시] Convert Objective-C to Swift 사이트</title>
      <link>https://applecider2020.tistory.com/65</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;현업에서 Objective-C로 작성된 레거시 코드를 이해해야 할 때 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;span&gt;근데 공부하기는 싫을 때...&lt;/span&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;Object-C를 Swift로 변환해주는 기능&lt;/b&gt;을 사용하면 좋다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;a href=&quot;https://swiftify.com/converter/code/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;SWIFTIFY&lt;/a&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swift화 해준다는 뜻의 &lt;a href=&quot;https://swiftify.com/converter/code/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;SWIFTIFY&lt;/a&gt;라는 사이트를 추천한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #9d9d9d;&quot;&gt;사이트 네이밍 잘 지었다.&lt;/span&gt;&lt;/i&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;Objective-C 코드를 넣고 하단의 Convert Now 버튼을 탭하면 Swift로 변환이 된다.&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;b&gt;0.5KB&lt;/b&gt;을 초과하면 Sign in (회원가입)을 하라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;무료&lt;/b&gt; 서비스지만 회원가입 시&amp;nbsp;&lt;b&gt;2회/일&lt;/b&gt;로 횟수제한이 있어서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;귀찮아도 회원가입 안하고 코드를 여러 번 복붙하면서 쓰고 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2978&quot; data-origin-height=&quot;1126&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Tpu1k/btrVWf89fW0/RkIXPTNzKLUghehLA6S10K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Tpu1k/btrVWf89fW0/RkIXPTNzKLUghehLA6S10K/img.png&quot; data-alt=&quot;변환 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Tpu1k/btrVWf89fW0/RkIXPTNzKLUghehLA6S10K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTpu1k%2FbtrVWf89fW0%2FRkIXPTNzKLUghehLA6S10K%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;800&quot; height=&quot;302&quot; data-origin-width=&quot;2978&quot; data-origin-height=&quot;1126&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;변환 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ChatGPT&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;23년 2월 업데이트&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;이제 Swiftify 같은 거 필요없다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ChatGPT한테 물어보면 길이가 긴 코드도 Swift로 바꿔주고, 뜻도 설명해준다.&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;a href=&quot;https://stackoverflow.com/questions/38435920/backslashes-added-in-json-string-for-web-service-in-swift&quot;&gt;StackOverflow&lt;/a&gt;에서 objc로 작성된 코드가 궁금했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래처럼 검색하면 된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;convert this objective-c code to Swift code.&lt;br /&gt;{objc 코드 복붙하기}&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;convert this objective-c code to Swift code.&lt;br /&gt;NSMutableURLRequest *authRequest = [[[NSURLRequest alloc] initWithURL:authURL] mutableCopy]; [authRequest setHTTPMethod:@&quot;POST&quot;]; NSURLSession *session = [NSURLSession sharedSession]; [authRequest setValue:@&quot;application/json&quot; forHTTPHeaderField:@&quot;Content-Type&quot;]; NSDictionary *bodyDictionary = @{@&quot;User_Name&quot;: user, @&quot;Password_Hash&quot;: password}; if ([NSJSONSerialization isValidJSONObject:bodyDictionary]) { NSError *error; NSData *bodyData = [NSJSONSerialization dataWithJSONObject:bodyDictionary options:0 error:&amp;amp;error]; if (!error) { [authRequest setHTTPBody:bodyData]; } else { NSLog(@&quot;Unable to convert to JSON DATA %@&quot;, error.localizedDescription); } }&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 ChatGPT의 답변이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;놀랍다..&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1502&quot; data-origin-height=&quot;1298&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYViiL/btr0H7dlyUZ/Q7eOuRM2IStEU6NrK061Zk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYViiL/btr0H7dlyUZ/Q7eOuRM2IStEU6NrK061Zk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYViiL/btr0H7dlyUZ/Q7eOuRM2IStEU6NrK061Zk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYViiL%2Fbtr0H7dlyUZ%2FQ7eOuRM2IStEU6NrK061Zk%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;800&quot; height=&quot;691&quot; data-origin-width=&quot;1502&quot; data-origin-height=&quot;1298&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Cheat sheet&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Objective-C 및 Swift를 비교한 &lt;a href=&quot;https://www.hackingwithswift.com/articles/114/objective-c-to-swift-conversion-cheat-sheet&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Cheat sheet&lt;/a&gt;을 활용하는 것도 좋다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1482&quot; data-origin-height=&quot;1002&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/telvh/btrVZ7bEmcf/lhjUletKg1KBUUj3rUgztK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/telvh/btrVZ7bEmcf/lhjUletKg1KBUUj3rUgztK/img.png&quot; data-alt=&quot;Objective-C 때 코딩 안배워서 다행이다..&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/telvh/btrVZ7bEmcf/lhjUletKg1KBUUj3rUgztK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ftelvh%2FbtrVZ7bEmcf%2FlhjUletKg1KBUUj3rUgztK%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;800&quot; height=&quot;541&quot; data-origin-width=&quot;1482&quot; data-origin-height=&quot;1002&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Objective-C 때 코딩 안배워서 다행이다..&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1508&quot; data-origin-height=&quot;404&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/L9haC/btrVZ73MBqu/KR7TRFmHdw9hxKtCBGTCh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/L9haC/btrVZ73MBqu/KR7TRFmHdw9hxKtCBGTCh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/L9haC/btrVZ73MBqu/KR7TRFmHdw9hxKtCBGTCh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FL9haC%2FbtrVZ73MBqu%2FKR7TRFmHdw9hxKtCBGTCh0%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;800&quot; height=&quot;214&quot; data-origin-width=&quot;1508&quot; data-origin-height=&quot;404&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1468&quot; data-origin-height=&quot;370&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9FjB5/btrVZW9jVln/P28fuEaksL9yPa8hxkPjR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9FjB5/btrVZW9jVln/P28fuEaksL9yPa8hxkPjR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9FjB5/btrVZW9jVln/P28fuEaksL9yPa8hxkPjR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9FjB5%2FbtrVZW9jVln%2FP28fuEaksL9yPa8hxkPjR0%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;800&quot; height=&quot;202&quot; data-origin-width=&quot;1468&quot; data-origin-height=&quot;370&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1484&quot; data-origin-height=&quot;744&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k41Xo/btrVZtl3j1w/f9aIIHfcjEE3klPmQBkL30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k41Xo/btrVZtl3j1w/f9aIIHfcjEE3klPmQBkL30/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k41Xo/btrVZtl3j1w/f9aIIHfcjEE3klPmQBkL30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk41Xo%2FbtrVZtl3j1w%2Ff9aIIHfcjEE3klPmQBkL30%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;800&quot; height=&quot;401&quot; data-origin-width=&quot;1484&quot; data-origin-height=&quot;744&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1476&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dvtCBb/btrV1VnMJuF/jhsqN054BJaRmzVEAaHVkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dvtCBb/btrV1VnMJuF/jhsqN054BJaRmzVEAaHVkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dvtCBb/btrV1VnMJuF/jhsqN054BJaRmzVEAaHVkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdvtCBb%2FbtrV1VnMJuF%2FjhsqN054BJaRmzVEAaHVkk%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;800&quot; height=&quot;542&quot; data-origin-width=&quot;1476&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1480&quot; data-origin-height=&quot;396&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cihiL8/btrVYkv9luA/H6PHpx6iVkBsk48ruFGQdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cihiL8/btrVYkv9luA/H6PHpx6iVkBsk48ruFGQdK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cihiL8/btrVYkv9luA/H6PHpx6iVkBsk48ruFGQdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcihiL8%2FbtrVYkv9luA%2FH6PHpx6iVkBsk48ruFGQdK%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;800&quot; height=&quot;214&quot; data-origin-width=&quot;1480&quot; data-origin-height=&quot;396&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색 키워드 : convert&amp;nbsp;objc&amp;nbsp;to&amp;nbsp;swift&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; font-size: 16px; letter-spacing: 0px;&quot;&gt;- Reference&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://swiftify.com/converter/code/&quot;&gt;SWIFTIFY&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Hacking with Swift &amp;gt; &lt;a href=&quot;https://www.hackingwithswift.com/articles/114/objective-c-to-swift-conversion-cheat-sheet&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Cheat sheet&lt;/a&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>Swift</category>
      <category>CheatSheet</category>
      <category>Objective-C</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/65</guid>
      <comments>https://applecider2020.tistory.com/65#entry65comment</comments>
      <pubDate>Wed, 11 Jan 2023 20:23:38 +0900</pubDate>
    </item>
    <item>
      <title>[UIImage] withRenderingMode(.alwaysTemplate)은 언제 쓸까? - tintColor가 적용되는 영역만 남길 때</title>
      <link>https://applecider2020.tistory.com/64</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Profile 화면을 구현하다가 &lt;b&gt;컬러가 2개 이상 적용된 이미지&lt;/b&gt;를 사용했을 때&amp;nbsp;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이미지 전체가 &lt;b&gt;tintColor&lt;/b&gt;로 뒤덮이는 문제가 발생했다.&lt;br /&gt;(왼쪽처럼 구현하고 싶었는데, 오른쪽처럼 나타남)&lt;/span&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-origin-width=&quot;1038&quot; data-origin-height=&quot;172&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YYrIV/btrU298SvPQ/gLglw4chbXYZkk0DmkqW90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YYrIV/btrU298SvPQ/gLglw4chbXYZkk0DmkqW90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YYrIV/btrU298SvPQ/gLglw4chbXYZkk0DmkqW90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYYrIV%2FbtrU298SvPQ%2FgLglw4chbXYZkk0DmkqW90%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;600&quot; height=&quot;99&quot; data-origin-width=&quot;1038&quot; data-origin-height=&quot;172&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;대부분의 화면에서 이미지를 올릴 때&amp;nbsp;&lt;b&gt;renderingMode&lt;/b&gt;를 .alwaysTemplate으로 설정했는데, &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;이미지의 renderingMode 3개 종류인 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;automatic/alwaysOriginal/alwaysTemplate&lt;/span&gt;을 정리했다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;결론&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2개 종류의 이미지를 일반 화면 및 TabBar에 올리고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rendering Mode를 automatic, alwaysOriginal, alwaysTemplate 순으로 지정해봤다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Custom Image : 흰색, 회색&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;2개 컬러가 사용된 이미지&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;System Image :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;SF Symbol&lt;/b&gt;을 활용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 4개 이미지 모두 tintColor를 yellow로 지정했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0SIyF/btrUV3vGInE/ST4q8lKR7gdWCjniKiUVxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0SIyF/btrUV3vGInE/ST4q8lKR7gdWCjniKiUVxK/img.png&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;2556&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0SIyF/btrUV3vGInE/ST4q8lKR7gdWCjniKiUVxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0SIyF%2FbtrUV3vGInE%2FST4q8lKR7gdWCjniKiUVxK%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;1179&quot; height=&quot;2556&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/E4zVX/btrUXo0p4Md/pu7JFnPPQ8WQ0Vt8YwNQ2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/E4zVX/btrUXo0p4Md/pu7JFnPPQ8WQ0Vt8YwNQ2k/img.png&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;2556&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/E4zVX/btrUXo0p4Md/pu7JFnPPQ8WQ0Vt8YwNQ2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FE4zVX%2FbtrUXo0p4Md%2Fpu7JFnPPQ8WQ0Vt8YwNQ2k%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;1179&quot; height=&quot;2556&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o6xNU/btrU2JvDfTq/oC03SfKk9j4m90FsK8tV20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o6xNU/btrU2JvDfTq/oC03SfKk9j4m90FsK8tV20/img.png&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;2556&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.5581%;&quot; data-widthpercent=&quot;33.34&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o6xNU/btrU2JvDfTq/oC03SfKk9j4m90FsK8tV20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo6xNU%2FbtrU2JvDfTq%2FoC03SfKk9j4m90FsK8tV20%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;1179&quot; height=&quot;2556&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;Redering Mode 지정 (왼쪽부터 automatic, alwaysOriginal, alwaysTemplate 순)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2개 색상이 사용된 Custom 이미지를 먼저 보자.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;일반 화면 (TabBar가 아닌 영역) : alwaysTemplate을 적용하면 이미지가 날아간다.&lt;/li&gt;
&lt;li&gt;TabBar 영역 : automatic, alwaysTemplate을 적용하면 이미지가 날아간다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;즉, &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;2개 색상이 사용된 이미지&lt;/b&gt;는 원본 색상을 적용해야 하므로&amp;nbsp;&lt;b&gt;alwaysOriginal&lt;/b&gt;을 쓰면 된다!&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;SF Symbol을 사용한 System 이미지를 보자.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;alwaysOriginal이 아닐 때, 의도대로 투명한 사람 부분에 backgroundColor가 나타난다.&lt;/li&gt;
&lt;li&gt;즉, &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;1개 색상이 사용된&amp;nbsp;이미지&lt;/b&gt; 또는 System 이미지에 tintColor를 적용해야 하므로&amp;nbsp;&lt;b&gt;alwaysTemplate&lt;/b&gt;을 쓰면 된다!&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;automatic&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;&lt;b&gt;alwaysOriginal&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;&lt;b&gt;alwaysTemplate&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;일반 화면 - Custom Image&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;원본&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;원본&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;이미지 날아감&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;일반 화면 -&amp;nbsp;System Image&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;&lt;span style=&quot;color: #1b711d;&quot;&gt;투명한 영역 투명하게 보임&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;&lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;원본&lt;br /&gt;(투명한 부분도 원본색으로 보임)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #1b711d;&quot;&gt;투명한 영역 투명하게 보임&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;Tab Bar - Custom Image&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;이미지 날아감&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;원본&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;이미지 날아감&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;Tab Bar - System Image&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #1b711d;&quot;&gt;투명한 영역 투명하게 보임&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;원본&lt;br /&gt;&lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;(투명한 부분도 원본색으로 보임)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #1b711d;&quot;&gt;투명한 영역 투명하게 보임&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Rendering Mode&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/uiimage/renderingmode&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;UIImage.RenderingMode 공식문서&lt;/a&gt;부터 보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1514&quot; data-origin-height=&quot;166&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DSgVf/btrU2KOS2hg/h530eOOVY0GWXlyOU2ZRZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DSgVf/btrU2KOS2hg/h530eOOVY0GWXlyOU2ZRZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DSgVf/btrU2KOS2hg/h530eOOVY0GWXlyOU2ZRZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDSgVf%2FbtrU2KOS2hg%2Fh530eOOVY0GWXlyOU2ZRZK%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;800&quot; height=&quot;88&quot; data-origin-width=&quot;1514&quot; data-origin-height=&quot;166&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;rendering mode란 UIKit이 &lt;b&gt;이미지의 색상 정보를 어떻게 활용해서 이미지를 나타낼지&lt;/b&gt; 제어한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1456&quot; data-origin-height=&quot;326&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EdqBP/btrUWaaiY5L/azPsmoc8qeKKJkWrpM832K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EdqBP/btrUWaaiY5L/azPsmoc8qeKKJkWrpM832K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EdqBP/btrUWaaiY5L/azPsmoc8qeKKJkWrpM832K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEdqBP%2FbtrUWaaiY5L%2FazPsmoc8qeKKJkWrpM832K%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;800&quot; height=&quot;179&quot; data-origin-width=&quot;1456&quot; data-origin-height=&quot;326&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;automatic : default&lt;/li&gt;
&lt;li&gt;alwaysOriginal : 항상 원본 이미지를 나타낸다. &lt;span style=&quot;color: #ee2323;&quot;&gt;(원본의 색상 정보대로 그린다는 뜻)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;alwaysTemplate : 원본 색상 정보를 무시하고, *template 이미지를 나타낸다. &lt;span style=&quot;color: #ee2323;&quot;&gt;(tintColor가 적용되는 부분만 남긴다는 뜻)❗️&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;template 이미지는 &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/uiimage/providing_images_for_different_appearances&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Article - &lt;/a&gt;&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/uiimage/providing_images_for_different_appearances&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Providing images for different appearances&lt;/a&gt;에서 자세히 설명한다.&lt;br /&gt;내용 중&lt;b&gt; tintable images&lt;/b&gt;를 간단히 보자.&lt;br /&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 style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&lt;span&gt;말로 설명하면 어려운데 그림으로 보면 쉽다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이미지 중 투명한 영역은 backgroundColor가 보인다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이미지 중 100% opaque 영역에는 tintColor가 100% 적용되고, &lt;/span&gt;&lt;span&gt;25%&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;opaque 영역에는 tintColor가 25% 적용된다.&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;즉, tintColor는 &lt;b&gt;이미지 중 투명한 영역이 아닌 부분에 적용된다.&lt;br /&gt;&lt;/b&gt;그래서 tintColor가 적용되는 영역을 본 뜬 것 (색상만 바꾸면 되는 틀 역할)을 &lt;b&gt;template image&lt;/b&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;i&gt;tintColor 맨날 썼는데 이번에 처음 알았음..&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;330&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JJvGS/btrUYP4Av7w/vadk3kFwJ5tt69yh6wNFkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JJvGS/btrUYP4Av7w/vadk3kFwJ5tt69yh6wNFkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JJvGS/btrUYP4Av7w/vadk3kFwJ5tt69yh6wNFkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJJvGS%2FbtrUYP4Av7w%2Fvadk3kFwJ5tt69yh6wNFkk%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;800&quot; height=&quot;206&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;330&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이왕 찾아본 김에 &lt;a href=&quot;https://developer.apple.com/documentation/uikit/uiimageview/1621059-tintcolor&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;tintColor 공식문서&lt;/a&gt;도 확인해보자.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1566&quot; data-origin-height=&quot;680&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sH9zJ/btrUYqqmVM5/KQfBPQWP52YRDHjUJxtDd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sH9zJ/btrUYqqmVM5/KQfBPQWP52YRDHjUJxtDd0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sH9zJ/btrUYqqmVM5/KQfBPQWP52YRDHjUJxtDd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsH9zJ%2FbtrUYqqmVM5%2FKQfBPQWP52YRDHjUJxtDd0%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;800&quot; height=&quot;347&quot; data-origin-width=&quot;1566&quot; data-origin-height=&quot;680&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;tintColor는 &lt;b&gt;view hierarchy에 있는&lt;/b&gt; (== UIImageView 위에 올린 모든 view에 적용됨)&lt;br /&gt;&lt;b&gt;template images를 tint&lt;/b&gt;하는 색깔이다.&lt;/li&gt;
&lt;li&gt;위에서 다 정리했던 내용이라서 이해하기가 쉬웠다.&lt;/li&gt;
&lt;li&gt;즉, &lt;span style=&quot;color: #ee2323;&quot;&gt;template images에 적용되므로 Rendering mode를 .alwaysTemplate으로 설정해야 한다!&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예제 코드&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;예제 코드로 화면에 나타난 이미지를 확인하며 마무리해보자.&lt;br /&gt;전체 코드는 &lt;a href=&quot;https://github.com/just1103/Samples/tree/main/Samples&quot;&gt;GitHub Repo&lt;/a&gt;를 참고&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1672483901645&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private func makeLayout() {
    // 1️⃣ ImageView (custom)
    // ✅ 주의 - withRenderingMode()는 속성을 바꾼 새로운 image를 반환함 
    let returnedCustomImage = UIImage(named: &quot;custom_profile_fill&quot;)?.withRenderingMode(.alwaysOriginal)
//  let customImage = UIImage(named: &quot;custom_profile_fill&quot;)
//  let returnedCustomImage = customImage?.withRenderingMode(.automatic)       // 1
//  let returnedCustomImage = customImage?.withRenderingMode(.alwaysOriginal)  // 2
//  let returnedCustomImage = customImage?.withRenderingMode(.alwaysTemplate)  // 3
    customImageView.image = returnedCustomImage
    customImageView.tintColor = .customYellow

    // 2️⃣ ImageView (system)
    let sfImage = UIImage(systemName: &quot;person.circle.fill&quot;)
//  let returnedSFImage = sfImage?.withRenderingMode(.automatic)       // 1
    let returnedSFImage = sfImage?.withRenderingMode(.alwaysOriginal)  // 2
//  let returnedSFImage = sfImage?.withRenderingMode(.alwaysTemplate)  // 3
    systemImageView.image = returnedSFImage
    systemImageView.tintColor = .customYellow

    // 3️⃣ Tab Bar (custom)
    let customBarItemImage = UIImage(named: &quot;custom_profile_fill&quot;)
//  let returnedCustomBarItemImage = customBarItemImage?.withRenderingMode(.automatic)       // 1
    let returnedCustomBarItemImage =  customBarItemImage?.withRenderingMode(.alwaysOriginal) // 2
//  let returnedCustomBarItemImage =  customBarItemImage?.withRenderingMode(.alwaysTemplate) // 3
    customBarItem.image = returnedCustomBarItemImage

    // 4️⃣ Tab Bar (system)
    let systemBarItemImage = UIImage(systemName: &quot;person.circle.fill&quot;)
//  let returnedSystemBarItemImage = systemBarItemImage?.withRenderingMode(.automatic)      // 1
    let returnedSystemBarItemImage = systemBarItemImage?.withRenderingMode(.alwaysOriginal) // 2
//  let returnedSystemBarItemImage = systemBarItemImage?.withRenderingMode(.alwaysTemplate) // 3
    systemBarItem.image = returnedSystemBarItemImage

    mainTabBar.tintColor = .customYellow
    mainTabBar.barTintColor = .customGreen
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. automatic&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;default값이다.&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Custom 이미지 : 일반 화면에서는 잘 보이지만, &lt;span style=&quot;color: #ee2323;&quot;&gt;Tab Bar 영역에 올린 이미지는 날아간다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;System 이미지 : 일반/Tab Bar에서 모두&lt;span style=&quot;color: #1b711d;&quot;&gt; tintColor가 잘 적용된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1068&quot; data-origin-height=&quot;1112&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zNmDb/btrUV33ypsU/ykfeXxD9uVw0ccvuMDOq2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zNmDb/btrUV33ypsU/ykfeXxD9uVw0ccvuMDOq2k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zNmDb/btrUV33ypsU/ykfeXxD9uVw0ccvuMDOq2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzNmDb%2FbtrUV33ypsU%2FykfeXxD9uVw0ccvuMDOq2k%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;600&quot; height=&quot;625&quot; data-origin-width=&quot;1068&quot; data-origin-height=&quot;1112&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. alwaysOriginal&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Custom 이미지 : 원본 이미지의 색상을 사용하므로&lt;span style=&quot;color: #ee2323;&quot;&gt; tintColor가 적용되지 않는다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;System 이미지 : 사람 부분에 원본 이미지 (흰색)이 적용되고, 나머지 부분에만 tintColor가 적용된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  이상하게 System 이미지에는 tintColor가 적용된다?!&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;위에서 살펴본대로 alwaysOriginal을&lt;/span&gt;&amp;nbsp;사용하면 원래는 template image를 만들지 않으므로&lt;br /&gt;이미지에 tintColor가 적용되지 않아야 하지만..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SF Symbol을 활용한 system 이미지는 자체 template image가 있어서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tintColor가 나타나는 거라고 예상된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;998&quot; data-origin-height=&quot;1034&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RWQHo/btrUYQJesWO/2QQAkEGHGqfcwdLMzX5IR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RWQHo/btrUYQJesWO/2QQAkEGHGqfcwdLMzX5IR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RWQHo/btrUYQJesWO/2QQAkEGHGqfcwdLMzX5IR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRWQHo%2FbtrUYQJesWO%2F2QQAkEGHGqfcwdLMzX5IR1%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;600&quot; height=&quot;622&quot; data-origin-width=&quot;998&quot; data-origin-height=&quot;1034&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3. alwaysTemplate&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;원본 이미지의 색상을 무시하고, tintColor를 적용할 수 있는 template image를 만든다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Custom 이미지 : 원본 이미지의 색상이 모두 무시되므로 &lt;span style=&quot;color: #ee2323;&quot;&gt;이미지 전체 영역에 tintColor가 적용되어 이미지가 날아간다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;System 이미지 : 일반/Tab Bar에서 모두&lt;span style=&quot;color: #1b711d;&quot;&gt; tintColor가 잘 적용된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;956&quot; data-origin-height=&quot;998&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Xa1Qd/btrU45E7VLd/DZBC6FKZcEEENbTPsZCcr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Xa1Qd/btrU45E7VLd/DZBC6FKZcEEENbTPsZCcr1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Xa1Qd/btrU45E7VLd/DZBC6FKZcEEENbTPsZCcr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXa1Qd%2FbtrU45E7VLd%2FDZBC6FKZcEEENbTPsZCcr1%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;600&quot; height=&quot;626&quot; data-origin-width=&quot;956&quot; data-origin-height=&quot;998&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;참고 - Assets에서 Rendering Mode 설정하기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;특정 이미지에 대한 Rendering Mode를 직접 설정할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1970&quot; data-origin-height=&quot;418&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ziw7F/btrU2JCwpjz/OKi0aKfrWGQ99io2rJNxtK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ziw7F/btrU2JCwpjz/OKi0aKfrWGQ99io2rJNxtK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ziw7F/btrU2JCwpjz/OKi0aKfrWGQ99io2rJNxtK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fziw7F%2FbtrU2JCwpjz%2FOKi0aKfrWGQ99io2rJNxtK%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;1970&quot; height=&quot;418&quot; data-origin-width=&quot;1970&quot; data-origin-height=&quot;418&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&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;span&gt; &lt;/span&gt;예제 코드 &amp;gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/just1103/Samples/tree/main/Samples&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;GitHub Repo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Apple Developer Documentation &amp;gt;&lt;span&gt; &lt;a href=&quot;https://developer.apple.com/documentation/uikit/uiimage/renderingmode&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;UIImage.RenderingMode&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Apple Developer Documentation &amp;gt;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/uiimage/providing_images_for_different_appearances&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Article -&amp;nbsp;&lt;/span&gt;Providing images for different appearances&lt;/a&gt;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Apple Developer Documentation &amp;gt; &lt;a href=&quot;https://developer.apple.com/documentation/uikit/uiimageview/1621059-tintcolor&quot;&gt;UIImageView - tintColor&lt;/a&gt;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>iOS</category>
      <category>Rendering mode</category>
      <category>Tintcolor</category>
      <category>UIImage</category>
      <category>withRenderingMode</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/64</guid>
      <comments>https://applecider2020.tistory.com/64#entry64comment</comments>
      <pubDate>Sat, 31 Dec 2022 20:13:15 +0900</pubDate>
    </item>
    <item>
      <title>[mailto] 기본 메일 앱 및 이메일 템플릿 띄우기 (간단)</title>
      <link>https://applecider2020.tistory.com/63</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;대부분의 상용 앱에는 My 탭 또는 설정 탭의 '문의하기' 항목이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;문의하기를 누르면&lt;b&gt; Mail 앱&lt;/b&gt; (시스템 기본 앱)을 띄우면서 이메일을 작성하도록 사용자를 유도할 수 있는데,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이때 수신자 &lt;b&gt;메일주소, 제목, 내용&lt;/b&gt;에 미리 원하는 값을 채워서 이메일 템플릿을 만들 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;pre id=&quot;code_1671347610040&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private func showEmailWithTemplate(subject: String, body: String) {
    // ✅ 이메일 템플릿 설정
    var components = URLComponents()
    components.scheme = &quot;mailto&quot; 
    components.path = &quot;whatWeEat_help@gmail.com&quot; // 메일 주소
    components.queryItems = [
          URLQueryItem(name: &quot;subject&quot;, value: subject), // 메일 제목
          URLQueryItem(name: &quot;body&quot;, value: body)        // 메일 내용
    ]
    
    guard let url = components.url else { return }

    // ✅ 현재 앱에서 기본 이메일 앱을 띄움
    if UIApplication.shared.canOpenURL(url) {
        UIApplication.shared.open(url, options: [:], completionHandler: nil)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1671547448745&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 사용부
let subject = &quot;Menu Inquiry&quot;
let body = &quot;&quot;&quot;
              Hello! Please fill out the following template.
           &quot;&quot;&quot; + &quot;\n\n&quot;
       +   &quot;&quot;&quot;
              1. Menu Name
              2. User Email
              3. Inquiry
           &quot;&quot;&quot; 

showEmailWithTemplate(subject: subject, body: body)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://applecider2020.tistory.com/56&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;AppStore 앱&lt;/a&gt; 또는 &lt;a href=&quot;https://applecider2020.tistory.com/57&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Settings 앱&lt;/a&gt;을 띄우는 것과 비슷하게&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;UIApplication.shared.canOpenURL()&lt;/span&gt;로 이동이 가능한지 확인하고,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;UIApplication.shared.open()&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;위 코드로 Mail 앱을 띄웠을 때 나타나는 내용은 아래와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;친절하게 Mail 앱에 이미 키보드 처리가 되어있기 때문에 별도로 키보드 올림/내림 처리를 할 필요가 없다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1290&quot; data-origin-height=&quot;2796&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cPlwwM/btrUatOdMc6/GZLOUrzApHj0yQ1cBpc760/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cPlwwM/btrUatOdMc6/GZLOUrzApHj0yQ1cBpc760/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cPlwwM/btrUatOdMc6/GZLOUrzApHj0yQ1cBpc760/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcPlwwM%2FbtrUatOdMc6%2FGZLOUrzApHj0yQ1cBpc760%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;400&quot; height=&quot;867&quot; data-origin-width=&quot;1290&quot; data-origin-height=&quot;2796&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색 키워드 - ios mailto scheme&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://stackoverflow.com/questions/8821934/launch-apple-mail-app-from-within-my-own-app&quot;&gt;Launch&amp;nbsp;Apple&amp;nbsp;Mail&amp;nbsp;App&amp;nbsp;from&amp;nbsp;within&amp;nbsp;my&amp;nbsp;own&amp;nbsp;App?&lt;/a&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Stackoverflow &amp;gt; &lt;a href=&quot;https://stackoverflow.com/questions/8821934/launch-apple-mail-app-from-within-my-own-app&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Launch&amp;nbsp;Apple&amp;nbsp;Mail&amp;nbsp;App&amp;nbsp;from&amp;nbsp;within&amp;nbsp;my&amp;nbsp;own&amp;nbsp;App?&lt;/a&gt;&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>iOS</category>
      <category>Mail Template</category>
      <category>mailto</category>
      <category>Scheme</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/63</guid>
      <comments>https://applecider2020.tistory.com/63#entry63comment</comments>
      <pubDate>Thu, 22 Dec 2022 07:00:35 +0900</pubDate>
    </item>
    <item>
      <title>[attributedString] UILabel의 텍스트 커스텀하기 - font, underline, strike, alignment, lineHeight, paragraphStyle</title>
      <link>https://applecider2020.tistory.com/62</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;나중에 복붙해서 사용하려고&lt;/span&gt;&lt;span&gt;UILabel의 attributedString 설정값을 간단히 정리해봤다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;226&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4wtSJ/btrTWNZXWZy/e7cubgdeZRI5V7rhUrybf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4wtSJ/btrTWNZXWZy/e7cubgdeZRI5V7rhUrybf1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4wtSJ/btrTWNZXWZy/e7cubgdeZRI5V7rhUrybf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4wtSJ%2FbtrTWNZXWZy%2Fe7cubgdeZRI5V7rhUrybf1%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;400&quot; height=&quot;133&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;226&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1671345998099&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private lazy var customLabel: UILabel = {
    let label = UILabel()
    label.translatesAutoresizingMaskIntoConstraints = false
    label.numberOfLines = 0

    let paragraphStyle = NSMutableParagraphStyle()
    paragraphStyle.lineBreakMode = .byTruncatingTail
    paragraphStyle.lineBreakStrategy = .hangulWordPriority
    paragraphStyle.alignment = .center
    paragraphStyle.minimumLineHeight = 21

    // ✅ NSMutableAttributedString 및 paragraphStyle 활용
    let fullText = &quot;안녕하세요. 애플사이다의 iOS 개발일지 블로그에 오셨네요. 메리 크리스마스. 저는 이따 아바타2 보러가요. 그럼 이만!&quot;
    let attributedText = NSMutableAttributedString(
        string: fullText,
        attributes: [.foregroundColor: UIColor.darkGray,
                     .font: UIFont.systemFont(ofSize: 18, weight: .bold),
                     .underlineStyle: NSUnderlineStyle.single.rawValue,
//                     .strikethroughStyle: NSUnderlineStyle.single.rawValue,
//                     .strikethroughColor: UIColor.systemRed,
                     .paragraphStyle: paragraphStyle])

    // ✅ 텍스트 일부에 효과주기
    attributedText.addAttribute(.font,
                                value: UIFont.systemFont(ofSize: 11),
                                range:(fullText as NSString).range(of: &quot;그럼 이만!&quot;))

    label.attributedText = attributedText
    return label
}()&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&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;span style=&quot;background-color: #ffffff; color: #333333;&quot;&gt; &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #5c5c5c;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;예제 코드 &amp;gt;&lt;/span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/just1103/Samples/tree/main/Samples/Sample_LineBreak&quot;&gt;GitHub Repo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Apple Developer Documentation &amp;gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/uilabel&quot;&gt;UILabel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333;&quot;&gt;관련 포스트&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://applecider2020.tistory.com/60&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;[줄바꿈]&amp;nbsp;lineBreakMode&amp;nbsp;및&amp;nbsp;lineBreakStrategy&amp;nbsp;차이점&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://applecider2020.tistory.com/61&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;[줄바꿈]&amp;nbsp;UILabel&amp;nbsp;attributedText에&amp;nbsp;lineBreak&amp;nbsp;설정&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>iOS</category>
      <category>AttributedString</category>
      <category>paragraphStyle</category>
      <category>UILabel</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/62</guid>
      <comments>https://applecider2020.tistory.com/62#entry62comment</comments>
      <pubDate>Wed, 21 Dec 2022 07:00:57 +0900</pubDate>
    </item>
    <item>
      <title>[줄바꿈] UILabel attributedText에 lineBreak 설정 - paragraphStyle.lineBreakMode</title>
      <link>https://applecider2020.tistory.com/61</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;지난 포스트&amp;nbsp;&lt;a href=&quot;https://applecider2020.tistory.com/60&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[줄바꿈] lineBreakMode 및 lineBreakStrategy 차이점&lt;/a&gt;을 작성하면서 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;UILabel에 attributedText를 사용하는 경우에는&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;label.lineBreakMode&lt;/span&gt;가 무시되고,&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;attributedText의 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;paragraphStyle.lineBreakMode&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;근데 실험해보니 잘못된 정보였음..  &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;둘 다 사용했을 때 &lt;b&gt;마지막에 설정한 값&lt;/b&gt;으로 적용되는 것을 확인했다.&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;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아래처럼 2개 방법으로 코드를 작성해서 비교해봤다.&lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;방법-1. &lt;span style=&quot;background-color: #dddddd;&quot;&gt;label.lineBreakMode&lt;/span&gt; 먼저 -&amp;gt; &lt;span style=&quot;background-color: #dddddd;&quot;&gt;paragraphStyle.lineBreakMode&lt;/span&gt; 나중에 설정&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;방법-2. &lt;span style=&quot;letter-spacing: 0px; background-color: #dddddd;&quot;&gt;paragraphStyle.lineBreakMode&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;먼저 -&amp;gt; &lt;span style=&quot;background-color: #dddddd;&quot;&gt;label.lineBreakMode&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; 나중에 설정&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1671341697666&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private func setupKoreanLayout() {
    // 방법-1. 먼저
    hangulKoreanLabel.lineBreakMode = .byTruncatingMiddle // ✅
    hangulKoreanLabel.lineBreakStrategy = .pushOut

    let paragraphStyle = NSMutableParagraphStyle()
    paragraphStyle.lineBreakMode = .byTruncatingTail // ✅
    paragraphStyle.lineBreakStrategy = .hangulWordPriority

    let text = &quot;이 프로퍼티는 줄임표(&amp;hellip;)로 잘라내거나 텍스트를 크으으을리핑하는 등 텍스트 시스템이 해당 컨테이너에 맞지 않는 줄을 배치하는 방법을 제어합니다. 이 속성은 줄임표(&amp;hellip;)로 잘라내거나 텍스트를 클리핑하는 등 텍스트 시스템이 해당 컨테이너에 맞지 않는 줄을 배치하는 방법을 제어합니다. 하하.&quot;
    let attributedText = NSMutableAttributedString(
        string: text,
        attributes: [.foregroundColor: UIColor.darkGray,
                     .font: UIFont.systemFont(ofSize: 14),
                     .underlineStyle: NSUnderlineStyle.single.rawValue,
                     .paragraphStyle: paragraphStyle])
    hangulKoreanLabel.attributedText = attributedText

    // 방법-2. 나중에
    // hangulKoreanLabel.lineBreakMode = .byTruncatingMiddle // ✅
    // hangulKoreanLabel.lineBreakStrategy = .pushOut
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-renderer-start-pos=&quot;129&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;방법-1 결과&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;나중에 호출된&lt;/span&gt; &lt;span style=&quot;background-color: #dddddd;&quot;&gt;paragraphStyle.lineBreakMode&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;의 설정값이 적용됐다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;412&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dYBVvR/btrTTpL9Kok/rUkNNIm6gvQjhetgK3amuK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dYBVvR/btrTTpL9Kok/rUkNNIm6gvQjhetgK3amuK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dYBVvR/btrTTpL9Kok/rUkNNIm6gvQjhetgK3amuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdYBVvR%2FbtrTTpL9Kok%2FrUkNNIm6gvQjhetgK3amuK%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;400&quot; height=&quot;242&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;412&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-renderer-start-pos=&quot;129&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;방법-2 결과&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;나중에 호출된 &lt;/span&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;label.lineBreakMode&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;의 설정값이 적용됐다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;공식문서에 따르면 시스템이 label.lineBreakMode를 무시해서 방법-1과 동일한 결과가 나올 거라고 예상했는데&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아니었음..&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;678&quot; data-origin-height=&quot;416&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XZxd4/btrTRBNmuNS/5fpp8FphEsw5kS7irLtKpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XZxd4/btrTRBNmuNS/5fpp8FphEsw5kS7irLtKpk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XZxd4/btrTRBNmuNS/5fpp8FphEsw5kS7irLtKpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXZxd4%2FbtrTRBNmuNS%2F5fpp8FphEsw5kS7irLtKpk%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;400&quot; height=&quot;245&quot; data-origin-width=&quot;678&quot; data-origin-height=&quot;416&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;결론&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #ee2323;&quot;&gt;둘 다 사용했을 때&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;마지막에 설정한 값&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #ee2323;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/uilabel/3667730-linebreakstrategy&quot;&gt;UILabel - lineBreakStrategy&lt;/a&gt; 공식문서에 보면&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;attributedString이 있는 경우, 시스템은 label.lineBreakMode를 무시하므로&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;attributedString의 paragraphStyle.lineBreakMode를 설정해야 한다고 설명하고 있는데..&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;공식문서가 틀린 것 같다..?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아니면 예제 코드에서 NSParagraphStyle 대신 &lt;a href=&quot;https://developer.apple.com/documentation/uikit/nsmutableparagraphstyle&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;NSMutableParagraphStyle&lt;/a&gt;을 써서 그렇거나.. (아마 이게 이유인듯)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1504&quot; data-origin-height=&quot;388&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czkDUj/btrTSxXZ3U9/KOQ9JCRmI6Mo5OvPRQv5pk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czkDUj/btrTSxXZ3U9/KOQ9JCRmI6Mo5OvPRQv5pk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czkDUj/btrTSxXZ3U9/KOQ9JCRmI6Mo5OvPRQv5pk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FczkDUj%2FbtrTSxXZ3U9%2FKOQ9JCRmI6Mo5OvPRQv5pk%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;800&quot; height=&quot;206&quot; data-origin-width=&quot;1504&quot; data-origin-height=&quot;388&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;개인적으로 혼동을 막기 위해 attributedString을 사용할 때는 paragraphStyle.lineBreakMode를 사용하도록&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;검색 키워드 : label attributedText lineBreak&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;-&amp;nbsp;&lt;a href=&quot;https://stackoverflow.com/questions/16831207/uilabel-attributedtext-with-multiple-line-break-modes&quot;&gt;UILabel attributedText with multiple line break modes&lt;/a&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference&lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #5c5c5c;&quot;&gt; &amp;nbsp;&lt;/span&gt;예제 코드 &amp;gt;&amp;nbsp;&lt;a href=&quot;https://github.com/just1103/Samples/tree/main/Samples/Sample_LineBreak&quot;&gt;GitHub Repo&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple Developer Documentation &amp;gt;&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/nsparagraphstyle/1529937-linebreakmode&quot;&gt;NSParagraphStyle&amp;nbsp;-&amp;nbsp;lineBreakMode&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple Developer Documentation &amp;gt;&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/nsparagraphstyle/3667463-linebreakstrategy&quot;&gt;NSParagraphStyle&amp;nbsp;-&amp;nbsp;lineBreakStrategy&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple Developer Documentation &amp;gt;&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/uilabel/1620525-linebreakmode&quot;&gt;UILabel - lineBreakMode&lt;/a&gt;&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple Developer Documentation &amp;gt;&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/uilabel/3667730-linebreakstrategy&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;UILabel - lineBreakStrategy&lt;/a&gt;&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>iOS</category>
      <category>attributedText</category>
      <category>lineBreakMode</category>
      <category>lineBreakStrategy</category>
      <category>UILabel</category>
      <category>줄바꿈</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/61</guid>
      <comments>https://applecider2020.tistory.com/61#entry61comment</comments>
      <pubDate>Tue, 20 Dec 2022 07:00:25 +0900</pubDate>
    </item>
    <item>
      <title>[줄바꿈] lineBreakMode 및 lineBreakStrategy 차이점 - 둘 다 필요할지도</title>
      <link>https://applecider2020.tistory.com/60</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;lineBreakMode, lineBreakStrategy는 둘 다 필요할까?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;결론부터 말하자면 원하는 스타일에 따라&amp;nbsp;&lt;b&gt;둘 다 필요할 수도 있다.&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;UILabel의 텍스트가 길어질 때 &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;한글 단어 기준으로 줄바꿈하기 위해 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;lineBreakStrategy = .hangulWordPriority&lt;/span&gt;를 사용한 적이 있었는데,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이때 lineBreakStrategy가 lineBreakMode를 대체 가능한 거라고 착각했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;둘의 차이점을 알아보자.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1048&quot; data-origin-height=&quot;192&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbOqMb/btrTSLIfZv4/vDVQtKj3QXKZ9Sq8MxzDf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbOqMb/btrTSLIfZv4/vDVQtKj3QXKZ9Sq8MxzDf0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbOqMb/btrTSLIfZv4/vDVQtKj3QXKZ9Sq8MxzDf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbOqMb%2FbtrTSLIfZv4%2FvDVQtKj3QXKZ9Sq8MxzDf0%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;700&quot; height=&quot;128&quot; data-origin-width=&quot;1048&quot; data-origin-height=&quot;192&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;결론&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1884&quot; data-origin-height=&quot;1496&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zOUlp/btrTWNekGLZ/OTzbT9ZoG0MH6jghAhYl21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zOUlp/btrTWNekGLZ/OTzbT9ZoG0MH6jghAhYl21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zOUlp/btrTWNekGLZ/OTzbT9ZoG0MH6jghAhYl21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzOUlp%2FbtrTWNekGLZ%2FOTzbT9ZoG0MH6jghAhYl21%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;800&quot; height=&quot;635&quot; data-origin-width=&quot;1884&quot; data-origin-height=&quot;1496&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;876&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qniwp/btrTQhBVHdN/pkxkQRN9Q0iS7O1kgeWSsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qniwp/btrTQhBVHdN/pkxkQRN9Q0iS7O1kgeWSsK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qniwp/btrTQhBVHdN/pkxkQRN9Q0iS7O1kgeWSsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fqniwp%2FbtrTQhBVHdN%2FpkxkQRN9Q0iS7O1kgeWSsK%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;350&quot; height=&quot;307&quot; data-origin-width=&quot;876&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;/figure&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;lineBreakMode, lineBreakStrategy 둘 다 필요하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;714&quot; data-origin-height=&quot;252&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNJOj2/btrTUEI3Kkq/kLWSKAxKGcz5AOZ8Kmrnrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNJOj2/btrTUEI3Kkq/kLWSKAxKGcz5AOZ8Kmrnrk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNJOj2/btrTUEI3Kkq/kLWSKAxKGcz5AOZ8Kmrnrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNJOj2%2FbtrTUEI3Kkq%2FkLWSKAxKGcz5AOZ8Kmrnrk%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;350&quot; height=&quot;124&quot; data-origin-width=&quot;714&quot; data-origin-height=&quot;252&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. lineBreakMode&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;공식문서를 보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/nsparagraphstyle/1529937-linebreakmode&quot;&gt;NSParagraphStyle&lt;span&gt;&amp;nbsp;&lt;/span&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;lineBreakMode&lt;/a&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1496&quot; data-origin-height=&quot;210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bf8bVB/btrTRG1PGSz/K4Gr2s3wxJVZLNBebixbO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bf8bVB/btrTRG1PGSz/K4Gr2s3wxJVZLNBebixbO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bf8bVB/btrTRG1PGSz/K4Gr2s3wxJVZLNBebixbO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbf8bVB%2FbtrTRG1PGSz%2FK4Gr2s3wxJVZLNBebixbO0%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;800&quot; height=&quot;112&quot; data-origin-width=&quot;1496&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;linebreakMode는&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;텍스트가 컨테이너보다 넘칠 때, 텍스트를 배치하는 방법을 조절한다.&lt;br /&gt;&lt;/b&gt;예를 들면 텍스트 끝부분을 ...으로 자르거나, 텍스트를 자르는 방법이 있다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;lineBreakStrategy와 다른데,&lt;br /&gt;&lt;/span&gt;lineBreakStrategy는&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;줄바꿈을 문단 중 어디에 배치할지를 조절한다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;직접 Label에 &lt;a href=&quot;https://developer.apple.com/documentation/uikit/nslinebreakmode&quot;&gt;Enum&amp;nbsp;-&amp;nbsp;NSLineBreakMode&lt;/a&gt;&amp;nbsp;를 적용해보면 쉽게 이해할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1358&quot; data-origin-height=&quot;764&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxfIcD/btrTQQDXtWp/S5UFRv2LB6kMvD21iX8mHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxfIcD/btrTQQDXtWp/S5UFRv2LB6kMvD21iX8mHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxfIcD/btrTQQDXtWp/S5UFRv2LB6kMvD21iX8mHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcxfIcD%2FbtrTQQDXtWp%2FS5UFRv2LB6kMvD21iX8mHK%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;800&quot; height=&quot;450&quot; data-origin-width=&quot;1358&quot; data-origin-height=&quot;764&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;예제 코드를 작성해봤다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;모두 numberOfLine = 3으로 설정했다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1682&quot; data-origin-height=&quot;908&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgmU9P/btrTTpd377f/RDUEkzSs7iAsKRnZGlK5Tk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgmU9P/btrTTpd377f/RDUEkzSs7iAsKRnZGlK5Tk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgmU9P/btrTTpd377f/RDUEkzSs7iAsKRnZGlK5Tk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgmU9P%2FbtrTTpd377f%2FRDUEkzSs7iAsKRnZGlK5Tk%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;800&quot; height=&quot;434&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1682&quot; data-origin-height=&quot;908&quot;/&gt;&lt;/span&gt;&lt;/figure&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;span style=&quot;color: #ee2323;&quot;&gt;byWordWrapping&lt;/span&gt; :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;영단어&lt;/span&gt;&lt;/b&gt;&lt;span&gt; (Word) &lt;/span&gt;단위로 감싸서 줄바꿈을 한다. &lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;줄바꿈으로 인해 영단어가 쪼개지지 않도록 한다는 뜻&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;byCharWrapping : &lt;b&gt;문자&lt;/b&gt; (Charactor) 단위이므로 줄바꿈으로 인해 영단어가 쪼개진다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;byTruncatingTail&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: &lt;b&gt;텍스트 끝부분을 ...으로 자른다. 그리고 영단어가 쪼개지지 않는다.&lt;/b&gt;&lt;br /&gt;그래서 이커머스 앱의 &lt;b&gt;상품목록 화면&lt;/b&gt;에서 이 값을 주로 사용한다. ✨✨✨&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;엥.. 원래 한글 단어는 쪼개지는 문제가 생겼었는데 애플이 업데이트해준 건가.. 왜 멀쩡하지?&lt;/i&gt;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;byTruncatingMiddle : 컨테이너 마지막 줄에서 중간 부분을 ...으로 표시한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;byClipping : 영단어가 쪼개지지 않고, ... 없이 텍스트가 끊기는 곳에서 문자가 잘린다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;  UILabel의 default lineBreakMode값은 뭘까?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;NSLineBreakMode 문서를 뒤져봐도 안나왔는데, 알고 보니&amp;nbsp;&lt;/span&gt;&lt;span&gt;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/uilabel/1620525-linebreakmode&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;UILabel - lineBreakMode&lt;/a&gt; 문서가 따로 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1528&quot; data-origin-height=&quot;424&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cHnijX/btrTRGAHl5v/G2KMD0wk1ZMy9AQMHWIzd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cHnijX/btrTRGAHl5v/G2KMD0wk1ZMy9AQMHWIzd0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cHnijX/btrTRGAHl5v/G2KMD0wk1ZMy9AQMHWIzd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcHnijX%2FbtrTRGAHl5v%2FG2KMD0wk1ZMy9AQMHWIzd0%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;800&quot; height=&quot;222&quot; data-origin-width=&quot;1528&quot; data-origin-height=&quot;424&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;attibutedText 미사용 시 : lineBreakMode가 &lt;b&gt;text&lt;/b&gt; 프로퍼티의 문자열 전체에 직접 적용된다.&lt;/li&gt;
&lt;li&gt;attributedText 사용 시 : lineBreakMode가 &lt;b&gt;attributedText&lt;/b&gt; 프로퍼티의 문자열 전체에 직접 적용된다.&lt;/li&gt;
&lt;li&gt;일부 문자열에만 적용하려면 별도의 attributedText를 생성하고 lineBreakMode를 적용해야 한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/nsparagraphstyle&quot;&gt;NSParagraphStyle&lt;/a&gt; 프로퍼티는 단어가 아니라 문단 전체에 적용된다.&lt;br /&gt;- ParagraphStyle 설정값이 text/attributedText 설정값보다 우선시된다는 뜻인듯?&lt;br /&gt;- ParagraphStyle : attributedText에 적용되는 문단 스타일이다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;default는&lt;/span&gt; &lt;span style=&quot;background-color: #dddddd;&quot;&gt;.byTruncatingTail&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. lineBreakStrategy&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;위에서 설명했듯이 lineBreakStrategy는&amp;nbsp;&lt;b&gt;줄바꿈을 문단 중 어디에 배치할지를 조절한다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;공식문서 &lt;a href=&quot;https://developer.apple.com/documentation/uikit/nsparagraphstyle/3667463-linebreakstrategy&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot; data-inline-card=&quot;true&quot; data-card-url=&quot;https://developer.apple.com/documentation/uikit/nsparagraphstyle/1529937-linebreakmode&quot;&gt;NSParagraphStyle - lineBreakStrategy&lt;/span&gt;&lt;/a&gt;를 보자.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1496&quot; data-origin-height=&quot;298&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4T5ju/btrTWOdcoHG/q8PR2BSRippO2ndOSUlD0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4T5ju/btrTWOdcoHG/q8PR2BSRippO2ndOSUlD0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4T5ju/btrTWOdcoHG/q8PR2BSRippO2ndOSUlD0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4T5ju%2FbtrTWOdcoHG%2Fq8PR2BSRippO2ndOSUlD0k%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;800&quot; height=&quot;159&quot; data-origin-width=&quot;1496&quot; data-origin-height=&quot;298&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;lineBreakMode와의 차이점을 다시 설명한다.&lt;/li&gt;
&lt;li&gt;lineBreakMode가 multiple lines를 지원하지 않는 .byClipping인 경우, lineBreakStrategy를 무시한다.&lt;/li&gt;
&lt;li&gt;default값은 none이라고 되어있는데, &lt;a href=&quot;https://developer.apple.com/documentation/uikit/uilabel/3667730-linebreakstrategy&quot;&gt;UILabel - lineBreakStrategy&lt;/a&gt;을 보면 &lt;span style=&quot;color: #ee2323;&quot;&gt;default값은&lt;/span&gt; &lt;span style=&quot;background-color: #dddddd;&quot;&gt;.standard&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;lineBreakStrategy의 종류는 enum이 아니라 structure 및 static 프로퍼티로 구현되어있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lineBreakStrategy가 비교적 최근에 추가돼서 그런듯?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/nsparagraphstyle/linebreakstrategy&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Structure - NSParagraphStyle.LineBreakStrategy&lt;/a&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1478&quot; data-origin-height=&quot;394&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l6Ze2/btrTRdZXSC9/X8X1iOPa5ZhqDrDzW9yOY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l6Ze2/btrTRdZXSC9/X8X1iOPa5ZhqDrDzW9yOY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l6Ze2/btrTRdZXSC9/X8X1iOPa5ZhqDrDzW9yOY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl6Ze2%2FbtrTRdZXSC9%2FX8X1iOPa5ZhqDrDzW9yOY0%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;800&quot; height=&quot;213&quot; data-origin-width=&quot;1478&quot; data-origin-height=&quot;394&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1696&quot; data-origin-height=&quot;570&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nhloV/btrTSxcmH29/Ksi8affD8hKpAIcdxTcYIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nhloV/btrTSxcmH29/Ksi8affD8hKpAIcdxTcYIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nhloV/btrTSxcmH29/Ksi8affD8hKpAIcdxTcYIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnhloV%2FbtrTSxcmH29%2FKsi8affD8hKpAIcdxTcYIK%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;800&quot; height=&quot;269&quot; data-origin-width=&quot;1696&quot; data-origin-height=&quot;570&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;.hangulWordPriority&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: &lt;b&gt;줄바꿈으로 인해 한글 단어가 쪼개지지 않도록 한다.&lt;br /&gt;&lt;/b&gt;lineBreakMode의 &lt;span style=&quot;color: #333333;&quot;&gt;byTruncatingTail (default)만 쓰면, 영단어는 쪼개지지 않지만 한글 단어가 쪼개지는 문제가 있었다.&lt;br /&gt;&lt;/span&gt;그래서 애플이 친절하게 이걸 추가해줬다. 당연히 &lt;b&gt;한글을 쓰는 대부분의 상용 앱&lt;/b&gt;에서 활용한다. ✨✨✨&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;.pushOut&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 마지막 줄에 남겨진 단어가 동떨어지는 것을 방지하기 위해 개별 line을 밀어낸다.&amp;nbsp;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. lineBreakMode + lineBreakStrategy 둘 다 쓰기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;그렇다면 &lt;b&gt;lineBreakMode&lt;/b&gt; = .byWordWrapping&amp;nbsp;/&amp;nbsp;.byTruncatingTail&amp;nbsp;/&amp;nbsp;.byTruncatingMiddle에다가&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;모두 &lt;b&gt;lineBreakStrategy&lt;/b&gt; = .hangulWordPriority를 적용하면 어떻게 될까?&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;674&quot; data-origin-height=&quot;566&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceBzOT/btrTRdFL7QD/2sRe9p1o8qdy6q0E0JO7Vk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceBzOT/btrTRdFL7QD/2sRe9p1o8qdy6q0E0JO7Vk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceBzOT/btrTRdFL7QD/2sRe9p1o8qdy6q0E0JO7Vk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FceBzOT%2FbtrTRdFL7QD%2F2sRe9p1o8qdy6q0E0JO7Vk%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;380&quot; height=&quot;319&quot; data-origin-width=&quot;674&quot; data-origin-height=&quot;566&quot;/&gt;&lt;/span&gt;&lt;/figure&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;br /&gt;원하는 스타일에 따라 사용하면 될듯&lt;/li&gt;
&lt;li&gt;.byWordWrapping + .hangulWordPriority : &lt;b&gt;텍스트 끝이 ... 처리되지 않는다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;.byTruncatingTail +&amp;nbsp;.hangulWordPriority : &lt;b&gt;텍스트 끝이 ... 처리된다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;.byTruncatingMiddle +&amp;nbsp;.hangulWordPriority : 컨테이너 마지막 줄에서 텍스트 중간이 ... 처리된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제 코드는 &lt;a href=&quot;https://github.com/just1103/Samples/tree/main/Samples/Sample_LineBreak&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;GitHub repo&lt;/a&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;참고로 검색하면서 UILabel에 attributedText를 사용하는 경우에는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;label.lineBreakMode가 무시되고,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;attributedText의 paragraphStyle.lineBreakMode를 설정해야 적용된다는 걸 봤다.&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;- 검색 키워드 : label attributedText lineBreak&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://stackoverflow.com/questions/16831207/uilabel-attributedtext-with-multiple-line-break-modes&quot;&gt;UILabel attributedText with multiple line break modes&lt;/a&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #5c5c5c;&quot;&gt;  &lt;/span&gt;예제 코드 &amp;gt; &lt;a href=&quot;https://github.com/just1103/Samples/tree/main/Samples/Sample_LineBreak&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;GitHub Repo&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple Developer Documentation &amp;gt; &lt;a href=&quot;https://developer.apple.com/documentation/uikit/nsparagraphstyle/1529937-linebreakmode&quot;&gt;NSParagraphStyle&amp;nbsp;-&amp;nbsp;lineBreakMode&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple Developer Documentation &amp;gt; &lt;a href=&quot;https://developer.apple.com/documentation/uikit/nsparagraphstyle/3667463-linebreakstrategy&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot; data-inline-card=&quot;true&quot; data-card-url=&quot;https://developer.apple.com/documentation/uikit/nsparagraphstyle/1529937-linebreakmode&quot;&gt;NSParagraphStyle&amp;nbsp;-&amp;nbsp;lineBreakStrategy&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple Developer Documentation &amp;gt; &lt;a href=&quot;https://developer.apple.com/documentation/uikit/uilabel/1620525-linebreakmode&quot;&gt;UILabel - lineBreakMode&lt;/a&gt;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>iOS</category>
      <category>byTruncatingTail</category>
      <category>hangulWordPriority</category>
      <category>label</category>
      <category>lineBreak</category>
      <category>lineBreakMode</category>
      <category>lineBreakStrategy</category>
      <category>줄바꿈</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/60</guid>
      <comments>https://applecider2020.tistory.com/60#entry60comment</comments>
      <pubDate>Sun, 18 Dec 2022 07:00:32 +0900</pubDate>
    </item>
    <item>
      <title>[PTR] RefreshControl 및 ImageView에 Gif 이미지 넣기</title>
      <link>https://applecider2020.tistory.com/59</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;PTR (Pull To Refresh) 기능을 구현할 때 기본 RefreshControl을 사용할 수도 있지만 &lt;/span&gt;&lt;span&gt;이번에는 Gif 파일을 넣어서 만들어봤다.&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;Gif 이미지 처리에 대한 포스팅이 별로 없어서 기록해본다.&lt;/span&gt;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이건 Apple에서 제공하는 &lt;a href=&quot;https://developer.apple.com/documentation/uikit/uirefreshcontrol&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;default RefreshControl&lt;/a&gt;이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;858&quot; data-origin-height=&quot;363&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lDpAk/btrTgdtjZKW/BrHUllfC7HSvbkB89nL3T0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lDpAk/btrTgdtjZKW/BrHUllfC7HSvbkB89nL3T0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lDpAk/btrTgdtjZKW/BrHUllfC7HSvbkB89nL3T0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlDpAk%2FbtrTgdtjZKW%2FBrHUllfC7HSvbkB89nL3T0%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;400&quot; height=&quot;169&quot; data-origin-width=&quot;858&quot; data-origin-height=&quot;363&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Gif를 적용한 RefreshControl을 아래처럼 만들어봤다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span&gt;RefreshControl의 목적은 &quot;지금&amp;nbsp;&lt;/span&gt;Loading 중임. 잠시만 기다려&quot; 이므로 좀 더 Loading 효과스러운 Gif를 써도 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이번에는 애니메이션을 설명하려고 호머 심슨 Gif를 가져왔다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;refreshControl.gif&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;1085&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/s6e8V/btrTk2QGm9h/pr5U6rvPuQUXS0m18tBng0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/s6e8V/btrTk2QGm9h/pr5U6rvPuQUXS0m18tBng0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/s6e8V/btrTk2QGm9h/pr5U6rvPuQUXS0m18tBng0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/s6e8V/btrTk2QGm9h/pr5U6rvPuQUXS0m18tBng0/img.gif&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;651&quot; data-filename=&quot;refreshControl.gif&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;1085&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3단계만 있으면 된다. 매우 간단하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. Gif를 넣어서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;ImageView&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;초기화&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. RefreshControl에다가 Gif 올리기&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3. CollectionView에다가 RefreshControl 넣기&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000; font-size: 1.62em; letter-spacing: -1px;&quot;&gt;1. Gif를 넣어서 &lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000; font-size: 1.62em; letter-spacing: -1px;&quot;&gt;ImageView &lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000; font-size: 1.62em; letter-spacing: -1px;&quot;&gt;초기화&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;먼저 Gif 파일을 드래그해서 Bundle에 넣는다. &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1394&quot; data-origin-height=&quot;1072&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dveJyy/btrTgqzfaLr/OKDoJkaFAnInNQLUPD3bhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dveJyy/btrTgqzfaLr/OKDoJkaFAnInNQLUPD3bhK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dveJyy/btrTgqzfaLr/OKDoJkaFAnInNQLUPD3bhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdveJyy%2FbtrTgqzfaLr%2FOKDoJkaFAnInNQLUPD3bhK%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;500&quot; height=&quot;385&quot; data-origin-width=&quot;1394&quot; data-origin-height=&quot;1072&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이제 UIImageView initializer를 생성한다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Asset 이미지를 활용할 때 &lt;/span&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;UIImageView(named:)&lt;/span&gt; 형태로 초기화하니까&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이밍을 비슷하게 &lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px; background-color: #dddddd;&quot;&gt;UIImageView(gifNamed: &quot;Simpson&quot;&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;)&lt;/span&gt;로 하는 게 이쁠 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1670651698658&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;extension UIImageView {
    convenience init?(gifNamed: String, animationDuration: Double = 1.0) { 
        // ✅ gifURL = 사용할 gif 파일의 경로
        // ✅ CGImageSourceCreateWithData = gif 데이터를 통해 CGImageSource를 만들겠다
        guard let gifURL = Bundle.main.url(forResource: gifNamed, withExtension: &quot;gif&quot;), 
              let gifData = try? Data(contentsOf: gifURL),
              let source =  CGImageSourceCreateWithData(gifData as CFData, nil) else { 
            return nil
        }
        
        var images = [UIImage]()
        // Finder에서 gif 파일을 열면 여러 장의 이미지가 들어있다
        // ✅ gif 파일을 구성하는 이 이미지들의 개수 == CGImageSourceGetCount
        let imageCount = CGImageSourceGetCount(source)
        
        for index in 0..&amp;lt;imageCount {
            // ✅ for문을 통해 개별 이미지에 접근할 거다 (타입 변환 CGImage =&amp;gt; UIImage)
            if let image = CGImageSourceCreateImageAtIndex(source, index, nil) {
                images.append(UIImage(cgImage: image))
            }
        }
        
        // ✅ gif 이미지들을 담고 있는 UIImageView를 만든다
        // duration == gif 애니메이션이 지속될 시간 (1초가 적당한듯)
        let animatedImage = UIImage.animatedImage(with: images, duration: animationDuration)
        self.init(image: animatedImage)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;UIImageView extension으로 initializer를 추가한다.&lt;br /&gt;초기화 과정에서 gif 파일이 유효하지 않은 등 초기화에 실패할 수 있으므로 실패가능한 이니셜라이저로 만들었다.&lt;/li&gt;
&lt;li&gt;맨 위의 guard문부터 보자. &lt;br /&gt;사용할 gif 파일의 경로를 받아서 -&amp;gt; gif 파일을 Data 타입으로 만들고 -&amp;gt; 그걸로 &lt;span style=&quot;color: #ee2323;&quot;&gt;CGImageSource&lt;/span&gt;를 만든다.&lt;/li&gt;
&lt;li&gt;보통 Finder에서 gif 파일을 열면 애니미이션을 구성하는 여러 장의 이미지가 들어있다.&lt;br /&gt;그 이미지들이 CGImageSource라고 보면 된다.&lt;/li&gt;
&lt;li&gt;CGImageSource의 개수 (imageCount)만큼 for문을 돌린다.&lt;br /&gt;그래서 개별 이미지에 접근해서 CGImage를 우리가 사용할 UIImage로 바꾸고, images 배열에 넣어준다.&lt;/li&gt;
&lt;li&gt;그래서 만든 images 배열을 &lt;span style=&quot;color: #ee2323;&quot;&gt;UIImage의 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;animatedImage&lt;/span&gt;&lt;/span&gt;에 넣어서 애니메이션으로 만들고,&lt;br /&gt;그걸 &lt;span style=&quot;color: #ee2323;&quot;&gt;UIImageView의 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;image&lt;/span&gt; &lt;span style=&quot;color: #333333;&quot;&gt;프로퍼티&lt;/span&gt;&lt;/span&gt;에 넣어주면 된다.&lt;br /&gt;네이밍만 보면 image 프로퍼티에 1개 이미지만 넣어야할 것 같은데 아니었음  &lt;/li&gt;
&lt;li&gt;animatedImage를 만들 때 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;duration&lt;/span&gt;을 지정해주는데, &quot;애니메이션 1 cycle이 지속될 시간&quot;이라고 보면 된다.&lt;br /&gt;즉, 첫번째 이미지부터 마지막 이미지까지를 몇 초 이내에 보여줄지이다. (해보니까 완전히 딱 떨어지지는 않음)&lt;br /&gt;RefreshControl에 넣을 거면 1초가 적당하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. RefreshControl에다가 Gif 올리기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;위에서 만든 ImageView를 RefreshControl에다가 올려주자.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1670652836482&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;final class SimpleRefreshControl: UIRefreshControl { // UIRefreshControl 상속
    override init() {
        super.init()
        
        configureLayout()
    }
    
    required init?(coder: NSCoder) {
        fatalError(&quot;init(coder:) has not been implemented&quot;)
    }
    
    func configureLayout() {
        // ✅ 위에서 만든 UIImageView init 활용
        // bundle에 넣은 gif 파일이름 입력 (&quot;simpson&quot;)
        guard let loadingImageView = UIImageView(gifNamed: &quot;simpson&quot;) else { return }
        
        // ✅ RefreshControl의 subview로 추가
        addSubview(loadingImageView) 
        loadingImageView.snp.makeConstraints { make in
            make.center.equalToSuperview()
            make.width.equalTo(63.0)
            make.height.equalTo(45.0)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Custom RefreshControl 클래스를 만든다.&lt;/li&gt;
&lt;li&gt;위에서 만든 UIImageView initializer를 활용한다. &lt;br /&gt;gif 파일이름 &quot;simpson&quot;을 넣어서 loadingImageView를&amp;nbsp;초기화했다.&lt;/li&gt;
&lt;li&gt;그다음 RefreshControl 위에 addSubview를 하고, constraints를 설정한다.&lt;br /&gt;imageView 크기가 너무 크지 않으면 center 정렬로 맞추면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3. CollectionView에다가 RefreshControl 넣기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ScrollView 또는 CollectionView에 이미&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;refreshControl&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;프로퍼티가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 refreshControl을 만들어서 거기에 넣어주면 끝!&lt;span&gt;&amp;nbsp;&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;ActivityControl과 다르게 startAnimating 같은 걸 호출할 필요도 없다.&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;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;refreshControl.endRefreshing()&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;만 호출하면 끝!&lt;/p&gt;
&lt;pre id=&quot;code_1670653305263&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;final class MainViewController: UIViewController {
    // ✅ 아까 만든 custom refreshControl
    private lazy var refreshControl: SimpleRefreshControl = {
        let refreshControl = SimpleRefreshControl()
        refreshControl.addTarget(self, action: #selector(pulledToRefresh), for: .valueChanged) 
        return refreshControl
    }()
    
    private lazy var mainCollectionView: UICollectionView = {
        let flowLayout = createListFlowLayout()
        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
        collectionView.refreshControl = refreshControl // ✅ 참 쉽죠?
        collectionView.dataSource = self
        collectionView.register(MainCell.self, forCellWithReuseIdentifier: &quot;MainCell&quot;)
        return collectionView
    }()
    
    // ✅ pull 했을 때 할 일
    @objc private func pulledToRefresh() {
        print(&quot;!!!Refresh!!!&quot;)
        
//        mainViewModel.reset() // ViewModel 통해 API 재요청 (코드 생략)
        didReceiveResponse()    // API 호출 완료됨 (가정)
    }
    
    func didReceiveResponse() {
        // ViewModel 통해 API response 확인한 시점에 숨기기 (코드 생략)
        // ✅ API 요청 후 3초 뒤 response가 도착함 (가정)
        DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) { [weak self] in 
            // 애니메이션 1cycle을 1초로 설정했으므로 약 3cycle이 실행됨
            self?.refreshControl.endRefreshing() 
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RefreshControl을 CollectionView에 넣고, PTR에 할 일을 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;pulledToRefresh()&lt;/span&gt;에 구현하면 된다.&lt;/li&gt;
&lt;li&gt;보통 PTR할 때 ViewModel을 통해 API를 재요청하고,&lt;br /&gt;API Response가 도착하면 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;refreshControl.endRefreshing&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;()&lt;/span&gt;을 호출해서 숨겨준다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;didReceiveResponse 메서드를 보자.&lt;br /&gt;예제에서 API를 구현하지 않았기 때문에 DispatchQueue의 asyncAfter 메서드를 임시로 호출했다.&lt;br /&gt;deadline 매개변수로 &lt;span style=&quot;color: #333333; background-color: #dddddd;&quot;&gt;.now() + 3.0&lt;/span&gt;을 넣어줬는데&lt;br /&gt;UIImageView에서 animationDuration을 1초 (애니메이션 1cycle = 1초)로 설정했으므로&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;refreshControl이 1번 나타날 때 애니메이션은 약 3cycle이 실행된다.&lt;/span&gt; (해보니까 실제로는 3.5 cycle 정도 실행됨)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt; 전체 코드는 &lt;a href=&quot;https://github.com/just1103/Samples/tree/main/Samples/Sample_GIF&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;GitHub Repo&lt;/a&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 style=&quot;color: #9d9d9d;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;만약 imageURL로 Gif 띄우기 등을 하고 싶다면&lt;/span&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&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;a href=&quot;https://github.com/kaishin/Gifu&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;kaishin/Gifu 라이브러리&lt;/a&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 style=&quot;color: #9d9d9d;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;구글링 키워드 : load&amp;nbsp;gif&amp;nbsp;image&amp;nbsp;on&amp;nbsp;refreshControl&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/4386675/add-animated-gif-image-in-iphone-uiimageview&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Stack overflow - Add animated Gif image in Iphone UIImageView&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/27919620/how-to-load-gif-image-in-swift&quot;&gt;Stack overflow - How to load GIF image in Swift?&lt;/a&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&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;span&gt; &lt;/span&gt;예제 코드 &amp;gt; &lt;a href=&quot;https://github.com/just1103/Samples/tree/main/Samples/Sample_GIF&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;GitHub Repo&lt;/a&gt;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Apple Developer Documentation &amp;gt; &lt;a href=&quot;https://developer.apple.com/documentation/uikit/uirefreshcontrol&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;UIRefreshControl&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://stackoverflow.com/questions/27919620/how-to-load-gif-image-in-swift&quot;&gt;Stack overflow - How to load GIF image in Swift?&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://stackoverflow.com/questions/4386675/add-animated-gif-image-in-iphone-uiimageview&quot;&gt;Stack overflow - Add animated Gif image in Iphone UIImageView&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;GitHub &amp;gt; &lt;a href=&quot;https://github.com/kaishin/Gifu&quot;&gt;kaishin/Gifu 라이브러리&lt;/a&gt;&amp;nbsp;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>iOS</category>
      <category>gif</category>
      <category>refreshControl</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/59</guid>
      <comments>https://applecider2020.tistory.com/59#entry59comment</comments>
      <pubDate>Sat, 10 Dec 2022 15:51:07 +0900</pubDate>
    </item>
    <item>
      <title>[Redirect] 설정 앱을 띄워서 특정 앱의 설정 변경하기 (간단)</title>
      <link>https://applecider2020.tistory.com/57</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;대부분 앱의 설정 화면에서, 또는 특정 기능을 사용하기 직전에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 Push Notification, 광고 추적 허용 등의 설정값을 변경할 수 있도록 &lt;b&gt;기본 설정 앱 (Settings 앱)&lt;/b&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;구글링으로 간단히 찾을 수 있었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LWZBP/btrQr1VIfk3/fIKkQ7f7Hs06kK44MsZm6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LWZBP/btrQr1VIfk3/fIKkQ7f7Hs06kK44MsZm6k/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;2532&quot; data-filename=&quot;IMG_3773.PNG&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LWZBP/btrQr1VIfk3/fIKkQ7f7Hs06kK44MsZm6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLWZBP%2FbtrQr1VIfk3%2FfIKkQ7f7Hs06kK44MsZm6k%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;1170&quot; height=&quot;2532&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AUJtk/btrQqK8pfrM/gpSmt0LKfZJrDlQ3d3qEsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AUJtk/btrQqK8pfrM/gpSmt0LKfZJrDlQ3d3qEsk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;2532&quot; data-filename=&quot;IMG_3771.PNG&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AUJtk/btrQqK8pfrM/gpSmt0LKfZJrDlQ3d3qEsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAUJtk%2FbtrQqK8pfrM%2FgpSmt0LKfZJrDlQ3d3qEsk%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;1170&quot; height=&quot;2532&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buOFHD/btrQuNPQoNy/fhdKBKMvKs8SXKxoYpZNm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buOFHD/btrQuNPQoNy/fhdKBKMvKs8SXKxoYpZNm0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;2532&quot; data-filename=&quot;IMG_3772.PNG&quot; data-widthpercent=&quot;33.34&quot; style=&quot;width: 32.5581%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buOFHD/btrQuNPQoNy/fhdKBKMvKs8SXKxoYpZNm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbuOFHD%2FbtrQuNPQoNy%2FfhdKBKMvKs8SXKxoYpZNm0%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;1170&quot; height=&quot;2532&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;설정 앱에서 특정 앱 확인하기&lt;/figcaption&gt;
&lt;/figure&gt;
&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;검색 키워드 : ios&amp;nbsp;how&amp;nbsp;to&amp;nbsp;show&amp;nbsp;settings&amp;nbsp;app&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Stack overflow &amp;gt; &lt;a href=&quot;https://stackoverflow.com/questions/5655674/opening-the-settings-app-from-another-app&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Opening the Settings app from another app&lt;/a&gt;에 나온 대로 해보니 잘 실행된다.&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1667671985102&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    private func showAppSettings() {
        guard let settingsURL = URL(string: UIApplication.openSettingsURLString) else { return }
        UIApplication.shared.open(settingsURL)
    }&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;보통 redirect할 때 UIApplication&lt;span&gt;.&lt;/span&gt;&lt;span&gt;shared&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;canOpenURL&lt;/span&gt;&lt;span&gt;() 메서드로 확인하는 절차를 거치지만,&lt;br /&gt;설정 앱 (Settings 앱)은 아이폰 기본 앱이므로 생략했다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;코드 중 settingsURL을 생성하는 부분에서 사용된 타입 프로퍼티 &lt;/span&gt;&lt;span data-v-97436168=&quot;&quot; data-v-91bcffaa=&quot;&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/uiapplication/1623042-opensettingsurlstring&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;openSettingsURLString&lt;/a&gt;를 살펴보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-v-97436168=&quot;&quot; data-v-91bcffaa=&quot;&quot;&gt;원하던 내용이 모두 설명되어있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-v-97436168=&quot;&quot; data-v-91bcffaa=&quot;&quot;&gt;만약 해당 앱이 출시 이전이라서 AppStore를 통해 설치한 게 아니라면, 설정 앱으로 이동하는 것까지만 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;805&quot; data-origin-height=&quot;503&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d9z756/btrQtScQiwI/kRh2KKCLrasTryevBjKMWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d9z756/btrQtScQiwI/kRh2KKCLrasTryevBjKMWk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d9z756/btrQtScQiwI/kRh2KKCLrasTryevBjKMWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd9z756%2FbtrQtScQiwI%2FkRh2KKCLrasTryevBjKMWk%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;805&quot; height=&quot;503&quot; data-origin-width=&quot;805&quot; data-origin-height=&quot;503&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;참고 - Stack overflow 내용 중 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;prefs&lt;/span&gt;를 설정해서 이동하는 방법도 있는데,&lt;br /&gt;이 방법을 사용하면 Apple이 앱을 reject 시킨다고 한다.&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Stack overflow &amp;gt; &lt;a href=&quot;https://stackoverflow.com/questions/5655674/opening-the-settings-app-from-another-app&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Opening the Settings app from another app&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Apple Developer Documentation &amp;gt; &lt;a href=&quot;https://developer.apple.com/documentation/uikit/uiapplication/1623042-opensettingsurlstring&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;openSettingsURLString&lt;/a&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>iOS</category>
      <category>deeplink</category>
      <category>Settings</category>
      <category>앱설정</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/57</guid>
      <comments>https://applecider2020.tistory.com/57#entry57comment</comments>
      <pubDate>Mon, 14 Nov 2022 07:00:08 +0900</pubDate>
    </item>
    <item>
      <title>[Redirect] 업데이트 버튼 탭하면 사용자에게 AppStore 앱 띄우기 (간단)</title>
      <link>https://applecider2020.tistory.com/56</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;대부분 앱의 설정 화면에서 &lt;b&gt;현재 설치된 버전 정보&lt;/b&gt;를 알 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그리고 해당 앱의 최신버전이 나왔다면 업데이트 버튼을 탭했을 때,&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&amp;nbsp;사용자에게 &lt;b&gt;AppStore 화면을 띄워준다.&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;덕분에 사용자가 AppStore에서 해당 앱을 직접 검색할 필요가 없다.&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&lt;span&gt;구글링을 통해 간단히 방법을 찾을 수 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bifPCw/btrQqj4benM/KuoullEjTC6g1T39X94FfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bifPCw/btrQqj4benM/KuoullEjTC6g1T39X94FfK/img.png&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;2532&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bifPCw/btrQqj4benM/KuoullEjTC6g1T39X94FfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbifPCw%2FbtrQqj4benM%2FKuoullEjTC6g1T39X94FfK%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;1170&quot; height=&quot;2532&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lgRyF/btrQqLM4KLx/rXQgWPFkkZ2heoStliLnT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lgRyF/btrQqLM4KLx/rXQgWPFkkZ2heoStliLnT1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;2532&quot; data-filename=&quot;IMG_3770.PNG&quot; data-widthpercent=&quot;50&quot; style=&quot;width: 49.4186%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lgRyF/btrQqLM4KLx/rXQgWPFkkZ2heoStliLnT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlgRyF%2FbtrQqLM4KLx%2FrXQgWPFkkZ2heoStliLnT1%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;1170&quot; height=&quot;2532&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;예시 - 당근마켓 설정화면&lt;/figcaption&gt;
&lt;/figure&gt;
&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;&lt;span&gt;검색 키워드 - ios&amp;nbsp;how&amp;nbsp;to&amp;nbsp;link&amp;nbsp;appstore&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Stack overflow &amp;gt; &lt;/span&gt;&lt;a href=&quot;https://stackoverflow.com/questions/433907/how-to-link-to-apps-on-the-app-store&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;How to link to apps on the app store&lt;/a&gt;에서 시킨대로 했더니 잘 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1667671037116&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    @objc private func updateButtonTapped() {
        let appID = &quot;1234&quot;
        let appStoreURL = &quot;itms-apps://itunes.apple.com/app/id\(appID)&quot;
        guard let appStoreURL = URL(string: appStoreURL),
              UIApplication.shared.canOpenURL(appStoreURL) else { return }
        UIApplication.shared.open(appStoreURL)
    }&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&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;span&gt;Stack overflow &amp;gt;&lt;/span&gt;&amp;nbsp;&lt;a href=&quot;https://stackoverflow.com/questions/433907/how-to-link-to-apps-on-the-app-store&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;How to link to apps on the app store&lt;/a&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>iOS</category>
      <category>appstore</category>
      <category>redirect</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/56</guid>
      <comments>https://applecider2020.tistory.com/56#entry56comment</comments>
      <pubDate>Fri, 11 Nov 2022 07:00:51 +0900</pubDate>
    </item>
    <item>
      <title>책 &amp;lt;함께 자라기&amp;gt; 리뷰 - 팀과 함께 학습하는 방법</title>
      <link>https://applecider2020.tistory.com/58</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;김창준 저 &amp;lt;함께 자라기&amp;gt;를 읽었다. &lt;a href=&quot;http://www.yes24.com/Product/Goods/67350256&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;(&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;도서 링크)&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서 자기 계발에 대해 얘기하다가 팀 리더의 추천으로 책을 읽게 되었다.&lt;span&gt;&amp;nbsp;&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;lsquo;학습&amp;rsquo;과 &amp;lsquo;협동&amp;rsquo;이라고 말한다. 팀원들과 신뢰를 쌓고, 팀이 함께 학습하고 현업에 성공적으로 적용하기 위해 필요한 것들을 설명하며, 다양한 성공/실패 예시를 담고 있다.&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;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;ldquo;&amp;rdquo;로 표시했다.)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1부. 자라기&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;야생 학습&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lsquo;학교 학습&amp;rsquo;과 상반된 개념으로 &lt;b&gt;&amp;lsquo;야생 학습&amp;rsquo;&lt;/b&gt;이 있다. 저자가 이 책에서 설명하는 학습은 &amp;lsquo;야생 학습&amp;rsquo;이다. 여러 명의 멘토가 상반된 조언을 한다면, 현실과 비슷한 문제 상황을 맞닥뜨렸다고 생각해야 한다. 오히려 상반된 의견과 정보 속에서 스스로 생각하는 훈련을 할 기회로 삼아야 한다.&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;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;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;불확실성이 높은 환경일수록 야생 학습이 중요하다.&quot;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;경력과 성과의 상관관계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어 엔지니어의 실력은 겉으로는 기술자격, 학력 등이 결정하는 것 같아 보이지만 사실상 &amp;lsquo;경험&amp;rsquo;이라는 요소가 가장 결정적인 역할을 한다. 이때 &amp;lsquo;경험&amp;rsquo;이란 질적으로 얼마나 폭넓고 다양했는지를 의미한다.&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;ldquo;&lt;b&gt;경력이 10년인 개발자가 2년인 개발자보다 더 우수하지 않았다.&lt;/b&gt; 경력과 생산성은 아무 상관관계가 없었다. 단, 언어를 접한 경험이 6개월 미만인 개발자들은 전반적으로 나머지 개발자들보다 성적이 저조했다. (...) 즉, 최소한도의 경험치만 넘어가면 경력 연수와 실제 직무 성과의 상관성이 생각보다 낮다.&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;잘 뽑는 것 이상으로 뽑은 사람을 성장시키는 것이 중요하다. &amp;ldquo;최근 일주일 동안 업무 능력 향상을 위해 얼마나 시간을 쓰는지 물었는데, 이 시간의 양과 직무 성과 간에 통계적으로 유의미한 양의 상관성이 있었다. 그들이 자주 하는 수련으로는&lt;span style=&quot;color: #ee2323;&quot;&gt; &amp;lsquo;머릿속에서 시뮬레이션하기 (클라이언트와 어려운 대화 상황을 머릿속에 그리고, 가능한 시나리오를 탐색해 봄)&amp;rsquo;, &amp;lsquo;피드백 요청하기&amp;rsquo;&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;lsquo;1만 시간 법칙&amp;rsquo;이 유행했었다. 여기서 말하는 1만 시간은 종종 경력/근무시간으로 오인되기도 하는데, &amp;rsquo;자신의 기량을 향상시킬 목적으로 하는 &lt;span style=&quot;color: #ee2323;&quot;&gt;의도적인 수련&lt;/span&gt;&amp;rsquo;을 한 시간을 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 업무를 하면서도 의도적 수련을 할 수 있는 방법이 있다. &lt;span style=&quot;color: #ee2323;&quot;&gt;&amp;ldquo;피드백을 짧은 주기로 얻는 것&amp;rdquo;, 그리고 &amp;ldquo;실수를 교정할 기회가 있는 것&amp;rdquo;&lt;/span&gt;이다. 이 환경에서 학습 효과가 훨씬 좋다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;자기계발은 복리로 돌아온다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주기적인 회고가 중요한데, 이때 자기계발을 얼마나 했는지 되짚어봐야 한다. &amp;ldquo;현재 나에게 무엇을 투자했느냐가 1년, 혹은 2년 후의 나를 결정한다고 느끼기 때문이다.&amp;rdquo;&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;ldquo;잡코리아의 2012년 조사에 따르면 직장인의 하루 평균 자기계발 시간은 1~2시간이 54%, 1시간 미만 34%, 2~3시간 9%, 3시간 이상 3% 수준이었다. (&amp;hellip;) 하루 평균 1시간도 투자하지 않는 사람은 자기계발이란 면에서 직장인의 하위 1/3에 속하는 셈이다. 무서운 사실은 이게 축적이 되면 엄청난 차이를 만들 거라는 점이다. &lt;b&gt;자기가 습득한 지식이나 능력은 복리로 이자가 붙기 때문이다.&lt;/b&gt;&amp;rdquo;&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;b&gt;&amp;ldquo;내가 만든 결과물을 나의 일부로 만들어서 다음 단계에 보탬이 되도록 이용해먹는 것이다. 결과물이 다음 단계의 도구가 된다.&amp;rdquo;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOHlQP/btrQAwfRueJ/FXhE30mQGqwIytNsuxpF20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOHlQP/btrQAwfRueJ/FXhE30mQGqwIytNsuxpF20/img.png&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;579&quot; data-is-animation=&quot;false&quot; style=&quot;width: 54.3028%; margin-right: 10px;&quot; data-widthpercent=&quot;54.94&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOHlQP/btrQAwfRueJ/FXhE30mQGqwIytNsuxpF20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOHlQP%2FbtrQAwfRueJ%2FFXhE30mQGqwIytNsuxpF20%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;1170&quot; height=&quot;579&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9F5xj/btrQrFeOnWn/zEWaYkUPjsKmUwAL4rVJV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9F5xj/btrQrFeOnWn/zEWaYkUPjsKmUwAL4rVJV0/img.png&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;706&quot; data-is-animation=&quot;false&quot; style=&quot;width: 44.5344%;&quot; data-widthpercent=&quot;45.06&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9F5xj/btrQrFeOnWn/zEWaYkUPjsKmUwAL4rVJV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9F5xj%2FbtrQrFeOnWn%2FzEWaYkUPjsKmUwAL4rVJV0%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;1170&quot; height=&quot;706&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;왼쪽 - 일반적인 조직 / 오른쪽 - 복리 조직&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;학습 프레임과 실행 프레임 ✨&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무작위로 2개 그룹을 나누고, 그림 그리는 활동에 대해 다르게 안내하는 실험을 진행했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실행 프레임:&lt;/b&gt; 당신이 얼마나 그림을 잘 그리는지, 창의성을 평가하여 점수를 매길 것이라는 안내&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;학습 프레임:&lt;/b&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 style=&quot;color: #ee2323;&quot;&gt;즉, 실행 프레임은 &amp;lsquo;잘하기&amp;rsquo;에 초점을 맞추고, 학습 프레임은 &amp;lsquo;자라기&amp;rsquo;에 초점을 맞춘다.&lt;/span&gt; (&amp;hellip;) 결과적으로 학습 정도를 비교하면 학습 프레임의 실험자들이 훨씬 학습 효과가 좋다. 이 결과는 사회학, 심리학, 교육학 등 여러 분야에서 여러 연구를 통해 거듭 발견된 현상이다.&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;ldquo;여기서 말하는 실행 프레임은 사람들이 현재 주어진 과업이 &lt;b&gt;뭔가 좋은 성과를 내는 걸로 생각하는 틀&lt;/b&gt;을 말한다. 학습 프레임은 현재 주어진 과업이 &lt;b&gt;내가 얼마나 배우느냐로 여기게 되는 틀&lt;/b&gt;을 말한다. 업무 중에 실행 프레임으로만 세상을 보는 사람들은 아마 내가 인정받아 다음 단계로 올라가냐 아니냐에 관심이 많을 것이다. 만약 다음 단계로 가지 못하고 떨어지면 자신이 속한 곳에서의 학습 기회를 보기보다 다른 경쟁체제와 다른 타이틀, 다른 자리에 관심을 둘 것이다. &lt;span style=&quot;color: #ee2323;&quot;&gt;실행 프레임은 당신의 목표가 학습을 통한 성장이라면 불리한 선택이다.&lt;/span&gt;&amp;rdquo;&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;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;생각해보면 절대 실수가 발생해서는 안 되는 현장 (건설 현장, 병원, 군대 등)에서는 실수 예방 문화가 형성될 수밖에 없다. 실수가 곧 인명 사고로 이어질 수 있기 때문이다. 그래서 위계적인 문화, 군대 문화, 수직적인 문화, 태움 문화 등의 수식이 따라붙는다. &lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;그래서 인명 피해로 이어질 위험이 낮은 IT 기업의 개발 조직은 상대적으로 실수에 대해 관대할 수 있다.&amp;nbsp;&lt;/i&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;가장 학습하기 힘든 직업이 살아남는다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;우리의 일자리가 인공지능에 대체되지 않으려면, 아래처럼&lt;b&gt; 학습하기 힘든 환경&lt;/b&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;lsquo;암묵지&amp;rsquo;, &amp;lsquo;직관&amp;rsquo; 같은 것들이 작동하는 회색 영역이다. 자신이 왜 이런 선택을 했는지 쉽게 설명할 수 없는 것들이다.&amp;ldquo;&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;b&gt;내가 실제로 매일 하는 일이 어떤 성격인지&lt;/b&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;ldquo;현재 자신의 업무 상황 속에서 창의적으로, 사회적으로 (다른 사람의 생각과 마음에 관심을 갖고, 그들을 설득하고 협상하는 것) 일하지 않는 기간이 계속된다면 결국 자신의 커리어에 막대한 손해가 될 수 있다는 점이다. 혼자서 딱 정해진 일만 할 수 있는 환경이 축복이 아니라 저주가 될 수 있다.&amp;rdquo; 결론적으로 미래에는 암묵지와 직관을 잘 학습하는 사람이 높은 경쟁력을 가질 것이다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;당신이 제자리걸음인 이유&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;의도적 수련이 가능하게 하려면 나의 실력과 작업의 난이도가 비슷해야 한다.&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;&quot;아래 그림의 &lt;b&gt;C부분에서 사람은 몰입을 경험하고, 학습 능력이 최대치가 된다.&lt;/b&gt; 교육학의 인지부하 이론도 이와 유사한데, 핵심은 적절한 난이도를 설정하는 것이다.&amp;rdquo;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;현재 A&lt;/b&gt; (실력은 높은데 작업 난이도는 낮아서 지루함)라면 2개 방법이 있다. &lt;b&gt;실력을 낮추는 것&lt;/b&gt; (평소 일하며 사용하던 보조 툴을 의도적으로 사용하지 않기, 컴파일 주기를 30초에서 5분으로 늘리기), &lt;b&gt;일의 난이도를 높이는 것&lt;/b&gt; (하루 종일 걸리는 일을 1시간 내에 하기, 익숙한 작업을 새로운 언어로 진행해보기, 리팩터링하기, 자동화 테스트 달기, &lt;span style=&quot;color: #ee2323;&quot;&gt;직접 나만의 도구를 개발하기&lt;/span&gt; ex. C파일에 로그를 삽입해서 스레드/프로세스의 동작을 쉽게 분석해주는 도구) 이렇게 안 해도 되는 업무를 자발적으로 했을 때 업무 성과와 직무 만족도가 높고, 번아웃을 적게 경험한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;602&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhFHRo/btrQtUhZylU/Elutit673ycSkigm7JzrzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhFHRo/btrQtUhZylU/Elutit673ycSkigm7JzrzK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhFHRo/btrQtUhZylU/Elutit673ycSkigm7JzrzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhFHRo%2FbtrQtUhZylU%2FElutit673ycSkigm7JzrzK%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;500&quot; height=&quot;257&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;602&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;현재 C&lt;/b&gt; (실력은 낮은데 작업 난이도가 높아서 불안함)라면 마찬가지로 2개 방법을 쓰면 된다.&lt;b&gt; 실력을 높이는 것&lt;/b&gt; (스터디 참가, 교육 수강, 다른 전문가에게 도움 요청, 라이브러리 등 툴을 활용), &lt;b&gt;일의 난이도를 낮추는 것&lt;/b&gt; (&lt;span style=&quot;color: #ee2323;&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;한 연구에서 피실험자를 A/B 그룹으로 나누어 어려운 문제, 쉬운 문제 순을 다르게 설정하여 코딩 테스트를 진행했다. 결과적으로&lt;b&gt; 쉬운 문제를 먼저 푼 그룹의 결함률이 50% 이상 낮았다.&lt;/b&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;위 전략을 취하려면 메타인지가 필요하다. 즉, 스스로 자신의 상태를 알아차려야 한다. (mindfulness)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;프로그래밍 언어 배우기의 달인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저자는 &amp;ldquo;적극적 읽기&amp;rdquo;를 권한다. 무언가를 읽을 때 구체적인 질문이나 목적을 가지고 읽는 방법이다. 예를 들어 튜토리얼을 읽다가 이 정도면 프로그램을 작성할 수 있겠다는 생각이 들면 읽기를 멈추고, 코딩을 시작하는 방식이다.&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;b&gt;표준 라이브러리 소스코드&lt;/b&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;b&gt;구체적인 사건에 대해 말하도록 유도&lt;/b&gt;해야 한다. 예를 들어 특정 언어를 익힌 과정을 시간대별로 짚어가며 어떤 행동을 했는지, 암묵적인 의사결정과 상황판단이 무엇이었는지 추출해야 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실수는 예방하는 것이 아니라 관리하는 것이다 ✨&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;이 챕터도 주니어 개발자로서 많은 위로를 받았다.&lt;/i&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;ldquo;실수 예방&amp;rdquo; 문화와 &amp;ldquo;실수 관리&amp;rdquo; 문화로 구분했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실수 예방&lt;/b&gt;은 행동에서 실수로 가지 않도록 요구한다. 즉 실수를 저지르지 말라고 요구한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이것은 불가능에 가깝다. &lt;span style=&quot;color: #ee2323;&quot;&gt;&amp;ldquo;전문가도 1시간에 평균 3~5개의 실수를 저지른다.&amp;rdquo;&lt;/span&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;b&gt;실수 관리&lt;/b&gt;의 관점은 이렇다. &amp;ldquo;실수는 어떻게든 할 수밖에 없다. (코딩하다가 == 대신 =을 작성) 대신 그 실수가 나쁜 결과 (서버가 죽음)가 되기 전에 일찍 발견하고 빨리 고치면 된다.&amp;rdquo;&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;b&gt;실수를 한 사람을 비난하지 않고, 오히려 실수를 공개하고, 서로 실수가 빨리 회복되도록 돕고, 실수에 대해 서로 이야기하며 배우는 분위기가 형성된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;보통 회사가 실수 관리 문화에 가까울수록 회사의 수익성이 좋고, 혁신 정도가 더 높다. &lt;span style=&quot;color: #ee2323;&quot;&gt;실수가 없으면 학습하지 못하기 때문이다.&amp;rdquo;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;나 홀로 전문가에 대한 미신&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;어떤 기술적 실천법이라도 그걸 현실에서 적용하기 위해서는 사회적 자본과 기술이 필요하다. 설사 나 혼자 하는 실천법이라 해도 그렇다. 예컨대 상사가 내가 하는 일을 보고 반대한다면 그를 설득해야 하며, 하다가 모르는 것이 생기면 주변에 물어봐야 하기 때문이다.&amp;rdquo;&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;ldquo;사회적 기술에서 &amp;lsquo;음의 기술&amp;rsquo;을 가진 사람도 존재한다. 예컨대 커뮤니케이션할수록 신뢰가 깨지는 사람을 말한다.&amp;rdquo;&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;ldquo;뛰어난 소프트웨어 개발자일수록 타인과 인터렉션에 더 많은 시간을 쓰며, 초보개발자들에게 조언할 때 사회적인 측면이 포함된다. 기술적인 조언만 하는 게 아니라는 뜻이다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2부. 함께&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;신뢰를 깎는 공유인가 신뢰를 쌓는 공유인가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2명의 디자이너가 광고를 디자인할 때, 피드백을 주고받는 방법에 따라 어떤 영향을 받았는지 실험을 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업을 공유하는 방식은 3개였다. (1) 1개 디자인을 만들고 1개를 공유, (2) 3개 디자인을 만들고 그중 본인이 가장 잘한 것을 골라서 1개를 공유, (3) 여러 개의 디자인을 만들고 여러 개를 공유&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1번, 2번의 경우 작업 공유가 오히려 부정적인 결과를 낳았다.&lt;/b&gt; 공유에 대한 기대감보다 불안감을 가지게 되며, 상대방의 작업에 대해 솔직한 피드백을 내는 것을 조심스러워하고, 또 부정적인 평가를 받았을 때 보다 방어적인 태도를 갖게 됐기 때문이다. &amp;ldquo;상대가 부정적으로 들릴 만한 의견을 주면 그건 곧 나의 전문성에 대한 도전이 되는 것이다. 나의 작품이 하나밖에 없으니 &amp;lsquo;작업물 = 나&amp;rsquo;가 되는 것이다. 나름 방어를 해낸다고 해도 자기효능감이 떨어지기 쉽다.&amp;rdquo;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;하향식 접근의 함정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;인공지능 연구에선 이 세상의 문제를 두 종류로 나눈다. 잘 정의된 문제 (well-defined)와 잘 정의되지 않은 문제 (ill-defined) (&amp;hellip;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;전문가들은 잘 정의되지 않은 문제를 접하면 탑다운과 바텀업을 섞어서 쓴다.&lt;/b&gt; 비전문가는 탑다운이나 바텀업 중 한 가지에만 억지로 집착하려고 한다. (ex. 이번 문제는 복잡하니까 더 철저하게 계획하고 설계해야겠다.)&amp;rdquo;&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;ldquo;다음 그림은 엘리베이터 설계 시 전문가들의 문제해결 흐름을 보여주고 있다. 엘리베이터 설계에서 추상성이 높은 것은 알고리즘이나 기능 등이 될 테고, 추상성이 낮은 것은 모터의 가속도 제어나 회로 차원이 될 것이다. 전문가는 추상성의 정도를 오르락내리락거리고, 특히&lt;b&gt; 탑다운과 바텀업의 방향이 전환되는 시점들에서 &amp;lsquo;아하 순간&amp;rsquo;이 찾아왔다.&lt;/b&gt;&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;532&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5d0tM/btrQyVAcALc/bAk5aq28OCg6EkCkkvNkCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5d0tM/btrQyVAcALc/bAk5aq28OCg6EkCkkvNkCk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5d0tM/btrQyVAcALc/bAk5aq28OCg6EkCkkvNkCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5d0tM%2FbtrQyVAcALc%2FbAk5aq28OCg6EkCkkvNkCk%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;500&quot; height=&quot;227&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;532&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;비전문가일수록 자신이 애초에 세운 계획에 집착했다. 전문가일수록 자신의 계획을 수정한 횟수가 많았다.&amp;rdquo;&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;ldquo;한 번에 처리되는 일의 양 (batch size)을 줄여야 한다. 배치 사이즈를 줄여서 지속적 흐름을 만들고, 짧은 시간 내에 탑, 바텀을 오가게 한다.&amp;rdquo;&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;ldquo;흔히들 말하는 개발의 5단계도 비슷하다. 분석, 설계, 구현, 테스트, 전개를 1년이라는 프로젝트 기간에 얼마씩 배치할까로 고민하지 말고, 어떻게 해야 첫 달부터, 아니 첫 주부터 분석, 설계, 구현, 테스트, 전개를 모두 왔다 갔다 할 수 있을까를 고민해야 할 것이다.&amp;ldquo;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;구글이 밝힌 탁월한 팀의 비밀&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구글은 뛰어난 팀의 특징을 찾기 위해 2년간 노력했다. 2015년 구글이 공개한 연구 결과의 일부는 아래와 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;팀에 누가 있는지 (전문가, 내향/외향, 지능 등)보다 &lt;span style=&quot;color: #ee2323;&quot;&gt;팀원들이 서로 어떻게 상호작용하고 자신의 일을 어떻게 바라보는지&lt;/span&gt;가 훨씬 중요했다.&lt;/li&gt;
&lt;li&gt;5가지 성공적인 팀의 특징을 찾았는데, 그중 압도적으로 높은 예측력을 보인 변수는 팀의 &lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;심리적 안전감&lt;/span&gt;&lt;/b&gt;이었다. 심리적 안전감이란, &lt;span style=&quot;color: #ee2323;&quot;&gt;내 생각이나 의견, 질문, 걱정, 혹은 실수가 드러났을 때 처벌받거나 놀림받지 않을 거라는 믿음&lt;/span&gt;을 말한다.&lt;/li&gt;
&lt;li&gt;팀 토론 등 특별히 고안된 활동을 통해 심리적 안전감을 개선할 수 있었다. 단순히 우리팀의 현상황에 대해 열린 대화를 시작하는 것만으로 변화가 시작될 수 있다.&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3부. 애자일&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;애자일&amp;rdquo;은 불확실성이 클 때 우리가 어떻게 해야 하는지를 고민한 결과물이다. (&amp;hellip;) 좀 더 짧은 주기로 더 일찍부터 피드백을 받고, 더 다양한 사람으로부터 더 자주 그리고 더 일찍 피드백을 받는 것으로 정의할 수 있다.&amp;rdquo;&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;ldquo;학습과 협력이 애자일이 불확실성을 다루는 핵심적인 구동원리이다. (&amp;hellip;) 불확실성이 높을수록 이동하면서 계속 배워나가야 한다.&amp;rdquo;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;애자일의 씨앗&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애자일의 핵심을 한 문장으로 표현하면 &lt;b&gt;&amp;ldquo;고객에게 매일 가치를 전하라.&amp;rdquo;&lt;/b&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;li&gt;비슷한 수준의 가치를 더 값싸게 전달하는 방법은?&lt;/li&gt;
&lt;li&gt;가치를 우리가 갖고 있지 않고 고객에게 정말 전달하고 있는가? 고객이 정말 가치를 얻고 있는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;성공하는 조직들에는 항상 뛰어난 애자일 코치가 있었다.&amp;rdquo;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코치의 특징 중 &lt;b&gt;성장 사고관&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;YES24 &amp;gt; &lt;a href=&quot;http://www.yes24.com/Product/Goods/67350256&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;김창준 저 &amp;lt;함께 자라기&amp;gt;&lt;/span&gt;&lt;/a&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>프로그래밍 철학/책 리뷰</category>
      <category>김창준</category>
      <category>애자일</category>
      <category>책리뷰</category>
      <category>함께자라기</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/58</guid>
      <comments>https://applecider2020.tistory.com/58#entry58comment</comments>
      <pubDate>Mon, 7 Nov 2022 00:06:01 +0900</pubDate>
    </item>
    <item>
      <title>[UserDefaults] object(forKey:) 및 bool(forKey:), string(forKey:)의 차이점은?</title>
      <link>https://applecider2020.tistory.com/55</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;UserDefaults 관련 코드를 보다가 UserDetaults.standard.string(forKey:) 메서드를 봤는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;object(forKey:) 메서드와의 차이점이 궁금해서 검색해봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;UserDefaults, 기본적인 key-value pair와의 차이점에 대한 기본적인 설명은 생략했다.&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;UserDefaults 관련 메서드는 이렇게 다양한데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저장값을 string, bool, integer, double 등 타입별로 구분하면 번거로우니까&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 방에 퉁쳐서 저장할 수 있는 object를 쓰는 게 낫지 않을까? 하는 생각을 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;-&amp;gt; 결론적으로 아니었음&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;489&quot; data-origin-height=&quot;603&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kDb4E/btrQr7nSeAS/avJQQlcEsbfXaGgxXb4a5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kDb4E/btrQr7nSeAS/avJQQlcEsbfXaGgxXb4a5K/img.png&quot; data-alt=&quot;UserDefaults 관련 메서드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kDb4E/btrQr7nSeAS/avJQQlcEsbfXaGgxXb4a5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkDb4E%2FbtrQr7nSeAS%2FavJQQlcEsbfXaGgxXb4a5K%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;489&quot; height=&quot;603&quot; data-origin-width=&quot;489&quot; data-origin-height=&quot;603&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;UserDefaults 관련 메서드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;공식문서&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;우선 공식문서를 살펴봤다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;대표적인 메서드인 &lt;b&gt;object(forKey:) / bool(forKey:) / string(forKey:)&lt;/b&gt; 3개만 살펴보자.&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;일단 API가 추가된 시점은 모두 iOS 2.0으로 동일하다.&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;&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;object 메서드는 옵셔널 타입 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;Any?&lt;/span&gt;,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;bool 메서드는 none-옵셔널 타입 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;Bool&lt;/span&gt;,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;string 메서드는 옵셔널 타입 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;String?&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;  직관적으로 옵셔널 타입을 반환하는 object / string 메서드는 &lt;b&gt;특정 key에 저장된 값이 없으면 nil을 반환,&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;none-옵셔널 타입을 반환하는 bool 메서드는 다른 방식일 것을 예상할 수 있다.&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;저장값이 없는 경우, bool 메서드는 &lt;b&gt;false&lt;/b&gt;를 반환하고,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;integer, double 메서드는 &lt;b&gt;0&lt;/b&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;또 신기한 점이 integer/double 메서드는 저장값이 true/false이면 각각 1/0을 반환한다. (반대로 bool 메서드로는 숫자값을 반환 불가)&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;836&quot; data-origin-height=&quot;565&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eb8mfK/btrQvZ3t6xc/ndL2HO4iJk7h9J4z9QwXc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eb8mfK/btrQvZ3t6xc/ndL2HO4iJk7h9J4z9QwXc1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eb8mfK/btrQvZ3t6xc/ndL2HO4iJk7h9J4z9QwXc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Feb8mfK%2FbtrQvZ3t6xc%2FndL2HO4iJk7h9J4z9QwXc1%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;800&quot; height=&quot;541&quot; data-origin-width=&quot;836&quot; data-origin-height=&quot;565&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;790&quot; data-origin-height=&quot;458&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dU9wvm/btrQvNWjNIc/K7U0AlvkoAOvBU0gBu1Mvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dU9wvm/btrQvNWjNIc/K7U0AlvkoAOvBU0gBu1Mvk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dU9wvm/btrQvNWjNIc/K7U0AlvkoAOvBU0gBu1Mvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdU9wvm%2FbtrQvNWjNIc%2FK7U0AlvkoAOvBU0gBu1Mvk%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;800&quot; height=&quot;464&quot; data-origin-width=&quot;790&quot; data-origin-height=&quot;458&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;599&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Fk2JN/btrQq4FsRoD/QHLnSKtIEiKfMdNSKnfLNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Fk2JN/btrQq4FsRoD/QHLnSKtIEiKfMdNSKnfLNK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Fk2JN/btrQq4FsRoD/QHLnSKtIEiKfMdNSKnfLNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFk2JN%2FbtrQq4FsRoD%2FQHLnSKtIEiKfMdNSKnfLNK%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;800&quot; height=&quot;599&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;599&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;구글링 - bool 및 object 메서드의 차이점&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;공식문서를 봤으니 이제 구글링을 해보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- 검색 키워드 : when use userdefaults string(forKey:), how to store string in UserDefaults&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;CocoaCasts의 &lt;a href=&quot;https://cocoacasts.com/ud-7-how-to-check-if-a-value-exists-in-user-defaults-in-swift&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;How to Check if a Value Exists in User Defaults in Swift&lt;/a&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;bool(forKey:) 메서드는 특정 key에 저장된 값이 없거나, 저장된 값이 bool 타입이 아니면 false를 반환하므로&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;특정 key에 대한 값이 존재하는지 여부를 알고 싶을 때 적절하지 않다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(반환값으로 false를 받았을 때, 이게 내가 저장한 값인지, 해당 key가 존재하지 않는지 구분이 불가하므로)&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그래서 해결책으로 &lt;span style=&quot;color: #ee2323;&quot;&gt;bool 메서드 쓰지 말고 object 메서드를 쓰라&lt;/span&gt;고 권한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;object 메서드는 저장된 값이 없을 때 친절하게 nil을 반환해주기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예제 코드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이 아티클을 기반으로 예제 코드를 작성해봤다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1667655091877&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1️⃣ 저장
UserDefaults.standard.set(true, forKey: &quot;KeyForBool&quot;)

// 2️⃣ 저장한 값 꺼내보기
let value = UserDefaults.standard.bool(forKey: &quot;KeyForBool&quot;)
let value2 = UserDefaults.standard.object(forKey: &quot;KeyForBool&quot;)
let typeCastedValue2 = value2 as? Bool

print(value)  // true
print(value2) // optional(1) &amp;lt;- ✅ true가 아님. Any? 타입이라서 type casting 필요함 
print(typeCastedValue2) // optional(true)

// 3️⃣ 저장하지 않은 값 꺼내보기
let invalidValue = UserDefaults.standard.bool(forKey: &quot;InvalidKey&quot;)
let invalidValue2 = UserDefaults.standard.object(forKey: &quot;InvalidKey&quot;)

print(invalidValue)  // false &amp;lt;- ✅ 주의. 저장된 key가 없으면 false 반환 (nil이 아니라서 혼동됨)
print(invalidValue2) // nil&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;KeyForBool 이라는 key에다가 bool 값을 저장했고, 해당 key로 저장된 값을 다시 꺼내봤다.&lt;/li&gt;
&lt;li&gt;bool 메서드로 꺼내면 true가 반환되고, object로 꺼내면 Any? 타입이므로 &lt;span style=&quot;color: #ee2323;&quot;&gt;optional(1)&lt;/span&gt;이 나온다.&lt;br /&gt;그래서 as? Bool로 &lt;span style=&quot;color: #ee2323;&quot;&gt;타입캐스팅&lt;/span&gt;해야 optioanl(true)를 얻을 수 있다.&lt;/li&gt;
&lt;li&gt;유효하지 않은 key로 저장된 값을 꺼내보면,&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;bool 메서드는 false를 반환하므로 주의해야 한다.&lt;/span&gt; &lt;span&gt;내가 저장한 값인지, 해당 key가 존재하지 않는지 구분이 불가하기 때문이다.&lt;br /&gt;object 메서드는 nil을 반환하므로 존재하지 않는 값임을 알 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;구글링 - string 및 object 메서드의 차이점&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그럼 이제 bool은 봤으니까 string 메서드만 확인하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;동일한 블로그에서 이 질문에 대한 답도 얻을 수 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;CocoaCasts의&lt;span&gt; &lt;/span&gt;&lt;a href=&quot;https://cocoacasts.com/ud-2-how-to-store-a-string-in-user-defaults-in-swift&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;How&amp;nbsp;to&amp;nbsp;Store&amp;nbsp;a&amp;nbsp;String&amp;nbsp;In&amp;nbsp;User&amp;nbsp;Defaults&amp;nbsp;In&amp;nbsp;Swift&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&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;UserDefaults에 문자열 값을 저장하고, 다시 이 값을 꺼낼 때 &lt;/span&gt;&lt;span&gt;string, object 둘 다 써도 된다.&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그렇다면 어떤 상황에서 string / object 메서드를 쓰는 게 좋을까?&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 style=&quot;color: #ee2323;&quot;&gt;string을 쓰는 게 좋다. 타입캐스팅이 필요 없고, 가독성이 좋기 때문이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- 본론: I'm sure you agree that the &lt;span style=&quot;background-color: #dddddd;&quot;&gt;string(forKey:)&lt;/span&gt; method is the more elegant option and easier to read and understand.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예제 코드&lt;/h3&gt;
&lt;pre id=&quot;code_1667656299919&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1️⃣ 저장
UserDefaults.standard.set(&quot;저장할 문자열&quot;, forKey: &quot;KeyForString&quot;)

// 2️⃣ 저장한 값 꺼내보기
let value3 = UserDefaults.standard.string(forKey: &quot;KeyForString&quot;)
let value4 = UserDefaults.standard.object(forKey: &quot;KeyForString&quot;)
let typeCastedValue4 = value4 as? String

print(value3)  // Optional(&quot;저장할 문자열&quot;)
print(value4)  // Optional(저장할 문자열) 
print(typeCastedValue4) // Optional(&quot;저장할 문자열&quot;)

// 3️⃣ 저장하지 않은 값 꺼내보기
let invalidValue3 = UserDefaults.standard.string(forKey: &quot;InvalidKey&quot;)
let invalidValue4 = UserDefaults.standard.object(forKey: &quot;InvalidKey&quot;)

print(invalidValue3) // nil ✅
print(invalidValue4) // nil ✅&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위에서 본 bool 메서드와 달리 string 메서드는 key에 저장된 값이 없으면 nil을 반환하므로 헷갈리지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로 UserDefaults 메서드 중에서 &lt;b&gt;반환타입이 옵셔널인 메서드&lt;/b&gt;인 string, array, dictionary, url 등은&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가독성이 좋고, 타입캐스팅이 필요 없으므로 object 메서드 대신 사용하는 게 좋다.&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;b&gt;반환타입이 none-옵셔널인 메서드&lt;/b&gt;인 bool, integer, double 등은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;key에 저장된 값이 없을 때 nil을 반환할 수 없어 혼동될 수 있으므로 object 메서드로 대체하는 것이 좋다.&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Apple Developer Documentation &amp;gt; UserDefaults &amp;gt; &lt;a href=&quot;https://developer.apple.com/documentation/foundation/userdefaults/1410095-object&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;object(forKey:)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Apple Developer Documentation &amp;gt; UserDefaults &amp;gt; &lt;a href=&quot;https://developer.apple.com/documentation/foundation/userdefaults/1416700-string&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;string(forKey:)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Apple Developer Documentation &amp;gt; UserDefaults &amp;gt; &lt;a href=&quot;https://developer.apple.com/documentation/foundation/userdefaults/1416388-bool&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;bool(forKey:)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Blog &amp;gt; CocoaCasts &amp;gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://cocoacasts.com/ud-7-how-to-check-if-a-value-exists-in-user-defaults-in-swift&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;How to Check if a Value Exists in User Defaults in Swift&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Blog &amp;gt; CocoaCasts &amp;gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://cocoacasts.com/ud-2-how-to-store-a-string-in-user-defaults-in-swift&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;How&amp;nbsp;to&amp;nbsp;Store&amp;nbsp;a&amp;nbsp;String&amp;nbsp;In&amp;nbsp;User&amp;nbsp;Defaults&amp;nbsp;In&amp;nbsp;Swift&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>iOS/영문 공식문서 뜯어보기-iOS</category>
      <category>object/bool/string(forKey:)</category>
      <category>UserDefaults</category>
      <category>메서드비교</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/55</guid>
      <comments>https://applecider2020.tistory.com/55#entry55comment</comments>
      <pubDate>Sun, 6 Nov 2022 07:00:02 +0900</pubDate>
    </item>
    <item>
      <title>[Kingfisher] 이미지 처리 라이브러리의 소스코드 뜯어보기 (1/2)</title>
      <link>https://applecider2020.tistory.com/54</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;주니어 개발자 면접을 보면서 이미지 처리에 대한 질문을 꽤 많이 받았다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #9d9d9d;&quot;&gt;앱 출시 프로젝트 때는 서버에서 자체적으로 적당한 크기의 이미지를 저장하도록 수작업..했었기 때문에 대응이 어려웠다..&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #9d9d9d;&quot;&gt;-&amp;gt; 현업에서도 이미지를 서버에서 일괄 관리하는 게 이상적이라고 한다. 즉, 서버가&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; 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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;현업에서는&lt;b&gt; 대용량 이미지 처리, 특히 캐싱&lt;/b&gt;을 위해 &lt;a href=&quot;https://github.com/onevcat/Kingfisher&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;b&gt;Kingfisher&lt;/b&gt;&lt;/a&gt; 라이브러리를 쓴다고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Kingfisher의 주요 기능은 &lt;b&gt;URL&lt;/b&gt;을 넣으면 &lt;b&gt;비동기&lt;/b&gt;로 이미지를 &lt;b&gt;다운로드&lt;/b&gt;하고 &lt;b&gt;캐싱&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span&gt;이번 기회에 Kingfisher 소스코드를 뜯어보자.&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;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;공식문서로 Kingfisher 기능 이해하기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://github.com/onevcat/Kingfisher&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Kingfisher GitHub 페이지&lt;/a&gt;부터 읽어보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Swift 언어로 웹의 이미지를 다운로드 및 캐싱 (Caching)하는 데 사용하는 라이브러리라고 설명하고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;848&quot; data-origin-height=&quot;250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WiNjg/btrM1eQVURI/0aapxfp3WhuLJeWlEbkrq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WiNjg/btrM1eQVURI/0aapxfp3WhuLJeWlEbkrq1/img.png&quot; data-alt=&quot;Kingfisher - GitHub README&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WiNjg/btrM1eQVURI/0aapxfp3WhuLJeWlEbkrq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWiNjg%2FbtrM1eQVURI%2F0aapxfp3WhuLJeWlEbkrq1%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;800&quot; height=&quot;236&quot; data-origin-width=&quot;848&quot; data-origin-height=&quot;250&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Kingfisher - GitHub README&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;주요 기능&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그 다음 &lt;b&gt;Features&lt;/b&gt; 항목 중에 눈에 띄는 게 많았다. 유용할 듯..&lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;이미지 비동기 처리 (Asynchronous)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;이미지 processors &amp;amp; filters를 제공&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;메모리 및 디스크 캐싱을 위한 Multiple-layer hybrid cache (?)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;캐싱 정밀 조정 (&lt;b&gt;expiration date, size limit&lt;/b&gt; 설정 등)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;성능 향상을 위한&lt;b&gt; 다운로드 취소 기능,&lt;/b&gt; 기존 데이터 재사용&lt;span style=&quot;color: #9d9d9d;&quot;&gt; (캐싱이니까 당연함..)&lt;/span&gt;, &lt;/span&gt;&lt;b&gt;이미지 prefetching&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;다운로딩, 캐싱, 이미지 처리 기능을 독립적으로 사용 가능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;URL만 넣으면 이미지를 넣어주는 기능&lt;/b&gt; (UIImageView, UIButton 등 익스텐션)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;이미지 셋팅 시 애니메이션 효과&lt;/b&gt; (ex. fade-in)&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;이미지 로딩 시 custom placeholder, activity indicator&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;Low data 모드 지원&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;기본 사용방법&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;Kingfisher 101 &lt;/b&gt;항목에서 친절하게 사용방법을 안내하고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아래처럼 매우 간단하다. Kingfisher의 UIImageView 익스텐션을 통해 &lt;b&gt;URL만으로 이미지를 다운받아 나타낸다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그리고 자동으로 &lt;b&gt;메모리 및 디스크 캐싱&lt;/b&gt;이 된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1664115012214&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import Kingfisher

let url = URL(string: &quot;https://example.com/image.png&quot;)
imageView.kf.setImage(with: url)  // Kingfisher의 UIImageView 익스텐션을 통해 이미지가 적용됨&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아래 예시를 보자. 사용방법이 아주 간단하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(이외에도 UIImageView 익스텐션 대신 KF builder를 활용하면 SwiftUI로 변환이 아주 쉽다.)&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1664115838500&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let url = URL(string: &quot;https://example.com/high_resolution_image.png&quot;)
let processor = DownsamplingImageProcessor(size: imageView.bounds.size) // ✅ Downsamples it to match the image view size.
             |&amp;gt; RoundCornerImageProcessor(cornerRadius: 20) // Makes it round cornered with a given radius.
imageView.kf.indicatorType = .activity // ✅ Shows a system indicator while downloading.
imageView.kf.setImage(
    with: url,
    placeholder: UIImage(named: &quot;placeholderImage&quot;), // Shows a placeholder image while downloading.
    options: [
        .processor(processor),
        .scaleFactor(UIScreen.main.scale),
        .transition(.fade(1)), // When prepared, it animates the small thumbnail image with a &quot;fade in&quot; effect.
        .cacheOriginalImage // ✅ The original large image is also cached to disk for later use, to get rid of downloading it again in a detail view.
    ])
{
    // A console log is printed when the task finishes, either for success or failure.
    result in
    switch result {
    case .success(let value):
        print(&quot;Task done for: \(value.source.url?.absoluteString ?? &quot;&quot;)&quot;)
    case .failure(let error):
        print(&quot;Job failed: \(error.localizedDescription)&quot;)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;자세한 내용은 본문에서 제공하는 &lt;a href=&quot;https://github.com/onevcat/Kingfisher/wiki/Cheat-Sheet&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Cheat Sheet&lt;/a&gt;를 참고해보자.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Kingfisher 소스코드 뜯어보기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;라이브러리의 소스코드를 확인하려면 항상 &lt;b&gt;Sources&lt;/b&gt; 폴더를 보면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;799&quot; data-origin-height=&quot;633&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2Y2WZ/btrMZEWGA69/vLuAbFjplyNNZhpLEcnreK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2Y2WZ/btrMZEWGA69/vLuAbFjplyNNZhpLEcnreK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2Y2WZ/btrMZEWGA69/vLuAbFjplyNNZhpLEcnreK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2Y2WZ%2FbtrMZEWGA69%2FvLuAbFjplyNNZhpLEcnreK%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;600&quot; height=&quot;475&quot; data-origin-width=&quot;799&quot; data-origin-height=&quot;633&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. Cache&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;먼저 Cache 부분을 뜯어보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #9d9d9d;&quot;&gt;소스코드만 봤는데 이해가 안 돼서.. &lt;span style=&quot;color: #333333;&quot;&gt;Cheat Sheet의 &lt;a href=&quot;https://github.com/onevcat/Kingfisher/wiki/Cheat-Sheet#cache&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Cache&lt;/a&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&quot;URL&quot; 자체가 &lt;b&gt;cache key&lt;/b&gt;가 된다. &lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(cache key를 바꾸고 싶다면&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;ImageResource를 사용해서 지정할 수 있다.)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;cache type&lt;/b&gt;은 .memory, .disk, .none 세 가지가 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이미지를 다운받을 때 processor를 사용했다면, processed 이미지가 캐싱된다. (setImage 메서드 매개변수로 processor를 전달)&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;메모리 관리를 위해 캐싱 삭제 정책이 있다. (NSCache 문서 내용과 매우 비슷하다!)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;메모리 저장&lt;/b&gt;과 관련해서&lt;b&gt; totalCostLimit, countLimit&lt;/b&gt;을 설정할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;default로 &lt;span style=&quot;color: #ee2323;&quot;&gt;기기 전체 메모리의 25%를 메모리 캐시의 totalCostLimit으로 설정&lt;/span&gt;하고, countLimit은 없다.&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;디스크 저장&lt;/b&gt;과 관련해서 파일시스템의 저장공간인&amp;nbsp;&lt;b&gt;sizeLimit&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;메모리 및 디스크 캐싱 모두 &lt;b&gt;expiration&lt;/b&gt; (만료 기간)을 설정 가능하다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;default로 메모리 캐시는 접근 이후 &lt;span style=&quot;color: #ee2323;&quot;&gt;5분 뒤&lt;/span&gt; 만료되며, &lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;디스크 캐시는 &lt;span style=&quot;color: #ee2323;&quot;&gt;1주 뒤&lt;/span&gt; 만료된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;대략적인 Kingfisher의 Cache 기능에 대해 이해한 것 같다.. 이제 드디어 코드를 뜯어보자.&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1) Cache - MemoryStorage&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;먼저 익숙한 &lt;b&gt;MemoryStorage&lt;/b&gt;&amp;nbsp;폴더를 보자.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1664119537927&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public enum MemoryStorage {
    public class Backend&amp;lt;T: CacheCostCalculable&amp;gt; {
        let storage = NSCache&amp;lt;NSString, StorageObject&amp;lt;T&amp;gt;&amp;gt;()
        // Keys trackes the objects once inside the storage.
        var keys = Set&amp;lt;String&amp;gt;()

        private var cleanTimer: Timer? = nil
        private let lock = NSLock()

        public var config: Config {
            didSet {
                storage.totalCostLimit = config.totalCostLimit
                storage.countLimit = config.countLimit
            }
        }

        /// Creates a `MemoryStorage` with a given `config`.
        public init(config: Config) {
            self.config = config
            storage.totalCostLimit = config.totalCostLimit
            storage.countLimit = config.countLimit

            cleanTimer = .scheduledTimer(withTimeInterval: config.cleanInterval, repeats: true) { [weak self] _ in
                guard let self = self else { return }
                self.removeExpired()
            }
        }
 // ...&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예상했듯이 storage 타입이&amp;nbsp;&lt;span style=&quot;color: #ee2323;&quot;&gt;NSCache&lt;/span&gt;이다. 위에서 cache key를 URL String으로 쓰고 있기 때문에 &lt;br /&gt;NSCache의 key 타입이 NSString으로 되어있다.&lt;/li&gt;
&lt;li&gt;NSCache 밖에서 캐시 기능을 구현하고 있기 때문에 &lt;b&gt;NSLock&lt;/b&gt;을 통해 thread-safe하게 처리를 해주고 있다.&lt;/li&gt;
&lt;li&gt;init으로 전달하는 Config 타입을 보면&lt;b&gt; totalCostLimit, countLimit&lt;/b&gt;을 프로퍼티로 가진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1664119813469&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        /// Removes the expired values from the storage.
        public func removeExpired() {
            lock.lock()
            defer { lock.unlock() }
            for key in keys {
                let nsKey = key as NSString
                guard let object = storage.object(forKey: nsKey) else {
                    keys.remove(key) // 유효하지 않은 key이므로 keys에서 제거함
                    continue
                }
                if object.isExpired { // 만료됐다면 storage에서 제거함
                    storage.removeObject(forKey: nsKey)
                    keys.remove(key)
                }
            }
        }
        
        /// Stores a value to the storage under the specified key and expiration policy.
        public func store(
            value: T,
            forKey key: String,
            expiration: StorageExpiration? = nil)
        {
            storeNoThrow(value: value, forKey: key, expiration: expiration)
        }
        
        func storeNoThrow(
            value: T,
            forKey key: String,
            expiration: StorageExpiration? = nil)
        {
            lock.lock()
            defer { lock.unlock() }
            let expiration = expiration ?? config.expiration
            // The expiration indicates that already expired, no need to store.
            guard !expiration.isExpired else { return }
            
            let object: StorageObject&amp;lt;T&amp;gt;
            if config.keepWhenEnteringBackground {
                object = BackgroundKeepingStorageObject(value, expiration: expiration)
            } else {
                object = StorageObject(value, expiration: expiration)
            }
            storage.setObject(object, forKey: key as NSString, cost: value.cacheCost) // 저장
            keys.insert(key)
        }&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;storage 및 keys에서 만료된 이미지를 삭제하고, 새로운 이미지를 저장하는 기능이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1664120130014&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        /// Gets a value from the storage.
        public func value(forKey key: String, extendingExpiration: ExpirationExtending = .cacheTime) -&amp;gt; T? {
            guard let object = storage.object(forKey: key as NSString) else {
                return nil
            }
            if object.isExpired {
                return nil
            }
            object.extendExpiration(extendingExpiration)
            return object.value
        }

        /// Whether there is valid cached data under a given key.
        public func isCached(forKey key: String) -&amp;gt; Bool {
            guard let _ = value(forKey: key, extendingExpiration: .none) else {
                return false
            }
            return true
        }&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;value&lt;/span&gt; 메서드를 통해 메모리에 캐싱되어 있는 이미지를 얻는다.&lt;br /&gt;만료된 이미지라면 nil을 반환한다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;isCached&lt;/span&gt; 메서드를 통해 특정 URL 이미지의 캐싱 여부를 확인 가능하다.&lt;/li&gt;
&lt;li&gt;이외에도 Config 타입을 보면 default로 &lt;b&gt;expiration은 .seconds(300)&lt;/b&gt; == 5분, &lt;b&gt;keepWhenEnteringBackground는 false&lt;/b&gt; (앱이 백그라운드로 넘어가자마자 메모리 캐시를 삭제함), cleanInterval = 120 등 세부사항을 알 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2) Cache - Storage&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맨 마지막에 있던 StorageExpiration 타입이 궁금했는데, Cache &amp;gt; &lt;b&gt;Storage&lt;/b&gt; 파일에 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;StorageExpiration&lt;span&gt; &lt;/span&gt;&lt;/span&gt;열거형에는 5개 case (never, seconds, days, date, expired)가 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1664121005362&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    func estimatedExpirationSince(_ date: Date) -&amp;gt; Date {
        switch self {
        case .never: return .distantFuture
        case .seconds(let seconds):
            return date.addingTimeInterval(seconds)
        case .days(let days):
            let duration: TimeInterval = TimeInterval(TimeConstants.secondsInOneDay) * TimeInterval(days)
            return date.addingTimeInterval(duration)
        case .date(let ref):
            return ref
        case .expired:
            return .distantPast
        }
    }
    
    var estimatedExpirationSinceNow: Date {
        return estimatedExpirationSince(Date())  // Date() == 현재 시각
    }&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재로부터 예상 만료기간을 구하기 위해 위 코드가 작성됐다. &lt;span style=&quot;color: #9d9d9d;&quot;&gt;가독성 좋은 코드이다..&lt;/span&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;ExpirationExtending 열거형은 특정 이미지에 접근했을 때 expiration 기간을 연장시킬지 여부를 관리하기 위한 타입이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1664121300699&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/// Represents the expiration extending strategy used in storage to after access.
public enum ExpirationExtending {
    /// The item expires after the original time, without extending after access.
    case none
    /// The item expiration extends by the original cache time after each access.
    case cacheTime
    /// The item expiration extends by the provided time after each access.
    case expirationTime(_ expiration: StorageExpiration)
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;none이면 해당 이미지에 접근했더라도 만료기한을 연장하지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3) Cache - ImageCache&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cache &amp;gt; ImageCache 파일이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;유효기한 만료, 최대저장용량 초과 등 캐시 삭제 정책에 의해 디스크 캐시가 비워졌을 때 아래의 Notification이 post된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1664121461298&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;extension Notification.Name {
    public static let KingfisherDidCleanDiskCache =
        Notification.Name(&quot;com.onevcat.Kingfisher.KingfisherDidCleanDiskCache&quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;CacheType&lt;/b&gt;에는&amp;nbsp;.none,&amp;nbsp;.memory,&amp;nbsp;.disk가&amp;nbsp;있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1664122015934&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/// Represents a hybrid caching system which is composed by a `MemoryStorage.Backend` and a `DiskStorage.Backend`.
open class ImageCache {
    public static let `default` = ImageCache(name: &quot;default&quot;)  // 싱글톤 사용
    
    public let memoryStorage: MemoryStorage.Backend&amp;lt;KFCrossPlatformImage&amp;gt;
    public let diskStorage: DiskStorage.Backend&amp;lt;Data&amp;gt;
    
    private let ioQueue: DispatchQueue  // 비동기 처리 목적&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;hybrid caching system이라길래 대단한 건 줄 알았는데 그냥 &lt;b&gt;메모리 캐싱 + 디스크 캐싱 둘 다 된다&lt;/b&gt;는 뜻이었다..&lt;/li&gt;
&lt;li&gt;비동기 처리를 위해 DispatchQueue를 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드디어 이미지 저장 기능을 하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;store&lt;/span&gt;&lt;span&gt;&amp;nbsp;메서드...!를&lt;/span&gt; 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1664122519517&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    // MARK: Storing Images
    open func store(_ image: KFCrossPlatformImage,
                    original: Data? = nil,
                    forKey key: String,
                    options: KingfisherParsedOptionsInfo,
                    toDisk: Bool = true,
                    completionHandler: ((CacheStoreResult) -&amp;gt; Void)? = nil)
    {
        let identifier = options.processor.identifier
        let callbackQueue = options.callbackQueue
        
        let computedKey = key.computedKey(with: identifier)
        // Memory storage should not throw.
        memoryStorage.storeNoThrow(value: image, forKey: computedKey, expiration: options.memoryCacheExpiration)
        
        guard toDisk else {
            if let completionHandler = completionHandler {
                let result = CacheStoreResult(memoryCacheResult: .success(()), diskCacheResult: .success(()))
                callbackQueue.execute { completionHandler(result) }
            }
            return
        }
        
        ioQueue.async {
            let serializer = options.cacheSerializer
            if let data = serializer.data(with: image, original: original) {
                self.syncStoreToDisk(
                    data,
                    forKey: key,
                    processorIdentifier: identifier,
                    callbackQueue: callbackQueue,
                    expiration: options.diskCacheExpiration,
                    writeOptions: options.diskStoreWriteOptions,
                    completionHandler: completionHandler)
            } else {
                guard let completionHandler = completionHandler else { return }
                
                let diskError = KingfisherError.cacheError(
                    reason: .cannotSerializeImage(image: image, original: original, serializer: serializer))
                let result = CacheStoreResult(
                    memoryCacheResult: .success(()),
                    diskCacheResult: .failure(diskError))
                callbackQueue.execute { completionHandler(result) }
            }
        }
    }&lt;/code&gt;&lt;/pre&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;b&gt;메모리 캐시에 저장&lt;/b&gt; : memoryStorage.storeNoThrow 메서드를 통해 (Cache &amp;gt; MemoryStorage 파일 참고)&lt;br /&gt;특정 key에 대한 이미지 value를 expiration 옵션으로 저장한다.&lt;/li&gt;
&lt;li&gt;매개변수 toDisk가 true이면 completionHadler를 통해 뭔가를 실행하는데..&lt;br /&gt;completionHandler에 무슨 값이 올지 확인해봐야 할 것 같다.&lt;br /&gt;일단 CacheStoreResult에다가 메모리 캐시, 디스크 캐시 모두 success 했다고 보낸다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;디스크 캐시에 저장&lt;/b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;:&lt;span&gt; &lt;/span&gt;&lt;/span&gt;cacheSerializer를 통해서 특정 이미지와 원본 이미지 데이터가 있으면, &lt;br /&gt;이 데이터를 디스크에다가 저장해서 동기화 (sync)하는 작업을 진행한다.&lt;br /&gt;데이터가 없으면 diskError를 생성해서 failure에 담고 다시 CacheStoreResult에 보낸다. &lt;br /&gt;(위에서 success라고 처리했는데, serialize 과정에서 문제가 생겼으니까 다시 failure로 바꿔주는 듯)&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;이 라이브러리를 직접 써봐야 더 확실히 알 것 같다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. Processor&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이미지 처리 기능은 어떤 게 있는지 보자. (Cheat Sheet 참고)&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1664117706946&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Round corner
let processor = RoundCornerImageProcessor(cornerRadius: 20)

// Downsampling
let processor = DownsamplingImageProcessor(size: CGSize(width: 100, height: 100))

// Cropping
let processor = CroppingImageProcessor(size: CGSize(width: 100, height: 100), anchor: CGPoint(x: 0.5, y: 0.5))

// Blur
let processor = BlurImageProcessor(blurRadius: 5.0)&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;RoundCornerImageProcessor : 모서리 둥글게 처리&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;DownSamplingImageProcessor&lt;/span&gt;&lt;b&gt;&amp;nbsp;:&amp;nbsp;&lt;/b&gt;샘플이미지 다운&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;CroppingImageProcessor : 이미지 크롭&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;BlurImageProcessor : 블러 처리&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;이외에도 overlay, tint color 등 여러 가지가 있음&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;README 예시에서 봤던 것처럼 &lt;span style=&quot;color: #ee2323;&quot;&gt;DownSamplingImageProcessor&lt;/span&gt;를 통해 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;고해상도 이미지를 다운받을 때, 메모리에 로딩하기 전에 썸네일 이미지를 만들어서 UIView 크기에 맞게 사용&lt;/b&gt;하도록 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이 기능이 정말 유용할 듯..&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;성능 효율을 위한 팁&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CollectionView에서 빠르게 스크롤되면서 화면에서 사라지는 Cell의 이미지는 didEndDisplaying 메서드에서 다운로드 작업을 cancel 시킨다.&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;&lt;span&gt;Cache 내용이 방대해서 포스트가 길어졌는데, 다음에 다른 기능에서 더 다뤄보면 좋을 것 같다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;GitHub &amp;gt; &lt;a href=&quot;https://github.com/onevcat/Kingfisher&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;onevcat/Kingfisher&lt;/a&gt; &amp;gt; &lt;a href=&quot;https://github.com/onevcat/Kingfisher/wiki/Cheat-Sheet&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;CheatSheet&lt;/a&gt;&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>비전공자용 노력/개발 툴</category>
      <category>Cache</category>
      <category>Kingfisher</category>
      <category>라이브러리</category>
      <category>이미지처리</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/54</guid>
      <comments>https://applecider2020.tistory.com/54#entry54comment</comments>
      <pubDate>Sat, 5 Nov 2022 16:24:01 +0900</pubDate>
    </item>
    <item>
      <title>[APNs] Push Notification 보내기 (1/2) - 관련 공식문서 요약</title>
      <link>https://applecider2020.tistory.com/53</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;앱이 Background 상태에 있더라도&amp;nbsp;사용자에게 알림을 띄우는 기능인&amp;nbsp;Push Notification에 대해 알아보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;*Notification의 디자인은 &lt;a href=&quot;https://applecider2020.tistory.com/52&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;HIG 문서 요약본 포스트&lt;/a&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이번 포스팅에서는 공식문서를 훑어보고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;다음 포스팅에서 &lt;span&gt;Notification 관련 4가지 주체에 대해 정리하면서, 예제 코드를 작성해볼 계획이다.&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;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Push Notification이란?&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;특정 앱에서 사용자에게 알림을 띄우는 것을 말하는데,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아래 화면처럼 &lt;b&gt;잠금 화면, 알림 센터, 배너, 사운드, 배지&lt;/b&gt; 등의 형태가 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;2532&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dro4se/btrNv8jRKe4/s7ydnZwdLsxkODQ9JGiHV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dro4se/btrNv8jRKe4/s7ydnZwdLsxkODQ9JGiHV1/img.png&quot; data-alt=&quot;설정 &amp;amp;gt; 알림 &amp;amp;gt; 특정 앱&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dro4se/btrNv8jRKe4/s7ydnZwdLsxkODQ9JGiHV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdro4se%2FbtrNv8jRKe4%2Fs7ydnZwdLsxkODQ9JGiHV1%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;300&quot; height=&quot;649&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;2532&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;설정 &amp;gt; 알림 &amp;gt; 특정 앱&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;여기서 &lt;b&gt;배지 (Badge)&lt;/b&gt;란 아래처럼 앱 우상단에 빨간 점으로 표시되는 형태이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;참고 -&amp;nbsp;&lt;a href=&quot;https://discussions.apple.com/thread/250735184&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;What is a badge?&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;385&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mNwLr/btrNwjegDmF/ve6iU5KLiuqFGZMdkFMhFK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mNwLr/btrNwjegDmF/ve6iU5KLiuqFGZMdkFMhFK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mNwLr/btrNwjegDmF/ve6iU5KLiuqFGZMdkFMhFK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmNwLr%2FbtrNwjegDmF%2Fve6iU5KLiuqFGZMdkFMhFK%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;300&quot; height=&quot;193&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;385&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;APNs란? &lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;APNs는 &lt;b&gt;Apple Push Notification service&lt;/b&gt;를 말한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;third party 개발자 (ex. Push Server)가 앱에 Notification을 보낼 수 있도록 Apple에서 만든 알림 서비스이다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;User Notifications 공식문서들&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;대략적인 개념을 정리했으니 이제 공식문서를 읽어보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Push Notification 관련 최상위 공식문서는 Framework -&amp;nbsp;User&amp;nbsp;Notifications이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;Framework라고 되어있으니까 나중에&lt;/span&gt; &lt;span style=&quot;background-color: #dddddd;&quot;&gt;import 어쩌구&lt;/span&gt; &lt;span style=&quot;color: #9d9d9d;&quot;&gt;해서 사용할 거라고 유추할 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt; &amp;nbsp;&lt;/span&gt;Framework&amp;nbsp;-&amp;nbsp;User&amp;nbsp;Notifications &lt;a href=&quot;https://developer.apple.com/documentation/usernotifications&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;(링크)&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;요약 : 사용자 기기에게 notifications를 push한다. 이 notifications를 remote 서버로부터 보내거나, notifications 자체를 앱으로부터 local하게 생성해서 띄울 수 있다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Push&amp;nbsp;user-facing&amp;nbsp;notifications&amp;nbsp;to&amp;nbsp;the&amp;nbsp;user&amp;rsquo;s&amp;nbsp;device&amp;nbsp;from&amp;nbsp;a&amp;nbsp;server,&amp;nbsp;or&amp;nbsp;generate&amp;nbsp;them&amp;nbsp;locally&amp;nbsp;from&amp;nbsp;your&amp;nbsp;app.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1006&quot; data-origin-height=&quot;507&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1ouXX/btrNyF18gsT/R0hW5YoeaoLTJHlYPE8701/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1ouXX/btrNyF18gsT/R0hW5YoeaoLTJHlYPE8701/img.png&quot; data-alt=&quot;User Notifications - 알림 센터 (왼쪽), 배너 (오른쪽)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1ouXX/btrNyF18gsT/R0hW5YoeaoLTJHlYPE8701/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1ouXX%2FbtrNyF18gsT%2FR0hW5YoeaoLTJHlYPE8701%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;600&quot; height=&quot;302&quot; data-origin-width=&quot;1006&quot; data-origin-height=&quot;507&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;User Notifications - 알림 센터 (왼쪽), 배너 (오른쪽)&lt;/figcaption&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Overview를 한 줄씩 읽어보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;앱의 사용자에게 중요한 메시지를 전달할 때 notification을 사용할 수 있는데, &lt;b&gt;앱이 실행 중인지와 상관없이 가능하다.&lt;/b&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;즉, 앱이 Foreground에 있어도 Background에 있어도 notification을 띄울 수 있다는 의미이다.&amp;nbsp;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예를 들어 &lt;b&gt;스포츠앱&lt;/b&gt;은 사용자에게 어떤 팀의 게임 스코어를 알려줄 수 있다. 이때&amp;nbsp;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;notification은 특정 앱에게 데이터를 다운 받아서 인터페이스를 업데이트하도록 알릴 수 있다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;notification은 alert를 표시하거나, 사운드를 내거나, 배지를 설정하는 형태로 나타난다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;notification은 앱으로부터 &lt;b&gt;local하게 생성&lt;/b&gt;할 수도 있고, 개발자가 관리하는 서버로부터 &lt;b&gt;remote하게 생성&lt;/b&gt;할 수도 있다.&lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;local notification : 앱 자체적으로 notification의 content 및 condition을 설정한다. 이때 condition (=&amp;nbsp;알림을 trigger할 조건)으로 time, location 등을 설정할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;remote notification : &lt;span style=&quot;color: #ee2323;&quot;&gt;회사의 백엔드 서버가 push notification을 생성하면, APNs가 notification을 관리하고 있다가&amp;nbsp;사용자의 기기에 해당 notification을 전달한다.&lt;br /&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;-&amp;gt; 앱 대부분 이 방식에다가&amp;nbsp;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;FMC (&lt;/span&gt;&lt;/i&gt;Firebase의 Cloud Messaging)을 추가적으로 활용한다.&amp;nbsp;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;User Notifications Framework는 아래의 목적으로 활용하면 된다.&lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;앱이 지원하는 notifications의 타입을 정의한다. &lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;(사운드, 배지, 알림 센터 등을 말하는 듯)&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;notification과 관련된 custom action을 정의한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;local notification을 스케쥴링한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이미 전달된 notification을 처리한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;user-selected action에 대응한다. &lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;(사용자가 알림의 특정 옵션을 선택했을 때를 말하는 듯)&lt;/span&gt;&lt;/i&gt;&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;시스템은 local/remote notification을 정해진 시점에 전달하기 위해 최대한 노력하겠지만, &lt;b&gt;전달이 보장되지는 않는다. (delivery&amp;nbsp;isn&amp;rsquo;t&amp;nbsp;guaranteed)&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;엥.. 이유는 모르겠지만.. notification이 설정한 대로 전달되지 않을 수 있는 가능성을 염두에 두어야 할 듯&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;PushKit&lt;/b&gt; framework가 특정한 타입의 notification에 대해서는 시간을 더 잘 지키는 전달 메커니즘을 제공한다. 예를 들어&amp;nbsp;VoIP&amp;nbsp;및&amp;nbsp;watchOS를&amp;nbsp;합쳐서&amp;nbsp;사용하는&amp;nbsp;경우가 그렇다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이제 공식문서 하단의 Topics에서 읽어볼 만한 것을 추려보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;최상단의 &lt;b&gt;Essentials, Notification Management&lt;/b&gt;&amp;nbsp;항목은 당연히 읽어야 하고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;대부분 FCM을 활용한 remote notification을 구현하므로 &lt;b&gt;Remote&amp;nbsp;Notifications&lt;/b&gt;도 읽으면 될 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;시간이 있으면&amp;nbsp;Notification&amp;nbsp;Categories&amp;nbsp;and&amp;nbsp;User&amp;nbsp;Actions, Notification&amp;nbsp;Responses도 보면 좋을 듯&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;  Article&lt;/span&gt;&amp;nbsp;- Asking&amp;nbsp;Permission&amp;nbsp;to&amp;nbsp;Use&amp;nbsp;Notifications &lt;a href=&quot;https://developer.apple.com/documentation/usernotifications/asking_permission_to_use_notifications&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;(링크)&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;요약 : notification에 대응하고자 alerts/sound/badge를 표시하려면, 사용자에게 permission을 요청해야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;문서의 permission = authorization&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;local/remote notification을 받으면 사용자가 볼 수 있게 alerts/sound/badge를 표시하게 되는데, 이러한 interaction은 &lt;span style=&quot;color: #ee2323;&quot;&gt;앱이 실행 중이지 않거나, background에 있을 때&lt;/span&gt; 발생한다. (앱이 foreground에 있을 때는 시스템이 알아서 안 띄운다는 뜻인가???)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;448&quot; data-origin-height=&quot;312&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxEyuG/btrNwEXuGYI/tpIqxeUN6HEewtTtTHehK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxEyuG/btrNwEXuGYI/tpIqxeUN6HEewtTtTHehK1/img.png&quot; data-alt=&quot;Requesting permission&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxEyuG/btrNwEXuGYI/tpIqxeUN6HEewtTtTHehK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxEyuG%2FbtrNwEXuGYI%2FtpIqxeUN6HEewtTtTHehK1%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;260&quot; height=&quot;181&quot; data-origin-width=&quot;448&quot; data-origin-height=&quot;312&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Requesting permission&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 id=&quot;2979370&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Explicitly Request Authorization in Context&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1664769468556&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let center = UNUserNotificationCenter.current()  // 싱글톤

// ✅ options에 요청할 iteraction type을 명시
center.requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in  
    // ✅ granted : 사용자가 alert에 대해 allow 했다면 true, not allowed 했다면 false 
    // error: 에러가 발생했다면 error info, 에러가 없었다면 nil
    
    if let error = error {
        // Handle the error here.
    }
    
    // Enable or disable features based on the authorization.
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;UNUserNotificationCenter : 싱글톤 객체이고, notification 관련 activities를 관리한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;사용자에게 permission을 요청하기 위해 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;requestAuthorization&lt;/span&gt; 메서드를 호출한다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;위 코드에서는 &lt;span style=&quot;color: #ee2323;&quot;&gt;요청할&amp;nbsp;iteraction type&lt;/span&gt;으로 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;.alert,&amp;nbsp;.sound,&amp;nbsp;.badge&lt;/span&gt;를 설정했다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;앱이 최초로 authorization을 요청할 때,&lt;/b&gt; 시스템이 위 사진처럼 alert를 띄워서 allow 할지 말지를 사용자에게 묻고, &lt;b&gt;사용자 응답을 기록&lt;/b&gt;한다. 그래서 이후로는 사용자에게 묻지 않는다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;문맥상 왜 notification 기능이 필요한지 사용자가 이해할 수 있게 하면 좋다.&lt;/b&gt; &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예를 들어 &lt;span style=&quot;color: #ee2323;&quot;&gt;task-tracking 앱이라면, 첫 번째 task를 생성한 시점에 request를 띄우도록 한다.&lt;/span&gt; &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(앱을 최초 실행했을 때 요청하는 것보다 UX 측면에서 낫다.)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Use Provisional Authorization to Send Trial Notifications&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;사용자 입장에서는 앱의 notification을 사용해보기도 전에 permission 요청에 대해 응답해야 한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;따라서 사용자가 써보고 결정할 수 있도록 &lt;b&gt;체험판 (provisional) authorization&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;provisional notification은 시스템이 &lt;b&gt;알림 센터&lt;/b&gt;에만 띄운다. (sound, 배지, 잠금화면에 나타나지 않는다.)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;그리고 사용자는 버튼을 통해 notification을 유지할지 끌지 결정할 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;816&quot; data-origin-height=&quot;474&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6ECsS/btrNIKBuU0h/uOXvKbGjnPH0yepZkKg6e0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6ECsS/btrNIKBuU0h/uOXvKbGjnPH0yepZkKg6e0/img.png&quot; data-alt=&quot;알림 센터에 뜬 provisional notification&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6ECsS/btrNIKBuU0h/uOXvKbGjnPH0yepZkKg6e0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6ECsS%2FbtrNIKBuU0h%2FuOXvKbGjnPH0yepZkKg6e0%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;350&quot; height=&quot;203&quot; data-origin-width=&quot;816&quot; data-origin-height=&quot;474&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;알림 센터에 뜬 provisional notification&lt;/figcaption&gt;
&lt;/figure&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Keep 버튼 : prominent/quiet notification 선택지를 보여준다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;prominent : notification permission을 얻은 상태가 되어 alert/sound/badge를 띄울 수 있다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(엄밀히는 provisional request에 포함된 항목들을 띄울 수 있다.)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;quiet : 알림 센터에서만 띄울 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Turn off 버튼 : authorization을 거부하려는 사용자의 선택을 재확인하는 절차를 거친다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;provisional authorization을 요청하는 건 쉽다. &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;위에서 본 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;requestAuthorization&lt;/span&gt; 메서드의 options 매개변수에 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;.provisional&lt;/span&gt;만 추가하면 된다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1664795271580&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .sound, .badge, .provisional]) { granted, error in  // options에 .provisional만 추가됨
    
    if let error = error {
        // Handle the error here.
    }
    
    // Provisional authorization granted.
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;위에서 봤던 explicitly requesting authorization과 달리, &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;provisional authorization 코드는 &lt;b&gt;사용자에게 permission을 요청하는 alert를 띄우지 않는다.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;대신, 메서드를 최초 호출하는 시점에 자동으로 권한이 부여된다.&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;사용자가 Keep/Turn off 버튼을 탭할 때까지 &lt;span style=&quot;color: #ee2323;&quot;&gt;authorization status&lt;/span&gt;는 .provisional 상태가 된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;provisional authorization을 사용하면 앱을 최초 실행하는 시점에 authorization을 요청할 수 있다. ???&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;-&amp;gt; 뭐지... 방금 alert 안 띄우고 자동으로 권한 부여한다며... 확인 필요&lt;/i&gt;&lt;/span&gt;&lt;br /&gt;사용자가 notification을 계속 사용할지 말지는 notification을 받은 이후에나 설정할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Customize&amp;nbsp;Notifications&amp;nbsp;Based&amp;nbsp;on&amp;nbsp;the&amp;nbsp;Current&amp;nbsp;Authorizations&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;local notification을 스케쥴링하기 전에 앱의 authorization&amp;nbsp;status를 항상 확인해야 한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;사용자는 언제든 특정 앱의&amp;nbsp;authorization 설정과 interation type을 변경할 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;getNotificationSettings&lt;/span&gt; 메서드를 호출하면 &lt;b&gt;현재 &lt;/b&gt;&lt;b&gt;notification 설정&lt;/b&gt;을 알아낼 수 있고,&lt;br /&gt;이 설정값을 바탕으로 custom notification을 만들 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1664796146852&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let center = UNUserNotificationCenter.current()
center.getNotificationSettings { settings in  // 현재 설정값을 가져옴
    guard (settings.authorizationStatus == .authorized) ||
          (settings.authorizationStatus == .provisional) else { return }

    if settings.alertSetting == .enabled {
        // Schedule an alert-only notification.
    } else {
        // Schedule a notification with a badge and sound.
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이 코드는 guard문을 통해 authorization status가 .authorized 또는 .provisional일 때만, 특정 notification을 스케쥴링하도록 했다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;또한 if문에서 interaction type에 따라 notification을 구성했다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;일부&amp;nbsp;interaction에&amp;nbsp;대한&amp;nbsp;권한이&amp;nbsp;없더라도&amp;nbsp;alert/sound/badge를&amp;nbsp;띄울&amp;nbsp;수&amp;nbsp;있다.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;만약 UNNotificationSettings의 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;notificationCenterSetting&lt;/span&gt; 프로퍼티를 .enabled로 설정하면, 시스템은 알림 센터에 alert를 띄울 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;또한 notification&amp;nbsp;center&amp;nbsp;delegate의 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;a style=&quot;background-color: #dddddd;&quot; href=&quot;https://developer.apple.com/documentation/usernotifications/unusernotificationcenterdelegate/1649518-usernotificationcenter&quot;&gt;userNotificationCenter(_:willPresent:withCompletionHandler:)&lt;/a&gt;&lt;/span&gt; 메서드를 통해 &lt;span style=&quot;color: #ee2323;&quot;&gt;앱이 foreground에 있을 때 도착한 notification을 처리&lt;/span&gt;할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;*참고로 notification center delegate의 &lt;a href=&quot;https://developer.apple.com/documentation/usernotifications/unusernotificationcenterdelegate/1649501-usernotificationcenter&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;userNotificationCenter(_:didReceive:withCompletionHandler:)&lt;/a&gt; 메서드를 통해 &lt;span style=&quot;color: #ee2323;&quot;&gt;사용자가 반응한 notification action (user-selected action)을 처리&lt;/span&gt;할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;  Class&lt;/span&gt; - UNUserNotificationCenter&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/usernotifications/unusernotificationcenter&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;(링크)&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;요약 : notification 관련 activities를 관리한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;네이밍에서 알 수 있듯이 notification을 관리하는 중심 역할을 한다.&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;User Notification 문서의 Topics에서도 대부분 볼 수 있는 내용이다.&lt;/span&gt;&lt;/i&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;UNUserNotificationCenter 싱글톤 객체를 사용해야 하는 상황은 아래와 같다.&lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;alerts/sound/badge를 표시할 수 있도록 &lt;b&gt;사용자에게 authorization 요청&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;notification type 및 custom action을 선언&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;notification 스케쥴링&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;APNs를 거쳐서 전달된 remote notification의 &lt;b&gt;payload를 처리&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이미 전달되어 알림 센터에 떠있는 notification 관리&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;custom notification type과 관련된 user-selected action을 처리&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;앱에 대한 notification 관련 설정&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;incoming notifications and notification-related actions을 처리하려면, UNUserNotificationCenterDelegate을 채택한 객체가 필요하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;여러 스레드에서 동시에 notification center에 접근했다면 (싱글톤 객체이므로) 접근한 순서대로 작업을 처리한다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;  &lt;span style=&quot;color: #ee2323;&quot;&gt;Article - Setting&amp;nbsp;Up&amp;nbsp;a&amp;nbsp;Remote&amp;nbsp;Notification&amp;nbsp;Server&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;(링크)&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;요약 : notification을 생성하고 사용자 기기에 띄운다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;✨ 대부분 FCM을 활용하므로 이 문서가 중요하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;remote notification의 주요 요소&lt;/b&gt;&lt;/span&gt;는 아래와 같다.&lt;/span&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;b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;회사 서버 (provider server)&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;APNs&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt; (Apple Push Notification service)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;사용자 기기&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;앱&lt;/b&gt; (사용자 기기에 설치된 상태)&lt;/span&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;remote notification은&lt;span style=&quot;color: #006dd7;&quot;&gt; &lt;b&gt;회사 서버&lt;/b&gt;&lt;/span&gt;에서 시작하는데, 먼저 어떤 notification을 언제 보낼지 정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 시점이 되면 &lt;b&gt;(1) notification 데이터&lt;/b&gt;와 &lt;b&gt;(2) 사용자 기기 identifier&lt;/b&gt;를 포함한 request를 생성한 뒤, request를 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;APNs&lt;/b&gt;&lt;/span&gt;로 보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;APNs는 notification을 사용자 기기에 전달하는 것을 관리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;사용자 기기&lt;/b&gt;&lt;/span&gt;가 notification을 받으면, 기기 OS가 모든 사용자 interaction을 처리하고 notification을 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;앱&lt;/b&gt;&lt;/span&gt;으로 전달한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;866&quot; data-origin-height=&quot;256&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bN3pxA/btrNJBLkEAO/Z6k8fx0RwHe540tFOflOF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bN3pxA/btrNJBLkEAO/Z6k8fx0RwHe540tFOflOF0/img.png&quot; data-alt=&quot;remote notification의 전달 과정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bN3pxA/btrNJBLkEAO/Z6k8fx0RwHe540tFOflOF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbN3pxA%2FbtrNJBLkEAO%2FZ6k8fx0RwHe540tFOflOF0%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;820&quot; height=&quot;242&quot; data-origin-width=&quot;866&quot; data-origin-height=&quot;256&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;remote notification의 전달 과정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;개발자는 provider server를 준비하고, 앱에서 notification을 처리하도록 구성해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;앱이 remote notification을 처리하는 방법은&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/usernotifications/registering_your_app_with_apns&quot;&gt;Registering Your App with APNs&lt;/a&gt; &lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;문서를 참고해야 한다.&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;Build&amp;nbsp;Custom&amp;nbsp;Infrastructure&amp;nbsp;for&amp;nbsp;Notifications&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;remote notification server를 셋업하려면 몇 가지 task가 필요하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;이러한 task를 어떻게 구현할지는 회사의 infrastructure에 따라 다르며, 적절한 기술을 활용하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;앱 인스턴스로부터 device token을 얻고, 이 token을 앱 사용자 계정에 연동시킨다.&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;See&lt;/span&gt; &lt;a href=&quot;https://developer.apple.com/documentation/usernotifications/registering_your_app_with_apns&quot;&gt;Registering Your App with APNs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;사용자에게 언제 notification을 보낼지 정하고, &lt;span style=&quot;color: #ee2323;&quot;&gt;notification payloads&lt;/span&gt;를 생성한다.&lt;br /&gt;See &lt;a href=&quot;https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/generating_a_remote_notification&quot;&gt;Generating a remote notification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;HTTP/2 and TLS ???를 사용해서 APNs에 연결하는 것을 관리한다.&lt;br /&gt;See &lt;a href=&quot;https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/sending_notification_requests_to_apns&quot;&gt;Sending Notification Requests to APNs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;payload를 포함한 POST request&lt;/span&gt;를 생성하고, request를 HTTP/2 연결을 통해 전송한다.&lt;br /&gt;See &lt;a href=&quot;https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/sending_notification_requests_to_apns&quot;&gt;Sending Notification Requests to APNs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;token-based authentication을 위해 정기적으로 token을 재생성한다.&lt;br /&gt;See&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/establishing_a_token-based_connection_to_apns&quot;&gt;Establishing a Token-Based Connection to APNs&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;Establish&amp;nbsp;a&amp;nbsp;Trusted&amp;nbsp;Connection&amp;nbsp;to&amp;nbsp;APNs&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;provider server와 APNs 간의 소통은 secure connection 하에서 이루어져야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;따라서 connection을 생성하려면 AAA Certificate Services root certificate이 필요하다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;notification을 보내려면 provider server는 반드시 &lt;b&gt;token-based or certificate-based trust&lt;/b&gt; with APNs 둘 중 하나가 필요하다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #9d9d9d;&quot;&gt;-&amp;gt; 둘의 장단점은 나중에 더 찾아보자...&lt;/span&gt;&lt;/i&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;Understand&amp;nbsp;What&amp;nbsp;APNs&amp;nbsp;Provides&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;APNs를 사용하면 UX에 좋다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자 기기에 대해 인증된, 암호화된, 영구적인 IP 연결을 관리한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용자&amp;nbsp;기기가&amp;nbsp;offline&amp;nbsp;상태일&amp;nbsp;때&amp;nbsp;전달된&amp;nbsp;notification을&amp;nbsp;저장해뒀다가,&amp;nbsp;기기가&amp;nbsp;online이&amp;nbsp;되면&amp;nbsp;포워딩한다.&amp;nbsp;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;기기의 power가 낮거나 offline일 때 등 notification이 즉시 전달되지 않았다면, 동일한 bundleID에 대한&amp;nbsp;&lt;b&gt;notifications를&amp;nbsp;병합&lt;/b&gt;&amp;nbsp;(coalesce)&amp;nbsp;할&amp;nbsp;수&amp;nbsp;있다.&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;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Remote notification 관련 문서를 더 읽어보자..&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;/span&gt;Article - Registering Your App with APNs&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/usernotifications/registering_your_app_with_apns&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;(링크)&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요약 : APNs와 통신하고, 앱을 식별하는 device token을 받는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;b&gt;APNs는 사용자 기기의 주소를 알아야 한다.&lt;/b&gt; (&lt;span&gt;notification의 도착점)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;이 주소는 device/app에 대해 고유한 값인 &lt;span style=&quot;color: #ee2323;&quot;&gt;device token &lt;/span&gt;형태이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;앱을 실행 (launch)할 때 &lt;span style=&quot;color: #006dd7;&quot;&gt;앱&lt;/span&gt;이 &lt;span style=&quot;color: #006dd7;&quot;&gt;APNs&lt;/span&gt;와 통신해서 device token을 받아오면, &lt;span style=&quot;color: #006dd7;&quot;&gt;provider server&lt;/span&gt;에다가 전달하게 된다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;provider server는 notification을 전달할 때마다 이 token을 넣어서 보낸다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;Enable&amp;nbsp;the&amp;nbsp;Push&amp;nbsp;Notifications&amp;nbsp;Capability&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;Xcode의 Capabilities에다가 Push Notification을 enable해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #9d9d9d;&quot;&gt;개발자 계정에서 AppID에다가 push notification service도 enable 시켜야 한다.. 귀찮지만 인증서 필요..&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1108&quot; data-origin-height=&quot;254&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l04Kz/btrNAyJaxk7/a3ua6HXhhE6sYdo4hDy2I1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l04Kz/btrNAyJaxk7/a3ua6HXhhE6sYdo4hDy2I1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l04Kz/btrNAyJaxk7/a3ua6HXhhE6sYdo4hDy2I1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl04Kz%2FbtrNAyJaxk7%2Fa3ua6HXhhE6sYdo4hDy2I1%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;850&quot; height=&quot;195&quot; data-origin-width=&quot;1108&quot; data-origin-height=&quot;254&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;Register&amp;nbsp;Your&amp;nbsp;App&amp;nbsp;and&amp;nbsp;Retrieve&amp;nbsp;Your&amp;nbsp;App's&amp;nbsp;Device&amp;nbsp;Token&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #ee2323;&quot;&gt;APN에 앱을 등록하고, device token을 받는다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #333333;&quot;&gt;provider server는 이 token을 가지고 있어야 notification을 보낼 수 있다.&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 style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;앱을 실행 (launch)할 때마다&lt;/span&gt; &lt;span style=&quot;color: #333333;&quot;&gt;앱을 등록해서 device token을 받아와야 한다.&lt;/span&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt; -&amp;gt; 굳이 왜 매번 발급하는지 궁금하다면 이유는 아래에&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;이때 &lt;a href=&quot;https://developer.apple.com/documentation/uikit/uiapplication/1623078-registerforremotenotifications&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;registerForRemoteNotifications()&lt;/span&gt;&lt;/a&gt; 메서드를 활용해서 앱을 등록하고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;App Delegate의 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;a style=&quot;background-color: #dddddd;&quot; href=&quot;https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622958-application&quot;&gt;application(_:didRegisterForRemoteNotificationsWithDeviceToken:)&lt;/a&gt;&lt;/span&gt; &lt;span style=&quot;color: #333333;&quot;&gt;메서드를 통해 device token을 받아온다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;등록에 실패할 경우에 대비해서 &lt;a href=&quot;https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622962-application&quot;&gt;application(_:didFailToRegisterForRemoteNotificationsWithError:)&lt;/a&gt; 메서드를 구현한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1664813266552&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func application(_ application: UIApplication,
           didFinishLaunchingWithOptions launchOptions:
           [UIApplicationLaunchOptionsKey: Any]?) -&amp;gt; Bool {
   // Override point for customization after application launch.         
   UIApplication.shared.registerForRemoteNotifications()  // ✅ 앱을 APNs에 등록
   return true
}

func application(_ application: UIApplication,
            didRegisterForRemoteNotificationsWithDeviceToken 
                deviceToken: Data) {  // ✅ 앱이 등록되면 APNs가 device token을 보내줌
   self.sendDeviceTokenToServer(data: deviceToken)  // ✅ token을 provider server에 저장
}

func application(_ application: UIApplication,
            didFailToRegisterForRemoteNotificationsWithError  // ✅ 앱 등록에 실패했을 때 처리
                error: Error) {
   // Try again later.
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;*왜 APNs를 앱을 실행할 때마다 device token을 갱신할까?&lt;/span&gt; -&amp;gt; &lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;Important 항목에 답이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;256&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmFbSS/btrNxeEIZ1m/43g7oBhH9UrUrHhY9COUs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmFbSS/btrNxeEIZ1m/43g7oBhH9UrUrHhY9COUs1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmFbSS/btrNxeEIZ1m/43g7oBhH9UrUrHhY9COUs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmFbSS%2FbtrNxeEIZ1m%2F43g7oBhH9UrUrHhY9COUs1%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;1536&quot; height=&quot;256&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;256&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;로컬 저장소에 device token을 cache하면 안 된다. APNs는 사용자가 백업으로부터 장치를 복원할 때, 사용자가 앱을 새로운 기기에 설치할 때, 사용자가 OS를 재설치할 때 새로운 token을 발급한다. 매번 시스템에서 &lt;span&gt;token&lt;/span&gt;을 제공하도록 요청하면, &lt;span&gt;token이 항상 최신 상태임을 보장받을 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #ee2323;&quot;&gt;-&amp;gt; 결론 : APNs가 token을 자주 갱신하기 때문에 앱을 실행할 때마다 새로 받는 게 낫다는 뜻..&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;Forward&amp;nbsp;Tokens&amp;nbsp;to&amp;nbsp;Your&amp;nbsp;Provider&amp;nbsp;Server&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;token을&amp;nbsp;받으면&amp;nbsp;사용자&amp;nbsp;계정&amp;nbsp;정보와&amp;nbsp;연결시키고,&amp;nbsp;암호화시켜서&amp;nbsp;&lt;b&gt;provider&amp;nbsp;server&lt;/b&gt;로&amp;nbsp;보낸다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;provider&amp;nbsp;server는&amp;nbsp;notification을&amp;nbsp;보낼&amp;nbsp;때&amp;nbsp;기기&amp;nbsp;정보가&amp;nbsp;필요하다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;따라서 provider server에서는&amp;nbsp;&lt;span style=&quot;color: #ee2323;&quot;&gt;token을&amp;nbsp;사용자&amp;nbsp;계정&amp;nbsp;데이터에다가&amp;nbsp;함께&amp;nbsp;저장&lt;/span&gt;해야&amp;nbsp;한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;사용자가&amp;nbsp;&lt;b&gt;여러&amp;nbsp;개의&amp;nbsp;기기&lt;/b&gt;를&amp;nbsp;사용할&amp;nbsp;가능성이&amp;nbsp;있으므로&amp;nbsp;사용자&amp;nbsp;계정에&amp;nbsp;여러&amp;nbsp;개의&amp;nbsp;token이&amp;nbsp;저장될&amp;nbsp;수도&amp;nbsp;있다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;/span&gt;Article - Generating&amp;nbsp;a&amp;nbsp;remote&amp;nbsp;notification&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/generating_a_remote_notification&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;(링크)&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요약 : &lt;span style=&quot;color: #ee2323;&quot;&gt;JSON payload&lt;/span&gt;를 통해 사용자 기기에 notification을 보낸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;remote notification은 JSON payload의 형태로 사용자에게 전달된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;payload는&amp;nbsp;user&amp;nbsp;interaction&amp;nbsp;(alert/sound/badge)를&amp;nbsp;지정하고,&amp;nbsp;notification에&amp;nbsp;대응할&amp;nbsp;custom&amp;nbsp;데이터를&amp;nbsp;포함한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;910&quot; data-origin-height=&quot;264&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q5trx/btrNLBc9FD3/KbHCIHDFYBG9Jxi1mw2ywk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q5trx/btrNLBc9FD3/KbHCIHDFYBG9Jxi1mw2ywk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q5trx/btrNLBc9FD3/KbHCIHDFYBG9Jxi1mw2ywk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq5trx%2FbtrNLBc9FD3%2FKbHCIHDFYBG9Jxi1mw2ywk%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;770&quot; height=&quot;223&quot; data-origin-width=&quot;910&quot; data-origin-height=&quot;264&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;기본적인 payload는 Apple에서 정의한 key와 custom value로 구성되고,&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;custom key와 value를 추가할 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;APNs는 payload의 최대 용량을 제한하고 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Voice over Internet Protocol (VoIP) notifications : 5KB&lt;/li&gt;
&lt;li&gt;그 외 remote notification : &lt;span style=&quot;color: #ee2323;&quot;&gt;4KB&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;2943359&quot; data-ke-size=&quot;size20&quot;&gt;Create the JSON payload&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSON dictionary를 사용해서 payload를 지정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래처럼 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;aps key&lt;/span&gt;에 대한 value로 dictionary가 들어있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이 dictionary는 1개 이상의 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;Apple-defined key&lt;/span&gt;로 구성된다. (이 key에다가 &lt;b&gt;alert/sound/badge/silent&lt;/b&gt;를 넣는다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;silent를 사용하면 background에서 조용히 시스템이 notification을 처리하도록 할 수 있다.&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;아래의 payload는 사용자를 game에 초대하기 위해 &lt;span&gt;alert를 띄운다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;category key가 &lt;a href=&quot;https://developer.apple.com/documentation/usernotifications/unnotificationcategory&quot;&gt;UNNotificationCategory&lt;/a&gt;에 등록되어 있다면, 시스템은 alert에 대한&amp;nbsp;&lt;b&gt;action 버튼&lt;/b&gt;을 추가해준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그리고 사용자가 게임에 참가할 수 있도록 &lt;b&gt;custom key/value&lt;/b&gt; (gameID)를 추가했다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1664815102733&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
   &quot;aps&quot; : {
      &quot;alert&quot; : {
         &quot;title&quot; : &quot;Game Request&quot;,
         &quot;subtitle&quot; : &quot;Five Card Draw&quot;,
         &quot;body&quot; : &quot;Bob wants to play poker&quot;
      },
      &quot;category&quot; : &quot;GAME_INVITATION&quot;
   },
   &quot;gameID&quot; : &quot;12345678&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;이 부분은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;백엔드 서버&lt;/span&gt;에서 관리하기 때문에 iOS 개발자는 신경 쓸 필요가 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 payload는 badge, sound를 띄운다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sound 파일은 사용자 기기에 이미 존재하는 상태 (app bundle 또는 Library/Sounds 폴더)여야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 notification을 만들어낸 message를 식별할 수 있도록&amp;nbsp;&lt;b&gt;custom key/value &lt;/b&gt;(messageID)를 추가했다.&lt;/p&gt;
&lt;pre id=&quot;code_1664815116890&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
   &quot;aps&quot; : {
      &quot;badge&quot; : 9,
      &quot;sound&quot; : &quot;bingbong.aiff&quot;
   },
   &quot;messageID&quot; : &quot;ABCDEFGHIJ&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;2948651&quot; data-ke-size=&quot;size20&quot;&gt;Localize your alert messages&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;remote notification의 컨텐츠를 localize (지역화)하는 2개 방법이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;payload에 직접 localized strings를 포함시키는 방법&amp;nbsp;&lt;/b&gt;&lt;br /&gt;- provider server에서 사용자의 preferred&amp;nbsp;language를 추적해야 함&lt;br /&gt;- 사용자 기기에 저장된 preferred language 정보를 이용하면 되니까 서버가 몰라도 되는 거 아닌가???&lt;/li&gt;
&lt;li&gt;&lt;b&gt;app bundle에다가 localized message strings를 추가&lt;/b&gt;하고, 시스템이 어떤 문자열을 사용할지 선택하게 하는 방법&lt;br /&gt;- title-loc-key, subtitle-loc-key, loc-key 등의 payload keys를 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1664818113317&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
   &quot;aps&quot; : {
      &quot;alert&quot; : {
         &quot;loc-key&quot; : &quot;GAME_PLAY_REQUEST_FORMAT&quot;,
         &quot;loc-args&quot; : [ &quot;Shelly&quot;, &quot;Rick&quot;]
      }
   }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;2943360&quot; data-ke-size=&quot;size20&quot;&gt;Payload key reference&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본문 table을 통해 aps dictionary에 들어갈 수 있는 key/value 정보를 확인할 수 있다.&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;/span&gt;Article - Sending&amp;nbsp;Notification&amp;nbsp;Requests&amp;nbsp;to&amp;nbsp;APNs&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/sending_notification_requests_to_apns&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;(링크)&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요약 : payload 및 device token을 APNs에 전송한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;provider server는 &lt;b&gt;POST request&lt;/b&gt;를 구성해서 APNs에 보내야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;request에 포함시켜야 할 정보는 아래와 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JSON&amp;nbsp;payload&lt;/li&gt;
&lt;li&gt;device&amp;nbsp;token&lt;/li&gt;
&lt;li&gt;어떻게 notification을 전달할지를 지정하는 Request-header&amp;nbsp;fields&lt;/li&gt;
&lt;li&gt;token-based authentication을 위한 provider server의 현재 authentication&amp;nbsp;token&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;APNs가 request를 받으면, authentication token 또는 서버의 certificate을 통해 request를 validate한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;validate하다고 판단하면 &lt;b&gt;APNs는 device token을 활용해서 사용자 기기를 식별하고, payload를 기기에 보낸다.&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h4 id=&quot;2947606&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Establish a Connection to APNs&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;HTTP/2 and TLS 1.2를 사용해서 provider server를 development/production server 중 하나에 연결시킨다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;development server는 테스트용, production server는 상용앱 목적으로 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 id=&quot;2947607&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Create and Send a POST Request to APNs&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;HTTP/2 and TLS를 이용해서 &lt;b&gt;POST notification&lt;/b&gt;을 APNs에다가 보낸다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;POST notification을 만들려면 아래의 정보가 필요하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;device&amp;nbsp;token&lt;/li&gt;
&lt;li&gt;current&amp;nbsp;authentication&amp;nbsp;token&amp;nbsp;(only&amp;nbsp;if&amp;nbsp;you&amp;rsquo;re&amp;nbsp;using&amp;nbsp;token-based&amp;nbsp;authentication)&lt;/li&gt;
&lt;li&gt;JSON payload&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;&lt;span style=&quot;color: #333333;&quot;&gt;그리고&lt;span style=&quot;color: #ee2323;&quot;&gt; header fields&lt;/span&gt;를 추가해야 하는데, 본문 table을 참고하면 된다. (required = 필수사항)&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 style=&quot;color: #333333;&quot;&gt;APNs는 HPACK (header compression for HTTP/2)을 사용하도록 하는데, header key/value가 중복 저장되는 것을 막아준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;header를 특정 방식에 따라 encoding 해야 한다.&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 style=&quot;color: #333333;&quot;&gt;POST request 예시는 아래와 같다. (authentication token을 활용)&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1664821438781&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;HEADERS
  - END_STREAM
  + END_HEADERS
  :method = POST
  :scheme = https
  :path = /3/device/00fc13adff785122b4ad28809a3420982341241421348097878e577c991de8f0
  host = api.sandbox.push.apple.com
  authorization = bearer eyAia2lkIjogIjhZTDNHM1JSWDciIH0.eyAiaXNzIjogIkM4Nk5WOUpYM0QiLCAiaWF0I
		 jogIjE0NTkxNDM1ODA2NTAiIH0.MEYCIQDzqyahmH1rz1s-LFNkylXEa2lZ_aOCX4daxxTZkVEGzwIhALvkClnx5m5eAT6
		 Lxw7LZtEQcH6JENhJTMArwLf3sXwi
  apns-id = eabeae54-14a8-11e5-b60b-1697f925ec7b
  apns-push-type = alert
  apns-expiration = 0
  apns-priority = 10
  apns-topic = com.example.MyApp
DATA
  + END_STREAM
  { &quot;aps&quot; : { &quot;alert&quot; : &quot;Hello&quot; } }&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위와 마찬가지로 이 부분은 &lt;span style=&quot;color: #ee2323;&quot;&gt;백엔드 서버&lt;/span&gt;에서 관리하기 때문에 iOS 개발자는 신경 쓸 필요가 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;/span&gt;Article - Handling&amp;nbsp;Notification&amp;nbsp;Responses&amp;nbsp;from&amp;nbsp;APNs&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/handling_notification_responses_from_apns&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;(링크)&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요약 : APNs server가 반환해주는 status code에 대응한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;provider server가 APNs에게 POST request를 전달하면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 APNs는 provider server에게 response를 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;response는 &lt;b&gt;header&lt;/b&gt;를 포함하는데, 여기에는 &lt;b&gt;response status를 나타내는 fields&lt;/b&gt;가 들어있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;request에 성공했다면 response body는 비어있고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에러가 발생했다면 body에 에러 정보를 담고 있는 JSON dictionary가 들어있다.&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;본문 표를 통해 header response의 의미를 파악할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;request에 성공했다면 status code는 200이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;payload 용량이 너무 큰 경우는 413이다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;request에 성공한 경우 response 예시는 아래와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1664822754206&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;HEADERS
  + END_STREAM
  + END_HEADERS
  apns-id = eabeae54-14a8-11e5-b60b-1697f925ec7b
  :status = 200&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;/span&gt;Article -&lt;span&gt; Handling&amp;nbsp;Notifications&amp;nbsp;and&amp;nbsp;Notification-Related&amp;nbsp;Actions&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/usernotifications/handling_notifications_and_notification-related_actions&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;(링크)&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요약 : 사용자 interation에 대응한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 notification을 확인하고 interaction한 것에 대해 앱이 대응할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대응할 수 있는 사용자 interaction은 아래를 말한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시스템의 notification interface에 대한 user-selected action&lt;/li&gt;
&lt;li&gt;앱이 foreground에서 실행 중일 때 도착한 notification&lt;/li&gt;
&lt;li&gt;silent notification&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;2942080&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Handle User-Selected Actions&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;actionable notification은 아래처럼 1개 이상의 &lt;b&gt;버튼&lt;/b&gt;을 제공한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;사용자가 특정 버튼을 탭하면, &lt;b&gt;선택된 &lt;span style=&quot;color: #ee2323;&quot;&gt;action&lt;/span&gt;이 앱에게 전달된다.&lt;/b&gt; 이때 app은 foreground에 올라오지 않는다.&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;1206&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/boDNBr/btrNB57PRad/YvkBqeMPjHabZAV5NnhuF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boDNBr/btrNB57PRad/YvkBqeMPjHabZAV5NnhuF1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boDNBr/btrNB57PRad/YvkBqeMPjHabZAV5NnhuF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FboDNBr%2FbtrNB57PRad%2FYvkBqeMPjHabZAV5NnhuF1%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;280&quot; height=&quot;563&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;1206&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;선택된 action을 처리하려면 UNUserNotificationCenter &lt;b&gt;delegate&lt;/b&gt;가 필요하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 특정 action을 선택하면, 시스템은 앱을&lt;span style=&quot;color: #ee2323;&quot;&gt; background에서 실행&lt;/span&gt;하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;delegate의 userNotificationCenter(_:didReceive:withCompletionHandler:)&lt;/span&gt; 메서드를 호출한다.&lt;/p&gt;
&lt;pre id=&quot;code_1664824132271&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func userNotificationCenter(_ center: UNUserNotificationCenter,
            didReceive response: UNNotificationResponse,
            withCompletionHandler completionHandler: 
               @escaping () -&amp;gt; Void) {
   // ✅ Get the meeting ID from the original notification.
   let userInfo = response.notification.request.content.userInfo
        
   if response.notification.request.content.categoryIdentifier ==
              &quot;MEETING_INVITATION&quot; {
      // Retrieve the meeting details.
      let meetingID = userInfo[&quot;MEETING_ID&quot;] as! String
      let userID = userInfo[&quot;USER_ID&quot;] as! String
            
      switch response.actionIdentifier {  // ✅ 사용자가 선택한 action에 따라 분기처리
      case &quot;ACCEPT_ACTION&quot;:
         sharedMeetingManager.acceptMeeting(user: userID, 
               meetingID: meetingID)
      case &quot;DECLINE_ACTION&quot;:
         sharedMeetingManager.declineMeeting(user: userID, 
               meetingID: meetingID)
      case UNNotificationDefaultActionIdentifier,  // ✅ 사용자가 앱을 실행하거나 notification을 dismiss했을 때
           UNNotificationDismissActionIdentifier:
         // Queue meeting-related notifications for later
         //  if the user does not act.
         sharedMeetingManager.queueMeetingForDelivery(user: userID,
               meetingID: meetingID)
      default:
         break
      }
   }
   else {
      // Handle other notification types...
   }
        
   completionHandler()  // 처리가 끝났음을 시스템에게 알리기 위해 호출함
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;2942082&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Handle Notifications While Your App Runs in the Foreground&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;notification이 전달됐을 때 앱이 &lt;b&gt;foreground&lt;/b&gt; 상태라면, 시스템은 notification을 직접 앱에게 전달한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;notification을 받으면 &lt;b&gt;payload&lt;/b&gt;를 통해 원하는 작업을 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 payload에 들어있는 특정 데이터를 사용해서 앱의 interface를 업데이트할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음&amp;nbsp;예약된&amp;nbsp;alert를&amp;nbsp;숨기거나&amp;nbsp;수정할&amp;nbsp;수&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;앱이 foreground 상태일 때 notification이 도착하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템은 &lt;span&gt;UNUserNotificationCenter&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;delegate의&lt;/b&gt; &lt;a href=&quot;https://developer.apple.com/documentation/usernotifications/unusernotificationcenterdelegate/1649518-usernotificationcenter&quot;&gt;userNotificationCenter(_:willPresent:withCompletionHandler:)&lt;/a&gt; 메서드를 호출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 메서드를 활용해서 notification을 처리하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1664824593559&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func userNotificationCenter(_ center: UNUserNotificationCenter,
         willPresent notification: UNNotification,
         withCompletionHandler completionHandler: 
            @escaping (UNNotificationPresentationOptions) -&amp;gt; Void) {
   if notification.request.content.categoryIdentifier == 
            &quot;MEETING_INVITATION&quot; {
      // Retrieve the meeting details.
      let meetingID = notification.request.content.
                       userInfo[&quot;MEETING_ID&quot;] as! String
      let userID = notification.request.content.
                       userInfo[&quot;USER_ID&quot;] as! String
            
      // Add the meeting to the queue.
      // ✅ 앱의 interface를 업데이트하는 custom method
      sharedMeetingManager.queueMeetingForDelivery(user: userID,
            meetingID: meetingID)

      // ✅ Play a sound to let the user know about the invitation.
      completionHandler(.sound)
      return
   }
   else {
      // Handle other notification types...
   }

   // ✅ Don't alert the user for other types.
   completionHandler(UNNotificationPresentationOptions(rawValue: 0))
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;/span&gt;Article -&lt;span&gt;&lt;span&gt; Declaring Your Actionable Notification Types&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/usernotifications/declaring_your_actionable_notification_types&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;(링크)&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요약 : notification&amp;nbsp;interface에다가&amp;nbsp;action&amp;nbsp;버튼을&amp;nbsp;추가한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;actionable notification을 사용하려면 아래 작업이 필요하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;앱 실행 시점에 1개 이상의 notification categories를 선언한다.&lt;/li&gt;
&lt;li&gt;notification categories에 action을 할당한다.&lt;/li&gt;
&lt;li&gt;등록한 action을 처리한다.&lt;/li&gt;
&lt;li&gt;notification을 생성할 때 payload에다가 category&amp;nbsp;identifiers를 할당한다.&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;아래는 ation을 등록하는 예시 코드이다.&lt;/p&gt;
&lt;pre id=&quot;code_1664824998047&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Define the custom actions.
let acceptAction = UNNotificationAction(identifier: &quot;ACCEPT_ACTION&quot;,
      title: &quot;Accept&quot;, 
      options: [])
let declineAction = UNNotificationAction(identifier: &quot;DECLINE_ACTION&quot;,
      title: &quot;Decline&quot;, 
      options: [])
// Define the notification type
let meetingInviteCategory = 
      UNNotificationCategory(identifier: &quot;MEETING_INVITATION&quot;,
      actions: [acceptAction, declineAction], 
      intentIdentifiers: [], 
      hiddenPreviewsBodyPlaceholder: &quot;&quot;,
      options: .customDismissAction)
// Register the notification type.
let notificationCenter = UNUserNotificationCenter.current()
notificationCenter.setNotificationCategories([meetingInviteCategory])&lt;/code&gt;&lt;/pre&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;생각보다 공식문서 양이 많았는데, 비슷한 내용이 반복되기 때문에 그렇게 오래 걸리지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 포스팅에서 &lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;상용앱에서 대부분 활용하는 FCM (Firebase Cloud Messaging) 등의 중계 서비스를 포함한&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Notification 관련 4가지 주체에 대해 정리하면서, 예제 코드를 작성해보자.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;기존 포스팅 - &lt;a href=&quot;https://applecider2020.tistory.com/52&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[HIG]&amp;nbsp;Notifications&amp;nbsp;-&amp;nbsp;title,&amp;nbsp;content,&amp;nbsp;actions&amp;nbsp;설정&amp;nbsp;시&amp;nbsp;고려사항&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple Developer Documentation &amp;gt; &lt;a href=&quot;https://developer.apple.com/documentation/usernotifications&quot;&gt;Framework -User&amp;nbsp;Notifications&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple Design &amp;gt; Human Interface Guidelines &amp;gt; System experiences &amp;gt; &lt;a href=&quot;https://developer.apple.com/design/human-interface-guidelines/components/system-experiences/notifications/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Notifications&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;YouTube &amp;gt; iOS Academy -&amp;nbsp;&lt;a href=&quot;https://www.youtube.com/watch?v=UJree24HWx0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Push&amp;nbsp;Notifications&amp;nbsp;Tutorial&amp;nbsp;(2022)&lt;/a&gt;&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>iOS/영문 공식문서 뜯어보기-iOS</category>
      <category>APNS</category>
      <category>push notification</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/53</guid>
      <comments>https://applecider2020.tistory.com/53#entry53comment</comments>
      <pubDate>Mon, 17 Oct 2022 00:10:25 +0900</pubDate>
    </item>
    <item>
      <title>[HIG] Notifications - title, content, actions 설정 시 고려사항</title>
      <link>https://applecider2020.tistory.com/52</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;APNs (Apple Push Notification service)&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;를 스터디하다가 오랜만에&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt; HIG 문서&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;를 읽게 됐다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;참고 - &lt;a href=&quot;https://developer.apple.com/design/human-interface-guidelines/components/system-experiences/notifications/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Notifications HIG 문서&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;요약&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Notification은 사용자에게 적당한 시간에 (timely), 중요한 정보를, 한눈에 (at a glance) 전달한다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;사용자에게 notification을 보내기 전에 반드시 &lt;b&gt;사용자의 동의 (consent)&lt;/b&gt;를 얻어야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;사용자가 동의한 이후, 대부분의 사용자는 &lt;b&gt;설정 앱&lt;/b&gt;을 통해 &lt;span&gt;notification 스타일과 &lt;span&gt;notification의 중요도에 따른&amp;nbsp;&lt;/span&gt;알림 시간을 정한다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8gagB/btrNwDKwoZN/OIhh9fhNaugKIWo0oogH5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8gagB/btrNwDKwoZN/OIhh9fhNaugKIWo0oogH5K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8gagB/btrNwDKwoZN/OIhh9fhNaugKIWo0oogH5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8gagB%2FbtrNwDKwoZN%2FOIhh9fhNaugKIWo0oogH5K%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;300&quot; height=&quot;169&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;a href=&quot;https://developer.apple.com/design/human-interface-guidelines/components/system-experiences/notifications/#anatomy&quot;&gt;Anatomy&lt;/a&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;플랫폼에 따라 &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;notification의 다양한 스타일을 활용할 수 있다.&lt;/span&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;b&gt;잠금 화면 (lock screen), 홈 화면,&lt;/b&gt; 데스크탑&lt;/li&gt;
&lt;li&gt;배지 : 앱 아이콘&lt;/li&gt;
&lt;li&gt;알림 센터 (notification center)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;a href=&quot;https://developer.apple.com/design/human-interface-guidelines/components/system-experiences/notifications/#best-practices&quot;&gt;Best practices&lt;/a&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최대한 간략하게 notification을 구성한다.&lt;/li&gt;
&lt;li&gt;사용자가 반응하지 않더라도 동일한 항목에 대해 여러 개의 notification을 보내지 않는다.&lt;/li&gt;
&lt;li&gt;✅ &lt;b&gt;사용자에게 특정 작업을 수행하도록 할 목적으로 notification을 사용하지 않는다.&lt;/b&gt;&lt;br /&gt;&lt;span&gt;- 앱을 열어서 할 작업을 notification에 나타내면,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;notification이 꺼졌을 때 사용자가 내용을 잊어버리기 쉽다.&lt;/b&gt;&lt;br /&gt;- 사용자가 앱을 열지 않고도 할 수 있는 간단한 작업이라면 &lt;b&gt;notification actions&lt;/b&gt; 기능을 활용한다.&lt;/li&gt;
&lt;li&gt;에러 메시지를 나타내려면 notification이 아니라 alert를 사용한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;✅ 앱이 foreground에 있을 때는 notification이 튀지 않도록 한다.&lt;/b&gt;&lt;br /&gt;- 앱이 스크린에 띄워진 상태에서는 notification이 나타나지 않지만, 앱은 계속해서 데이터를 받는다. (?)&lt;br /&gt;- ex. 사용자가 메일함을 스크린에 띄운 상태에서 새로운 메일이 도착했다면, &lt;br /&gt;&amp;nbsp; &amp;nbsp;&lt;span style=&quot;color: #ee2323;&quot;&gt;notification을 띄우는 것이 불필요하고 몰입을 방해할 수 있으므로&lt;/span&gt; 단순히 읽지 않은 메일 목록에 추가되도록 하는 게 좋다.&lt;/li&gt;
&lt;li&gt;사용자 개인정보를 notification에 띄우지 않는다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;a href=&quot;https://developer.apple.com/design/human-interface-guidelines/components/system-experiences/notifications/#content&quot;&gt;Content&lt;/a&gt;&lt;span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;notification의&lt;b&gt; title&lt;/b&gt;을 설정하면, 시스템이 해당 내용을 가장 눈에 잘 띄는 위치인 &lt;b&gt;맨 윗 줄&lt;/b&gt;에 나타낸다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;직접적인 커뮤니케이션 기능과 관련된 notification인 경우, 시스템이 자동으로 &lt;b&gt;발신자의 이름&lt;/b&gt;을 title area에 나타낸다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;커뮤니케이션과 관련 없는 notification인 경우, title이 없다면 시스템이 &lt;b&gt;앱의 이름&lt;/b&gt;을 나타낸다.&lt;/span&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;b&gt;title&lt;/b&gt;은 짧은 것이 좋다.&lt;br /&gt;- 커뮤니케이션과 관련 없고, 세부적인 title을 제공할 수 없는 경우 (ex. New Document) 차라리 &lt;b&gt;앱 이름&lt;/b&gt;을 title로 두는 게 낫다.&amp;nbsp;&lt;br /&gt;- title-style&amp;nbsp;capitalization을 사용하고, 마침표를 찍지 않는다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;content&lt;/b&gt;는 간략하고 가독성 있게 작성한다.&lt;br /&gt;- complete sentence를 사용한다.&lt;br /&gt;- 메시지를 truncate (잘림 처리)할 필요가 없다. 필요하면 시스템이 알아서 해준다.&lt;/li&gt;
&lt;li&gt;&lt;span&gt;✅&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;notification &lt;b&gt;미리보기&lt;/b&gt;가 불가할 때 나타낼 &lt;b&gt;설명 텍스트&lt;/b&gt;를 제공한다.&lt;br /&gt;- 사용자가 설정에서 모든 앱에 대한 미리보기를 감추도록 할 수 있다. 이때 시스템은 notification에 앱 아이콘과 default title만 나타낸다.&amp;nbsp;&lt;br /&gt;- 사용자가 notification 전체 내용을 볼지 말지 결정할 수 있도록 간결한 &lt;b&gt;body text&lt;/b&gt;를 작성한다.&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp;(ex. &amp;ldquo;Friend&amp;nbsp;request,&amp;rdquo;&amp;nbsp;&amp;ldquo;New&amp;nbsp;comment,&amp;rdquo;&amp;nbsp;&amp;ldquo;Reminder,&amp;rdquo; &amp;nbsp;&amp;ldquo;Shipment&amp;rdquo;)&lt;br /&gt;- &lt;span style=&quot;background-color: #dddddd;&quot;&gt;hiddenPreviewsBodyPlaceholder&lt;/span&gt; 프로퍼티에다가 설정하면 된다.&lt;/li&gt;
&lt;li&gt;시스템이 알아서 보여주는 앱 아이콘, 앱 이름을 포함시키지 않는다.&lt;/li&gt;
&lt;li&gt;sound를 제공할지 고려한다.&lt;br /&gt;- &lt;b&gt;앱마다 고유의 custom sound를 설정 가능&lt;/b&gt;하므로 사용자는 화면을 보지 않고도 특정 앱의 notification을 인지할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;a href=&quot;https://developer.apple.com/design/human-interface-guidelines/components/system-experiences/notifications/#notification-actions&quot;&gt;Notification actions&lt;/a&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;notification에는 custom detail view를 나타내는 기능이 있는데,&amp;nbsp;&lt;br /&gt;&lt;b&gt;최대 4개까지 버튼을 통해 &lt;span style=&quot;color: #ee2323;&quot;&gt;사&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;용자가 앱을 열지 않고도&lt;/span&gt; 특정 action을 수행한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 Calendar event notification은 이벤트 알람을 몇 분간 연기할 수 있는 &lt;b&gt;Snooze 버튼&lt;/b&gt;을 제공한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;notification의 context에 맞는 actions를 제공한다.&lt;br /&gt;- 일반적이고, 시간을 절약할 수 있는 작업을 설정하면 좋다.&lt;br /&gt;- 버튼에는 title-case의 짧은 텍스트를 사용한다.&lt;/li&gt;
&lt;li&gt;notification을 탭하면 앱이 열리기 때문에 앱을 여는 목적의 action button은 만들지 않는다.&lt;/li&gt;
&lt;li&gt;&lt;span&gt;✅&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;destructive action은 피한다.&lt;br /&gt;- 사용자가 의도치 않게 선택할 수 있다.&lt;/li&gt;
&lt;li&gt;action 버튼에 가독성 좋은 &lt;b&gt;interface 아이콘&lt;/b&gt;을 제공한다.&amp;nbsp;&lt;br /&gt;- 이 아이콘은 action title의 trailing side에 표시된다.&lt;br /&gt;- SF Symbols 참고&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;a href=&quot;https://developer.apple.com/design/human-interface-guidelines/components/system-experiences/notifications/#badging&quot;&gt;Badging&lt;/a&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배지는 사용자가 &lt;b&gt;읽지 않은 notification의 개수&lt;/b&gt;를 나타내기 위해 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱 아이콘에 숫자를 나타낸다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;notification과 관련 없는 숫자를 나타내지 않는다.&lt;/li&gt;
&lt;li&gt;사용자는 배지를 사용하지 않도록 설정할 수 있다. &lt;br /&gt;- 중요한 정보는 사용자가 앱을 열자마자 확인 가능하도록 해야 한다.&lt;/li&gt;
&lt;li&gt;배지를 지속 업데이트한다.&lt;br /&gt;- 사용자가 확인한 notification을 반영해서 배지의 숫자를 업데이트한다.&lt;br /&gt;- ✅ 배지 개수를 0으로 설정하면, 알림 센터의 notification이 모두 사라진다.&lt;/li&gt;
&lt;li&gt;custom 이미지 등으로 배지의 형태나 동작을 모방하는 것을 지양한다.&lt;br /&gt;- 설정에서 배지를 사용하지 않도록 설정했는데, 배지가 보이면 안 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;a href=&quot;https://developer.apple.com/design/human-interface-guidelines/components/system-experiences/notifications/#platform-considerations&quot;&gt;Platform considerations&lt;/a&gt;&amp;nbsp;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 플랫폼에 따라 고려할 사항이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구체적인 내용은 일단 iPhone 위주로 보고 있으므로 패스&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;WatchOS : notification을 2개 stage로 구분한다. (short/long look)&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple Developer Documentation &amp;gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://developer.apple.com/documentation/usernotifications&quot;&gt;Framework -User&amp;nbsp;Notifications&lt;/a&gt;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple Design &amp;gt; Human Interface Guidelines &amp;gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;System experiences &amp;gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://developer.apple.com/design/human-interface-guidelines/components/system-experiences/notifications/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Notifications&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>iOS/영문 공식문서 뜯어보기-iOS</category>
      <category>HIG</category>
      <category>Human Interface Guidelines</category>
      <category>notifications</category>
      <category>System experiences</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/52</guid>
      <comments>https://applecider2020.tistory.com/52#entry52comment</comments>
      <pubDate>Mon, 3 Oct 2022 00:54:21 +0900</pubDate>
    </item>
    <item>
      <title>[iOS] GitHub Action으로 Xcode 테스트 자동화하기 - CI/CD</title>
      <link>https://applecider2020.tistory.com/50</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;개발자가 설정한 &lt;b&gt;특정 상황이 발생하면 자동으로 실행할 동작을 미리 정해두는 기능&lt;/b&gt;이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예를 들어 &lt;b&gt;특정 branch에 merge 또는 PR을 요청했을 때 테스트가 실행&lt;/b&gt;되도록 하는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Git Flow 기능과 함께 사용하면 더욱 유용하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;*Git Flow 설명은 지난 포스트 참고 -&lt;/span&gt; &lt;a href=&quot;https://applecider2020.tistory.com/36&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Git] Git Flow로 브랜치 관리하기&lt;/a&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이렇게 테스트 코드를 실행하거나, 새로운 버전을 release 하기 전에 TestFlight를 실행할 수 있는데&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이러한 자동화 기능을 &lt;b&gt;CI/CD&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;CI/CD를 위한 툴은 Jenkins, &lt;span style=&quot;color: #666666;&quot;&gt;fastlane, Buildkite 등&lt;/span&gt; 다양한데,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;오늘은&lt;b&gt; GitHub Action&lt;/b&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;CI/CD란?&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;CI &lt;/b&gt;: &lt;b&gt;Continuous&lt;/b&gt; &lt;b&gt;Integration&lt;/b&gt; (지속적 통합)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;CD&lt;/b&gt; : &lt;b&gt;Continuous&lt;/b&gt; &lt;b&gt;Deployment&lt;/b&gt; (지속적&amp;nbsp;배포) 또는 지속적인&amp;nbsp;서비스&amp;nbsp;제공(Continuous&amp;nbsp;Delivery)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;CI&lt;/b&gt;는 &lt;span style=&quot;color: #ee2323;&quot;&gt;빌드/테스트 자동화 과정&lt;/span&gt;이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;CD&lt;/b&gt;는 &lt;span style=&quot;color: #ee2323;&quot;&gt;배포 자동화 과정&lt;/span&gt;이다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;GitHub Action이란?&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Git에 코드를 올리는 것만으로 &lt;b&gt;빌드/테스트, 배포를 자동으로 실행&lt;/b&gt;하는 기능이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;GitHub의 &lt;a href=&quot;https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Understanding&amp;nbsp;GitHub&amp;nbsp;Actions&lt;/a&gt; 페이지에 친절히 설명되어 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;448&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bom0bK/btrKGyFvgyC/uUkQyn876FIT0sanhVIHe1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bom0bK/btrKGyFvgyC/uUkQyn876FIT0sanhVIHe1/img.png&quot; data-alt=&quot;The components of GitHub Actions&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bom0bK/btrKGyFvgyC/uUkQyn876FIT0sanhVIHe1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbom0bK%2FbtrKGyFvgyC%2FuUkQyn876FIT0sanhVIHe1%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;730&quot; height=&quot;255&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;448&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;The components of GitHub Actions&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;Events&lt;/b&gt; : 특정 상황이 발생한다면 (ex. 특정 branch에 Push 했을 때)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;WorkFlows&lt;/b&gt; : 이 작업들을 실행시키겠다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;Jobs&lt;/b&gt; : WorkFlows에 명시된 &lt;b&gt;1개의 작업&lt;/b&gt;이다. &amp;nbsp;(ex. Unit Test를 실행)&lt;br /&gt;- 여러 개의 Jobs는 병렬적 (동시 다발적)으로 실행된다.&lt;br /&gt;- Job 내부에서 순차적으로 실행할 내용을 &lt;b&gt;Step&lt;/b&gt;으로 구분한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;Actions&lt;/b&gt; : 자주 사용하는 명령을 Action에 정의해뒀다가 Step에서 사용할 수 있다. (재사용성 높음)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;Runners&lt;/b&gt; : Job을 실행하는 주체이다. (VM 개념)&lt;br /&gt;- 각각의 Job은 독립적인 Runner를 가진다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;*전반적인 설명은&amp;nbsp;&lt;a href=&quot;https://docs.github.com/en/actions&quot;&gt;GitHub의 소개 영상&lt;/a&gt;을 참고&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;*Event, Workflows, Jobs, Actions, Runners에 대한 자세한 설명은&amp;nbsp;&lt;a href=&quot;https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions&quot;&gt;GitHub 링크&lt;/a&gt;를 참고&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;GitHub Action 사용하기 - 테스트 코드 자동 실행&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이제 &lt;b&gt;yml 파일&lt;/b&gt;을 추가해서 CI를 구현해보자.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;먼저 GitHub의 &lt;b&gt;Actions 탭&lt;/b&gt;에서 &lt;b&gt;Swift&lt;/b&gt; 항목의 Actions를 생성한다. 아래 캡쳐 참고&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(Swift Package를 빌드/테스트해야 하기 때문)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;520&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/coEnTd/btrKFQmiR4P/r7AVB0HJhyJFy6oNgkmOLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/coEnTd/btrKFQmiR4P/r7AVB0HJhyJFy6oNgkmOLK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/coEnTd/btrKFQmiR4P/r7AVB0HJhyJFy6oNgkmOLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcoEnTd%2FbtrKFQmiR4P%2Fr7AVB0HJhyJFy6oNgkmOLK%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;800&quot; height=&quot;443&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;520&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이제 아래처럼 &lt;b&gt;yml 파일&lt;/b&gt;에 원하는 자동화 조건을 작성할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;*우측의 &lt;b&gt;MarketPlace&lt;/b&gt;에 xcodebuild 등 명령어를 검색하면 자세한 사용 방법이 나온다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;yml에 작성한 코드를 살펴보자.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2368&quot; data-origin-height=&quot;1076&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/38aoj/btrKHubg3mV/E0B1R0R4sLTq2z5cqtKyHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/38aoj/btrKHubg3mV/E0B1R0R4sLTq2z5cqtKyHK/img.png&quot; data-alt=&quot;yml 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/38aoj/btrKHubg3mV/E0B1R0R4sLTq2z5cqtKyHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F38aoj%2FbtrKHubg3mV%2FE0B1R0R4sLTq2z5cqtKyHK%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;860&quot; height=&quot;391&quot; data-origin-width=&quot;2368&quot; data-origin-height=&quot;1076&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;yml 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1661585176144&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Workflow 이름
name: Run Test

// ✅ 트리거 조건 (push 하거나 PR 하면 하단의 jobs를 실행하겠다는 뜻)
on:
  push:
    branches: [ develop ]  // develop branch로 push한 상황
  pull_request:
    branches: [ develop ]  // develop branch로 PR 생성한 상황

// ✅ 트리거 발생 시 실행할 작업들 
jobs:
  // ✅ 이번 예시에서는 &quot;build&quot;라는 1개 작업만 진행
  build:  

    runs-on: macos-latest  // iOS 플랫폼에서 실행한다는 뜻

    // ✅ job 내부에서 순차적으로 실행할 내용
    steps:
    - uses: actions/checkout@v3  // repository에 체크아웃해서 job이 접근 가능하게 한다는 뜻 (Github Actions에서 미리 정의한 명령)
    - name: Build Xcode  // 실행할 작업 이름
      run: |  // 여러가지 명령어를 사용할 때 &quot;|&quot; 입력
        xcodebuild clean test -project WhatWeEat.xcodeproj \
        -scheme WhatWeEat \
        -destination 'platform=iOS Simulator,name=iPhone 13,OS=latest'
        // Xcode로 특정 환경에서 clean 및 test를 진행하겠다는 뜻
        // ❗-project WhatWeEat/WhatWeEat.xcodeproj를 쓰면 경로를 못찾는 문제 발생 가능
        // ❗-destination '...,OS=14.1'를 쓰면 OS 버전이 안맞는 문제 발생 가능&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;on&lt;/b&gt;에서 트리거 조건을 설정하고,&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;jobs&lt;/b&gt;에서 트리거 발생 시 실행할 작업들을 설정하고,&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;steps&lt;/b&gt;에서 job에서 실행할 일을 설정한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이 예시에서는 &lt;b&gt;develop branch로 merge하거나 PR을 생성하는 시점에 &lt;/b&gt;&lt;b&gt;테스트 코드를 실행하도록 자동화했다.&lt;br /&gt;&lt;/b&gt;(이외에도 새로운 Issue가 생성됐을 때, 특정 Label이 추가됐을 때 등의 시점으로도 설정 가능하다.)&lt;b&gt;&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;❗로 표시한 부분에 주의하자..&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(&lt;s&gt;에러를 해결한&lt;/s&gt; 삽질 과정은 아래에 기록했다.)&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;uses 키워드&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;steps의&amp;nbsp; uses&amp;nbsp;키워드 를 통해&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;GitHub Action에서 미리 정의한 명령을 가져와서 편리하게 사용할 수 있다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;위 코드의&amp;nbsp;&lt;b&gt;actions/checkout@v3&lt;/b&gt; 명령은 뭘 의미할까?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&quot;repository에 체크아웃해서 job이 접근 가능하게 한다&quot;는 뜻이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #9d9d9d;&quot;&gt;*체크아웃 개념이 궁금하다면 &lt;a href=&quot;https://www.daleseo.com/github-actions-checkout/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이 링크&lt;/a&gt;를 참고&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;run&amp;nbsp;키워드&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;코드 하단의 run 키워드에는 이런 내용이 들어있다.&lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;-project&lt;/b&gt; 플래그 : test 할 프로젝트이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;-scheme&lt;/b&gt; 플래그 : 빌드할 scheme 이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;-destination&lt;/b&gt; 플래그 : 빌드 시 사용할 플랫폼, 기기, iOS 버전이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;CocoaPods을 사용했다면&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;만약 SPM 대신 &lt;b&gt;CocoaPods&lt;/b&gt;을 사용했다면 에러가 발생한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;pod install, project 대신 workspace로 실행해야 하기 때문이다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;따라서 run 부분을 아래 코드로 바꿔야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1661586413995&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// CocoaPods 사용 시
run: |
  pod install --repo-update --clean-install --project-directory=HappyChuseok/
  xcodebuild clean test -workspace HappyChuseok/HappyChuseok.xcworkspace -scheme HappyChuseok -destination 'platform=iOS Simulator,name=iPhone 11 Pro,OS=latest'
        
// 참고 - SPM 사용 시
run: | 
  xcodebuild clean test -project WhatWeEat/WhatWeEat.xcodeproj -scheme WhatWeEat -destination 'platform=iOS Simulator,name=iPhone 13 Pro,OS=14.1'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이외에도 원하는&amp;nbsp;&lt;b&gt;swift-version&lt;/b&gt;을 명시하는 것도 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;*GitHub의&amp;nbsp;&lt;a href=&quot;https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-swift&quot;&gt;Building and testing Swift&lt;/a&gt;&amp;nbsp;페이지를 참고&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Workflow 작동 확인하기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아래처럼 yml 파일을 추가하면서 commit하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;808&quot; data-origin-height=&quot;684&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/daaoSL/btrKF1OGaYD/IrUH1LMaLnJtzxjfHEh9UK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/daaoSL/btrKF1OGaYD/IrUH1LMaLnJtzxjfHEh9UK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/daaoSL/btrKF1OGaYD/IrUH1LMaLnJtzxjfHEh9UK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdaaoSL%2FbtrKF1OGaYD%2FIrUH1LMaLnJtzxjfHEh9UK%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;430&quot; height=&quot;364&quot; data-origin-width=&quot;808&quot; data-origin-height=&quot;684&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Actions 탭에 들어갔는데 아무 일도 일어나지 않았다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아래처럼 Run Test라는 WorkFlow는 추가됐지만, 아직 작동 (Run)되지 않은 것을 볼 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;952&quot; data-origin-height=&quot;504&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kQBii/btrKGoCVnAK/2nciqZzannYrae4VJ6nQHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kQBii/btrKGoCVnAK/2nciqZzannYrae4VJ6nQHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kQBii/btrKGoCVnAK/2nciqZzannYrae4VJ6nQHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkQBii%2FbtrKGoCVnAK%2F2nciqZzannYrae4VJ6nQHK%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;800&quot; height=&quot;424&quot; data-origin-width=&quot;952&quot; data-origin-height=&quot;504&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;원인은&amp;nbsp;트리거 조건이 발생하지 않았기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(develop branch에 Push/PR하는 게 트리거 조건이었는데, 위에서 master branch에 커밋했음)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그리고 생각해보니 Unit Test를 추가하지 않았다;&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;다시 Unit Test를 추가하고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;새로운 branch를 생성한 뒤 develop branch로 PR을 생성했더니 아래처럼 실패 메시지가 생겼다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;224&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEHJDM/btrKHbpx8RC/UbtUAkKdATM0tWe1iQCSTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEHJDM/btrKHbpx8RC/UbtUAkKdATM0tWe1iQCSTK/img.png&quot; data-alt=&quot;PR 생성 후 하단에 뜬 메시지 (실패)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEHJDM/btrKHbpx8RC/UbtUAkKdATM0tWe1iQCSTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEHJDM%2FbtrKHbpx8RC%2FUbtUAkKdATM0tWe1iQCSTK%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;800&quot; height=&quot;243&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;224&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;PR 생성 후 하단에 뜬 메시지 (실패)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아래처럼 Actions 탭에서 상세내용을 확인 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1059&quot; data-origin-height=&quot;266&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cVsz60/btrKHvH5mqc/3sAfxs4Xp1LvH88rKisxE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cVsz60/btrKHvH5mqc/3sAfxs4Xp1LvH88rKisxE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cVsz60/btrKHvH5mqc/3sAfxs4Xp1LvH88rKisxE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcVsz60%2FbtrKHvH5mqc%2F3sAfxs4Xp1LvH88rKisxE1%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;800&quot; height=&quot;201&quot; data-origin-width=&quot;1059&quot; data-origin-height=&quot;266&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1087&quot; data-origin-height=&quot;520&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EyJVb/btrKF6Cz0xl/9KhnCm3ljI5AAnCTlBAZ4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EyJVb/btrKF6Cz0xl/9KhnCm3ljI5AAnCTlBAZ4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EyJVb/btrKF6Cz0xl/9KhnCm3ljI5AAnCTlBAZ4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEyJVb%2FbtrKF6Cz0xl%2F9KhnCm3ljI5AAnCTlBAZ4k%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;800&quot; height=&quot;383&quot; data-origin-width=&quot;1087&quot; data-origin-height=&quot;520&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;에러 메시지에 &quot;xcodebuild: error: 'WhatWeEat/WhatWeEat.xcodeproj' does not exist.&quot; 내용이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;&quot;&gt;*참고 - 아래처럼 현재 Unit Test 파일이 들어있는 디렉토리는 분리되어 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;351&quot; data-origin-height=&quot;218&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tyWtb/btrKHuPYqjM/leCRr4U3idUfcGMh7k49KK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tyWtb/btrKHuPYqjM/leCRr4U3idUfcGMh7k49KK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tyWtb/btrKHuPYqjM/leCRr4U3idUfcGMh7k49KK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtyWtb%2FbtrKHuPYqjM%2FleCRr4U3idUfcGMh7k49KK%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;340&quot; height=&quot;211&quot; data-origin-width=&quot;351&quot; data-origin-height=&quot;218&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;검색해보니 디렉토리를 못찾을 수 있어서 working-directory를 추가하라는 내용이 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(검색 키워드 : GitHub Action, ios, exit code 66)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/59961897/process-completed-with-exit-code-66-during-setup-of-github-actions-ios-project&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Process&amp;nbsp;completed&amp;nbsp;with&amp;nbsp;exit&amp;nbsp;code&amp;nbsp;66&amp;nbsp;during&amp;nbsp;setup&amp;nbsp;of&amp;nbsp;Github&amp;nbsp;Actions&amp;nbsp;iOS&amp;nbsp;project&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;working-directory에 WhatWeEatTests 디렉토리를 넣어봤지만 그래도 안됨..&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그래서 경로 설정에 문제가 있다고 판단하여&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt; -project &lt;b&gt;WhatWeEat/WhatWeEat.xcodeproj -&amp;gt;&lt;/b&gt; &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;WhatWeEat.xcodeproj&lt;/b&gt;&lt;/span&gt;로 바꿔봤는데 exit code 66 문제가 해결됐다!!!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;근데 다른 에러가 생김..&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;815&quot; data-origin-height=&quot;498&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dTVqku/btrKIxS4CrI/GOiCs5XqCZ5Ot8pCDrPpb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dTVqku/btrKIxS4CrI/GOiCs5XqCZ5Ot8pCDrPpb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dTVqku/btrKIxS4CrI/GOiCs5XqCZ5Ot8pCDrPpb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdTVqku%2FbtrKIxS4CrI%2FGOiCs5XqCZ5Ot8pCDrPpb1%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;800&quot; height=&quot;489&quot; data-origin-width=&quot;815&quot; data-origin-height=&quot;498&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;157번 줄에 &quot;xcodebuild:&amp;nbsp;error:&amp;nbsp;Unable&amp;nbsp;to&amp;nbsp;find&amp;nbsp;a&amp;nbsp;destination&amp;nbsp;matching&amp;nbsp;the&amp;nbsp;provided&amp;nbsp;destination&amp;nbsp;specifier:&quot;라는 내용이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;162번 줄 아래로 available destination을 보여주는데 전부 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;OS: 15.2&lt;/b&gt;&lt;/span&gt;로 되어있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이유는 모르겠지만 최신 버전의 OS를 써야하는듯..?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그래서&lt;b&gt;&amp;nbsp;-destination 'platform=iOS Simulator,name=iPhone 13 Pro,OS=14.1' -&amp;gt; &lt;span style=&quot;color: #ee2323;&quot;&gt;OS=latest&lt;/span&gt;&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;근데 그래도 실패&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;747&quot; data-origin-height=&quot;68&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8TRl7/btrKHU116xy/7wG4Cl3pUYgqYBOp9tI8E0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8TRl7/btrKHU116xy/7wG4Cl3pUYgqYBOp9tI8E0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8TRl7/btrKHU116xy/7wG4Cl3pUYgqYBOp9tI8E0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8TRl7%2FbtrKHU116xy%2F7wG4Cl3pUYgqYBOp9tI8E0%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;800&quot; height=&quot;73&quot; data-origin-width=&quot;747&quot; data-origin-height=&quot;68&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;169번 줄에 &quot;Reason: Cannot test target &amp;ldquo;WhatWeEatTests&amp;rdquo; on &amp;ldquo;iPhone 13 Pro&amp;rdquo;:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;iPhone&amp;nbsp;13&amp;nbsp;Pro&amp;rsquo;s&amp;nbsp;iOS&amp;nbsp;Simulator&amp;nbsp;15.2&amp;nbsp;doesn&amp;rsquo;t&amp;nbsp;match&amp;nbsp;&lt;span style=&quot;color: #ee2323;&quot;&gt;WhatWeEatTests&amp;rsquo;s iOS Simulator 15.5 deployment target.&lt;span style=&quot;color: #333333;&quot;&gt;&quot; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;WhatWeEat의 deployment target은 14.0으로 설정했고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;엥.. WhatWeEatTests의 deployment target은 따로 정한 적이 없는데..?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;근데 &lt;b&gt;WhatWeEatTests Target의 Build Settings&lt;/b&gt;를 확인해보니.. 15.5로 설정되어 있었다;; 에러 메시지가 맞았다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1083&quot; data-origin-height=&quot;594&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ur6CM/btrKGREZIMc/sPsXZ6mDRRk08rFUmFkXUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ur6CM/btrKGREZIMc/sPsXZ6mDRRk08rFUmFkXUk/img.png&quot; data-alt=&quot;Tests Target의 iOS deployment target 설정을 꼭 확인하자&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ur6CM/btrKGREZIMc/sPsXZ6mDRRk08rFUmFkXUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fur6CM%2FbtrKGREZIMc%2FsPsXZ6mDRRk08rFUmFkXUk%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;800&quot; height=&quot;439&quot; data-origin-width=&quot;1083&quot; data-origin-height=&quot;594&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Tests Target의 iOS deployment target 설정을 꼭 확인하자&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그래서 &lt;span style=&quot;color: #ee2323;&quot;&gt;WhatWeEatTests의 iOS deployment target을 14.0 (&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;WhatWeEat Target과 동일하게)로 변경&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이번에는 성공!!!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;테스트에 15분이나 걸렸다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #9d9d9d;&quot;&gt;(중간중간 codesign 내용도 있던데.. 트리거 조건에 master branch가 있어서 그런가?)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;746&quot; data-origin-height=&quot;194&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFdHfI/btrKF6CD5bS/BwepZzrwJpUdrPe6iwxskK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFdHfI/btrKF6CD5bS/BwepZzrwJpUdrPe6iwxskK/img.png&quot; data-alt=&quot;PR 생성 후 하단에 뜬 메시지 (성공!!)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFdHfI/btrKF6CD5bS/BwepZzrwJpUdrPe6iwxskK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFdHfI%2FbtrKF6CD5bS%2FBwepZzrwJpUdrPe6iwxskK%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;800&quot; height=&quot;208&quot; data-origin-width=&quot;746&quot; data-origin-height=&quot;194&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;PR 생성 후 하단에 뜬 메시지 (성공!!)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1086&quot; data-origin-height=&quot;330&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JRVOU/btrKHaEiAQc/lhLKn7QK9IlT3zmVAirsv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JRVOU/btrKHaEiAQc/lhLKn7QK9IlT3zmVAirsv1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JRVOU/btrKHaEiAQc/lhLKn7QK9IlT3zmVAirsv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJRVOU%2FbtrKHaEiAQc%2FlhLKn7QK9IlT3zmVAirsv1%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;800&quot; height=&quot;243&quot; data-origin-width=&quot;1086&quot; data-origin-height=&quot;330&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;README에 Badge 추가하기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아래처럼 Workflow의 성공 여부를 나타내는 Badge를 만들 수 있다. (맨 오른쪽)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;401&quot; data-origin-height=&quot;90&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwYngP/btrKHYpTamz/Wfyhq9IaaB1dF4RMOEaY51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwYngP/btrKHYpTamz/Wfyhq9IaaB1dF4RMOEaY51/img.png&quot; data-alt=&quot;Badge 에시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwYngP/btrKHYpTamz/Wfyhq9IaaB1dF4RMOEaY51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwYngP%2FbtrKHYpTamz%2FWfyhq9IaaB1dF4RMOEaY51%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;530&quot; height=&quot;119&quot; data-origin-width=&quot;401&quot; data-origin-height=&quot;90&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Badge 에시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Actions 탭에서 Workflow를 선택한 뒤&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우상단의 ... 버튼을 탭하고, Create status badge를 탭한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 Markdown 소스코드가 나오는데, 그걸 README에 복붙하면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1051&quot; data-origin-height=&quot;308&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCWtGf/btrKHZCjwjr/kn0OKyr42rgpjBeTEIuzSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCWtGf/btrKHZCjwjr/kn0OKyr42rgpjBeTEIuzSK/img.png&quot; data-alt=&quot;Badge 생성하는 방법&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCWtGf/btrKHZCjwjr/kn0OKyr42rgpjBeTEIuzSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCWtGf%2FbtrKHZCjwjr%2Fkn0OKyr42rgpjBeTEIuzSK%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;800&quot; height=&quot;234&quot; data-origin-width=&quot;1051&quot; data-origin-height=&quot;308&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Badge 생성하는 방법&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Badge를 만드는 코드를 아래에 남겨본다.&lt;/p&gt;
&lt;pre id=&quot;code_1661608334057&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;![iOS 14.0](https://img.shields.io/badge/iOS-14.0-lightgrey?style=flat&amp;amp;color=181717)
[![Swift 5.6](https://img.shields.io/badge/Swift-5.6-F05138.svg?style=flat&amp;amp;color=F05138)](https://swift.org/download/) 
[![Xcode 13.4](https://img.shields.io/badge/Xcode-13.4-147EFB.svg?style=flat&amp;amp;color=147EFB)](https://apps.apple.com/kr/app/xcode/id497799835?mt=12) 
[![Run Test](https://github.com/just1103/WhatWeEat/actions/workflows/test_on_develop.yml/badge.svg)](https://github.com/just1103/WhatWeEat/actions/workflows/test_on_develop.yml)&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;GitHub &amp;gt;&amp;nbsp;&lt;a href=&quot;https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions&quot;&gt;Understanding GitHub Actions&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;GitHub &amp;gt;&amp;nbsp;&lt;a href=&quot;https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-swift&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Building and testing Swift&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Blog &amp;gt; &lt;a href=&quot;https://seosh817.tistory.com/104&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;CI/CD란?&lt;/a&gt;&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Blog &amp;gt; &lt;a href=&quot;https://sujinnaljin.medium.com/ci-cd-github-actions-%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-xcode-build-test-%EC%9E%90%EB%8F%99%ED%99%94-73b90a3dcc65&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Github&amp;nbsp;Actions&amp;nbsp;를&amp;nbsp;이용한&amp;nbsp;xcode&amp;nbsp;build&amp;nbsp;&amp;amp;&amp;nbsp;test&amp;nbsp;자동화&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;YouTube &amp;gt; 드림코딩 - &lt;a href=&quot;https://www.youtube.com/watch?v=iLqGzEkusIw&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;제발 깃허브 액션 모르는 개발자 없게해 주세요&lt;/a&gt;&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>비전공자용 노력/개발 툴</category>
      <category>ci/cd</category>
      <category>Github action</category>
      <category>IOS</category>
      <category>Swift</category>
      <category>Xcode</category>
      <category>자동화</category>
      <category>테스트자동화</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/50</guid>
      <comments>https://applecider2020.tistory.com/50#entry50comment</comments>
      <pubDate>Mon, 29 Aug 2022 07:00:44 +0900</pubDate>
    </item>
    <item>
      <title>[Keychain] 키체인 사용하기 (예제 코드)</title>
      <link>https://applecider2020.tistory.com/49</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;저번 포스팅 &lt;a href=&quot;https://applecider2020.tistory.com/48&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;b&gt;[Keychain] 공식문서 읽는 방법 - 공식문서만으로 키체인을 이해해보자&lt;/b&gt;&lt;/a&gt;&lt;b&gt; &lt;/b&gt;에서 Keychain 이론을 다뤘다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이제 간단한 예제를 통해 Keychain을 사용해보자.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;*이 예제에서는 비밀번호를 저장하지만, 실제로는 사용자 인증 등 토큰을 저장하는 형태로 흔히 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. Keychain에 비밀번호 추가 (등록)하기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;먼저 Keychain에 저장할 데이터 타입을 정의하자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이번 예제에서는 password만 저장하지만, 다른 프로퍼티를 추가/저장하는 것도 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1661525966277&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct Credentials {
//    var username: String  // 이번에는 필요 없음
    var password: String
}&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Keychain 관련 Error 타입도 정의하자.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1661526061140&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;enum KeychainError: Error {
    case noPassword
    case unexpectedPasswordData
    case unhandledError(status: OSStatus)
}&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이제 Keychain 타입을 정의하고, Keychain에 비밀번호를 추가하는 메서드를 구현하자.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1661526193705&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct Keychain {
    static func addItemsOnKeyChain(with password: String) {
        let credentials = Credentials(password: password)  // 저장할 비밀번호
        let password = credentials.password.data(using: String.Encoding.utf8)!  // pw를 암호화시킨 것

        // query (질의)를 통해 개발자는 keychain에게 질문을 함 
        // &quot;이걸 Keychain에 보관해줄래?&quot;
        // &quot;kSecClassGenericPassword (일반적인 비밀번호)라는 query인데, 암호화해서 보관할 데이터는 password야&quot;
        let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
//                                    kSecAttrAccount as String: account,  // 이번에는 필요 없음
                                    kSecValueData as String: password]  
        
        let status = SecItemAdd(query as CFDictionary, nil) 
        // &quot;query를 던질건데 pw는 신경쓰지 않겠다, nil 대신 item에 담아서 서버로 보내겠다&quot;
        // 기밀정보가 많다면, data 중에서 뭘 필요로 할지 필터링할 수 있도록 한 것

        if status == errSecSuccess {
            print(&quot;add success&quot;)
        } else if status == errSecDuplicateItem {
            print(&quot;keychain에 Item이 이미 있음&quot;)
        } else {
            print(&quot;add failed&quot;)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;iOS에서는 번들마다 keychain을 부여한다. (번들은 앱이 가지고 있는 주머니)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;위에서 정의한 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;addItemsOnKeyChain&lt;/span&gt; 메서드를 통해 keychain의 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;kSecClassGenericPassword&lt;/span&gt;에 value가 추가된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;한 번 추가되면 동일한 내용을 다시 저장할 수 없다. (errSecDuplicateItem라는 OSStatus status가 발생한다.)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;저번 포스트의 공식문서에서 봤듯이 &lt;span style=&quot;color: #d44c47;&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;/span&gt;❗&lt;b&gt;query (질의)는 Keychain에 저장할 Item이 어떤 종류의 정보인지 등을 나타낸다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Keychain에 Item을 add 할 때는 add에 맞는 질의를 하고, search를 할 때는 search에 맞는 질의를 해야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;완성된 query는 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;SecItemAdd&lt;/span&gt; 함수의 attributes 매개변수로 전달됐다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;result 매개변수에는 nil이 전달됐다. SecItemAdd 메서드의 반환값이 필요 없다는 의미이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. Keychain에 등록한 비밀번호 검색하기&lt;/span&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1661526834689&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static func searchItemFromKeychain() throws -&amp;gt; String? {
    let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
//                                kSecMatchLimit as String: kSecMatchLimitOne,
                                kSecReturnAttributes as String: true,  // search query에 Attribute가 있어야 원하는 값을 찾을 수 있음
                                kSecReturnData as String: true]

    var item: CFTypeRef?
    let status = SecItemCopyMatching(query as CFDictionary, &amp;amp;item)  // &amp;amp;item: 키체인 참조가 아님! 복사값을 넘김
    guard status != errSecItemNotFound else { throw KeychainError.noPassword }
    guard status == errSecSuccess else { throw KeychainError.unhandledError(status: status) }

    guard let existingItem = item as? [String : Any],  // 검색해서 찾은 Item을 타입 캐스팅해서 사용하면 됨
          let passwordData = existingItem[kSecValueData as String] as? Data,
          let password = String(data: passwordData, encoding: String.Encoding.utf8) else {
              throw KeychainError.unexpectedPasswordData
          }

    return password
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;완성된 search query를 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;SecItemCopyMatching&lt;/span&gt; 메서드의 query 매개변수에 전달했다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;result 매개변수에는 &amp;amp;item이 전달됐다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;검색 결과 Keychain에 저장된 Item을 찾았다면, 변수 item에 할당된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;item을 타입 캐스팅하여 값에 접근할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3. Keychain에 등록한 비밀번호 업데이트하기&lt;/span&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1661527183886&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Item을 업데이트한 뒤 결과에 따라 Alert를 띄울 예정
static func updateItemOnKeyChain(with password: String) -&amp;gt; UIAlertController { 
    let previousQuery: [String: Any] = [kSecClass as String: kSecClassGenericPassword]  // search query

    let newPW = password.data(using: .utf8)
    let updateQuery: [String: Any] = [kSecValueData as String: newPW]  // 업데이트할 Item
    let status = SecItemUpdate(previousQuery as CFDictionary, updateQuery as CFDictionary)

    if status == errSecSuccess {
        print(&quot;update complete&quot;)
        return AlertPresenter.showAlert(message: &quot;비밀번호가 변경되었습니다&quot;)
    } else {
        print(&quot;not finished update&quot;)
        return AlertPresenter.showAlert(message: &quot;주의 - 비밀번호 변경 중에 오류가 발생했습니다&quot;)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;마찬가지로 update query가 필요하다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;SecItemUpdate&lt;/span&gt; 메서드를 활용한다.&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;관련 포스트&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://applecider2020.tistory.com/48&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Keychain]&amp;nbsp;공식문서&amp;nbsp;읽는&amp;nbsp;방법&amp;nbsp;-&amp;nbsp;공식문서만으로&amp;nbsp;키체인을&amp;nbsp;이해해보자&amp;nbsp;(긴글&amp;nbsp;주의)&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #5c5c5c;&quot;&gt;Apple Developer Documentation &amp;gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://developer.apple.com/documentation/security/keychain_services&quot;&gt;Keychain Services&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple Developer Documentation &amp;gt; Article -&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/security/keychain_services/keychain_items/adding_a_password_to_the_keychain&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;Adding a Password to the Keychain&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple Developer Documentation &amp;gt;&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/security/1401659-secitemadd&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;SecItemAdd(_:_:)&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple Developer Documentation &amp;gt; Article -&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/security/keychain_services/keychain_items/searching_for_keychain_items&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;Searching for Keychain Items&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple Developer Documentation &amp;gt;&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/security/1398306-secitemcopymatching&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;SecItemCopyMatching(&lt;/a&gt;&lt;a href=&quot;https://developer.apple.com/documentation/security/1398306-secitemcopymatching&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;:&lt;/a&gt;&lt;a href=&quot;https://developer.apple.com/documentation/security/1398306-secitemcopymatching&quot; data-token-index=&quot;2&quot; data-reactroot=&quot;&quot;&gt;:)&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple Developer Documentation &amp;gt; Article -&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/security/keychain_services/keychain_items/updating_and_deleting_keychain_items&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;Updating and Deleting Keychain Items&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple Developer Documentation &amp;gt;&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/security/1393617-secitemupdate&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;SecItemUpdate(&lt;/a&gt;&lt;a href=&quot;https://developer.apple.com/documentation/security/1393617-secitemupdate&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;::&lt;/a&gt;&lt;a href=&quot;https://developer.apple.com/documentation/security/1393617-secitemupdate&quot; data-token-index=&quot;2&quot; data-reactroot=&quot;&quot;&gt;)&lt;/a&gt;&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>iOS/영문 공식문서 뜯어보기-iOS</category>
      <category>IOS</category>
      <category>Keychain</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/49</guid>
      <comments>https://applecider2020.tistory.com/49#entry49comment</comments>
      <pubDate>Sat, 27 Aug 2022 00:26:19 +0900</pubDate>
    </item>
    <item>
      <title>[Keychain] 공식문서 읽는 방법 - 공식문서만으로 키체인을 이해해보자 (긴글 주의)</title>
      <link>https://applecider2020.tistory.com/48</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;공식문서를 읽다 보면 링크를 타고 다른 문서를 읽게 되고,&lt;br /&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이번 포스트에서는 &lt;span&gt;&lt;b&gt;공식문서들을 효율적으로 떠돌아다니는 방법&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;다른 키워드에 비해 &lt;b&gt;Keychain&lt;/b&gt;에 대한 공식문서 양이 (그나마) 방대하지 않아서&lt;span style=&quot;color: #9d9d9d;&quot;&gt; (&amp;larr; 수박 조언 감사해요)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;오직 공식문서만으로 Keychain을 이해해보는 시도를 했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아래의 떠돌이 과정 (&lt;span style=&quot;color: #ee2323;&quot;&gt;빨간색 글씨 참고&lt;/span&gt;)을 통해 &lt;b&gt;공식문서의 중요도를 판단&lt;/b&gt;하는 데 도움이 되었으면 한다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. 최상단의 문서 - Keychain&amp;nbsp;Services&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Keychain에 대한 설명이 시작되는 문서는 &lt;a href=&quot;https://developer.apple.com/documentation/security/keychain_services&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Keychain&amp;nbsp;Services&lt;/a&gt;이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;951&quot; data-origin-height=&quot;503&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xmOqF/btrKFBBU1nn/RNTMlbD3rDyruahNyAN4n0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xmOqF/btrKFBBU1nn/RNTMlbD3rDyruahNyAN4n0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xmOqF/btrKFBBU1nn/RNTMlbD3rDyruahNyAN4n0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxmOqF%2FbtrKFBBU1nn%2FRNTMlbD3rDyruahNyAN4n0%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;450&quot; height=&quot;238&quot; data-origin-width=&quot;951&quot; data-origin-height=&quot;503&quot;/&gt;&lt;/span&gt;&lt;/figure&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;b&gt;요약 : Keychain service란 사용자 대신 작은 데이터 덩어리(small chunks of data)를 안전하게 저장하기 위한 기능이다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;이게 왜 필요할까? 사용자가 비밀번호 등의 데이터를 일일이 기억하는 것이 번거롭기 때문이다.&lt;/li&gt;
&lt;li&gt;Keychain service API는 앱에게 작은 데이터를 Keychain에 저장할 수 있는 메커니즘을 제공한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Keychain (열쇠고리)&lt;/b&gt;은 작은 데이터를 저장하는 &amp;ldquo;암호화된 데이터베이스&amp;rdquo; (encrypted database)이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;작은 데이터&lt;/b&gt;란 비밀번호, 신용카드 정보, 짧은 메모 등의 기밀 정보, 그리고 사용자가 인지하지 못하는 암호키 (cryptographic keys), 인증서 등을 말한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;*다음은 문서 하단 Topics의 &lt;b&gt;API Components의 3개 링크&lt;/b&gt;를 따라가 읽으면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. API&amp;nbsp;Components&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-1) &lt;a href=&quot;https://developer.apple.com/documentation/security/keychain_services/keychain_items&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;Keychain Item&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1058&quot; data-origin-height=&quot;319&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bN1zzU/btrKHbvbhi8/PRxrcvveJK1AfFAWUIyDeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bN1zzU/btrKHbvbhi8/PRxrcvveJK1AfFAWUIyDeK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bN1zzU/btrKHbvbhi8/PRxrcvveJK1AfFAWUIyDeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbN1zzU%2FbtrKHbvbhi8%2FPRxrcvveJK1AfFAWUIyDeK%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;500&quot; height=&quot;151&quot; data-origin-width=&quot;1058&quot; data-origin-height=&quot;319&quot;/&gt;&lt;/span&gt;&lt;/figure&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;b&gt;Keychain에 Data (기밀 정보)를 저장할 때, Data를 포장해서 Item 형태로 저장한다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Data&lt;/b&gt;와 &lt;b&gt;Attribute&lt;/b&gt;를 함께 포장해서 1개의 Item으로 만든다. &lt;b&gt;Attribute를 통해 Data를 검색/접근한다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Keychain service는 무슨 일을 할까? Data를 암호화하고, Keychain에 Item을 저장하는 것을 관리한다. (Keychain은 디스크에 저장된 &amp;ldquo;암호화된 데이터베이스&amp;rdquo;이다.)&lt;/li&gt;
&lt;li&gt;나중에 &amp;ldquo;승인된 프로세스&amp;rdquo; (authorized processes)를 통해 Keychain service를 사용하여 Item을 찾고 Data의 암호를 해제한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1654&quot; data-origin-height=&quot;294&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pHk7s/btrKGx6CF0W/660cYeSMZyG8QKoChI92PK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pHk7s/btrKGx6CF0W/660cYeSMZyG8QKoChI92PK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pHk7s/btrKGx6CF0W/660cYeSMZyG8QKoChI92PK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpHk7s%2FbtrKGx6CF0W%2F660cYeSMZyG8QKoChI92PK%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;750&quot; height=&quot;133&quot; data-origin-width=&quot;1654&quot; data-origin-height=&quot;294&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;*문서 하단 Topics에 &lt;b&gt;First Steps&lt;/b&gt; 항목이 있다면 반드시 읽어야 한다. 기초 개념을 다루기 때문이다.&lt;/span&gt;&lt;br /&gt;&lt;b&gt;*아래의 Adding Keychain Items, Keychain Item Search, Keychain Item Modification&lt;/b&gt; 항목도 중요해보인다. &lt;br /&gt;(&lt;b&gt;Legacy&lt;/b&gt; 어쩌구 주제들은 '과거에 이 방식을 사용했구나' 하고 넘어가면 된다.) &lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;*일단 &lt;b&gt;1번 문서&lt;/b&gt;와 연결된 나머지 링크들을 먼저 읽자.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2-2) &lt;a href=&quot;https://developer.apple.com/documentation/security/keychain_services/keychains&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;Keychains&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요약 :&lt;b&gt; macOS의 전체 Keychain을 생성하고 관리한다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;iOS Keychain과 macOS Keychain의 특성이 다르다.&lt;/li&gt;
&lt;li&gt;iOS
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 앱들은 &lt;b&gt;single Keychain&lt;/b&gt;에 접근할 수 있다. &lt;br /&gt;*single Keychain이란? &lt;br /&gt;앱마다 개별적인 Keychain을 가지는 게 아니라, &lt;b&gt;여러 앱들이 동일한 열쇠고리를 공유한다&lt;/b&gt;는 의미이다.&lt;/li&gt;
&lt;li&gt;아이폰의 잠금/잠금 해제 상태에 따라 Keychain도 자동으로 아이폰과 동일한 잠금/잠금 해제 상태가 된다.&lt;/li&gt;
&lt;li&gt;여러 앱들이 동일한 Keychain을 공유하고 있지만, &lt;b&gt;1개 앱이 Keychain에 들어있는 모든 Item들에 접근할 수 있는 것이 아니다.&lt;/b&gt; 특정 앱은 자신이 가진 Keychain Item에만 접근 가능하다. &lt;br /&gt;(앱이 소속된 group이 있는 경우, 해당 group의 Item에는 접근 가능하다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;macOS
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 개의 Keychain을 가질 수 있다.&lt;/li&gt;
&lt;li&gt;맥북의 Keychain Access 앱을 통해 &lt;b&gt;사용자가 직접 Keychain을 관리&lt;/b&gt;할 수 있고, iOS와 마찬가지로 default Keychain으로 암시적으로 작업할 수 있다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;그럼에도 Keychain service API는 개발자가 직접 Keychain을 조작 (생성/수정)할 수 있도록 함수를 제공한다. &lt;br /&gt;ex. 특정 앱 전용의 Keychain을 생성/관리할 수 있다.&lt;/li&gt;
&lt;li&gt;&amp;rdquo;강력한 접근 제어 메커니즘&amp;rdquo;은 일반적으로 &amp;ldquo;키 체인 접근 utility&amp;rdquo;를 복제하려는 앱 이외의 다른 앱에 대해 이것 (Keychain 조작)을 불필요하게 만든다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;*요약 부분을 때 'macOS에 대한 내용이니까 당장 앱 개발할 때는 필요 없겠구나'를 알 수 있다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;*문서 하단 Topics를 훑어보며 'Creation and Deletion, Search 등이 있구나'를 체크하고 넘어가면 된다. &lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2-3) &lt;a href=&quot;https://developer.apple.com/documentation/security/keychain_services/access_control_lists&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;Access Control Lists&lt;/span&gt;&lt;/a&gt; (AC List)&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1276&quot; data-origin-height=&quot;620&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kJzSC/btrKIsKjcQL/EbZh0kFCkCvsVfRqUySQC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kJzSC/btrKIsKjcQL/EbZh0kFCkCvsVfRqUySQC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kJzSC/btrKIsKjcQL/EbZh0kFCkCvsVfRqUySQC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkJzSC%2FbtrKIsKjcQL%2FEbZh0kFCkCvsVfRqUySQC0%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;650&quot; height=&quot;316&quot; data-origin-width=&quot;1276&quot; data-origin-height=&quot;620&quot;/&gt;&lt;/span&gt;&lt;/figure&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;b&gt;macOS에서 Keychain Item에 접근 가능한 앱을 제어한다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;macOS에서 &lt;b&gt;1개 Keychain Item&lt;/b&gt;은 &lt;b&gt;Access 인스턴스&lt;/b&gt;를 가진다. &lt;br /&gt;(Access 인스턴스에는 &lt;b&gt;&amp;ldquo;AC List&amp;rdquo;&lt;/b&gt;가 들어있다. AC List는 여러 개의 &lt;b&gt;&amp;ldquo;Entry(항목)&amp;rdquo;&lt;/b&gt;로 구성된다.)&lt;/li&gt;
&lt;li&gt;Entry에는 &lt;b&gt;Operations 배열 및&lt;/b&gt; (해당 item을 사용하여 operations를 수행할 수 있음을 신뢰할 수 있는)&lt;b&gt; Trusted app 배열&lt;/b&gt;이 들어있다.&lt;/li&gt;
&lt;li&gt;AC List는 해당 Keychain Item에 대한 접근 가능성 (Accessibility)을 제어한다.&lt;/li&gt;
&lt;li&gt;이러한 AC List는 어떻게 사용될까? &lt;br /&gt;특정 앱이 문서에 서명 (sign) 하기 위해 Keychain Item에 접근하려 한다면, 시스템은 AC List를 뒤져서 원하는 operation을 갖고 있는 Entry를 찾아낸다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;만약 해당 Entry가 없으면 시스템은 접근을 거부한다.&lt;/b&gt; (거부 당한 앱은 다른 작업을 시도하거나, 사용자에게 알림을 줄 수 있다.)&lt;/li&gt;
&lt;li&gt;해당 Entry가 있으면 시스템은 Entry의 Trusted app 배열에 그 앱이 있는지 확인한다. &lt;br /&gt;- 만약 앱이 있으면 시스템은 접근권한을 부여 (grants access)한다. &lt;br /&gt;- 앱이 없으면 시스템은 사용자에게 확인 메시지를 표시한다. 사용자는 Deny, Allow, Always Allow 등을 선택할 수 있다. 사용자가 접근을 허용하면, 시스템은 그 앱을 Entry의 Trusted app에 추가한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Important: AC List는&lt;b&gt; iOS, 그리고 iCloud Keychain을 사용하는 macOS 앱에는 사용 불가하다.&lt;/b&gt; &lt;br /&gt;이러한 환경에서 Keychain Item을 공유하려면, Access Group을 사용해야 한다. &lt;br /&gt;(&lt;a href=&quot;https://developer.apple.com/documentation/security/keychain_services/keychain_items/sharing_access_to_keychain_items_among_a_collection_of_apps&quot;&gt;Sharing Access to Keychain Items Among a Collection of Apps&lt;/a&gt; 링크를 훑어보면, &amp;ldquo;하나의 개발 팀&amp;rdquo;에서 개발한 family apps가 있고, 그 앱들이 동일한 사용자 정보를 활용하는 상황이라면, Access Group을 지정해서 사용자 정보 (Keychain Items)를 공유할 수 있다는 내용이 나온다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;*문서 하단 Topics에 Access Creation, Access Query 등이 있음을 훑고 넘어간다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;*여기까지 1번 문서 Topics의 API Components를 모두 읽었다. 이제 다시 &lt;b&gt;2-1번 문서&lt;/b&gt;에서 봤던 &lt;b&gt;First Steps&lt;/b&gt; 링크를 보러간다.&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3. Article - &lt;a href=&quot;https://developer.apple.com/documentation/security/keychain_services/keychain_items/using_the_keychain_to_manage_user_secrets&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;Using the Keychain to Manage User Secrets&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요약 : &lt;b&gt;사용자의 작은 기밀정보를 Keychain에 보관하여 사용자가 일일이 기억할 필요가 없게 하자.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Keychain service를 통해 사용자는 암호화된 저장소에 쉽게 접근할 수 있다. &lt;br /&gt;ex. 인터넷 비밀번호를 저장하기 위해 Keychain Item을 사용한 프로세스 예시
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;아래는 사용자의 인터넷 비밀번호가 인증되면 (End 단계), 네트워크 접근을 할 수 있음을 나타내는 그림이다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;비밀번호가 들어있는 Keychain Item을 Add 또는 Modify하거나 비밀번호를 인증하는 과정을 설명한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;SecItemCopyMatching&lt;/span&gt;을 사용하여 Item을 검색한다. Item이 없으면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;SecItemAdd&lt;/span&gt;를 통해 Item을 Add한다.&lt;/li&gt;
&lt;li&gt;Item이 있으면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Authenticate(인증)&lt;/b&gt;을 진행한다. 인증에 실패하면&lt;span&gt;&amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;SecItemUpdate&lt;/span&gt;&amp;nbsp;을&lt;/span&gt; 통해 Item을 Modify한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1128&quot; data-origin-height=&quot;436&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqSr3w/btrKHdf3Ppo/E0id1e82ZHNe2ZDM5bFNe1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqSr3w/btrKHdf3Ppo/E0id1e82ZHNe2ZDM5bFNe1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqSr3w/btrKHdf3Ppo/E0id1e82ZHNe2ZDM5bFNe1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqSr3w%2FbtrKHdf3Ppo%2FE0id1e82ZHNe2ZDM5bFNe1%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;680&quot; height=&quot;263&quot; data-origin-width=&quot;1128&quot; data-origin-height=&quot;436&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Involve the User When Needed (필요시에만 사용자를 통해 입력값을 받음)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;앱이 최초로 &lt;b&gt;Credentials (자격 증명)&lt;/b&gt;을 필요로 할 때는 Keychain에 저장된 비밀번호가 없다.&lt;/li&gt;
&lt;li&gt;이 경우 앱은 사용자에게 메시지를 표시하여 Credentials를 입력받는다.&lt;b&gt; (위 그림의 오른쪽 Flow 참고)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;앱은 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;SecItemAdd&lt;/span&gt; 함수를 통해 이 사용자 입력값을 저장한다. 이제 앱은 네트워크 접근을 이어갈 수 있다.&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;(본문 링크는 중요하므로 꼭 확인해야 한다.)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;b&gt;참고 - 오른쪽 Flow에서 사용자 입력값에 대한 Authenticate (인증)이 필요한 이유는?&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;- 이 상황에서 '인증'이란 사용자의 id/pw가 일치하는지 서버에게 물어보는 것이다.&amp;nbsp;&lt;br /&gt;- &lt;u&gt;mac에서 사용하던 웹사이트를, 처음으로 앱에서 로그인할 때, id/pw는 있고, 키체인은 없을 때&lt;/u&gt; 오른쪽 Flow를 탄다. &lt;br /&gt;- 제대로 된 사용자 입력값을 받을 때까지 Item Add를 하지 않는다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;나중에 서버가 &lt;b&gt;재인증 (reauthentication)&lt;/b&gt;을 요구할 때, &lt;b&gt;앱은 사용자를 거치지 않고 Keychain에 저장한 Credentials를 꺼내올 수 있다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Keychain에 Item을 추가하는 방법은 Adding a Password to the Keychain 문서를 확인하면 된다. &lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;(마찬가지로 본문 링크이므로 꼭 확인해야 한다.)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Avoid Bothering the User in the Common Case
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자로부터 비밀번호를 입력받거나, 새 비밀번호를 설정하는 등의 interaction을 최소화하는 것이 좋다. &lt;br /&gt;따라서 &lt;b&gt;일반적으로 가운데 Flow를 따른다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;사용자가 앱을 오랜만에 사용했을 때 등의 상황에서 Secure network resource는 주기적으로 reauthentication을 요구한다.&lt;/li&gt;
&lt;li&gt;이에 대응하기 위해 앱은 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;SecItemCopyMatching&lt;/span&gt; 함수를 통해 Keychain에서 해당 비밀번호를 찾는다. &lt;br /&gt;이 비밀번호로 인증이 성공하면 사용자 interaction이 필요 없어서 좋다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Handle Changes Gracefully
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가끔 사용자가 앱 영역 밖에서 비밀번호를 수정할 수 있다.&lt;b&gt; (ex. 웹사이트에서 사용자가 비밀번호를 변경하는 경우)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;이때 앱의 subsequent keychain item을 찾아서 얻은 비밀번호는 수정 이전의 비밀번호이므로 인증에 실패하게 된다.&lt;/li&gt;
&lt;li&gt;이 경우 사용자를 통해 새로운 비밀번호를 받고, &lt;span style=&quot;background-color: #dddddd;&quot;&gt;SecItemUpdate&lt;/span&gt; 함수를 통해 기존 저장값을 수정한다. &lt;br /&gt;&lt;b&gt;(위 그림의 왼쪽 Flow 참고)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;SecItemDelete&lt;/span&gt; 함수를 통해 Keychain에서 비밀번호를 완전히 삭제할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;*문서 하단 Topics의 Item Creation and Modification의 3개 링크가 중요해 보인다. &lt;span style=&quot;color: #9d9d9d;&quot;&gt;(&lt;b&gt;2-1번 문서&lt;/b&gt;의 상위 Topic에도 있었던 링크) &lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;*See Also의 &lt;b&gt;First Steps&lt;/b&gt; 링크를 먼저 보는 것이 좋다.&lt;/span&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; 더 기초적인 개념일 테니..&lt;/span&gt;&lt;br /&gt;&amp;nbsp; (위 링크를 따라가도 좋지만, 어차피 본문에 First Steps 링크가 들어있다.)&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. Class - &lt;a href=&quot;https://developer.apple.com/documentation/security/seckeychainitem&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;SecKeychainItem&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요약 : &lt;b&gt;Keychain을 나타내는 opaque 타입이다.&lt;/b&gt; &lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;*opaque 타입이란? &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;반환타입이 Opaque 타입인 함수는 반환 정보를 외부에 숨길 수 있다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;예를 들어 함수의 반환타입이 Opaque 타입이라면, &quot;A 프로토콜을 채택한 타입&quot;이면 모두 가능하도록 설정할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Keychain에 저장된 certificate (인증서)에 대한 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;SecKeyChainItem&lt;/span&gt; 객체는 SecCertificate로 안전하게 캐스팅될 수 있다.&lt;br /&gt;(본문 링크인&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://developer.apple.com/documentation/security/seccertificate&quot;&gt;SecCertificate&lt;/a&gt;을 훑어보면, 특정 인증서를 나타내는 CoreFoundation 추상 타입의 객체임을 알 수 있다.) &lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;*인증서 관련 개념은 나중에 찾아보기로 하고 일단 패스...&lt;br /&gt;(Apple은 네이밍을 신경 쓴다. 클래스 이름을 먼저 확인하자. 클래스 요약을 보면 이 내용은 OS, 네트워크 프로토콜 관련 내용이므로 일단 패스해도 된다고 유추할 수 있다.)&lt;/span&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;*문서 하단 Topics에 또 &lt;b&gt;First Steps&lt;/b&gt;가 있다.&lt;/span&gt; &lt;span style=&quot;color: #9d9d9d;&quot;&gt;젠장...&lt;/span&gt; &lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;그래도 하나는 이미 읽은 링크이고, 하나는 2번 문서의 First Steps 마지막 링크와 일치하니까 다행이다.. &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;이제 이 마지막 링크를 보자.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. Function - &lt;a href=&quot;https://developer.apple.com/documentation/security/1394040-seckeychainitemgettypeid&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;SecKeychainItemGetTypeID()&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요약 : &lt;b&gt;Keychain Item 객체가 속한 Opaque 타입의 identifier를 반환한다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;이 함수의 반환값을 &lt;a href=&quot;https://developer.apple.com/documentation/corefoundation/cftypeid&quot;&gt;CFTypeID&lt;/a&gt; identifier와 비교할 수 있다. CFTypeID identifier는 &lt;a href=&quot;https://developer.apple.com/documentation/corefoundation/1521218-cfgettypeid&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;CFGetTypeID(_:)&lt;/a&gt;&amp;nbsp;함수의 반환값이다. &lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;(본문 링크를 훑어보면 CFTypeID는 UInt의 typealias이고, Core Foundation에서 타입 identifier를 정의하며, 타입 identifier란 CoreFoundation 객체가 &quot;속해 있는&quot; Opaque 타입을 식별하는 UInt 값임을 알 수 있다. 대충 넘어가자..)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;이 함수의 반환값은 release 마다, platform 마다 변경될 수 있다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;*문서 하단 See Also의 First Steps는 이미 읽은 링크들이다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;이제 &lt;b&gt;3번 문서의 Item Creation and Modification의 링크&lt;/b&gt;를 따라간다. 이게 본론인 듯. 드디어 코드가 나온다..&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;6. Article - &lt;a href=&quot;https://developer.apple.com/documentation/security/keychain_services/keychain_items/adding_a_password_to_the_keychain&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;Adding a Password to the Keychain&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요약 : &lt;b&gt;사용자를 대신해서 Keychain에 네트워크 Credentials (자격 증명)을 추가하는 방법을 설명한다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Keychain service를 사용하면 사용자 비밀번호를 간단히 저장할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(1) Get Set Up (설정하기)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;앱에서 사용할 Credentials 정보를 저장할 구조체를 정의해라.&lt;/li&gt;
&lt;li&gt;그다음, Keychain 접근 결과를 나타내기 위한 Error 열거형을 정의해라.&lt;/li&gt;
&lt;li&gt;그리고 앱의 서버를 입력해라.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1661519569610&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct Credentials {
    var username: String
    var password: String
}
enum KeychainError: Error {
    case noPassword
    case unexpectedPasswordData
    case unhandledError(status: OSStatus)
}
static let server = &quot;www.example.com&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(2) Create an Add Query&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위에서 정의한 Credentials 구조체의 인스턴스, server 상수를 사용하여 Add query를 생성해라.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1661519596178&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let credentials = Credentials(username: &quot;Applecider&quot;, password: &quot;1234&quot;)
let account = credentials.username
let password = credentials.password.data(using: String.Encoding.utf8)!  // 비밀번호는 Data 타입으로 인코딩함
var query: [String: Any] = [kSecClass as String: kSecClassInternetPassword,
                            kSecAttrAccount as String: account,
                            kSecAttrServer as String: server,
                            kSecValueData as String: password]&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위 코드의 query 딕셔너리를 자세히 보자.&lt;/li&gt;
&lt;li&gt;첫 번째 key-value : &lt;span style=&quot;background-color: #dddddd;&quot;&gt;kSecClass&lt;/span&gt;가 있는 key-value는&lt;b&gt; &amp;ldquo;Item이 어떤 종류의 정보인지&amp;rdquo;&lt;/b&gt; 나타낸다. &lt;br /&gt;예시의 value인 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;kSecClassInternetPassword&lt;/span&gt;는 &lt;b&gt;&quot;Item이 Internet password (인터넷 비밀번호)임&quot;&lt;/b&gt;을 나타낸다. &lt;br /&gt;&lt;b&gt;&amp;rarr; ❗Keychain service는 이것을 보고, Data가 기밀 정보이고 암호화를 필요로 한다고 추론한다. 또한 Item이 Attribute를 가지는 것을 보장한다. 여기서 Attribute는 다른 인터넷 비밀번호와 이 Item을 구별하는 역할을 한다.&lt;/b&gt; &lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;(다른 인터넷 비밀번호의 Attribute와 이 Item의 Attribute가 구별된다는 뜻인 듯)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;This also ensures that the item has attributes that distinguish it from other Internet passwords, such as &lt;b&gt;the server and account&lt;/b&gt; to which it applies.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;두세 번째 key-value : 위에서 말한 &lt;b&gt;Attribute 정보를 제공하고 있다.&lt;/b&gt; &lt;br /&gt;(상수 account를 통해 사용자의 이름을, 상수 server를 통해 도메인 이름을 첨부한다.)&lt;/li&gt;
&lt;li&gt;가장 중요한 &lt;b&gt;비밀번호&lt;/b&gt;는 Data 인스턴스로 &lt;b&gt;인코딩&lt;/b&gt;하여 query에 넣는다.&lt;/li&gt;
&lt;li&gt;Note: Keychain service는 관련된 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;kSecClassGenericPassword&lt;/span&gt; 를 제공한다. Generic password (일반적인 비밀번호, 제네릭 타입 아님)는 Internet password와 비슷하지만, remote access와 관련된 Attribute가 없다. kSecAttrServer 등의 Attribate가 필요 없을 때 사용하면 된다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;(본문 링크를 타고&amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;a style=&quot;color: #9d9d9d; background-color: #dddddd;&quot; href=&quot;https://developer.apple.com/documentation/security/ksecclassgenericpassword&quot;&gt;kSecClassGenericPassword&lt;/a&gt;&lt;/span&gt;&amp;nbsp;를 훑어보면, &lt;b&gt;일반적인 비밀번호 Item을 나타내는 &amp;ldquo;값&amp;rdquo;&lt;/b&gt;이며, 전역 변수임을 알 수 있다. 아래의 Attribute들은 이 클래스의 Item에 적용된다. 그리고 이 전역변수의 타입은&amp;nbsp;&lt;a style=&quot;color: #9d9d9d;&quot; href=&quot;https://developer.apple.com/documentation/corefoundation/cfstring&quot;&gt;CFString&lt;/a&gt;&amp;nbsp;클래스인데, Core Foundation에 속한다. iOS 개발에 대부분 사용되는 Foundation 프레임워크의 Definition을 뜯어보면 이 Core Foundation을 import하고 있다.)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;참고 - 접두어 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;k-&lt;/span&gt; 는 왜 붙일까? (참고: &lt;a href=&quot;https://stackoverflow.com/questions/675816/what-does-the-k-prefix-indicate-in-apples-apis&quot;&gt;What does the 'k' prefix indicate in Apple's APIs?&lt;/a&gt;)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;The k means &amp;ldquo;constant&amp;rdquo; in hungarian notation. (Hungarian noun &amp;ldquo;konstans&amp;rdquo; means &amp;ldquo;constant&amp;rdquo;.)&lt;/li&gt;
&lt;li&gt;macintosh 프로그래밍 초기부터 사용한 네이밍 컨벤션이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(3) Add the Item&lt;/h4&gt;
&lt;pre id=&quot;code_1661520135447&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let status = SecItemAdd(query as CFDictionary, nil)
guard status == errSecSuccess else { throw KeychainError.unhandledError(status: status) }&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;query가 완성되면 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;SecItemAdd&lt;/span&gt; 함수에 전달하면 된다.&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;*본문 링크는 중요하니까 &lt;span style=&quot;background-color: #dddddd; color: #333333;&quot;&gt;SecItemAdd&lt;/span&gt; 함수에 대한 본문 링크를 먼저 확인해보자. (7번 문서 요약 참고)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;일반적으로 Add operation의 두 번째 argument에서 참조 형태로 제공되는 return data를 무시한다. &lt;br /&gt;하지만 &lt;b&gt;함수의 return status를 확인&lt;/b&gt;하여 operation이 성공했는지 항상 확인하는 것이 좋다. &lt;br /&gt;ex. 주어진 Attribute를 가진 Item이 이미 존재하는 경우 Add operation이 실패할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(4) Ensure Searchability&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;나중에 Item을 검색하려면, Attribute를 활용해야 한다.&lt;/li&gt;
&lt;li&gt;위 예시에서 server 및 account는 Item을 다른 Item과 구별하는 특징이다.&lt;/li&gt;
&lt;li&gt;server와 같은 constant attribute는 검색하는 동안 동일한 값을 사용하지만, account는 런타임 때 사용자가 제공한 값을 가지고 있으므로&lt;b&gt; dynamic attribute&lt;/b&gt;이다. &lt;br /&gt;앱이 다양한 Attribute를 가지는 유사한 Item을 추가할 일이 없다면, dynamic attribute를 search 매개변수에서 생략할 수 있다. &lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;*뒷부분은 나중에 보자...&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;*(본문 링크 SecItemAdd 함수를 봤다면) Add Item을 다 봤으니 Search Item 부분을 읽어보자. (9번 문서 요약 참고)&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;7. Function - &lt;a href=&quot;https://developer.apple.com/documentation/security/1401659-secitemadd&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;SecItemAdd(_:_:)&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1661520457651&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func SecItemAdd(_ attributes: CFDictionary, 
              _ result: UnsafeMutablePointer&amp;lt;CFTypeRef?&amp;gt;?) -&amp;gt; OSStatus&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요약 : &lt;b&gt;1개 이상의 Item을 Keychain에 추가한다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;문서가 길지만.. 침착히 매개변수 attributes, result 그리고 반환타입을 알아보자.&lt;/li&gt;
&lt;li&gt;attributes 매개변수
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;타입은 딕셔너리 (CFDictionary)이다.&lt;span style=&quot;color: #9d9d9d;&quot;&gt; (CFDictionary 클래스는 immutable 딕셔너리 객체에 대한 참조이다.)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;딕셔너리의 역할은 &lt;b&gt;Keychain에 추가할 Item을 설명&lt;/b&gt;하는 것이다. &lt;b&gt;query&lt;/b&gt;가 이 매개변수에 전달된다.&lt;/li&gt;
&lt;li&gt;✅&amp;nbsp;&lt;b&gt;query에는 Item의 class, data, atrribute 정보가 담겨있다.&lt;/b&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Item의 Class
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Items의 Classes에 따라 다른 Attribute와 behavior이 활용된다. &lt;span style=&quot;background-color: #dddddd;&quot;&gt;kSecClass&lt;/span&gt;라는 key에 대한 value는 무엇을 의미할까? ✅&amp;nbsp;&lt;b&gt;value는 Keychain service에게 &amp;ldquo;저장할 Data가 어떤 종류인지&amp;rdquo; 알려주는 역할을 한다. 저장할 Data가 비밀번호인지, certificate인지, cryptographic key인지 나타낼 수 있다. &lt;/b&gt;(ex. kSecClassGenericPassword는 저장할 데이터가 일반적인 비밀번호임을 알려준다.)&lt;/li&gt;
&lt;li&gt;본문 링크의 &lt;a href=&quot;https://developer.apple.com/documentation/security/ksecclass&quot;&gt;kSecClass&lt;/a&gt;를 보면, 딕셔너리의 key이고 value는 Item의 Class를 나타내는 전역 변수임을 알 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.apple.com/documentation/security/keychain_services/keychain_items/item_class_keys_and_values&quot;&gt;Item Class Keys and Values&lt;/a&gt;를 보면, &lt;b&gt;Keychain Item의 종류 (비밀번호, certificate 등)에 따라 Class를 지정해야 함&lt;/b&gt;을 알 수 있다. &lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;*문서 하단 Topics의 Item Class의 Key와 Value로 들어갈 수 있는 k- 상수들을 확인할 수 있다! &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;훑어보니 중요한 문서인 듯 (8번 문서 요약 참고)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Data
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;✅&amp;nbsp;암호화시켜서 저장하게 될 데이터&lt;/b&gt;를 의미한다. &lt;span style=&quot;background-color: #dddddd;&quot;&gt;kSecValueData&lt;/span&gt;라는 key를 사용하고, key의 value에 저장할 Data (ex. 비밀번호)를 넣는다. Keychain service는 Item의 종류에 따라 이 Data를 암호화할지 말지를 결정한다. Item이 기밀정보라면 (즉, 비밀번호 타입이거나 private key를 포함하고 있는 경우) Data를 암호화한다.&lt;/li&gt;
&lt;li&gt;본문 링크의 kSecValueData를 보면, 이 딕셔너리 key의 value는 CFData 타입이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Attribute (optional)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Attribute key 정보를 말한다. ✅&amp;nbsp;&lt;b&gt;이 key는 나중에 Item을 검색할 수 있게 해주고,&lt;/b&gt; Data가 사용되거나 공유되는 방법을 나타낸다. 원하는 만큼 Attribute를 추가할 수 있지만, Item Class에 따라 추가할 수 있는 Attribute의 종류가 정해져 있다.&lt;/li&gt;
&lt;li&gt;본문 링크의 &lt;a href=&quot;https://developer.apple.com/documentation/security/keychain_services/keychain_items/item_attribute_keys_and_values&quot;&gt;Item Attribute Keys and Values&lt;/a&gt;를 보면, Attribute 정보를 나타내기 위한 key/value를 확인할 수 있다. (ex. 아래의 Topics의 Password Attribute Keys를 보면 저장할 Data가 비밀번호인 경우에 사용 가능한 Attribute key들이 나와있다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;반환 타입 (optional)&lt;/span&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;1개 이상의 반환 타입 key를 말한다. &lt;b&gt;이 key가 의미하는 것은 ✅ 성공적으로 완료되면 (SecItemAdd 메서드를 통한 Add Operation이 완료되면) 반환되도록 할 Data가 무엇인지이다.&lt;br /&gt;&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&amp;rarr; 이걸 언제 사용할까? &lt;br /&gt;&lt;b&gt;&amp;ldquo;Keychain&amp;rdquo;에 Item을 추가하는 것 (Add Operation)과 별개로 &amp;ldquo;서버&amp;rdquo;에도 정보를 보내서 저장하게 할 수도 있다. 이때 서버에 보내는 작업을 할 때 정보를 result 매개변수에 담아서 보내면 된다. (Keychain을 통해 암호화한 비밀번호를 result에 담아서 서버에게 보낸다는 뜻이다.)&lt;/b&gt; 그리고 서버에 정보를 보낼 때 Item에 담긴 기밀정보를 모두 보내도 되지만, 기밀정보가 많다면 원하는 정보만 필터링해서 보낼 수도 있다. 보통&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt; SecItemAdd 함수의 반환값을 무시하는데, 이때는 result 매개변수에 반환값 key를 넣을 필요가 없다.&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;본문 링크의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://developer.apple.com/documentation/security/keychain_services/keychain_items/item_return_result_keys&quot;&gt;Item Return Result Keys&lt;/a&gt;를 보면, SecItemAdd 함수는 result 매개변수를 통해 Item의 Data 및 Attribute를 반환하고, 이 result 매개변수에게 pointer를 제공한다. (???) &lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;query 딕셔너리에 Item result key를 사용하여 result를 어떤 형태 (format)로 나타낼지 지정한다. &lt;span&gt;(???)&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;*이 부분을 이해하기 힘들었다. 이 함수의 result 매개변수를 먼저 보는 것이 좋겠다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;result 매개변수
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;타입은 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;a style=&quot;background-color: #dddddd;&quot; href=&quot;https://developer.apple.com/documentation/swift/unsafemutablepointer&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;UnsafeMutablePointer&lt;/a&gt;&amp;lt;&lt;a style=&quot;background-color: #dddddd;&quot; href=&quot;https://developer.apple.com/documentation/corefoundation/cftyperef&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;CFTypeRef&lt;/a&gt;?&amp;gt;?&lt;/span&gt; 이다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;CFTypeRef&lt;/span&gt;는 AnyObject의 typealias이다. &lt;br /&gt;(Core Foundation에서 정의된 base 타입이다. polymorphic 함수 (???)의 반환(타입)으로 사용된다.)&lt;/li&gt;
&lt;li&gt;UnsafeMutablePointer는 Generic Structure &lt;span style=&quot;background-color: #dddddd;&quot;&gt;@frozen struct UnsafeMutablePointer&amp;lt;Pointee&amp;gt;&lt;/span&gt; 이다. &lt;br /&gt;이 인스턴스를 사용하여 메모리에 있는 특정 타입 (Pointee 타입)의 데이터에 접근할 수 있다.&lt;/li&gt;
&lt;li&gt;반환 시 (SecItemAdd 메서드의 반환) 새롭게 추가된 Item에 대한 참조이다. 이 매개변수의 정확한 타입은 attribute 매개변수의 특정 value에 따라 결정된다. (attribute 매개변수의 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;4. 반환 타입 key&lt;/span&gt;의 value를 말하는 듯)&lt;/li&gt;
&lt;li&gt;result가 필요 없으면 nil을 할당하고, 만약 필요하면 앱 내부에서 참조한 객체를 메모리에서 해제해줘야 한다. (???)&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;참고 - result는 왜 포인터/참조 사용할까?&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;사실 정답이 없는 문제이다. 값을 전달해도 되지만, 여기서는 참조를 넘겨주도록 구현했을 뿐이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&amp;nbsp;반환값
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;ldquo;result code&amp;rdquo;라고만 설명되어 있다.&lt;/li&gt;
&lt;li&gt;타입은 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;OSStatus&lt;/span&gt; 이다. (근데 이상하게도 OSStatus 타입은 링크가 없다..)&lt;/li&gt;
&lt;li&gt;본문 링크의 &lt;a href=&quot;https://developer.apple.com/documentation/security/1542001-security_framework_result_codes&quot;&gt;Security Framework Result Codes&lt;/a&gt;를 보면, Evaluate result codes common to many Security framework functions... 보안 관련 기능인가..?&lt;/li&gt;
&lt;li&gt;OSStatus을 구글링하면, 특정 함수의 &amp;ldquo;result code&amp;rdquo;라고 나온다. &lt;span style=&quot;color: #ee2323;&quot;&gt;Keychain 관련 오류라고 봐도 될 듯?&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;OSStatus 의미를 찾아주는 사이트: &lt;a href=&quot;https://www.osstatus.com/&quot;&gt;https://www.osstatus.com/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Discussion
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Keychain에 여러 Item을 한꺼번에 추가하려면, &lt;span style=&quot;background-color: #dddddd;&quot;&gt;kSecUseItemList&lt;/span&gt; key를 사용하고, value에 딕셔너리들의 배열을 넣으면 된다. 단, 비밀번호가 아닌 Item만 가능하다.&lt;/li&gt;
&lt;li&gt;Xcode는 &lt;b&gt;application-identifier entitlement (자격)&lt;/b&gt;을 앱 bundle에 추가한다. Keychain service는 이 entitlement를 사용하여 앱의 Keychain Item에 대한 접근권한을 부여한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #ee2323;&quot;&gt;*이제 attributes 매개변수 부분의 본문 링크인 Item&amp;nbsp;Class&amp;nbsp;Keys&amp;nbsp;and&amp;nbsp;Values를 살펴보자.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;8. &lt;a href=&quot;https://developer.apple.com/documentation/security/keychain_services/keychain_items/item_class_keys_and_values&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;Item Class Keys and Values&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요약 : &lt;b&gt;Keychain Item의 Class를 지정해라.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Keychain Item의 종류 (ex. 비밀번호, certificate 등)에 따라 Class를 지정해야 한다.&lt;/li&gt;
&lt;li&gt;Item의 Class는 무엇을 의미할까? 적용할 Attribute를 정하고, 시스템에게 저장할 Data를 암호화할지 말지를 결정하게 한다. &lt;br /&gt;(ex. 비밀번호는 암호화하고, certificate은 암호화하지 않는다.)&lt;/li&gt;
&lt;li&gt;key와 아래 목록의 value를 사용하여 새로 추가할 Item의 Class를 지정한다. (Item을 추가하려면 SecItemAdd 함수를 호출하면서 attributes 매개변수에 key/value 딕셔너리를 전달하면 된다.) &lt;span style=&quot;color: #ee2323;&quot;&gt;&amp;larr; 이렇게 보니 공식문서가 친절하긴 하다...&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;나중에 Item을 검색 (search)할 때는 query 딕셔너리의 동일한 key/value를 사용하면 된다. 이때 &lt;a href=&quot;https://developer.apple.com/documentation/security/1398306-secitemcopymatching&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;SecItemCopyMatching(_:_:)&lt;/span&gt;&lt;/a&gt;,&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/security/1393617-secitemupdate&quot; data-token-index=&quot;2&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;SecItemUpdate(_:_:)&lt;/span&gt;&lt;/a&gt;,&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/security/1395547-secitemdelete&quot; data-token-index=&quot;4&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;SecItemDelete(_:)&lt;/span&gt;&lt;/a&gt; 함수&amp;nbsp;중 하나를 호출한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;*아래 Topics의 Item Class의 Key와 Value로 들어갈 수 있는 k- 들을 확인할 수 있다!&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;9. Article - &lt;a href=&quot;https://developer.apple.com/documentation/security/keychain_services/keychain_items/searching_for_keychain_items&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;Searching for Keychain Items&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요약 : &lt;b&gt;개발자가 지정한 검색 criteria (기준)에 따라 Keychain Item을 검색한다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Keychain Item을 검색하려면 query 딕셔너리를 사용하면 된다. ❗&lt;b&gt;query 딕셔너리는 Keychain service API에게 어떤 Item Attribute를 검색할지, match (일치하는 항목)을 찾으면 무엇을 반환할지 알려준다.&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Attribute가 있어야 data를 찾을 수 있다! &lt;br /&gt;정확히는 Keychain Item이 아니라 &lt;b&gt;&amp;rdquo;search query에 Attribute가 있어야&amp;rdquo; 제대로 반환된다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;또한 검색을 구체화하기 위해 개발자는 query 딕셔너리를 통해 추가적인 매개변수를 지정할 수 있다. &lt;br /&gt;ex. 문자열 Attribute를 match하거나, 일치 항목의 개수를 제한하고 싶은 경우, case sensitivity (대소문자 구분 여부) 등을 제어하면 된다.&lt;/li&gt;
&lt;li&gt;Keychain에 비밀번호를 추가하는 저번 예시를 생각해보자. 사용자는 네트워크 서비스에 대한 Credentials (앱에 저장됨)를 제공하고, 앱을 사용하다가 다른 작업으로 넘어간다. 사용자가 다시 앱을 사용할 때, 앱이 계속 작동하려면 서버에서 reauthenticate이 필요할 수 있다.&lt;b&gt; 이때 앱은 Keychain을 통해 비밀번호를 load한다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(1) &lt;span&gt;❗&lt;/span&gt;Create a Search Query&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1661522157133&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let query: [String: Any] = [kSecClass as String: kSecClassInternetPassword,
                            kSecAttrServer as String: server,  // 이 Attribute를 가지는 Item을 검색하겠음
                            kSecMatchLimit as String: kSecMatchLimitOne,  // 검색 결과를 limit함
                            kSecReturnAttributes as String: true,  // Item을 찾으면 Attribute와 Data를 꺼내겠음
                            kSecReturnData as String: true]&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;query 딕셔너리를 구성하여 검색을 시작한다.&lt;/li&gt;
&lt;li&gt;이 query는 &lt;b&gt;server attribute가 일치하는 인터넷 비밀번호 Item을 검색한다. &lt;/b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;(10번 문서 요약 참고)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;kSecMatchLimit&lt;/span&gt;라는 search 매개변수를 통해 result (검색 결과로 나온 Item)를 single value로 제한한다.&lt;/b&gt; &lt;br /&gt;이 경우 keychain에서 찾은 &lt;b&gt;첫 번째 항목만&lt;/b&gt; 얻게 된다.&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;*SecItemCopyMatching 메서드 참고 - query 매개변수의 구성요소 중 &lt;b&gt;search 매개변수&lt;/b&gt;가 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;이 query는 &lt;b&gt;Item의 Attribute 및 Data를 모두 요구하고 있다.&lt;/b&gt; &lt;br /&gt;둘 다 필요한 이유는 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;kSecAttrAccount&lt;/span&gt; attribute가 사용자 name을 포함하고 있고, Item의 Data가 비밀번호 자체를 가지고 있기 때문이다. (Item Add query에서 사용한 Attribute를 말함)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(2) Initiate the Search&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1661522358725&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var item: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &amp;amp;item) // add 함수와 달리 result 매개변수가 nil이 아님
guard status != errSecItemNotFound else { throw KeychainError.noPassword } // 비밀번호를 저장한 적이 없는 경우 발생하는 error
guard status == errSecSuccess else { throw KeychainError.unhandledError(status: status) }&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;SecItemCopyMatching&lt;/span&gt; 함수를 호출하여 검색을 시작한다. &lt;span style=&quot;color: #ee2323;&quot;&gt;*본문 링크를 따라 이 함수를 먼저 알아보자. (10번 문서 요약 참고)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;먼저 반환된 status 값을 테스트해야 한다. match되는 Item이 없으면 error가 발생할 수 있다. &lt;br /&gt;ex. 주어진 server에 비밀번호를 이전에 추가하지 않은 경우&lt;/li&gt;
&lt;li&gt;이 경우 error가 발생하면 &lt;a href=&quot;https://developer.apple.com/documentation/security/errsecitemnotfound&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;errSecItemNotFound&lt;/a&gt;&amp;nbsp;result를 얻게 된다. &lt;br /&gt;이 error를 통해 앱의 로그인 Flow를 처음 진행하고 있음을 감지할 수 있다.&lt;/li&gt;
&lt;li&gt;검색에 성공하면 SecItemCopyMatching 메서드는 item 매개변수를 통해 result (검색 결과)를 제공한다. &lt;br /&gt;반환된 Item의 타입은 query의 특성에 따라 다르다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(3) Extract the Result&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;검색할 때 &lt;b&gt;여러 반환 타입을 요청했고 (Item의 Attribute 및 Data를 요청했음),&lt;/b&gt; single result로 제한했으므로&lt;b&gt; result가 딕셔너리일 것을 예상해야 한다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;result를 &lt;b&gt;[String: Any] 타입으로 타입 캐스팅&lt;/b&gt;하면, 검색해서 찾은 &lt;b&gt;Item&lt;/b&gt;에 접근할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;kSecAttrAccount&lt;/span&gt; key와 관련된 attribute 값으로부터 username을 복구할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;kSecValueData&lt;/span&gt; key를 사용하여 비밀번호 Data를 추출할 수 있다. (추출해서 String 타입으로 인코딩하면 된다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1661523514075&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;guard let existingItem = item as? [String : Any],
    let passwordData = existingItem[kSecValueData as String] as? Data,
    let password = String(data: passwordData, encoding: String.Encoding.utf8),
    let account = existingItem[kSecAttrAccount as String] as? String 
else {
    throw KeychainError.unexpectedPasswordData
}
let credentials = Credentials(username: account, password: password)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #ee2323;&quot;&gt;*위에서 말했듯이 SecItemCopyMatching 함수를 알아볼 차례이다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;10. Function - &lt;a href=&quot;https://developer.apple.com/documentation/security/1398306-secitemcopymatching&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;SecItemCopyMatching(&lt;/span&gt;&lt;/a&gt;&lt;a href=&quot;https://developer.apple.com/documentation/security/1398306-secitemcopymatching&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;:&lt;/span&gt;&lt;/a&gt;&lt;a href=&quot;https://developer.apple.com/documentation/security/1398306-secitemcopymatching&quot; data-token-index=&quot;2&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;:)&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요약 : &lt;b&gt;search query와 match되는 1개 이상의 Keychain Item을 반환하거나, 특정 Item의 Attribute를 복사한다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;query 매개변수
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SecItemAdd 메서드와 구조가 유사하다. CFDictionary 타입이다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Item의 Class&lt;span style=&quot;color: #9d9d9d;&quot;&gt; (SecItemAdd 메서드와 동일하므로 생략)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Attribute
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;검색할 Item이 가져야 할 Attribute를 지정하여 검색 범위를 좁힌다!&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Search 매개변수
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다양한 방법으로 &lt;b&gt;검색의 조건을 설정한다.&lt;/b&gt; ex. result (검색 결과)를 특정 개수의 Item으로 제한할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.apple.com/documentation/security/keychain_services/keychain_items/search_attribute_keys_and_values&quot;&gt;Search Attribute Keys and Values&lt;/a&gt;&amp;nbsp;문서를 통해 특정 조건을 설정하기 위한 key/value를 확인 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;1개 이상의 반환 타입
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;검색할 대상이 무엇인지 나타낸다. 이때 대상은 the item&amp;rsquo;s attributes, the item&amp;rsquo;s data, a reference to the data, a persistent reference to the data, or a combination of these 등으로 지정할 수 있다.&lt;br /&gt;&lt;b&gt;&amp;rarr; 검색할 대상이 항상 Data가 아닐 수 있고, 2번의 검색 범위를 좁히는 Attribute를 활용해서 Item을 찾고, 그 내부의 다른 Attribute를 반환하도록 할 수 있다는 뜻&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&amp;rarr; Item에 든 내용이 많을 수도 있는데, 그중에 원하는 값들을 선별해서 받아올 수 있다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;a style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot; href=&quot;https://developer.apple.com/documentation/security/keychain_services/keychain_items/item_return_result_keys&quot;&gt;Item Return Result Keys&lt;/a&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt; 문서를 통해 적절한 key/value를 확인 가능하다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;만약 1개 이상의 반환 타입을 지정하면, 검색 결과로 요청한 타입들을 가지고 있는 딕셔너리를 반환해준다.&lt;/li&gt;
&lt;li&gt;만약 여러 개의 result를 검색하도록 허용했다면, Item 배열 형태로 반환된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;result&amp;nbsp;매개변수
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;a style=&quot;background-color: #dddddd;&quot; href=&quot;https://developer.apple.com/documentation/swift/unsafemutablepointer&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;UnsafeMutablePointer&lt;/a&gt;&amp;lt;&lt;a style=&quot;background-color: #dddddd;&quot; href=&quot;https://developer.apple.com/documentation/corefoundation/cftyperef&quot; data-token-index=&quot;2&quot; data-reactroot=&quot;&quot;&gt;CFTypeRef&lt;/a&gt;?&amp;gt;? 타입이다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;반환 시 (SecItemCopyMatching 메서드의 반환), 검색하여 찾은&lt;b&gt; Item에 대한 참조&lt;/b&gt;이다. &lt;br /&gt;(찾은 Item의 주소를 가리키는 포인터 타입)&lt;br /&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;아래 코드를 보자.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;첫째 줄 : 변수 item의 타입은 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;CFTypeRef?&lt;/span&gt; 이다. &lt;b&gt;참조 (주소)&lt;/b&gt;를 저장하겠다는 뜻이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;둘째 줄 : &lt;span style=&quot;background-color: #dddddd;&quot;&gt;SecItemCopyMatching&lt;/span&gt; 메서드를 통해 검색하여 찾은 Keychain Item의 참조를 변수 item에 할당한다. 근데 왜 item 앞에 &amp;amp;가 붙어있을까? (포인터가 가리키는) 참조를 나타내기 위해서이다.&lt;br /&gt;&lt;b&gt;&amp;rarr; 이때 Data이든 Attribute이든 &amp;ldquo;복사값&amp;rdquo; (참조가 아니라)을 반환한다는 게 중요하다. &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&amp;amp;item&lt;/span&gt;의 원본은 키체인에 있다!&lt;/b&gt;&lt;br /&gt;&lt;b&gt;키체인 참조를 직접 주면 함부로 수정할 수 있어서 위험하기 때문이다.&lt;/b&gt; &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1661524354736&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var item: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &amp;amp;item)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;반환값
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OSStatus 타입이다. result code이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Discussion
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;default로 첫 번째로 match된 Item을 반환한다. &lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;*나머지 내용은 다음에...&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #ee2323;&quot;&gt;*아까 보기로 했던 링크를 확인해보자.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;11. Article - &lt;a href=&quot;https://developer.apple.com/documentation/security/keychain_services/keychain_items/updating_and_deleting_keychain_items&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;Updating and Deleting Keychain Items&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요약 : &lt;b&gt;사용자 데이터가 변경됐을 때 Keychain Item을 수정한다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Overview
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자가 앱 외부에서 비밀번호를 변경 (ex. 웹 사이트에서 변경)하면, 앱 Bundle에 저장된 Keychain의 비밀번호와 일치하지 않으므로 로그인에 실패하게 된다. 이 경우 &lt;b&gt;사용자로부터 새로운 Credentials를 입력받아 Keychain에 변경사항을 반영해야 한다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;이때 SecItemAdd 메서드를 사용하면 안 된다. (기존에 Keychain에 등록해둔 Item과 동일한 Attributes를 가지는 Item은 공존할 수 없기 때문이다.) 기존의 Item을 업데이트해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;span&gt;&lt;/span&gt;(1) Prepare a Search Query and New Attributes&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1661524641317&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// search query
let query: [String: Any] = [kSecClass as String: kSecClassInternetPassword,  
                            kSecAttrServer as String: server]

// 업데이트할 새로운 Item
let account = credentials.username
let password = credentials.password.data(using: String.Encoding.utf8)! // String-&amp;gt;Data로 전환한 비밀번호
let attributes: [String: Any] = [kSecAttrAccount as String: account,   
                                 kSecValueData as String: password]&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Item을 업데이트하려면 두 가지가 필요하다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;먼저 1) search query를 통해 Item을 찾아야 한다.&lt;/b&gt; &lt;br /&gt;(이건 implicit search이고, SecItemCopyMatching 메서드는 explicit search라고 부름) &lt;br /&gt;위 예시는 kSecClass, kSecAttrServer 두 가지에 부합하는 Item을 검색하도록 했다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;그다음 2) 업데이트할 새로운 Item 정보가 담긴 딕셔너리를 준비해야 한다.&lt;/b&gt; &lt;br /&gt;이때 사용자의 Credentials 중에서 account 정보는 동일하고, password만 바꾸는 것도 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;(2) Execute an Update&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1661524715832&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let status = SecItemUpdate(query as CFDictionary, attributes as CFDictionary)
guard status != errSecItemNotFound else { throw KeychainError.noPassword }
guard status == errSecSuccess else { throw KeychainError.unhandledError(status: status) }&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이제 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;SecItemUpdate&lt;/span&gt; 메서드를 호출하면, 작성한 Attribute에 해당하는 모든 Item이 업데이트된다. &lt;span style=&quot;color: #ee2323;&quot;&gt;(12번 문서 요약 참고)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;마찬가지로 반환된 OSStatus를 확인해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;(3) Delete Items That You No Longer Need&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1661524750967&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let status = SecItemDelete(query as CFDictionary)
guard status == errSecSuccess || status == errSecItemNotFound else { throw KeychainError.unhandledError(status: status) }&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;필요 없는 Item은 Keychain에서 삭제하면 된다. (ex. 사용자가 앱에서 서버 로그아웃할 때)&lt;/li&gt;
&lt;li&gt;Item 업데이트할 때 사용한 search query를 사용해서 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;SecItemDelete&lt;/span&gt; 메서드를 호출하면 된다. &lt;span style=&quot;color: #ee2323;&quot;&gt;(13번 문서 요약 참고)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;default로 Keychain service는 search query에 match되는 모든 Item을 삭제한다. &lt;br /&gt;특정 Item만 삭제하려면 search query에 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;kSecMatchItemList&lt;/span&gt; key를 추가하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;*마지막으로 update / delete 관련 메서드를 보자.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;12. Function - &lt;a href=&quot;https://developer.apple.com/documentation/security/1393617-secitemupdate&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;SecItemUpdate(&lt;/span&gt;&lt;/a&gt;&lt;a href=&quot;https://developer.apple.com/documentation/security/1393617-secitemupdate&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;::&lt;/span&gt;&lt;/a&gt;&lt;a href=&quot;https://developer.apple.com/documentation/security/1393617-secitemupdate&quot; data-token-index=&quot;2&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;)&lt;/span&gt;&lt;/a&gt;&amp;nbsp;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요약 : &lt;b&gt;search query에 match되는 Item을 수정한다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;query 매개변수
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;(Item을 add/search하는 메서드와 마찬가지로) &lt;span style=&quot;background-color: #dddddd;&quot;&gt;CFDictionary&lt;/span&gt; 타입이다.&lt;/li&gt;
&lt;li&gt;업데이트하려는 Keychain Item에 대한&lt;b&gt; search query&lt;/b&gt;이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;attributesToUpdate 매개변수
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;CFDictionary&lt;/span&gt; 타입이다.&lt;/li&gt;
&lt;li&gt;1) 업데이트하려는 값에 대한 Attribute, 2) 새로운 값을 포함하는 딕셔너리이다.&lt;/li&gt;
&lt;li&gt;meta Attribute (???)는 이 딕셔너리에 넣을 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;반환값
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;result code이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;13. Function - &lt;a href=&quot;https://developer.apple.com/documentation/security/1395547-secitemdelete&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;SecItemDelete(_:)&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요약 : &lt;b&gt;search query에 match되는 Item을 삭제한다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;query 매개변수
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;삭제하려는 Keychain Item에 대한&lt;b&gt; search query&lt;/b&gt;이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;반환값
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;result code이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Discussion
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;default로 search query에 해당하는 모든 Item을 삭제한다.&lt;/li&gt;
&lt;li&gt;key를 추가하면 특정 조건의 Item만 삭제하도록 변경할 수 있다.&lt;/li&gt;
&lt;/ul&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Apple Developer Documentation &amp;gt; &lt;a href=&quot;https://developer.apple.com/documentation/security/keychain_services&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Keychain&amp;nbsp;Services&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Apple Developer Documentation &amp;gt; &lt;a href=&quot;https://developer.apple.com/documentation/security/keychain_services/keychain_items&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;Keychain Item&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Apple Developer Documentation &amp;gt; &lt;a href=&quot;https://developer.apple.com/documentation/security/keychain_services/keychains&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;Keychains&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Apple Developer Documentation &amp;gt; &lt;a href=&quot;https://developer.apple.com/documentation/security/keychain_services/access_control_lists&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;Access Control Lists&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Apple Developer Documentation &amp;gt; Article - &lt;a href=&quot;https://developer.apple.com/documentation/security/keychain_services/keychain_items/using_the_keychain_to_manage_user_secrets&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;Using the Keychain to Manage User Secrets&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Apple Developer Documentation &amp;gt; Class - &lt;a href=&quot;https://developer.apple.com/documentation/security/seckeychainitem&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;SecKeychainItem&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Apple Developer Documentation &amp;gt; &lt;a href=&quot;https://developer.apple.com/documentation/security/1394040-seckeychainitemgettypeid&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;SecKeychainItemGetTypeID()&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Apple Developer Documentation &amp;gt; Article - &lt;a href=&quot;https://developer.apple.com/documentation/security/keychain_services/keychain_items/adding_a_password_to_the_keychain&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;Adding a Password to the Keychain&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Apple Developer Documentation &amp;gt; &lt;a href=&quot;https://developer.apple.com/documentation/security/1401659-secitemadd&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;SecItemAdd(_:_:)&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Apple Developer Documentation &amp;gt; &lt;a href=&quot;https://developer.apple.com/documentation/security/keychain_services/keychain_items/item_class_keys_and_values&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;Item Class Keys and Values&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Apple Developer Documentation &amp;gt; Article - &lt;a href=&quot;https://developer.apple.com/documentation/security/keychain_services/keychain_items/searching_for_keychain_items&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;Searching for Keychain Items&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Apple Developer Documentation &amp;gt; &lt;a href=&quot;https://developer.apple.com/documentation/security/1398306-secitemcopymatching&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;SecItemCopyMatching(&lt;/span&gt;&lt;/a&gt;&lt;a href=&quot;https://developer.apple.com/documentation/security/1398306-secitemcopymatching&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;:&lt;/span&gt;&lt;/a&gt;&lt;a href=&quot;https://developer.apple.com/documentation/security/1398306-secitemcopymatching&quot; data-token-index=&quot;2&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;:)&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Apple Developer Documentation &amp;gt; Article - &lt;a href=&quot;https://developer.apple.com/documentation/security/keychain_services/keychain_items/updating_and_deleting_keychain_items&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;Updating and Deleting Keychain Items&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Apple Developer Documentation &amp;gt; &lt;a href=&quot;https://developer.apple.com/documentation/security/1393617-secitemupdate&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;SecItemUpdate(&lt;/span&gt;&lt;/a&gt;&lt;a href=&quot;https://developer.apple.com/documentation/security/1393617-secitemupdate&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;::&lt;/span&gt;&lt;/a&gt;&lt;a href=&quot;https://developer.apple.com/documentation/security/1393617-secitemupdate&quot; data-token-index=&quot;2&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;)&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Apple Developer Documentation &amp;gt; &lt;a href=&quot;https://developer.apple.com/documentation/security/1395547-secitemdelete&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;SecItemDelete(_:)&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>iOS/영문 공식문서 뜯어보기-iOS</category>
      <category>Apple</category>
      <category>IOS</category>
      <category>Keychain</category>
      <category>공식문서</category>
      <category>공식문서읽는방법</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/48</guid>
      <comments>https://applecider2020.tistory.com/48#entry48comment</comments>
      <pubDate>Fri, 26 Aug 2022 23:53:37 +0900</pubDate>
    </item>
    <item>
      <title>[SwiftLint] SPM으로 SwiftLint 설치 - Homebrew 필요 (SPM으로 못함)</title>
      <link>https://applecider2020.tistory.com/47</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;SwiftLint란?&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;SwiftLint는 &lt;a href=&quot;https://github.com/realm/SwiftLint&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;라이브러리&lt;/a&gt;이다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이걸 설치하면 &lt;b&gt;Xcode에서 코딩 컨벤션 관련 오류를 알아서 잡아내는&lt;/b&gt; &lt;b&gt;맞춤법 검사기&lt;/b&gt; 기능을 한다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;SwiftLint 설치 방법은?&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;SwiftLint를 설치하는 방법은 크게 CocoaPods를 사용할 건지, 사용하지 않을 건지로 나뉜다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;개인적으로 나는 최근 CocoaPods보다 &lt;a href=&quot;https://www.swift.org/package-manager/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;SPM (Swift Package Manager)&lt;/a&gt;을 더 활발하게 사용하고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;별도로 pod install을 할 필요가 없고, 코드리뷰를 할 때 리뷰어도 추가 작업 없이 코드를 확인할 수 있다는 장점이 너무 크기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;*그래도 CocoaPods으로 설치하고 싶다면&lt;/span&gt; &lt;a href=&quot;https://zeddios.tistory.com/447&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Zeddios님 링크&lt;/a&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;두 방법 모두 설치과정은&amp;nbsp;&lt;a href=&quot;https://github.com/realm/SwiftLint&quot;&gt;SwiftLint&amp;nbsp;&lt;/a&gt;&lt;a href=&quot;https://github.com/realm/SwiftLint&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공식문서&lt;/a&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;하지만 나는 헤맸으므로.. 그 과정을 기록해본다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;먼저 아래 과정이 필요하다&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. brew 설치&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Homebrew 설치방법은 &lt;a href=&quot;https://coconuts.tistory.com/161&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이 링크&lt;/a&gt;를 참고&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. brew를 통해 SwiftLint 설치&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://github.com/realm/SwiftLint/blob/master/README_KR.md&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이 링크&lt;/a&gt;는 번역본이라 조금 쉽다. 여기에 나온 대로 터미널에서 아래 코드만 넣으면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1660577232222&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;brew install swiftlint&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3. Xcode 프로젝트에 적용&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;바로 될 줄 알았지만 Xcode가 SwiftLint를 찾지 못하는 문제가 발생한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;많은 사람들이 겪은 문제라서 해답이 있다. 이 작업만 하면 끝난다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://github.com/realm/SwiftLint/issues/3734&quot;&gt;https://github.com/realm/SwiftLint/issues/3734&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;br /&gt;&lt;b&gt;Targets &amp;gt; 프로젝트 폴더 &amp;gt; 상단의 Build Phases &amp;gt; + 버튼 &amp;gt; New Run Script Phase &amp;gt; Run Script&lt;/b&gt;에 아래 코드를 복붙하면 된다.&lt;br /&gt;brew path에 위치한 SwiftLint를 찾아가도록 하는 명령어이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2156&quot; data-origin-height=&quot;1392&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ctP8Xm/btrJDhqGLy0/iKYhlipIkQ4tVfBxCu2tB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ctP8Xm/btrJDhqGLy0/iKYhlipIkQ4tVfBxCu2tB0/img.png&quot; data-alt=&quot;Target의 Build Phases에서 Run Script를 추가해야 한다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ctP8Xm/btrJDhqGLy0/iKYhlipIkQ4tVfBxCu2tB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FctP8Xm%2FbtrJDhqGLy0%2FiKYhlipIkQ4tVfBxCu2tB0%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;2156&quot; height=&quot;1392&quot; data-origin-width=&quot;2156&quot; data-origin-height=&quot;1392&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Target의 Build Phases에서 Run Script를 추가해야 한다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;578&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p4pom/btrJD3UyxOe/LU0eI3hWwMTrjjtsYcdcJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p4pom/btrJD3UyxOe/LU0eI3hWwMTrjjtsYcdcJ0/img.png&quot; data-alt=&quot;+ 버튼을 클릭하고 New Run Script Phase를 클릭하면 &amp;quot;Run Script&amp;quot;가 생긴다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p4pom/btrJD3UyxOe/LU0eI3hWwMTrjjtsYcdcJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp4pom%2FbtrJD3UyxOe%2FLU0eI3hWwMTrjjtsYcdcJ0%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;1268&quot; height=&quot;578&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;578&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;+ 버튼을 클릭하고 New Run Script Phase를 클릭하면 &quot;Run Script&quot;가 생긴다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1659235786266&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if test -d &quot;/opt/homebrew/bin/&quot;; then
  PATH=&quot;/opt/homebrew/bin/:${PATH}&quot;
fi

export PATH

if which swiftlint &amp;gt;/dev/null; then
  swiftlint
else
  echo &quot;warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint&quot;
fi&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;4. &lt;span style=&quot;background-color: #dddddd;&quot;&gt;.swiftlint.yml&lt;/span&gt; 파일 생성&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;맞춤법을 검사할 때 적용할 규칙을 정하는 파일이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;새 파일 &amp;gt; iOS &amp;gt; &lt;span style=&quot;color: #ee2323;&quot;&gt;Other 항목의 Empty 파일&lt;/span&gt;을 선택하고 파일명을 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;.swiftlint.yml&lt;/span&gt;&amp;nbsp;로 입력한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #ee2323;&quot;&gt;주의 - Documentation 항목의 Empty 파일을 하면 안 된다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그럼 아래처럼 경고창이 뜨는데, &lt;span style=&quot;background-color: #dddddd;&quot;&gt;Use &quot;.&quot;&lt;/span&gt;을 클릭하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;546&quot; data-origin-height=&quot;518&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t7pUV/btrJKsyLvV1/4IBPiLkSe2k1iixtnWc6Rk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t7pUV/btrJKsyLvV1/4IBPiLkSe2k1iixtnWc6Rk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t7pUV/btrJKsyLvV1/4IBPiLkSe2k1iixtnWc6Rk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft7pUV%2FbtrJKsyLvV1%2F4IBPiLkSe2k1iixtnWc6Rk%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;300&quot; height=&quot;285&quot; data-origin-width=&quot;546&quot; data-origin-height=&quot;518&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;5. 정상 작동하는지 확인&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&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: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아래 컴파일러가 경고 메시지를 띄운 건 &lt;b&gt;의미 없는 줄바꿈을 2번 이상했기 때문이다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2054&quot; data-origin-height=&quot;402&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbonPv/btrJLTiCDbD/NTAjnjSm86kKJ8Yi9PTkoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbonPv/btrJLTiCDbD/NTAjnjSm86kKJ8Yi9PTkoK/img.png&quot; data-alt=&quot; SwiftLint가 코드 컨벤션 규칙을 위반했다고 알려준다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbonPv/btrJLTiCDbD/NTAjnjSm86kKJ8Yi9PTkoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbonPv%2FbtrJLTiCDbD%2FNTAjnjSm86kKJ8Yi9PTkoK%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;2054&quot; height=&quot;402&quot; data-origin-width=&quot;2054&quot; data-origin-height=&quot;402&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt; SwiftLint가 코드 컨벤션 규칙을 위반했다고 알려준다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;*이 규칙을 적용하고 싶지 않으면?&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;.swiftlint.yml&lt;span&gt;&amp;nbsp;&lt;/span&gt;파일에 경고 메시지 맨 뒤에 표시된&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;vertical_whitespace&lt;/span&gt;를 넣으면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;6. 규칙 추가/제거&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;내가 주로 사용하는 커스텀 룰을 적어봤다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아까 만든&amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;.swiftlint.yml&lt;/span&gt; 파일에 작성하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;나머지 규칙은 검색하면 다양하게 나온다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1660578770535&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;disabled_rules:  // 아래 룰은 검사 대상에서 제외하겠다는 뜻
- trailing_whitespace
- xctfail_message
- function_parameter_count
- legacy_constructor
- force_cast
- trailing_comma
- force_try
- identifier_name

type_body_length: 250  // 타입/함수/파일 등의 코드길이 제한을 변경하겠다는 뜻
line_length: 120
function_body_length: 50
file_length: 500&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;GitHub &amp;gt; &lt;a href=&quot;https://github.com/realm/SwiftLint&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;SwiftLint&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Swift.org &amp;gt;&amp;nbsp;&lt;a href=&quot;https://www.swift.org/package-manager/&quot;&gt;SPM&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;기타 링크는 본문 참고&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>비전공자용 노력/개발 툴</category>
      <category>BREW</category>
      <category>SPM</category>
      <category>swiftlint</category>
      <category>코드맞춤법검사</category>
      <category>코딩컨벤션</category>
      <category>코딩컨벤션검사</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/47</guid>
      <comments>https://applecider2020.tistory.com/47#entry47comment</comments>
      <pubDate>Tue, 16 Aug 2022 01:05:02 +0900</pubDate>
    </item>
    <item>
      <title>[인디 앱개발] ✨ 앱 기획부터 출시까지 참고한 링크 - 앱 기획, 디자인, 개발, 배포 준비</title>
      <link>https://applecider2020.tistory.com/46</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;최근 2개월 동안 개발했던 앱을 출시하게 됐다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;기획부터 시작해서 디자인, 서버 개발, iOS 개발에 직접 관여하며 진행했던 프로젝트인 만큼 재밌었고 배운 게 많았다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그 과정에서 단계별로 고려했던 점과 유용한 사이트를 공유하려고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;✏️ 기획&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;프로젝트 실행 목적과 목표를 설정한다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이때 목적은 취준용 앱 출시 경험일 수도, 평소 만들고 싶었던 서비스를 구현하는 것일 수도 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;팀원, 진행 기간, 출시 목표일정, 타깃 사용자 등을 설정한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;개발자 지인이 있다면 기술 난이도에 대해 조언을 구한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;앱 출시 이후 업데이트를 진행해도 되므로 일단 최소한의 기능을 담은 &lt;b&gt;MVP&lt;/b&gt;를 고민한다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;과욕을 부리면 나중에 꼭 후회하게 된다. 최소한의 기능만 구현해도 예상보다 오래 걸린다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;&lt;b&gt;디스코드 채널 개설&lt;/b&gt;,&amp;nbsp;&lt;/b&gt;Notion, 구글 스프레드시트, 구글 드라이브, &lt;a href=&quot;https://hackmd.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;HackMD&lt;/a&gt; (다중커서 작업 가능)을 통해 팀의 소통 창구를 만든다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;히스토리 관리가 어려운 카카오톡은 비추&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;앱의 복잡도에 따라 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;와이어프레임&lt;/b&gt;&lt;/span&gt; (필수), 정보구조도, WBS, 프로토타입 등을 활용한다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;기능이 복잡하지 않다면 &lt;a href=&quot;http://figma.com&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Figma&lt;/a&gt;를 사용하여 와이어프레임만 나타내도 충분하다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;프로토타입은&lt;/span&gt; &lt;a href=&quot;https://www.protopie.io/learn/docs/ko/introducing-protopie/getting-started&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;프로토파이&lt;/a&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;가 유명하다고 한다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;기획문서의 종류는 &lt;/span&gt;&lt;a href=&quot;https://www.castingn.com/sourcing/kkultip_detail/153&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;이 블로그&lt;/a&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;, 앱 설계서 작성방법은&lt;/span&gt; &lt;a href=&quot;https://brunch.co.kr/@supernova9/165&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이 블로그&lt;/a&gt;&lt;span style=&quot;color: #333333;&quot;&gt;, 화면설계 및 프로토타이핑 툴의 종류는&lt;/span&gt; &lt;a href=&quot;https://yslab.kr/94&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이 블로그&lt;/a&gt;&lt;span style=&quot;color: #333333;&quot;&gt;를 참고&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1003&quot; data-origin-height=&quot;706&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfbSWR/btrHK0E5l6X/tCKw1q4i9UI7f9fKD7MKkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfbSWR/btrHK0E5l6X/tCKw1q4i9UI7f9fKD7MKkk/img.png&quot; data-alt=&quot;예시 - 우리뭐먹지 프로젝트 Figma&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfbSWR/btrHK0E5l6X/tCKw1q4i9UI7f9fKD7MKkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfbSWR%2FbtrHK0E5l6X%2FtCKw1q4i9UI7f9fKD7MKkk%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;600&quot; height=&quot;422&quot; data-origin-width=&quot;1003&quot; data-origin-height=&quot;706&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;예시 - 우리뭐먹지 프로젝트 Figma&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  UX / UI 디자인&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://brunch.co.kr/@jinha/9#&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&amp;lt;'네이버웹툰' 모바일 서비스 UX를 분석해보자&amp;gt; 시리즈&lt;/a&gt; : 네이버웹툰 앱을 예시로 UX 관련 고려사항을 쉽게 설명한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://developer.apple.com/design/human-interface-guidelines/guidelines/overview/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Apple의 HIG 문서&lt;/a&gt; (Human Interface Guidelines) : Apple의 디자인 원칙을 설명하는 문서이며, 디자이너/기획자/개발자 모두 읽어야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;디자인 툴은 Figma (무료), AdobeXD (유료) 등을 많이 사용한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;앱의 주요 기능을 고려해서 먼저&lt;b&gt; 대략적인 컨셉&lt;/b&gt;을 정한다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;게임 앱이라면 컴포넌트를 둥글고, 포인트 컬러를 2~3개 사용하는 귀여운 컨셉으로&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이커머스 앱이라면 선이 얇고, 미니멀한 디자인을 사용하는 세련된 컨셉으로 잡는 것이 좋다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;컨셉을 정했다면 컴포넌트, 탭바, 네비게이션바 등의 구체적인 형태를 잡는다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;비슷한 앱을 여러 개 다운받아서 살펴보거나,&amp;nbsp;&lt;a href=&quot;https://www.pinterest.co.kr/&quot;&gt;Pinterest&lt;/a&gt;에서 이미지를 검색해보자.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(검색 키워드 : App design, Mobile app UX/UI 등)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;포인트 컬러를 정할 때 Pinterest에서 &lt;b&gt;Color palette&lt;/b&gt;로 검색하면 유용하다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;컴포넌트가 너무 밋밋해 보이거나, 특정 버튼을 강조하고 싶다면 그림자 효과를 주면 좋다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;기본 한글 폰트가 구려 보여서 커스텀 폰트를 사용하고 싶다면 &lt;a href=&quot;https://fonts.google.com/noto/fonts?noto.lang=ko_Kore&amp;amp;noto.continent=Asia&amp;amp;noto.script=Kore&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;구글 무료 폰트&lt;/a&gt;, &lt;a href=&quot;https://hangeul.naver.com/font&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;네이버 무료 폰트&lt;/a&gt;를 사용하면 된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이미지 또는 아이콘이 필요하면 Unsplash, Shutterstock 등의 &lt;b&gt;무료 저작권 사이트&lt;/b&gt;를 활용한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;애니메이션은 &lt;b&gt;Lottie&lt;/b&gt;가 유용하다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1011&quot; data-origin-height=&quot;907&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dF1AvD/btrHKdqZcbr/90U8kyCI03d1RpyQGDKu9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dF1AvD/btrHKdqZcbr/90U8kyCI03d1RpyQGDKu9K/img.png&quot; data-alt=&quot;예시 - 우리뭐먹지 프로젝트 AdobeXD&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dF1AvD/btrHKdqZcbr/90U8kyCI03d1RpyQGDKu9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdF1AvD%2FbtrHKdqZcbr%2F90U8kyCI03d1RpyQGDKu9K%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;600&quot; height=&quot;538&quot; data-origin-width=&quot;1011&quot; data-origin-height=&quot;907&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;예시 - 우리뭐먹지 프로젝트 AdobeXD&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  서버 개발&amp;nbsp;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;흔한 아이디어인데 출시된 앱이 별로 없다면, 그만한 이유가 있을 수 있다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;대표적인 이유는 &lt;b&gt;DB 또는 서버 API가 구축이 안되어 있는 경우&lt;/b&gt;이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;우리뭐먹지 프로젝트의 경우에도 처음부터 메뉴 DB를 구축하고, 추천 알고리즘을 설계하고, 서버 API를 만드는 작업을 모두 해야 해서 시간이 오래 걸렸다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아이디어를 구상하는 단계에서&amp;nbsp;&lt;b&gt;이미 만들어진 &lt;a href=&quot;https://github.com/dl0312/open-apis-korea&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Open API 목록&lt;/a&gt;을 살펴보고 영감을 얻는 게 좋다.&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  iOS 개발&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;작업을 세부적으로 구분하여 STEP별 명세서를 작성한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;개발 도중에 디자인이나 서버 API가 변경되는 경우가 빈번하므로&lt;/span&gt; 디자이너 및 기획자와 긴밀히 소통하고, &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;개발 과정에서도 기능 변화에 대비하여 유지보수가 용이하고 가독성이 좋은 코드를 작성한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;StyleShare,&amp;nbsp;에어비앤비&amp;nbsp;등의&amp;nbsp;&lt;b&gt;Code&amp;nbsp;Convention&lt;/b&gt;을&amp;nbsp;참고한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;프로젝트 개발기간 동안 MacOS, Xcode가 자동 업데이트되지 않도록 설정한다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Xcode 버전은 팀원과 동일한 것이 안전하고, Minor 버전이 0인 것은 피한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;GitHub의 &lt;b&gt;PR 기능&lt;/b&gt;을 통해 Feature마다 배경, 작업 내용, 테스트 방법, 리뷰 노트, 스크린샷을 남겨두면 협업에 큰 도움이 된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;GitHub의 &lt;b&gt;Wiki 기능&lt;/b&gt;을 통해 프로젝트 일정 및 아카이브 문서를 관리하면 좋다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;브랜치 관리는 &lt;b&gt;Git Flow&lt;/b&gt;를 추천한다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Git Flow 사용방법은 지난 포스트 &lt;a href=&quot;https://applecider2020.tistory.com/36&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Git Flow로&amp;nbsp;브랜치&amp;nbsp;관리하기&lt;/a&gt;를 참고&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;개발 중에 UX/UI 관련 &lt;b&gt;커스텀 컴포턴트 또는 애니메이션&lt;/b&gt; 등을 고민할 때도 &lt;b&gt;Pinterest&lt;/b&gt;가 유용하다.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(검색 키워드 : Tinder like swipe, Card swipe animation 등)&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;난이도가 있는 작업은 누군가 이미 구현해서 공개해뒀을 가능성이 높다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;GitHub에서 여러 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;라이브러리의 소스코드&lt;/b&gt;&lt;/span&gt;를 뒤져보면 힌트를 얻을 수 있다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(검색 키워드 : Menu underbar, Underline menu bar, Custom segmentedcontrol 등)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;915&quot; data-origin-height=&quot;599&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Apyv2/btrHL23MDOo/iO2KvKEcGzDx99qTxUxRPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Apyv2/btrHL23MDOo/iO2KvKEcGzDx99qTxUxRPk/img.png&quot; data-alt=&quot;예시 - 우리뭐먹지 프로젝트 PR&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Apyv2/btrHL23MDOo/iO2KvKEcGzDx99qTxUxRPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FApyv2%2FbtrHL23MDOo%2FiO2KvKEcGzDx99qTxUxRPk%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;600&quot; height=&quot;393&quot; data-origin-width=&quot;915&quot; data-origin-height=&quot;599&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;예시 - 우리뭐먹지 프로젝트 PR&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  배포 준비 (AppStore 심사 체크리스트)&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://developer.apple.com/kr/app-store/review/guidelines/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Apple 공식문서의 AppStore 심사지침&lt;/a&gt;&amp;nbsp;: Apple이 민감하게 심사하는 요소를 알 수 있다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;*심사지침 요약은 지난 포스트&lt;/span&gt; &lt;a href=&quot;https://applecider2020.tistory.com/35&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;헬스케어&amp;nbsp;기능에도&amp;nbsp;민감하다&lt;/a&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;를 참고&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;개발자 계정 (개인 $99, 팀 $299) : 1~4번은 AppStore Connect 사이트를 통해 생성한다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;*상세 내용은&lt;/span&gt; &lt;a href=&quot;https://hiseung7.tistory.com/entry/iOS-%EC%95%B1-%EC%A0%9C%EC%B6%9C-%EB%B0%B0%ED%8F%AC-%EC%88%9C%EC%84%9C?category=835229&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이 블로그&lt;/a&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;를 참고&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;인증서 (개발용, 배포용)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;App ID&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Provisioning Profile (개발용, 배포용)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;개인정보 처리방침 : &lt;a href=&quot;https://www.privacy.go.kr/a3sc/per/inf/perInfStep01.do&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;개인정보보호포털&lt;/a&gt; 사이트에서 생성한다. URL을 제출해야 하므로 Notion으로 만드는 것을 추천한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Test Flight (생략 가능)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;앱 아이콘 (로고) &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- 1024*1024 이미지 1개 (png만 가능)를 준비한다.&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;- Xcode 14 미만인 경우, 또는 크기별로 이미지 디테일을 다르게 표현하고 싶은 경우에는&lt;/span&gt;&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/Xcode/configuring-your-app-icon&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;11개의 크기별 이미지를 준비한다.&lt;/a&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&amp;nbsp; &amp;nbsp;*이미지 세트를&amp;nbsp;자동 생성해주는&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; text-align: left;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; text-align: left;&quot; href=&quot;https://appicon.co/&quot;&gt;사이트&lt;/a&gt; &lt;span style=&quot;color: #9d9d9d;&quot;&gt;참고&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Apple이 알아서 모서리를 둥글게 만들어주므로 &lt;b&gt;배경이 정사각형&lt;/b&gt; 형태여야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;AppStore 미리보기 영상 및 스크린샷&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- 영상은 생략 가능하고, &lt;b&gt;스크린샷은 필수이다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Simulator 화면의 캡쳐본을 제출해도 되고, &lt;b&gt;MockUp 툴&lt;/b&gt;을 활용해서 화면별 기능을 설명해주는 이미지를 만들어 제출해도 된다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- 이미지 크기 때문에 심사 탈락하는 경우가 많다고 한다. &lt;a href=&quot;https://help.apple.com/app-store-connect/#/devd274dd925&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Apple 공식문서의 스크린샷 사양&lt;/a&gt;을 꼭 확인하자.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;- iOS만 지원한다면 총 2개 세트 (아래 1~2번), iOS/iPad를 모두 지원한다면 총 4개 세트 (아래 1~4번)를 준비해야 한다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&amp;nbsp; &amp;nbsp;*요구사항 항목에 &quot;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;앱이 iPhone에서 실행되는 경우 필요&lt;/span&gt;&quot;라고 되어있으면 필수적으로 제출해야 한다는 뜻이다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&amp;nbsp;&lt;span style=&quot;color: #ee2323;&quot;&gt; &amp;nbsp;1) iPhone 6.5형 예시&lt;/span&gt; : &lt;span style=&quot;background-color: #ffffff; color: #484848;&quot;&gt;iPhone 13 Pro Max, &lt;span style=&quot;background-color: #ffffff; color: #484848;&quot;&gt;1284 x 2778 픽셀, 실기기처럼 홈버튼이 아닌 노치가 있으면 통과한다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&amp;nbsp; &amp;nbsp;2) iPhone 5.5형 예시&lt;/span&gt; : &lt;span style=&quot;background-color: #ffffff; color: #484848;&quot;&gt;iPhone 8 Plus, &lt;/span&gt; &lt;span style=&quot;background-color: #ffffff; color: #484848;&quot;&gt;1242 x 2208 픽셀, 실기기처럼 홈버튼이 있으면 통과한다.&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&amp;nbsp; &amp;nbsp;3) iPad &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #484848;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;12.9형 예시&lt;/span&gt; : iPad Pro(4세대, 3세대), &lt;span style=&quot;background-color: #ffffff; color: #484848;&quot;&gt;2048 x 2732 픽셀&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&amp;nbsp; &amp;nbsp;4) iPad &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #484848;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;12.9형 예시&lt;/span&gt; : iPad Pro(2세대), 2048&amp;nbsp;x&amp;nbsp;2732&amp;nbsp;픽셀&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;App 기본 정보 : 앱 이름, 부제, 카테고리, 가격 등급, 개발자 연락처&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;AppStore 마케팅 문구 : &lt;b&gt;프로모션 텍스트, 설명 (더보기 항목), 등록 키워드&lt;/b&gt; 등이 필요하다.&lt;br /&gt;- 마케팅 경험이 없다면 여기서 시간 소요가 크다. 주변 문과 친구들의 도움을 받으면 좋다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;*상세 내용은&lt;/span&gt; &lt;a href=&quot;https://devsrkim.tistory.com/entry/앱-스토어-앱-배포-하기&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이 블로그&lt;/a&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;를 참고&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;앱 사용방법을 나타낸&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt; 영상&lt;/b&gt;&lt;/span&gt;을 참고하면 보다 신속하게 심사결과를 받을 수 있다. (생략 가능)&lt;br /&gt;실기기로 앱을 실행화면을 녹화한 영상을 제출했었는데 심사요청 10분 만에 승인을 받을 수 있었다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1691652321468&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Configuring your app icon | Apple Developer Documentation&quot; data-og-description=&quot;Add app icon variations to represent your app in places such as Settings, search results, and the App Store.&quot; data-og-host=&quot;developer.apple.com&quot; data-og-source-url=&quot;https://developer.apple.com/documentation/Xcode/configuring-your-app-icon&quot; data-og-url=&quot;https://docs.developer.apple.com/documentation/Xcode/configuring-your-app-icon&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/6SiYN/hyTByCHFe5/zwHhAulwU10cjlbdLWM0m1/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bb8lM9/hyTBCyiVWR/OiDwnPWumLE8bqoEhRB4N1/img.jpg?width=1024&amp;amp;height=512&amp;amp;face=0_0_1024_512&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/Xcode/configuring-your-app-icon&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.apple.com/documentation/Xcode/configuring-your-app-icon&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/6SiYN/hyTByCHFe5/zwHhAulwU10cjlbdLWM0m1/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bb8lM9/hyTBCyiVWR/OiDwnPWumLE8bqoEhRB4N1/img.jpg?width=1024&amp;amp;height=512&amp;amp;face=0_0_1024_512');&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;Configuring your app icon | Apple Developer Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Add app icon variations to represent your app in places such as Settings, search results, and the App Store.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.apple.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;figure id=&quot;og_1691652317399&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Configuring your app icon | Apple Developer Documentation&quot; data-og-description=&quot;Add app icon variations to represent your app in places such as Settings, search results, and the App Store.&quot; data-og-host=&quot;developer.apple.com&quot; data-og-source-url=&quot;https://developer.apple.com/documentation/Xcode/configuring-your-app-icon&quot; data-og-url=&quot;https://docs.developer.apple.com/documentation/Xcode/configuring-your-app-icon&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/6SiYN/hyTByCHFe5/zwHhAulwU10cjlbdLWM0m1/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bb8lM9/hyTBCyiVWR/OiDwnPWumLE8bqoEhRB4N1/img.jpg?width=1024&amp;amp;height=512&amp;amp;face=0_0_1024_512&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/Xcode/configuring-your-app-icon&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.apple.com/documentation/Xcode/configuring-your-app-icon&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/6SiYN/hyTByCHFe5/zwHhAulwU10cjlbdLWM0m1/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bb8lM9/hyTBCyiVWR/OiDwnPWumLE8bqoEhRB4N1/img.jpg?width=1024&amp;amp;height=512&amp;amp;face=0_0_1024_512');&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;Configuring your app icon | Apple Developer Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Add app icon variations to represent your app in places such as Settings, search results, and the App Store.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.apple.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d6QDlY/btrHKcMnyS6/E0PWYRthuEgx0TbPUrvu3k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d6QDlY/btrHKcMnyS6/E0PWYRthuEgx0TbPUrvu3k/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;665&quot; data-origin-height=&quot;1440&quot; data-filename=&quot;KakaoTalk_Photo_2022-07-20-17-36-12.jpeg&quot; style=&quot;width: 37.6874%; margin-right: 10px;&quot; data-widthpercent=&quot;38.13&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d6QDlY/btrHKcMnyS6/E0PWYRthuEgx0TbPUrvu3k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd6QDlY%2FbtrHKcMnyS6%2FE0PWYRthuEgx0TbPUrvu3k%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;665&quot; height=&quot;1440&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TvDtp/btrHNLgcOuq/5fhftDlKpXHCuU5lk1OYq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TvDtp/btrHNLgcOuq/5fhftDlKpXHCuU5lk1OYq1/img.png&quot; data-origin-width=&quot;1079&quot; data-origin-height=&quot;1440&quot; data-is-animation=&quot;false&quot; width=&quot;400&quot; height=&quot;534&quot; style=&quot;width: 61.1499%;&quot; data-widthpercent=&quot;61.87&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TvDtp/btrHNLgcOuq/5fhftDlKpXHCuU5lk1OYq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTvDtp%2FbtrHNLgcOuq%2F5fhftDlKpXHCuU5lk1OYq1%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;1079&quot; height=&quot;1440&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;AppStore에 출시된 우리뭐먹지 앱 (iOS/iPad)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  홍보 / 마케팅&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;출시 직후 단기간에 다운로드 수가 높아야 AppStore 추천 앱에 언급될 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;오픈카톡, 디스코드, IT서비스 관련 SNS 등 개발자 커뮤니티를 통해 홍보해야 한다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;홍보에 크게 신경 쓰지 못하더라도 &lt;b&gt;ASO (앱스토어 최적화), SNS 마케팅&lt;/b&gt;은 해야 하는 것 같다.&lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://disquiet.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;디스콰이엇&lt;/a&gt; : IT 서비스 메이커들의 소셜 네트워크이다. 앱을 게시하거나, 기획자/디자이너/투자자 등과 소통할 수 있다.&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://www.wisetracker.co.kr/blog/aso/&quot;&gt;앱스토어 최적화(ASO) 알아보기&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;포스트&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://iphonedev.co.kr/fiveStarApps&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;iOS 개발자 커뮤니티&lt;/a&gt;의 추천앱 탭&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://brunch.co.kr/@vicotorlee/170&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;앱 출시 후 홍보에 대한 생각&lt;/a&gt; 포스트, &lt;a href=&quot;https://www.adjust.com/ko/blog/your-guide-to-app-marketing-strategies/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;9가지 앱 마케팅 전략&lt;/a&gt; 포스트&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;본문의 링크 참고&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;관련 포스트&amp;nbsp;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://applecider2020.tistory.com/35&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[인디&amp;nbsp;앱개발]&amp;nbsp;Apple의&amp;nbsp;App&amp;nbsp;Store&amp;nbsp;심사지침&amp;nbsp;요약&amp;nbsp;-&amp;nbsp;헬스케어&amp;nbsp;기능에도&amp;nbsp;민감하다&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://applecider2020.tistory.com/36&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Git] Git 브랜치 이름은 어떻게 정할까? - Git Flow로 브랜치 관리하기&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  &lt;a href=&quot;https://github.com/just1103/WhatWeEat/wiki&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;우리뭐먹지 - 프로젝트 GitHub WiKi&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  &lt;a href=&quot;https://apps.apple.com/app/1632157845&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;우리뭐먹지 - 앱&amp;nbsp;다운로드&amp;nbsp;링크&lt;/a&gt;&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>iOS</category>
      <category>Apple</category>
      <category>HIG</category>
      <category>IOS</category>
      <category>앱개발</category>
      <category>앱기획</category>
      <category>앱디자인</category>
      <category>앱배포</category>
      <category>앱출시</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/46</guid>
      <comments>https://applecider2020.tistory.com/46#entry46comment</comments>
      <pubDate>Wed, 20 Jul 2022 17:47:13 +0900</pubDate>
    </item>
    <item>
      <title>[Dictionary] value에 nil을 저장하려면? - nil의 타입을 명시</title>
      <link>https://applecider2020.tistory.com/45</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Dictionary의 value 타입을 옵셔널로 지정하고, value에 nil을 할당하려면 어떻게 해야 할까?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;궁금해서 구글링 해봤는데, StackOverFlow에서 간단히 답을 찾을 수 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- 검색 키워드 : how&amp;nbsp;to&amp;nbsp;save&amp;nbsp;nil&amp;nbsp;at&amp;nbsp;dictionary&amp;nbsp;value&amp;nbsp;in&amp;nbsp;swift&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference : &lt;a href=&quot;https://stackoverflow.com/questions/26544573/how-to-add-nil-value-to-swift-dictionary&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;How&amp;nbsp;to&amp;nbsp;add&amp;nbsp;nil&amp;nbsp;value&amp;nbsp;to&amp;nbsp;Swift&amp;nbsp;Dictionary?&lt;/a&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Dictionary 기본 문법&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;일반적으로 Dictionary는 이렇게 다룬다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1658119600377&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Dictionary 생성
var juices: [String: String] = [&quot;Apple&quot;: &quot;Applecider&quot;, &quot;Lemon&quot;: &quot;Lemonade&quot;]

// ✅ key/value pair 삭제
juices[&quot;Lemon&quot;] = nil

// ✅ key/value pair 추가
juices[&quot;Grape&quot;] = &quot;Grape Juice&quot; 

print(juices)  // [&quot;Apple&quot;: &quot;Applecider&quot;, &quot;Grape&quot;: &quot;Grape Juice&quot;] 출력&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;삭제 : Dictionary의 value에 &lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;nil을 할당하면, key &quot;Lemon&quot;과 value &quot;Lomonade&quot;가 삭제된다.&lt;br /&gt;&lt;/span&gt;&lt;/b&gt;주의 - key &quot;Lemon&quot;의 &lt;b&gt;value로 nil을 저장하는 것이 아니다.&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;추가 : 새로운 key &quot;Grape&quot;와 value &quot;Grape Juice&quot;를 Dictionary에 추가한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;위와 같이 Dictionary의 value에&amp;nbsp;&lt;span style=&quot;color: #333333;&quot;&gt;nil을 할당하면,&amp;nbsp;해당 key/value pair가 삭제된다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;그런데 value에 nil을 저장하려면 어떻게 해야 할까?&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Dictionary의 value 타입을 옵셔널로 지정하고, 다시 nil을 할당해보자.&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_1658120201899&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var juices: [String: String?] = [&quot;Apple&quot;: &quot;Applecider&quot;, &quot;Starfruit&quot;: nil]

juices[&quot;Apple&quot;] = nil  // 기존 key - key/value pair 삭제됨
juices[&quot;Plum&quot;] = nil   // 새로운 key - key/value pair 추가 안됨

print(juices)  // [&quot;Starfruit&quot;: nil] 출력&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;맨 위부터 한 줄씩 살펴보자.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Starfruit : Dictionary를 생성할 때, value에 nil을 할당하면 정상적으로 저장된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple : 기존에 있던 key인 &quot;Apple&quot;에 nil을 할당하면, &lt;span style=&quot;color: #ee2323;&quot;&gt;key/value pair가 삭제된다. (value로 nil이 저장되지 않음)&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Plum : 기존에 없던 새로운 key인 &quot;Plum&quot;에 nil을 할당하면, &lt;span style=&quot;color: #ee2323;&quot;&gt;key/value pair가 추가되지 않는다.&lt;span style=&quot;color: #ee2323;&quot;&gt;&amp;nbsp;(&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;value로 nil이 저장되지 않음&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nil이&amp;nbsp;저장되지&amp;nbsp;않고,&amp;nbsp;여전히&amp;nbsp;key/value&amp;nbsp;pair가&amp;nbsp;삭제된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서&amp;nbsp;아래&amp;nbsp;세&amp;nbsp;가지&amp;nbsp;방법을&amp;nbsp;활용해야&amp;nbsp;한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;방법-1. nil을 할당할 때 &lt;b&gt;String? 타입임을 명시한다.&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1658125824696&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var juices: [String: String?] = [&quot;Apple&quot;: &quot;Applecider&quot;, &quot;Starfruit&quot;: nil]

//   String? 타입의 nil을 할당
juices[&quot;Apple&quot;] = nil as String?  // 기존 key - nil이 저장됨
juices[&quot;Plum&quot;] = nil as String?   // 새로운 key - nil이 저장됨

print(juices)  // [&quot;Starfruit&quot;: nil, &quot;Apple&quot;: nil, &quot;Plum&quot;: nil] 출력&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Dictionary의 value로 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;nil as String?&lt;/span&gt; 을 할당한다는 것은 nil이 String? 타입임을 명시한다는 뜻이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple : 기존에 있던 key인 &quot;Apple&quot;에 상수 nilAsValue를 할당하면,&amp;nbsp;&lt;span style=&quot;color: #ee2323;&quot;&gt;value로 nil이 저장된다!&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Plum : 기존에 없던 새로운 key인 &quot;Plum&quot;에 nil을 할당하면,&amp;nbsp;&lt;span style=&quot;color: #ee2323;&quot;&gt;key/value pair가 추가되고&amp;nbsp;&lt;span style=&quot;color: #ee2323;&quot;&gt;value로 nil이 저장된다!&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;방법-2. &lt;b&gt;updateValue 메서드&lt;/b&gt;를 사용한다.&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1658126182626&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var juices: [String: String?] = [&quot;Apple&quot;: &quot;Applecider&quot;, &quot;Starfruit&quot;: nil]

//   updateValue 메서드를 호출
juices.updateValue(nil, forKey: &quot;Apple&quot;)  // 기존 key - nil이 저장됨
juices.updateValue(nil, forKey: &quot;Plum&quot;)   // 새로운 key - nil이 저장됨

print(juices)  // [&quot;Starfruit&quot;: nil, &quot;Apple&quot;: nil, &quot;Plum&quot;: nil] 출력&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Dictionary의 updateValue 메서드를 호출한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;updateValue를 자동 완성했을 때 컴파일러가 &lt;b&gt;타입 유추&lt;/b&gt;를 통해 value가 String? 타입임을 예상하기 때문에 nil 할당이 가능하다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;따라서 value에 nil이 저장된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;방법-3.&amp;nbsp;&lt;b&gt;nil을 상수/변수에 할당&lt;/b&gt;하고, 해당 상수/변수를 value에 할당한다.&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1658126345089&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var juices: [String: String?] = [&quot;Apple&quot;: &quot;Applecider&quot;, &quot;Starfruit&quot;: nil]

//   nil을 상수에 할당하고, 해당 상수를 value로 할당
let nilAsValue: String? = nil
juices[&quot;Apple&quot;] = nilAsValue  // 기존 key - nil이 저장됨
juices[&quot;Plum&quot;] = nilAsValue   // 새로운 key - nil이 저장됨

print(juices)  // [&quot;Starfruit&quot;: nil, &quot;Apple&quot;: nil, &quot;Plum&quot;: nil] 출력&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Dictionary의 value로 nil 자체를 바로 할당하지 않는다.&lt;br /&gt;&lt;b&gt;nil을 별도의 상수 nilAsValue에 할당하고, 해당 상수를&amp;nbsp;Dictionary의 value에 할당한다.&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이를 통해 컴파일러는 value가 String? 타입임을 알 수 있게 된다. 따라서&amp;nbsp;value에 nil이 저장된다.&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Swift Language Guide &amp;gt;&amp;nbsp;&lt;a href=&quot;https://docs.swift.org/swift-book/LanguageGuide/CollectionTypes.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Collection Types&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;StackOverFlow &amp;gt; &lt;a href=&quot;https://stackoverflow.com/questions/26544573/how-to-add-nil-value-to-swift-dictionary&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;How to add nil value to Swift Dictionary?&lt;/a&gt;&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Swift/Swift 문법</category>
      <category>Dictionary</category>
      <category>NIL</category>
      <category>Swift</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/45</guid>
      <comments>https://applecider2020.tistory.com/45#entry45comment</comments>
      <pubDate>Mon, 18 Jul 2022 16:10:00 +0900</pubDate>
    </item>
    <item>
      <title>[AppleWatch] 애플케어 리뷰 - 생활기스로 리퍼받은 애플워치 상태</title>
      <link>https://applecider2020.tistory.com/44</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;애플워치5를 쓰다가 스크린에 너무 기스가 많아서 애플케어 보증기한을 1개월 앞두고 센터를 찾았다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;애플워치의 보증기간은 아이폰에서&amp;nbsp;&lt;b&gt;Watch 앱 &amp;gt; 일반 &amp;gt; 정보 탭&lt;/b&gt;에 들어가면 확인할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;공인 센터에 미리 예약을 하고 방문해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;애플워치 리퍼 신청&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;스크린이 박살난 게 아니라서 &lt;b&gt;생활&amp;nbsp;기스만으로 리퍼가 가능할지 긴가민가 했었는데,&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;의외로 &lt;b&gt;제품 교환이 가능하다&lt;/b&gt;는 답변을 받았다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;자부담금은 &lt;b&gt;8만원&lt;/b&gt;이었다. (2021년 11월 이후부터 무상교환이 안된다.)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;*리퍼 관련 상세 내용은&lt;/span&gt;&amp;nbsp;&lt;a href=&quot;https://gent.tistory.com/400&quot;&gt;여기&lt;/a&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;애플워치는 고장 나면 부분 수리가 불가하고, 무조건 새 제품으로 교환이 원칙이라고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그리고 &quot;리퍼 제품으로 교환&quot;이라고 안내를 받았는데,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;센터에 여쭤보니&amp;nbsp;교환 목적의 리퍼 제품이라는 게 따로 없고, 애플워치의 부품 특성상 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;실질적으로 완전히 새 제품으로 교환해준다&lt;/b&gt;고 보면 된단다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;*&lt;/span&gt;&lt;a href=&quot;https://discussionskorea.apple.com/thread/120039547&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;애플 커뮤니티&lt;/a&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;에도 동일하게 나와있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;교환&amp;nbsp;접수가&amp;nbsp;된&amp;nbsp;애플워치&amp;nbsp;상태&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2326&quot; data-origin-height=&quot;2324&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEb7kM/btrEYInazOt/RiWUs0ChlekPSC6RcATLj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEb7kM/btrEYInazOt/RiWUs0ChlekPSC6RcATLj0/img.png&quot; data-alt=&quot;센터에 가져갔던 애플워치 상태 - 스크린&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEb7kM/btrEYInazOt/RiWUs0ChlekPSC6RcATLj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEb7kM%2FbtrEYInazOt%2FRiWUs0ChlekPSC6RcATLj0%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;700&quot; height=&quot;699&quot; data-origin-width=&quot;2326&quot; data-origin-height=&quot;2324&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;센터에 가져갔던 애플워치 상태 - 스크린&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2642&quot; data-origin-height=&quot;2642&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcYBRs/btrEZNnpB38/KoYiZvEhRex0HnLESMVqOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcYBRs/btrEZNnpB38/KoYiZvEhRex0HnLESMVqOK/img.png&quot; data-alt=&quot;센터에 가져갔던 애플워치 상태 - Crown 버튼&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcYBRs/btrEZNnpB38/KoYiZvEhRex0HnLESMVqOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcYBRs%2FbtrEZNnpB38%2FKoYiZvEhRex0HnLESMVqOK%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;700&quot; height=&quot;700&quot; data-origin-width=&quot;2642&quot; data-origin-height=&quot;2642&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;센터에 가져갔던 애플워치 상태 - Crown 버튼&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;스크린에 잔 기스가 많고, 옆쪽의 Crown 버튼도 패어 있다.&lt;/span&gt;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;접수일로부터 &lt;b&gt;13일 후&lt;/b&gt;에 제품을 수령했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;새로운 제품을 받고 지금까지 6개월 정도 사용했는데 아무 이상이 없다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;희안한 점이 하나 있는데, 똑같은 애플워치5인데 새로 받은 제품은 막 다뤘는데도 생활 기스가 전혀 안 난다는 거다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;두 제품의 스펙이 완전히 동일하다면...&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;기존 제품은 수영장 대리석 바닥에서 여러 번 떨어트렸는데, 이게 충격이 컸던 것으로 추측된다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;결론 : 애플케어 신청하는 게 좋고, (조금 심한) 생활기스로도 제품 교환이 가능하다.&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference&lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple &amp;gt; &lt;a href=&quot;https://www.apple.com/kr/support/products/watch/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;애플케어&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>IT 제품 리뷰</category>
      <category>Apple</category>
      <category>applecare</category>
      <category>applewatch</category>
      <category>applewatch5</category>
      <category>무료교환</category>
      <category>애플</category>
      <category>애플워치</category>
      <category>애플워치5</category>
      <category>애플케어</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/44</guid>
      <comments>https://applecider2020.tistory.com/44#entry44comment</comments>
      <pubDate>Fri, 17 Jun 2022 14:05:33 +0900</pubDate>
    </item>
    <item>
      <title>[AutoLayout] CHCR이란? Label의 default CHCR 값은? - 공식문서 오류 정정</title>
      <link>https://applecider2020.tistory.com/43</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;CHCR은&amp;nbsp;&lt;span&gt;Compression-Resistance 및&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Content-Hugging를 뜻한다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;화면에 여러 가지 View를 나타내는 경우, 이 CHCR 값을 조정하게 되는데&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #9d9d9d;&quot;&gt;(특히 StackView 내부에 여러 가지 View를 ArrangedSubviews로 올리는 경우)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관련 개념을 알아보다가 &lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;공식문서에서 몇 몇 오류를 발견해서 정리해봤다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span&gt;  &lt;/span&gt;Intrinsic Size란?&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;원래는 View의 위치 및 크기 (Origin 및 Size)를 모두 지정해줘야 하는데, &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  일부 View는 &lt;b&gt;View 내부의 컨텐츠에 따라 자체적으로 Size를 가지도록 되어있다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;이 걸 Intrinsic Size (내재적인 크기, 자체적인 크기)라고 부른다.&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예를 들어 Button의 Intrinsic Size는 뭘까?&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;&amp;ldquo;Button 내부의 텍스트 + default margin&amp;rdquo; 크기&lt;/b&gt;가 Button의 Intrinsic Size 이다. (font 등의 영향을 받는다.)&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple Archive의 &lt;a href=&quot;https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/AutolayoutPG/AnatomyofaConstraint.html#//apple_ref/doc/uid/TP40010853-CH9-SW1&quot;&gt;Anatomy of a Constraint&lt;/a&gt;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;에 Intrinsic Size 목록이 나와있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;624&quot; data-origin-height=&quot;184&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Gxplj/btrESC1osGx/AyawjDrymunDkTK9yLDG90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Gxplj/btrESC1osGx/AyawjDrymunDkTK9yLDG90/img.png&quot; data-alt=&quot;View 종류별 Intrinsic Size&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Gxplj/btrESC1osGx/AyawjDrymunDkTK9yLDG90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGxplj%2FbtrESC1osGx%2FAyawjDrymunDkTK9yLDG90%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;680&quot; height=&quot;201&quot; data-origin-width=&quot;624&quot; data-origin-height=&quot;184&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;View 종류별 Intrinsic Size&lt;/figcaption&gt;
&lt;/figure&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;UIView : 없음&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Slider : 높이만 가능 (iOS) &lt;span style=&quot;color: #ee2323;&quot;&gt;&amp;larr; width는 오타인듯 (공식문서 오류)&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Label, Button, Switch, Text Field : 높이/너비 가능 (위치만 정해주면, 크기는 자동으로 반영된다는 뜻)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;TextView / ImageView : 상황에 따라 다름. 스크롤이 없으면 가능&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span&gt;  &lt;/span&gt;Intrinsic Size를 왜 도입했을까?&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;View를 화면에 나타낼 때, 모든 View에 대해 Origin 및 Size를 일일이 설정할 필요가 없어지므로&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;관리할 constraint 개수가 줄어들어서 편리하다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span&gt;  &lt;/span&gt;CHCR이란?&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;CHCR은&amp;nbsp;&lt;/span&gt;&lt;span&gt;&lt;b&gt;Compression-Resistance 및&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&lt;b&gt;Content-Hugging&lt;/b&gt;를 의미한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Intrinsic Content Size를 활용하여 Layout을 설정하려면 CHCR 조정이 필요하다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예를 들어 화면에 여러 가지 View를 올리는 경우, Device Screen 크기에 따라 유동적으로 나타내기 위해&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;특정 ImageView는 Screen 너비만큼 늘어나게 하고, 특정 Label은 텍스트가 짤리지 않도록 설정&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이때 CHCR은 어떻게 사용할까?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이렇게 ImageView가 늘어나도록 하는 것은 &lt;b&gt;Content-Hugging 우선도를 감소&lt;/b&gt;시키고,&lt;br /&gt;Label이 잘리지 않도록 하는 것은 &lt;span&gt;&lt;b&gt;Compression-Resistance 우선도를 증가&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span&gt;CHCR을 정확히 정의해보자.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;716&quot; data-origin-height=&quot;246&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c4WNQt/btrEDR5ln5F/8rzoafQX2AVzMrKOwaMeSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c4WNQt/btrEDR5ln5F/8rzoafQX2AVzMrKOwaMeSk/img.png&quot; data-alt=&quot;CHCR 설명&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c4WNQt/btrEDR5ln5F/8rzoafQX2AVzMrKOwaMeSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc4WNQt%2FbtrEDR5ln5F%2F8rzoafQX2AVzMrKOwaMeSk%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;430&quot; height=&quot;148&quot; data-origin-width=&quot;716&quot; data-origin-height=&quot;246&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;CHCR 설명&lt;/figcaption&gt;
&lt;/figure&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;Compression-Resistance&lt;/b&gt; (압축 저항) : 외부 압력에 의해 콘텐츠가 잘리지 않도록 버티는 힘&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;Content-Hugging&lt;/b&gt; (컨텐츠 허깅) : Intrinsic Content Size와 딱맞게 줄어들려고 하는 힘&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;둘은 반대 개념이 아니며, 다른 constraint와 마찬가지로 CHCR에도 우선도가 적용된다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;우선도 (Constraint Priority)&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;constraint를 동일한 영역에 대해 중복으로 설정할 수 있는데, 이때 우선순위가 높은 constraint에 따라 배치한다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(화면에 나타낼 때 우선순위가 낮은 constraint는 무시된다.)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;우선도는 1~1000 사이에서 조절할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&amp;nbsp;&lt;/span&gt;CHCR 값을 설정하는 방법&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;View의 CHCR 값을 설정하는 방법은 두 가지가 있다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;1. UILayoutPriority의 타입 프로퍼티 활용&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;간단하게 사용할 수 있도록 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;.defaultLow&lt;/span&gt;, &lt;span style=&quot;background-color: #dddddd;&quot;&gt;.defaultHigh&lt;/span&gt;, &lt;span style=&quot;background-color: #dddddd;&quot;&gt;.required&lt;/span&gt; 가 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;rawValue는 각각 1000, 750, 250 이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1655102544813&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 타입 프로퍼티 활용
label.setContentHuggingPriority(.required, for: .horizontal)
label.setContentHuggingPriority(.defaultHigh, for: .horizontal)  
label.setContentHuggingPriority(.defaultLow, for: .horizontal)  

// 프로퍼티별 rawValue
.required    == 1000
.defaultHigh ==  750
.defaultLow  ==  250&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;검색 키워드 : ios&amp;nbsp;chcr&amp;nbsp;required&amp;nbsp;rawvalue&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/69555789/when-to-use-uilayoutpriority-required-in-setcontentcompressionresistanceprior&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;when-to-use-uilayoutpriority-required-in-setcontentcompressionresistanceprior&lt;/a&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;2. UILayoutPriority의 init(rawValue: Float) 활용&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;우선도는 1~1000의 범위 내에서 원하는 값을 지정할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1655102550266&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// rawValue 활용
label.setContentHuggingPriority(.init(rawValue: 150), for: .horizontal)&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span&gt;  &lt;/span&gt;공식문서 오류 - default CHCR 값&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/AutolayoutPG/ViewswithIntrinsicContentSize.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공식문서 내용&lt;/a&gt; 중에서 잘못된 정보는 아래와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;509&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vSzW1/btrEwx7cOqr/FhSGYBVZwvxyLqjiU6RxE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vSzW1/btrEwx7cOqr/FhSGYBVZwvxyLqjiU6RxE1/img.png&quot; data-alt=&quot;오류 - Name Label의 Hugging은 251로 표기되어 있음 (실제로는 250)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vSzW1/btrEwx7cOqr/FhSGYBVZwvxyLqjiU6RxE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvSzW1%2FbtrEwx7cOqr%2FFhSGYBVZwvxyLqjiU6RxE1%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;770&quot; height=&quot;306&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;509&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;오류 - Name Label의 Hugging은 251로 표기되어 있음 (실제로는 250)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;위 표처럼 &lt;b&gt;Label의 default Hugging&lt;/b&gt;은&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;251&lt;/span&gt;&lt;/b&gt;이라고 표기되어 있지만,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;실제로는 &lt;/b&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;2&lt;/span&gt;50&lt;/b&gt;&lt;/span&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;이것 때문에 StackView에 여러 개의 Label을 넣고, CHCR 값을 조정하는 과정에서 삽질을 많이 했다...&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Debug View Hierarchy 탭에서 &lt;b&gt;Label의 Hugging&lt;/b&gt; 값을 직접 확인해봤다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1112&quot; data-origin-height=&quot;536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FbIuS/btrEHBAowUb/XItF43bGcbf4BIggzeEAg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FbIuS/btrEHBAowUb/XItF43bGcbf4BIggzeEAg0/img.png&quot; data-alt=&quot;Label의 default CHCR&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FbIuS/btrEHBAowUb/XItF43bGcbf4BIggzeEAg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFbIuS%2FbtrEHBAowUb%2FXItF43bGcbf4BIggzeEAg0%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;860&quot; height=&quot;415&quot; data-origin-width=&quot;1112&quot; data-origin-height=&quot;536&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Label의 default CHCR&lt;/figcaption&gt;
&lt;/figure&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;왼쪽에 표시한 아이콘을 탭하면 &lt;b&gt;Debug View Hierarchy&lt;/b&gt;를 볼 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;화면에서 원하는 &lt;b&gt;Label&lt;/b&gt;을 클릭하면, 우측에서 관련 정보를 확인할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;현재 선택된 Label (5% 텍스트가 들어있는 Label)의 CHCR 값을 따로 지정하지 않았으므로 default 값으로 설정되어 있다. &lt;br /&gt;&lt;b&gt;Label의 default Hugging&lt;/b&gt;은 Horizontal, vertical 모두 &lt;span style=&quot;background-color: #dddddd; color: #ee2323;&quot;&gt;&lt;b&gt;250&lt;/b&gt;&lt;/span&gt;이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예제코드 - CHCR priority 설정하기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;참고로 5% 텍스트가 들어있는 &lt;b&gt;Label&lt;/b&gt;은 아래 코드로 생성했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;priority를 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;.defaultHigh&lt;/span&gt;로 변경하면 Hugging 값이 250에서 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;750&lt;/span&gt;으로 바뀐다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1655102196568&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private let bargainRateLabel: UILabel = {
    let label = UILabel()
    label.translatesAutoresizingMaskIntoConstraints = false
    label.textAlignment = .right
    label.font = Design.bargainRateLabelFont
    label.textColor = Design.bargainRateLabelTextColor
    // ✅ Hugging 값이 250 (default) -&amp;gt; 750 (.defaultHigh)으로 변경됨
    label.setContentHuggingPriority(.defaultHigh, for: .horizontal)  
    return label
}()&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;setContentHuggingPriority, setContentCompressionResistancePriority 메서드&lt;/b&gt;를 통해 CHCR priority를 설정한다.&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예제코드 전체 : GitHub &amp;gt;&amp;nbsp;&lt;a href=&quot;https://github.com/just1103/MVVM-RX-OpenMarket&quot;&gt;MVVM-RX-OpenMarket&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple &amp;gt; Documentation Archive &amp;gt; AutoLayout Guide &amp;gt; &lt;a href=&quot;https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/AutolayoutPG/AnatomyofaConstraint.html#//apple_ref/doc/uid/TP40010853-CH9-SW1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Anatomy of a Constraint&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple &amp;gt; Documentation Archive &amp;gt; AutoLayout Guide &amp;gt;&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/AutolayoutPG/ViewswithIntrinsicContentSize.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Views&amp;nbsp;with&amp;nbsp;Intrinsic&amp;nbsp;Content&amp;nbsp;Size&lt;/a&gt;&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>iOS/영문 공식문서 뜯어보기-iOS</category>
      <category>autoLayout</category>
      <category>CHCR</category>
      <category>Intrinsic Size</category>
      <category>label</category>
      <category>공식문서 오류</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/43</guid>
      <comments>https://applecider2020.tistory.com/43#entry43comment</comments>
      <pubDate>Thu, 16 Jun 2022 13:12:26 +0900</pubDate>
    </item>
    <item>
      <title>[CollectionView] 배너 하단에 PageControl 구현 - FooterView에 넣기</title>
      <link>https://applecider2020.tistory.com/42</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;저번 포스팅 &lt;a href=&quot;https://applecider2020.tistory.com/40&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[CollectionView] Diffable DataSource 이해하기 (3/3) - 상품 배너/목록/상세 화면을 구현한 예제코드&lt;/a&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;아래 화면처럼 &lt;b&gt;배너&amp;nbsp;하단에&amp;nbsp;PageControl&lt;/b&gt;을 구현해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333;&quot;&gt;배너를 왼쪽 &amp;lt;-&amp;gt; 오른쪽으로 Scroll 하면, PageControl이 바뀌는 것을 볼 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;618&quot; data-origin-height=&quot;1236&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpBdh8/btrECzDllTO/YTr2xt6lPcxMsIKfipcPG0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpBdh8/btrECzDllTO/YTr2xt6lPcxMsIKfipcPG0/img.gif&quot; data-alt=&quot;배너를 Scroll 하면 PageControl이 바뀜&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpBdh8/btrECzDllTO/YTr2xt6lPcxMsIKfipcPG0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bpBdh8/btrECzDllTO/YTr2xt6lPcxMsIKfipcPG0/img.gif&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;600&quot; data-origin-width=&quot;618&quot; data-origin-height=&quot;1236&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;배너를 Scroll 하면 PageControl이 바뀜&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 예제코드에서는 CompositionalLayout, orthogonalScrollingBehavior, PageControl, RxSwift를 활용했다.&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;첫째, 배너를 Horizontal Scroll할 때 화면에 보이는 &quot;현재 페이지 index&quot;를 PageControl에 전달하는 작업,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 둘째, PageControl의 위치를 잡는 것이 관건이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;  &lt;/span&gt;실패 기록 - scrollViewWillEndDragging 메서드는 못쓴다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;화면을 보면 위의 banner section은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;horizontal scroll&lt;/b&gt;을, 아래의 list section은&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;vertical scroll&lt;/b&gt;을 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1개 CollectionView 내에서 Scroll 방향이 여러 가지인 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 banner와 list &lt;span style=&quot;color: #ee2323;&quot;&gt;2개 section을 모두 구현하려면 scrollViewWillEndDragging 메서드를 쓸 수 없다.&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;scrollViewWillEndDragging에 아래처럼 코드를 작성하면, &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;의도했던 Horizontal Scroll 뿐만 아니라 &lt;span style=&quot;color: #ee2323;&quot;&gt;Vertical Scroll을 할 때도 호출되기 때문에 PageControl이 비정상적으로 작동한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1655095349007&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;extension ProductDetailViewController: UICollectionViewDelegate {
    func scrollViewWillEndDragging(_ scrollView: UIScrollView,
                                   withVelocity velocity: CGPoint,
                                   targetContentOffset: UnsafeMutablePointer&amp;lt;CGPoint&amp;gt;) {
        let sideInset: CGFloat = 30
        let page = Int(targetContentOffset.pointee.x / (view.frame.width - sideInset))
        productDetailScrollView.imageNumberPageControl.currentPage = page  // PageControl에 현재 페이지 index를 전달함
      }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;  &lt;/span&gt;실패 기록 - Cell이 PageControl을 갖도록 하면 나쁜 UX가 된다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는  Cell에서 PageControl을 생성하고, ViewController가 Cell에게 페이지 수 및 현재 페이지를 전달하는 방법을 생각했었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Cell을 Scroll할 때,&lt;span style=&quot;color: #ee2323;&quot;&gt; PageControl이 고정되어 있지 않고 Cell과 함께 이동하는 형태가 되어 어색하게 느껴졌다.&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;따라서 ViewController에서 PageControl을 생성하고,&lt;br /&gt;그 PageControl을&amp;nbsp;&lt;b&gt;banner section의 footerView&lt;/b&gt;에 넣는 게 적절하다고 판단했다.&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;따라서 PageControl을&amp;nbsp;&lt;b&gt;FooterView&lt;/b&gt;에 배치하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;section.&lt;/span&gt;&lt;b&gt;visibleItemsInvalidationHandler&lt;/b&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;코드로 하나씩 살펴보자.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. FooterView 추가&lt;/h2&gt;
&lt;pre id=&quot;code_1655096750518&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import UIKit
import RxSwift
import RxCocoa

final class FooterView: UICollectionReusableView {
    // MARK: - Properties
    private let bannerPageControl: UIPageControl = {
        let pageControl = UIPageControl()
        pageControl.translatesAutoresizingMaskIntoConstraints = false
        pageControl.pageIndicatorTintColor = .systemGray2
        pageControl.currentPageIndicatorTintColor = CustomColor.darkGreenColor
        pageControl.currentPage = 0
        pageControl.isUserInteractionEnabled = false
        return pageControl
    }()
    
    private let disposeBag = DisposeBag()
    
    // MARK: - Initializers
    override init(frame: CGRect) {
        super.init(frame: frame)
        configureUI()
    }
    
    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError(&quot;init(coder:) has not been implemented&quot;)
    }
    
    // MARK: - Methods
    func bind(input: Observable&amp;lt;Int&amp;gt;, indexPath: IndexPath, pageNumber: Int) {
        bannerPageControl.numberOfPages = pageNumber
        if indexPath.section == 1 {
            self.isHidden = true
        } else {
            input
                .subscribe(onNext: { [weak self] currentPage in
                    // ✅ pageControl의 현재 페이지를 조정
                    self?.bannerPageControl.currentPage = currentPage  
                })
                .disposed(by: disposeBag)
        }
    }
    
    private func configureUI() {
        addSubview(bannerPageControl)
        NSLayoutConstraint.activate([
            bannerPageControl.topAnchor.constraint(equalTo: topAnchor),
            bannerPageControl.bottomAnchor.constraint(equalTo: bottomAnchor),
            bannerPageControl.leadingAnchor.constraint(equalTo: leadingAnchor),
            bannerPageControl.trailingAnchor.constraint(equalTo: trailingAnchor)
        ])
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;custom header / footer를 생성할 때는 &lt;b&gt;UICollectionReusableView&lt;/b&gt;를 상속받도록 한다.&lt;/li&gt;
&lt;li&gt;footer 내부에서 &lt;b&gt;PageControl&lt;/b&gt;을 생성하고, footer의 subview로 올렸다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;bind 메서드&lt;/b&gt;&lt;/span&gt;를 통해 ViewController에서 변경된 현재 페이지 index (currentPage)를 받도록 했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. CollectionView Layout에 FooterView 위치 잡기&lt;/h2&gt;
&lt;pre id=&quot;code_1655096092905&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import UIKit
import RxSwift
import RxCocoa

final class ProductListViewController: UIViewController {
    // MARK: - Nested Types
    private enum SupplementaryKind {
        static let header = &quot;header-element-kind&quot;
        static let footer = &quot;footer-element-kind&quot;
    }
        
    // MARK: - Properties
    // ✅ footer에 전달될 정보
    private let currentBannerPage = PublishSubject&amp;lt;Int&amp;gt;()  
    
    private func createLayout() -&amp;gt; UICollectionViewLayout {
    let layout = UICollectionViewCompositionalLayout { [weak self] sectionIndex, _ -&amp;gt; NSCollectionLayoutSection? in
        // section 정보에 접근
        guard let sectionKind = SectionKind(rawValue: sectionIndex) else {
            self?.showUnknownSectionErrorAlert()
            return nil
        }
        
        // item 설정
        // Cell 내부 컨텐츠의 높이를 알아서 계산하여 반영하는 estimatedHeight를 적용
        let screenWidth = UIScreen.main.bounds.width
        let estimatedHeight = NSCollectionLayoutDimension.estimated(screenWidth)
        let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: estimatedHeight)
        let item = NSCollectionLayoutItem(layoutSize: itemSize)

        // group 설정
        let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: estimatedHeight)
        let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize,
                                                       subitem: item,  
                                                       count: sectionKind.columnCount)  // 1개 group에 나타낼 item 개수를 설정
        // section 설정
        let section = NSCollectionLayoutSection(group: group)
        section.orthogonalScrollingBehavior = sectionKind.orthogonalScrollingBehavior()  // Section별 Scroll 방향을 할당       
        section.visibleItemsInvalidationHandler = { [weak self] _, contentOffset, environment in
            let bannerIndex = Int(max(0, round(contentOffset.x / environment.container.contentSize.width)))
            if environment.container.contentSize.height == environment.container.contentSize.width {
                self?.currentBannerPage.onNext(bannerIndex)
            }
        }

        // ✅ header 및 footer 설정
        let sectionHeader = NSCollectionLayoutBoundarySupplementaryItem(
            layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: estimatedHeight),
            elementKind: SupplementaryKind.header,
            alignment: .top
        )
        let sectionFooter = NSCollectionLayoutBoundarySupplementaryItem(
            layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: estimatedHeight),
            elementKind: SupplementaryKind.footer,
            alignment: .bottom
        )
        section.boundarySupplementaryItems = [sectionHeader, sectionFooter]
        return section
    }
    return layout
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;header / footer 이외 코드는 생략했다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;custom footer를 CollectionView에 연결&lt;/b&gt;하기 위해&amp;nbsp;&lt;br /&gt;&lt;/span&gt;&lt;span&gt;section.&lt;b&gt;boundarySupplementaryItems 프로퍼티&lt;/b&gt;에&lt;span&gt;&amp;nbsp;&lt;/span&gt;header / footer를 할당한다.&lt;br /&gt;이때 alignment를 header는 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;.top&lt;/span&gt;, footer는 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;.bottom&lt;/span&gt;으로 설정한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;검색 키워드 : collectionView, pageControl, banner, currentPage&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333;&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: #333333;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;a href=&quot;https://nemecek.be/blog/141/how-to-show-page-indicator-with-compositional-layout&quot;&gt;how-to-show-page-indicator-with-compositional-layout&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;3. &lt;/span&gt;&lt;/span&gt;visibleItemsInvalidationHandler 활용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드 중에서 visibleItemsInvalidationHandler와 관련된 내용을 자세히 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1655096334035&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 위의 createLayout 메서드에 포함된 코드 
section.visibleItemsInvalidationHandler = { [weak self] _, contentOffset, environment in
    // 비교용 - Device의 Screen 크기
    print(&quot;=== Screen Width/Height :&quot;, UIScreen.main.bounds.width, &quot;/&quot;, UIScreen.main.bounds.height)

    // ✅ contentOffset은 CollectionView의 bound를 기준으로 Scroll 결과 보여지는 컨텐츠의 Origin을 나타냄
    // 배너 및 목록화면의 경우, Scroll하면 어디서 클릭해도 0부터 시작
    // 상세화면의 경우, Scroll하면 어디서 클릭해도 약 -30부터 시작 (기기마다 다름, CollectionView의 bound를 기준으로 cell(이미지)의 leading이 왼쪽 (-30)에 위치하므로 음수임)
    print(&quot;OffsetX :&quot;, contentOffset.x)

    // ✅ environmnet는 collectionView layout 관련 정보를 담고 있음
    // environment.container.contentSize는 CollectionView 중에서 현재 Scroll된 Group이 화면에 보이는 높이를 나타냄
    print(&quot;environment Width :&quot;, environment.container.contentSize.width)   // Device의 스크린 너비와 동일
    print(&quot;environment Height :&quot;, environment.container.contentSize.height) // Horizontal Scroll하면 스크린 너비와 같고, Vertical Scroll하면 그보다 커짐

    let bannerIndex = Int(max(0, round(contentOffset.x / environment.container.contentSize.width)))  // 음수가 되는 것을 방지하기 위해 max 사용
    if environment.container.contentSize.height == environment.container.contentSize.width {  // ❗Horizontal Scroll 하는 조건
        self?.currentBannerPage.onNext(bannerIndex)  // 클로저가 호출될 때마다 pageControl의 currentPage로 값을 보냄
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;visibleItemsInvalidationHandler에 할당되는 클로저를 뜯어보자.&lt;/li&gt;
&lt;li&gt;Horizontal Scroll 했을 때&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;바뀐&amp;nbsp;Banner 페이지의 index&lt;/b&gt;를 pageControl의 currentPage로 지정하는 기능이다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;contentOffset&lt;/span&gt;,&amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;environment.container.contentSize&lt;/span&gt;에 대한 이해가 필요하다. 직접 프린트를 찍어보면 쉽다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;contentOffset&lt;/b&gt;은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;CollectionView의 bound를 기준으로 Scroll 결과 보여지는 컨텐츠의 Origin&lt;/span&gt;을 나타낸다.&lt;br /&gt;오른쪽으로 Horizontal Scroll 하면, contentOffset.x의 값은 계속해서 커진다.&lt;/li&gt;
&lt;li&gt;environment.container.contentSize는 공식문서에 명확히 나와있지 않아서 실험이 필요했다.&lt;br /&gt;일단 environmnet는 collectionView layout 관련 정보를 담고 있다.&lt;br /&gt;&lt;b&gt;environment.container.contentSize&lt;/b&gt;는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;CollectionView 중에서 현재 Scroll된 Group이 화면에 보이는 높이&lt;/span&gt;를 나타낸다.&lt;br /&gt;이 contentSize의 height를 찍어보면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;u&gt;Horizontal Scroll할 때는 스크린 너비와 같고, Vertical Scroll할 때는 그보다 커진다.&lt;br /&gt;&lt;/u&gt;(아래 gif 참고)&lt;br /&gt;-&amp;gt; 따라서 이 특징을 활용하여 if문의 조건으로 Horizontal Scroll하는 경우를 찾은 뒤, PageControl에 현재 페이지를 보내줬다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;env2.gif&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;390&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LB1K0/btrECv8ge4u/B8vpzaV6lFpku5mZa96HEK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LB1K0/btrECv8ge4u/B8vpzaV6lFpku5mZa96HEK/img.gif&quot; data-alt=&quot;Scroll 방향에 따라 달라지는 offSetX 및 environment height 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LB1K0/btrECv8ge4u/B8vpzaV6lFpku5mZa96HEK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/LB1K0/btrECv8ge4u/B8vpzaV6lFpku5mZa96HEK/img.gif&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;700&quot; height=&quot;546&quot; data-filename=&quot;env2.gif&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;390&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Scroll 방향에 따라 달라지는 offSetX 및 environment height 확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;검색 키워드 : orthogonalScrollingBehavior, page control&lt;br /&gt;&lt;a href=&quot;https://stackoverflow.com/questions/59514541/how-can-i-detect-orthogonal-scroll-events-when-using-uicollectionviewcompositio&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;How can I detect orthogonal scroll events when using `UICollectionViewCompositionalLayout`?&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;(근데 이 클로저도 Vertical Scroll할 때 호출되므로 한계가 있어 보였지만,&lt;/span&gt;&lt;span&gt;다른 해결방법을 찾지 못했다.)&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;4. FooterView 등록 및 &lt;span&gt;DataSource에 연결&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333;&quot;&gt;Custom Footer를 등록하고, DataSource에 연결시키면 된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333;&quot;&gt;Custom Cell을 처리하는 과정과 비슷하다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1655098169139&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ✅ typealias 활용
private typealias HeaderRegistration = UICollectionView.SupplementaryRegistration&amp;lt;HeaderView&amp;gt;  
private typealias FooterRegistration = UICollectionView.SupplementaryRegistration&amp;lt;FooterView&amp;gt;

private func configureSupplementaryViewRegistrationAndDataSource() {
    // ✅ header, footer 등록
    let headerRegistration = HeaderRegistration(elementKind: SupplementaryKind.header) { view, _, indexPath in
        view.apply(indexPath)
    }
    let footerRegistration = FooterRegistration(elementKind: SupplementaryKind.footer) { [weak self] view, _, indexPath in
        guard let self = self else { return }
        view.bind(input: self.currentBannerPage.asObservable(),
                  indexPath: indexPath,
                  pageNumber: Content.bannerCount)
    }

    // ✅ dataSource에 header, footer 연결
    dataSource.supplementaryViewProvider = { [weak self] _, kind, index in
        switch kind {
        case SupplementaryKind.header:
            return self?.collectionView.dequeueConfiguredReusableSupplementary(using: headerRegistration,
                                                                               for: index)
        case SupplementaryKind.footer:
            return self?.collectionView.dequeueConfiguredReusableSupplementary(using: footerRegistration,
                                                                               for: index)
        default:
            return UICollectionReusableView()
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SupplementaryRegistration의 제네릭 매개변수로 &lt;b&gt;Custom header / footer 타입&lt;/b&gt; (HeaderView / FooterView)을 전달한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;header / footer를 등록&lt;/b&gt;하고, &lt;b&gt;dataSource에 연결&lt;/b&gt;하면 끝이다.&lt;/li&gt;
&lt;li&gt;dataSource를 snapshot에 반영하는 과정은 생략했다.&lt;br /&gt;전체 코드는&amp;nbsp;&lt;a style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot; href=&quot;https://github.com/just1103/MVVM-RX-OpenMarket&quot;&gt;GitHub 링크&lt;/a&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555;&quot;&gt;를 참고&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&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;span&gt;&lt;span&gt;&lt;span&gt;예제코드 전체 : GitHub &amp;gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://github.com/just1103/MVVM-RX-OpenMarket&quot;&gt;MVVM-RX-OpenMarket&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span&gt;Apple &amp;gt; Collection View &amp;gt; Sample Code -&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/views_and_controls/collection_views/updating_collection_views_using_diffable_data_sources&quot;&gt;Updating&amp;nbsp;Collection&amp;nbsp;Views&amp;nbsp;Using&amp;nbsp;Diffable&amp;nbsp;Data&amp;nbsp;Sources&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Apple &amp;gt; WWDC 2019 &amp;gt;&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2019/220&quot;&gt;Advances in UI Data Sources&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Stackoverflow &amp;gt; &lt;a href=&quot;https://stackoverflow.com/questions/59514541/how-can-i-detect-orthogonal-scroll-events-when-using-uicollectionviewcompositio&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;how-can-i-detect-orthogonal-scroll-events-when-using-uicollectionviewcompositio&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Blog &amp;gt; &lt;a href=&quot;https://nemecek.be/blog/141/how-to-show-page-indicator-with-compositional-layout&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;how-to-show-page-indicator-with-compositional-layout&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;관련 포스트&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://applecider2020.tistory.com/40&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[CollectionView] Diffable DataSource 이해하기 (3/3)&amp;nbsp;-&amp;nbsp;상품&amp;nbsp;배너/목록/상세&amp;nbsp;화면을&amp;nbsp;구현한&amp;nbsp;예제코드&lt;/a&gt;&lt;/li&gt;
&lt;/ul&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>iOS</category>
      <category>banner</category>
      <category>CollectionView</category>
      <category>footerview</category>
      <category>PageControl</category>
      <category>visibleItemsInvalidationHandler</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/42</guid>
      <comments>https://applecider2020.tistory.com/42#entry42comment</comments>
      <pubDate>Wed, 15 Jun 2022 01:30:51 +0900</pubDate>
    </item>
    <item>
      <title>[CollectionView] estimatedHeight 사용 시 is stuck in its update/layout loop 에러 발생</title>
      <link>https://applecider2020.tistory.com/41</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;CollectionView의 CompositionalLayout에서 &lt;b&gt;estimatedHeight&lt;/b&gt;&lt;span style=&quot;color: #333333; font-size: 16px; letter-spacing: 0px;&quot;&gt;를 사용해봤다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Item / Group의 크기를 고정하지 않고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Cell 내부 컨텐츠의 높이를 알아서 계산하여 반영해주므로 매우 유용하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;*예제코드는 &lt;a href=&quot;https://applecider2020.tistory.com/40&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[CollectionView] Diffable DataSource 이해하기 (3/3) - 상품 배너/목록/상세 화면을 구현한 예제코드 포스트&lt;/a&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그런데&lt;b&gt; &lt;span style=&quot;color: #ee2323;&quot;&gt;약 iOS 15.0~15.3에서 crash가 발생했다.&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;에러 문구는 아래와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;is stuck in its update/layout loop. This can happen for many reasons, including self-sizing views whose preferred attributes are not returning a consistent size. To debug this issue, check the Console app for logs in the &quot;UICollectionViewRecursion&quot; category.'&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;확인해보니 &lt;b&gt;특정 버전에서&lt;/b&gt;&amp;nbsp;&lt;b&gt;estimatedHeight와 관련한 버그가 발생&lt;/b&gt;한 게 원인이었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/70213395/self-sizing-collection-view-enters-a-recursive-loop-in-ios-15&quot;&gt;https://stackoverflow.com/questions/70213395/self-sizing-collection-view-enters-a-recursive-loop-in-ios-15&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;검색 키워드 : is stuck in its update/layout loop&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;버그 해결 방법&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;iOS 15 미만, iOS 15.4 이상에서는 문제가 없음을 확인했다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #9d9d9d;&quot;&gt;아래 코드를 추가하면 해결된다는 코멘트도 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1655092355591&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;override func shouldInvalidateLayout(
    forPreferredLayoutAttributes preferredAttributes: UICollectionViewLayoutAttributes,
    withOriginalAttributes originalAttributes: UICollectionViewLayoutAttributes
) -&amp;gt; Bool {
    return true
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;버그 대응 예제코드&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이 버그에 어떻게 대응하는 게 좋을까?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;사용자의 기기 버전이 iOS 15.0~15.3인 경우, 에러가 발생하므로 아래처럼 &lt;b&gt;Alert&lt;/b&gt;를 띄우도록 구현했다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1655093394065&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ViewController에 정의한 메서드
private func checkIOSVersion() {
    // ✅ 사용자 기기의 현재 iOS 버전 정보를 받아옴
    let versionNumbers = UIDevice.current.systemVersion.components(separatedBy: &quot;.&quot;)
    let major = versionNumbers[0]
    let minor = versionNumbers[1]
    let version = major + &quot;.&quot; + minor

    guard let systemVersion = Double(version) else { return }
    let errorVersion = 15.0..&amp;lt;15.4
    // ✅ 해당 버전만 버그가 발생하므로 Alert로 업데이트 권고
    if  errorVersion ~= systemVersion {
        showErrorVersionAlert()
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;checkIOSVersion&lt;/b&gt; 메서드를 추가하여 사용자 기기의 현재 iOS 버전 정보를 받아오고, &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;버그가 발생하는 특정 버전이면 Alert를 띄워서 업데이트 권고하도록 구현했다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;Alert&lt;/b&gt;를 띄우는 메서드를 분리했다.&lt;/span&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;기타 코드는 아래를 참고&lt;/p&gt;
&lt;pre id=&quot;code_1655093798469&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private func showErrorVersionAlert() {
    let okAlertAction = UIAlertAction(title: Content.okAlertActionTitle, style: .default)
    let alert = AlertFactory().createAlert(title: Content.versionErrorTitle,
                                           message: Content.versionErrorMessage,
                                           actions: okAlertAction)
    present(alert, animated: true)
}

private enum Content {
    static let versionErrorTitle = &quot;기기를 iOS 15.4 이상으로 업데이트 해주세요&quot;
    static let versionErrorMessage = &quot;애플이 잘못했어요&quot;
    static let okAlertActionTitle = &quot;OK&quot;
}

// 참고 - AlertFactory
struct AlertFactory {
    func createAlert(style: UIAlertController.Style = .alert,
                     title: String? = nil,
                     message: String? = nil,
                     actions: UIAlertAction...) -&amp;gt; UIAlertController {
        let alert = UIAlertController(title: title, message: message, preferredStyle: style)
        actions.forEach { action in
            alert.addAction(action)
        }
        
        return alert
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;AlertFactory&lt;/b&gt;를 추가해서 코드 재사용성을 개선했다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;중첩 타입 (Nested Type)으로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Content 열거형&lt;/b&gt;을 추가하여 문자열 상수를 분리했다.&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&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;span&gt;&lt;span&gt;&lt;span&gt;예제코드 전체 : GitHub &amp;gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://github.com/just1103/MVVM-RX-OpenMarket&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;MVVM-RX-OpenMarket&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span&gt;&lt;span&gt;Stackoverflow &amp;gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://stackoverflow.com/questions/70213395/self-sizing-collection-view-enters-a-recursive-loop-in-ios-15&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;self-sizing-collection-view-enters-a-recursive-loop-in-ios-15&lt;/a&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span&gt;관련 포스트&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://applecider2020.tistory.com/40&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[CollectionView] Diffable DataSource 이해하기 (3/3) - 상품 배너/목록/상세 화면을 구현한 예제코드 포스트&lt;/a&gt;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>iOS</category>
      <category>CollectionView</category>
      <category>estimatedHeight</category>
      <category>is stuck in its update/layout loop</category>
      <category>버그</category>
      <category>에러</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/41</guid>
      <comments>https://applecider2020.tistory.com/41#entry41comment</comments>
      <pubDate>Mon, 13 Jun 2022 13:02:51 +0900</pubDate>
    </item>
    <item>
      <title>[CollectionView] Diffable DataSource 이해하기 (3/3) - 상품 배너/목록/상세 화면을 구현한 예제코드</title>
      <link>https://applecider2020.tistory.com/40</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;DiffableData을 처음 들어봤다면,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://applecider2020.tistory.com/37&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[CollectionView] Diffable DataSource 이해하기 (1/3) - Advances in UI Data Sources (WWDC19) 포스팅&lt;/a&gt;을 참고&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://applecider2020.tistory.com/38&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[CollectionView] Diffable DataSource 이해하기 (2/3) - 흔히 하는 실수, Modern Collection Views 예제코드 포스팅&lt;/a&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;orthogonalScrollingBehavior을 처음 들어봤다면,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://applecider2020.tistory.com/39&quot;&gt;[CollectionView] Section마다 다른 Scroll Direction 설정하기, Carousel Paging 구현하기 (feat. AppStore)&lt;/a&gt;&amp;nbsp;포스팅을 참고&lt;/span&gt;&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이전 포스팅에서 다룬 내용을 써먹어보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;CollectionView의 &lt;b&gt;DiffableDataSource, CompositionalLayout, orthogonalScrollingBehavior&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;총 2개 화면이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;화면-1 : &lt;b&gt;상품 배너와 상품 목록&lt;/b&gt;을 보여준다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;화면-2 : 사용자가 배너 또는 목록의 상품을 탭하면, 해당 상품의&lt;b&gt; 상세 화면&lt;/b&gt;을 띄운다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt; &amp;nbsp;상품 배너 및 목록 예제코드&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;먼저 배너와 상품 목록을 구현해보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;위의 banner section은 &lt;b&gt;horizontal scroll&lt;/b&gt;을, 아래의 list section은&lt;b&gt; vertical scroll&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그리고 1개 CollectionView 내에서&amp;nbsp;&quot;Section마다 Scroll Direction을 다르게&quot; 지정하고 있으므로&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;compositionalLayout의&amp;nbsp;&lt;b&gt;orthogonalScrollingBehavior&lt;/b&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;&lt;b&gt;  &lt;/b&gt;상품 목록을&amp;nbsp;좀 더 자세히 보면,&amp;nbsp;상단의 메뉴 버튼을 통해&lt;b&gt; &lt;b&gt;Grid &lt;/b&gt;형태와&amp;nbsp;&lt;b&gt;Table &lt;/b&gt;형태&lt;/b&gt; 2개 종류로 나타냈다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;이를 위해 2개 종류의 Cell 타입 (GridCell, TableCell)을 사용했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #9d9d9d;&quot;&gt;메뉴 버튼을 구현할 때, 버튼에 UnderLine을 만들고, 버튼을 탭할 때 &lt;span style=&quot;color: #9d9d9d;&quot;&gt;UnderLine&lt;/span&gt;의 위치가 이동하도록 했는데&lt;br /&gt;이 내용은 별도로 포스팅할 예정이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YMOOD/btrEznWYawD/Fw8y7ZYbkAkIhfyGvad9x0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YMOOD/btrEznWYawD/Fw8y7ZYbkAkIhfyGvad9x0/img.gif&quot; data-origin-width=&quot;566&quot; data-origin-height=&quot;1126&quot; data-is-animation=&quot;true&quot; data-widthpercent=&quot;50.13&quot; style=&quot;width: 49.5499%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YMOOD/btrEznWYawD/Fw8y7ZYbkAkIhfyGvad9x0/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYMOOD%2FbtrEznWYawD%2FFw8y7ZYbkAkIhfyGvad9x0%2Fimg.gif&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;566&quot; height=&quot;1126&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbKsLh/btrEDRpbtBl/gEmCUT4YeuIY6ktJOjNAz1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbKsLh/btrEDRpbtBl/gEmCUT4YeuIY6ktJOjNAz1/img.gif&quot; data-origin-width=&quot;618&quot; data-origin-height=&quot;1236&quot; data-is-animation=&quot;true&quot; data-widthpercent=&quot;49.87&quot; style=&quot;width: 49.2873%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbKsLh/btrEDRpbtBl/gEmCUT4YeuIY6ktJOjNAz1/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbKsLh%2FbtrEDRpbtBl%2FgEmCUT4YeuIY6ktJOjNAz1%2Fimg.gif&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;618&quot; height=&quot;1236&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1. CollectionView의 Section 및 Layout 설정&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;먼저 CollectionView에 나타낼 Section의 종류를 정의하고, compositionalLayout을 잡아준다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1655036448432&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private enum SectionKind: Int {
    // Section 종류
    case banner
    case list

    // ✅ 한 줄에 보여줄 item의 개수를 설정 
    var columnCount: Int {
        switch self {
        case .banner:
            return 1
        case .list:
            if ProductListViewController.isGrid {
                return 2  // grid 형태면 한 줄에 2개의 item을 나타냄
            } else {
                return 1  // table 형태면 한 줄에 1개의 item을 나타냄
            }
        }
    }

    // ✅ banner section은 horizontal scroll, list section은 vertical scroll로 설정
    func orthogonalScrollingBehavior() -&amp;gt; UICollectionLayoutSectionOrthogonalScrollingBehavior {
        switch self {
        case .banner:
            return .groupPagingCentered
        case .list:
            return .none
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;SectionKind&lt;/b&gt;는 배너, 목록 2개 종류로 구분했다.&lt;br /&gt;이때 선택지를 한정 짓는 상황이므로 enum으로 선언했다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;columnCount 프로퍼티&lt;/b&gt;를 통해 상품 목록을 나타낼 때, 한 줄 (row)에 보여줄 item의 개수를 설정한다.&lt;br /&gt;한 줄에 Grid 형태는 2개씩, Table 형태는 1개씩 나타낸다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;orthogonalScrollingBehavior 메서드&lt;/b&gt;에서 각 Section 마다 어떤 형태로 Scroll 할지 지정한다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;banner는 horizontal scroll을 하므로&amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;.groupPagingCentered&lt;/span&gt;를, list section은 vertical scroll을 해야하므로&amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;.none&lt;/span&gt;으로 설정했다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1654692780027&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private func createLayout() -&amp;gt; UICollectionViewLayout {
    let layout = UICollectionViewCompositionalLayout { [weak self] sectionIndex, _ -&amp;gt; NSCollectionLayoutSection? in
        // section 정보에 접근
        guard let sectionKind = SectionKind(rawValue: sectionIndex) else {
            self?.showUnknownSectionErrorAlert()
            return nil
        }
        
        // item 설정
        // ✅ Cell 내부 컨텐츠의 높이를 알아서 계산하여 반영하는 estimatedHeight를 적용
        let screenWidth = UIScreen.main.bounds.width
        let estimatedHeight = NSCollectionLayoutDimension.estimated(screenWidth)
        let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: estimatedHeight)
        let item = NSCollectionLayoutItem(layoutSize: itemSize)

        // group 설정
        let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: estimatedHeight)
        let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize,
                                                       subitem: item,  
                                                       count: sectionKind.columnCount)  // 1개 group에 나타낼 item 개수를 설정
        // section 설정
        let section = NSCollectionLayoutSection(group: group)
        section.orthogonalScrollingBehavior = sectionKind.orthogonalScrollingBehavior()  // ✅ 여기서 Section별 Scroll 방향을 할당       
        section.visibleItemsInvalidationHandler = { [weak self] _, contentOffset, environment in
            let bannerIndex = Int(max(0, round(contentOffset.x / environment.container.contentSize.width)))
            if environment.container.contentSize.height == environment.container.contentSize.width {
                self?.currentBannerPage.onNext(bannerIndex)
            }
        }

        // header 및 footer 설정
        let sectionHeader = NSCollectionLayoutBoundarySupplementaryItem(
            layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: estimatedHeight),
            elementKind: SupplementaryKind.header,
            alignment: .top
        )
        let sectionFooter = NSCollectionLayoutBoundarySupplementaryItem(
            layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: estimatedHeight),
            elementKind: SupplementaryKind.footer,
            alignment: .bottom
        )
        section.boundarySupplementaryItems = [sectionHeader, sectionFooter]
        return section
    }
    return layout
}

// ✅ createLayout 메서드의 반환값은 CollectionView의 collectionViewLayout 프로퍼티에 할당함
// 아래의 두 형태 모두 가능
collectionView = UICollectionView(frame: .zero, collectionViewLayout: createLayout()) 
collectionView.collectionViewLayout = createLayout()&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;createLayout 메서드에서 item / group / section, header / footer에 대한 레이아웃을 설정한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;estimatedHeight&lt;/b&gt;&lt;/span&gt;를 사용하면&amp;nbsp;Cell 내부 컨텐츠의 높이를 알아서 계산하여 반영해주므로 매우 유용하다.&lt;br /&gt;이때 &lt;span style=&quot;color: #ee2323;&quot;&gt;itemSize와 groupSize에 모두 적용&lt;/span&gt;해야 작동한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;.fractionalWidth / .&lt;/b&gt;&lt;b&gt;fractionalHeight&lt;/b&gt;은 화면 크기에 대한 상대적인 크기를 적용하므로 다양한 기기에 유연하게 대응 가능하다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;section의&amp;nbsp;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;orthogonalScrollingBehavior&lt;/span&gt; 프로퍼티&lt;/b&gt;로 Section마다 Scroll 방향을 설정한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;section의 boundarySupplementaryItems 프로퍼티로 &lt;b&gt;header 및 footer&lt;/b&gt;를 설정한다.&lt;br /&gt;alignment를 header는 .top, footer는 .bottom으로 설정한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;createLayout 메서드의 반환값은 CollectionView의&amp;nbsp;&lt;b&gt;collectionViewLayout 프로퍼티&lt;/b&gt;에 할당한다.&lt;br /&gt;CollectionView를 초기화할 때 할당하는 것, 초기화한 이후에 할당하는 것 모두 가능하다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;❗배너의 좌우로 다른 item이 보이게 하려면?&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;item의 fractionalWidth는 1.0, group의 fractionalWidth는 0.7 정도로 설정하고,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;item의 contentInsets을 약 10으로 설정하면 된다. (아래의 상세화면 코드에서 설명)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;  참고 - 배너 하단에 PageControl 구현하기&lt;/b&gt;&lt;br /&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;*자세한 설명은 &lt;/span&gt;&lt;a href=&quot;https://applecider2020.tistory.com/42&quot;&gt;[CollectionView]&amp;nbsp;배너&amp;nbsp;하단에&amp;nbsp;PageControl&amp;nbsp;구현&lt;/a&gt;&amp;nbsp;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;포스트를 참고&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예제처럼 PageControl을 구현하려면 추가적인 작업이 필요하다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;배너를 Horizontal Scroll을 했을 때 현재 페이지의 index를 구한 다음 PageControl의 currentPage에 할당해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이 작업은 어디서 처리했을까?&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;createLayout 메서드의 section 부분에&amp;nbsp;&lt;b&gt;visibleItemsInvalidationHandler&lt;/b&gt;로 클로저를 할당하여 처리했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(예제에서 PageControl을 FooterView에 배치했는데, 이 부분은 별도로 포스팅할 예정이다.)&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 style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;참고 - scrollViewWillEndDragging 메서드에 이 코드를 작성하면,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;의도했던 Horizontal Scroll 뿐만 아니라 Vertical Scroll을 할 때도 호출되기 때문에 PageControl이 비정상적으로 작동한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1655038895478&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 위의 createLayout 메서드에 포함된 코드 
section.visibleItemsInvalidationHandler = { [weak self] _, contentOffset, environment in
    // 비교용 - Device의 Screen 크기
    print(&quot;=== Screen Width/Height :&quot;, UIScreen.main.bounds.width, &quot;/&quot;, UIScreen.main.bounds.height)

    // ✅ contentOffset은 CollectionView의 bound를 기준으로 Scroll 결과 보여지는 컨텐츠의 Origin을 나타냄
    // 배너 및 목록화면의 경우, Scroll하면 어디서 클릭해도 0부터 시작
    // 상세화면의 경우, Scroll하면 어디서 클릭해도 약 -30부터 시작 (기기마다 다름, CollectionView의 bound를 기준으로 cell(이미지)의 leading이 왼쪽 (-30)에 위치하므로 음수임)
    print(&quot;OffsetX :&quot;, contentOffset.x)

    // ✅ environmnet는 collectionView layout 관련 정보를 담고 있음
    // environment.container.contentSize는 CollectionView 중에서 현재 Scroll된 Group이 화면에 보이는 높이를 나타냄
    print(&quot;environment Width :&quot;, environment.container.contentSize.width)   // Device의 스크린 너비와 동일
    print(&quot;environment Height :&quot;, environment.container.contentSize.height) // Horizontal Scroll하면 스크린 너비와 같고, Vertical Scroll하면 그보다 커짐

    let bannerIndex = Int(max(0, round(contentOffset.x / environment.container.contentSize.width)))  // 음수가 되는 것을 방지하기 위해 max 사용
    if environment.container.contentSize.height == environment.container.contentSize.width {  // ❗Horizontal Scroll 하는 조건
        self?.currentBannerPage.onNext(bannerIndex)  // 클로저가 호출될 때마다 pageControl의 currentPage로 값을 보냄
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;visibleItemsInvalidationHandler에 할당되는 클로저를 뜯어보자.&lt;/li&gt;
&lt;li&gt;Horizontal Scroll 했을 때 &lt;b&gt;바뀐&amp;nbsp;Banner 페이지의 index&lt;/b&gt;를 pageControl의 currentPage로 지정하는 기능이다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;contentOffset&lt;/span&gt;, &lt;span style=&quot;background-color: #dddddd;&quot;&gt;environment.container.contentSize&lt;/span&gt;에 대한 이해가 필요하다. 직접 프린트를 찍어보면 쉽다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;contentOffset&lt;/b&gt;은 &lt;span style=&quot;color: #ee2323;&quot;&gt;CollectionView의 bound를 기준으로 Scroll 결과 보여지는 컨텐츠의 Origin&lt;/span&gt;을 나타낸다.&lt;br /&gt;오른쪽으로 Horizontal Scroll 하면, contentOffset.x의 값은 계속해서 커진다.&lt;/li&gt;
&lt;li&gt;environment.container.contentSize는 공식문서에 명확히 나와있지 않아서 실험이 필요했다.&lt;br /&gt;일단 environmnet는 collectionView layout 관련 정보를 담고 있다.&lt;br /&gt;&lt;b&gt;environment.container.contentSize&lt;/b&gt;는 &lt;span style=&quot;color: #ee2323;&quot;&gt;CollectionView 중에서 현재 Scroll된 Group이 화면에 보이는 높이&lt;/span&gt;를 나타낸다.&lt;br /&gt;이 contentSize의 height를 찍어보면 &lt;u&gt;Horizontal Scroll할 때는 스크린 너비와 같고, Vertical Scroll할 때는 그보다 커진다.&lt;br /&gt;&lt;/u&gt;(아래 gif 참고)&lt;br /&gt;-&amp;gt; 따라서 이 특징을 활용하여 if문의 조건으로 Horizontal Scroll하는 경우를 찾은 뒤, PageControl에 현재 페이지를 보내줬다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;390&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1lVIc/btrEuFYJ09E/8YuqvJtYm3vRFbW7Yeux91/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1lVIc/btrEuFYJ09E/8YuqvJtYm3vRFbW7Yeux91/img.gif&quot; data-alt=&quot;Scroll 방향에 따라 달라지는 offSetX 및 environment height 확인 &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1lVIc/btrEuFYJ09E/8YuqvJtYm3vRFbW7Yeux91/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/1lVIc/btrEuFYJ09E/8YuqvJtYm3vRFbW7Yeux91/img.gif&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;650&quot; height=&quot;507&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;390&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Scroll 방향에 따라 달라지는 offSetX 및 environment height 확인 &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;검색 키워드 : orthogonalScrollingBehavior, page control &lt;br /&gt;&lt;a href=&quot;https://stackoverflow.com/questions/59514541/how-can-i-detect-orthogonal-scroll-events-when-using-uicollectionviewcompositio&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;How can I detect orthogonal scroll events when using `UICollectionViewCompositionalLayout`?&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(근데 이 클로저도 Vertical Scroll할 때 호출되므로 한계가 있어보였지만, &lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;다른 해결방법을 찾지 못했다.)&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;2. Custom Cell 생성&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;CollectionView에 나타내기 위해 총 3개 종류의 Custom Cell을 생성한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;배너에 1개 (BannerCell), 목록에 2개 (GridListCell, TableListCell) 종류가 필요하다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;StackView를 추가하고, 내부에 필요한 UI를 addArrangedSubview 메서드로 넣어서 배치한다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;3. DiffableDataSource 생성 및 SnapShot 적용&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이제&amp;nbsp;&lt;b&gt;DiffableDataSource를 사용하는 4단계 STEP&lt;/b&gt;을 따라가 보자.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Connect a diffable data source to your collection view.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Implement a cell provider to configure your collection view&amp;rsquo;s cells.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Generate the current state of the data.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Display the data in the UI.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;STEP 1~2로 Cell을 등록하고, DiffableDataSource를&amp;nbsp;생성하고,&amp;nbsp;ViewController와&amp;nbsp;연결한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아래처럼 typealias를 활용하면 가독성을 개선할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1655041012523&quot; class=&quot;kotlin&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private typealias DiffableDataSource = UICollectionViewDiffableDataSource&amp;lt;SectionKind, UniqueProduct&amp;gt;  
// ❗제네릭 매개변수로 SectionIdentifier, ItemIdentifier를 설정

private typealias BannerCellRegistration = UICollectionView.CellRegistration&amp;lt;BannerCell, UniqueProduct&amp;gt;
private typealias TableListCellRegistration = UICollectionView.CellRegistration&amp;lt;TableListCell, UniqueProduct&amp;gt;
private typealias GridListCellRegistration = UICollectionView.CellRegistration&amp;lt;GridListCell, UniqueProduct&amp;gt;

private typealias HeaderRegistration = UICollectionView.SupplementaryRegistration&amp;lt;HeaderView&amp;gt;
private typealias FooterRegistration = UICollectionView.SupplementaryRegistration&amp;lt;FooterView&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Section 및 Item의 타입 (SectionIdentifier, ItemIdentifier)은 모두&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Hashable&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;해야 한다.&lt;/li&gt;
&lt;li&gt;Section 타입인 &lt;b&gt;SectionKind&lt;/b&gt;는 위 코드에 있고, Item 타입인 &lt;b&gt;UniqueProduct&lt;/b&gt;는 내부에 UUID 타입의 id 프로퍼티를 갖고 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655040789585&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private var collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewLayout())
private var dataSource: DiffableDataSource!

private func configureCellRegistrationAndDataSource() {
    // ✅ Cell 등록
    // ❗제네릭 매개변수로 Item 타입을 지정해주면 알아서 Item 데이터가 전달됨 (기존의 indexPath가 필요 없음)
    let bannerCellRegistration = BannerCellRegistration { cell, _, uniqueProduct in
        cell.apply(imageURL: uniqueProduct.product.thumbnail, productID: uniqueProduct.product.id)  // Cell에 데이터를 전달하여 화면에 나타냄
    }
    let tableListCellRegistration = TableListCellRegistration { cell, _, uniqueProduct in
        cell.apply(data: uniqueProduct.product)
    }
    let gridListCellRegistration = GridListCellRegistration { cell, _, uniqueProduct in
        cell.apply(data: uniqueProduct.product)
    }

    // ✅ DiffableDataSource 생성
    dataSource = DiffableDataSource(collectionView: collectionView,
                                    cellProvider: { collectionView, indexPath, product in
        guard let sectionKind = SectionKind(rawValue: indexPath.section) else {
            return UICollectionViewCell()
        }

        // 3개 종류의 Cell을 각각 dequeque 하도록 설정
        switch sectionKind {
        case .banner:
            return  collectionView.dequeueConfiguredReusableCell(using: bannerCellRegistration,
                                                                 for: indexPath,
                                                                 item: product)
        case .list:
            switch ProductListViewController.isGrid {
            case true:
                return collectionView.dequeueConfiguredReusableCell(using: gridListCellRegistration,
                                                                    for: indexPath,
                                                                    item: product)
            case false:
                return collectionView.dequeueConfiguredReusableCell(using: tableListCellRegistration,
                                                                    for: indexPath,
                                                                    item: product)
            }
        }
    })
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;3개 종류의 Custom Cell&lt;/b&gt;을 사용하므로 모든 Cell을 등록하고, DataSource를 생성한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;header 및 footer&lt;/b&gt;도 비슷하게 처리하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UICollectionReusableView을 상속받아 Custom View를 생성하여 등록하고, DataSource에 연결시키면 된다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655041470014&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private func configureSupplementaryViewRegistrationAndDataSource() {
    // ✅ header 및 footer 등록
    let headerRegistration = HeaderRegistration(elementKind: SupplementaryKind.header) { view, _, indexPath in
        view.apply(indexPath)
    }
    let footerRegistration = FooterRegistration(elementKind: SupplementaryKind.footer) { [weak self] view, _, indexPath in
        guard let self = self else { return }
        view.bind(input: self.currentBannerPage.asObservable(),
                  indexPath: indexPath,
                  pageNumber: Content.bannerCount)
    }

    // ✅ DiffableDataSource에 반영
    dataSource.supplementaryViewProvider = { [weak self] _, kind, index in
        switch kind {
        case SupplementaryKind.header:
            return self?.collectionView.dequeueConfiguredReusableSupplementary(using: headerRegistration,
                                                                               for: index)
        case SupplementaryKind.footer:
            return self?.collectionView.dequeueConfiguredReusableSupplementary(using: footerRegistration,
                                                                               for: index)
        default:
            return UICollectionReusableView()
        }
    }
}&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;background-color: #ffffff; color: #ee2323;&quot;&gt;S&lt;span style=&quot;background-color: #ffffff; color: #ee2323;&quot;&gt;TEP 3~4로&amp;nbsp;&lt;/span&gt;전체 데이터를 전달해서 초기 Snapshot을 만들고 DiffableDataSource에 적용한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서는 초기화면을 구현할 때, 서버에서 20개의 상품 데이터를 받아서 Snapshot을 만들고 apply 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 Vertical Scroll로 아래로 이동하면, 추가로 20개의 데이터를 받아서 기존 Snapshot에 append하고 apply 했다.&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;먼저 Inital Snapshot을 구성하는 코드이다.&lt;/p&gt;
&lt;pre id=&quot;code_1655041996443&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private var snapshot: NSDiffableDataSourceSnapshot&amp;lt;SectionKind, UniqueProduct&amp;gt;!

private func configureInitialSnapshotWith(listProducts: [UniqueProduct], bannerProducts: [UniqueProduct]) {
    // ✅ 새로운 snapshot 생성
    snapshot = NSDiffableDataSourceSnapshot&amp;lt;SectionKind, UniqueProduct&amp;gt;()

    // ✅ Section 및 Item에 순서대로 데이터를 append
    snapshot.appendSections([.banner])
    snapshot.appendItems(bannerProducts)
    snapshot.appendSections([.list])
    snapshot.appendItems(listProducts)
    
    // ✅ 현재 snapshot을 적용하여 View를 그림
    dataSource.apply(snapshot, animatingDifferences: true)
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;추가 데이터를 받아서 Snapshot을 업데이트할 수 있도록 &lt;b&gt;snapshot 프로퍼티&lt;/b&gt;를 생성했다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;appendSections(), appendItems()&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #5c5c5c;&quot;&gt;를 통해 각 Section마다 원하는 Item 데이터를 넣어준다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Snapshot을 만든 뒤에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;apply&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;메서드를 호출한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;마지막으로 snapshot을 업데이트하는 코드를 보자.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1655042282591&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private func applySnapshotWith(listProducts: [UniqueProduct]) {
    // ✅ snapshot을 업데이트 - list section에만 특정 데이터를 추가함
    snapshot.appendItems(listProducts, toSection: .list)
    
    // ✅ &quot;DiffiableDataSource야, 업데이트한 snapshot을 apply해서 View를 다시 그려줘&quot;
    dataSource.apply(snapshot, animatingDifferences: true)
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버에서 추가로 받아온 상품 데이터를 list section에 append하는 작업이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;appendItems(_:toSection:)&lt;/b&gt; 메서드로 데이터를 추가할 특정 Section을 지정한다.&lt;/li&gt;
&lt;li&gt;위의 configureInitialSnapshotWith 메서드에서 생성한 &lt;b&gt;snapshot에 다시 apply하여 View를 새로 그린다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt; &amp;nbsp;상품&amp;nbsp;상세화면&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;상세화면 코드도 가져와봤다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;여러 이미지를 Scroll할 때 1칸씩 Paging 되도록 구현했다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;paging.gif&quot; data-origin-width=&quot;386&quot; data-origin-height=&quot;802&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5Ehrw/btrEt36nQYH/DHgP7bEd2phpCxqlXPkeCK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5Ehrw/btrEt36nQYH/DHgP7bEd2phpCxqlXPkeCK/img.gif&quot; data-alt=&quot;상품 상세화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5Ehrw/btrEt36nQYH/DHgP7bEd2phpCxqlXPkeCK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/b5Ehrw/btrEt36nQYH/DHgP7bEd2phpCxqlXPkeCK/img.gif&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;350&quot; height=&quot;727&quot; data-filename=&quot;paging.gif&quot; data-origin-width=&quot;386&quot; data-origin-height=&quot;802&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;상품 상세화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지가 들어 갈 위치를 잡아서 CollectionView와 PageControl을 올리고, Custom Cell을 등록하고,&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;DiffableDataSource를 사용하지 않았다.&lt;/p&gt;
&lt;pre id=&quot;code_1655042623057&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// CollectionView 생성
private let imageCollectionView: UICollectionView = {
    let collectionView = UICollectionView(frame: .zero,
                                          collectionViewLayout: UICollectionViewLayout())
    collectionView.translatesAutoresizingMaskIntoConstraints = false
    collectionView.heightAnchor.constraint(equalTo: collectionView.widthAnchor, multiplier: 0.85).isActive = true
    collectionView.backgroundColor = CustomColor.backgroundColor
    collectionView.isScrollEnabled = false
    return collectionView
}()

// PageControl 생성
private let imagePageControl: UIPageControl = {  // 참고 - numberOfPages를 지정은 별도 메서드에서 처리
    let pageControl = UIPageControl()
    pageControl.translatesAutoresizingMaskIntoConstraints = false
    pageControl.pageIndicatorTintColor = .systemGray
    pageControl.currentPageIndicatorTintColor = CustomColor.darkGreenColor
    pageControl.currentPage = 0
    pageControl.isUserInteractionEnabled = false
    pageControl.hidesForSinglePage = true
    return pageControl
}()

// Cell 등록 (1개 이미지가 들어감)
private func configureCollectionView() {
    imageCollectionView.register(ProductDetailImageCell.self,
                                 forCellWithReuseIdentifier: String(describing: ProductDetailImageCell.self))
    imageCollectionView.collectionViewLayout = createCollectionViewLayout()
}

// Compositional Layout 설정
private func createCollectionViewLayout() -&amp;gt; UICollectionViewLayout {
    let layout = UICollectionViewCompositionalLayout { _, _ -&amp;gt; NSCollectionLayoutSection? in
        let screenWidth = UIScreen.main.bounds.width
        let estimatedHeight = NSCollectionLayoutDimension.estimated(screenWidth)
        let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                              heightDimension: estimatedHeight)
        let item = NSCollectionLayoutItem(layoutSize: itemSize)
        item.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 10)  // ✅ item contentInsets 설정

        let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.85),
                                               heightDimension: estimatedHeight)
        let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: 1)

        let section = NSCollectionLayoutSection(group: group)
        section.orthogonalScrollingBehavior = .groupPagingCentered  // ✅ 여기서 설정
        section.visibleItemsInvalidationHandler = { [weak self] _, contentOffset, environment in
            let bannerIndex = Int(max(0, round(contentOffset.x / environment.container.contentSize.width)))
            self?.imagePageControl.currentPage = bannerIndex   // PageControl에 현재 페이지를 반영시킴
        }
        return section
    }
    return layout
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;orthogonalScrollingBehavior에&amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;.groupPagingCentered&lt;/span&gt;을 할당했다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;❗배너의 좌우로 다른 item이 보이게 하기 위해&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;item의 fractionalWidth는 &lt;span style=&quot;color: #ee2323;&quot;&gt;1.0&lt;/span&gt;, group의 fractionalWidth는 &lt;span style=&quot;color: #ee2323;&quot;&gt;0.85&lt;/span&gt;로 설정하고,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;item contentInsets으로 leading 및 trailing에 &lt;span style=&quot;color: #ee2323;&quot;&gt;10&lt;/span&gt;을 설정했다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이때 아래 사진처럼 Cell이 겹치는 문제가 발생한다면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cell의 contentView가 아니라 self에 대해 constraint를 적용해야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1655094151207&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 문제 발생 - Cell이 겹침
private func configureUI() {
    addSubview(productImageView)
    NSLayoutConstraint.activate([
        productImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
        productImageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
        productImageView.topAnchor.constraint(equalTo: contentView.topAnchor),
        productImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
    ])
}

// 해결
private func configureUI() {
    addSubview(productImageView)
    NSLayoutConstraint.activate([
        productImageView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
        productImageView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
        productImageView.topAnchor.constraint(equalTo: self.topAnchor),
        productImageView.bottomAnchor.constraint(equalTo: self.bottomAnchor)
    ])
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LgT28/btrEDQkts2F/NJcJ4ZQMdeoRAvxYBEBXH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LgT28/btrEDQkts2F/NJcJ4ZQMdeoRAvxYBEBXH0/img.png&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;2532&quot; data-is-animation=&quot;false&quot; width=&quot;200&quot; height=&quot;433&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LgT28/btrEDQkts2F/NJcJ4ZQMdeoRAvxYBEBXH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLgT28%2FbtrEDQkts2F%2FNJcJ4ZQMdeoRAvxYBEBXH0%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;1170&quot; height=&quot;2532&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCy6q0/btrEEZ2Bye8/vkrDe7tWpR5o5lHY4oA9Ok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCy6q0/btrEEZ2Bye8/vkrDe7tWpR5o5lHY4oA9Ok/img.png&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;2532&quot; data-is-animation=&quot;false&quot; width=&quot;200&quot; height=&quot;433&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCy6q0/btrEEZ2Bye8/vkrDe7tWpR5o5lHY4oA9Ok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCy6q0%2FbtrEEZ2Bye8%2FvkrDe7tWpR5o5lHY4oA9Ok%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;1170&quot; height=&quot;2532&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;왼쪽 : Cell이 겹치는 문제 -&amp;gt; 오른쪽 : 해결&lt;/figcaption&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;쓰다 보니 글이 길어졌지만, 한 번 예제코드를 작성해보면 생각보다 간단하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;전체 코드는 &lt;a href=&quot;https://github.com/just1103/MVVM-RX-OpenMarket&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;GitHub 링크&lt;/a&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;- Reference&lt;/span&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;span&gt;&lt;span&gt;&lt;span&gt;예제코드 전체 : GitHub &amp;gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://github.com/just1103/MVVM-RX-OpenMarket&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;MVVM-RX-OpenMarket&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span&gt;Apple &amp;gt; Collection View &amp;gt; Sample Code -&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/views_and_controls/collection_views/updating_collection_views_using_diffable_data_sources&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Updating&amp;nbsp;Collection&amp;nbsp;Views&amp;nbsp;Using&amp;nbsp;Diffable&amp;nbsp;Data&amp;nbsp;Sources&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Apple &amp;gt; WWDC 2019 &amp;gt;&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2019/220&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Advances in UI Data Sources&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;관련 포스트&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://applecider2020.tistory.com/37&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[CollectionView] Diffable DataSource 이해하기 (1/3) - Advances in UI Data Sources (WWDC19)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://applecider2020.tistory.com/38&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[CollectionView] Diffable DataSource 이해하기 (2/3) - 흔히 하는 실수, Modern Collection Views 예제코드&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://applecider2020.tistory.com/42&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[CollectionView]&amp;nbsp;배너&amp;nbsp;하단에&amp;nbsp;PageControl&amp;nbsp;구현&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>iOS/영문 공식문서 뜯어보기-iOS</category>
      <category>CollectionView</category>
      <category>CompositionalLayout</category>
      <category>CustomCell</category>
      <category>DiffableDataSource</category>
      <category>orthogonalScrollingBehavior</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/40</guid>
      <comments>https://applecider2020.tistory.com/40#entry40comment</comments>
      <pubDate>Sun, 12 Jun 2022 23:29:29 +0900</pubDate>
    </item>
    <item>
      <title>[CollectionView] Section마다 다른 Scroll Direction 설정하기, Carousel Paging 구현하기 (feat. AppStore) - orthogonalScrollingBehavior</title>
      <link>https://applecider2020.tistory.com/39</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아래의 AppStore처럼 화면을 구현하려면 어떻게 할까?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;일반적인 E-commerce 앱에서도 &quot;상품 배너&quot;와 &quot;상품 목록&quot; 화면을 이런 형태로 구현한 것을 자주 볼 수 있다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;화면을 살펴보면 ✅ &lt;b&gt;맨 위의 Section은 Horizontal Scroll을, 그 아래 Section들은 Vertical Scroll&lt;/b&gt;을 하도록 되어있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;즉, 동일한 CollectionView 내에서 &quot;Section마다 Scroll Direction을 다르게&quot; 지정하고 있다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그리고 ✅ &lt;b&gt;Horizontal Scroll을 할 때, Cell이 한 칸씩 일정하게 움직이고, 양옆의 Item이 살짝 보인다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;appstore1.gif&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;1038&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNDObX/btrDU1m80Ub/UTR1vciFmbQOFmWxTGbVZ1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNDObX/btrDU1m80Ub/UTR1vciFmbQOFmWxTGbVZ1/img.gif&quot; data-alt=&quot;AppStore 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNDObX/btrDU1m80Ub/UTR1vciFmbQOFmWxTGbVZ1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bNDObX/btrDU1m80Ub/UTR1vciFmbQOFmWxTGbVZ1/img.gif&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;280&quot; height=&quot;606&quot; data-filename=&quot;appstore1.gif&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;1038&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;AppStore 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이렇게 Cell이 한 칸씩 일정하게 Scroll되는 화면을 뭐라고 부를까?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Paging, Pagination, &lt;b&gt;Carousel Paging,&lt;/b&gt; Snap Paging 등 다양하게 불린다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;어떻게 검색할지 모르겠을 때는 AppStore like horizontal scroll 등의 키워드로 찾아보면 된다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이런 화면을 구현하는 방법은 여러 가지가 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;원래는 방법-2가 흔했는데, 애플에서 편하게 쓰라고 방법-1을 만들어준 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;방법-1. orthogonalScrollingBehavior 프로퍼티 활용&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;결론부터 말하면, &lt;span style=&quot;background-color: #dddddd;&quot;&gt;orthogonalScrollingBehavior&lt;/span&gt; 프로퍼티를 설정할 때&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;첫 번째 Section은&amp;nbsp;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;.groupPagingCentered&lt;/span&gt;, 두 번째 Section은&amp;nbsp;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;.none&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 style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;'orthogonal'은 수직 방향을 의미한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;CollectionView의 layout axis를 기준으로 수직 방향이라는 뜻이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(위 화면처럼 CollectionView의 layout axis가 vertical이라면 -&amp;gt; orthogonal은 horizontal이다.)&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;orthogonalScrollingBehavior 프로퍼티의 default는 .none이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그래서 아무것도 설정하지 않을 때는 vertical scroll이 되는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;✅ horizontal scroll을 하려면 .continuous, .paging, .groupPagingCentered 등 .none이 아닌 걸로 설정해주면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;✅ 이때 Horizontal Scroll을 하면서 양옆의 Item이 살짝 보이도록 하려면 .groupPagingCentered으로 하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;scrollDirection 프로퍼티와 orthogonalScrollingBehavior 프로퍼티의 차이점은?&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;기본적으로 CollectionView의 Item을 Scroll 하는 방향은 어떻게 결정될까?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;CollectionView의&amp;nbsp;Layout configuration의&amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;scrollDirection&lt;/span&gt;&amp;nbsp;프로퍼티에 vertical 또는 horizontal을 할당하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;하지만 이렇게 하면 CollectionView의 &quot;모든 Section&quot;의 Scroll 방향에 일괄 적용된다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그래서 동일한 CollectionView 내에서 &quot;Section마다 Scroll Direction을 다르게&quot; 지정하려면,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;section의&amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;orthogonalScrollingBehavior&lt;/span&gt;&amp;nbsp;프로퍼티를 설정해야 한다. &lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  CollectionView 공식문서&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  CollectionView 공식문서 하단의&amp;nbsp;&lt;b&gt;Topics&lt;/b&gt;를 잘 살펴봤다면 금방 찾을 수 있다.&lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;CollectionView &amp;gt; Layouts &amp;gt;&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/nscollectionlayoutsection&quot;&gt;Section&lt;/a&gt;&amp;nbsp;&amp;gt; Specifying Scrolling Behavior -&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/nscollectionlayoutsection/3199094-orthogonalscrollingbehavior&quot;&gt;orthogonalScrollingBehavior&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;CollectionView &amp;gt; Layouts &amp;gt;&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/uicollectionlayoutsectionorthogonalscrollingbehavior&quot;&gt;UICollectionLayoutSectionOrthogonalScrollingBehavior&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;공식문서는 아래처럼 친절하게 그림으로도 설명해준다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;953&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GkAb1/btrAhFPaQgH/SvkiUWwTQSxKcKISACpt40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GkAb1/btrAhFPaQgH/SvkiUWwTQSxKcKISACpt40/img.png&quot; data-alt=&quot;UICollectionLayoutSectionOrthogonal 공식문서 그림&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GkAb1/btrAhFPaQgH/SvkiUWwTQSxKcKISACpt40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGkAb1%2FbtrAhFPaQgH%2FSvkiUWwTQSxKcKISACpt40%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;450&quot; height=&quot;472&quot; data-origin-width=&quot;953&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;UICollectionLayoutSectionOrthogonal 공식문서 그림&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; &amp;nbsp;Modern&amp;nbsp;Collection&amp;nbsp;Views&amp;nbsp;예제코드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/views_and_controls/collection_views/implementing_modern_collection_views&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Modern Collection Views 예제코드&lt;/a&gt; 중에도 관련 내용이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #dddddd;&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;*기본적인 예제는&lt;/span&gt; &lt;a href=&quot;https://applecider2020.tistory.com/38&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Diffable DataSource 이해하기 (2/3) - 흔히 하는 실수, Modern Collection Views 예제 코드&lt;/a&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Compositional Layout 폴더 &amp;gt; Advanced Layouts View Controllers 폴더 &amp;gt; OrthogonalScrollBehaviorViewController 파일이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;groupPagingCentered 외에도 &lt;/span&gt;continuous, continuousGroupLeadingBoundary, paging, groupPaging 등&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;orthogonalScrollingBehavior 종류별로 어떤 형태로 Scroll 되는지 볼 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/O3cBD/btrDJvUPDof/mfRKaIDQ2kYj94K4ISHOVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/O3cBD/btrDJvUPDof/mfRKaIDQ2kYj94K4ISHOVK/img.png&quot; data-origin-width=&quot;1284&quot; data-origin-height=&quot;2778&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.4304%; margin-right: 10px;&quot; data-widthpercent=&quot;50.01&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/O3cBD/btrDJvUPDof/mfRKaIDQ2kYj94K4ISHOVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FO3cBD%2FbtrDJvUPDof%2FmfRKaIDQ2kYj94K4ISHOVK%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;1284&quot; height=&quot;2778&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nuKGk/btrDVB2Qu5a/iDrfwKdhGNWZK0bG71Kshk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nuKGk/btrDVB2Qu5a/iDrfwKdhGNWZK0bG71Kshk/img.gif&quot; width=&quot;280&quot; height=&quot;606&quot; data-is-animation=&quot;true&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;1039&quot; data-filename=&quot;paging1.gif&quot; style=&quot;width: 49.4068%;&quot; data-widthpercent=&quot;49.99&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nuKGk/btrDVB2Qu5a/iDrfwKdhGNWZK0bG71Kshk/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnuKGk%2FbtrDVB2Qu5a%2FiDrfwKdhGNWZK0bG71Kshk%2Fimg.gif&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;480&quot; height=&quot;1039&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;Orthogonal Scroll Behavior 예제코드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1654048352292&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;enum SectionKind: Int, CaseIterable {
    case continuous, continuousGroupLeadingBoundary, paging, groupPaging, groupPagingCentered, none

    // ✅ orthogonalScrollingBehavior 종류
    func orthogonalScrollingBehavior() -&amp;gt; UICollectionLayoutSectionOrthogonalScrollingBehavior {
        switch self {
        case .none:
            return UICollectionLayoutSectionOrthogonalScrollingBehavior.none
        case .continuous:
            return UICollectionLayoutSectionOrthogonalScrollingBehavior.continuous
        case .continuousGroupLeadingBoundary:
            return UICollectionLayoutSectionOrthogonalScrollingBehavior.continuousGroupLeadingBoundary
        case .paging:
            return UICollectionLayoutSectionOrthogonalScrollingBehavior.paging
        case .groupPaging:
            return UICollectionLayoutSectionOrthogonalScrollingBehavior.groupPaging
        case .groupPagingCentered:
            return UICollectionLayoutSectionOrthogonalScrollingBehavior.groupPagingCentered
        }
    }
}
    
func createLayout() -&amp;gt; UICollectionViewLayout {

    let config = UICollectionViewCompositionalLayoutConfiguration()
    config.interSectionSpacing = 20

    let layout = UICollectionViewCompositionalLayout(sectionProvider: {
        (sectionIndex: Int, layoutEnvironment: NSCollectionLayoutEnvironment) -&amp;gt; NSCollectionLayoutSection? in
        guard let sectionKind = SectionKind(rawValue: sectionIndex) else { fatalError(&quot;unknown section kind&quot;) }

        // ✅ leadingItem의 contentInsets을 할당
        let leadingItem = NSCollectionLayoutItem(layoutSize: NSCollectionLayoutSize(
            widthDimension: .fractionalWidth(0.7), heightDimension: .fractionalHeight(1.0)))
        leadingItem.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)

        // ✅ trailingItem
        let trailingItem = NSCollectionLayoutItem(layoutSize: NSCollectionLayoutSize(
            widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(0.3)))
        trailingItem.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)
        let trailingGroup = NSCollectionLayoutGroup.vertical(layoutSize: NSCollectionLayoutSize(
            widthDimension: .fractionalWidth(0.3), heightDimension: .fractionalHeight(1.0)),
                                                   subitem: trailingItem,
                                                   count: 2)

        let orthogonallyScrolls = sectionKind.orthogonalScrollingBehavior() != .none
        let containerGroupFractionalWidth = orthogonallyScrolls ? CGFloat(0.85) : CGFloat(1.0)
        // ✅ group의 fractionalWidth를 1.0 보다 작게 설정
        let containerGroup = NSCollectionLayoutGroup.horizontal(
            layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(containerGroupFractionalWidth),
                                               heightDimension: .fractionalHeight(0.4)),
            subitems: [leadingItem, trailingGroup])  // 주의 - trailingItem이 아니라 trailingGroup
            
        let section = NSCollectionLayoutSection(group: containerGroup)
        // ✅ orthogonalScrollingBehavior 설정
        section.orthogonalScrollingBehavior = sectionKind.orthogonalScrollingBehavior()

        // section header 설정
        let sectionHeader = NSCollectionLayoutBoundarySupplementaryItem(
            layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                               heightDimension: .estimated(44)),
            elementKind: OrthogonalScrollBehaviorViewController.headerElementKind,
            alignment: .top)
        section.boundarySupplementaryItems = [sectionHeader]
        return section

    }, configuration: config)
    return layout
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;예제코드답게 SectionKind&lt;/b&gt;는 &lt;span&gt;orthogonalScrollingBehavior 종류별로 여러 개가 있다.&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;enum의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;ort&lt;span style=&quot;color: #333333;&quot;&gt;hogonalScrollingBehavior 메서드&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;에서 각 Section 마다 어떤 형태로 Scroll 할지 지정한다.&lt;br /&gt;이중에서 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;.groupPagingCentered&lt;/b&gt;&lt;/span&gt;&amp;nbsp;방식을 가장 유용하게 쓰고 있다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;createLayout 메서드의 반환값은 CollectionView의&amp;nbsp;&lt;b&gt;collectionViewLayout 프로퍼티&lt;/b&gt;에 할당된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;❗양옆으로 다른 item이 보이게 하려면?&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;item의 fractionalWidth는&lt;b&gt; &lt;span style=&quot;color: #ee2323;&quot;&gt;1.0&lt;/span&gt;&lt;/b&gt;, group의 fractionalWidth는 &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;0.7&lt;/span&gt;&lt;/b&gt; 정도로 설정하고,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;item의 contentInsets을&lt;/span&gt; 좌우 약 &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;10&lt;/span&gt;&lt;/b&gt;으로 설정하면 된다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예제코드를 보면 Cell 크기가 큰 것, 작은 것이 섞여있어서 처음 볼 때는 약간 헷갈릴 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그래서 아래처럼 단순화시킨 화면의 코드도 공유해본다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1284&quot; data-origin-height=&quot;2778&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zi57v/btrDGb38Lka/rFTKHmMcnozamFe8ikYwWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zi57v/btrDGb38Lka/rFTKHmMcnozamFe8ikYwWk/img.png&quot; data-alt=&quot;예제코드를 단순화한 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zi57v/btrDGb38Lka/rFTKHmMcnozamFe8ikYwWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fzi57v%2FbtrDGb38Lka%2FrFTKHmMcnozamFe8ikYwWk%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;280&quot; height=&quot;606&quot; data-origin-width=&quot;1284&quot; data-origin-height=&quot;2778&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;예제코드를 단순화한 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1654268878011&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ✅ 0번 = leadingItem, 1번/2번 = nestedGroup
//   +-----------------------------------------------------+
//   | +---------------------------------+  +-----------+  |
//   | |                                 |  |           |  |
//   | |                                 |  |           |  |
//   | |                                 |  |     1     |  |
//   | |                                 |  |           |  |
//   | |                                 |  |           |  |
//   | |                                 |  +-----------+  |
//   | |               0                 |                 |
//   | |                                 |  +-----------+  |
//   | |                                 |  |           |  |
//   | |                                 |  |           |  |
//   | |                                 |  |     2     |  |
//   | |                                 |  |           |  |
//   | |                                 |  |           |  |
//   | +---------------------------------+  +-----------+  |
//   +-----------------------------------------------------+

func createLayout() -&amp;gt; UICollectionViewLayout {
    let config = UICollectionViewCompositionalLayoutConfiguration() 
    config.interSectionSpacing = 20

    // 매개변수 sectionProvider, configuration
    let layout = UICollectionViewCompositionalLayout(sectionProvider: {
        (sectionIndex: Int, layoutEnvironment: NSCollectionLayoutEnvironment) -&amp;gt; NSCollectionLayoutSection? in
        guard let sectionKind = SectionKind(rawValue: sectionIndex) else { fatalError(&quot;unknown section kind&quot;) }

        // ❗양옆으로 다른 item이 보이게 하는 방법
        // item의 fractionalWidth는 1.0, group의 fractionalWidth는 0.7로 설정하고,
        // item의 contentInsets을 주면 됨
        let leadingItem = NSCollectionLayoutItem(layoutSize: NSCollectionLayoutSize(
            widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(1.0)))
        leadingItem.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)

        // ✅ trailingItem을 사용하지 않고 단순화시킴
//        let trailingItem = NSCollectionLayoutItem(layoutSize: NSCollectionLayoutSize(
//            widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(0.3)))
//        trailingItem.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)
//        let trailingGroup = NSCollectionLayoutGroup.vertical(layoutSize: NSCollectionLayoutSize(
//            widthDimension: .fractionalWidth(0.3), heightDimension: .fractionalHeight(1.0)),
//                                                             subitem: trailingItem,
//                                                             count: 2)

        // group의 width를 .fractionalWidth(0.7)로 주면 양옆으로 다른 item들이 보임 (centerPaging)
        let containerGroup = NSCollectionLayoutGroup.horizontal(
            layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.7),
                                               heightDimension: .fractionalHeight(0.4)),
            subitems: [leadingItem])
//          subitems: [leadingItem, trailingGroup])
        let section = NSCollectionLayoutSection(group: containerGroup)
        section.orthogonalScrollingBehavior = sectionKind.orthogonalScrollingBehavior() // section별로 scroll direction을 다르게 설정!!!

        // header 설정
        let sectionHeader = NSCollectionLayoutBoundarySupplementaryItem(
            layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                              heightDimension: .estimated(44)),
            elementKind: OrthogonalScrollBehaviorViewController.headerElementKind,
            alignment: .top)
        section.boundarySupplementaryItems = [sectionHeader]
        return section
    }, configuration: config)
    return layout
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;방법-2. scrollViewWillEndDragging 메서드 활용&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;ScrollViewDelegate의 scrollViewWillEndDragging 메서드를 사용해도 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;스크롤할 때 Dragging이 끝나기 직전에 호출되는 메서드이다.&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이 방법은 &lt;a href=&quot;https://jintaewoo.tistory.com/33&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이 블로그&lt;/a&gt;에서 친절하게 설명해주고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;코드만 보면 이렇다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1654269193420&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var currentIndex: CGFloat = 0  // 현재 화면에 보이는 페이지의 index

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) {
    // item의 사이즈와 item 간 간격을 구해서 하나의 item 크기로 설정
    let layout = self.collectionView.collectionViewLayout as! UICollectionViewFlowLayout
    let cellWidthIncludingSpacing = layout.itemSize.width + layout.minimumLineSpacing

    // ✅ targetContentOffset 이용하여 x좌표가 얼마나 이동했는지 확인
    // ✅ 이동한 x좌표값과 item 크기를 비교하여 몇 번째 index로 이동할지 구함
    var offset = targetContentOffset.pointee
    let index = (offset.x + scrollView.contentInset.left) / cellWidthIncludingSpacing
    var roundedIndex = round(index)

    // ✅ scrollView, targetContentOffset의 좌표 값으로 스크롤 방향을 알 수 있음
    // index를 반올림 (round)하면 item의 절반 크기만큼 스크롤해야 페이지가 넘어감
    // 스크롤 방향을 체크하여 올림 (ceil), 내림 (floor)을 사용하면 좀 더 자연스러운 페이징 효과가 나옴
    if scrollView.contentOffset.x &amp;gt; targetContentOffset.pointee.x {
        roundedIndex = floor(index)
    } else if scrollView.contentOffset.x &amp;lt; targetContentOffset.pointee.x {
        roundedIndex = ceil(index)
    } else {
        roundedIndex = round(index)
    }

    if isOneStepPaging {
        if currentIndex &amp;gt; roundedIndex {
            currentIndex -= 1
            roundedIndex = currentIndex
        } else if currentIndex &amp;lt; roundedIndex {
            currentIndex += 1
            roundedIndex = currentIndex
        }
    }

    // 위 코드를 통해 이동할 페이지의 좌표값을 targetContentOffset에 대입하면 됨
    offset = CGPoint(x: roundedIndex * cellWidthIncludingSpacing - scrollView.contentInset.left, y: -scrollView.contentInset.top)
    targetContentOffset.pointee = offset
}
// 출처: https://jintaewoo.tistory.com/33&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이렇게 공식문서를 참고해서 아래 화면처럼 상품 배너 및 목록을 구현해봤다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;코드는 이 포스팅을 참고 (링크 추가 예정)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;490&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NOfDb/btrDWEdoS3n/Nv8IBMY0B14KkJkK2rKvoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NOfDb/btrDWEdoS3n/Nv8IBMY0B14KkJkK2rKvoK/img.png&quot; data-alt=&quot;Section 구분 - 배너, 목록&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NOfDb/btrDWEdoS3n/Nv8IBMY0B14KkJkK2rKvoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNOfDb%2FbtrDWEdoS3n%2FNv8IBMY0B14KkJkK2rKvoK%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;300&quot; height=&quot;551&quot; data-origin-width=&quot;490&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Section 구분 - 배너, 목록&lt;/figcaption&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&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;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예제코드 : Apple &amp;gt;&amp;nbsp;Collection View &amp;gt;&amp;nbsp;Sample Code -&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/views_and_controls/collection_views/implementing_modern_collection_views&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Implementing&amp;nbsp;Modern&amp;nbsp;Collection&amp;nbsp;Views&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Apple &amp;gt; WWDC 2019 &amp;gt;&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2019/220&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Advances in UI Data Sources&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;Apple Developer Documentation &amp;gt;&amp;nbsp;&lt;/span&gt;Coll&lt;/span&gt;ectionView &amp;gt; Layouts &amp;gt;&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/nscollectionlayoutsection&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Section&lt;/a&gt;&amp;nbsp;&amp;gt; Specifying Scrolling Behavior -&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/nscollectionlayoutsection/3199094-orthogonalscrollingbehavior&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;orthogonalScrollingBehavior&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;Apple Developer Documentation &amp;gt;&amp;nbsp;&lt;/span&gt;Collecti&lt;/span&gt;onView &amp;gt; Layouts &amp;gt;&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/uicollectionlayoutsectionorthogonalscrollingbehavior&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;UICollectionLayoutSectionOrthogonalScrollingBehavior&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Blog &amp;gt; &lt;a href=&quot;https://jintaewoo.tistory.com/33&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;CollectionView paging 해보기&lt;/a&gt;&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>AppStore like Scroll</category>
      <category>CollectionView</category>
      <category>orthogonalScrollingBehavior</category>
      <category>ScrollDirection</category>
      <category>scrollViewWillEndDragging</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/39</guid>
      <comments>https://applecider2020.tistory.com/39#entry39comment</comments>
      <pubDate>Wed, 8 Jun 2022 22:13:57 +0900</pubDate>
    </item>
    <item>
      <title>[CollectionView] Diffable DataSource 이해하기 (2/3) - 흔히 하는 실수, Modern Collection Views 예제 코드</title>
      <link>https://applecider2020.tistory.com/38</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;저번에는 &lt;a href=&quot;https://applecider2020.tistory.com/37&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[CollectionView] Diffable DataSource 이해하기 (1/3) - Advances in UI Data Sources (WWDC19) 포스팅&lt;/a&gt;에서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Diffable DataSource의 장점과 기초 개념을 알 아봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 Implementing Modern Collection Views 예제 코드를 보면서 이해도를 높여보자.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;흔히 하는 실수-1&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Diffable DataSource는 간단해 보이지만 막상 써보면 막히는 부분이 있는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 프로젝트에서는 여러 종류의 Section을 사용하거나, 여러 종류의 Custom Cell을 쓰기 때문인 것 같다.&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;span style=&quot;color: #ee2323;&quot;&gt;&lt;span&gt;❗ &lt;/span&gt;&quot;여러 Section에 동일한 Item을 반영하는 것&quot;&lt;/span&gt;&amp;nbsp;때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 컴파일은 되지만, View를 보면 마지막 Section에만 Item이 나타나게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 콘솔 로그에 아래의 오류 문구가 뜬다..!&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;[UIDiffableDataSource] Diffable data source detected an attempt to insert or append 5 item identifiers that already exist in the snapshot. The existing item identifiers will be moved into place instead, but this operation will be more expensive. For best performance, inserted item identifiers should always be unique. Set a symbolic breakpoint on BUG_IN_CLIENT_OF_DIFFABLE_DATA_SOURCE__IDENTIFIER_ALREADY_EXISTS to catch this in the debugger. Item identifiers that already exist: ( // 중복 Item 정보 )&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;snapshot에는 동일한 item이 들어갈 수 없다&quot;&lt;/b&gt;는 내용이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식문서에서 Section 타입 (Section identifier)과 Item 타입 (Item identifier)은 모두 Hashable해야 한다고 했던 게 이 때문이다.&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;*그럼에도 불구하고 View에 동일한 콘텐츠를 나타내고 싶다면? &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;item identifier가 중복되지 않도록 Item 타입에 UUID 등 identifier 프로퍼티를 추가해서 다른 item인 척해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;흔히 하는 실수-2&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DiffableDataSource를 사용할 때 Custom Cell에 데이터를 전달하는 방법도 낯설다.&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;기존에는 DataSource 역할을 하는 items 배열이 있고, items[indexPath.row] 형태로 데이터에 접근한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 DiffableDataSource에서는 indexPath가 필요 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;span&gt;❗&lt;/span&gt;Cell register를 할 때 제네릭 매개변수로 Item 타입을 지정해주면 알아서 Item 데이터가 전달된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;snapshot을 통해 Item 데이터를 전달받는데, 이때 모든 Item이 Hashable하기 때문에 가능한 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1653643488040&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 제네릭 매개변수 ItemIdentifier에다가 Product 타입을 전달
let bannerCellRegistration = BannerCellRegistration&amp;lt;BannerCell, Product&amp;gt; { cell, indexPath, product in
//  cell.titleLabel.text = products[indexPath.row]  // 잘못된 예시
    cell.titleLabel.text = product  // cellProvider의 product (itemIdentifier)를 전달받음
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 오류를 피하려면 애플이 만들어준 예제 코드를 자세히 살펴봐야 한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Implementing&amp;nbsp;Modern&amp;nbsp;Collection&amp;nbsp;Views&amp;nbsp;예제&amp;nbsp;코드&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3개 예제 코드를 살펴볼 텐데, 큰 틀은 모두 똑같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1️⃣ Cell을 등록하고, 2️⃣ Diffable DataSource를 생성하고, 3️⃣ Snapshot을 만들어 적용하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;예제를 볼 때, 가장 먼저 DataSource와 Snapshot의 제네릭 매개변수 &amp;lt;section, item&amp;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&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;Compositional layout도 중요한 주제인데 별도로 포스팅할 예정이다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예제 종류-1. 새로운 Snapshot을 생성하여 적용 (1개 Section)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Diffable 폴더의 MountainsViewController 파일이다.&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;b&gt;Section 종류도 1개이고, Item 및 Cell 타입도 1개 종류 &lt;/b&gt;를 사용하는 가장 기본적인 예제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 전체 데이터를 보여주도록 View를 그렸다가,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 이벤트를 받으면 일부 데이터만 보이도록 View를 업데이트한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;wwdc_diffable1.gif&quot; data-origin-width=&quot;256&quot; data-origin-height=&quot;532&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NS34l/btrDl0o7Z0t/EAOknKUtNVxT9x5HbjhyQ0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NS34l/btrDl0o7Z0t/EAOknKUtNVxT9x5HbjhyQ0/img.gif&quot; data-alt=&quot;Mountains 예제코드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NS34l/btrDl0o7Z0t/EAOknKUtNVxT9x5HbjhyQ0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/NS34l/btrDl0o7Z0t/EAOknKUtNVxT9x5HbjhyQ0/img.gif&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;256&quot; height=&quot;532&quot; data-filename=&quot;wwdc_diffable1.gif&quot; data-origin-width=&quot;256&quot; data-origin-height=&quot;532&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Mountains 예제코드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드에서 2개만 확인하면 된다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;먼저 전체 Mountains 데이터를 하나씩 Cell에 담아 나타낸다. &lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;-&amp;gt; 전체 데이터로 inital snapshot을 생성&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;상단의 SearchBar에 사용자가 문자를 입력할 때마다, 입력값을  포함하는 검색결과만 필터링하여 View를 업데이트한다.&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;-&amp;gt; 데이터를 필터링하여 새로운 snapshot을 생성&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1653807479561&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Mountains 데이터 (Raw Data)
let mountainsRawData = &quot;&quot;&quot;
Mount Everest,8848
K2,8611
Kangchenjunga,8586
Lhotse,8516
//...
&quot;&quot;&quot;

// ✅ Section 타입 (SectionIdentifier)
enum Section: CaseIterable {
    case main
}

// ✅ Item 타입 (ItemIdentifier)
struct Mountain: Hashable {
    let name: String
    let height: Int
    let identifier = UUID()  // Item identifier는 unique해야 하므로
    
    func hash(into hasher: inout Hasher) { // dataSource가 snapshot이 달라진 것 인식하기 위해 필요함
        hasher.combine(identifier)
    }
    static func == (lhs: Mountain, rhs: Mountain) -&amp;gt; Bool {
        return lhs.identifier == rhs.identifier
    }
    func contains(_ filter: String?) -&amp;gt; Bool {
        guard let filterText = filter else { return true }
        if filterText.isEmpty { return true }
        let lowercasedFilter = filterText.lowercased()  // 기존 데이터, 입력값 모두 소문자로 전환하여 비교
        return name.lowercased().contains(lowercasedFilter)
    }
}

class MountainsViewController: UIViewController {
    var mountainsCollectionView: UICollectionView!
    var dataSource: UICollectionViewDiffableDataSource&amp;lt;Section, MountainsController.Mountain&amp;gt;!
    
    func configureDataSource() {
        // 1️⃣ Cell 등록
        let cellRegistration = UICollectionView.CellRegistration
        &amp;lt;LabelCell, MountainsController.Mountain&amp;gt; { (cell, indexPath, mountain) in
            // Populate the cell with our item description.
            cell.label.text = mountain.name
        }
        
        // 2️⃣ DiffableDataSource 생성
        dataSource = UICollectionViewDiffableDataSource&amp;lt;Section, MountainsController.Mountain&amp;gt;(collectionView: mountainsCollectionView) {
            (collectionView: UICollectionView, indexPath: IndexPath, identifier: MountainsController.Mountain) -&amp;gt; UICollectionViewCell? in
            return collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: identifier)
        }
    }
}

// 3️⃣ Snapshot 생성 및 적용
// 1) 화면을 띄울 때 (viewDidLoad), 2) SearchBar 입력값이 바뀔 때 (textDidChange) 호출됨
func performQuery(with filter: String?) {
    // SearchBar 입력값을 포함하는 item만 필터링
    let mountains = mountainsController.filteredMountains(with: filter).sorted { $0.name &amp;lt; $1.name }

    // ✅ 새로운 snapshot 생성
    var snapshot = NSDiffableDataSourceSnapshot&amp;lt;Section, MountainsController.Mountain&amp;gt;() 
    snapshot.appendSections([.main])
    snapshot.appendItems(mountains)  // 필터링된 item만 snapshot에 반영시킴  
    
    // ✅ &quot;DiffiableDataSource야, 업데이트한 snapshot을 apply해서 View를 다시 그려줘&quot;
    dataSource.apply(snapshot, animatingDifferences: true) 
}&lt;/code&gt;&lt;/pre&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;h3 data-ke-size=&quot;size23&quot;&gt;예제 종류-2. 2개 종류의 Section을 사용 (동일한 Item 타입)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 Diffable 폴더의 WiFiSettingsViewController 파일이다.&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;설정 앱의 WiFi 탭과 비슷하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째 Section은 WiFi 연결 상태 (ON/OFF, 현재 연결된 네트워크), 두번째 Section은 연결 가능한 네트워크를 보여준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Simulator Screen Recording - iPhone 13 Pro Max - 2022-05-29 at 16.41.02.gif&quot; data-origin-width=&quot;296&quot; data-origin-height=&quot;640&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmulkr/btrDtgDzx5z/7tz7RtumiYn5LAVNsFQ3eK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmulkr/btrDtgDzx5z/7tz7RtumiYn5LAVNsFQ3eK/img.gif&quot; data-alt=&quot;WiFi 예제코드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmulkr/btrDtgDzx5z/7tz7RtumiYn5LAVNsFQ3eK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bmulkr/btrDtgDzx5z/7tz7RtumiYn5LAVNsFQ3eK/img.gif&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;296&quot; height=&quot;640&quot; data-filename=&quot;Simulator Screen Recording - iPhone 13 Pro Max - 2022-05-29 at 16.41.02.gif&quot; data-origin-width=&quot;296&quot; data-origin-height=&quot;640&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;WiFi 예제코드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 &lt;b&gt;Section이 2개 종류이고, Item 및 Cell은 1개 종류&lt;/b&gt;를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;어떻게 여러 Section을 구현하고, 각각 원하는 Item을 넣는지 보자.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1653808341102&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Section 타입 (2개 종류)
enum Section: CaseIterable {
    case config, networks  
}

// Item 타입
struct Item: Hashable {
    let title: String
    let type: ItemType
    let network: WiFiController.Network?
}

class WiFiSettingsViewController: UIViewController {
    let tableView = UITableView(frame: .zero, style: .insetGrouped)
    var dataSource: UITableViewDiffableDataSource&amp;lt;Section, Item&amp;gt;! = nil
    var currentSnapshot: NSDiffableDataSourceSnapshot&amp;lt;Section, Item&amp;gt;! = nil
    
    // 1️⃣ Cell 등록
    func configureTableView() {
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: WiFiSettingsViewController.reuseIdentifier)
        //...
    }
    
    // 2️⃣ DiffableDataSource 생성
    func configureDataSource() {
        self.dataSource = UITableViewDiffableDataSource&amp;lt;Section, Item&amp;gt;(tableView: tableView) { [weak self] (tableView: UITableView, indexPath: IndexPath, item: Item) -&amp;gt; UITableViewCell? in
            guard let self = self, let wifiController = self.wifiController else { return nil }
            let cell = tableView.dequeueReusableCell(withIdentifier: WiFiSettingsViewController.reuseIdentifier, for: indexPath)
            // cell의 상태 (isNetwork / isConfig)에 따라 Layout을 변경함 (생략)
        }   
    }
    
    // 3️⃣ snapshot 생성 및 적용
    func updateUI(animated: Bool = true) {
        guard let controller = self.wifiController else { return }
        let configItems = configurationItems.filter { !($0.type == .currentNetwork &amp;amp;&amp;amp; !controller.wifiEnabled) }

        currentSnapshot = NSDiffableDataSourceSnapshot&amp;lt;Section, Item&amp;gt;()
        currentSnapshot.appendSections([.config])  // ✅ 1번 Section 및 Item 설정
        currentSnapshot.appendItems(configItems, toSection: .config)

        // wifi가 연결되면 2번 Section이 나타남
        if controller.wifiEnabled {
            let sortedNetworks = controller.availableNetworks.sorted { $0.name &amp;lt; $1.name }
            let networkItems = sortedNetworks.map { Item(network: $0) }
            currentSnapshot.appendSections([.networks])  // ✅ 2번 Section 및 Item 설정
            currentSnapshot.appendItems(networkItems, toSection: .networks)
        }

        self.dataSource.apply(currentSnapshot, animatingDifferences: animated)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;appendSections(), appendItems()&lt;/b&gt;를 통해 각 Section마다 원하는 Item 데이터를 넣어준다. &lt;br /&gt;앞의&amp;nbsp;예제 코드와 뭐가 다를까?&lt;b&gt;&lt;br /&gt;&lt;/b&gt;이 번에는 Section 종류가 여러 개라서 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;appendItems(_:&lt;span style=&quot;color: #ee2323;&quot;&gt;toSection:&lt;/span&gt;)&lt;/span&gt; 메서드로 &lt;span style=&quot;color: #ee2323;&quot;&gt;특정 Section을 지정하여 Item을 넣는다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;또한 &lt;b&gt;호출 순서&lt;/b&gt;도 중요하다.&lt;br /&gt;appendSections([.section1]) - appendItems(items1, toSection: .section1) - appendSections([.section2]) - appendItems(items2, toSection: .section2) 순으로 호출해야 한다.&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;(appendSections([.section1]) - appendSections([.section2]) 등으로 호출하면 안된다는 뜻)&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✨ 예제 종류-3. Section마다 다른 Cell 타입을 사용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compositional Layout 폴더의 Basics View Controllers 폴더의 DistinctSectionsViewController 파일이다.&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;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;Section이 3개 종류이고, Cell은 2개 종류&lt;/b&gt;를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Section 마다 &lt;b&gt;Item의 Layout을 다르게 설정했다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMxgu4/btrDoTW0psQ/BtQl3DHDywotQDQ00yPiuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMxgu4/btrDoTW0psQ/BtQl3DHDywotQDQ00yPiuk/img.png&quot; data-origin-width=&quot;1284&quot; data-origin-height=&quot;2778&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMxgu4/btrDoTW0psQ/BtQl3DHDywotQDQ00yPiuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMxgu4%2FbtrDoTW0psQ%2FBtQl3DHDywotQDQ00yPiuk%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;1284&quot; height=&quot;2778&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KZpW9/btrDlYLI7Uj/0TLcYVEbGrp7mSDFWJfYo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KZpW9/btrDlYLI7Uj/0TLcYVEbGrp7mSDFWJfYo1/img.png&quot; data-origin-width=&quot;1284&quot; data-origin-height=&quot;2778&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KZpW9/btrDlYLI7Uj/0TLcYVEbGrp7mSDFWJfYo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKZpW9%2FbtrDlYLI7Uj%2F0TLcYVEbGrp7mSDFWJfYo1%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;1284&quot; height=&quot;2778&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;Distinct Sections 예제코드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드에서 2개만 확인하면 된다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Section마다 다른 Cell 타입을 사용했다.&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;-&amp;gt; 첫번째 Section은 List 형태 (ListCell 타입)로,  두/세번째 Section은 Grid 형태 (TextCell 타입)로 dequeue한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;또한 Section 마다 한 줄에 몇 개의 Item을 보여줄지 다르게 설정했다.&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;-&amp;gt; 첫번째 Section은 한 줄에&lt;span&gt;&amp;nbsp;&lt;/span&gt;Item이 1개, 두번째 Section은 5개, 세번째 Section은 3개씩 배치된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1653811153247&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Section별로 다른 Layout을 사용
enum SectionLayoutKind: Int, CaseIterable {
    case list, grid5, grid3  // rawValue가 각각 0,1,2로 지정됨

    // createLayout()에서 호출되며, 1개 row에 몇 개의 item이 들어갈지 설정함
    var columnCount: Int {  
        switch self {
        case .grid3:
            return 3
        case .grid5:
            return 5
        case .list:
            return 1
        }
    }
}

class DistinctSectionsViewController: UIViewController {
    var dataSource: UICollectionViewDiffableDataSource&amp;lt;SectionLayoutKind, Int&amp;gt;! = nil
    var collectionView: UICollectionView! = nil

    func configureDataSource() {
        // 1️⃣ Cell 등록 
        // 2개 종류의 Cell Type을 사용 (ListCell / TextCell)
        let listCellRegistration = UICollectionView.CellRegistration&amp;lt;ListCell, Int&amp;gt; { (cell, indexPath, identifier) in
            cell.label.text = &quot;\(identifier)&quot;
        }

        let textCellRegistration = UICollectionView.CellRegistration&amp;lt;TextCell, Int&amp;gt; { (cell, indexPath, identifier) in
            cell.label.text = &quot;\(identifier)&quot;
            //...
        }
        
        // 2️⃣ DiffableDataSource 생성
        // ✅ 주의 - dataSource 프로퍼티를 2개로 나누지 않음!
        dataSource = UICollectionViewDiffableDataSource&amp;lt;SectionLayoutKind, Int&amp;gt;(collectionView: collectionView) {
            (collectionView: UICollectionView, indexPath: IndexPath, identifier: Int) -&amp;gt; UICollectionViewCell? in
            // 일반적인 dataSource를 사용할 때처럼 화면에 보이는 Cell의 개수만큼 호출됨
            // ✅ Setion이 .list이면 ListCell 타입, .grid5, .grid3이면 TextCell 타입으로 dequeue
            return SectionLayoutKind(rawValue: indexPath.section)! == .list ?
            collectionView.dequeueConfiguredReusableCell(using: listCellRegistration, for: indexPath, item: identifier) :
            collectionView.dequeueConfiguredReusableCell(using: textCellRegistration, for: indexPath, item: identifier)
        }

        // 3️⃣ snapshot 생성 및 적용
        // initial data
        let itemsPerSection = 10
        var snapshot = NSDiffableDataSourceSnapshot&amp;lt;SectionLayoutKind, Int&amp;gt;()
        SectionLayoutKind.allCases.forEach {
            snapshot.appendSections([$0])
            // ✅ Section마다 다른 Item을 지정 - Section1은 1~9, Section2는 10~19, Section3은 20~29로 구성
            let itemOffset = $0.rawValue * itemsPerSection
            let itemUpperbound = itemOffset + itemsPerSection
            snapshot.appendItems(Array(itemOffset..&amp;lt;itemUpperbound))
        }
        dataSource.apply(snapshot, animatingDifferences: false)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;dequeue 메서드가 호출되는 시점은 언제일까?&lt;br /&gt;초기화면에서는 일반적인 dataSource를 사용할 때처럼 &quot;화면에 보이는 Cell의 개수만큼&quot; 호출된다.&lt;/li&gt;
&lt;li&gt;이때 &lt;b&gt;Setion이 .list이면 ListCell 타입, .grid5, .grid3이면 TextCell 타입으로 dequeue한다.&lt;/b&gt;&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Cell 타입이 다르다고 dataSource를 여러 개 만들면 안된다. dataSource는 1개여야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Snapshot을 생성하는 부분은 2번 예제와 비슷하지만 살짝 다르다.&lt;br /&gt;&lt;b&gt;appendSections(), appendItems()&lt;/b&gt; 메서드를 사용한다. &lt;span style=&quot;color: #ee2323;&quot;&gt;(appendItems(_:toSection:) 메서드를 쓰지 않았음)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;이 예제에서 Section에 들어가는 Item의 타입은 모두 Int이다.&lt;br /&gt;&lt;b&gt;만약 Section마다 다른 타입의 Item을 넣어주고 싶다면?&lt;/b&gt;&lt;br /&gt;해당 Item들을 &lt;span style=&quot;color: #ee2323;&quot;&gt;추상화&lt;/span&gt; (프로토콜 타입)해서 사용해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고 - Compositional Layout을 사용하여 Item의 배치 형태를 설정하는 코드는 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1653815914630&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func createLayout() -&amp;gt; UICollectionViewLayout {
    // ✅ sectionProvider를 통해 CollectionView 관련 정보를 받아서 활용함 (cellProvider와 비슷)
    let layout = UICollectionViewCompositionalLayout { (sectionIndex: Int,
        layoutEnvironment: NSCollectionLayoutEnvironment) -&amp;gt; NSCollectionLayoutSection? in

        guard let sectionLayoutKind = SectionLayoutKind(rawValue: sectionIndex) else { return nil }  // section의 rawValue로 column을 찾아둠
        let columns = sectionLayoutKind.columnCount  // ✅ Section 종류별로 column 개수가 다름

        // The group auto-calculates the actual item width to make
        // the requested number of columns fit, so this widthDimension is ignored.
        let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                             heightDimension: .fractionalHeight(1.0))
        let item = NSCollectionLayoutItem(layoutSize: itemSize)
        item.contentInsets = NSDirectionalEdgeInsets(top: 2, leading: 2, bottom: 2, trailing: 2)

        let groupHeight = columns == 1 ?
            NSCollectionLayoutDimension.absolute(44) :
            NSCollectionLayoutDimension.fractionalWidth(0.2)
        let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                              heightDimension: groupHeight)
        let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: columns) // ✅ count의 매개변수로 전달됨

        let section = NSCollectionLayoutSection(group: group)
        section.contentInsets = NSDirectionalEdgeInsets(top: 20, leading: 20, bottom: 20, trailing: 20)
        return section
    }
    return layout
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예제 종류-4. Header / Footer 구현&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compositional Layout 폴더의 Basics View Controllers 폴더의 SectionHeadersFootersViewController 파일이다.&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;마지막으로 Header 와 Footer를 구현한 코드를 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아주 간단하다. &lt;span style=&quot;color: #ee2323;&quot;&gt;1️⃣ Cell을 등록하고, 2️⃣ Diffable DataSource 에 끼워넣기만 하면 된다.&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;위에서 Item에 들어갈 Cell을 등록했듯이 &lt;b&gt;Header/Footer에 들어갈 Cell을 등록&lt;/b&gt;하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 Cell은 &lt;b&gt;UICollectionReusableView&lt;/b&gt;를 상속받도록 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lYRdo/btrDpP7Tfmq/EeCzWKIUvKflZnbzsXuK70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lYRdo/btrDpP7Tfmq/EeCzWKIUvKflZnbzsXuK70/img.png&quot; data-origin-width=&quot;1284&quot; data-origin-height=&quot;2778&quot; data-is-animation=&quot;false&quot; width=&quot;200&quot; height=&quot;433&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lYRdo/btrDpP7Tfmq/EeCzWKIUvKflZnbzsXuK70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlYRdo%2FbtrDpP7Tfmq%2FEeCzWKIUvKflZnbzsXuK70%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;1284&quot; height=&quot;2778&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vDIVj/btrDnq8QnL9/ksfBM4ImIoV1WkMWgfW771/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vDIVj/btrDnq8QnL9/ksfBM4ImIoV1WkMWgfW771/img.png&quot; data-origin-width=&quot;1284&quot; data-origin-height=&quot;2778&quot; data-is-animation=&quot;false&quot; width=&quot;200&quot; height=&quot;433&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vDIVj/btrDnq8QnL9/ksfBM4ImIoV1WkMWgfW771/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvDIVj%2FbtrDnq8QnL9%2FksfBM4ImIoV1WkMWgfW771%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;1284&quot; height=&quot;2778&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;Headers/Footers 예제코드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1653817374878&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ✅ Custom Header/Footer Cell 타입
class TitleSupplementaryView: UICollectionReusableView {
    let label = UILabel()
    //...
}
    
class SectionHeadersFootersViewController: UIViewController {
    static let sectionHeaderElementKind = &quot;section-header-element-kind&quot;
    static let sectionFooterElementKind = &quot;section-footer-element-kind&quot;

    var dataSource: UICollectionViewDiffableDataSource&amp;lt;Int, Int&amp;gt;! = nil
    var collectionView: UICollectionView! = nil

    func configureDataSource() {
        // 1️⃣ Header Cell 등록 
        let headerRegistration = UICollectionView.SupplementaryRegistration
        &amp;lt;TitleSupplementaryView&amp;gt;(elementKind: SectionHeadersFootersViewController.sectionHeaderElementKind) {
            (supplementaryView, string, indexPath) in
            supplementaryView.label.text = &quot;\(string) for section \(indexPath.section)&quot;
            //...
        }
        
        // 1️⃣ Footer Cell 등록 
        let footerRegistration = UICollectionView.SupplementaryRegistration
        &amp;lt;TitleSupplementaryView&amp;gt;(elementKind: SectionHeadersFootersViewController.sectionFooterElementKind) {
            (supplementaryView, string, indexPath) in
            supplementaryView.label.text = &quot;\(string) for section \(indexPath.section)&quot;
            //...
        }
            
        dataSource = UICollectionViewDiffableDataSource&amp;lt;Int, Int&amp;gt;(collectionView: collectionView) {
            (collectionView: UICollectionView, indexPath: IndexPath, identifier: Int) -&amp;gt; UICollectionViewCell? in
            return collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: identifier)
        }
        
        // 2️⃣ DiffableDataSource에 끼워넣기
        dataSource.supplementaryViewProvider = { (view, kind, index) in
            return self.collectionView.dequeueConfiguredReusableSupplementary(
                using: kind == SectionHeadersFootersViewController.sectionHeaderElementKind ? headerRegistration : footerRegistration, for: index)
        }

        // Snapshot 생성 및 적용 (다른 예제와 동일함)
        // initial data
        let itemsPerSection = 5
        let sections = Array(0..&amp;lt;5)
        var snapshot = NSDiffableDataSourceSnapshot&amp;lt;Int, Int&amp;gt;()
        var itemOffset = 0
        sections.forEach {
            snapshot.appendSections([$0])
            snapshot.appendItems(Array(itemOffset..&amp;lt;itemOffset + itemsPerSection))
            itemOffset += itemsPerSection
        }
        dataSource.apply(snapshot, animatingDifferences: false)
    }&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Custom Header/Footer Cell을 구현하고, 해당 Cell을 등록했다.&lt;/li&gt;
&lt;li&gt;dataSource에 끼워넣을 때는 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;dataSource.supplementaryViewProvider &lt;/span&gt;에 클로저를 전달하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 Compositional Layout을 생성하는 부분에서도 작업이 필요하다.&lt;/p&gt;
&lt;pre id=&quot;code_1653817074239&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func createLayout() -&amp;gt; UICollectionViewLayout {
    let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                         heightDimension: .fractionalHeight(1.0))
    let item = NSCollectionLayoutItem(layoutSize: itemSize)
    let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                          heightDimension: .absolute(44))
    let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
    let section = NSCollectionLayoutSection(group: group)
    section.interGroupSpacing = 5
    section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 10)

    // ✅ Header/Footer 관련 설정
    let headerFooterSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                                 heightDimension: .estimated(44))
    let sectionHeader = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerFooterSize,
        elementKind: SectionHeadersFootersViewController.sectionHeaderElementKind, alignment: .top)
    let sectionFooter = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerFooterSize,
        elementKind: SectionHeadersFootersViewController.sectionFooterElementKind, alignment: .bottom) 
    // ✅ 주의 - alignment는 Header는 .top, Footer는 .bottom으로 설정
    section.boundarySupplementaryItems = [sectionHeader, sectionFooter]

    let layout = UICollectionViewCompositionalLayout(section: section)
    return layout
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;section의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;boundarySupplementaryItems&lt;/span&gt;에 Header 및 Footer를 넣어주면 된다.&lt;/li&gt;
&lt;li&gt;이때 &lt;span style=&quot;color: #ee2323;&quot;&gt;alignment&lt;/span&gt;는&amp;nbsp;Header는&amp;nbsp;.top,&amp;nbsp;Footer는&amp;nbsp;.bottom으로&amp;nbsp;설정해야 한다.&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;다음 포스팅에서는 개인적으로 인디앱 개발 프로젝트를 진행하면서 Diffable DataSource를 적용했던 코드를 살펴보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 E-commerce 분야 상용앱에서 자주 보이는 화면인데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화면 상단에 &lt;b&gt;Horizontal Scroll이 가능한 배너&lt;/b&gt; (Banner  Section)와 &lt;b&gt;Vertical Scroll이 가능한 상품 목록&lt;/b&gt; (List Section)을 CollectionView로 구현해봤다.&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;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference&lt;/span&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;span&gt;&lt;span&gt;예제코드 : Apple &amp;gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Collection View &amp;gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Sample Code -&lt;/span&gt;&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/views_and_controls/collection_views/implementing_modern_collection_views&quot;&gt;Implementing&amp;nbsp;Modern&amp;nbsp;Collection&amp;nbsp;Views&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span&gt;Apple &amp;gt; Collection View &amp;gt; Sample Code -&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/views_and_controls/collection_views/updating_collection_views_using_diffable_data_sources&quot;&gt;Updating&amp;nbsp;Collection&amp;nbsp;Views&amp;nbsp;Using&amp;nbsp;Diffable&amp;nbsp;Data&amp;nbsp;Sources&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Apple &amp;gt; WWDC 2019 &amp;gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2019/220&quot;&gt;Advances in UI Data Sources&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Apple &amp;gt; WWDC 2020 &amp;gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2020/10045/?time=8&quot;&gt;Advances in diffable data sources&lt;/a&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>iOS/영문 공식문서 뜯어보기-iOS</category>
      <category>CollectionView</category>
      <category>Compositional Layout</category>
      <category>DiffableDataSource</category>
      <category>TableView</category>
      <category>WWDC</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/38</guid>
      <comments>https://applecider2020.tistory.com/38#entry38comment</comments>
      <pubDate>Mon, 30 May 2022 10:05:03 +0900</pubDate>
    </item>
    <item>
      <title>[CollectionView] Diffable DataSource 이해하기 (1/3) - Advances in UI Data Sources (WWDC19)</title>
      <link>https://applecider2020.tistory.com/37</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;CollectionView / TableView와 관련해서&amp;nbsp;&lt;b&gt;&quot;Diffable DataSource&quot;&lt;/b&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;&lt;span&gt;✏️ &lt;/span&gt;새로운 기술을 습득하기 가장 좋은 방법은 Apple이 만든 WWDC 영상을 보는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UIKit 담당 팀에서 직접 기술이 등장한 배경, 활용 방법에 대해 짜임새 있게 소개하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음엔 공식문서의 예제를 따라 하면서 직접 View를 그려보면 된다.&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;Diffable DataSource가 뭔지 간단히 정리하고,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WWDC 세션 내용을 살펴보자.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Diffable DataSource란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Diffable = Different + Ability&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Diffable은 &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;CollectionView / TableView를 그리기 위해서는 DataSource가 필요하다. &lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;기존에는&lt;/span&gt; &lt;a href=&quot;https://developer.apple.com/documentation/uikit/uicollectionviewdatasource&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;CollectionView DataSource Protocol&lt;/a&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;을 채택하고, Protocol에 구현되어 있던 메서드를 사용하는 방법을 사용했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;사용자 이벤트를 받아서 View를 업데이트해야 하는 상황을 가정해보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;기존 방법으로는 데이터를 변경한 뒤에 전체 View를 다시 그리도록 reloadData() 또는 performBatchUpdates()를 호출해야 했다.&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;Diffable DataSource는 새로운 방식의 DataSource이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Cell에 어떤 종류의 데이터가 들어갈지만 정해놓고, 데이터를 Snapshot 형태로 사진을 찍어서 View에 반영한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터가 바뀌면 새로운 Snapshot을 만들어서 반영시키면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;View를 그릴 때 이전의 Snapshot과 새로운 Snapshot 사이에 달라진 부분을 애니메이션으로 자연스럽게 연결시켜준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;따라서 DataSource가 변경되었을 때 View를 보다 효율적이고 dynamic하게 업데이트할 수 있고, &lt;/b&gt;&lt;b&gt;UX 관점에서 유리하다.&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Diffable DataSource와 RxSwift&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 방법에서 RxCocoa를 사용한다면&amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;collectionView&lt;span style=&quot;color: #ee2323;&quot;&gt;.rx.items&lt;/span&gt;(cellIdentifier:cellType:)&lt;/span&gt; 형태로 데이터를 binding 시킨다.&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;이와 달리, Diffable DataSource는 snapshot을 사용하므로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;RxSwift처럼 reactive하게 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 위 코드가 필요 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Snapshot을 apply() 하는 부분에만 Rx를 사용하면 된다.&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;b&gt;개인적으로 Diffable DataSource 자체가 reactive해서 RxSwift와 잘 어울리지 않는 면이 많다고 느꼈다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 특정 Cell을 탭하는 이벤트가 발생했을 때, 기존에는 collectionView&lt;span style=&quot;color: #ee2323;&quot;&gt;.rx.modelSelected&lt;/span&gt;(CustomCellType.self).bind() 형태로 selectedCell을 받아올 수 있어서 매우 유용했는데, Diffable DataSource를 사용하면 이 메서드가 정상 작동하지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분은 다다음 포스팅에서 자세히 다뤄보려고 한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Advances in UI Data Sources (WWDC19)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적인 개념은 이렇다. &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2019/220&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;링크는 여기에&lt;/a&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DiffableDataSource에서는 복잡한 performBatchUpdates 메서드 대신 &lt;b&gt;apply 메서드&lt;/b&gt;를 사용한다.&lt;/li&gt;
&lt;li&gt;이제부터 UI를 업데이트할 때 indexPath 대신&amp;nbsp;&lt;b&gt;Snapshot&lt;/b&gt;을 사용한다. Snapshot은 &quot;현재 UI 상태의 truth&quot;를 뜻한다.&lt;br /&gt;이때 Snapshot의 Section identifier 및 Item identifier은 모두 &lt;b&gt;unique&lt;/b&gt; 해야 한다. (Hashable을 준수해야 한다.)&lt;/li&gt;
&lt;li&gt;기존 DataSource는 &quot;프로토콜&quot; 타입, DiffableDataSource (&lt;a href=&quot;https://developer.apple.com/documentation/uikit/uicollectionviewdiffabledatasource&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;UICollectionViewDiffableDataSource&lt;/a&gt;)는 &quot;제네릭 클래스&quot; 타입이다.&lt;/li&gt;
&lt;li&gt;Snapshot (&lt;a href=&quot;https://developer.apple.com/documentation/uikit/nsdiffabledatasourcesnapshot&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;NSDiffableDataSourceSnapshot&lt;/a&gt;)은 &quot;제네릭 구조체&quot; 타입이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예제 코드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;✅ &lt;/span&gt;DiffableDataSource를 사용하는 &lt;a href=&quot;https://developer.apple.com/documentation/uikit/views_and_controls/collection_views/implementing_modern_collection_views&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Implementing Modern Collection Views 예제 코드를&lt;/a&gt; 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상단의 SearchBar에 사용자가 문자를 입력할 때마다, 입력값을 포함하는 검색결과만 필터링하여 CollectionView를 다시 그린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색결과가 바뀔 때마다 애니메이션이 자동 적용된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;wwdc_diffable1.gif&quot; data-origin-width=&quot;256&quot; data-origin-height=&quot;532&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4S4F4/btrDiJGjYpi/enqLJRXOwde2DCDkNbbLLk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4S4F4/btrDiJGjYpi/enqLJRXOwde2DCDkNbbLLk/img.gif&quot; data-alt=&quot;Mountains Search 예제코드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4S4F4/btrDiJGjYpi/enqLJRXOwde2DCDkNbbLLk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/4S4F4/btrDiJGjYpi/enqLJRXOwde2DCDkNbbLLk/img.gif&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;250&quot; height=&quot;520&quot; data-filename=&quot;wwdc_diffable1.gif&quot; data-origin-width=&quot;256&quot; data-origin-height=&quot;532&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Mountains Search 예제코드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CollectionView를 그리기 위해 &lt;a href=&quot;https://developer.apple.com/documentation/uikit/uicollectionviewdiffabledatasource&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;DiffableDataSource&lt;/a&gt; 공식문서에 나와있는 4단계 STEP을 따른다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Connect a diffable data source to your collection view.&lt;/li&gt;
&lt;li&gt;Implement a cell provider to configure your collection view&amp;rsquo;s cells.&lt;/li&gt;
&lt;li&gt;Generate the current state of the data.&lt;/li&gt;
&lt;li&gt;Display the data in the UI.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;먼저 DiffableDataSource를 생성하고, ViewController와 연결한다. (STEP 1~2번)&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1650814906754&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Modern Collection Views 예제코드 &amp;gt; Diffable 폴더
class MountainsViewController: UIViewController {
    var mountainsCollectionView: UICollectionView!
    var dataSource: UICollectionViewDiffableDataSource&amp;lt;Section, MountainsController.Mountain&amp;gt;!
    
    func configureDataSource() {
        // cell register
        let cellRegistration = UICollectionView.CellRegistration
        &amp;lt;LabelCell, MountainsController.Mountain&amp;gt; { (cell, indexPath, mountain) in
            // Populate the cell with our item description.
            cell.label.text = mountain.name
        }
        
        // ✅ DiffableDataSource 생성
        dataSource = UICollectionViewDiffableDataSource&amp;lt;Section, MountainsController.Mountain&amp;gt;(collectionView: mountainsCollectionView) {
            (collectionView: UICollectionView, indexPath: IndexPath, identifier: MountainsController.Mountain) -&amp;gt; UICollectionViewCell? in
            return collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: identifier)
        }
    }
}

// ✅ Section 타입 (SectionIdentifier)
enum Section: CaseIterable {
    case main
}

// ✅ Item 타입 (ItemIdentifier)
struct Mountain: Hashable {
    let name: String
    let height: Int
    let identifier = UUID()  // Item identifier는 unique해야 하므로
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Cell을 등록&lt;/b&gt;하고, Cell 등록정보 (cellRegistration)를 활용해서 DiffableDataSource를 생성한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;DiffiableDataSource의 &lt;a href=&quot;https://developer.apple.com/documentation/uikit/uicollectionviewdiffabledatasource/3255138-init&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이니셜라이저&lt;/a&gt;&lt;/b&gt;를 보자. 제네릭 클래스이므로 &amp;lt;SectionIdentifier, ItemIdentifier&amp;gt; 형태로 초기화하고, 매개변수로 collectionView, cellProvider를 전달한다.&lt;br /&gt;이때 &lt;a href=&quot;https://developer.apple.com/documentation/uikit/uicollectionviewdiffabledatasource/cellprovider&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;cellProvider&lt;/a&gt;는 클로저 타입의 typealias인데, 대략 (collectionView, indexPath, itemIdentifier) 형태이다.&lt;/li&gt;
&lt;li&gt;SectionIdentifier로 &lt;b&gt;enum&lt;/b&gt;타입을 사용한 이유는 뭘까?&lt;br /&gt;연관값이 없는 enum은 항상 Hashable하기 때문이다. &lt;span style=&quot;color: #9d9d9d;&quot;&gt;(모든 연관값이 Hashable하면 enum도 Hashable하다.)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;ItemIdentifier 내부에 &lt;b&gt;UUID&lt;/b&gt; 타입의 identifier 프로토콜을 만든 이유는 뭘까?&lt;br /&gt;마찬가지로 Hashable해야 하기 때문이다. UUID는 임의의 고유 값이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;그다음, 전체 데이터를 전달해서 초기 Snapshot을 만들고 DiffableDataSource에 적용한다. (STEP 3~4번)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예제에서는 SearchBar 입력값을 받을 때마다 Snapshot을 다시 만들고 화면을 업데이트한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1650815731069&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func performQuery(with filter: String?) {
    // SearchBar 입력값을 포함하는 item만 필터링
    let mountains = mountainsController.filteredMountains(with: filter).sorted { $0.name &amp;lt; $1.name }

    // ✅ 새로운 snapshot 생성
    var snapshot = NSDiffableDataSourceSnapshot&amp;lt;Section, MountainsController.Mountain&amp;gt;() 
    snapshot.appendSections([.main])
    snapshot.appendItems(mountains)  // 필터링된 item만 snapshot에 반영시킴  
    
    // ✅ &quot;DiffiableDataSource야, 업데이트한 snapshot을 apply해서 View를 다시 그려줘&quot;
    dataSource.apply(snapshot, animatingDifferences: true) 
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;performQuery 메서드는 1) 화면을 띄울 때 (viewDidLoad), 2) SearchBar 입력값이 바뀔 때 (textDidChange) 호출된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Snapshot의 &lt;a href=&quot;https://developer.apple.com/documentation/uikit/nsdiffabledatasourcesnapshot&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이니셜라이저&lt;/a&gt;&lt;/b&gt;를 보자. DiffableDataSource와 비슷하다.&lt;br /&gt;제네릭 구조체이므로 &amp;lt;SectionIdentifierType, ItemIdentifierType&amp;gt; 형태로 초기화한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;appendSections(), appendItems()&lt;/b&gt;를 통해 각 Section마다 원하는 Item 데이터를 넣어준다.&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;*이때 section이 여러 개라면? &lt;br /&gt;appendSections([.section1]) - appendItems(items1) - appendSections([.section2]) -&amp;nbsp;appendItems(items2) 순으로 호출해야 한다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Snapshot을 만든 뒤에 &lt;b&gt;apply&lt;/b&gt; 메서드를 호출하면 된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;위 예제는 Snapshot을 생성할 때 empty snapshot을 만들고 section/item을 append하고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;다른 방법으로, 현재 View의 snapshot을 그대로 가져오고 싶다면 이렇게 하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1650819587585&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let snapshot = dataSource.snapshot()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 예제와 달리, 기존 item을 삭제하고, 새로운 item을 반영하고 싶다면 이렇게 하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1653638319174&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private func deleteAndApplySnapshot(newListProducts: [UniqueProduct]) {
    let previousProducts = snapshot.itemIdentifiers(inSection: .list)
    snapshot.deleteItems(previousProducts)  // 기존 item 삭제
    // snapshot.deleteAllItems()  // 이것도 가능
    snapshot.appendItems(listProducts, toSection: .list)

    dataSource.apply(snapshot, animatingDifferences: true)
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Diffable DataSource의 장점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 DataSource를 쓰고 reloadData 메서드를 호출해도 되는데 뭐가 다를까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;reloadData를 쓰면 애니메이션이 끊겨서 UX에 안 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 DiffiableDataSource는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;View를 다시 그릴 때 Snapshot의 변화를 스스로 파악하고, 그걸 똑똑하게 애니메이션으로 나타내준다!&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;(애니메이션이 싫다면 apply 메서드의 animatingDifferences에 false를 할당하면 된다.)&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;b&gt;그리고 synchronization 버그, crash 발생을 예방하므로 안전하다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;819&quot; data-origin-height=&quot;143&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBzRZ8/btrDhf6P6xe/vXZzdkuuDx4YtUU9q1QIzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBzRZ8/btrDhf6P6xe/vXZzdkuuDx4YtUU9q1QIzK/img.png&quot; data-alt=&quot;기존 방법에서 자주 발생하는 synchronization 버그...&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBzRZ8/btrDhf6P6xe/vXZzdkuuDx4YtUU9q1QIzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBzRZ8%2FbtrDhf6P6xe%2FvXZzdkuuDx4YtUU9q1QIzK%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;750&quot; height=&quot;131&quot; data-origin-width=&quot;819&quot; data-origin-height=&quot;143&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;기존 방법에서 자주 발생하는 synchronization 버그...&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;이외에도 Sectional Snapshot이라는 보완 기술을 소개한 WWDC 세션도 있는데, 나중에 다룰 예정이다.&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 style=&quot;color: #9d9d9d;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;다음 포스팅에서는&lt;/span&gt; &lt;a href=&quot;https://developer.apple.com/documentation/uikit/views_and_controls/collection_views/implementing_modern_collection_views&quot;&gt;Implementing Modern Collection Views 예제 코드&lt;/a&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;/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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&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;span&gt;&lt;span&gt;예제코드 : Apple &amp;gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Collection View &amp;gt;&lt;span&gt; Sample Code -&lt;/span&gt;&amp;nbsp;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/views_and_controls/collection_views/implementing_modern_collection_views&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Implementing&amp;nbsp;Modern&amp;nbsp;Collection&amp;nbsp;Views&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span&gt;Apple &amp;gt; Collection View &amp;gt; Sample Code -&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/views_and_controls/collection_views/updating_collection_views_using_diffable_data_sources&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Updating&amp;nbsp;Collection&amp;nbsp;Views&amp;nbsp;Using&amp;nbsp;Diffable&amp;nbsp;Data&amp;nbsp;Sources&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Apple &amp;gt; WWDC 2019 &amp;gt; &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2019/220&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Advances in UI Data Sources&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Apple &amp;gt; WWDC 2020 &amp;gt; &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2020/10045/?time=8&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Advances in diffable data sources&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span&gt;블로그 &amp;gt; &lt;a href=&quot;https://zeddios.tistory.com/1197&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Zeddios - Diffable Datasource&lt;/a&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>iOS/영문 공식문서 뜯어보기-iOS</category>
      <category>CollectionView</category>
      <category>DiffableDataSource</category>
      <category>SECTION</category>
      <category>snapshot</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/37</guid>
      <comments>https://applecider2020.tistory.com/37#entry37comment</comments>
      <pubDate>Fri, 27 May 2022 17:15:11 +0900</pubDate>
    </item>
    <item>
      <title>[Git] Git 브랜치 이름은 어떻게 정할까? - Git Flow로 브랜치 관리하기</title>
      <link>https://applecider2020.tistory.com/36</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;부트캠프에서는 프로젝트를 진행할 때 STEP별 명세서를 받았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 자연스레 브랜치 이름도 step1, step2... 등으로 네이밍했었다.&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;'Git 브랜치 이름'을 키워드로 검색했더니, 온통 &lt;b&gt;&quot;Git Flow 전략&quot;&lt;/b&gt;에 대한 자료만 나왔다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Git Flow가 뭘까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;브랜치 모델 (Branch Model)&lt;/b&gt;이란 &lt;b&gt;브랜치 이름, 브랜치별 임무를 규정한 것&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 브랜치 모델 중 가장 유명한 게 Vincent Driessen가 만든&amp;nbsp;&lt;a href=&quot;https://nvie.com/posts/a-successful-git-branching-model/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;b&gt;Git Flow&lt;/b&gt;&lt;/a&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;&lt;b&gt;master, develop, feature, release, hotfix&lt;/b&gt; 5개 종류로 브랜치를 구분한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1150&quot; data-origin-height=&quot;1524&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsOiQd/btrC2waOFmJ/CoPVxNcZYVr7Zs1Uuc55WK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsOiQd/btrC2waOFmJ/CoPVxNcZYVr7Zs1Uuc55WK/img.png&quot; data-alt=&quot;Git Flow&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsOiQd/btrC2waOFmJ/CoPVxNcZYVr7Zs1Uuc55WK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsOiQd%2FbtrC2waOFmJ%2FCoPVxNcZYVr7Zs1Uuc55WK%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;650&quot; height=&quot;861&quot; data-origin-width=&quot;1150&quot; data-origin-height=&quot;1524&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Git Flow&lt;/figcaption&gt;
&lt;/figure&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;대부분의 작업은 feature -&amp;gt; develop -&amp;gt; release -&amp;gt; master 순으로 merge 시켜서 관리한다는 것만 알면 된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;feature : 새로운 기능을 개발할 때는 &lt;b&gt;feature&lt;/b&gt; 브랜치에서 기능별 브랜치를 만들어 작업한다.&lt;/li&gt;
&lt;li&gt;develop : 기능 개발이 완료되면 해당 feature 브랜치를 &lt;b&gt;develop&lt;/b&gt; 브랜치에 merge 시킨다.&lt;/li&gt;
&lt;li&gt;release : release 준비가 완료되면 develop 브랜치를 &lt;b&gt;release&lt;/b&gt; 브랜치로 merge 시키고, release cycle을 진행한다.&lt;/li&gt;
&lt;li&gt;master : 최종 product를 release할 때는 release 브랜치를 &lt;b&gt;master &lt;/b&gt;브랜치로 merge 시키고, 해당 버전에 대해 Tag를 단다.&lt;/li&gt;
&lt;li&gt;hotfix : product release 이후 중요한 버그가 발생하면 &lt;b&gt;hotfix &lt;/b&gt;브랜치로 작업하고, develop 및 master 브랜치로 merge 시킨다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Git Flow의 장점&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브랜치가 자동으로 생성/삭제/merge 되어 편리하다.&lt;/li&gt;
&lt;li&gt;브랜치 이름을 일관적으로 사용할 수 있다.&lt;/li&gt;
&lt;li&gt;브랜치별 역할이 명확히 구분되어 있어 &lt;b&gt;각 브랜치의 상태에 따라 배포 및 테스트를 진행&lt;/b&gt;하기에 용이하다.&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;feature 브랜치를 업데이트하면 unit test를 실행&lt;/li&gt;
&lt;li&gt;develop에 merge request 발생 시 integration&amp;nbsp;test와 slack 메시지 발송&lt;/li&gt;
&lt;li&gt;develop 브랜치를 업데이트하면 개발 서버 배포 및 integration test 실행&lt;/li&gt;
&lt;li&gt;release 브랜치 배포 시 relase 서버 배포 및 QA 팀에 메일로 전달&lt;/li&gt;
&lt;li&gt;master 배포 시 실 서버 배포&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;승인된 개발자만 코드에 접근할 수 있게 해서 안전하게 소스코드를 관리할 수 있다&lt;/li&gt;
&lt;li&gt;feature 브랜치를 통해 기능 단위로 독립적인 개발이 가능하다.&lt;br /&gt;- 이때 PR을 활용하여 기능별 히스토리를 관리할 수 있다.&lt;/li&gt;
&lt;li&gt;feature 브랜치는 칸반 보드의 티켓과 연동되므로 기능별 추적이 쉽고, 오류 발생 시 특정 기능을 UNDO 시킬 수 있다.&lt;/li&gt;
&lt;li&gt;multi thread(배포 버전과 개발 버전 분리)가 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Git Flow의 단점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;찾아보니 단점으로 아래의 의견도 있었다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;release 브랜치의 활용도가 낮다. (버전별 Tag가 존재하므로)&lt;/li&gt;
&lt;li&gt;master 브랜치의 활용도가 낮다. (develop 브랜치가 역할을 대신할 수 있다.)&lt;/li&gt;
&lt;li&gt;feature 브랜치 특성상 복수형인 features 네이밍이 적절하다.&lt;/li&gt;
&lt;li&gt;브랜치 관리보다 적절한 커밋 단위를 설정하는 게 더 중요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Git Flow 생성하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Git Flow는 Git의 extension 기능이고, 별도 설치가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;macOS 사용자는 터미널에서 아래를 입력하면 된다.&lt;/p&gt;
&lt;pre class=&quot;swift&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;$ brew install git  // git을 미설치한 경우에만
$ brew install git-flow-avh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음&amp;nbsp;프로젝트 폴더 위치에서 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;git flow init&lt;/span&gt;을 입력하면 된다. (일반 git을 생성할 때 터미널에서 git init을 했듯이)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 아래처럼 브랜치 이름을 입력하라는 안내가 뜨는데, 그냥 모두 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;Enter&lt;/span&gt;키로 넘기면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1653445224741&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git flow init

Which branch should be used for bringing forth production releases?
   - develop
   - master
Branch name for production releases: [master]

Which branch should be used for integration of the &quot;next release&quot;?
   - develop
Branch name for &quot;next release&quot; development: [develop]

How to name your supporting branch prefixes?
Feature branches? [feature/]
Bugfix branches? [bugfix/]
Release branches? [release/]
Hotfix branches? [hotfix/]
Support branches? [support/]
Version tag prefix? []
Hooks and filters directory? [/Users/projects/project1/.git/hooks]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정이 완료되고 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;git branch -a&lt;/span&gt;를 찍어보면 아래처럼 develop, master가 생성된 게 보인다.&lt;/p&gt;
&lt;pre id=&quot;code_1653445599148&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;* develop
* master
  remotes/origin/HEAD -&amp;gt; origin/develop
  remotes/origin/develop
  remotes/origin/master&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Git Flow 사용하기&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Feature&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 feature를 개발할 때&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;git flow feature start login&lt;/span&gt;&amp;nbsp;을 입력하면 자동으로 develop/feature/login 브랜치를 생성해준다.&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;해당 feature를 팀원과 협업하여 개발한다면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;git flow feature publish login&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(일반 git처럼 git push를 입력해도 됨)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;git flow feature pull origin login&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(일반 git처럼 git pull를 입력해도 됨)을 입력하여 주고받기하면 된다.&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;span style=&quot;background-color: #dddddd;&quot;&gt;git flow feature finish login&lt;/span&gt;&amp;nbsp;을 입력하면 자동으로 login 브랜치를 develop에 merge 시키고, login 브랜치를 삭제한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Release / HotFix&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브랜치 이름은 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;release-어쩌구&lt;/span&gt; 그리고 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;hotfix-어쩌구&lt;/span&gt; 형태로 네이밍한다.&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;필요한 상황에 따라 명령어를 찾아 쓰기만 하면 되므로 매우 간단하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령어를 요약해둔 &lt;a href=&quot;https://danielkummer.github.io/git-flow-cheatsheet/index.ko_KR.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;CheatSheet&lt;/a&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;a href=&quot;https://jinwoo1990.github.io/git/git-flow-summary/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이 링크&lt;/a&gt;를 활용하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 Git 충돌 방지를 위한 팁은 &lt;a href=&quot;https://velog.io/@couchcoding/%EC%B6%A9%EB%8F%8C-%EC%97%86%EB%8A%94-Git%EC%9D%84-%EC%9C%84%ED%95%B4-Git-Flow%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이 링크&lt;/a&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 style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- Reference &lt;/span&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;a href=&quot;https://nvie.com/posts/a-successful-git-branching-model/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Vincent&amp;nbsp;Driessen의&amp;nbsp;A&amp;nbsp;successful&amp;nbsp;Git&amp;nbsp;branching&amp;nbsp;model&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Daniel Kummer의&amp;nbsp;&lt;a href=&quot;https://danielkummer.github.io/git-flow-cheatsheet/index.ko_KR.html&quot;&gt;Git Flow CheatSheet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;생활코딩 &amp;gt; &lt;a href=&quot;https://www.youtube.com/watch?v=w2F8O9J1keM&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Git Flow -&amp;nbsp;출시와&amp;nbsp;개발을&amp;nbsp;동시에&amp;nbsp;진행하는&amp;nbsp;방법&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;GitHub Docs &amp;gt; &lt;a href=&quot;https://gist.github.com/ihoneymon/a28138ee5309c73e94f9&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;GIT을 기반으로 한 프로젝트 개발프로세스&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Blog - &lt;a href=&quot;https://techblog.woowahan.com/2553/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;우아한형제들 기술블로그 &amp;gt; 우린 Git-flow를 사용하고 있어요&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Blog -&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://jinwoo1990.github.io/git/git-flow-summary/&quot;&gt;Jin의 Git Flow 사용법 요약 및 팁&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Blog - &lt;a href=&quot;https://velog.io/@couchcoding/%EC%B6%A9%EB%8F%8C-%EC%97%86%EB%8A%94-Git%EC%9D%84-%EC%9C%84%ED%95%B4-Git-Flow%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;CouchCoding의 충돌&amp;nbsp;없는&amp;nbsp;Git을&amp;nbsp;위해&amp;nbsp;Git&amp;nbsp;Flow에&amp;nbsp;대해&amp;nbsp;알아보자&lt;/a&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;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;  포스트가 도움이 되었다면, 공감  / 구독  / 공유  / 댓글✏️ 로 응원해주세요. 감사합니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>비전공자용 노력/개발 툴</category>
      <category>Gitflow</category>
      <category>GitFlow전략</category>
      <author>Applecider</author>
      <guid isPermaLink="true">https://applecider2020.tistory.com/36</guid>
      <comments>https://applecider2020.tistory.com/36#entry36comment</comments>
      <pubDate>Wed, 25 May 2022 11:57:54 +0900</pubDate>
    </item>
  </channel>
</rss>