<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>ORI - Overcome, Refine, Innovate</title>
    <link>https://kwon-ori.tistory.com/</link>
    <description>게임 만드는 오리</description>
    <language>ko</language>
    <pubDate>Fri, 15 May 2026 19:44:38 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Kwon_Ori</managingEditor>
    <image>
      <title>ORI - Overcome, Refine, Innovate</title>
      <url>https://tistory1.daumcdn.net/tistory/7481729/attach/427a2de2b6124c1e9129a1d6fce8c732</url>
      <link>https://kwon-ori.tistory.com</link>
    </image>
    <item>
      <title>FacebookSDK 제한적 로그인에 대한 정리 (with Unity)</title>
      <link>https://kwon-ori.tistory.com/13</link>
      <description>&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;Meta (= Facebook) SDK v17 부터 iOS 앱에서 앱 추적을 거절한 상태에서 제한적 로그인을 사용하지 않으면 액세스 토큰을 반환하지 않도록 변경되었다. 메타에서는 이를 제대로 공지했으나,최근까지도 많은 개발자들이 '페이스북 인증이 되지 않는다!' 라는 하소연을 하고 있는 것을 쉽게 발견할 수 있었다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;아무튼 우리 프로젝트도 위와 같은 문제로 고대의 누군가가 구현해둔 페이스북 로그인 기능을 제한적 로그인으로 교체했는데 작업하면서 알게된 (또는 테스트로 밝혀진) 것들에 대해 정리해 보았다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;1) 제한적 로그인을 사용하면 AccessToken 과 그에 관련된 대부분의 정보를 사용할 수 없다. 유니티용 페이스북 SDK 는 페이스북 로그인을 하게 되면 내부적으로 갱신해버리고, 전역으로 선언된 변수를 사용하게끔 만들어져있다보니 기존에 사용하던 액세스 토큰을 '그대로 써도 되나?' 라는 생각부터 들텐데 절대 쓰면 안 된다. 제한적 로그인은 액세스 토큰을 전혀 반환하지 않고 JWT 형식의 인증 토큰과 프로필이라는 형태로 되돌려준다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;2) 제한적 로그인을 사용하면 서버 사이드 인증도 크게 바뀐다. 이것도 1) 과 같이 문서에 잘 명시된 내용이지만 살짝 난해하게 설명되어 있는데, 한 줄로 요약하면 '클라이언트로부터 받은 JWT 를 서버 내부 로직으로 알아서 검증해!' 이다. (원랜 액세스 토큰 값을 던지면 본인들 서버에서 검증해줌)&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;3) 제한적 로그인으로 반환하는 정보가 변경되면서 UserID 와 같은 값들이 바뀌지 않을까? 라는 우려가 있었는데, 다행히 UserID 는 기존 값을 그대로 반환해주고 있었다. 너무 당연한 내용이라서 그런건지 모르겠지만, 이와 관련하여 속 시원하게 명시해둔 곳이 없어서 두 가지 로그인 방식으로 직접 검증을 해봤다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;아래 두 항목은 확실하게 밝혀진 것이 아닌, 관찰된 현상들이다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;내 추측으로는 앱 내에서 페이스북 로그인을 했을 때 남아있는 인증 정보로 인한 현상으로 보인다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;4) (제한적 로그인 미적용 상태) FB.Mobile.SetAdvertiserTrackingEnabled(...) 메서드를 사용하는 경우, 액세스 토큰을 반환하지 않는 현상이 재현되지 않기도 했다. 애초에 v17 부터 호출할 필요가 없는 기능이지만, 인터페이스 자체는 남아있어서 실수로 남겨두는 경우 ATT 거절을 하든 말든 액세스 토큰이 정상적으로 반환되는 말도 안 되는 현상이 발생한다. 따라서, v17 을 사용할 땐 해당 메서드를 반드시 제거해야한다. 우리 프로젝트도 QA에서 발견되지 않아 한동안 라이브까지 나갔는데 어느날 갑자기 로그인이 되지 않는다는 제보가 빗발쳐서 대응하게 되었다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;5) (제한적 로그인 미적용 상태) ATT 를 한 번 거절한 이후, 앱을 재설치 하고 ATT 팝업을 다시 만나서 허용할 때까지 ATT 거절로 판정되어 액세스 토큰을 던져주지 않는다. ATT 가 제대로 갱신되지 않는 현상은... iOS14 부터 발견되었던 현상이라   뭐가 원인인지는 여전히 알 수 없다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;일단 지금까지 알게 된 사항은 이 정도이다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;우리 게임이 오래된 프로젝트라 기술 부채로 인한 잔고장...일 수도 있는데, 혹시나 비슷한 문제가 생긴 누군가(또는 미래의 나)에게 도움이 될까하여 남겨보았다.&lt;/p&gt;</description>
      <category>오리 도서관/(미분류) 삽질 방지 개발 지식</category>
      <category>Facebook</category>
      <author>Kwon_Ori</author>
      <guid isPermaLink="true">https://kwon-ori.tistory.com/13</guid>
      <comments>https://kwon-ori.tistory.com/13#entry13comment</comments>
      <pubDate>Fri, 14 Mar 2025 17:13:16 +0900</pubDate>
    </item>
    <item>
      <title>[TIP] 코딩 테스트를 연습할 때, 파일로 입력을 받기 ( C/C++ )</title>
      <link>https://kwon-ori.tistory.com/12</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;스켈레톤 코드와 테스트 실행 환경을 쥐어주는 Leet Code 나 프로그래머스와 달리 백준 온라인 저지는 입력부터 출력까지 모든 부분을 작성하고, 제출 전 테스트 또한 본인이 쓰고 있는 IDE 에서 실행해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아마 나를 포함해서 대부분의 유저들은 실행, 입력 복붙, 출력 확인, 아이고 틀렸네를 반복하고 있을텐데 이를 파일을 통해 간단하게 입력을 받아 테스트 하는 방법이 있었다. 파일 입출력은 매번 실행할 때마다 복붙을 해야하는 번거로움을 없애줄 뿐만 아니라 가끔 종료 조건이 eof 인 문제들도 깔끔하게 테스트 할 수 있다는 장점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방법은 정말 간단하므로, IDE 에 한 번 세팅해서 두고두고 써먹으면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) (VS 기준) 소스코드와 동일한 위치에 테스트 입력 파일을 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) 실행할 메인문의 입력부에 아래 코드를 추가한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ONLINE_JUDGE 는 백준 온라인 저지 채점 서버 쪽에 정의된 매크로인데, ifndef 로 조건부 컴파일을 걸어두면 제출할 때마다 파일 입력 코드 라인을 지울 필요가 없어진다. ( 그 외에 디버그 출력에도 조건부 컴파일을 걸어두면 굉장히 편리하다 )&lt;/p&gt;
&lt;pre id=&quot;code_1739168332163&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#ifndef ONLINE_JUDGE
    freopen(&quot;input.txt&quot;, &quot;r&quot;, stdin);
#endif&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;3) 경우에 따라 freopen_s 를 사용하지 않았다며 컴파일 에러가 나올텐데, 제출과 무관한 코드이므로 freopen_s 를 사용하도록 코드를 수정하거나 전처리기 정의에 &lt;b&gt;_CRT_SECURE_NO_WARNINGS&amp;nbsp;&lt;/b&gt;를 추가하면 된다.&lt;/p&gt;</description>
      <category>오리 도서관/(미분류) 삽질 방지 개발 지식</category>
      <author>Kwon_Ori</author>
      <guid isPermaLink="true">https://kwon-ori.tistory.com/12</guid>
      <comments>https://kwon-ori.tistory.com/12#entry12comment</comments>
      <pubDate>Mon, 10 Feb 2025 19:00:09 +0900</pubDate>
    </item>
    <item>
      <title>[Build your own git] #5 write-tree 명령어 만들기</title>
      <link>https://kwon-ori.tistory.com/9</link>
      <description>&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이전 연재글&lt;/b&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;color: #666666; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://kwon-ori.tistory.com/1&quot;&gt;[Build your own git] #0 시작&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #666666; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://kwon-ori.tistory.com/2&quot;&gt;[Build your own git] #1 git init 과 .git 폴더의 구조&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #666666; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://kwon-ori.tistory.com/3&quot;&gt;[Build your own git] #2 cat-file 명령어와 Git Object&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #666666; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://kwon-ori.tistory.com/6&quot;&gt;[Build your own git] #3 git hash-object 명령어 만들기&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #666666; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://kwon-ori.tistory.com/7&quot;&gt;[Build your own git] #4 ls-tree 명령어와 tree objects&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&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;git write-tree&lt;/li&gt;
