<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>yun_hkr.log</title>
        <link>https://velog.io/</link>
        <description>윤성킴</description>
        <lastBuildDate>Thu, 10 Oct 2024 15:29:08 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>yun_hkr.log</title>
            <url>https://velog.velcdn.com/images/yun_hkr/profile/7ef1d39e-fc98-4cbe-a12b-98d18a358d16/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. yun_hkr.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/yun_hkr" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Ethernaut - Level 2. Fallout]]></title>
            <link>https://velog.io/@yun_hkr/Ethernaut-Level-2.-Fallout</link>
            <guid>https://velog.io/@yun_hkr/Ethernaut-Level-2.-Fallout</guid>
            <pubDate>Thu, 10 Oct 2024 15:29:08 GMT</pubDate>
            <description><![CDATA[<p>이 레벨을 완료하기 위해서는, 컨트랙트의 owner가 되어야 한다.</p>
<p>문제에서는 Solidity Remix IDE가 힌트가 될 수 있을 것이라고 한다.
Remix IDE는 스마트 컨트랙트를 작성할 때 실시간으로 여러 유용한 기능, 정보를 제공한다.</p>
<hr>
<p>코드</p>
<p><img src="https://velog.velcdn.com/images/yun_hkr/post/5117f7c1-8f8d-43d4-b14f-e6e59213862f/image.png" alt=""></p>
<hr>
<p>먼저, 문제 이름이 Fallout이므로, 왜 Fallout일지(문제 내의 사진을 보면 Fal1out이다.) 생각해보자. Fallback처럼 기존에 있는 함수인지 찾아보았으나 없다.</p>
<p>일단 모르겠다.</p>
<p>그럼 코드를 살펴보자.</p>
<hr>
<p>먼저, SafeMath 라이브러리를 가져와 안전한 연산과 overflow/underflow를 방지한다.
using SafeMath for uint256;은 uint 자료형으로 수행되는 연산을 안전하게 수행할 수 있도록 한다.</p>
<p>mapping(key =&gt; value) variable;
는 매핑으로, 키-값 쌍으로 동작한다.
여기서는 allocation[owner] = msg.value에서 owner는 address, msg.value는 uint로, owner주소에 msg.value 값을 기록하는 것이다.</p>
<hr>
<h4 id="fal1out">Fal1out</h4>
<p>constructor가(자칭) 정의되어있는데, 자세히 보면 함수의 이름이 Fallout이 아니라 Fal1out이다.
즉, 문제에서도 보았듯이 훼이크를 친 것 같다.
/<em>constructor</em>/구문은 깔끔히 무시해주고, 일반 함수로 받아들이자!</p>
<p>이 함수가 실행되면 함수 호출자가 owner가 된다.
함께 전송된 이더는 allocation[owner]에 저장된다.</p>
<hr>
<h4 id="onlyowner">onlyOwner</h4>
<p>이전 문제에도 있었듯이, modifier를 사용해 require문이 참인지 확인한다. 즉, 함수 호출자 = 소유주인지 확인하여 옳지 않으면 &quot;caller is not the owner&quot; 메세지를 출력하고 트랜잭션에 실패하며, 참이라면, modifier가 적용된 함수 본문의 코드가 실행된다.(_;코드 덕분에)</p>
<hr>
<h4 id="allocate">allocate</h4>
<p>이 함수가 실행되면 함수 호출자가 보낸 이더의 값을 allocation 매핑에 추가하여 저장한다.
추가로, add함수를 사용하여 오버플로우를 방지하였다.</p>
<p>function add(uint256 a, uint256 b) internal pure returns (uint256) {
    uint256 c = a + b;
    require(c &gt;= a, &quot;SafeMath: addition overflow&quot;);
    return c;
}
이는 SafeMath 라이브러리에 있는 add함수 코드이다.</p>
<p>위 방식을 통해 오버플로우를 감지한다.
만약 위 방식을 사용하지 않았더라면 어떤 일이 발생할 지 모르며, 의도치 않은 결과가 나올 수 있다.</p>
<hr>
<h4 id="sendallocation">sendAllocation</h4>
<p>이더를 보낼 수 있는 allocator의 주소를 파라미터로 받아온다.
transfer함수는 지정된 주소에 이더를 전송하는 함수이다.
allocator에게 할당된 값이 0 이상이라면, allocator의 주소에 할당된 이더의 값이 전송된다.</p>
<hr>
<h4 id="collectallocations">collectAllocations</h4>
<p>위의 modifier에서 require문이 참인 경우(함수 호출자 = 소유주인 경우) 실행할 수 있는 함수이다. 즉, owner만 실행할 수 있다.
여기에서 this는 해당 스마트 계약의 인스턴스 주소이므로, address(this).balance는 이 스마트 계약에 저장된 모든 잔액이다. 즉, msg.sender(=owner)에게 스마트 계약의 모든 남은 금액을 전송하는 함수로, 내용만 보아도 owner만이 실행해야 한다.</p>
<hr>
<h4 id="allocatorbalance">allocatorBalance</h4>
<p>view로 선언되었으므로 단순히 정보를 볼 때만 쓰는 함수이다.(상태를 변경하지 않는 조회 함수)
allocator의 주소에 할당된 이더가 얼마인지 보여준다. 즉, 잔액 조회 용도이다.</p>
<hr>
<p>실습!</p>
<p>인스턴스를 생성한다. 그림은 생략하겠다.</p>
<p>contract.owner()로 owner주소 체크</p>
<p><img src="https://velog.velcdn.com/images/yun_hkr/post/7f170963-474f-43df-aa14-4577f20f9dba/image.png" alt=""></p>
<p>0x000000~~0000
아직 owner가 없다.
내가 owner가 되어야겠다.</p>
<p>contract.Fal1out()</p>
<p><img src="https://velog.velcdn.com/images/yun_hkr/post/ad61c8b1-fffa-4a37-8b95-70292615b6a6/image.png" alt=""></p>
<p>이제 컨펌받고 다시 owner주소를 확인해보면 내가 owner이다!
아마 이 문제에서 의도한 것은 외부인이 owner가 될 수 없도록 설계해야 하는데 생성자에 오타를 내어 일반 함수로, 그것도 owner가 되기 쉬운 매우 취약한 함수로 만들었다. 오타를 조심하라는 문제인 듯 하다.</p>
<p>생각해보니 owner가 되기 전에 다른 함수들 실행해볼걸,,(owner가 아닐 때 오류 발생하는 구문을 보고싶엇다..)</p>
<p>일단 안에 얼마있는지 확인해보자.</p>
<p><img src="https://velog.velcdn.com/images/yun_hkr/post/f0f7fe20-34a3-42d3-9728-9dfa9fc2024f/image.png" alt=""></p>
<p>Fal1out함수로 이더를 따로 전송하진 않았으므로 비어있다고 뜬다.(i라고 뜸, 이게 비었다는 뜻인가? 이것도 질문해야겠다)</p>
<p>contract.allocate({value:toWei(&quot;0.0000000000000001&quot;)})
로 1Wei를 보내보았다.</p>
<p>근데 왜 contract.allocatorBalance(player)을 하면 가진 금액이 뜨지 않는지 모르겠다. 위 사진과 동일하게 나온다. (질문 예정, 이후 수정)</p>
<p>내가 소유주이므로 컨트랙트의 모든 이더를 나에게 보내기 위해 collectAllocations()를 실행하고 컨펌받았다.</p>
<p>일단 할 것은 다 했으니 Submit instance를 하였다.</p>
<p><img src="https://velog.velcdn.com/images/yun_hkr/post/d4b14a1b-eb7b-47ee-9cbc-2d8618a97761/image.png" alt=""></p>
<p>성공!</p>
<h3 id="소감문">소감문</h3>
<p>사실 0, 1을 풀 땐 크게 느끼지 못했는데, 확실히 이렇게 정리를 하면서 각 함수를 제대로 이해하고, 모르는 것은 찾아보고, 그 후에 문제를 푸니까 술술 풀린다. (물론 초반 문제라 쉬운 것도 있다. 하지만 느낌이 달라졌다는 것이 중요!) 
문제 유형에 익숙해진 것도 있지만, 어떻게 해야할 지 조금씩 알겠다. 코드들도 아직은 익숙치 않음에도 몇 번 보니까 익숙해졌고, 정리하면서도 까먹을 수 있었던 부분들이 기억이 난다. 이제 꾸준하게만 하면 된다.. 아직 너무 쪼끔했다.</p>
<p>열공~</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[가끔씩 까먹는 것들 기억해두는 곳]]></title>
            <link>https://velog.io/@yun_hkr/%EA%B0%80%EB%81%94%EC%94%A9-%EA%B9%8C%EB%A8%B9%EC%9D%84-%EB%95%8C-%EA%B8%B0%EC%96%B5%ED%95%B4%EB%91%90%EB%8A%94-%EA%B3%B3</link>
            <guid>https://velog.io/@yun_hkr/%EA%B0%80%EB%81%94%EC%94%A9-%EA%B9%8C%EB%A8%B9%EC%9D%84-%EB%95%8C-%EA%B8%B0%EC%96%B5%ED%95%B4%EB%91%90%EB%8A%94-%EA%B3%B3</guid>
            <pubDate>Mon, 07 Oct 2024 09:39:57 GMT</pubDate>
            <description><![CDATA[<p>EOA : Externally Owned Account, 외부 소유 계정 - 개인 사용자가 보유하는 이더리움 주소. 이더스캔에서 주소를 쓰면 address라고 뜸.
CA : Contract Account, 스마트 컨트랙트가 배포된 계정. 이더스캔에서 주소를 쓰면 contract라고 뜨며, ethernaut에서 인스턴스가 생성될 때 주소가 여기에 해당
ENS : Ethereum Name Service, 0x~~의 주소 대신 yunseong.eth처럼 주소 대체 서비스.(연간 비용이 듦) NFT의 일종이다.</p>
<p>마찬가지로 생각이 날 때마다 업데이트 될 예정!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Ethernaut - Level 1. Fallback]]></title>
            <link>https://velog.io/@yun_hkr/Ethernaut-Level-1.-Fallback</link>
            <guid>https://velog.io/@yun_hkr/Ethernaut-Level-1.-Fallback</guid>
            <pubDate>Mon, 07 Oct 2024 08:35:40 GMT</pubDate>
            <description><![CDATA[<p>Level 1은 Fallback이라는 이름의 문제이다.</p>
<p>Fallback이란?
<strong>어떤 기능이 약해지거나 제대로 동작하지 않을 때, 이에 대처하는 기능 또는 동작</strong></p>
<p>스마트 컨트랙트에는 Fallback 함수가 있다.</p>
<h3 id="fallback-function">Fallback Function</h3>
<p>fallback함수는 무기명 함수이다.
직접 호출되지 않고, 특정 상황에서 제대로 동작하지 않을 때 디폴트로 fallback함수가 실행된다.</p>
<p>컨트랙트에서는 호출한 함수가 컨트랙트 내에서 조회되지 않는 경우(주소가 확인되지 않는 경우)가 있다.
또한 Ether를 보낼 때도 자동으로 실행된다.</p>
<p>Reference
<a href="https://velog.io/@octo__/Solidity-fallback-function">https://velog.io/@octo__/Solidity-fallback-function</a></p>
<hr>
<p>이 레벨을 통과하기 위해서는 다음 두 가지 조건이 필요하다.</p>
<ol>
<li>contract의 ownership를 선언한다.(내가 owner가 되고자 한다.)</li>
<li>계좌의 잔액을 0으로 만든다.</li>
</ol>
<hr>
<p>아래는 컨트랙트 코드이다.</p>
<pre><code>// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Fallback {
mapping(address =&gt; uint256) public contributions;
address public owner;

constructor() {
    owner = msg.sender;
    contributions[msg.sender] = 1000 * (1 ether);
}

modifier onlyOwner() {
    require(msg.sender == owner, &quot;caller is not the owner&quot;);
    _;
}

function contribute() public payable {
    require(msg.value &lt; 0.001 ether);
    contributions[msg.sender] += msg.value;
    if (contributions[msg.sender] &gt; contributions[owner]) {
        owner = msg.sender;
    }
}

function getContribution() public view returns (uint256) {
    return contributions[msg.sender];
}

function withdraw() public onlyOwner {
    payable(owner).transfer(address(this).balance);
}

receive() external payable {
    require(msg.value &gt; 0 &amp;&amp; contributions[msg.sender] &gt; 0);
    owner = msg.sender;
}
}</code></pre><p>이 레벨을 풀 때 도움이 될 지도 모르는 힌트들</p>
<ol>
<li>ABI와 상호작용할 때 어떻게 이더를 보낼까?</li>
<li>ABI 외부로 어떻게 이더를 보낼까?</li>
<li>help() 커맨드로 wei/ether 전환</li>
<li>Fallback 함수</li>
</ol>
<p>먼저, 코드를 살펴보자.</p>
<h4 id="constructor">constructor</h4>
<p>owner를 msg.sender로 설정, 즉 컨트랙트 배포자(트랜잭션을 보낸 사람)의 주소를 소유자로 설정했다.
contributions[msg.sender] = 1000 * (1 ether)로, 소유자의 초기 기여도(?)를 1000 ETH로 설정했다.</p>
<h4 id="onlyowner">onlyOwner</h4>
<p>modifier를 사용해 함수 호출자가 소유자인지 확인한다.
require함수로 트랜잭션을 보낸 사람의 주소가 컨트랙트의 소유자 주소와 동일한지 확인한다. 같지 않으면 &quot;caller is not the owner&quot; 메세지를 출력 후 트랜잭션을 실패하게 한다.
_;는 modifier가 적용된 함수 본문의 코드가 실행된다.
function withdraw() public onlyOwner 에서 owner만이 해당 코드(payable(owner).transfer(address(this).balance);)를 실행할 수 있다.</p>
<h4 id="contribute">contribute</h4>
<p>공개적으로 호출 가능, payable 키워드를 사용했으므로 ETH를 받을 수 있다.
함수 호출자가 보내는 이더의 양(msg.value)가 0.001 ETH보다 적어야 한다. 이보다 많으면 트랜잭션이 실패한다.
if문에서는 기여자가 보낸 이더의 총합이 현재 소유자가 보낸 이더의 양보다 클 경우, 기여자를 새 소유자(owner)로 설정한다.</p>
<p>*즉, 기여도가 가장 큰 사람 = owner</p>
<h4 id="getcontribution">getContribution</h4>
<p>공개로, view로 표시되어 데이터를 조회하는 함수이다.(상태 변경 x)
현재 호출자 주소에 대한 기여도를 반환하여 사용자는 자신의 기여도를 이 함수로 확인가능하다.</p>
<h4 id="withdraw">withdraw</h4>
<p>modifier로 설정된 onlyOwner 조건을 만족하는 경우에만 해당 함수가 실행된다. 즉, owner만 호출할 수 있다.
payable이므로 이더를 받을 수 있음을 의미하며, 이 컨트랙트가 보유한 잔액을 transfer 함수로 owner에게 모두 전송한다.
즉, 소유자가 컨트랙트의 모든 자금을 인출할 수 있는 기능이다.</p>
<h4 id="receive">receive</h4>
<p>function 키워드를 사용하지 않는 특별한 함수로, receive라는 예약된 이름을 사용해 선언한다.
스마트 컨트랙트가 직접 이더를 전송받을 때 호출된다.(자동 호출)
external이므로 외부에서만 호출 가능하며, 이더 수신이 가능하다.
함수 호출자의 기여도가 0보다 높고 사용자가 0 이상의 이더를 전송해야만 실행되며, 이더를 보낸 사용자의 주소를 owner로 변경한다.</p>
<hr>
<p>contract.owner()키워드로 주소 확인을 해보면,
0x3c99F231E92c4F0009aC726dd310Bd76d1c755bB
(이후에 바뀌므로 잠시만 기억해놓자)</p>
<p>contract.contribute({value:toWei(&quot;0.0000000000000001&quot;)})
1Wei를 보내보았다.</p>
<p><img src="https://velog.velcdn.com/images/yun_hkr/post/b13998c2-b748-485f-bac6-3d51731315b5/image.png" alt=""></p>
<p>를 해보니
인스턴스 주소로 1Wei가 전송된 것을 확인할 수 있었다.</p>
<p><strong>전송했으니 내가 owner이다.</strong></p>
<h3 id="라고-생각했으면-반성하자">라고 생각했으면 반성하자.</h3>
<p>코드를 다시 보면 0.001 이더 미만을 보내고, 컨트랙트로 0 이상의 이더를 보내야 한다.
즉 아직 owner가 되지 않은 상태이다.
contract.owner()을 해보면 아직 내 주소로 변경되지 않았다.
다시 0.0005 ETH를 보내고(1Wei는 너무 적어서 조금 더 보내보았다), 컨펌시킨 후 컨트랙트로 0.0001 이더를 보내보자.</p>
<p>help()를 보면 sendTransaction({options})가 있다.</p>
<p>활용하여, <strong>contract.sendTransaction({value:toWei(&quot;0.0001&quot;)})</strong>
컨펌되었으면, contract.owner()을 다시 해보자.</p>
<p><img src="https://velog.velcdn.com/images/yun_hkr/post/9a1a22a1-55b5-4ae4-bfc5-52b3a838aa02/image.png" alt=""></p>
<p>내가 owner가 되었다!!</p>
<p>이제 contract.withdraw()로 컨트랙트의 이더를 0으로 만들자.</p>
<p><img src="https://velog.velcdn.com/images/yun_hkr/post/24bc7ba7-89be-4619-8907-419b680dbb06/image.png" alt=""></p>
<p>성공!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Ethernaut - Level -1. About The Ethernaut]]></title>
            <link>https://velog.io/@yun_hkr/Ethernaut-Level-1.-About-The-Ethernaut</link>
            <guid>https://velog.io/@yun_hkr/Ethernaut-Level-1.-About-The-Ethernaut</guid>
            <pubDate>Mon, 07 Oct 2024 04:47:44 GMT</pubDate>
            <description><![CDATA[<p>순서가 조금 바뀌었으나, Ethernaut에 대해 알고자 쓰는 글이다.
(Level 0은 이미 있어서 -1로 ㅎㅎ,,)</p>
<p>Ethernaut은 OpenZepplin이 제공하는 EVM에서 플레이할 수 있는 Web3/Solidity 기반의 워게임이다.(스마트 컨트랙트 보안 교육용 게임)
<em><strong>각 단계마다 필요한 취약점을 찾아야 한다.</strong></em></p>
<p>0<del>31까지 총 32단계이며, 0</del>5까지의 난이도로 구성되어있다.(현재까지는 최대 4이며, 이전부터 단계가 조금씩 추가되는 중이다. 6년 전 자료를 보면 15개였다.)</p>
<p>Ethernet과는 이름은 비슷하지만 관련은 없는 듯 하다.</p>
<p>뭔가 쓸게 많을 줄 알고 글 쓰기 시작했는데 막상 써보니 마땅히 쓸 것이 없다,,</p>
<p>Level을 올려가며 풀이하면서, 여기에 쓰면 좋겠다고 생각나는 것이 있을 때마다 수정할 계획이다.</p>
<p>이상!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Ethernaut - Level 0. Hello Ethernaut]]></title>
            <link>https://velog.io/@yun_hkr/Ethernaut-Level-0.-Hello-Ethernaut</link>
            <guid>https://velog.io/@yun_hkr/Ethernaut-Level-0.-Hello-Ethernaut</guid>
            <pubDate>Sun, 06 Oct 2024 14:34:25 GMT</pubDate>
            <description><![CDATA[<h2 id="1-set-up-metamask">1. Set up MetaMask</h2>
<p>크롬에 메타마스크를 설치하고, network를 sepolia로 switch.</p>
<hr>
<h2 id="2-open-the-browsers-console">2. Open the browser&#39;s console</h2>
<p>f12를 눌러 개발자모드 콘솔 창 열어주고, player()로 player의 주소를 볼 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/yun_hkr/post/a8868424-42ea-4180-a586-3028faee68d6/image.png" alt=""></p>
<hr>
<h2 id="3-use-the-console-helpers">3. Use the console helpers</h2>
<p><img src="https://velog.velcdn.com/images/yun_hkr/post/76680ac7-815b-486c-8a14-9dbed74b1d23/image.png" alt=""></p>
<p>getBalance(player)로 현재 Ether 잔액을 확인한다.</p>
<p>help()로 다양한 정보를 얻을 수 있다.</p>
<hr>
<h2 id="4-the-ethernaut-contract">4. The ethernaut contract</h2>
<p>ethernaut라는 main contract를 활용할 예정이다.</p>
<hr>
<h2 id="5-interact-with-the-abi">5. Interact with the ABI</h2>
<p>ethernaut는 Ethernaut.sol이라는 contract로, TruffleContract에 배포되어있다.
컨트랙트의 ABI는 Ethernaut.sol의 메인 메소드들을 보여준다.</p>
<p><img src="https://velog.velcdn.com/images/yun_hkr/post/7ab957f8-c1aa-4d49-850f-f2e2cc7fc215/image.png" alt=""></p>
<hr>
<h2 id="6-get-test-ether">6. Get test ether</h2>
<p>7번에서 인스턴스를 생성하는데, 이를 위해서는 수수료의 지불이 필요하다. 
공짜로 얻기 위해 <a href="https://blog.chain.link/sepolia-eth/">https://blog.chain.link/sepolia-eth/</a>
에서 0.1ETH를 얻을 수 있었다.
(그러나 이후 holesky로 네트워크를 변경하고 지인에게 받았다.)</p>
<hr>
<h2 id="7-getting-a-level-instance">7. Getting a level instance</h2>
<p>웹 페이지의 하단에 보면 Get new instance가 있다.</p>
<p>음?
왜이렇게 많이 나오지?</p>
<p><img src="https://velog.velcdn.com/images/yun_hkr/post/ab408541-e7b2-45be-aee6-7433d8e33100/image.png" alt=""></p>
<p>이유는 모르겠으나,, holesky로 바꾸어 성공했다.</p>
<p>메타마스크로 이동하여 수수료를 지불해주면 인스턴스가 생성되었음을 확인할 수 있다.</p>
<hr>
<h2 id="8-inspecting-the-contract">8. Inspecting the contract</h2>
<p><img src="https://velog.velcdn.com/images/yun_hkr/post/9223e256-a663-426b-9043-f7b055c3cbc8/image.png" alt=""></p>
<hr>
<h2 id="9-interact-with-the-contract-to-complete-the-level">9. Interact with the contract to complete the level</h2>
<p>그냥 단순히 따라가기만 하면 된다.</p>
<p><img src="https://velog.velcdn.com/images/yun_hkr/post/9f718802-f158-43d7-ab9d-207e57e1eed1/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/yun_hkr/post/b9a11772-44e5-4344-8381-066bbf33d21c/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/yun_hkr/post/b75f7060-7475-401f-a2c7-ea5ebee2a30f/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/yun_hkr/post/9d914c29-66a4-42d9-b9cc-a948583e471c/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/yun_hkr/post/3d891e55-0c6c-489b-9531-3fcd3358abcd/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/yun_hkr/post/4955bd06-a26b-46c8-a4cb-811a86d9730c/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/yun_hkr/post/7d7baff1-5390-453d-b393-096945d3a8b9/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/yun_hkr/post/ddd1f8f8-3c8c-4861-bdba-2599c9bfae7f/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/yun_hkr/post/8a2d9c66-bd37-4c2e-a60c-08039a377e84/image.png" alt=""></p>
<p>성공!</p>
<p><img src="https://velog.velcdn.com/images/yun_hkr/post/0d22bbdc-e987-4cb8-8950-c40b4f5f1d55/image.png" alt=""></p>
<p>앞으로는 조금 더 자세한 설명과 내가 고민했던 것들을 구체적으로 적을 예정이다.</p>
]]></description>
        </item>
    </channel>
</rss>