<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>minjunkim-dev.log</title>
        <link>https://velog.io/</link>
        <description>trial and error</description>
        <lastBuildDate>Thu, 09 Jun 2022 10:47:26 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>minjunkim-dev.log</title>
            <url>https://images.velog.io/images/minjunkim-dev/profile/0f7161e3-de8f-4c23-961a-bf256542a969/social.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. minjunkim-dev.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/minjunkim-dev" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Property Wrapper]]></title>
            <link>https://velog.io/@minjunkim-dev/Property-Wrapper</link>
            <guid>https://velog.io/@minjunkim-dev/Property-Wrapper</guid>
            <pubDate>Thu, 09 Jun 2022 10:47:26 GMT</pubDate>
            <description><![CDATA[<p>Swift 5.1에서 추가된 기능인 Property Wrapper에 대해서 살펴보도록 하겠습니다.</p>
<h3 id="필요성">필요성</h3>
<blockquote>
<p>연산 프로퍼티의 중복되는 로직을 프로퍼티 래퍼에 한 번만 구현하고,
이 로직을 필요로 하는 프로퍼티들 자체에 연결하여 사용할 수 있게 하여
코드의 재사용성을 높일 수 있습니다.</p>
</blockquote>
<h3 id="정의하는-방법">정의하는 방법</h3>
<p>struct, class, enum을 만들고 @propertyWrapper 를 맨 앞에 명시해주면 프로퍼티 래퍼를 정의할 수 있습니다.</p>
<p>그리고 반드시 wrappedValue라는 연산 프로퍼티를 구현하여야 합니다.</p>
<p>UserDefaults 예제를 통해 한 번 알아보겠습니다. 먼저 Property Wrapper 전의 코드 상태입니다.</p>
<pre><code>class UserManager {
    static var usesTouchID: Bool {
        get { return UserDefaults.standard.bool(forKey: &quot;usesTouchID&quot;) }
        set { UserDefaults.standard.set(newValue, forKey: &quot;usesTouchID&quot;) }
    }

    static var myEmail: String {
        get { return UserDefaults.standard.string(forKey: &quot;myEmail&quot;) }
        set { UserDefaults.standard.set(newValue, forKey: &quot;myEmail&quot;) }
    }

    static var numberOfAccounts: Bool {
        get { return UserDefaults.standard.bool(forKey: &quot;numberOfAccounts&quot;) }
        set { UserDefaults.standard.set(newValue, forKey: &quot;numberOfAccounts&quot;) }
    }
}</code></pre><p>첫째로, UserDefaults를 활용하여 값을 읽거나 쓸 때, 그 key 값으로 항상 String을 사용하고 있는 것을 확인할 수 있습니다.</p>
<p>둘째로, 값을 읽거나 쓸 때, 값의 타입과 관계없이 UserDefaults.standard까지는 동일하게 사용하고 있는 것 역시 확인할 수 있습니다.</p>
<p>마지막으로 가장 중요한건, 위 클래스의 세 연산 프로퍼티가 결국 공통적인 로직을 공유하고 있습니다.</p>
<p>위 세 가지 특징을 파악했으니, 이를 활용하여 Property Wrapper를 이용해서 개선해보도록 하겠습니다.</p>
<pre><code>@propertyWrapper
struct UserDefault&lt;T&gt; {
    let key: String // key는 항상 String 타입let defaultValue: T 
    let defaultValue: T
    private let storage: UserDefaults

    var wrappedValue: T { // property wrapper를 사용하려면 반드시 이 이름으로 연산 프로퍼티를 구현하여야 함
        get {
            storage.object(forKey: key) as? T ?? defaultValue
        }
        set {
            storage.set(newValue, forKey: key)
        }
    }

    init(key: String, defaultValue: T, storage: UserDefaults = .standard) {
        self.key = key
        self.defaultValue = defaultValue
        self.storage = storage
    }
}

class UserManager {
    @UserDefault(key: &quot;usesTouchID&quot;, defaultValue: false)
    static var usesTouchID: Bool

    @UserDefault(key: &quot;myEmail&quot;, defaultValue: &quot;&quot;)
    static var myEmail: String

    @UserDefault(key: &quot;numberOfAccounts&quot;, defaultValue: 0)
    static var numberOfAccounts: Int
}

UserManager.usesTouchID = true // set
print(UserManager.usesTouchID) // get

UserManager.myEmail = &quot;myEmail&quot; // set
print(UserManager.myEmail) // get

UserManager.numberOfAccounts = 3 // set
print(UserManager.numberOfAccounts) // get</code></pre><p>위 코드의 결과는 다음과 같습니다.</p>
<blockquote>
<p>true
myEmail
3</p>
</blockquote>
<p>원하는대로 동작했음을 확인할 수 있습니다.</p>
<h3 id="projectedvalue">projectedValue</h3>
<p>추가로 제공하고 싶은 정보가 있다면 어떤 타입이라도 projectedValue를 정의하여 사용할 수 있습니다.
이 값은 프로퍼티 래퍼로 감싼 프로퍼티 앞에 $를 붙여 접근할 수 있습니다.</p>
<p>예를 들어, 위의 예시에서 defaultValue 값을 알고는 있지만 UserManager 클래스를 통해 현재 설정되어 있는 defaultValue를 갖고 오고 싶다고 가정을 해보겠습니다. 현재 상태에서는 바로 defaultValue에 접근할 수 없습니다. 이 때, projectValue를 활용하면 쉽게 defaultValue를 얻어낼 수 있습니다.</p>
<pre><code>@propertyWrapper
struct UserDefault&lt;T&gt; {
    let key: String // key는 항상 String 타입let defaultValue: T 
    let defaultValue: T
    private let storage: UserDefaults

    var wrappedValue: T { // property wrapper를 사용하려면 반드시 이 이름으로 연산 프로퍼티를 구현하여야 함
        get {
            storage.object(forKey: key) as? T ?? defaultValue
        }
        set {
            storage.set(newValue, forKey: key)
        }
    }

    var projectedValue: T { // 반드시 이 이름을 사용하여야 동작함
        defaultValue
    }

    init(key: String, defaultValue: T, storage: UserDefaults = .standard) {
        self.key = key
        self.defaultValue = defaultValue
        self.storage = storage
    }
}

class UserManager {
    @UserDefault(key: &quot;usesTouchID&quot;, defaultValue: false)
    static var usesTouchID: Bool

    @UserDefault(key: &quot;myEmail&quot;, defaultValue: &quot;&quot;)
    static var myEmail: String

    @UserDefault(key: &quot;numberOfAccounts&quot;, defaultValue: 0)
    static var numberOfAccounts: Int
}

print(UserManager.$usesTouchID) // projectedValue
print(UserManager.$myEmail) // projectedValue
print(UserManager.$numberOfAccounts) // projectedValue</code></pre><p>위 코드의 결과는 다음과 같습니다.</p>
<blockquote>
<p>false</p>
</blockquote>
<p>0</p>
<p>정상적으로 동작했음을 확인할 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[WWDC19] Data Flow Through SwiftUI]]></title>
            <link>https://velog.io/@minjunkim-dev/WWDC19-Data-Flow-Through-SwiftUI</link>
            <guid>https://velog.io/@minjunkim-dev/WWDC19-Data-Flow-Through-SwiftUI</guid>
            <pubDate>Thu, 09 Jun 2022 09:31:08 GMT</pubDate>
            <description><![CDATA[<h3 id="data-flow의-원리">Data Flow의 원리</h3>
<ul>
<li>뷰에서 데이터를 읽으면 해당 뷰에 대한 종속성이 발생하게 됩니다.
다시 말해, 데이터가 변경될 때마다 변경된 값을 반영하기 위해 해당 뷰를 다시 그려야 합니다.</li>
<li>뷰 계층에서 읽은 모든 데이터마다 source of truth를 가지는데, source of truth는 뷰 계층 내 또는 외부(의 모델)에 있을 수 있습니다.</li>
<li>그러나 반드시 서로 일치해야 하는 하나의 데이터에 대해 여러 개의 source of truth가 존재하고, 이 중 하나라도 값이 바뀐다면, 나머 source of truth에 대해서도 모두 동일하게 관리를 해주어야 일치시켜야 합니다. 이는 매우 번거롭고 어려운 일입니다.</li>
<li>이처럼 중복으로 인해 발생하는 비정합성 등의 문제를 해결하려면 하나의 데이터에는 &quot;단 하나의&quot; source of truth만을 가지고 있는 것이 필요합니다.</li>
<li>SwiftUI의 Data Flow는 앱의 데이터에 대한 Single Source Of Truth(SSOT)를 유지하는데 도움을 주는 다양한 기능들을 제공하여 위에서 언급한 문제를 해결하게 됩니다.</li>
</ul>
<h4 id="state">@State</h4>
<ul>
<li>프로퍼티를 @State 프로퍼티 래퍼로 감싸면, SwiftUI는 해당 프로퍼티가 언제 변경되는지 알 수 있습니다.</li>
<li>@State 프로퍼티 래퍼로 감싼 프로퍼티를 private로 접근 제어하면, 해당 뷰만이 그 프로퍼티를 소유하고 관리하게 할 수 있습니다.</li>
<li>@State 프로퍼티 래퍼로 감싼 프로퍼티가 변경되면, SwiftUI는 런타임에 해당 뷰와 그 자식 뷰들을 모두 재계산합니다.</li>
<li>모든 변경 사항은 항상 뷰 계층을 따라 전파됩니다.</li>
<li>SwiftUI는 뷰를 비교하여 변경된 것만 다시 렌더링하기 때문에, 매우 효율적으로 뷰들의 변화를 반영할 수 있습니다.</li>
</ul>
<h4 id="binding">@Binding</h4>
<ul>
<li>@Binding 프로퍼티 래퍼를 통해 프로퍼티를 직접 소유하지 않고도 해당 프로퍼티의 source of truth에 대한 명시적인 의존성을 정의할 수 있습니다.</li>
<li>Toggle, TextField, Slider와 같은 primitives view들은 binding을 필요로 합니다.</li>
<li>binding으로 프로퍼티를 &quot;직접 소유하지 않고 참조&quot;하여 읽거나 쓸 수 있습니다. 이를 통해 별도로 source of truth를 또 만들지 않고 하나만 유지할 수 있습니다.</li>
</ul>
<h3 id="publisherexternal-changes">Publisher(External Changes)</h3>
<ul>
<li>뷰 계층 내부가 아닌 외부의 source로부터 이벤트를 받는 경우에는 Combine의 Publisher를 활용하게 됩니다.</li>
</ul>
<h4 id="observableobjectexternal-data">ObservableObject(External Data)</h4>
<ul>
<li>데이터가 뷰 외부에 있을 때, SwiftUI는 해당 데이터의 변경에 어떻게 반응할 것인지에 대해 알아야 합니다.</li>
<li>먼저, 뷰 외부의 데이터가 ObservableObject 프로토콜을 준수하도록 함으로써, 데이터가 변경될 때마다 발생하는 objectWillChange 프로퍼티를 통해 액세스할 수 있는 Publisher가 있어야 합니다.</li>
<li>이후에 모델을 @ObservedObject 프로퍼티 래퍼로 감싸고, ObservedObject를 뷰의 이니셜라이저에 전달하면 됩니다.</li>
<li>SwiftUI에서의 뷰는 value type(struct)이지만, reference type(class)을 사용할 때마다 @ObservedObject 프로퍼티 래퍼를 사용해야 합니다. 이렇게 하면 SwiftUI는 데이터가 언제 변경되는지 알고 뷰 계층을 최신 상태로 유지할 수 있습니다.</li>
</ul>
<h3 id="environment">Environment</h3>
<ul>
<li>.environment modifier 덕분에 ObservableObject를 환경에 추가할 수 있습니다.
######</li>
<li>@EnvironemntObject 프로퍼티 래퍼를 사용하면 이러한 환경 객체들에 대한 종속성을 만들 수 있습니다.</li>
</ul>
<h3 id="object-binding-vs-environment">Object Binding vs Environment</h3>
<ul>
<li>@Binding으로 전체 앱을 빌드할 수는 있지만, 뷰에서 뷰로 모델을 전달하는 것은 번거로울 수 있습니다.
######</li>
<li>EnvironmentObject는 계층 주변에 데이터를 간접적으로 전달하기에 편리합니다. 각 뷰에 객체를 직접 전달할 필요가 없어지기 때문입니다.</li>
</ul>
<h3 id="state-vs-observedobject">@State vs @ObservedObject</h3>
<ul>
<li>@State은 로컬 뷰, value type, 관리되는 데이터에 적합합니다.</li>
<li>@State는 SwiftUI에 의해 할당되고, 생성되고 관리됩니다.</li>
<li>@State는 value type입니다.
######</li>
<li>@ObservedObject는 데이터베이스와 같은 SwiftUI의 외부 데이터를 나타내기 위한 것입니다.</li>
<li>@ObservedObject는 우리가 직접 관리하는 storage이며,
이미 가지고 있는 모델에 적합합니다.</li>
<li>@ObservedObject는 SwiftUI가 아니라 우리가 직접 관리합니다.</li>
<li>@ObservedObject는 reference type입니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Git 이해하기 Part 2]]></title>
            <link>https://velog.io/@minjunkim-dev/Git-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-Part-2</link>
            <guid>https://velog.io/@minjunkim-dev/Git-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-Part-2</guid>
            <pubDate>Wed, 20 Apr 2022 06:05:48 GMT</pubDate>
            <description><![CDATA[<h1 id="reset-vs-revert-이해하기">Reset vs Revert 이해하기</h1>
<blockquote>
</blockquote>
<p>Reset: 특정 과거 시점으로 돌아가기
Revert: &quot;변경 커밋을 남기며&quot; 특정 과거 시점으로 돌아가기</p>
<p>기본적으로 둘다 과거 시점으로 돌아가기 위해 사용하는 명령어지만,
엄연히 둘 간의 차이점이 있다. 알아보도록 하자!</p>
<h2 id="reset-이해하기">Reset 이해하기</h2>
<p>reset의 경우 과거로 돌아가는 방법에는 3가지가 있으며,
방법에 따라 변화가 달라진다.</p>
<table>
<thead>
<tr>
<th align="center">option</th>
<th align="center">Working Directory</th>
<th align="center">Staging Area</th>
<th align="center">Local Repository</th>
</tr>
</thead>
<tbody><tr>
<td align="center">--soft</td>
<td align="center">변화 없음</td>
<td align="center">변화 없음</td>
<td align="center">reset 시점(commit id)</td>
</tr>
<tr>
<td align="center">--mixed</td>
<td align="center">변화 없음</td>
<td align="center">reset 시점(commit id)</td>
<td align="center">reset 시점(commit id)</td>
</tr>
<tr>
<td align="center">--hard</td>
<td align="center">reset 시점(commit id)</td>
<td align="center">reset 시점(commit id)</td>
<td align="center">reset 시점(commit id)</td>
</tr>
</tbody></table>
<p>명확한 이해를 위해 예시를 통해 자세히 살펴보자.</p>
<p><img src="https://velog.velcdn.com/images/minjunkim-dev/post/13998ea8-1a7f-48da-8572-fd8edc66aa6e/image.png" alt=""></p>
<p>현재 HEAD가 Main Branch가 가리키고 있는 274aacc를 가리키고 있는 상황이다.
그 상태에서 Working Directory에서 코드를 추가로 작성하여 변경사항이 발생하였다.
이 때,</p>
<pre><code>git reset --hard Da3de11</code></pre><p>를 실행한다면
<img src="https://velog.velcdn.com/images/minjunkim-dev/post/9b4ba7d1-207d-41af-8cef-5cda26cef908/image.png" alt="">
Working Directory 뿐만 아니라 Staging Area, Local Repository까지 Da3de11 시점으로 돌아가게 된다.</p>
<p>반면,</p>
<pre><code>git reset (--mixed) Da3de11
(reset의 default값은 --mixed이므로 생략 가능)</code></pre><p>의 경우에는 Working Directory만 변경되지 않고, 나머지는 모두 Da3de11 커밋 시점으로 돌아갈 것이다.</p>
<p>마지막으로,</p>
<pre><code>git reset --soft Da3de11
(reset의 default값은 --mixed이므로 생략 가능)</code></pre><p>의 경우에는 Local Repository만 Da3de11 커밋 시점으로 돌아갈 것이다.</p>
<p>결국 reset을 사용할 때는 다음을 고려하여 옵션을 결정하고 사용해야 한다.</p>
<ul>
<li>코드 변경 상태 적용 여부<ul>
<li>Working Directory만 변경 사항이 적용 되었는가?</li>
<li>Working Directory + Staging Area까지 변경 사항이 적용 되었는가?</li>
<li>Working Directory + Staging Area + Local Repository까지 변경 사항이 적용 되었는가?</li>
</ul>
</li>
<li>개발자의 reset 목적<ul>
<li>Working Directory, Staging Area, Local Repository 중에서 특정 시점으로 되돌릴 대상은 명확히 어디인가?</li>
</ul>
</li>
</ul>
<h3 id="🤔-reset을-잘못했을-때-어떻게-해야하지">🤔 reset을 잘못했을 때, 어떻게 해야하지?</h3>
<p>아래는 원래 HEAD가 90ac000 커밋을 가리키고 있었으나 </p>
<pre><code>git reset --hard 48ea1f3</code></pre><p>을 통해 48ea1f3 커밋 시점으로 되돌아간 상태를 보여주고 있다.
<img src="https://velog.velcdn.com/images/minjunkim-dev/post/8b06c05f-c7a7-48d5-807c-bb0e47ecec77/image.png" alt=""></p>
<p>option이 --hard이므로 Local Repository로부터 Working Directory까지 모두 48ea1f3 시점으로 변경된 상태이다.
근데 reset을 잘못하여 다시 원래대로 돌아가고 싶을 때 어떻게 해야할까?</p>
<p>당황스러운 마음으로 일단,</p>
<pre><code>git log</code></pre><p>를 입력해보지만, 이미 reset 되어 다시 원래대로 가고자 하는 commit은 전혀 보이지 않는다... 눈물을 머금고 코드를 다시 작성해야 할까?</p>
<p>아니다! 다시 복원할 수 있다. &quot;git reflog&quot;와 &quot;git reset&quot;을 활용하여 &quot;reset을 다시 reset&quot; 할 수 있다.</p>
<h4 id="🤔-git-reflog">🤔 git reflog</h4>
<ul>
<li>reference log</li>
<li>git log와 달리 한번이라도 commit이 된 내용이라면 현재 저장소 tree에 일반적으로 보이지 않는 모든 commit들을 보여줌</li>
<li>따라서 git reflog를 통해 reset으로 인해 유실된 commit id를 먼저 찾고,<pre><code>git reset --hard (유실된 commit id)</code></pre>를 통해 다시 원래대로 돌아갈 수 있다.</li>
</ul>
<h3 id="reset의-특징을-다시-정리해보면">reset의 특징을 다시 정리해보면...</h3>
<ul>
<li>git reset --soft: Local Repo만 리셋</li>
<li>git reset --mixed: Local Repo + Staing Area만 리셋</li>
<li>git reset --hard: Working Directory까지 모두 리셋</li>
<li>잘못된 리셋의 복구도 리셋으로 가능<ul>
<li>git reflog를 통해 먼저 유실된 commit id를 확인</li>
<li>git reset --hard (유실된 commit id)를 통해 복구</li>
</ul>
</li>
</ul>
<h3 id="reset의-주의사항">reset의 주의사항!</h3>
<ul>
<li>커밋의 시점을 변동하기 때문에, 협업 시 주의가 필요<ul>
<li>reset했을 때 Local Repository가 변경되어 Remote Repository와 불일치하여 충돌 가능성이 다분</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/minjunkim-dev/post/1381f23d-77d8-4a21-a8f5-2decf2de363d/image.png" alt=""></p>
<ul>
<li>따라서 &quot;remote repo에 push하기 전&quot; 상황에서 사용하는 것이 가급적 좋으며, &quot;remote repo를 혼자 사용하거나 팀원과 협의된 경우&quot;에는 </li>
</ul>
<pre><code>git push --force</code></pre><p>로 해결도 가능</p>
<h2 id="revert-이해하기">Revert 이해하기</h2>
<p>revert의 경우 reset과 달리 option이 없고 변경 커밋을 남기며 과거 시점으로 돌아간다는 것이 핵심이다. reset과의 비교를 통해 알아보자.</p>
<p><img src="https://velog.velcdn.com/images/minjunkim-dev/post/2576a2f3-03ae-43bc-bc63-01fc3dac6fb9/image.png" alt=""></p>
<p>위와 같은 상태에서 Da3de11 커밋 시점으로 돌아가고자 한다.
먼저, reset의 경우에는 HEAD를 돌아가고자 하는 커밋 시점을 가리키게 한다. 물론 이로 인해 유실된 commit id는 git reflog를 통해 확인할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/minjunkim-dev/post/155c730d-d0ab-420e-ad95-c8342e9b0037/image.png" alt=""></p>
<p>다음으로 revert의 경우를 살펴보자.
revert의 경우는 변경 커밋을 남기면서 돌아가고자 하는 시점의 커밋과 동일한 내용으로 만든다.
과거 시점 커밋의 내용과 동일하게 만들기 위해 가장 최근 시점의 커밋부터 차례대로 이를 상쇄하는 변경 커밋을 만들어간다고 이해하면 될 것이다.
따라서, 실행 결과는 과거 시점 커밋의 내용과 일치하지만 reset과 달리 HEAD는 미래 시점의 커밋을 가리키고 있다.</p>
<p>아래 그림은 </p>
<pre><code>git revert 274aacc...Da3de11</code></pre><p>를 실행한 결과를 그림으로 보여준다.
<img src="https://velog.velcdn.com/images/minjunkim-dev/post/a5fd8fc6-30f1-4cd3-8b00-4c3e24844643/image.png" alt=""></p>
<h3 id="revert의-특징을-다시-정리해보면">revert의 특징을 다시 정리해보면...</h3>
<ul>
<li>과거 커밋 시점으로 돌아갈 때, reset과 달리 변경 이력을 남기며 돌아감</li>
<li>코드의 시점은 과거로 변하지만, 커밋은 미래 시점으로 변경<ul>
<li>reset과 달리 충돌없이 Remote Repository Push가 가능</li>
<li>협업시 가급적 reset이 아닌 revert가 권장됨</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/minjunkim-dev/post/08e3d97f-6a98-40d4-8569-698d8193b6b8/image.png" alt=""></p>
<ul>
<li>다만, revert도 잘못 사용하면 충돌이 일어날 수도 있음<ul>
<li>e.g. 과거 시점의 특정 commit만 revert 하는 경우(권장하지 않음)</li>
</ul>
</li>
</ul>
<h4 id="🤔-git-commit---amend">🤔 git commit --amend</h4>
<ul>
<li>가장 최근 커밋을 수정하는 편리한 방법</li>
<li>새 커밋을 별도로 생성하는 것이 아니라 기존의 최신 커밋을 수정함</li>
<li>사용 예시<ul>
<li>commit의 message를 수정하고 싶을 때<ul>
<li>git commit --amend 명령어 입력 후 message 수정 후 commit</li>
</ul>
</li>
<li>commit된 file, code를 수정하고 싶을 때<ul>
<li>git add 명령어를 통해 먼저 Staging Area를 변경</li>
<li>그 이후에 git comit --amend 명령어 입력 후 다시 commit</li>
</ul>
</li>
</ul>
</li>
<li>주의할 점은 수정 전의 최신 커밋을 이미 remote repo에 push한 경우에는 git commit --amend로 커밋을 수정한 후에 push할 때 충돌이 발생함</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[swift의 빌드 과정은 과연 어떻게 되는걸까?]]></title>
            <link>https://velog.io/@minjunkim-dev/swift%EC%9D%98-%EB%B9%8C%EB%93%9C-%EA%B3%BC%EC%A0%95%EC%9D%80-%EA%B3%BC%EC%97%B0-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%90%98%EB%8A%94%EA%B1%B8%EA%B9%8C</link>
            <guid>https://velog.io/@minjunkim-dev/swift%EC%9D%98-%EB%B9%8C%EB%93%9C-%EA%B3%BC%EC%A0%95%EC%9D%80-%EA%B3%BC%EC%97%B0-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%90%98%EB%8A%94%EA%B1%B8%EA%B9%8C</guid>
            <pubDate>Sun, 13 Feb 2022 11:24:07 GMT</pubDate>
            <description><![CDATA[<p>swift로 iOS 개발 공부를 시작하면서, 코드 구현의 결과물을 확인하기 위해 수도 없이 xcode에서 (command + R)을 눌러왔습니다. (command + R)은 &quot;실행(run)&quot;의 단축키로, 우리가 작성한 코드를 빌드(build)하고 이를 특정 타겟(디바이스 or 시뮬레이터)에서 실행시킵니다.</p>
<p>그런데 돌아보니 단 한번도 &quot;swift는 과연 어떤 과정을 거쳐 빌드가 되고 있는가?&quot;에 대한 진지한 고민은 한 번도 해본 적이 없다는 것을 깨달았습니다. 그저 단축키만 무작정 눌러왔는데.. 별로 좋은 습관은 아니겠죠..? 그래서 한번 알아보았습니다!</p>
<h1 id="기초개념-컴파일-링크-빌드-런">기초개념: 컴파일? 링크? 빌드? 런?</h1>
<p>먼저, 기본적인 개념부터 알아봐야겠죠? 정말 많이 들어봤을 위 개념들에 대해 매우 간단하게 먼저 살펴보죠!</p>
<blockquote>
<p>컴파일러
컴파일러는 특정 프로그래밍 언어로 쓰여 있는 문서를 다른 프로그래밍 언어로 옮기는 언어 번역 프로그램을 말한다.
-<em>위키 백과</em></p>
</blockquote>
<p>사람이 작성한 언어를 기계가 읽을 수 있도록 번역하는 것이 바로 컴파일입니다!</p>
<blockquote>
<p>링크
컴파일러의 결과물인 목적(object)코드들을 최종 실행 가능한 실행파일 만들기 위해 연결 및 병합 해주는 작업
-<em>위키 백과</em></p>
</blockquote>
<p>컴파일시, 연관된 파일들을 엮어서 합쳐주는 과정입니다!</p>
<blockquote>
<p>빌드
빌드는 소스코드 파일을 컴퓨터나 휴대폰에서 실행할 수 있는 독립 소프트웨어 가공물로 변환하는 과정을 말하거나 그에 대한 결과물을 일컫는다.
-<em>위키 백과</em></p>
</blockquote>
<p>빌드는 코드, 리소스 등을 하나로 합쳐 실행 가능한 파일을 만드는 과정입니다!</p>
<blockquote>
<p>런(실행)
어떠한 제품을 실제 장치에서 실행시키는 프로세스이다.
-<em>위키 백과</em></p>
</blockquote>
<p>빌드된 파일을 시뮬레이터나 실제 디바이스에서 실행시키는 과정입니다!</p>
<p>정리하면,</p>
<ul>
<li>런(실행) 하기 위해서는 빌드된 파일이 있어야 하고,</li>
<li>빌드 안에는 컴파일 과정이 포함되어 있으며</li>
<li>컴파일 과정 안에 링크 과정이 포함되어 있습니다!</li>
</ul>
<h1 id="빌드-과정">빌드 과정</h1>
<p>이제부터는 우리의 주 관심인 빌드 과정을 큰 틀을 따라가면서 자세히 살펴봅시다!</p>
<p><img src="https://images.velog.io/images/minjunkim-dev/post/6265cd77-0789-4603-aff6-de40824b3e00/Screen%20Shot%202022-02-13%20at%2017.55.13.png" alt=""></p>
<p>많이 들어봤을 전처리기, 컴파일러, 어셈블러, 링커 등이 보입니다. 위 그림의 흐름을 따라가면서 swift의 빌드 과정을 살펴봅시다!</p>
<h2 id="전처리기preprocessor">전처리기(preprocessor)</h2>
<p>작성한 소스코드를 컴파일러에 제공할 수 있도록, 컴파일 전에 전처리를 먼저 실행합니다.</p>
<ul>
<li>매크로를 실제 정의로 바꿈(e.g. #define)</li>
<li>파일 간의 참조 관계(종속성)를 파악(e.g. #include)</li>
<li>컴파일 조건문을 파악(e.g. #if~, #if else, #endif)</li>
</ul>
<p><img src="https://images.velog.io/images/minjunkim-dev/post/a0cb5699-e5c8-4098-80bb-75bfd45c8a2e/Screen%20Shot%202022-02-13%20at%2019.42.39.png" alt=""></p>
<p>그러나, swift 컴파일러에는 전처리기가 없습니다!  </p>
<ul>
<li>그래서, swift에서는 매크로를 사용할 수 없음..</li>
</ul>
<p>🤔 하지만, swift에서도 파일 간의 참조 관계(종속성)나 컴파일 조건문은 분명 존재하는데, 이를 어떻게 처리하고 있는걸까요..?</p>
<ul>
<li>llbuild(하위 레벨 빌드 시스템)을 통해 종속성을 해결함<ul>
<li>llbuild(low-level build system): 빌드 시스템을 구축하기 위한 라이브러리 세트</li>
</ul>
</li>
</ul>
<ul>
<li>컴파일 조건문은 아래 그림과 같이 프로젝트의 Build Settings에서 flag를 설정하여 사용 가능
<img src="https://images.velog.io/images/minjunkim-dev/post/9038ae99-933f-4f88-8b10-6e728747d2d0/Screen%20Shot%202022-02-13%20at%2018.29.14.png" alt=""></li>
<li>Active Compliation Conditions에 RELEASE를 추가하여 DEBUG 모드냐 RELEASE 모드냐에 따라 코드가 다르게 동작하게 할 수 있음<blockquote>
</blockquote>
#if RELEASE
print(&quot;RELEASE에서만 할 코드&quot;)
#elseif DEBUG
print(&quot;DEBUG에서만 할 코드&quot;)
#endif</li>
</ul>
<ul>
<li><p>Other Swift Flags에 원하는 flag를 추가하여 사용 가능</p>
<ul>
<li>단, -D라는 prefix를 반드시 붙여주어야 함</li>
</ul>
<blockquote>
</blockquote>
<p>#if FLAG1
 print(&quot;앱 타켓에서만 할 코드&quot;)
#endif</p>
</li>
</ul>
<h2 id="컴파일러complier">컴파일러(Complier)</h2>
<p>소스코드에 대한 정보를 수집하는 symbol table을 작성 및 관리하며, 아직 고수준 언어를 저수준 언어인 어셈블리어로 변환합니다.</p>
<p><img src="https://images.velog.io/images/minjunkim-dev/post/79d9e0b2-20ef-4fb0-a117-4d9a48f14ba3/Screen%20Shot%202022-02-13%20at%2019.50.07.png" alt=""></p>
<p>그리고, swift의 컴파일 과정은 아래 그림과 같이 LLVM을 거쳐서 실행됩니다.
<img src="https://images.velog.io/images/minjunkim-dev/post/08c0a825-f912-4c5c-8600-2155a6d1a446/Screen%20Shot%202022-02-13%20at%2018.37.11.png" alt=""></p>
<p>🤔 LLVM이란?(<em>약자가 아닌 프로젝트명입니다!</em>)</p>
<ul>
<li>프로그램을 컴파일, 링크, 런타임 등의 상황에서 프로그램의 작성 언어에 상관없이 최적화를 쉽게 구현할 수 있도록 구성되어 있는 컴파일러이자 툴킷</li>
<li>중간 표현(Intermediate Representation, IR), 이진(binary) 코드를 구성, 최적화 및 생성하는데 사용되는 라이브러리</li>
</ul>
<p>본격적으로 swift의 컴파일 과정을 한번 살펴봅시다!
<img src="https://images.velog.io/images/minjunkim-dev/post/7f1a97b9-3d7d-4258-9cb9-95e5a0228c54/Screen%20Shot%202022-02-16%20at%2019.29.43.png" alt=""></p>
<ul>
<li>프론트엔드<ul>
<li>소스코드 =&gt; swift AST</li>
<li>swift AST =&gt; swift IL(SIL)</li>
<li>swift IL =&gt; LLVM IR</li>
</ul>
</li>
<li>LLVM IR =&gt; 백엔드</li>
<li>백엔드(LLVM)<ul>
<li>LLVM IR =&gt; 어셈블리어</li>
<li>어셈블리어 =&gt; 기계어</li>
</ul>
</li>
</ul>
<p>즉, swift는 총 2번의 최적화 과정을 거칩니다!</p>
<ul>
<li>프론트엔드: swift AST =&gt; SIL</li>
<li>백엔드: LLVM IR =&gt; 기계어</li>
</ul>
<p>🤔 SIL(Swift Intermediate Language)이란?</p>
<ul>
<li>swift의 소스 코드를 실행 가능한 바이너리 코드로 바꿀 때, 다른 컴파일러(LLVM)가 취급하기 쉬운 중간 표현(IR)로 변환한 코드(<em>* LLVM IR과 다름!</em>)</li>
<li>swift를 분석하고 최적화하는데 적합하게 변환된 코드
<a href="%5Bapple/swift-llbuild%5D(https://github.com/apple/swift-llbuild)">참고</a>: <a href="https://techblog.woowahan.com/2564/">[번역] SIL(Swift Intermediate Language), 일단 시작해보기까지</a></li>
</ul>
<h3 id="1-프론트엔드">1. 프론트엔드</h3>
<ul>
<li>두 가지 컴파일러를 지원
<img src="https://images.velog.io/images/minjunkim-dev/post/14a7a7b3-7947-4119-9a62-e95516c004c5/Screen%20Shot%202022-02-13%20at%2019.46.37.png" alt=""><ul>
<li>Swift Compiler: swift 용 </li>
<li>Clang: C languagues 용<ul>
<li>소스 코드에 대한 정보를 수집하는 symbol table를 생성</li>
<li>소스 코드를 분석 및 최적화하고, 이를 LLVM이 어셈블리어로 번역해 줄 중간 언어인 LLVM IR을 생성</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="2-백엔드llvm">2. 백엔드(LLVM)</h3>
<ul>
<li>프론트엔드로부터 넘겨받은 LLVM IR을 어셈블리어로 변환하고, 어셈블러가 이를 기계어로 변환</li>
<li>이 때, 비트코드를 생성하는 것으로 설정하면 비트코드로도 변환<ul>
<li>LLVM 어셈블러인 llvm-as가 LLVM IR을 LLVM 비트코드로 변환</li>
<li>아래 그림처럼 설정이 가능하고, Yes로 설정하지 않으면 비트코드 파일은 생성되지 않고 기계어만 생성됨</li>
</ul>
</li>
</ul>
<p><img src="https://images.velog.io/images/minjunkim-dev/post/f3d58b35-8458-4542-aebc-2d4e68efeccc/Screen%20Shot%202022-02-16%20at%2019.20.25.png" alt=""></p>
<p>🤔 비트코드(bitcode, .bc)란?</p>
<ul>
<li>IR(Intermediate Representation) 종류 중 하나</li>
<li>LLVM에 의해 컴파일된, 소스코드와 기계어의 중간단계로 어떤 아키텍처(디바이스)에서도 실행되기를 준비하는 중간단계</li>
</ul>
<h2 id="어셈블러">어셈블러</h2>
<p>사람이 읽고 해석할 수 있는 어셈블리 코드를 재배치가능한(relocatable) 기계어로 변환합니다. 이 때, Mach-O(Mach Object file format) 파일을 생성합니다.</p>
<ul>
<li>컴파일 과정에서 언급한 백엔드(LLVM)의 어셈블러가 어셈블리어를 기계어로 변환합니다!</li>
</ul>
<p><img src="https://images.velog.io/images/minjunkim-dev/post/b3128d81-0065-46a4-a4b9-d027f313cd05/Screen%20Shot%202022-02-13%20at%2020.05.23.png" alt=""></p>
<ul>
<li><p>🤔 기계어란?</p>
<ul>
<li>CPU에서 직접 실행할 수 있는 명령들의 집합을 나타내는 이진 언어</li>
</ul>
</li>
<li><p>🤔 Mach-O(Mach Object file format)란?</p>
<ul>
<li>의미가 있는 data chunks로 그룹화된 바이너리 바이트 스트림으로 구성됨</li>
<li>Darwin 기반 OS(iOS, MacOS, ...)에서 쓰이는 특정한 바이너리 파일 포맷</li>
<li>chunks에는 바이트 순서, CPU 타입, chunks 크기 등과 같은 메타 데이터가 포함됨</li>
</ul>
</li>
<li><p>🤔 재배치가능한 파일이란?</p>
<ul>
<li>실행가능한 파일이나 공유 오브젝트 파일을 생성하기 위해 다른 오브젝트 파일들과 링킹할 수 있는 코드와 데이터를 갖는 파일</li>
<li>아직 실행가능하진 않지만, 실행가능한 오브젝트 파일을 만들기 위해 재배치가능한 다른 오브젝트 파일들과 링크가 가능</li>
</ul>
</li>
<li><p>🤔 실행파일이란?</p>
<ul>
<li>바이너리 코드와 데이터를 가지고 있으며, 메모리로 직접 로드되어 실행될 수 있음</li>
</ul>
</li>
</ul>
<h2 id="링커">링커</h2>
<p>Darwin 기반 OS에서 실행할 수 있는 단일 Mach-O 실행 파일을 만들기 위해 다양한 오브젝트 파일들과 라이브러리들을 병합합니다.</p>
<p><img src="https://images.velog.io/images/minjunkim-dev/post/2165a80b-9b24-42b5-9fc8-61f50bd03df2/Screen%20Shot%202022-02-13%20at%2019.59.04.png" alt=""></p>
<ul>
<li>input: 오브젝트 파일(어셈블러의 결과물) + 라이브러리 파일(동적, 정적)</li>
<li>어셈블러와의 공통점: 둘 다 Mach-O 파일을 output으로 생성</li>
<li>어셈블러와의 차이점: 어셈블러의 Mach-O 파일들은 불완전한 파일<ul>
<li>어셈블러: 재배치가능한 오브젝트 파일을 생성<ul>
<li>다른 오브젝트 파일들이나 라이브러리들을 참조하는 부분들이 누락된 상태</li>
</ul>
</li>
<li>링커: 아래 그림처럼 프로젝트의 Build Settings의 Linking에서 지정한 타입의 파일을 생성<ul>
<li>컴파일 과정에서 생성된 symbol table을 활용하여 여러 오브젝트 파일들과 라이브러리들에 대한 참조를 확인하고, 이들을 연결시켜 줌</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><img src="https://images.velog.io/images/minjunkim-dev/post/4d285043-1dd9-4dae-b959-707346864842/Screen%20Shot%202022-02-13%20at%2019.24.33.png" alt=""></p>
<ul>
<li>archive를 통해 앱을 업로드하는 과정에 링킹이 포함되어 있음<ul>
<li>Enable Bitcode No 설정: 최종 컴파일 결과인 실행 가능한 바이너리 파일 자체를 업로드<ul>
<li>앱이 실행될 수 있는 모든 환경(다양한 아키텍쳐, 디바이스)에 대한 바이너리를 생성하여 하나의 파일로! =&gt; &quot;fat binary&quot;</li>
</ul>
</li>
<li>Enable Bitcode Yes 설정: fat binary를 만들지 않고, 비트코드를 업로드<ul>
<li>앱 스토어는 업로드된 비트코드를 가지고 각 환경별 최적의 바이너리를 생성(앱 시닝)</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="비트코드의-장점">비트코드의 장점</h2>
<ul>
<li>비트코드는 컴파일된 프로그램의 중간 표현(IR), 따라서 다양한 방식으로 다시 컴파일을 시도할 수 있음<ul>
<li>애플은 애플 서버에 제출된 비트코드를 다시 컴파일하여 사용자에게 맞는 최적의 바이너리 파일을 제공할 수 있음</li>
</ul>
</li>
<li>애플은 비트코드로 인해 새로운 CPU에 대한 지원을 앱 스토어의 백엔드에 쉽게 추가가 가능해짐<ul>
<li>최신 아키텍쳐로 컴파일하는 방법을 비트코드에 표시하여 이에 맞게 컴파일되게 할 수 있음</li>
</ul>
</li>
</ul>
<h2 id="비트코드-단점은-없을까">비트코드, 단점은 없을까?</h2>
<p>crash report를 디버깅하고자 할 때(e.g. Firebase Crashlytics), 비트코드를 사용하면 &quot;unsymbolicated crash from missing dSYMs&quot; 에러가 발생하는 경우가 있음</p>
<ul>
<li>개발자가 archive시 생성된 바이너리에 포함된 dSYM 파일과, 앱 스토어에서 비트코드로 생성한 바이너리에 포함된 dSYM 파일이 불일치하여 나타나는 현상</li>
<li>해결방법은 앱 스토어에 가서 dSYM 파일을 다운로드 받아, 업로드하여 사용</li>
</ul>
<p><img src="https://images.velog.io/images/minjunkim-dev/post/2847c29f-84ab-4c8a-8b12-d1e7b8856291/Screen%20Shot%202022-02-17%20at%204.31.31.png" alt=""></p>
<ul>
<li>🤔 dSYM(debug symbol file)이란?<ul>
<li>컴파일러가 소스코드를 기계어로 변환할때 생성되고, 역할은 기계어를 다시 소스코드 라인으로 매핑하는 정보를 가진 파일</li>
<li>Xcode의 build setting에서 Debug Information Format의 설정에 따라 Debug Symbol을 최종 바이너리에 포함시킬지, 별도의 파일로 추출되도록 할 지 설정할 수 있음</li>
</ul>
</li>
</ul>
<p><img src="https://images.velog.io/images/minjunkim-dev/post/9809f659-cd28-4c1f-a40d-5b0cc9e222bf/70849116-9bd06700-1ebd-11ea-80db-19a2b04d7d7e.png" alt=""></p>
<h2 id="앱-시닝app-thinning">앱 시닝(App Thinning)</h2>
<p>서로 다른 각 디바이스에서 필요한 내용만을 선택하여 앱 번들로 만들고 전달하는 과정입니다!</p>
<p><img src="https://images.velog.io/images/minjunkim-dev/post/10d3b80f-77c4-43c5-9dd7-0d43d3e53de3/R1280x0.png" alt=""></p>
<p>위 그림처럼 범용 앱(universal app) 하나를 업로드 하면, 앱 스토어에서 슬라이스가 발생하게 됩니다!</p>
<p>앱 스토어가 디바이스의 특성을 보고, 아래 그림처럼 필요한 것만 조합해서 별도의 IPA를 만듭니다.</p>
<p><img src="https://images.velog.io/images/minjunkim-dev/post/d9ee1b54-e272-4b4b-8d1d-98f450d46b55/R1280x0-2.png" alt=""></p>
<ul>
<li>🤔 IPA란?<ul>
<li>iOS App Store Package의 줄임말로, 패키징이 끝난 압축파일</li>
<li>애플 모바일 스마트기기의 운영체제인 iOS에서 사용하는 앱의 설치 파일   - Xcode에서 앱을 만들면 .app 파일이 되는데, 이 파일을 .zip 형식으로 압축해 확장자만 .ipa로 바꾸는 방식을 사용</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Git 이해하기 Part 1]]></title>
            <link>https://velog.io/@minjunkim-dev/Git-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-Part-1</link>
            <guid>https://velog.io/@minjunkim-dev/Git-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-Part-1</guid>
            <pubDate>Fri, 11 Feb 2022 11:23:27 GMT</pubDate>
            <description><![CDATA[<h1 id="git이란">Git이란?</h1>
<blockquote>
<p>깃(Git)은 컴퓨터 파일의 변경사항을 추적하고 여러 명의 사용자들 간에 해당 파일들의 작업을 조율하기 위한 분산 버전 관리 시스템이다. 또는 이러한 명령어를 가리킨다.
소프트웨어 개발에서 소스 코드 관리에 주로 사용되지만 어떠한 집합의 파일의 변경사항을 지속적으로 추적하기 위해 사용될 수 있다. 기하학적 불변 이론을 바탕으로 설계됐고, 분산 버전 관리 시스템으로서 빠른 수행 속도에 중점을 두고 있는 것이 특징이며 데이터 무결성, 분산, 비선형 워크플로를 지원한다.
-<em>위키백과</em></p>
</blockquote>
<h1 id="소프트웨어-구성관리scm-software-configuration-management">소프트웨어 구성관리(SCM, Software Configuration Management)</h1>
<blockquote>
<p>소프트웨어 공학에서 소프트웨어 구성 관리(software configuration management, SCM, S/W CM)는 더 범위가 큰 구성 관리의 학문간 분야의 일부인, 소프트웨어의 변경사항을 추적하고 통제하는 작업이다.
SCM 행위에는 버전 관리, 베이스라인 확립이 포함된다. 무언가 잘못되는 경우 SCM은 변경사항과 변경한 사람을 특정할 수 있다. 구성이 잘 된 경우 SCM은 수많은 호스트에 걸쳐 이를 복제(replicate)하는 방법을 결정한다.
-<em>위키백과</em></p>
</blockquote>
<p>우리는 바로 &quot;Git&quot;을 통해 소프트웨어 구성관리를 하고 있다고 생각하면 될 것 같습니다!!!</p>
<h1 id="버전-관리-시스템vcs-version-control-system">버전 관리 시스템(VCS, Version Control System)</h1>
<blockquote>
<p>파일 변화를 시간에 따라 기록했다가 나중에 특정 시점의 버전으로 꺼내올 수 있는 시스템</p>
</blockquote>
<p>버전 관리 시스템에는 CVCS vs DVCS가 존재</p>
<h2 id="cvcs중앙집중식-vcs-cenral-version-control-system">CVCS(중앙집중식 VCS, Cenral Version Control System)</h2>
<h4 id="장점">장점</h4>
<ul>
<li>운영/관리가 직관적</li>
<li>관리자가 모든 작업에 대해 실시간 체크 가능</li>
</ul>
<h4 id="단점">단점</h4>
<ul>
<li>커밋시 바로 중앙 히스토리에 반영</li>
<li>자신만의 작업 히스토리는 존재하지 않음</li>
<li>규모가 커질수록 매우 느려짐. why?<ul>
<li>버전 관리 관련 연산을 &quot;중앙서버&quot;에서 모두 처리</li>
<li>델타(Delta) 방식으로 데이터를 관리</li>
</ul>
</li>
</ul>
<h4 id="🤔-델타-방식">🤔 델타 방식</h4>
<ul>
<li>프로젝트 내의 각 파일의 변경사항을 시간순으로 계속 쌓아가며 파일들의 집합을 관리하는 방식</li>
<li>파일의 생성부터 변경사항들을 계속 따라가야 하고,
버전을 가져올 때 변경사항들을 계산하여 가져와야 해서,
커밋이 많아질수록 속도가 느려짐<ul>
<li>버전 5를 가져오려면 파일의 생성을 기준으로, 모든 델타들을 계산하여 가져와야하기 때문에 커밋이 쌓일수록 느려질 수 밖에 없음</li>
</ul>
</li>
</ul>
<p><img src="https://images.velog.io/images/minjunkim-dev/post/7c298629-670c-4185-bf5f-355ae867b6af/R1280x0.png" alt=""></p>
<h3 id="dvcs분산형-vcs-distribution-version-control-system">DVCS(분산형 VCS, Distribution Version Control System)</h3>
<h4 id="장점-1">장점</h4>
<ul>
<li>코드의 분산관리로 인해 중앙서버 데이터가 유실되어도 복구 가능</li>
<li>자신만의 작업공간이 별도로 있기 때문에,
네트워크가 연결되어있지 않아도 커밋 등 버전 관리가 가능</li>
<li>규모가 커지더라도 속도가 빠름<ul>
<li>버전 관리를 위한 대부분의 연산이 &quot;로컬&quot;에서 이루어짐 </li>
<li>스냅샷(Snapshot) + 델타 방식으로 데이터를 관리</li>
</ul>
</li>
</ul>
<h4 id="🤔-스냅샷">🤔 스냅샷</h4>
<ul>
<li>파일을 하나씩 별도로 보며 차이점(즉, 델타)만 따로 관리하지 않고, 프로젝트 전체를 스냅샷 형태로 관리</li>
<li>매 순간마다 프로젝트 전체를 사진으로 찍어서 관리하는 것과 같은 형태</li>
<li>버전이 변경되면 변경된 버전의 스냅샷을 하나 만들어 놓고 그 스냅샷을 가리키는 레퍼런스를 저장</li>
<li>효율을 위해, 변경되지 않은 파일은 새로 저장을 하는 것이 아니라 이미 저장되어 있는 파일에 링크만 걸어줌</li>
</ul>
<p><img src="https://images.velog.io/images/minjunkim-dev/post/06bee819-7ab1-4d9a-90b7-53f620a086b7/R1280x0-2.png" alt=""></p>
<ul>
<li>매 버전마다 스냅샷을 만들고 저장하면 많은 용량을 차지하고 느려지지 않을까? =&gt; <em><strong>No!!!</strong></em> <ul>
<li>마지막 커밋의 스냅샷만 통째로 저장하고 나머지 커밋에 대해서는 스냅샷과 스냅샷이 차이를 기록한 &quot;델타&quot;만을 저장하기 때문임
(스냅샷 + 델타로 데이터 관리)</li>
</ul>
</li>
</ul>
<p><img src="https://images.velog.io/images/minjunkim-dev/post/a6feaef3-b2be-4300-82fb-270c0e4f7286/R1280x0-3.png" alt=""></p>
<ul>
<li>버전별로 전체 스냅샷을 모두 저장하지 않아도 마지막 스냅샷을 기준으로 특정 시점의 스냅샷을 만들 수 있어, 저장소의 크기도 자연스럽게 줄어듦</li>
</ul>
<h1 id="git의-중요-5대-개념">Git의 중요 5대 개념</h1>
<blockquote>
</blockquote>
<ol>
<li>파일상태</li>
<li>작업공간</li>
<li>커밋</li>
<li>브랜치</li>
<li>HEAD</li>
</ol>
<p>git의 대부분의 기능은 위 5가지 개념을 활용하는 과정</p>
<h1 id="git-init-git-시작을-위한-초기화">git init: Git 시작을 위한 초기화</h1>
<ul>
<li>CLI에 git init 명령어 입력시, .git 폴더가 생성됨</li>
<li>git은 코드 변화에 대한 히스토리 및 상태를 .git 폴더에서 관리<ul>
<li>.git 폴더 삭제시, 관리하던 모든 이력이 삭제됨<ul>
<li>따라서, 작업했던 프로젝트 파일이 있더라도 git 관리가 불가능</li>
</ul>
</li>
<li>커밋, 로그, 데이터 등을 git 내부적으로
Refs, Blob, Tree 시스템을 통해 관리</li>
</ul>
</li>
</ul>
<h1 id="git-add-working-directory-to-staging-area">git add: Working Directory to Staging Area</h1>
<p><img src="https://images.velog.io/images/minjunkim-dev/post/2f3ef8a7-8309-4f93-b591-50998418ecdc/Screen%20Shot%202022-02-11%20at%2018.48.19.png" alt=""></p>
<h2 id="working-directory">Working Directory</h2>
<h3 id="untrackted">Untrackted</h3>
<ul>
<li>아직 git에 의해 한번도 관리되지 않은 상태</li>
<li>즉, git add 명령어로 아직 한번도 Staging Area에 진입하지 않은 상태</li>
</ul>
<h3 id="tracked">Tracked</h3>
<ul>
<li>git add 명령어를 통해 Staging Area에 진입하여
git에 의한 관리가 시작된 상태</li>
</ul>
<h3 id="modified">Modified</h3>
<ul>
<li>&quot;Tracked&quot; 상태의 파일이 수정된 상태</li>
</ul>
<h2 id="staging-area">Staging Area</h2>
<ul>
<li>Git Index라고도 함</li>
<li>워킹 디렉토리의 변경사항을 바로 로컬 저장소에 기록(커밋)하는 것이 아니라, 여기에 먼저 기록하는 과정을 거침<ul>
<li>다음 커밋의 스냅샷을 위한 밑바탕</li>
<li>작업 과정에서의 중간 산출물을 적절하게 선택하여
커밋을 할 수 있음</li>
</ul>
</li>
<li>병합 충돌(merge conflict)가 일어났을 때, 임시 상태의 데이터를 관리<ul>
<li>Staging Area가 없다면 메모리 상에서 이를 관리해야 함</li>
</ul>
</li>
</ul>
<h1 id="git-commit-staging-area-to-local-repository">git commit: Staging Area to Local Repository</h1>
<p><img src="https://images.velog.io/images/minjunkim-dev/post/e2ea81db-ee26-4df6-8f51-1e8242233193/Screen%20Shot%202022-02-11%20at%2019.00.30.png" alt=""></p>
<h2 id="commit">commit</h2>
<ul>
<li>의미있는 변화에 대한 히스토리</li>
<li>Staging Area에 Index된 코드의 변화를 커밋을 통해 히스토리화</li>
<li>root commit을 제외한 커밋들은 parent commit을 가짐</li>
<li>현재 커밋의 이전 커밋이 parent commit
<img src="https://images.velog.io/images/minjunkim-dev/post/aad930e3-8e11-485a-9f7f-1a3d4f5f7918/Screen%20Shot%202022-02-11%20at%2019.06.58.png" alt=""></li>
<li>부모 커밋에 변화가 발생하면, 부모 커밋의 자식 커밋도 변화<ul>
<li>이러한 현상은 연쇄적으로 발생하기 때문에, 특정 커밋에 변화가 발생하면 이후의 모든 커밋에 변화가 발생</li>
<li>따라서, 특정 커밋을 변화하고자 시키고자 하는 경우에는 주의가 필요(특히, 협업시 주의 요망)</li>
</ul>
</li>
<li>변화된 값과 커밋 정보를 기반으로 SHA-1 알고리즘 해싱을 하여 commit id를 생성</li>
</ul>
<h3 id="커밋-정보log에는-어떠한-정보들이-있을까">커밋 정보(log)에는 어떠한 정보들이 있을까?</h3>
<blockquote>
</blockquote>
<ul>
<li>작성자, 작성일자</li>
<li>커밋 메세지</li>
<li>커밋 ID(hash)</li>
<li>변경이력</li>
<li>parent commit</li>
<li>Etc.</li>
</ul>
<h3 id="commit은-매우-중요하다">commit은 매우 중요하다!!!</h3>
<ul>
<li>프로젝트가 커지면 커밋의 수도 매우 늘어나므로, 프로젝트의 관리를 수월하게 하기 위해서는 commit을 &quot;잘&quot; 해야함<ul>
<li>커밋 메세지는 항상 자세히 기재</li>
<li>커밋 단위에 신경쓰기<ul>
<li>작업의 중간물을 저장하는 차원에서 커밋X</li>
<li>여러 기능을 묶어서 커밋X</li>
<li>정상 동작을 보장하는 경우에만 커밋O</li>
</ul>
</li>
<li>[참고]: [규칙적인 Commit 메세지로 개발팀 협업하기] (<a href="https://xtring-dev.tistory.com/entry/Git-%EA%B7%9C%EC%B9%99%EC%A0%81%EC%9D%B8-Commit-%EB%A9%94%EC%84%B8%EC%A7%80%EB%A1%9C-%EA%B0%9C%EB%B0%9C%ED%8C%80-%ED%98%91%EC%97%85%ED%95%98%EA%B8%B0-%F0%9F%91%BE">https://xtring-dev.tistory.com/entry/Git-규칙적인-Commit-메세지로-개발팀-협업하기-👾</a>)</li>
</ul>
</li>
</ul>
<h3 id="commit-id">commit id</h3>
<ul>
<li>git 무결성을 위한 체크썸</li>
<li><em>160bit_로 구성: _40자리 * 16진수(4bit)</em></li>
<li>SHA-1 알고리즘으로 만들어진 hash 값을 사용하기 때문에 중복값이 없어 commit의 순서를 기억할 수 있고, 이로 인해 온/오프라인 어떤 환경에서도 commit이 가능</li>
<li>일반적으로 8~10자로도 프로젝트 내에서 고유성을 유지하기에 충분하기 때문에, git에서는 기본값으로 7자리의 짧은 형태를 사용</li>
</ul>
<h1 id="git-pushfetch-pull-local-repository--remote-repository">git push/fetch, pull: Local Repository &lt;=&gt; Remote Repository</h1>
<p><img src="https://images.velog.io/images/minjunkim-dev/post/efecba75-c083-461f-ba1c-3c7c98b37e76/Screen%20Shot%202022-02-11%20at%2019.06.15.png" alt=""></p>
<h2 id="push">push</h2>
<ul>
<li>로컬 저장소에 커밋된 내용(HEAD가 가리키는 커밋)을 원격 저장소로 보내는 명령어</li>
</ul>
<h2 id="fetch">fetch</h2>
<ul>
<li>원격 저장소의 최신 내용을 로컬 저장소로 가져오지만, 병합하지는 않는 명령어</li>
</ul>
<h2 id="pull">pull</h2>
<ul>
<li>원격 저장소의 최신 내용을 로컬 저장소로 가져오면서 병합까지 하는 명령어(fetch + merge)</li>
</ul>
<h1 id="git의-branch와-head">Git의 BRANCH와 HEAD</h1>
<h2 id="branch">BRANCH</h2>
<ul>
<li>독립적인 작업공간을 생성하는 개념</li>
<li>여러 브랜치를 생성하여 하나의 프로젝트를 여러 갈래로 나누어서 독립적으로 관리할 수 있음</li>
<li>여러 개발자들이 동시에 다양한 작업을 할 수 있게 만들어 주는 기능</li>
<li>브랜치는 항상 생성된 해당 브랜치의 가장 마지막 커밋을 가리킴</li>
</ul>
<h2 id="head">HEAD</h2>
<ul>
<li>특정 커밋을 가리키는 특수한 포인터</li>
<li>HEAD에 따라 워킹 디렉토리의 파일도 변경됨</li>
<li>git checkout 명령어를 통해 HEAD 포인터를 변경하여 작업 공간을 전환함<ul>
<li>다른 브랜치로 옮겨 로컬 브랜치를 변경할 수 있음</li>
<li>해당 브랜치에서 최신 커밋이 아닌 과거의 커밋을 가리킬 수 있음</li>
</ul>
</li>
<li>새로운 커밋이 생성된 경우 새로운 커밋을 가리킴</li>
<li>HEAD의 변경은 git 내부에서 log로 관리</li>
<li>일반적으로 브랜치를 가리키지만, 다른 커밋을 가리키게 할 수 있음(Detached HEAD)<ul>
<li>과거 시점의 코드를 확인하고자 할 때</li>
<li>과거 시점에서 브랜치를 생성해야 할 때</li>
</ul>
</li>
</ul>
<h1 id="git-merge-3-way-vs-fast-forward">git merge: 3-way vs Fast-forward</h1>
<h2 id="3-way-merge">3-way merge</h2>
<ul>
<li>새로운 브랜치 생성 이후, 메인 브랜치에서 커밋이 발생하여 Fast-forward merge가 불가능한 상황에 진행</li>
</ul>
<p><img src="https://images.velog.io/images/minjunkim-dev/post/b5adb842-958d-425d-9404-a9fd1d62c2db/Screen%20Shot%202022-02-13%20at%2017.05.53.png" alt=""></p>
<ul>
<li>base commit: 브랜치가 생성되는 기점이 되는 commit</li>
<li>base commit(274aacc)과 각 브랜치의 commit(0f039ae, E7ac282)을 비교하여 병합<ul>
<li>3가지 commit을 비교하여 auto-merge 시도 이후,
해결되지 않는 부분에 대해서는 conflict가 발생</li>
</ul>
</li>
</ul>
<ul>
<li>3-way merge의 예시</li>
</ul>
<table>
<thead>
<tr>
<th align="center">Case</th>
<th align="center">M branch</th>
<th align="center">Base</th>
<th align="center">A Branch</th>
<th></th>
<th align="center">Merge</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><strong>case1</strong></td>
<td align="center">swift</td>
<td align="center">swift</td>
<td align="center">app</td>
<td>&gt;</td>
<td align="center">app</td>
</tr>
<tr>
<td align="center"><strong>case2</strong></td>
<td align="center">android</td>
<td align="center">iOS</td>
<td align="center">iOS</td>
<td>&gt;</td>
<td align="center">android</td>
</tr>
<tr>
<td align="center"><strong>case3</strong></td>
<td align="center">apple</td>
<td align="center">apple</td>
<td align="center"></td>
<td>&gt;</td>
<td align="center"></td>
</tr>
<tr>
<td align="center"><strong>case4</strong></td>
<td align="center">trial</td>
<td align="center">and</td>
<td align="center">error</td>
<td>&gt;</td>
<td align="center">&quot;conflict!&quot;</td>
</tr>
</tbody></table>
<ul>
<li>3-way merge의 경우, merge의 결과인 merge commit(92a84bb)이 생성됨</li>
</ul>
<h2 id="fast-forward-merge">Fast-forward merge</h2>
<ul>
<li>메인 브랜치에서 분기한 이후, 메인 브랜치에서는 변경 사항이 없는 경우 진행되는 머지</li>
</ul>
<p><img src="https://images.velog.io/images/minjunkim-dev/post/f2011f76-499f-4daa-8416-77fd8c1acdb8/Screen%20Shot%202022-02-13%20at%2017.14.32.png" alt=""></p>
<ul>
<li>구조적으로 conflict는 발생하지 않음</li>
<li>3-way와 달리 merge의 결과인 merge commit이 생기지는 않으며, 단지 메인 브랜치의 포인터가 분기한 브랜치의 최신 커밋으로 이동<ul>
<li>merge에 대한 히스토리가 남지 않기 때문에 이러한 형태의 병합을 선호하지 않는 경우도 있다고 함</li>
</ul>
</li>
</ul>
<h1 id="git-rebase">git rebase</h1>
<h2 id="rebase-다시-베이스를-만든다">rebase: 다시 베이스를 만든다!</h2>
<ul>
<li>말 그대로 base commit을 변경함</li>
<li>rebase시, base commit이 변경되어 그 이후의 커밋들이 모두 변경되므로 협업시 주의가 필요</li>
<li>rebase를 한 이후에는 자동으로 merge됨</li>
</ul>
<p><img src="https://images.velog.io/images/minjunkim-dev/post/064849ad-f444-4b91-84a9-58b6c97062bb/Screen%20Shot%202022-02-13%20at%2017.27.13.png" alt=""></p>
<ul>
<li>rebase가 불필요한 경우에는 할 필요 없음<ul>
<li>아래의 경우는 바로 merge(Fast-forward)하면 됨</li>
</ul>
</li>
</ul>
<p><img src="https://images.velog.io/images/minjunkim-dev/post/027f603e-a94b-4cb4-a01c-2e66448f8776/Screen%20Shot%202022-02-13%20at%2017.29.46.png" alt=""></p>
<h4 id="🤔-끝내기-전-짤막한-팁">🤔 끝내기 전, 짤막한 팁!!!</h4>
<ol>
<li>유닉스 시스템에서 파일명 앞에 .을 붙이면 &quot;숨김파일 처리&quot;가 됩니다.</li>
<li>Blob이란? Binary large object를 의미합니다.  큰 객체를 바이너리 형태로 저장한다는 의미로, 주로 멀티미디어 데이터가 이에 해당됩니다.</li>
<li>github의 private repository의 경우, 일반적으로 commit 작업 이력이 잔디에 표시되지 않습니다... 따라서, 필요한 경우 별도의 설정이 필요합니다. 
[참고]: [private 저장소의 작업 내역 github 잔디에 표시하기] (<a href="https://codedragon.tistory.com/8745">https://codedragon.tistory.com/8745</a>)</li>
</ol>
]]></description>
        </item>
    </channel>
</rss>