&lt;li&gt;tree objects&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;서론&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;지난 글에서 tree objects 에서 파일/디렉터리 이름을 가져오는 ls-tree --name-only&amp;nbsp; 기능을 구현해보았으니 이번엔 tree object 를 생성하는 기능인 write-tree 를 구현해보았다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;이것 또한 hash-object 처럼 읽는 것의 역순으로 차근차근 접근하면 문제 없지만, 단일 파일을 다루는 blob 과 달리 트리의 정보를 구축하는 동작이기 때문에 재귀적으로 하위에 있는 폴더와 파일들의 해시(20byte binary 형식) 을 계산할 필요가 있었다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;b&gt;리마인드&lt;/b&gt; - Tree Objects 의 구조&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;트리 오브젝트는 트리의 전체적인 정보를 나타내는 '헤더'와 해당 디렉터리에 속한 파일/하위 디렉터리의 정보가 나열된 '엔트리'로 구성되어 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1736215465454&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;tree &amp;lt;size&amp;gt;\0
&amp;lt;mode&amp;gt; &amp;lt;name&amp;gt;\0&amp;lt;20_byte_sha&amp;gt;
&amp;lt;mode&amp;gt; &amp;lt;name&amp;gt;\0&amp;lt;20_byte_sha&amp;gt;
(...)&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; 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;b&gt;&amp;lt;size&amp;gt;&lt;/b&gt; : 실제 하위 파일의 사이즈가 아닌 트리 오브젝트에서 &lt;u&gt;엔트리가 차지하는 바이트&lt;/u&gt;(압축하지 않은 상태)를 의미한다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;b&gt;&amp;lt;20_byte_sha&amp;gt;&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; : 지금까지 해싱을 할 때 사용했던 hex 형식의 문자열 아닌 정확히 20 바이트의 바이너리를 그대로 저장해야한다. (open ssl 로 SHA-1 해시를 생성했을 때 출력되는 uint8_t 배열을 파일에 그대로 입력)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&amp;lt;mode&amp;gt;&lt;/b&gt; : 실제 git 명령어로 모드 값을 추출했을 때 040000 으로 출력되는데, 실제 코드에서 모드 값을 입력할 때엔 &lt;u&gt;앞의 0은 제외&lt;/u&gt;하고 40000 으로 해야만 한다. 이걸 모르는 상태에서 트리 오브젝트를 생성했을 때, 예상되는 해시값이 나오지 않아 혼란에 빠졌다. 육안으로 보면 완전 동일했기 때문에 사이즈를 확인해보니 직접 생성한 파일의 사이즈가 2바이트 더 크게 나왔다. 트리 오브젝트를 생성하는 로직을 하나하나 살펴보던 중, mode 앞에 0을 붙인 것이 가장 의심이 되었다. 40000 은 디렉터리에 붙는 모드 값이고 0 문자열 하나는 1바이트, 그리고 현재 트리의 디렉터리 갯수는 2개. 가설이 맞다면 '0' 2개로 인해 2바이트가 늘어날 것이다. 그리고 그 가설은 진짜였다(...) 실제 파일을 까볼 수 없어 git 명령어로만 출력해서 본 탓에 생긴 문제였던 것 같다.&amp;nbsp; 직접 코드를 통해 압축을 해제해서 나온 값을 기준으로 작업했다면 발생하지 않았을 것 같다. 다음 기능에는 직접 압축을 풀어서 봐야겠다.&lt;/li&gt;
&lt;li&gt;엔트리의 순서는 : 디렉터리 &amp;gt; 파일로 저장되어야하며 동일한 타입끼린 오름차순으로 정렬해야한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;필요한 구현&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;write-tree 기능은 '스테이징 영역(Staging Area)' 에 있는 정보를 토대로 트리에 추가할지 말지 결정을 하는데, 이번 구현에서는 워킹 디렉터리에 있는 파일들이 모두 스테이징 영역에 있다는 가정하에 구현했다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;0) 재귀 호출을 위해 write_tree 라는 함수로 작성한다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;1) (shell , bash 기준) 현재 위치에 속한 파일과 디렉터리를 모두 순회한다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;2-1) 파일인 경우 hash-object 처럼 blob object 를 생성하고 만들어진 20byte 해시를 가져와서 파일의 데이터를 만든다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;ex) 100644 &amp;lt;파일 이름&amp;gt;\0 &amp;lt;20바이트 해시&amp;gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;2-2) 디렉터리인 경우 write_tree 를 재귀호출하고 만들어진 20byte 해시를 가져와서 디렉터리의 데이터를 만든다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;ex) 40000 &amp;lt;디렉터리 이름&amp;gt;\0 &amp;lt;20바이트 해시&amp;gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;3) 2-1), 2-2) 에서 만들어진 데이터들을 규칙에 맞게 정렬하고 하나로 합친다. (= 엔트리)&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;4) tree &amp;lt;엔트리의 사이즈(바이트)&amp;gt;\0 로 헤더를 만든다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;5) 헤더와 엔트리를 합쳐 원본 형태의 트리 오브젝트를 만들고 SHA-1 해싱한 값(여기서는 40-char hex 문자열)을 저장해둔다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;6) 원본 형태의 트리 오브젝트를 압축하고, 5)에서 만든 해시를 사용하여 objects/디렉터리(2-char) / 파일이름(38-char) 로 저장한다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;7) 모든 과정이 성공했다면 5) 의 hex 문자열 해시를 콘솔에 출력한다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;코드&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;아래는 write_tree 함수이다. 파일을 생성하고 그 결과로 나온 바이너리 형태의 해시와 텍스트 형식의 해시를 외부에 제공하기 위해 다소 요상한 형태의 매개변수 목록이 만들어졌다. 사실 바이너리 해시만 반환하고 외부에서 직접 텍스트로 바꾸면 간단해지지 않을까? 라고 생각했지만 내부에서도 트리 오브젝트 파일을 만들 때 디렉터리와 파일 이름을 위해 텍스트 형식의 해시를 만들어버리기 때문에 외부에서 텍스트 형식으로 변환하는건 중복으로 일하게 되는 꼴이라 함께 외부로 반환하게 되었다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;그 외에도 지적할 부분이 많아 보여서 지속적으로 개선할 계획이다 :(&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1736219063556&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int write_tree(const std::string&amp;amp; path, std::string&amp;amp; outHash_hex, uint8_t *&amp;amp; outHash) {
/*
tree &amp;lt;size&amp;gt;\0
&amp;lt;mode&amp;gt; &amp;lt;name&amp;gt;\0&amp;lt;20_byte_sha&amp;gt;
&amp;lt;mode&amp;gt; &amp;lt;name&amp;gt;\0&amp;lt;20_byte_sha&amp;gt;
&amp;lt;20_byte_sha&amp;gt; =&amp;gt; not hexadeciaml format
*/

    // iterate current directory
    // assume all files in the working directory are staged. (except .git)
    
    auto content_data = std::string();
    auto iter_directory = std::filesystem::directory_iterator(std::filesystem::path(path));

    // 간단하게 디렉터리와 파일의 순서를 정하기 위한 벡터들
    auto vec_string_directories = std::vector&amp;lt;std::string&amp;gt;();
    auto vec_string_files = std::vector&amp;lt;std::string&amp;gt;();
    
    for (const auto&amp;amp; dirEntry : iter_directory) {
        auto pathString = dirEntry.path().string();
        if (pathString.find(&quot;.git&quot;) != pathString.npos) {
            // ignore .git
            continue;
        }

        auto hash_hex = std::string();
        uint8_t* hash = new uint8_t[SHA_DIGEST_LENGTH];
        if (dirEntry.is_directory()) {

            if (auto res = write_tree(pathString, hash_hex, hash); res == EXIT_FAILURE) {
                return EXIT_FAILURE;
            }

            std::string strDirectory = &quot;40000 &quot; + dirEntry.path().filename().string() + '\0';
            strDirectory.append(reinterpret_cast&amp;lt;char*&amp;gt;(hash), SHA_DIGEST_LENGTH);
            vec_string_directories.push_back(strDirectory);
        }
        else {
            if (auto res = write_blob(pathString, hash_hex, hash); res == EXIT_FAILURE) {
                return EXIT_FAILURE;
            }

            std::string strFile = &quot;100644 &quot; + dirEntry.path().filename().string() + '\0';
            strFile.append(reinterpret_cast&amp;lt;char*&amp;gt;(hash), SHA_DIGEST_LENGTH);
            vec_string_files.push_back(strFile);
        }
    }

    // directory_iterator 는 이름 오름차순 정렬을 보장해주지 않았다.
    std::sort(vec_string_directories.begin(), vec_string_directories.end());
    std::sort(vec_string_files.begin(), vec_string_files.end());

    // 디렉터리 &amp;gt; 파일 순으로 최종 컨텐츠에 스트링을 더한다.
    for (const auto&amp;amp; strEntry : vec_string_directories)
    {
        content_data.append(strEntry);
    }

    for (const auto&amp;amp; strEntry : vec_string_files)
    {
        content_data.append(strEntry);
    }

    // 압축 전의 최종 컨텐츠의 사이즈가 트리의 사이즈 정보가 된다.
    int treeSize = content_data.size();
    std::string tree_object = &quot;tree &quot; + std::to_string(treeSize) + '\0' + content_data;
    std::string compressed_object = std::string();
    if (auto res = compressData(compressed_object, tree_object); res == EXIT_FAILURE) {
        return EXIT_FAILURE;
    }

	get_hash(tree_object, outHash);
	outHash_hex = hexStr(outHash, SHA_DIGEST_LENGTH);

	const auto tree_hash_hex = std::string_view(outHash_hex);
	const auto tree_dir = tree_hash_hex.substr(0, 2);
	const auto tree_name = tree_hash_hex.substr(2);

    // write tree file
    std::filesystem::path tree_dirPath = std::filesystem::path(&quot;.git/objects&quot;) / tree_dir;
    std::filesystem::create_directory(tree_dirPath);
    std::ofstream treeFile(tree_dirPath / tree_name);

    if (treeFile.is_open())
    {
        treeFile &amp;lt;&amp;lt; compressed_object;
        treeFile.close();
    }
    else
    {
        std::cerr &amp;lt;&amp;lt; &quot;Failed to create tree file. \n&quot;;
        return EXIT_FAILURE;
    }


    return EXIT_SUCCESS;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>주제별 모험기/Build Your Own X</category>
      <author>Kwon_Ori</author>
      <guid isPermaLink="true">https://kwon-ori.tistory.com/9</guid>
      <comments>https://kwon-ori.tistory.com/9#entry9comment</comments>
      <pubDate>Tue, 7 Jan 2025 20:00:34 +0900</pubDate>
    </item>
    <item>
      <title>[Build your own git] #4 ls-tree 명령어와  tree objects</title>
      <link>https://kwon-ori.tistory.com/7</link>
      <description>&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;git ls-tree&lt;/li&gt;
&lt;li&gt;tree objects&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;b&gt;서론&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;버전 관리를 위해 git 이 별도로 생성하고 관리하는 파일들을 git objects 라고 한다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;git objects 는 목적에 따라 몇 가지 종류가 있는데 cat-file, hash-object 에서 다뤘던 blob object 도 단일 파일을 위한 git objects 의 일종이다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;이번에 다룬 tree objects 는 파일의 디렉터리 구조 (또는 계층구조)를 위한 git objects 이며, 이 파일을 읽고 해당 트리에 소속된 폴더 및 파일의 이름을 알아낼 수 있는 git ls-tree --name-only &amp;lt;tree-hash&amp;gt; 를 구현해보았다.&lt;/p&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;b&gt;tree objects&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;이전에 다뤘던 blob 은 단일 파일에 대한 오브젝트라 해당 파일의 컨텐츠 정보만 있을뿐 디렉토리 구조에 대한 정보는 전혀 알 수 없었다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;실제로 다음과 같이 내용이 동일하지만 이름이나 위치만 다른 파일들을 git hash-object 로 해싱해보면 모두 동일한 해시값이 나오는 것을 확인할 수 있었다. 그리고 생성된 blob object 파일도 하나 뿐이었으며 모두 이 파일을 공유하고 있었다.&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;166&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBIH1g/btsLEGVInLH/ZHuBk7YijKlhVmxI5VE2lK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBIH1g/btsLEGVInLH/ZHuBk7YijKlhVmxI5VE2lK/img.png&quot; data-alt=&quot;내용이 동일하면 해시도 동일하다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBIH1g/btsLEGVInLH/ZHuBk7YijKlhVmxI5VE2lK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBIH1g%2FbtsLEGVInLH%2FZHuBk7YijKlhVmxI5VE2lK%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;321&quot; height=&quot;166&quot; data-origin-width=&quot;321&quot; data-origin-height=&quot;166&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 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;만약 blob 만 존재하여 계층 구조나 파일 이름을 구분할 정보가 없다면 동일한 내용의 파일은 동일한 해시 값을 가지게 되고, 변경 사항을 추적하기 매우 곤란해질 것이다. 이를 위해 사용하는 것이 tree objects 이고, tree objects 의 내용은 다음과 같은 형태를 띈다.&lt;/p&gt;
&lt;pre id=&quot;code_1736134865219&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(실제 파일은 압축되어 직접 읽을 수 없다)

tree &amp;lt;size&amp;gt;\0
&amp;lt;mode&amp;gt; &amp;lt;name&amp;gt;\0&amp;lt;20_byte_sha&amp;gt;
&amp;lt;mode&amp;gt; &amp;lt;name&amp;gt;\0&amp;lt;20_byte_sha&amp;gt;
(...)&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;- tree : blob 처럼 해당 오브젝트가 tree 타입인 것을 나타내기 위한 프리픽스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- size : 해당 트리의 사이즈&lt;/p&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;lt;mode&amp;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;mode : 파일 형식에 따른 값 ( ex. 실행 파일 = 100755, 디렉터리 = 40000, 텍스트 = 100644 )&lt;/li&gt;
&lt;li&gt;name : 파일(또는 폴더)의 이름&lt;/li&gt;
&lt;li&gt;\0 : 컨텐츠와 구분하기 위한 값&lt;/li&gt;
&lt;li&gt;&amp;lt;20_byte_sha&amp;gt; : 해시 값이긴 하지만 16진수 스트링이 아니라 20바이트 정보가 날것으로 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 구현&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ls-tree 명령어, 그 중에서 파일/폴더의 이름만 나열해주는 --name-only 옵션을 위해 필요한 구현을 생각해보았다.&lt;/p&gt;
&lt;pre id=&quot;code_1736135604094&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git ls-tree --name-only &amp;lt;tree hash&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;1) 입력 받은 tree 해시를 사용하여 파일을 찾는다. blob 과 마찬가지로 폴더 이름(2) + 파일 이름(38) 로 구성되어 있다. 아직 tree 의 해시를 만드는 기능을 구현하지 않았으므로 tree 의 해시값은 해당 폴더 위치로 이동 후 git write-tree 명령어를 호출하여 확인하였다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;2) 파일을 읽고 압축을 해제한다. 압축을 해제한 파일을 출력해보면 다음과 같이 영문 모를 문자도 포함되어 있는데, 이는 위에서 언급된 &amp;lt;20_byte_sha&amp;gt; 데이터이다. 처음엔 당연히 해시 값이니까 스트링일 것이라 착각했다가 아래 내용을 보고 당황했었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;520&quot; data-origin-height=&quot;33&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dIhwOw/btsLE4bfNm0/rKzZx7XPYCA8Z71Fv3KkH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dIhwOw/btsLE4bfNm0/rKzZx7XPYCA8Z71Fv3KkH1/img.png&quot; data-alt=&quot;20 바이트의 데이터는 스트링으로 바로 출력할 수 없었다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dIhwOw/btsLE4bfNm0/rKzZx7XPYCA8Z71Fv3KkH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdIhwOw%2FbtsLE4bfNm0%2FrKzZx7XPYCA8Z71Fv3KkH1%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;520&quot; height=&quot;33&quot; data-origin-width=&quot;520&quot; data-origin-height=&quot;33&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;20 바이트의 데이터는 스트링으로 바로 출력할 수 없었다.&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;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;3) 압축 해제된 결과에서 폴더의 이름만 추출하여 출력한다. 이름만 추출하는 방식은 구현자에 따라 천차만별인데, 나의 경우엔 string_view 를 사용하여 null 을 기준으로 헤더(tree &amp;lt;size&amp;gt;/0) 를 먼저 떼어낸 후, 처음으로 등장하는 공백과 그 다음 등장하는 null 문자 사이에 있는 것을 이름이라고 판정하여 더 이상 null 을 찾을 수 없을 때까지 반복하는 방식으로 구현했다. 다른 개발자들이 더 좋은 방법을 많이 쓰고 있어서 이것저것 참고하여 시도해보았다.&lt;/p&gt;
&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;pre id=&quot;code_1736143486927&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(중략)

else if (command == &quot;ls-tree&quot;) {
    if (argc &amp;lt; 4 || std::string(argv[2]) != &quot;--name-only&quot;) {
        std::cerr &amp;lt;&amp;lt; &quot;Invalid arguments, required --name-only &amp;lt;tree_sha&amp;gt;\n&quot;;
        return EXIT_FAILURE;
    }

    /*
          tree &amp;lt;size&amp;gt;\0
          &amp;lt;mode&amp;gt; &amp;lt;name&amp;gt;\0&amp;lt;20_byte_sha&amp;gt;
          &amp;lt;mode&amp;gt; &amp;lt;name&amp;gt;\0&amp;lt;20_byte_sha&amp;gt;

          &amp;lt;20_byte_sha&amp;gt; =&amp;gt; not hexadeciaml format
    */

    const auto treeHash = std::string_view(argv[3]);
    const auto tree_dir = treeHash.substr(0, 2);
    const auto tree_name = treeHash.substr(2);

	// 읽을 파일 경로를 만들어내고 압축을 해제하는 것까진 blob 과 동일하다.
    const auto tree_path = std::filesystem::path(&quot;.git&quot;) / &quot;objects&quot; / tree_dir / tree_name;
    auto in = std::ifstream(tree_path);
    if (!in.is_open()) {
        std::cerr &amp;lt;&amp;lt; &quot;Failed to open &quot; &amp;lt;&amp;lt; tree_path &amp;lt;&amp;lt; &quot; file.\n&quot;;
        return EXIT_FAILURE;
    }

    const std::string tree_data = std::string(std::istreambuf_iterator&amp;lt;char&amp;gt;(in), std::istreambuf_iterator&amp;lt;char&amp;gt;());

    std::string decompressed = std::string();
    if (auto res = decompressData(decompressed, tree_data); res == EXIT_FAILURE) {
        return EXIT_FAILURE;
    }

    // tree &amp;lt;size&amp;gt;
    auto firstnull = decompressed.find('\0');

    // &amp;lt;mode&amp;gt; &amp;lt;name&amp;gt;\0&amp;lt;20_byte_sha&amp;gt;
    // 처음 등장하는 null 바로 다음부터 시작하여 헤더를 건너뛴다
    auto start_position = firstnull + 1;
    auto entry = std::string_view(decompressed);

    // 더 이상 다음 지점을 찾을 수 없을 때까지 반복한다
    while (start_position &amp;lt; entry.length()) {
        entry = entry.substr(start_position);
        
        // &amp;lt;mode&amp;gt; &amp;lt;name&amp;gt;\0&amp;lt;20_byte_sha&amp;gt;
        // 공백과 \0 사이의 값을 이름이라고 판정하여 추출한다.
        auto pos_space = entry.find(' ');
        auto pos_null = entry.find('\0');
        auto nameLen = pos_null - (pos_space + 1);
        auto fileName = entry.substr(pos_space + 1, nameLen);
        std::cout &amp;lt;&amp;lt; fileName &amp;lt;&amp;lt; '\n';

        // next pos
        // null 이 나온 지점에서 + 20바이트 + 1 이 다음 시작할 문자열의 시작 인덱스이다.
        start_position = pos_null + 21;
    }
}&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;b&gt;참고자료&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;이 글에는 동작 원리나 구현에 대한 것만 작성했기 때문에 blob 을 인덱싱하여 write-tree 를 호출했을 때 트리 정보에 포함되도록 하는 방법 등 구체적인 테스트 방법은 빠져있다. 이와 관련하여 자세한 내용은 Git Objects 공식 문서에서 확인할 수 있다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;파일 하나하나 명령어를 호출하기 싫다면 github desktop 프로그램을 사용하여 커밋 한 번 해주면 필요한 정보를 쭉 생성해주므로 이를 이용하여 트리 정보를 읽는 테스트를 해보면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://git-scm.com/book/en/v2/Git-Internals-Git-Objects#_tree_objects&quot;&gt;https://git-scm.com/book/en/v2/Git-Internals-Git-Objects#_tree_objects&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1736141015852&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;Git - Git Objects&quot; data-og-description=&quot;You will get a different hash value because of different creation time and author data. Moreover, while in principle any commit object can be reproduced precisely given that data, historical details of this book&amp;rsquo;s construction mean that the printed commi&quot; data-og-host=&quot;git-scm.com&quot; data-og-source-url=&quot;https://git-scm.com/book/en/v2/Git-Internals-Git-Objects#_tree_objects&quot; data-og-url=&quot;https://git-scm.com/book/en/v2/Git-Internals-Git-Objects#_tree_objects&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/yQHr8/hyXWyxIvaf/AogxT4KP9PKLJ0t4Ea9Pk1/img.png?width=1437&amp;amp;height=1068&amp;amp;face=0_0_1437_1068,https://scrap.kakaocdn.net/dn/BnnGw/hyXWxliHaQ/Zs6APkfVnIiUaQ1G281tn0/img.png?width=1032&amp;amp;height=765&amp;amp;face=0_0_1032_765,https://scrap.kakaocdn.net/dn/cUfmrB/hyXWBH0ajX/7cWXm8KPoh601eKwmzw8kK/img.png?width=1032&amp;amp;height=765&amp;amp;face=0_0_1032_765&quot;&gt;&lt;a href=&quot;https://git-scm.com/book/en/v2/Git-Internals-Git-Objects#_tree_objects&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://git-scm.com/book/en/v2/Git-Internals-Git-Objects#_tree_objects&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/yQHr8/hyXWyxIvaf/AogxT4KP9PKLJ0t4Ea9Pk1/img.png?width=1437&amp;amp;height=1068&amp;amp;face=0_0_1437_1068,https://scrap.kakaocdn.net/dn/BnnGw/hyXWxliHaQ/Zs6APkfVnIiUaQ1G281tn0/img.png?width=1032&amp;amp;height=765&amp;amp;face=0_0_1032_765,https://scrap.kakaocdn.net/dn/cUfmrB/hyXWBH0ajX/7cWXm8KPoh601eKwmzw8kK/img.png?width=1032&amp;amp;height=765&amp;amp;face=0_0_1032_765');&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;Git - Git Objects&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;You will get a different hash value because of different creation time and author data. Moreover, while in principle any commit object can be reproduced precisely given that data, historical details of this book&amp;rsquo;s construction mean that the printed commi&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;git-scm.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222222; text-align: start;&quot;&gt;(다음 글에 계속)&lt;/span&gt;&lt;/p&gt;</description>
      <category>주제별 모험기/Build Your Own X</category>
      <author>Kwon_Ori</author>
      <guid isPermaLink="true">https://kwon-ori.tistory.com/7</guid>
      <comments>https://kwon-ori.tistory.com/7#entry7comment</comments>
      <pubDate>Tue, 7 Jan 2025 00:00:54 +0900</pubDate>
    </item>
    <item>
      <title>[Build your own git] #3 git hash-object 명령어 만들기</title>
      <link>https://kwon-ori.tistory.com/6</link>
      <description>&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;git hash-object&lt;/li&gt;
&lt;li&gt;blob object&lt;/li&gt;
&lt;li&gt;openssl&lt;/li&gt;
&lt;li&gt;SHA-1 hash&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;서론&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;지난번엔 이미 만들어진 blob object 를 cat-file -p &amp;lt;hash&amp;gt; 을 통해 읽는 기능을 구현해보았으니 이번엔 파일을 blob object 를 생성하는 기능에 대해 알아볼 차례다. 대부분의 개념들은 cat-file 에서 알아봤던 것과 겹치기 때문에 추가로 이해가 필요한 부분은 없었고, 명령어의 동작에 대해서만 조사했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;git 에서는 hash-object 라는 명령어로 원본 파일을 blob object 로 생성하는 기능을 제공한다.&lt;/p&gt;
&lt;pre id=&quot;code_1736129197195&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git hash-object -w &amp;lt;파일경로&amp;gt;&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;이 명령어를 호출하면 .git/objects 폴더 안에 해시 앞의 2자리는 폴더, 38자리는 파일 이름인 형태로 blob object 가 생성된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;93&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c4oSa5/btsLC5Ww1ar/zWs5JqTbK2KEPYCFTrxrck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c4oSa5/btsLC5Ww1ar/zWs5JqTbK2KEPYCFTrxrck/img.png&quot; data-alt=&quot;blob object 의 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c4oSa5/btsLC5Ww1ar/zWs5JqTbK2KEPYCFTrxrck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc4oSa5%2FbtsLC5Ww1ar%2FzWs5JqTbK2KEPYCFTrxrck%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;93&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;93&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;blob object 의 예시&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;hash-object 에 필요한 구현&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;조립은 분해의 역순이듯 cat-file 을 통해 blob object 를 읽는 행위를 역순으로 따라가니 구현 방법을 쉽게 떠올릴 수 있었다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;(실제 코드로 만드는게 쉽다고 하진 않았다)&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;기억을 되살려 cat-file 의 순서를 떠올려보면 [해시 값을 통해 폴더 위치 찾기] &amp;gt;&amp;gt; [압축 풀기] &amp;gt;&amp;gt; [압축 해제된 blob 파일에서 원본 컨텐츠 추출하기] 였다. 이를 반대로 뒤집고, 해싱하는 과정만 추가하면 hash-object 명령어가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;1)&amp;nbsp; 원본 컨텐츠를 blob 파일 형식으로 작성하기&lt;/p&gt;
&lt;pre id=&quot;code_1736129768285&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;blob &amp;lt;size&amp;gt;\0&amp;lt;content&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;2) SHA-1 으로 1)에서 만들어진 내용을 해싱하고 기억해두기 (&lt;u&gt;압축 전의 내용을 해싱해야 함&lt;/u&gt;)&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;3) 파일 내용을 zlib 으로 압축하기&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;4) 해시 값을 통해 앞의 2자리는 폴더, 나머지 38자리는 파일 이름으로 하여 blob-object 파일을 생성하기&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;코드&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;다음은 hash-object 구현 코드의 일부다. 해싱을 제외하면 cat-file 의 코드의 순서만 바꾼 정도이다. SHA-1 해싱에는 openssl 이라는 라이브러리를 사용했다.&lt;/p&gt;
&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;CMakeLists.txt&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1736130438228&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(종속성 설정만 보여줌)
find_package(ZLIB REQUIRED)
find_package(OpenSSL REQUIRED)

target_link_libraries(git PRIVATE ZLIB::ZLIB)
target_link_libraries(git PRIVATE OpenSSL::SSL)
target_link_libraries(git PRIVATE OpenSSL::Crypto)&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;b&gt;vcpkg.json&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1736130466418&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;dependencies&quot;: [
    &quot;zlib&quot;,
    &quot;openssl&quot;
  ]
}&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;b&gt;main.cpp&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;pre id=&quot;code_1736130646671&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;zlib.h&amp;gt;        // 압축에 사용된 헤더파일
#include &amp;lt;openssl/sha.h&amp;gt; // sha-1 해싱에 사용된 헤더파일

// 해싱된 바이트를 16진수 텍스트 라인으로 변경하는 함수
std::string hexStr(const uint8_t *data, int len) {
	std::stringstream ss;
	ss &amp;lt;&amp;lt; std::hex;

	for (int i = 0; i &amp;lt; len; ++i) {
		ss &amp;lt;&amp;lt; std::setw(2) &amp;lt;&amp;lt; std::setfill('0') &amp;lt;&amp;lt; (int)data[i];
	}

	return ss.str();
}

// 텍스트를 SHA-1 으로 해싱하고, 16진수로 표현된 문자열로 반환하는 함수
std::string hasing(std::string&amp;amp; src) {
	int dataLen = src.size();
	SHA_CTX shactx;
	uint8_t digest[SHA_DIGEST_LENGTH];

	SHA1_Init(&amp;amp;shactx);
	SHA1_Update(&amp;amp;shactx, (uint8_t*)src.data(), dataLen);
	SHA1_Final(digest, &amp;amp;shactx);

	return hexStr(digest, SHA_DIGEST_LENGTH);
}

(중략)

else if (command == &quot;hash-object&quot;) {
    // git hash-object -w &amp;lt;file&amp;gt;
    if (argc &amp;lt; 4 || std::string(argv[2]) != &quot;-w&quot;) {
        std::cerr &amp;lt;&amp;lt; &quot;Invalid arguments, required -w &amp;lt;file_path&amp;gt;\n&quot;;
        return EXIT_FAILURE;
    }

    std::ifstream in = std::ifstream(std::filesystem::path(argv[3]));
    if (!in.is_open()) {
        std::cerr &amp;lt;&amp;lt; &quot;Failed to open &quot; &amp;lt;&amp;lt; argv[3] &amp;lt;&amp;lt; '\n';
        return EXIT_FAILURE;
    }

    // blob &amp;lt;size&amp;gt;\0&amp;lt;content&amp;gt;
    const std::string content_data = std::string(std::istreambuf_iterator&amp;lt;char&amp;gt;(in), std::istreambuf_iterator&amp;lt;char&amp;gt;());
    std::string blob_data = &quot;blob &quot; + std::to_string(content_data.size()) + '\0' + content_data;

    // compress blob data
    std::string compreesedBlobData = std::string();
    if (auto res = compressData(compreesedBlobData, blob_data); res == EXIT_FAILURE) {
        return EXIT_FAILURE;
    }

    // hashing blob data
    std::string blobHashString = hasing(blob_data);
    const std::string_view blob_hash = std::string_view(blobHashString);
    std::cout &amp;lt;&amp;lt; blob_hash;

    // save to objects folder
    const std::string_view blob_directory = blob_hash.substr(0, 2);
    const std::string_view blob_name = blob_hash.substr(2);
    std::filesystem::path blob_dirPath = std::filesystem::path(&quot;.git/objects&quot;) / blob_directory;
    std::filesystem::create_directory(blob_dirPath);
    std::ofstream blobFile(blob_dirPath / blob_name);

    if (blobFile.is_open()) {
        blobFile &amp;lt;&amp;lt; compreesedBlobData;
        blobFile.close();
    }
    else {
        std::cerr &amp;lt;&amp;lt; &quot;Fained to create blob file. \n&quot;;
        return EXIT_FAILURE;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(다음 글에 계속)&lt;/p&gt;</description>
      <category>주제별 모험기/Build Your Own X</category>
      <author>Kwon_Ori</author>
      <guid isPermaLink="true">https://kwon-ori.tistory.com/6</guid>
      <comments>https://kwon-ori.tistory.com/6#entry6comment</comments>
      <pubDate>Mon, 6 Jan 2025 23:58:13 +0900</pubDate>
    </item>
    <item>
      <title>[vcpkg] mac os 환경에서 BUILD_FAILED 가 발생할 때</title>
      <link>https://kwon-ori.tistory.com/5</link>
      <description>&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;&lt;u&gt;error:&amp;nbsp;building&amp;nbsp;openssl:arm64-osx&amp;nbsp;failed&amp;nbsp;with:&amp;nbsp;BUILD_FAILED&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(가능성은 매우 낮지만) 내가 M2를 쓰고 있어서 환경에 맞는 패키지가 없어서 그런가 싶었는데, pkg-config 를 설치하지 않아 라이브러리 빌드에 필요한 정보가 누락되어 발생한 문제였다.&lt;/p&gt;
&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;home brew 를 쓰고 있다면 아래 한 줄만 입력하면 해결된다.&lt;/p&gt;
&lt;pre id=&quot;code_1735893449949&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;brew install pkg-config&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;b&gt;출처&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/microsoft/vcpkg/issues/31312&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/microsoft/vcpkg/issues/31312&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1735892623407&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;error: building openssl:arm64-osx failed with: BUILD_FAILED &amp;middot; Issue #31312 &amp;middot; microsoft/vcpkg&quot; data-og-description=&quot;Operating system Mac OS Compiler No response Steps to reproduce the behavior ./vcpkg/vcpkg install cpprestsdk Failure logs Computing installation plan... The following packages will be built and in...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/microsoft/vcpkg/issues/31312&quot; data-og-url=&quot;https://github.com/microsoft/vcpkg/issues/31312&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/w6nte/hyXWBN48Xv/700wiNOUlZBWytjZzLb1d1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bjV2Ed/hyXSrGkx0T/FG3sruvSAigMBqvteXgIU0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/microsoft/vcpkg/issues/31312&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/microsoft/vcpkg/issues/31312&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/w6nte/hyXWBN48Xv/700wiNOUlZBWytjZzLb1d1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bjV2Ed/hyXSrGkx0T/FG3sruvSAigMBqvteXgIU0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;error: building openssl:arm64-osx failed with: BUILD_FAILED &amp;middot; Issue #31312 &amp;middot; microsoft/vcpkg&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Operating system Mac OS Compiler No response Steps to reproduce the behavior ./vcpkg/vcpkg install cpprestsdk Failure logs Computing installation plan... The following packages will be built and in...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>오리 도서관/(미분류) 삽질 방지 개발 지식</category>
      <category>vcpkg</category>
      <author>Kwon_Ori</author>
      <guid isPermaLink="true">https://kwon-ori.tistory.com/5</guid>
      <comments>https://kwon-ori.tistory.com/5#entry5comment</comments>
      <pubDate>Fri, 3 Jan 2025 23:40:25 +0900</pubDate>
    </item>
    <item>
      <title>[vcpkg] 특정 버전의 패키지를 받고 싶을 때</title>
      <link>https://kwon-ori.tistory.com/4</link>
      <description>&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;서론&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;사내 네트워크 환경에서 vcpkg 를 사용하여 특정 프로젝트에 openssl 을 사용하도록&amp;nbsp; port 를 추가했는데 계속 실패가 발생했다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;외부 환경인 집에서는 별탈 없이 되는 것으로 보아 규칙이 불분명하기로 악명 높은 차단 규칙에 걸린 것 같았다. 혹시 몰라 특정 프로젝트가 아닌 vcpkg 루트에서 받도록 하니 놀랍게도 성공해버렸다. (왜 되는거야?)&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;뭐가 달랐는지 확인 해보니 다운로드 실패가 발생했던 것은 3.3.0 버전이고, 성공을 했던 것은 3.4.0 버전이었다. 이전에 인프라팀과 이야기했을 때를 떠올려보면 저가의(...) 솔루션을 사용하면서 본인들도 지정하지 않은 영문 모를 사유로 3.3.0 버전이 차단된&amp;nbsp; 것 같다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;같은 환경인데 어째서 특정 프로젝트에서 받을 땐 3.3.0 이고 vcpkg 본체에서 받았을 때 3.4.0 이 받아졌는지는 알 수 없었지만... 일단 성공적으로 받아진 녀석을 쓰기 위해 open ssl 버전을 특정 버전으로 고정할 필요가 있었다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;본론&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;서론이 불필요하게 길었는데, 프로젝트에만 관여하는 vcpkg.json 파일을 다음과 같이 설정해주면 특정 버전을 받을 수 있다. overrides 배열을 추가하고, dictionary 로 이름과 버전을 명시해주면 끝이다. 공식 문서에는 그 외의 방법도 많으니 이것저것 테스트 해봐야겠다.&lt;/p&gt;
&lt;pre id=&quot;code_1735828327840&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;dependencies&quot;: [
    &quot;zlib&quot;,
    &quot;openssl&quot;
  ],
  &quot;overrides&quot;:[
    {
        &quot;name&quot;: &quot;openssl&quot;,
        &quot;version&quot;: &quot;3.4.0&quot;
    }
  ]
}&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;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/vcpkg/consume/lock-package-versions?tabs=inspect-powershell&quot;&gt;https://learn.microsoft.com/en-us/vcpkg/consume/lock-package-versions?tabs=inspect-powershell&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1735826717646&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;Tutorial: Install a specific version of a package&quot; data-og-description=&quot;Learn to install specific versions of your dependencies using vcpkg in your projects.&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://learn.microsoft.com/en-us/vcpkg/consume/lock-package-versions?tabs=inspect-powershell&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/vcpkg/consume/lock-package-versions&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/FjQdX/hyXWAVPqOm/vk5lym36fYXlKpkOLHJb51/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/vcpkg/consume/lock-package-versions?tabs=inspect-powershell&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://learn.microsoft.com/en-us/vcpkg/consume/lock-package-versions?tabs=inspect-powershell&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/FjQdX/hyXWAVPqOm/vk5lym36fYXlKpkOLHJb51/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&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;Tutorial: Install a specific version of a package&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn to install specific versions of your dependencies using vcpkg in your projects.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.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;</description>
      <category>오리 도서관/(미분류) 삽질 방지 개발 지식</category>
      <category>vcpkg</category>
      <author>Kwon_Ori</author>
      <guid isPermaLink="true">https://kwon-ori.tistory.com/4</guid>
      <comments>https://kwon-ori.tistory.com/4#entry4comment</comments>
      <pubDate>Thu, 2 Jan 2025 23:33:48 +0900</pubDate>
    </item>
    <item>
      <title>[Build your own git] #2 cat-file 명령어와 Git Object</title>
      <link>https://kwon-ori.tistory.com/3</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&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;Git Object&lt;/li&gt;
&lt;li&gt;Blob&lt;/li&gt;
&lt;li&gt;cat-file&lt;/li&gt;
&lt;li&gt;zlib&lt;/li&gt;
&lt;li&gt;std::string_view&lt;/li&gt;
&lt;li&gt;std::istreambuf_iterator&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;b&gt;서론&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째로 진행한 기능은 git cat-file 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cat-file 을 이해하기에 앞서 git 이 파일 정보를 어떻게 저장하는지 간략하게 숙지할 필요가 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;git은 버전 관리를 위해 파일을 압축된 형식의 blob object 파일로 만들고&amp;nbsp; key-value 방식으로 관리한다. 여기서 key 는 blob object 파일의 내용을 sha1 알고리즘으로 해싱한 값이다. 이 값을 사용하면 해당 파일의 정보를 빠르게 획득할 수 있다. 이번에 작성한 git cat-file 이라는 명령어는 바로 sha1 해시를 사용하여 해당 키와 연결된 파일의 내용을 읽어들이기 위한 명령어이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;git의 파일 구조를 살펴보며 정말 재미있다고 느껴진 부분이 있었는데. 실제 파일 정보가 압축된 형태로 만들어진 blob object 파일을 저장할 때, key 의 앞 두글자를 폴더이름으로 사용하고 나머지 38글자를 git 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;어딘가 낯설지 않다고 느껴져 유니티 엔진의 라이브러리 폴더 내의 Artifacts 폴더를 살펴보니 유사한 방식으로 동작하고 있었다. (&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;유니티의 경우 파일 이름도 key 와 동일하게 쓰는게 차이점이었다.)&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Blob Object&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Blob&lt;span&gt;&amp;nbsp;&lt;/span&gt; Object 는 git 에 의해 관리되는 파일의 정보가 담겨있는데, 다음과 같은 형식으로 구성되어 있으며 최종적으로는 zlib 라는 압축 라이브러리를 통해 압축된 형태로 보관된다.&lt;/p&gt;
&lt;pre id=&quot;code_1735637846948&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;blob &amp;lt;size&amp;gt;\0&amp;lt;content&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- blob : 블롭 오브젝트임을 나타내는 프리픽스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &amp;lt;size&amp;gt; : 컨텐츠의 사이즈&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- \0 : null 문자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &amp;lt;content&amp;gt; : 실제 파일 내용&amp;nbsp;&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;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;cat-file 에 필요한 구현&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) 입력 받은 해시값에서 폴더와 blob object 파일의 이름을 추출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) 1)에서 추출한 경로 정보에 있는 파일을 읽고 zlib 라이브러리로 압축을 해제한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3) 압축을 해제한 결과에서 \0(null) 문자를 기준으로 그 이후에 있는 컨텐츠 부분을 출력한다.&lt;/p&gt;
&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;우선, 개발 환경이 vcpkg 와 cmake 로 되어 있어서 내가 커밋을 했을 때 codecrafters 에 있는 자동화 시스템도 동일한 zlib 라이브러리를 가지고 빌드를 해야하기 때문에 이걸 어떻게 설정하면 될지 삽질을 했고, 그 다음은 이 zlib 를 어떻게 사용해야 할지 삽질을 했다.&lt;/p&gt;
&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&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;1) zlib 를 이용한 압축 해제 함수&lt;/p&gt;
&lt;pre id=&quot;code_1735825355978&quot; class=&quot;cpp&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;int decompressData(std::string&amp;amp; des, const std::string&amp;amp; src) {
	des.resize(src.size());

	while (true){
    	// (압축된)데이터의 길이를 초기값으로 사용한다
		uLong len = des.size();
        
        // 버퍼 사이즈가 부족한 문제가 있다면 2배로 리사이징하여 재시도한다.
        // (압축을 풀었을 때의 크기를 예측할 수 없기 때문)
		if (auto res = uncompress((uint8_t*)des.data(), &amp;amp;len, (const uint8_t*)src.data(), src.size()); res == Z_BUF_ERROR) {
			des.resize(des.size() * 2);
		}
		else if (res != Z_OK) {
			std::cerr &amp;lt;&amp;lt; &quot;Failed to uncompress Zlib. (code: &quot; &amp;lt;&amp;lt; res &amp;lt;&amp;lt; &quot;)\n&quot;;
			return EXIT_FAILURE;
		}
		else {
            // 시도한 끝에 압축 해제에 성공했다면 실제 압축 해제된 길이만큼 리사이징한다.
			des.resize(len);
			break;
		}
	}

	return EXIT_SUCCESS;
}&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;2) cat-file -p 구현부&lt;/p&gt;
&lt;pre id=&quot;code_1735826069907&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 0   1        2  3
// git cat-file -p &amp;lt;blob_sha&amp;gt;
if (argc &amp;lt; 4 || std::string(argv[2]) != &quot;-p&quot;) {
    std::cerr &amp;lt;&amp;lt; &quot;Invalid arguments, required -p &amp;lt;blob_sha&amp;gt;\n&quot;;
    return EXIT_FAILURE;
}

// blob 의 sha 해시값을 string_view 에 저장해서 필요한 부분을 잘라서 사용한다 (C++17)
// string_view 는 문자열을 복사하지 않고 참조를 통해 동작하는 읽기 전용 클래스인데, 다른 포스팅에서 다뤄야겠다
const std::string_view blob_hash = std::string_view(argv[3], 40);

// 앞의 두 글자는 폴더명
const std::string_view blob_dir = blob_hash.substr(0, 2);

// 해시의 나머지 부분(38바이트)는 파일명
const std::string_view blob_name = blob_hash.substr(2);

// std::filesystem::path 는 / 가 연산자 오버로딩이 되어 있어서 아래와 같이 편리하게 경로를 합칠 수 있다.
const auto blob_path = std::filesystem::path(&quot;.git&quot;) / &quot;objects&quot; / blob_dir / blob_name;
auto in = std::ifstream(blob_path);
if (!in.is_open()) {
    std::cerr &amp;lt;&amp;lt; &quot;Failed to open &quot; &amp;lt;&amp;lt; blob_path &amp;lt;&amp;lt; &quot; file.\n&quot;;
    return EXIT_FAILURE;
}

// std::istreambuf_iterator 라는 이터레이터로 파일의 컨텐츠를 스트링 개체에 집어넣을 수 있다.
const std::string blob_data = std::string(std::istreambuf_iterator&amp;lt;char&amp;gt;(in), std::istreambuf_iterator&amp;lt;char&amp;gt;());

// 해시를 통해 파일 경로를 찾아서 읽어들인 값을 압축 해제한다
std::string decompressed = std::string();
if (auto res = decompressData(decompressed, blob_data); res == EXIT_FAILURE) {
    return EXIT_FAILURE;
}

// 압축 해제한 값의 형식은 blob &amp;lt;size&amp;gt;\0&amp;lt;content&amp;gt; 이므로,
// 내용을 읽기 위해 \0 의 위치를 찾아 그 다음에 등장하는 내용을 출력하면 된다.
std::cout &amp;lt;&amp;lt; std::string_view(decompressed).substr(decompressed.find('\0') + 1);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(다음 글에 계속)&lt;/p&gt;</description>
      <category>주제별 모험기/Build Your Own X</category>
      <author>Kwon_Ori</author>
      <guid isPermaLink="true">https://kwon-ori.tistory.com/3</guid>
      <comments>https://kwon-ori.tistory.com/3#entry3comment</comments>
      <pubDate>Thu, 2 Jan 2025 23:01:11 +0900</pubDate>
    </item>
    <item>
      <title>[Build your own git] #1 git init 과 .git 폴더의 구조</title>
      <link>https://kwon-ori.tistory.com/2</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;가장 먼저 구현한 기능은 'git init' 이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://git-scm.com/docs/git-init&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://git-scm.com/docs/git-init&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1735632989051&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;Git - git-init Documentation&quot; data-og-description=&quot;-q --quiet Only print error and warning messages; all other output will be suppressed. --bare Create a bare repository. If GIT_DIR environment is not set, it is set to the current working directory. --object-format= Specify the given object (hash algorithm&quot; data-og-host=&quot;git-scm.com&quot; data-og-source-url=&quot;https://git-scm.com/docs/git-init&quot; data-og-url=&quot;https://git-scm.com/docs/git-init&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://git-scm.com/docs/git-init&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://git-scm.com/docs/git-init&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;Git - git-init Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;-q --quiet Only print error and warning messages; all other output will be suppressed. --bare Create a bare repository. If GIT_DIR environment is not set, it is set to the current working directory. --object-format= Specify the given object (hash algorithm&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;git-scm.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;git init 은 git 에 사용할 로컬 저장소를 만드는 기능으로 특정 위치에 .git 이라는 폴더를 생성하여 해당 위치가 git 저장소로 활용되도록 한다. .git 폴더에는 버전 관리를 위한 폴더 및 파일들이 포함되어 있는데, 대표적으로 objects 폴더, refs 폴더, HEAD 파일이 있다.&lt;/p&gt;
&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;objects 폴더&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버전 관리되는 파일들의 정보가 담겨있는 Git Object 파일들이 저장되는 폴더이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Git Object 는 blob, tree 등 용도에 따른 종류가 있던데, 차차 진행해나가면서 하나씩 정리할 것이다.&lt;/p&gt;
&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;refs 폴더&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외우기 힘든 SHA-1 값을 외우기 쉬운 이름으로 된 포인터로 연결한 것을 Git Refs 라고 하는데, 쉬운 이름과 연결된 SHA-1 값을 저장하는 폴더이다.&lt;/p&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; 특정 커밋을 구분하는 해시값인 1a410efbd13591db07496601ebc7a059dd55cfe9 대신 .git/refs/&lt;b&gt;heads/master &lt;/b&gt;에 해시값을 저장하여 head/master 라는 이름만으로 해당 커밋을 찾을 수 있도록 만들어준다. 실제로 해당 경로에서 master 파일을 열어보면 40자의 SHA-1 해시값이 기록되어 있다.&lt;/p&gt;
&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;HEAD 파일&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Git 이 마지막 커밋의 SHA-1 을 값을 알기 위한 정보가 담겨있는 파일이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 refs 와 다르게 간접(symbolic) refs 라서 직접 특정한 SHA-1 값을 가리키는게 아닌 다른 Refs 를 가리키므로 고유한 SHA-1 값이 존재하지 않는다. 이 파일을 열어보면 다음과 같이 마스터 브랜치의 마지막 커밋을 가리키는 Refs 가 명시되어 있으며 기본적으로는 다음과 같이 마스터의 마지막 커밋의 refs를 가리키고 있다. (이 값은 git init 구현에 사용될 예정)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ref: refs/head/master&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구현할 기능은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) .git 폴더를 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) 그외 필요한 폴더(objects, refs) 를 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3) .git 폴더 안에 HEAD 파일을 생성하고 마스터의 헤드를 가리키는 간접 레퍼런스를 기록한다.&lt;/p&gt;
&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;codecrafters 에서는 처음에 이걸 굳이 고민하면서 구현할 가치가 없다고 생각했는지, 초기 코드에서 주석만 해제하도록 만들어뒀다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 C++ 로 작성된 코드이며, codecrafters 서비스 특성상 bash, cmake, vckpkg 를 사용하여 빌드와 테스트를 하므로&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;pre id=&quot;code_1735636516850&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    std::string command = argv[1];
    
    if (command == &quot;init&quot;) {
        try {
            std::filesystem::create_directory(&quot;.git&quot;);
            std::filesystem::create_directory(&quot;.git/objects&quot;);
            std::filesystem::create_directory(&quot;.git/refs&quot;);
    
            std::ofstream headFile(&quot;.git/HEAD&quot;);
            if (headFile.is_open()) {
                headFile &amp;lt;&amp;lt; &quot;ref: refs/heads/main\n&quot;;
                headFile.close();
            } else {
                std::cerr &amp;lt;&amp;lt; &quot;Failed to create .git/HEAD file.\n&quot;;
                return EXIT_FAILURE;
            }
    
            std::cout &amp;lt;&amp;lt; &quot;Initialized git directory\n&quot;;
        } catch (const std::filesystem::filesystem_error&amp;amp; e) {
            std::cerr &amp;lt;&amp;lt; e.what() &amp;lt;&amp;lt; '\n';
            return EXIT_FAILURE;
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222222; text-align: start;&quot;&gt;(다음 글에 계속)&lt;/span&gt;&lt;/p&gt;</description>
      <category>주제별 모험기/Build Your Own X</category>
      <author>Kwon_Ori</author>
      <guid isPermaLink="true">https://kwon-ori.tistory.com/2</guid>
      <comments>https://kwon-ori.tistory.com/2#entry2comment</comments>
      <pubDate>Wed, 1 Jan 2025 13:00:49 +0900</pubDate>
    </item>
    <item>
      <title>[Build your own git] #0 시작</title>
      <link>https://kwon-ori.tistory.com/1</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;프로그래밍 실력을 어떻게 향상 시킬 수 있을지 고민하던 중 Build your own X 라는 챌린지에 대해 알게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/codecrafters-io/build-your-own-x&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/codecrafters-io/build-your-own-x&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1735032555114&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - codecrafters-io/build-your-own-x: Master programming by recreating your favorite technologies from scratch.&quot; data-og-description=&quot;Master programming by recreating your favorite technologies from scratch. - codecrafters-io/build-your-own-x&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/codecrafters-io/build-your-own-x&quot; data-og-url=&quot;https://github.com/codecrafters-io/build-your-own-x&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/xmB9b/hyXSrqWLwy/HHZk9M6CHma8b3CDZK4Rm0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/tWDVz/hyXSuVvWVr/2epUJywzk1Kq6DBbYRqBE0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/codecrafters-io/build-your-own-x&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/codecrafters-io/build-your-own-x&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/xmB9b/hyXSrqWLwy/HHZk9M6CHma8b3CDZK4Rm0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/tWDVz/hyXSuVvWVr/2epUJywzk1Kq6DBbYRqBE0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - codecrafters-io/build-your-own-x: Master programming by recreating your favorite technologies from scratch.&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Master programming by recreating your favorite technologies from scratch. - codecrafters-io/build-your-own-x&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Build your own&lt;span&gt; git, &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Build your own&lt;span&gt; Redis 등 기존에 널리 쓰이는 프로그램들을 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&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;&lt;span&gt;이를 직접 구현해보는 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&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;&lt;span&gt;일종의 클론코딩과 비슷한 무언가인데, 평소에 사용하는 프로그램들의 기반이 되는 원리를 배우고 앞서 챌린지를 진행한 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&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;&lt;span&gt;고수들의 코드를 많이 볼 수 있는 좋은 기회라고 생각하여 시작해보았다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&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;Build your own X&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt; 로 만들어볼 &lt;/span&gt;첫번째 프로그램은 무려... &lt;b&gt;git&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; text-align: start;&quot;&gt;git 은 프로그래머라면 한 번 쯤 써봤을 만큼 널리 사용되는 버전 관리 프로그램으로 &lt;/span&gt;&lt;span style=&quot;color: #333333; 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;혼자 삽질하는 것도 좋지만 처음 접근해보는 작업이기도 하고, 스스로도 처음부터 만들 만큼 실력이 뛰어다나고 생각하지 않기 때문에 Build your own X 를 일종의 미션 형식으로 제공하는 CodeCrafters 라는 서비스를 이용하여 진행할 생각이다.&lt;/p&gt;
&lt;p data-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;&quot;&gt;CodeCrafters&lt;span&gt; &lt;/span&gt;&lt;/span&gt;는 구현에 필요한 단계를 알려줄 뿐만 아니라 자체 git 저장소, 테스트 자동화, 막혔을 경우 다른 개발자들의 훌륭한 예제 코드를 살펴볼 수 있게 해주기 때문에 나 같은 초심자(?)라면 한 번쯤 찍어먹어 봐도 좋을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로 틈틈히 Build own your X 를 진행하며, 모든 과정을 튜토리얼 처럼 따라해볼 수 있게끔 작성할...계획은 없고&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;</description>
      <category>주제별 모험기/Build Your Own X</category>
      <category>build_your_own_git</category>
      <author>Kwon_Ori</author>
      <guid isPermaLink="true">https://kwon-ori.tistory.com/1</guid>
      <comments>https://kwon-ori.tistory.com/1#entry1comment</comments>
      <pubDate>Wed, 1 Jan 2025 11:54:38 +0900</pubDate>
    </item>
  </channel>
</rss>