<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Joker</title>
    <link>https://caileb.tistory.com/</link>
    <description>computer programmer</description>
    <language>ko</language>
    <pubDate>Wed, 17 Jun 2026 16:40:47 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Joker2</managingEditor>
    <image>
      <title>Joker</title>
      <url>https://tistory1.daumcdn.net/tistory/357811/attach/cd835fd266f347acbb9a14dc1ecb7c25</url>
      <link>https://caileb.tistory.com</link>
    </image>
    <item>
      <title>ERC721 Token Standard</title>
      <link>https://caileb.tistory.com/210</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;ERC721&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;ERC721 Method&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;function &lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;balanceOf&lt;/span&gt;&lt;/b&gt;(address &lt;b&gt;_owner&lt;/b&gt;) external view returns (uint256)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 계정이 보유한 ERC721 토큰 수량을 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(OpenZepplin 기준) &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;i&gt;_balances&lt;/i&gt;&lt;/span&gt; 변수에 _owner 주소값을 key 으로 조회된 수량값을 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;function &lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;ownerOf&lt;/span&gt;&lt;/b&gt;(uint256 &lt;b&gt;_tokenId&lt;/b&gt;) external view returns (address)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 ERC721 토큰 소유자 계정의 eoa 를 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(OpenZepplin 기준) &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;i&gt;_owners&lt;/i&gt;&lt;/span&gt; 변수에 _tokenId 값을 key 로 조회한 사용자 eoa 주소를 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;function &lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;safeTransferFrom&lt;/span&gt;&lt;/b&gt;(address &lt;b&gt;_from&lt;/b&gt;, address &lt;b&gt;_to&lt;/b&gt;, uint256 &lt;b&gt;_tokenId&lt;/b&gt;, bytes &lt;b&gt;data&lt;/b&gt;) external payable&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;function &lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;safeTransferFrom&lt;/span&gt;&lt;/b&gt;(address &lt;b&gt;_from&lt;/b&gt;, address &lt;b&gt;_to&lt;/b&gt;, uint256 &lt;b&gt;_tokenId&lt;/b&gt;) external payable&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;function &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;transferFrom&lt;/b&gt;&lt;/span&gt;(address &lt;b&gt;_from&lt;/b&gt;, address &lt;b&gt;_to&lt;/b&gt;, uint256 &lt;b&gt;_tokenId&lt;/b&gt;) external payable&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;function &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;approve&lt;/b&gt;&lt;/span&gt;(address &lt;b&gt;_approved&lt;/b&gt;, uint256 &lt;b&gt;_tokenId&lt;/b&gt;) external payable&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 ERC721 토큰(_tokenId)을 소유자 대신에 다른 계정(_approved)이 전송할 수 있도록 설정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;(OpenZepplin 기준)&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;i&gt; _tokenApprovals&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;변수에 tokenId 값을 key 로, _approved 주소를 value 로 저장합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;function &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;setApprovalForAll&lt;/b&gt;&lt;/span&gt;(address &lt;b&gt;_operator&lt;/b&gt;, bool &lt;b&gt;_approved&lt;/b&gt;) external&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 ERC721 토큰을 소유자 대신에 다른 계정(_operator)이 전송할 수 있도록(or 전송 못하도록) 설정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;(OpenZepplin 기준)&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;i&gt; _operatorApprovals&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;변수에 TX 발생자(=소유자) 주소와 _approved 주소값을 key 로, 승인 여부(=true/false)를 value 로 저장합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;function &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;getApproved&lt;/b&gt;&lt;/span&gt;(uint256 &lt;b&gt;_tokenId&lt;/b&gt;) external view returns (address)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 ERC721 토큰(_tokenId)를 다른 계정이 전송할 수 있도록 설정된 경우 해당 계정 주소를 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(OpenZepplin 기준) &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;i&gt;_tokenApprovals&lt;/i&gt;&lt;/span&gt; 변수에 tokenId 값을 key로 조회하여 획득한 계정의 주소를 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;function &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;isApprovedForAll&lt;/b&gt;&lt;/span&gt;(address &lt;b&gt;_owner&lt;/b&gt;, address &lt;b&gt;_operator&lt;/b&gt;) external view returns (bool)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 ERC721 토큰을 특정 계정(_owner) 대신에 다른 계정(_operator)이 전송할 수 있도록(or 전송 못하도록) 설정된 여부를 반환합니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;(OpenZepplin 기준)&lt;/span&gt;&lt;span style=&quot;text-align: start; color: #ef5369;&quot;&gt;&lt;i&gt;_operatorApprovals&lt;/i&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;변수에 TX 발생자(=소유자) 주소와 _approved 주소값을 key 로, 승인 여부(=true/false)를 value 로 저장합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;ERC721 Event&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;event&amp;nbsp;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;Transfer&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;(address&amp;nbsp;indexed&amp;nbsp;&lt;b&gt;_from&lt;/b&gt;,&amp;nbsp;address&amp;nbsp;indexed&amp;nbsp;&lt;b&gt;_to&lt;/b&gt;,&amp;nbsp;uint256&amp;nbsp;indexed&amp;nbsp;&lt;b&gt;_tokenId&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;event&amp;nbsp;&lt;span style=&quot;color: #009a87; background-color: #f6e199;&quot;&gt;&lt;b&gt;Approval&lt;/b&gt;&lt;/span&gt;(address&amp;nbsp;indexed&amp;nbsp;&lt;b&gt;_owner&lt;/b&gt;,&amp;nbsp;address&amp;nbsp;indexed&amp;nbsp;&lt;b&gt;_approved&lt;/b&gt;,&amp;nbsp;uint256&amp;nbsp;indexed&amp;nbsp;&lt;b&gt;_tokenId&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;event&amp;nbsp;&lt;span style=&quot;color: #009a87; background-color: #f6e199;&quot;&gt;&lt;b&gt;ApprovalForAll&lt;/b&gt;&lt;/span&gt;(address&amp;nbsp;indexed&amp;nbsp;&lt;b&gt;_owner&lt;/b&gt;,&amp;nbsp;address&amp;nbsp;indexed&amp;nbsp;&lt;b&gt;_operator&lt;/b&gt;,&amp;nbsp;bool&amp;nbsp;&lt;b&gt;_approved&lt;/b&gt;);&lt;/p&gt;</description>
      <category>dev-tips/blockchain</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/210</guid>
      <comments>https://caileb.tistory.com/210#entry210comment</comments>
      <pubDate>Thu, 1 Aug 2024 17:35:51 +0900</pubDate>
    </item>
    <item>
      <title>ERC20 Token Standard</title>
      <link>https://caileb.tistory.com/209</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;ERC20&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이더리움에서 사용하는 (대체 가능한) 토큰(=Fungible Token, FT) 표준입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;ERC20 Method&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;function&amp;nbsp;&lt;b&gt;&lt;span style=&quot;color: #006dd7; background-color: #f6e199;&quot;&gt;name&lt;/span&gt;()&lt;/b&gt;&amp;nbsp;public&amp;nbsp;view&amp;nbsp;returns&amp;nbsp;(string)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ERC20 contract 에 설정된 토큰 이름(=name)을 반환하는 함수입니다. 보통 생성자(constructor)의 입력을 통해 설정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;function&amp;nbsp;&lt;b&gt;&lt;span style=&quot;color: #006dd7; background-color: #f6e199;&quot;&gt;symbol&lt;/span&gt;()&lt;/b&gt;&amp;nbsp;public&amp;nbsp;view&amp;nbsp;returns&amp;nbsp;(string)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ERC20 contract 에 설정된 토큰 심볼(=symbol)을 반환하는 함수입니다. 보통 생성자(constructor)의 입력을 통해 설정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;function&amp;nbsp;&lt;b&gt;&lt;span style=&quot;color: #006dd7; background-color: #f6e199;&quot;&gt;decimals&lt;/span&gt;()&lt;/b&gt;&amp;nbsp;public&amp;nbsp;view&amp;nbsp;returns&amp;nbsp;(uint8)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ERC20 contract 에 설정된 토큰 소수자릿수(=decimals)를 반환하는 함수입니다. 보통 생성자(constructor)의 입력을 통해 설정하거나, 생성시 별도로 설정하지 않는다면 많은 ERC20 에서 사용하는 18을 default 값으로 사용하는 경우가 일반적입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;function&amp;nbsp;&lt;b&gt;&lt;span style=&quot;color: #006dd7; background-color: #f6e199;&quot;&gt;totalSupply&lt;/span&gt;()&lt;/b&gt;&amp;nbsp;public&amp;nbsp;view&amp;nbsp;returns&amp;nbsp;(uint256)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ERC20 contract 를 통해 발행된 총 토큰수량을 반환하는 함수입니다. 만약, ERC20 토큰이 운영 중에 추가된다면 추가수량만큼 증가된 총 토큰 수량이 반환됩니다. 예를 들어, 어떤 ERC20 토큰이 OpenZeppelin 에서 (개발자 편의를 위해 미리 템플릿을 만들어 제공하는) ERC20 contract 로 개발되었고, 이 contract 가 총 수량을 추가할 수 있는 함수(ex, &lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://github.com/OpenZeppelin/openzeppelin-contracts/blob/05f218fb6617932e56bf5388c3b389c3028a7b73/contracts/token/ERC20/ERC20.sol#L221&quot;&gt;_mint()&lt;/a&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;함수)&lt;/span&gt;가 public 하게 오픈되어 있다면, 이 함수를 통해 총 발행 수량을 늘릴 수 있고, 이렇게 변경된 총 발행수량은 totalSupply() 함수의 결과값으로 반환하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;function&amp;nbsp;&lt;b&gt;&lt;span style=&quot;color: #006dd7; background-color: #f6e199;&quot;&gt;balanceOf&lt;/span&gt;(address&amp;nbsp;_owner)&lt;/b&gt;&amp;nbsp;public&amp;nbsp;view&amp;nbsp;returns&amp;nbsp;(uint256&amp;nbsp;balance)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ERC20 토큰을 _owner 계정(=eoa)이 얼마나 보유했는지 수량을 반환하는 함수입니다. ERC20 contract 에는 아래와 같이 &lt;span style=&quot;color: #8a3db6;&quot;&gt;_balances&lt;/span&gt; 라는 (외부서 직접 접근 및 변경이 불가능한) private 변수가 정의되어 있습니다. 이 변수는 key-value 구조인 map 자료형을 사용하는데, key 값은 사용자의 eoa 를 address 자료형으로 저장하고, 해당 key 의 value 값은 토큰 수량을 uint256 자료형으로 저장하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;i&gt;mapping(address account =&amp;gt; uint256) private _balances; &lt;/i&gt;&lt;br /&gt;&lt;i&gt;(출처 : &lt;a href=&quot;https://github.com/OpenZeppelin/openzeppelin-contracts/blob/05f218fb6617932e56bf5388c3b389c3028a7b73/contracts/token/ERC20/ERC20.sol#L30C5-L30C59&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;OpenZepplin&lt;/a&gt;)&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자(=eoa)가 ERC20 토큰을 전송할 때마다 전송한 수량만큼 value 값이 변경됩니다. balanceOf() 함수는 _owner 계정을 key 값으로 조회하여 해당 value 값을 그대로 반환하는 처리를 수행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;function&amp;nbsp;&lt;b&gt;&lt;span style=&quot;color: #006dd7; background-color: #f6e199;&quot;&gt;transfer&lt;/span&gt;(address&amp;nbsp;_to,&amp;nbsp;uint256&amp;nbsp;_value)&lt;/b&gt;&amp;nbsp;public&amp;nbsp;returns&amp;nbsp;(bool&amp;nbsp;success)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ERC20 토큰을 to 주소로 value 수량만큼 전송하는 함수입니다. transfer() 함수는 아래와 같이 contract 내의 _transfer(from, to, value) 함수를 호출하며, 이때 from 위치에는 TX를 실행한 사용자의 eoa 를, to 위치에는 입력값 중 _to 주소값을, 그리고 value 위치에는 입력된 value 값을 사용하여 호출하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;i&gt;address&amp;nbsp;owner&amp;nbsp;=&amp;nbsp;_msgSender();&lt;/i&gt;&lt;br /&gt;&lt;i&gt;_transfer(owner,&amp;nbsp;to,&amp;nbsp;value);&lt;/i&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;i&gt;(출처 : &lt;a href=&quot;https://github.com/OpenZeppelin/openzeppelin-contracts/blob/05f218fb6617932e56bf5388c3b389c3028a7b73/contracts/token/ERC20/ERC20.sol#L104&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;OpenZepplin&lt;/a&gt;)&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;_transfer() 함수는 ERC20 의 전송 처리를 담당하는 함수인데, 이 함수는 위에서 언급한 &lt;span style=&quot;color: #8a3db6;&quot;&gt;_balance&lt;/span&gt; 변수의 값을 변경시키며, _transfer() 에 입력된 from 을 key 값으로 조회하여 가져온 value 값을 증가시키고, _transfer() 에 입력된 to 를 key 값으로 조회하여 가져온 value 값을 감소시키는 작업을 수행하며, 이러한 숫자 변환 처리 자체가 전송 처리의 매커니즘을 만들게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;function&amp;nbsp;&lt;b&gt;&lt;span style=&quot;color: #006dd7; background-color: #f6e199;&quot;&gt;approve&lt;/span&gt;(address&amp;nbsp;_spender,&amp;nbsp;uint256&amp;nbsp;_value)&lt;/b&gt;&amp;nbsp;public&amp;nbsp;returns&amp;nbsp;(bool&amp;nbsp;success)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;ERC20 contract 는 자신의 토큰을 자신이 전송하는 것이 아닌, 다른 사람이 자신을 대신해서 전송할 수 있도록 하는 매커니즘이 있습니다. 아래와 같이 &lt;span style=&quot;color: #409d00;&quot;&gt;_allowances&lt;/span&gt; 라는 private 변수가 정의되어 있는데, 이 변수는 이중 key-value 구조를 가집니다. key 값은 TX를 실행한 본인의 eoa가 저장되며, value 값에는 자신을 대신하여 전송을 허용할 사용자들의 주소 혹은 컨트랙트 주소와 얼마나 허용할지에 대한 수량이 저장하고 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;i&gt;mapping(address&amp;nbsp;account&amp;nbsp;=&amp;gt;&amp;nbsp;mapping(address&amp;nbsp;spender&amp;nbsp;=&amp;gt;&amp;nbsp;uint256))&amp;nbsp;private&amp;nbsp;_allowances;&lt;/i&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;i&gt;(출처 :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://github.com/OpenZeppelin/openzeppelin-contracts/blob/05f218fb6617932e56bf5388c3b389c3028a7b73/contracts/token/ERC20/ERC20.sol#L32C5-L32C89&quot;&gt;OpenZepplin&lt;/a&gt;)&lt;/i&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;만약, 사용자 A가 사용자 B와 사용자C에게 자신을 대신해서 각각 50개씩 전송하도록 허용하고 있다면, &lt;span style=&quot;color: #409d00;&quot;&gt;_allowances&lt;/span&gt; 에는 아래처럼 저장되어 있을 것입니다. (참고, 아래는 json 포멧으로 map 구조를 변환하여 작성한 것입니다.)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;i&gt;{ &quot;사용자 A&quot; : {&amp;nbsp; &quot;사용자 B&quot; :&amp;nbsp; 50, &quot;사용자 C&quot; : 50 } }&lt;/i&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위처럼 &lt;span style=&quot;color: #409d00;&quot;&gt;_allowances&lt;/span&gt; 변수가 설정되어 있을때, 사용자 B와 사용자 C는 각자 사용자 A를 대신하여 사용자 A가 보유한 토큰을 최대 50개씩 전송이 가능하게 됩니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;function&amp;nbsp;&lt;b&gt;&lt;span style=&quot;color: #006dd7; background-color: #f6e199;&quot;&gt;allowance&lt;/span&gt;(address&amp;nbsp;_owner,&amp;nbsp;address&amp;nbsp;_spender)&lt;/b&gt;&amp;nbsp;public&amp;nbsp;view&amp;nbsp;returns&amp;nbsp;(uint256&amp;nbsp;remaining)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;_owner 의 계정이 _spender 계정에게 (_owner 의 계정을 대신하여 _owner 의 계정소유의 토큰을) 얼마나 대신해서 전송하는 것을 허용할지에 대한 수량을 반환하는 함수입니다. allowance() 함수는&amp;nbsp;&lt;span style=&quot;color: #409d00; text-align: start;&quot;&gt;_allowances &lt;/span&gt;변수에서 _owner 계정을 기준으로 key 값을 조회하고 _spender 값을 기준으로 value 값을 한번 더 (value 값 안에서 key 값으로) 조회하여 가져온 값(=허용수량)을 반환합니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;function&amp;nbsp;&lt;b&gt;&lt;span style=&quot;color: #006dd7; background-color: #f6e199;&quot;&gt;transferFrom&lt;/span&gt;(address&amp;nbsp;_from,&amp;nbsp;address&amp;nbsp;_to,&amp;nbsp;uint256&amp;nbsp;_value)&lt;/b&gt;&amp;nbsp;public&amp;nbsp;returns&amp;nbsp;(bool&amp;nbsp;success)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ERC20 토큰을 to 주소로 value 수량만큼 전송하는 함수입니다. transferFrom() 함수는 transfer() 함수와 다르게 _from 값을 추가로 받습니다. 이 함수는 TX 를 실행한 자신을 대신하여 다른 사용자가 자신의 토큰을 전송하려할 때 사용되는 함수입니다. 아래는 transferFrom() 함수의 처리과정의 일부분이며, &lt;span style=&quot;color: #409d00;&quot;&gt;_allowances&lt;/span&gt; 변수와 참조하는 로직이 추가로 있다는 점이 transfer() 함수와 다릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;i&gt;address&amp;nbsp;spender&amp;nbsp;=&amp;nbsp;_msgSender();&lt;/i&gt;&lt;br /&gt;&lt;i&gt;_spendAllowance(from,&amp;nbsp;spender,&amp;nbsp;value);&lt;/i&gt;&lt;br /&gt;&lt;i&gt;_transfer(from,&amp;nbsp;to,&amp;nbsp;value);&lt;/i&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;i&gt;(출처 : &lt;a href=&quot;https://github.com/OpenZeppelin/openzeppelin-contracts/blob/05f218fb6617932e56bf5388c3b389c3028a7b73/contracts/token/ERC20/ERC20.sol#L149&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;OpenZepplin&lt;/a&gt;)&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;_spendAllownace() 함수가 내부에서 호출되는 것을 볼 수 있는데, 이 함수는 from 계정이 spender 계정에 value 수량 이상을 허용하고 있는지 확인하는 과정과 (허용하고 있다면) &lt;span style=&quot;color: #409d00;&quot;&gt;_allowances&lt;/span&gt; 변수에서 from 계정이 spender 계정에 허용한 토큰 수량을 value 만큼 줄이는 과정이 포함되어 있습니다. 이 처리 과정 때문에 spender 는 허용된 수량만큼만 전송이 가능하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;ERC20 Event&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;event &lt;b&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;Transfer&lt;/span&gt;(address indexed _from, address indexed _to, uint256 _value)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ERC20 토큰이 전송하는 TX가 발생되는 경우 발생되는 event 입니다. 실제로 이 event 는&amp;nbsp;&lt;span style=&quot;color: #8a3db6; text-align: start;&quot;&gt;_balances &lt;/span&gt;변수값이 변경될 때 발생됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;event&amp;nbsp;&lt;b&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;Approval&lt;/span&gt;(address&amp;nbsp;indexed&amp;nbsp;_owner,&amp;nbsp;address&amp;nbsp;indexed&amp;nbsp;_spender,&amp;nbsp;uint256&amp;nbsp;_value)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ERC20 토큰이 전송하는 TX가 발생되는 경우 발생되는 event 입니다. 실제로 이 event 는 &lt;span style=&quot;color: #409d00;&quot;&gt;_allowances&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;변수값이 변경될 때 발생됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://eips.ethereum.org/EIPS/eip-20&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;ERC-20: Token Standard&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>dev-tips/blockchain</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/209</guid>
      <comments>https://caileb.tistory.com/209#entry209comment</comments>
      <pubDate>Sat, 13 Jul 2024 01:36:02 +0900</pubDate>
    </item>
    <item>
      <title>ModuleNotFoundError: No module named 'six'</title>
      <link>https://caileb.tistory.com/208</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Okta - &lt;span style=&quot;text-align: start;&quot;&gt;ModuleNotFoundError: No module named 'six'&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;에러 (Error)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;(okta-awscli) ModuleNotFoundError: No module named 'six'&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1438&quot; data-origin-height=&quot;420&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdf8rW/btsGlYofKuo/Hf6O36qVwzSQQLUXByZVa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdf8rW/btsGlYofKuo/Hf6O36qVwzSQQLUXByZVa1/img.png&quot; data-alt=&quot;Console Error Log&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdf8rW/btsGlYofKuo/Hf6O36qVwzSQQLUXByZVa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbdf8rW%2FbtsGlYofKuo%2FHf6O36qVwzSQQLUXByZVa1%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;767&quot; height=&quot;224&quot; data-origin-width=&quot;1438&quot; data-origin-height=&quot;420&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Console Error Log&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;환경 (Environment)&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Mac M1 Max&lt;/li&gt;
&lt;li&gt;Mac OS (Monterey 12.6)&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;원인 (Cause)&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;okta-awscli 를 사용해 로컬에서 인증을 시도할 때 에러가 발생했다. 인증이 되어야 local 에서 의존되어 있는 툴들을 실행할 수 있기 때문에 해결되어야 했다. 하루 전에 homebrew 업그레이드를 한 것이 okta-awscli 툴 사용에 영향이 미친 것은 아닐까 생각된다. 원인을 하나씩 찾다가 &lt;span style=&quot;color: #ee2323;&quot;&gt;python 버전이 okta-awscli 에서 사용하는 것과 local 에 설치된 python 버전이 다르다&lt;/span&gt;는 것을 알았다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;local 에 설치된 python 버전 (v3.12.2)&lt;/p&gt;
&lt;pre id=&quot;code_1712276821287&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;% python3 --version
Python 3.12.2&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;okta-awscli 에 사용 중인 python 버전 (v3.9)&lt;/p&gt;
&lt;pre id=&quot;code_1712276985067&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;에러에 포함된 문구
&quot;/opt/homebrew/lib/python3.9/site-packages/oktaawscli/okta_awscli.py&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://formulae.brew.sh/formula/okta-awscli&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;homebrew 사이트에 게시된 okta-awscli&lt;/a&gt; 에서 사용하는 python 버전은 3.12.2 (=2024.4.5 기준) 이므로, 기존에 local 에 설치되어 있던 버전은 old 버전인 것을 알았고, okta-awscli 를 최신으로 업데이트하면 문제는 해결될 것이라고 생각했다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;해결 (Solution)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 &lt;span style=&quot;color: #006dd7;&quot;&gt;okta-awscli 패키지 재설치&lt;/span&gt;를 함으로써 에러 상황이 더이상 발생되지 않았다. (참고, uninstall 경우, background 로 처리되어 실행 후 몇초 뒤에 설치하면 된다.)&lt;/p&gt;
&lt;pre id=&quot;code_1712276310897&quot; class=&quot;bash&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;패키지 삭제
% brew uninstall okta-awscli

패키지 설치
% brew install okta-awscli&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;만약, okta-awscli 를 재설치했는데도 python 버전이 이전과 동일한 경우, uninstall 때 기존 binary 파일이 완전히 삭제된 것이 아니니 해당 파일을 제거하면된다.&lt;/p&gt;
&lt;pre id=&quot;code_1712277708241&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;% mv /opt/homebrew/bin/okta-awscli /opt/homebrew/bin/okta-awscli-tmp&lt;/code&gt;&lt;/pre&gt;</description>
      <category>error/[err] mac, window</category>
      <category>OKTA</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/208</guid>
      <comments>https://caileb.tistory.com/208#entry208comment</comments>
      <pubDate>Fri, 5 Apr 2024 09:43:07 +0900</pubDate>
    </item>
    <item>
      <title>Uncaught TypeError: XX is not a function</title>
      <link>https://caileb.tistory.com/207</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;React - Uncaught TypeError: XX is not a function&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt; &lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;에러 (Error)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: #EE2323;&quot;&gt;XX.js:19 Uncaught TypeError: XX is not a function&lt;/span&gt;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1580&quot; data-origin-height=&quot;372&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PZJbl/btrX4mqZtyV/UlnCBxh3iIEg3E28nOBffK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PZJbl/btrX4mqZtyV/UlnCBxh3iIEg3E28nOBffK/img.png&quot; data-alt=&quot; Console Error Log &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PZJbl/btrX4mqZtyV/UlnCBxh3iIEg3E28nOBffK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPZJbl%2FbtrX4mqZtyV%2FUlnCBxh3iIEg3E28nOBffK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1580&quot; height=&quot;372&quot; data-origin-width=&quot;1580&quot; data-origin-height=&quot;372&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt; Console Error Log &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt; &lt;br&gt; &lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;상황 (Situation)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;redux 를 사용해서 개발 중에 function 을 찾을 수 없다는 에러가 발생했다. UI 에 필요한 component 들부터 개발한 후, BE의 API 응답을 받아 status 로 관리하기 위해 redux를 사용면서 코드 일부를 수정하는 과정에서 위와 같은 문제가 발생하였다. &lt;span style=&quot;color: #006DD7;&quot;&gt;문제되는 function (getUserInfo) 은 console 에서 undefined&lt;/span&gt; 로 찍히고 있었고, 에러가 발생하고 있던 코드는 아래와 같다. &lt;br&gt; &lt;br&gt;Dashboard.js&lt;/p&gt;
&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;import React, {useState, useEffect} from 'react';
import { bindActionCreators } from 'redux'
import { connect, useSelector } from 'react-redux'
import { getUserInfo as getUserInfoAction } from &quot;../modules/account&quot;;

export const Dashboard = ({fetchUserInfo}) =&amp;gt; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;useEffect(() =&amp;gt; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;	console.log(fetchUserInfo); &amp;lt;-- undefined 로 출력 확인
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;fetchUserInfo(token);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;},[]);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;..
}

..
const mapDispatchToProps = (dispatch) =&amp;gt; ({
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;fetchUserInfo: bindActionCreators(getUserInfoAction, dispatch),
});

export default connect(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mapStateToProps,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mapDispatchToProps,
)(Dashboard);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt; &lt;br&gt;MyPage.js&lt;/p&gt;
&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;import { Dashboard } from &quot;../container/Dashboard&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt; &lt;br&gt; &lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;원인 (Cause)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;redux를 적용하기 전의 기존 코드에서는 Dashboad.js 에서는 &quot;export const Dashboard&quot; 로 export 를 한 후, MyPage.js 에서는 &quot;import { Dashboard }&quot; 로 improt 를 하고 있었다. 그런데, &lt;span style=&quot;color: #EE2323;&quot;&gt;redux 를 적용하면서 Dashboard.js 에 &quot;export default connect()&quot; 코드가 추가되면서 default 로 export 를 하도록 바뀌었으나, MyPage.js 에서는 이를 고려하지 않았기 때문&lt;/span&gt;에 발생하였다.&lt;br&gt; &lt;br&gt; &lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;해결 (Solution)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: #006DD7;&quot;&gt;default 로 export 한 component 는 괄호(&quot;{}&quot;, brackets) 없이 import 를 해서 사용&lt;/span&gt;해야하므로, 아래와 같이 MyPage.js 에서 import 할 때 괄호를 제거함으로써 에러 상황이 더이상 발생되지 않았다.&lt;br&gt; &lt;br&gt;MyPage.js&lt;/p&gt;
&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;import Dashboard from &quot;../container/Dashboard&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt; &lt;br&gt; &lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;참고&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: #9D9D9D;&quot;&gt;stackoverflow : mapDispatchToProps not working. Props empty (error: redux action is not a function)&lt;/span&gt; &lt;span style=&quot;color: #006DD7;&quot;&gt;&lt;span style=&quot;color: #006DD7;&quot;&gt;link&lt;/span&gt;&lt;/span&gt;&lt;br&gt; &lt;/p&gt;</description>
      <category>error/[err] react</category>
      <category>export</category>
      <category>import</category>
      <category>react</category>
      <category>Redux</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/207</guid>
      <comments>https://caileb.tistory.com/207#entry207comment</comments>
      <pubDate>Sat, 4 Feb 2023 20:01:59 +0900</pubDate>
    </item>
    <item>
      <title>macOS(M1)에 node 설치하기</title>
      <link>https://caileb.tistory.com/206</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;macOS(M1)에 node 설치하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;글쓴이는 최신형 mac book을 새로 주문했습니다. 이전에 개발하던 프로그램들을 모두 하나씩 설치하는데, 새로운 mac book 의 환경이 달라져서 환경세팅을 하는도중에 몇가지 걸림돌들이 있었습니다. 그중 &lt;b&gt;nodeJS 환경을 세팅&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;현재 글쓴이의 mac book 스펙은 아래와 같습니다. M1칩을 사용하고 있는데, 이 칩은 Apple 이 자체개발한 것입니다. 이전 모델까지는 Intel사에서 만든 칩을 사용했었는데, 앞으로는 직접 개발한 M1칩을 사용한 것입니다. 그리고&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;, 이 Intel과 M1칩은 다르기 때문에, 이전에 사용하던 프로그램들이 &lt;b&gt;Intel과 M1 중에 어떤 환경을 대상으로 빌드되었는지&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;개발환경&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;mac book pro&lt;/li&gt;
&lt;li&gt;macOS Monterey&lt;/li&gt;
&lt;li&gt;버전 12.2.1&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;칩 Apple M1 Max&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;node 14.17.0&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;node 14 이상을 설치하려는 경우&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;error 내용&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;node 14.17.0 버전을 설치하려고 하는데 아래와 같이 에러가 발생했습니다. 이유는 M1칩에서 낮은 node 버전을 설치하려 시도했기 때문입니다. 아무래도 낮은 버전의 node 들은 Intel 칩만을 대상으로 빌드되었고, 높은 버전의 node 들은 M1 칩에서도 동작가능하도록 빌드되었기 때문으로 보입니다. 따라서, 낮은 버전의 node 들을 M1 칩에서 동작하기 위해서는 그대로 설치하면 안되고, [에러 1 : node 설치하기 (node 14 이상)] 가이드에 따라 설치해주면 되겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1648559461522&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...
clang: error: no such file or directory: 'CXX=c++'
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;node &amp;nbsp;설치하기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. nvm 설치&lt;/p&gt;
&lt;pre id=&quot;code_1648556384405&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ brew install nvm&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. nvm 폴더 생성&lt;/p&gt;
&lt;pre id=&quot;code_1648556400277&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ mkdir ~/.nvm&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. .zshrc 편집&lt;/p&gt;
&lt;pre id=&quot;code_1648556409686&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ vi ~/.zshrc&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1648556179316&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export PATH=/opt/homebrew/bin:$PATH

export NVM_DIR=&quot;$HOME/.nvm&quot; 
[ -s &quot;/opt/homebrew/opt/nvm/nvm.sh&quot; ] &amp;amp;&amp;amp; . &quot;/opt/homebrew/opt/nvm/nvm.sh&quot; # This loads nvm 
[ -s &quot;/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm&quot; ] &amp;amp;&amp;amp; . &quot;/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm&quot; # This loads nvm bash_completion&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;4. .zshrc 내용 pc에 적용&lt;/p&gt;
&lt;pre id=&quot;code_1648556488047&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ source ~/.zshrc&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;5. nvm 설치 (낮은 버전 ex, 14.17.0)&lt;/p&gt;
&lt;pre id=&quot;code_1648556576937&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ nvm install 14.17.0
$ node --version&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;node 14 이하를 설치하려는 경우&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;error 내용&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;M1칩 환경에서 node 개발 환경 관련 내용을 찾다보니, 글쓴이처럼 M1칩에서 node 14버전 이상은 [이슈1 : node 설치하기 (node 14d 이상)]에 따라 환경세팅이 가능하지만 node 14버전 이하를 사용하려할 때는 아래처럼 에러가 발생해서, 추가적인 세팅을 해주어야한다고 합니다. nvm 깃허브 공식 사이트에 나와있는 것처럼 아래가이드에 따라 세팅해주면 실행됩니다. (&lt;span style=&quot;color: #7e98b1;&quot;&gt;직접 해보진 않아서 해당 문제에 부딪치면 그때 본 내용을 업데이트해두겠습니다.&lt;/span&gt;)&lt;/p&gt;
&lt;pre id=&quot;code_1648559793183&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...
../deps/openssl/config/archs/linux-x86_64/asm/crypto/aes/aes-x86_64.s:2:1: error: unknown directive .type
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;&lt;b&gt;node&amp;nbsp;&amp;nbsp;설치하기&lt;/b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Macs with M1 chip&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;M1 칩을 사용하는 Mac 사용자들을 위한 가이드&lt;/p&gt;
&lt;p data-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: #006dd7;&quot;&gt;So,&amp;nbsp;if&amp;nbsp;you&amp;nbsp;want&amp;nbsp;to&amp;nbsp;run&amp;nbsp;a&amp;nbsp;version&amp;nbsp;prior&amp;nbsp;to&amp;nbsp;v16.0&amp;nbsp;on&amp;nbsp;an&amp;nbsp;M1&amp;nbsp;Mac,&amp;nbsp;it&amp;nbsp;may&amp;nbsp;be&amp;nbsp;best&amp;nbsp;to&amp;nbsp;compile&amp;nbsp;node&amp;nbsp;targeting&amp;nbsp;the&amp;nbsp;x86_64&amp;nbsp;Intel&amp;nbsp;architecture&amp;nbsp;so&amp;nbsp;that&amp;nbsp;Rosetta&amp;nbsp;2&amp;nbsp;can&amp;nbsp;translate&amp;nbsp;the&amp;nbsp;x86_64&amp;nbsp;processor&amp;nbsp;instructions&amp;nbsp;to&amp;nbsp;ARM-based&amp;nbsp;Apple&amp;nbsp;Silicon&amp;nbsp;instructions.&amp;nbsp;Here's&amp;nbsp;what&amp;nbsp;you&amp;nbsp;will&amp;nbsp;need&amp;nbsp;to&amp;nbsp;do:&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약에, M1 칩에서 node 16 이전버전을 사용하기를 원한다면, x86_64 Intel 칩 위에서 실행가능하도록 컴파일된 것을 사용하는 것이 가장 좋습니다.&amp;nbsp; macOS 에서 그렇게 실행하도록 도와주는 Rosetta 2 라는 프로그램이 있습니다. 이 프로그램은 x86_64 명령어들을 Apple Silicon 기반의 명령어들로 번역해줌으로써 M1 칩에서 동작가능하게 만듭니다. 자, 이어서 아래 가이드에 따라 Rosetta 를 실행해보겠습니다.&lt;/p&gt;
&lt;p data-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: #006dd7;&quot;&gt;This same thing can also be accomplished by finding the Terminal or iTerm App in Finder, right clicking, selecting &quot;Get Info&quot;, and then checking the box labeled &quot;Open using Rosetta&quot;. Open&amp;nbsp;a&amp;nbsp;shell&amp;nbsp;that's&amp;nbsp;running&amp;nbsp;using&amp;nbsp;Rosetta&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Finder 를 열고 &amp;gt; 응용프로그램의 Terminal 에서 &amp;gt; 마우스 우클릭 &amp;gt; 정보 가져오기 &amp;gt; Rosetta를 사용하여 열기를 체크하고 실행을 합니다. 그리고 아래 명령어를 실행합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1648557991538&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ arch -x86_64 zsh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Install&amp;nbsp;whatever&amp;nbsp;older&amp;nbsp;version&amp;nbsp;of&amp;nbsp;node&amp;nbsp;you&amp;nbsp;are&amp;nbsp;interested&amp;nbsp;in.&amp;nbsp;Let's&amp;nbsp;use&amp;nbsp;12.22.1&amp;nbsp;as&amp;nbsp;an&amp;nbsp;example.&amp;nbsp;This&amp;nbsp;will&amp;nbsp;fetch&amp;nbsp;the&amp;nbsp;node&amp;nbsp;source&amp;nbsp;code&amp;nbsp;and&amp;nbsp;compile&amp;nbsp;it,&amp;nbsp;which&amp;nbsp;will&amp;nbsp;take&amp;nbsp;several&amp;nbsp;minutes.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오래된 node 버전을 설치해보세요. 그럼 정상적으로 설치될 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1648558979597&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ nvm install v12.22.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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;참고&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(github) nvm &lt;a href=&quot;https://github.com/nvm-sh/nvm&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;링크&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;</description>
      <category>environment/macOS</category>
      <category>Apple M1</category>
      <category>Apple Silicon</category>
      <category>nodejs</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/206</guid>
      <comments>https://caileb.tistory.com/206#entry206comment</comments>
      <pubDate>Tue, 29 Mar 2022 22:32:50 +0900</pubDate>
    </item>
    <item>
      <title>redis transaction - 1. Multi, Exec, Discard, Watch, UnWatch</title>
      <link>https://caileb.tistory.com/205</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Redis Transaction&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;목차&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;u&gt;Multi, Exec, Discard, Watch, Unwatch&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;Pipeline, Non Transactional Pipeline&lt;/li&gt;
&lt;li&gt;Lua Script&lt;/li&gt;
&lt;/ul&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;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;요약&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Database 의 Transaction&lt;/span&gt;&amp;nbsp;: &lt;span style=&quot;color: #006dd7;&quot;&gt;ACID 를 보장하는 작업 단위&lt;/span&gt;를 말한다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Redis 의 Transaction&lt;/span&gt;&amp;nbsp;: &lt;span style=&quot;color: #006dd7;&quot;&gt;(ACID 까지는 아니지만) 독립적인 작업단위&lt;/span&gt;를 말한다.&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;Multi + Exec&lt;/span&gt; : &lt;span style=&quot;color: #006dd7;&quot;&gt;Single Isolation Operation &lt;/span&gt;방식으로 처리하는 방법이다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;Watch + Multi + Exec&lt;/span&gt; : &lt;span style=&quot;color: #006dd7;&quot;&gt;Optimistic Locking&lt;/span&gt; 방식으로 처리하는 방법이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&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;개발을 할 때, Redis의 Transaction을 다루는 경우 Database의 Transaction의 개념과 헷갈리는 부분들이 있었고, 정확한 이해를 위해 Redis와 Database 에서 말하는 Transaction이 어떤 차이가 있는지 정리합니다.&lt;/p&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;Database &lt;span style=&quot;color: #000000;&quot;&gt;에서의&lt;/span&gt; Transaction&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;트랜잭션은 데이터베이스(Database)에서&amp;nbsp;ACID (Atomic, Consistency, Isolation, Durability)를 보장하는 작업단위를 말합니다.&lt;/span&gt; ACID는 각각 원자성, 일관성, 독립성, 지속성을 의미하고 Transaction이라는 한가지 작업을 처리하는데 보장하는 성질을 의미합니다. 개발자는 Database 에 반영되어야 하는 한가지 작업을 완전히 수행하기 위해, 하나 혹은 둘 이상의 SQL문(ex, Select, Insert, Delete, Update) 조합하게 됩니다. 이 때, 둘 이상의 SQL 문들의 조합으로 구성하는 경우, &lt;span style=&quot;color: #006dd7;&quot;&gt;원자성(Atomic)&lt;/span&gt;&lt;span&gt;은 여러 개의 SQL 문들이 마치 하나처럼 완전히 실행되거나 실행되지 않도록&lt;/span&gt; &lt;span style=&quot;color: #006dd7;&quot;&gt;Single Operation&lt;/span&gt;&lt;span&gt;으로 동작하게 하는 것,&lt;/span&gt; &lt;span style=&quot;color: #006dd7;&quot;&gt;일관성(Consistency)&lt;/span&gt;&lt;span&gt;은 실행 이전과 실행 이후의 상태가 일관된 유지된다는 의미로 무결성 제약조건에 위배되지 않는 것,&lt;/span&gt; &lt;span style=&quot;color: #006dd7;&quot;&gt;독립성(Isolation)&lt;/span&gt;&lt;span&gt;은 Transaction 을 구성하는 SQL 문들이 실행 되는 중간에 다른 Transaction 들이 끼어들지 못하는 온전히 독립된 상태로 실행되는 것으로 &lt;span style=&quot;color: #006dd7;&quot;&gt;동시성 제어(&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Concurrency Control&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;)&lt;/span&gt;를 위함이고,&lt;/span&gt; &lt;span style=&quot;color: #006dd7;&quot;&gt;지속성(Durability)&lt;/span&gt;&lt;span&gt;은 실행 완료 이후 변경사항이 영구적으로 유지된다는 것으로 SSD, Disk 등에 보관된 상태로 존재한다는 것을 의미합니다&lt;/span&gt;. Transaction이 실제로 어떻게 사용될 수 있는지 아래의 2가지 예시를 통해 살펴봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;example 1&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A 테이블의 특정 Record의 Column 값에 따라 B와 C의 테이블에 한번에 반영이 되어야 한다면, A 테이블 조회를 위한 Select + B 테이블 갱신을 위한 Insert + C 테이블 갱신을 위한 Insert 를 필요로 할 것입니다. 이를 Query 문으로 작성한다면 아래와 같을 것이고 이 Query 문들은 전부 실행이 되어 반영이 되거나 혹은 전부 반영이 되지 않아야 합니다. 만약에, B 테이블은 제대로 반영이 되었는데, C 테이블은 primary key 가 중복되어 duplicate entry 상황으로 에러가 발생되었다고 해서 C 테이블에만 반영이 안되는 상황이 일어나면 안된다는 것입니다. 다시 말해서, &lt;span style=&quot;color: #006dd7;&quot;&gt;Database System 에서&amp;nbsp;Transaction의 특징 중 전체가 실행되거나 실행되지 않아야 한다는 원자성(Atomic) 보장해주고 있기하기 때문에 이러한 처리가 가능한 것&lt;/span&gt;입입니다.&lt;/p&gt;
&lt;p data-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: #006dd7;&quot;&gt; 각 query 문을 독립적으로 실행&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1625327945310&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select data1, data2 from A
insert into B (key) values (data1 조회 값)
insert into C (key) values (data2 조회 값)&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: #006dd7;&quot;&gt; 각 query 문을 하나의 transaction으로 묶어서 실행 (=transaction의 ACID 를 보장)&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1625327957935&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;gt; begin
&amp;gt; select data1, data2 from A
// data1, data2 조회값 확인
&amp;gt; insert into B (key) values (data1 조회 값)
&amp;gt; insert into C (key) values (data2 조회 값)
&amp;gt; commit&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: #000000;&quot;&gt;&lt;b&gt;example 2&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카카오페이, 토스, 국민은행 등에서 사용하는 돈을 관리하는 프로그램을 개발한다고 생각해본다면, 그리고 이 프로그램이 사용자의 계좌 정보를 유지하기 위해 만든 테이블을 간단히 구성해본다면 아래처럼 생각해볼 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;account table (계좌 테이블)&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 52.6744%; height: 55px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignCenter&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 19px;&quot;&gt;id&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 19px;&quot;&gt;account (계좌)&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 19px;&quot;&gt;balance (금액)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 18px;&quot;&gt;joker&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 18px;&quot;&gt;01-001-0001&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 18px;&quot;&gt;1000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 18px;&quot;&gt;batman&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 18px;&quot;&gt;01-001-0002&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 18px;&quot;&gt;2000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 처럼 계좌 테이블이 구성되어 있을 때, joker가 batman 계좌로 500원을 입금한다면 joker의 계좌에서 -500, batman 계좌에서 +500이 되어야 합니다. 그리고 아래처럼 이 작업은 2개의 Select, 2개의 Update 쿼리의 조합과 쿼리문들 사이에 개발자가 작성해놓은 프로그램의 코드가 일부 작성되어 있을 것입니다. 개발자가 의도한 것은 코딩되어 있는 4개의 쿼리가 하나의 Transaction이 되어 DB에서 모두 완벽히 수행되는 것입니다.&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;만약, Transaction 내의 SQL문 실행 중에 다른 Transaction 의 SQL 문이 동일한 Table의 Record를 접근하려 한다면, 이를 막고 먼저 실행되고 있는 Transaction 의 처리가 완료된 이후 다음 Transaction이 처리되어야 합니다.&lt;/p&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;예를 들어, joker가 의도적으로 PC 은행앱과 스마트폰 은행앱으로 동시에 500원을 batman 으로 보내는 상황을 만든다고 할지라도, PC 은행앱에서 요청한 Transaction과 스마트폰 은행앱에서 요청한 Transaction은 각각 독립적으로 실행되어야 한다는 것입니다. 두 Transaction 내의 SQL 문들이 섞여서 처리된다면 어떤 문제가 발생할지 모르기 때문입니다. 독립적으로 실행되지 않고 섞일 수 있다면 joker의 돈은 500원만 줄었는데 batman의 돈은 1000원이 늘어나는 상황이나 joker의 돈은 1000원이 줄었는데 batman의 돈은 500원만 늘어나는 상황들이 발생할 수도 있구요. 다시 말해서, &lt;span style=&quot;color: #006dd7;&quot;&gt;Database System 에서&amp;nbsp;Transaction의 특징 중 각 Transaction은 독립적인 환경에서 실행되어야 한다는 독립성(Isolation)을 보장해주고 있기하기 때문에 이러한 처리가 가능한 것&lt;/span&gt;입입니다.&lt;/p&gt;
&lt;p data-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: #006dd7;&quot;&gt;각 query 문을 독립적으로 실행&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1625327984533&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;gt; select balance from account where id = joker
// joker의 balance 조회값 확인
&amp;gt; select balance from account where id = batman
// batman의 balance 조회값 확인
&amp;gt; update account set balance = (joker의 balance 조회값 - 500) where id = joker
&amp;gt; update account set balance = (batman의 balance 조회값 + 500) where id = batman&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: #006dd7;&quot;&gt;각 query 문을 하나의 transaction으로 묶어서 실행 (=transaction의 ACID 를 보장)&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1625327996870&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;gt; begin
&amp;gt; select balance from account where id = joker
// joker의 balance 조회값 확인
&amp;gt; select balance from account where id = batman
// batman의 balance 조회값 확인
&amp;gt; update account set balance = (joker의 balance 조회값 - 500) where id = joker
&amp;gt; update account set balance = (batman의 balance 조회값 + 500) where id = batman
&amp;gt; commit&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;Redis &lt;span style=&quot;color: #000000;&quot;&gt;에서의&amp;nbsp;&lt;/span&gt;Transaction&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Redis 에서 Transaction 을 말하면 보통 Multi 와 Exec 명령의 조합으로 이루어진 Operation을 의미하며, 하나의 Single Isolation Operation 처리가 가능하도록 제공하는 것이 목적&lt;/span&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Transaction은 (Lua script를 제외하고) 일반적으로 두가지 형태로 사용하는데, &lt;span style=&quot;color: #006dd7;&quot;&gt;Multi + Exec 조합&lt;/span&gt;과 &lt;span style=&quot;color: #006dd7;&quot;&gt;Watch + Multi + Exec 조합&lt;/span&gt;의 형태입니다. 각 조합에 대한 방법을 설명을 하기 이전에, 우선 Transaction 관련 명령들의 역할을 소개하면 아래와 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Multi&lt;/span&gt; 명령 : &lt;span&gt;Transaction 시작&lt;/span&gt;을 나타내는 명령&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Exec&lt;/span&gt; 명령 : &lt;span&gt;Transaction 작성을 마치고 내용들을 실행&lt;/span&gt;하는 명령&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Discard&lt;/span&gt; 명령 : &lt;span&gt;Transaction 을 취소&lt;/span&gt;시키는 명령&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Watch&lt;/span&gt; 명령 : Transaction 이 마무리 되는 명령(Exec, Discard)이 실행되기 이전에 &lt;span&gt;모니터링 중이던 key 의 변경사항이 있다면 반영하지 않도록 하는 사전에 등록&lt;/span&gt;해두는 명령 (=마치 Exec 명령 앞에 IF 조건문을 사용하는 형태)&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Unwatch&lt;/span&gt; 명령 : Transaction 이 마무리 되는 명령(Exec, Discard)이 실행되기 이전에 &lt;span&gt;모니터링 중이던 key 를 모니터링하던 Watch의 처리를 중단&lt;/span&gt;하는 명령 (=Exec, Discard 명령은 Unwatch 명령을 포함함)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;Multi&lt;/span&gt; + &lt;span style=&quot;color: #f89009;&quot;&gt;Exec&lt;/span&gt; or &lt;span style=&quot;color: #f89009;&quot;&gt;Discard&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;587&quot; data-origin-height=&quot;393&quot; width=&quot;574&quot; height=&quot;384&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/s1bKy/btq8IgIhI9K/H9GpPyoG6DmX6bqwvMp9x1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/s1bKy/btq8IgIhI9K/H9GpPyoG6DmX6bqwvMp9x1/img.png&quot; data-alt=&quot;Redis Transaction (Multi + Exec)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/s1bKy/btq8IgIhI9K/H9GpPyoG6DmX6bqwvMp9x1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fs1bKy%2Fbtq8IgIhI9K%2FH9GpPyoG6DmX6bqwvMp9x1%2Fimg.png&quot; data-origin-width=&quot;587&quot; data-origin-height=&quot;393&quot; width=&quot;574&quot; height=&quot;384&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Redis Transaction (Multi + Exec)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Multi 명령 이후, &lt;span style=&quot;color: #006dd7;&quot;&gt;각 명령들은 Atomic 하게 진행되므로 Transaction이 진행되는 동안 다른 명령이 끼어들 수 없습니다&lt;/span&gt;. Multi 명령 이후, &lt;span style=&quot;color: #006dd7;&quot;&gt;Exec에 의해 한번에 실행될 때 만약 Multi ~ Exec 명령 이내의 어느 명령어가 실패하는 경우가 발생하더라도 나머지 명령은 실행&lt;/span&gt;될 수 있습니다. (이 말은 Database의 Transaction 처럼 Transaction내 명령들을 모두 반영하거나 모두 반영하지 않거나를 보장하지 않는다는 말입니다) Multi + Exec 명령으로 만들어진 Transaction은 &lt;span style=&quot;color: #006dd7;&quot;&gt;일부 실패하더라도 롤백(Rollback)이 되지 않습니다.&lt;/span&gt; 이 점이 DB의&amp;nbsp; Transaction과 다른 점입니다. Redis는 태생자체가 롤백을 지원하지 않지만 만약, &lt;span style=&quot;color: #ee2323;&quot;&gt;Watch 를 사용한다면 변경사항을 반영하기 이전에 처리 되는 중간에 참조하는 값에 대한 변화가 있었는지 체크하고 반영을 하지 않도록 처리하는 로직을 추가&lt;/span&gt;할 수는 있습니다. Watch에 대한 설명이 조금 어려운 것 같지만, Watch에 대해서는 아래에서 다시 설명합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;처리 순서&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Multi 명령&lt;/span&gt; 실행 이후의 다음 명령들은 Queue에 넣는다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;(한번에 실행될) 명령&lt;/span&gt;들(ex, &lt;span style=&quot;color: #865b00;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Get, Lpush, Set&lt;/span&gt;)&lt;/span&gt;을 Queue에 넣는다.&lt;/li&gt;
&lt;li&gt;실행할 경우,
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Exec 명령&lt;/span&gt; 실행으로, Queue의 모든 명령들이 실행되고 Trasnaction을 종료한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;실행하지 않을 경우,
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Discard 명령&lt;/span&gt; 실행으로 Queue는 모두 명령들을 비우고 Transaction을 종료한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;처리 예시&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1625328175536&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;MULTI
val1 = GET key1 // 코드 구현부 내용
val1 = val1 + 9 // 코드 구현부 내용
SET key1 $val1  // Multi/Exec 내에 있는 key들은 외부에서 변경이 불가능하다. (외부에서 key1 변경 불가능)
EXEC&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;Watch&lt;/span&gt; + &lt;span style=&quot;color: #f89009;&quot;&gt;Multi&lt;/span&gt; + &lt;span style=&quot;color: #f89009;&quot;&gt;Exec&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;749&quot; data-origin-height=&quot;465&quot; width=&quot;659&quot; height=&quot;409&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rgKBK/btq8Hfbwr2Q/xagdZi47jvwXgUbZ1NKvuK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rgKBK/btq8Hfbwr2Q/xagdZi47jvwXgUbZ1NKvuK/img.png&quot; data-alt=&quot;Redis Transaction Optimistic Locking (Watch + Multi + Exec)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rgKBK/btq8Hfbwr2Q/xagdZi47jvwXgUbZ1NKvuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrgKBK%2Fbtq8Hfbwr2Q%2FxagdZi47jvwXgUbZ1NKvuK%2Fimg.png&quot; data-origin-width=&quot;749&quot; data-origin-height=&quot;465&quot; width=&quot;659&quot; height=&quot;409&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Redis Transaction Optimistic Locking (Watch + Multi + Exec)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Transaction (Multi + Exec) 처리에서 &lt;span style=&quot;color: #ee2323;&quot;&gt;Watch는 (CAS와 유사한 방법으로) Optimistic Lock 기능이 추가&lt;/span&gt;됩니다. Transaction 처리에서 &lt;span style=&quot;color: #ee2323;&quot;&gt;Watch는 (Redis에서 처리하려는 Transaction에 포함된 키의 수정을 시도함으로써) Race Condition 상황을 발생시키는 다른 Client들의 요청들은 모두 실패&lt;/span&gt;로 만들어버립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Muti + Exec 조합에서 Watch 가 추가되면, 위 그림에서 보면 Multi + Exec 명령 이전에 Watch 명령이 추가되어 있는데, Watch A 명령은 Unwatch 명령을 만날 때까지 A 키 값의 변경사항이 있는지를 모니터링하다가, Exec 이전에 변경사항이 있다면 Transaction 실행을 하지 말고(=중단시키고) Unwatch 명령을 실행하라는 의미를 가지고 있습니다. 위 그림에서 Watch 로 인해 Transaction 처리가 어떻게 달라질 수 있는지 3가지 case를 살펴보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;case1&lt;/b&gt; &lt;/span&gt;은 Client X의 Transaction이 실행되기 전에 &quot;Transaction 내에 포함된 A키 값을 변경&quot;시키는 상황입니다. 이 경우, 먼저 실행된 Incr A 명령이 반영되고 그 뒤에 Transaction 내용은 모두 무효화됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1625330528894&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Client X 에서 실행한 명령

&amp;gt; watch a
OK
&amp;gt; incr a
&quot;1&quot;
&amp;gt; multi 
OK
&amp;gt; incr a 
QUEUED
&amp;gt; exec
(nil) 	// Client X의 &quot;incr a&quot;가 실행이 되지 않았기 때문에 (nil)이 출력됨
&amp;gt; get a
&quot;1&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;case2&lt;/b&gt;&amp;nbsp;&lt;/span&gt;는 Client X의 Transaction이 시작되기 전에 Client Y가 &quot;Client X의 Transaction 내에 포함된 A키 값을 변경&quot;시키는 상황입니다. 이 경우,&amp;nbsp;먼저 실행된 Client Y의 Incr A 명령이 반영되고 그 뒤에 Client X의 Transaction 내용은 모두 무효화됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1625330422236&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Client X 에서 실행한 명령

&amp;gt; watch a
OK
&amp;gt; multi // 이 명령을 실행하기 이전에 Client Y 에서 &quot;incr a&quot; 를 실행
OK
&amp;gt; incr a 
QUEUED
&amp;gt; exec
(nil) 	// Client X의 &quot;incr a&quot;가 실행이 되지 않았기 때문에 (nil)이 출력됨
&amp;gt; get a
&quot;1&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;case3&lt;/b&gt;&lt;/span&gt;&amp;nbsp;은 Client X의 Transaction이 완료되기 전에 Client Y가 &quot;Client X의 Transaction 내에 포함된 A키 값을 변경&quot;시키는 상황입니다. 이 경우,&amp;nbsp;먼저 실행처리가 완료된 Client Y의 Incr A 명령이 반영되고 실행처리가 완료되지 않은 Client X의 Transaction 내용은 모두 무효화됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1625330384107&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Client X 에서 실행한 명령

&amp;gt; watch a
OK
&amp;gt; multi 
OK
&amp;gt; incr a // 이 명령을 실행하기 이전에 Client Y 에서 &quot;incr a&quot; 를 실행
QUEUED
&amp;gt; exec
(nil) 	// Client X의 &quot;incr a&quot;가 실행이 되지 않았기 때문에 (nil)이 출력됨
&amp;gt; get a
&quot;1&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;처리 순서&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Watch 명령이후 특정 키 모니터링을 시작&lt;/span&gt;하고, &lt;span style=&quot;color: #006dd7;&quot;&gt;Exec(Unwatch 포함) or Discard 명령(Unwatch 포함) or Unwatch 명령 이전&lt;/span&gt;까지 변경사항을 확인한다.
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Multi 명령&lt;/span&gt; 실행 이후의 다음 명령들은 Queue에 넣는다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;(한번에 실행될) 명령&lt;/span&gt;들(ex, &lt;span style=&quot;color: #006dd7;&quot;&gt;Get, Lpush, Set&lt;/span&gt;)을 Queue에 넣는다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Exec 명령&lt;/span&gt; 실행으로, Queue의 모든 명령들이
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Watch 명령이후 모니터링하던 &lt;span style=&quot;color: #006dd7;&quot;&gt;키 변경사항이 있었다면, 모든 변경사항이 반영되지 않는다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Watch 명령이후 모니터링하던 &lt;span style=&quot;color: #006dd7;&quot;&gt;키 변경사항이 없었다면, 모든 변경사항이 반영된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;처리 예시&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1625328190568&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;WATCH key1
val1 = GET key1 // 코드 구현부 내용
val1 = val1 + 9 // Watch~Exec 내의 명령들이 Isolation Operation 처리가 되진 않지만, 모니터링을 하고 있으므로 변경을 감지한다.
MULTI
SET key1 $val1
EXEC&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Redis&lt;/span&gt; 에서의&amp;nbsp;&lt;span style=&quot;color: #ee2323;&quot;&gt;Transaction&lt;/span&gt; Q&amp;amp;A&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Q. Check-And-Set or Compare-And-Swap (CAS) ?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Atomic 명령이 가능하게 하는 알고리즘을 의미합니다. 멀티 스레드, 멀티 코드 등의 병렬 구조를 위한 알고리즘&lt;/span&gt;으로, 요약하면, 여러개의 스레드 중 하나의 스레드에 저장된 값과 메인 스레드에 저장된 값을 비교하여 일치한다면 메인 스레드의 값을 새로운 값으로 세팅(교체)하고, 틀리다면 실패 후 다시 시도함으로써 여러개의 스레드이더라도 모두 하나의 값을 참조할 수 있도록 제공하는 방법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Q. Race Condition ?&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘 이상의 프로세스, 스레드가 하나의 데이터에 접근하려는 경우를 말하며, 이 경우 먼저 집입한 스레드가 해당 데이터 접근에 Lock을 걸어버리면서 해결합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Q. Optimistic Lock ?&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Begin, Modify, Validate, Commit/Rollback 단계를 거쳐서 여러 Transaction 들이 각각 독립적으로 실행이 한번에 되는 것을 말합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Q. Redis 공식 홈페이지에서 설명하는 Transaction ?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redis 사이트에서는 Redis에서 말하는 Transaction이 무엇인지를 정의하고 있는데, 몇가지 내용을 발췌하면 아래와 같습니다.&lt;/p&gt;
&lt;p data-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: #006dd7;&quot;&gt;&quot;All the commands in a transaction are serialized and executed sequentially. This guarantees that the commands are executed as a single isolated operation.&quot;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 문장은 Transaction 내의 모든 명령은 순서대로 실행되며, 고립된 상태로 처리되어 중간에 다른 명령이 끼어들 수 없음을 의미합니다.&lt;/p&gt;
&lt;p data-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: #006dd7;&quot;&gt;&quot;Either all of the commands or none are processed, so a Redis transaction is also atomic.&quot;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 문장은 모든 명령이 실행되는 것도, 모든 명령이 실행되지 않는 것도 가능하기 때문에, Redis Transaction을 atomic 이라고 부르기도 한다는 것을 의미합니다. 아래가 모든 명령이 실행되지 않을 경우에 대한 예시코드입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1625329620638&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 아래 명령을 실행할 경우, LRANGE에 대한 문법이 틀려서 실패했지만, LPUSH를 실행하지 않는다.

&amp;gt; MULTI
OK
&amp;gt; LPUSH p 1 2 3 4 5
QUEUED
&amp;gt; LRANGE 0 -1 // 정상은 LRANGE p 0 -1 이므로 에러가 발생한다.
(error) ERR wrong number of arguments for 'lrange' command
&amp;gt; EXEC
(error) EXECABORT Transaction discard because of previous errors.
&amp;gt; get p
(nil)&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: #006dd7;&quot;&gt;&quot;It's important to note that even when a command fails, all the other commands in the queue are processed &amp;ndash; Redis will not stop the processing of commands.&quot;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 문장은 Transaction 내 명령 하나가 실패하더라도 다른 명령들이 처리되니, 이 점을 잘 염두해두고 사용해야 한다는 것을 의미합니다. 아래가 예시코드입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1625329641318&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 아래 명령을 실행할 경우, 2개는 정상처리되고 2개는 에러가 발생한다.

&amp;gt; SET a abc
OK
&amp;gt; SET b 1
OK
&amp;gt; MULTI
OK
&amp;gt; INCR a
QUEUED
&amp;gt; LPOP a
QUEUED
&amp;gt; INCR b
QUEUED
&amp;gt; LPOP c
QUEUED
&amp;gt; EXEC
1) (error) ERR value is not an integer or out of range // a는 integer 타입이 아니라서 증가시킬 수가 없다.
2) (error) WRONGTYPE Oeration against a key holding the wrong kind of value // a는 List 구조가 아니니 lpop 명령을 사용할수 없다.
3) &quot;2&quot;
4) (nil)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Redis) Transaction 설명 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://redis.io/topics/transactions&quot;&gt;링크&lt;/a&gt;, &lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://dark0096.github.io/redis/2018/10/27/redis-transaction.html&quot;&gt;번역링크&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Redis) Command 설명 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://redis.io/commands/multi&quot;&gt;MULTI&lt;/a&gt;, &lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://redis.io/commands/exec&quot;&gt;EXEC&lt;/a&gt;, &lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://redis.io/commands/discard&quot;&gt;DISCARD&lt;/a&gt;, &lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://redis.io/commands/watch&quot;&gt;WATCH&lt;/a&gt;, &lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://redis.io/commands/unwatch&quot;&gt;UNWATCH&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Redis) Redis에서 Transaction Rollback이 필요없는 이유 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://redislabs.com/blog/you-dont-need-transaction-rollbacks-in-redis/&quot;&gt;설명&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Blog) CAS 알고리즘 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://javaplant.tistory.com/23&quot;&gt;링크&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Blog) Redis Transaction - MULTI/EXEC + WATCH (Optimistic Lock) 설명 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://dol9.tistory.com/209&quot;&gt;링크&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Blog) Redis Transaction - WATCH/UNWATCH 설명 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://minholee93.tistory.com/entry/Redis-Transaction&quot;&gt;링크&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Blog) Redis Transaction - UNWATCH 사용 예 코드 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://www.programmersought.com/article/584038348/&quot;&gt;링크&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Blog) Redis Transaction - MULTI/EXEC, DISCARD - java 예제 코드 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://blog.naver.com/willygwu2003/130172706093&quot;&gt;링크&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Stackoverflow) Redis and Data Integrity &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://stackoverflow.com/questions/14682470/redis-and-data-integrity&quot;&gt;링크&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(IBM) ACID properties of transactions &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://www.ibm.com/docs/en/cics-ts/5.4?topic=processing-acid-properties-transactions&quot;&gt;링크&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(PPT) Redis Transaction - Optimistic locking &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://speakerdeck.com/lehmannro/redis-memcached-on-steroids?slide=66&quot;&gt;링크&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>dev-tips/redis</category>
      <category>exec</category>
      <category>multi</category>
      <category>optimistic locking</category>
      <category>redis</category>
      <category>transaction</category>
      <category>watch</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/205</guid>
      <comments>https://caileb.tistory.com/205#entry205comment</comments>
      <pubDate>Sun, 4 Jul 2021 02:19:36 +0900</pubDate>
    </item>
    <item>
      <title>AWS CloudFront - SignedURL 관련 CORS 에러</title>
      <link>https://caileb.tistory.com/204</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;AWS CloudFront - SignedURL 관련 CORS 에러&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;에러 (error)&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; Access to XMLHttpRequest at 'URL A' from origin 'URL B' hash been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2236.0&quot; data-origin-height=&quot;182.0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ydvbc/btqTxBUHaPq/wexmgKmk4iv2VTSLWBHnkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ydvbc/btqTxBUHaPq/wexmgKmk4iv2VTSLWBHnkk/img.png&quot; data-alt=&quot;[Browser Console Log]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ydvbc/btqTxBUHaPq/wexmgKmk4iv2VTSLWBHnkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYdvbc%2FbtqTxBUHaPq%2FwexmgKmk4iv2VTSLWBHnkk%2Fimg.png&quot; data-origin-width=&quot;2236.0&quot; data-origin-height=&quot;182.0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[Browser Console Log]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;[참고] &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;XMLHttpRequest ? Web Browser가 Web Server로 페이지 전환 없이 데이터를 요청하기 위한 방법을 제공하는 Web API이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Access-Control-Allow-Origin ? Web Server의 Http Response Header에 포함된 필드 중 하나로, 접근 허용할 Origin들이 포함된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;CORS ? Cross-Origin Resource Sharing의 약자로, 서버에서 자신의 Resource를 다른 Origin들과 공유할지 여부를 설정할 때 사용된다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;환경 (environment)&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;OS : macOS&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;Browser : Chrome&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;Server : Go&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;Client : ReactJS&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;상황 (Situation)&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;개발 중인 &lt;span style=&quot;color: #006dd7;&quot;&gt;Server에서 CloudFront Signed URL을 생성한 후, 개발 중인 Client에서 해당 URL을 받아서 접근&lt;/span&gt;하려고 했다. 이때, Signed URL 에 포함되는 정책 내용으로는 특정 시간 동안만 제한하는 기능을 사용하도록 하였다. 아래는 실제로 사용된 코드의 일부분이고, 코드를 따라 최종으로 생성된 &lt;span style=&quot;color: #006dd7;&quot;&gt;Signed URL은 Client가 Signed URL이 만들어진 생성시점부터 1시간 동안 접근이 가능&lt;/span&gt;하게 된다. 하지만, Client에서는 Server로부터 받은 &lt;span style=&quot;color: #006dd7;&quot;&gt;해당 Signed URL을 통해 접근을 시도했지만, 수십번을 다시 시도해도 위처럼 CORS 에러가 발생&lt;/span&gt;하였다.&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;maxima&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// go language

import (
	cf &quot;github.com/aws/aws-sdk-go/service/cloudfront/sign&quot;
)

now := time.Now()
policy := &amp;amp;cf.Policy{
   Statements: []cf.Statement{
      {
         Resource: rawURL,
         Condition: cf.Condition{
            DateGreaterThan: cf.NewAWSEpochTime(now),
            DateLessThan: cf.NewAWSEpochTime(now.Add(1 * time.Hour)),
         },
      },
   },
}

signer := cf.NewURLSigner(&quot;Key ID&quot;, &quot;Private Key&quot;)
signedURL, err := signer.SignWithPolicy(rawURL, policy)&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;원인&amp;nbsp; (Cause)&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Server의 시간과 CloudFront의 시간의 기준이 동일하지 않았다는 것이 주요 원인&lt;/span&gt;이다. 만약, 개발 중인 Server와 CloudFront가 동일한 시스템 내에서 실행하고 있다면 시간이 완전히 일치할 것이다. 하지만, 지금 개발 환경을 보면 Server는 로컬에서 개발되고 있고, CloudFront는 AWS 위에서 운영되고 있다. 다시 말해서 서로 다른 시스템 위에서 실행되고 있기 때문에, 두 시스템 간의 시간도 조금은 차이가 있을 수 있는 것이다. 실제로 테스트한 결과 &lt;span style=&quot;color: #006dd7;&quot;&gt;약 1초 이내의 차이&lt;/span&gt;가 나는 것을 확인할 수 있었고, 이 때문에 에러가 발생하고 있는 것이었다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;정리하면, &lt;span style=&quot;color: #006dd7;&quot;&gt;현재 Server보다 CloudFront가 조금 더 빠른 시간으로 세팅되어 실행되는 상황&lt;/span&gt;이었고, 이 상황에서는 Server 가 생성한 Signed URL의 접근 가능한 시작시간(=DateGreaterThan)이 CloudFront에서는 아직 지나가지 않은 시간일 수 있는 상황이 발생할 수 있다는 것이다. 예를 들어, Server 시스템의 시간이 CloudFront 시스템의 시간보다 3초가 빠르다면 아래와 같은 상황이 발생할 수 있다는 것이다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 171px;&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;&lt;b&gt;Server 시스템의 현재 시간&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;&lt;b&gt;CloudFront 시스템의 현재 시간&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt; 2021-01-14 14:00:15 &amp;lt;-- Server는 이때부터 &lt;b&gt;~&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;2021-01-04 14:00:12 &amp;lt;-- 이때 접근하면 에러 발생&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt; 2021-01-14 14:00:16&lt;/span&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;2021-01-04 14:00:13 &amp;lt;-- &lt;span style=&quot;color: #333333;&quot;&gt;이때.. &lt;span style=&quot;color: #ee2323;&quot;&gt;(이때 접근해서 에러 발생)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt; 2021-01-14 14:00:17&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;2021-01-04 14:00:14 &amp;lt;-- &lt;span style=&quot;color: #333333;&quot;&gt;이때 접근하면 에러 발생&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt; ...&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt; 2021-01-14 14:00:15 &amp;lt;-- Client는 이때부터 ~&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt; 2021-01-14 15:00:15 &amp;lt;-- 이때까지&amp;nbsp;유효한 Signed URL 을 생성&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt; 2021-01-14 14:00:16&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;2021-01-14 15:00:16&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt; 2021-01-14 14:00:17&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;2021-01-14 15:00:17&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt; ...&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;2021-01-14 15:00:18&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt; 2021-01-14 15:00:15 &amp;lt;-- 이때까지 CloudFront로 접근이 가능&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;위 표에서 나타난 것처럼 Server의 시간이 14:00:15 일때, CloudFront의 시간은 14:00:12 일 수 있다는 것이다. 그리고 해당 Signed URL을 사용해서 재빠르게 CloudFront로 접근을 시도한다면 유효시간이 아니기 때문에, 아래 그림처럼 실패나는 상황이 만들어지게 되는 것이다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;666.0&quot; data-origin-width=&quot;1552.0&quot; data-origin-height=&quot;464.0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cNIN84/btqTADErUa5/t7iSwl96OgjobARkHgu6U1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cNIN84/btqTADErUa5/t7iSwl96OgjobARkHgu6U1/img.png&quot; data-alt=&quot;[ error 발생해서 실패한 응답을 받는 상황 ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cNIN84/btqTADErUa5/t7iSwl96OgjobARkHgu6U1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNIN84%2FbtqTADErUa5%2Ft7iSwl96OgjobARkHgu6U1%2Fimg.png&quot; width=&quot;666.0&quot; data-origin-width=&quot;1552.0&quot; data-origin-height=&quot;464.0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ error 발생해서 실패한 응답을 받는 상황 ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;추가로, &lt;span style=&quot;color: #006dd7;&quot;&gt;현재 에러는 정확히 따지자면 CORS 관련 에러가 아닌, CloudFront에서 Policy 내용을 검증하다가 발생한 에러&lt;/span&gt;이다. 현재, &lt;span style=&quot;color: #006dd7;&quot;&gt;CloudFront 에서는 HTTP Status Code 403 과 Response 로 Access Denied 라는 XML 데이터를 반환&lt;/span&gt;하고 있었지만, &lt;span style=&quot;color: #006dd7;&quot;&gt;Chrome Browser Console 에서는 이러한 메시지가 나오지 않고 위 처럼 CORS 에러를 자동으로 내뱉고 있을 뿐&lt;/span&gt;이다. 그래서 실제로, 현재 문제를 해결하기 위해 CORS 부분만을 의심했었고, 이 때문에 CloudFront 설정, S3 설정, Client 요청코드에서 의심되는 부분들만을 검토하면서 원인을 파악하는데 시간을 많이 버렸었다. 참고로, CloudFront 에서 실패가 발생해서 403 코드와 Acces Denied 가 반환되는 내용은 Chrome Browser로는 확인이 되지 않기 때문에, 로컬에 별도의 proxy program(ex, mitmproxy)를 세팅한 후, 이를 통해 교환되는 메시지를 보고 확인을 했다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;해결 (Solution)&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;현재 상황을 해결하기 위한 방법이 아래처럼 몇가지 있을 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;시스템 시간을 직접 조정&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Server 시스템 시간을 CloudFront 시스템 시간보다 앞땅기는 것&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;CloudFront 시스템 시간을 Server 시스템 시간보다 뒤로 미루는 것&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Signed URL 의 정책시간을 조정&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;DateGreaterThan 시간을 두 시스템 간의 차이만큼 앞땅기는 것&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;DateGreaterThan 시간을 사용하지 않는 것&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;사실, 시스템이 서로 독립적이라면 시간의 차이는 (밀리초, 마이크로초 단위로) 조금이라도 분명 존재할 수 밖에 없다. 그리고 현재 개발 중인 프로그램은 Server에서 Signed URL로 접근하는 시점이 Signed URL을 생성한 직후부터 1시간으로 제한할 것이기 때문에, 이전의 시간까지 CloudFront에서 반드시 검증할 필요는 없다. 따라서, 위에 소개한 방법들 중에 4번째 방법처럼 &lt;span style=&quot;color: #006dd7;&quot;&gt;접근 가능을 위한 시작 시간을 사용하지 않도록 DateGreaterThan 항목을 제거함으로서&amp;nbsp;해결&lt;/span&gt;하였다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;606.0&quot; data-origin-width=&quot;1358.0&quot; data-origin-height=&quot;460.0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wQYre/btqTwXqbYFW/9wdhaXk2xQk7NMBPdKGkK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wQYre/btqTwXqbYFW/9wdhaXk2xQk7NMBPdKGkK0/img.png&quot; data-alt=&quot;[ error 없이, 성공적인 응답을 받는 상황 ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wQYre/btqTwXqbYFW/9wdhaXk2xQk7NMBPdKGkK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwQYre%2FbtqTwXqbYFW%2F9wdhaXk2xQk7NMBPdKGkK0%2Fimg.png&quot; width=&quot;606.0&quot; data-origin-width=&quot;1358.0&quot; data-origin-height=&quot;460.0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ error 없이, 성공적인 응답을 받는 상황 ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;참고 (Reference)&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;AWS CloudFront 서명된 URL 사용하기&amp;nbsp;&lt;u&gt;&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/AmazonCloudFront/latest/DeveloperGuide/private-content-creating-signed-url-custom-policy.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;color: #7e98b1;&quot;&gt;&lt;span style=&quot;color: #7e98b1;&quot;&gt;Link&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>error/[err] server</category>
      <category>ACCESS DENIED</category>
      <category>aws</category>
      <category>CloudFront</category>
      <category>CORS</category>
      <category>error</category>
      <category>GO</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/204</guid>
      <comments>https://caileb.tistory.com/204#entry204comment</comments>
      <pubDate>Fri, 15 Jan 2021 01:12:38 +0900</pubDate>
    </item>
    <item>
      <title>macOS 기본 명령어 (File, Folder 관련)</title>
      <link>https://caileb.tistory.com/203</link>
      <description>&lt;b&gt;macOS 기본 명령어&lt;/b&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: center;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;(File, Folder 관련)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;터미널 사용이 익숙하지 않다면 명령어들이 너무 많아 헷갈리곤 합니다. 시간이 지남에 따라 자연스럽게 알게 되겠지만, 터미널 환경이 어색하다면 정말 많이 사용하는 기본적인 명령어 몇가지는 메모장에 적어두고 사용하는 것이 마음에 편할 것입니다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/dNLpYo/btqNMhBgHuw/AkDLzj8M6xA43Fu1Zutr71/img.png&quot; width=&quot;337.0&quot; data-origin-width=&quot;870.0&quot; data-origin-height=&quot;462.0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dNLpYo/btqNMhBgHuw/AkDLzj8M6xA43Fu1Zutr71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dNLpYo/btqNMhBgHuw/AkDLzj8M6xA43Fu1Zutr71/img.png&quot; data-alt=&quot;[ 터미널 기본 명령어 구분 ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dNLpYo/btqNMhBgHuw/AkDLzj8M6xA43Fu1Zutr71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdNLpYo%2FbtqNMhBgHuw%2FAkDLzj8M6xA43Fu1Zutr71%2Fimg.png&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/dNLpYo/btqNMhBgHuw/AkDLzj8M6xA43Fu1Zutr71/img.png&quot; width=&quot;337.0&quot; data-origin-width=&quot;870.0&quot; data-origin-height=&quot;462.0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ 터미널 기본 명령어 구분 ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;터미널 명령어는 위 그림처럼 크게 &lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;macOS에서 제공하는 기본 명령어들과 그외 나머지 명령어들로 두가지 영역으로 구분&lt;/span&gt;할 수 있습니다. 두 영역의 명령어들 중에 이 글에서는 macOS 에서 자주 사용하는 기본 명령어들 몇가지들에 대해서만 소개합니다. 참고로 만약, nodejs 개발자라면 node를 설치한 후 npm 혹은 node 명령어를 사용할 것이고, android 개발자라면 android-sdk를 설치한 후 adb 혹은 logcat 등의 명령어들을 사용하게 될 것인데, 이때 외부 프로그램, 라이브러리, 툴 등을 설치해야만 사용 가능한 명령어들은 macOS 기본 명령어가 아닌 영역의 명령어들로 구분됩니다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot;&gt;&lt;li&gt;&lt;b&gt;macOS 기본 명령어&lt;/b&gt; : &lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;mac PC를 구입한 이후 아무것도 설치하지 않아도 사용&lt;/span&gt; 가능한 명령어를 의미 (ex, pwd, cd, ls, cp, touch 등)&lt;/li&gt;&lt;li&gt;&lt;b&gt;(macOS 기본 명령어가 아닌) 외부 프로그램 명렁어&lt;/b&gt; : 외부 &lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;프로그램을 직접 설치한 후 사용&lt;/span&gt; 가능한 명령어를 의미 (ex, brew, apt-get, npm, adb, logcat 등)&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/vnltD/btqNNiT3dxm/A4gXg3O3WWesR8mcqvkTm0/img.png&quot; width=&quot;487.0&quot; data-origin-width=&quot;1124.0&quot; data-origin-height=&quot;596.0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vnltD/btqNNiT3dxm/A4gXg3O3WWesR8mcqvkTm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vnltD/btqNNiT3dxm/A4gXg3O3WWesR8mcqvkTm0/img.png&quot; data-alt=&quot;[ 터미널 기본 명령어 영역별 구분 ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vnltD/btqNNiT3dxm/A4gXg3O3WWesR8mcqvkTm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvnltD%2FbtqNNiT3dxm%2FA4gXg3O3WWesR8mcqvkTm0%2Fimg.png&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/vnltD/btqNNiT3dxm/A4gXg3O3WWesR8mcqvkTm0/img.png&quot; width=&quot;487.0&quot; data-origin-width=&quot;1124.0&quot; data-origin-height=&quot;596.0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ 터미널 기본 명령어 영역별 구분 ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;그리고, macOS 기본명령어는 위 그림처럼 크게 영역을 몇가지로 구분해두고 사용하는 것이 이해하기 편할 것입니다. 기본 명령어의 대부분이 &lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;파일과 폴더를 제어하는 명령어&lt;/span&gt;들입니다. 따라서, 정말 많이 사용하는 명령어들은 파일과 폴더 관련된 명령어들이기 때문에, 이 명령어들이 익숙해지면, 그 이후에 사용자가 필요한 기능을 하는 명령어들을 하나씩 찾아서 사용해나가는 것이 좋습니다. 참고로, 이 글에서는 네트워크 명령어(ex, ifconfig)처럼 컴퓨터에 대해 깊이 있는 개념들이 필요한 상태에서 사용해야 명령어들은 설명에서 제외했습니다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot;&gt;&lt;li&gt;&lt;b&gt;파일&lt;/b&gt; : 파일 추가, 삭제, 복사, 내용보기, 내용변경하기 등&lt;/li&gt;&lt;li&gt;&lt;b&gt;폴더 (디렉토리)&lt;/b&gt; : 폴더 추가, 삭제, 복사, 이동, 목록보기 등&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;자주 사용되는 파일과 폴더 관련 명령어들에 대해 설명하겠습니다. 아래 명령어들을 사용해서 직접 PC에 파일을 추가하고 삭제하고 새로운 폴더를 만들고 이동시키는 등의 작업들을 하면서&lt;span style=&quot;color: rgb(51, 51, 51);&quot;&gt; 30분 정도 하나씩 테스트하다보면 금방 외워질 것입니다. &lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;아래 명령어들이 몇가지 안되지만, 아래 명령어들은 모든 개발자들이 터미널을 사용할 때 실행하는 50%이상의 명령어들이라고 할 수 있을 정도로 자주 사용되는 명령어&lt;/span&gt;들입니다.&lt;/span&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(51, 51, 51);&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: rgb(239, 111, 83);&quot;&gt;pwd&lt;/span&gt;&lt;/b&gt; (print working directory) : &lt;b&gt;현재위치보기&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;pwd 명령어는 &lt;span style=&quot;color: rgb(239, 83, 105);&quot;&gt;터미널에서 현재 위치&lt;/span&gt;가 어디인지를 나타냅니다. 평소에 윈도우나 macOS에서도 마우스를 주로 사용했다면 충분히 헷갈릴 수 있는 부분입니다. 터미널에서는 PC 화면에서 보이는 모든 파일과 폴더들에 접근할 수 있습니다. PC는 처음에 마우스로 조작할 수 있는 형태가 아니었습니다. 명령어들을 치면서 조작해야하는 형태였고, 사람들이 사용하기가 어렵기 때문에 마우스라는 것을 만들었고 마우스로 클릭이 가능한 UI들이 개발된 것입니다. 따라서, &quot;터미널&quot;은 명령어로 컴퓨터를 조작하는 형태이고, &quot;PC 화면&quot;은 마우스로 화면을 조작하는 형태라고 말할 수 있습니다.&lt;/p&gt;&lt;pre data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;$ pwd
/Users/joker/Desktop/lec&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: rgb(239, 111, 83);&quot;&gt;ls&lt;/span&gt;&lt;/b&gt; (list) : &lt;b&gt;목록보기&lt;/b&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;ls는 &lt;span style=&quot;color: rgb(239, 83, 105);&quot;&gt;현재 위치에 존재하는 파일의 목록&lt;/span&gt;을 보여주는 명령어입니다. 현재 위치를 pwd 명령어로 확인했다면 그 위치가 현재 위치이고, 바로 그 위치에서 ls 명령을 실행하면 그 위치에 존재하는 파일 목록들이 모두 출력됩니다. 대부분의 명령어들은 명령어 옆에 옵션(option)을 사용해서 실행하려는 명령어를 조금 다르게 사용할 수 있습니다. 옵션(option)은 말그대로 명령어를 실행할 때 사용해도 되고 사용하지 않아도 됩니다. 대부분의 명령어 옵션들은 &quot;-&quot;가 붙습니다. ls 명령어의 경우 자주 사용하는 옵션으로는 -l 과 -al 이 있습니다. 이 둘의 차이는 파일을 출력할 때, 숨김파일까지 보여줄 것인지에 대한 여부 차이뿐입니다. &lt;span style=&quot;color: rgb(239, 83, 105);&quot;&gt;&quot;ls -l&quot;은 숨김파일을 제외한 목록&lt;/span&gt;을 보여주고, &lt;span style=&quot;color: rgb(239, 83, 105);&quot;&gt;&quot;ls -al&quot;은 숨김파일을 포함한 목록&lt;/span&gt;을 보여줍니다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;참고로, 출력된 파일들 중에 파일과 디렉토리를 구분하는 방법이 있습니다. ls -l 혹은 ls -al 명령을 실행했을 때, 가장 앞에 &lt;span style=&quot;color: rgb(239, 83, 105);&quot;&gt;&quot;d&quot;라는 문자가 붙어 있는 경우 폴더(Directory)&lt;/span&gt;를 의미합니다. 만약에, &quot;-&quot;가 붙어 있다면 파일임을 의미합니다.&lt;/p&gt;&lt;pre data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;$ ls
abc aaa bbb ccc test.txt


$ ls -l
drwxr-xr-x@ 3 joker  staff     96  5 18  2020 abc
drwxr-xr-x@ 3 joker  staff     96  5 18  2020 aaa
drwxr-xr-x@ 3 joker  staff     96  5 18  2020 bbb
drwxr-xr-x@ 3 joker  staff     96  5 18  2020 ccc
-rw-r--r--  1 joker  staff    215 11  9 17:17 test.txt

$ ls -al
drwxr-xr-x@ 11 joker  staff    352 11  9 17:17 . &amp;lt;-- 현재 디렉토리
drwx------@ 24 joker  staff    768 11 18 23:06 .. &amp;lt;-- 상위 디렉토리
drwxr-xr-x@ 3 joker  staff     96  5 18  2020 abc &amp;lt;-- 현재 디렉토리의 abc 디렉토리
drwxr-xr-x@ 3 joker  staff     96  5 18  2020 aaa &amp;lt;-- 현재 디렉토리의 aaa 디렉토리
drwxr-xr-x@ 3 joker  staff     96  5 18  2020 bbb &amp;lt;-- 현재 디렉토리의 bbb 디렉토리
drwxr-xr-x@ 3 joker  staff     96  5 18  2020 ccc &amp;lt;-- 현재 디렉토리의 ccc 디렉토리
-rw-r--r--  1 joker  staff    215 11  9 17:17 test.txt &amp;lt;-- 현재 디렉토리의 test.txt 파일&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: rgb(239, 83, 105);&quot;&gt;cd&lt;/span&gt;&lt;/b&gt; (change directory) : &lt;b&gt;위치변경하기&lt;/b&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;cd는 터미널의 &lt;span style=&quot;color: rgb(239, 83, 105);&quot;&gt;현재 위치에서 다른 위치로 이동&lt;/span&gt;하는 명령어입니다. cd 뒤에 현재 위치에 있는 폴더들 중에 하나를 입력하면 그 폴더 안으로 이동됩니다. 참고로, 터미널의 어느 위치에서든 자주 사용되는 위치가 있는데, 바로 상위 디렉토리로 이동하는 경우와 홈(Home)으로 이동하는 경우입니다.&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(239, 83, 105);&quot;&gt;상위 디렉토리로 갈 때는 &quot;cd ..&quot;&lt;/span&gt;(점점)&amp;nbsp;를 입력하면 되고, &lt;span style=&quot;color: rgb(239, 83, 105);&quot;&gt;홈으로 이동할 때는 &quot;cd ~&quot;&lt;/span&gt;(물결) 을 입력하면 됩니다. 개발자가 아니라면, 홈(Home)이라는 키워드가 익숙하지 않을 수 있습니다. &lt;span style=&quot;color: rgb(239, 83, 105);&quot;&gt;홈은 현재 PC의 사용자 계정의 위치를 의미&lt;/span&gt;합니다. 현재 PC를 나말고 누군가와 같이 사용하고 있다면 계정이 여러개 존재할 수 있습니다. 예를 들어, 제 PC는 joker라는 계정과 batman이라는 계정을 만들어두었습니다. joker라는 계정으로 접속한 후 터미널에 cd ~ 를 입력하면 &quot;/Users/joker&quot; 의 위치로 이동하게 되고, batman이라는 계정으로 접속한 후 터미널에 cd ~ 를 입력하면&amp;nbsp;&quot;/Users/batman&quot; 의 위치로 이동하게 됩니다. 그리고 그 위치부터가 내가 맘대로 사용할 수 있는 공간입니다. 참고로, 바탕화면은 터미널에서 &quot;Desktop&quot;이라는 디렉토리 이름으로 접근이 가능하고, 이 Desktop 디렉토리는 홈(Home) 위치 안에 존재하므로, 만약 터미널 안에서 다른 어떤 위치에서 작업 중이었는데 &lt;span style=&quot;color: rgb(239, 83, 105);&quot;&gt;바탕화면으로 이동시키고 싶다면 &quot;cd ~/Desktop&quot;&lt;/span&gt; 명령을 실행하면 됩니다.&lt;/p&gt;&lt;pre data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;$ cd aaa
$ pwd
/Users/joker/Desktop/lec/aaa

$ cd ..
$ pwd
/Users/joker/Desktop/lec

$ cd ~
$ pwd
/Users/joker/Desktop&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: rgb(239, 83, 105);&quot;&gt;touch&lt;/span&gt;&lt;/b&gt; : &lt;b&gt;파일 생성하기&lt;/b&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;touch는 &lt;span style=&quot;color: rgb(239, 83, 105);&quot;&gt;파일을 생성&lt;/span&gt;하는 명령어입니다. touch를 사용해서 파일을 생성할 때는 확장자를 입력하는 것이 필수가 아니기 때문에 입력하지 않아도 파일이 생성됩니다. 만약, 생성하려는 파일 이름이 &quot;test.txt&quot; 혹은 &quot;test.html&quot; 처럼 확장자가 포함된 이름이었는데 실수로 이름을 &quot;test&quot;로 만들었다면, 다른 명령어(ex, mv)를 통해 이름을 변경할 수 있습니다.&amp;nbsp;&lt;/p&gt;&lt;pre data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;$ touch test.txt
$ ls
abc.txt aaa bbb ccc test.txt&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: rgb(239, 83, 105);&quot;&gt;cp&lt;/span&gt;&lt;/b&gt; (copy) : &lt;b&gt;복사하기&lt;/b&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;cp는 &lt;span style=&quot;color: rgb(239, 83, 105);&quot;&gt;파일을 복사&lt;/span&gt;하는 명령어입니다. cp를 사용해서 파일을 복사할 때는 &lt;span style=&quot;color: rgb(239, 83, 105);&quot;&gt;복사되어 새로 생성될 파일의 이름을 직접 작성&lt;/span&gt;해야합니다.&lt;/p&gt;&lt;pre data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;$ cp test.txt test2.txt
$ ls
abc.txt aaa bbb ccc test.txt test2.txt&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: rgb(239, 83, 105);&quot;&gt;mkdir&lt;/span&gt;&lt;/b&gt; (make directory) : &lt;b&gt;폴더 생성하기&lt;/b&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;mkdir은 &lt;span style=&quot;color: rgb(239, 83, 105);&quot;&gt;폴더를 생성&lt;/span&gt;하는 명령입니다. 파일을 생성할 때는 touch 명령을 사용하고 폴더를 생성할 때는 mkdir 명령을 사용하면됩니다.&lt;/p&gt;&lt;pre data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;$ mkdir ddd
$ ls
abc.txt aaa bbb ccc ddd test.txt test2.txt&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: rgb(239, 83, 105);&quot;&gt;mv&lt;/span&gt;&lt;/b&gt; (move) : &lt;b&gt;이동하기&lt;/b&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;mv는 &lt;span style=&quot;color: rgb(239, 83, 105);&quot;&gt;파일을 이동&lt;/span&gt;시키는 명령어업니다. 이 명령도 cp 명령과 마찬가지로 &lt;span style=&quot;color: rgb(239, 83, 105);&quot;&gt;복사되어 새로 생성될 파일의 이름을 직접 작성&lt;/span&gt;해야 합니다. 파일을 이동시키는 기능외에 &lt;span style=&quot;color: rgb(239, 83, 105);&quot;&gt;이름을 변경&lt;/span&gt;시키는 기능도 겸해서 사용할 수 있어 유용하게 사용할 수 있습니다. &lt;u&gt;&lt;span style=&quot;color: rgb(238, 35, 35);&quot;&gt;주의할 점은 이동시킬 때 정의한 이름과 동일한 이름의 파일이 존재한다면 기존 파일을 덮어씌워서 기존 파일이 없어져버리니 조심&lt;/span&gt;&lt;/u&gt;해서 사용해야 합니다.&amp;nbsp;&lt;/p&gt;&lt;pre data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;$ mv test.txt test2.txt
$ ls
abc.txt aaa bbb ccc ddd test2.txt &amp;lt;-- test.txt 파일은 test2.txt 파일로 덮어씌우기 되었습니다.

$ mv test2.txt ddd/d.txt
$ ls
abc.txt aaa bbb ccc ddd &amp;lt;-- test2.txt 파일은 ddd 폴더로 이동되었고, 이름은 d.txt 파일로 변경되었습니다.&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: rgb(239, 83, 105);&quot;&gt;rm&lt;/span&gt;&lt;/b&gt; : &lt;b&gt;파일/폴더 삭제하기&lt;/b&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;rm은 &lt;span style=&quot;color: rgb(239, 83, 105);&quot;&gt;파일 혹은 폴더를 삭제&lt;/span&gt;하는 명령어입니다. &lt;span style=&quot;color: rgb(239, 83, 105);&quot;&gt;rm 명령어만 사용한다면 파일만 삭제&lt;/span&gt;할 수 있습니다. 만약, &lt;span style=&quot;color: rgb(239, 83, 105);&quot;&gt;폴더를 삭제하고 싶다면 &quot;rm -rf&quot;&lt;/span&gt; 명령어를 실행해서 옵션을 추가로 작성해주어야 합니다. &quot;-rf&quot; 라는 옵션을 추가해주어야 하고, -r 과 -f 옵션 두가지를 적용시키겠다는 의미로, -r 은 recursive 라는 의미로 해당 폴더 안에 있는 내용들을 모두 재귀적으로 탐색해서 싹다 지워버리겠다는 의미이고, -f 는 force 라는 의미로 읽기 권한이 없는 파일이 없는 등의 이유로 삭제가 애매한 파일들이 있더라도 싹다 지워버리겠다는 의미입니다. &lt;u&gt;&lt;span style=&quot;color: rgb(238, 35, 35);&quot;&gt;주의할 점은 &quot;rm -rf&quot;로 삭제를 할 때 바로 해당 파일이나 폴더가 삭제되어 버리니 조심&lt;/span&gt;&lt;/u&gt;해서 사용해야 합니다.&amp;nbsp;예를 들어, 명령어 한번 실행으로 PC의 절반이 삭제되어버릴 수도 있으니 주의해야 합니다.&lt;/p&gt;&lt;pre data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;$ rm abc.txt
$ ls
aaa bbb ccc ddd &amp;lt;-- abc.txt 파일이 삭제되었습니다.

$ rm ddd &amp;lt;-- 빈 폴더가 아니기 때문에 삭제되지 않습니다.
$ rm -rf ddd &amp;lt;-- 폴더 삭제에 대한 옵션을 추가해서 삭제했습니다.
$ ls
aaa bbb ccc&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>environment/macOS</category>
      <category>Command</category>
      <category>macos</category>
      <category>Terminal</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/203</guid>
      <comments>https://caileb.tistory.com/203#entry203comment</comments>
      <pubDate>Fri, 20 Nov 2020 01:03:38 +0900</pubDate>
    </item>
    <item>
      <title>nodejs - Web Server 만들기</title>
      <link>https://caileb.tistory.com/202</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;nodejs로 Web Server 만들기&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;b&gt;(&lt;span style=&quot;color: #006dd7;&quot;&gt;Web Application 서버, Web API 서버 만들기&lt;/span&gt;)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-11-15 오후 9.28.46.png&quot; data-origin-width=&quot;1680&quot; data-origin-height=&quot;758&quot; width=&quot;598&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5IWAt/btqNr8Kzc3m/TNh8Xo2nM3eceNWCH69yI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5IWAt/btqNr8Kzc3m/TNh8Xo2nM3eceNWCH69yI0/img.png&quot; data-alt=&quot;[ Web Client - Web Server 구조 ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5IWAt/btqNr8Kzc3m/TNh8Xo2nM3eceNWCH69yI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5IWAt%2FbtqNr8Kzc3m%2FTNh8Xo2nM3eceNWCH69yI0%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-11-15 오후 9.28.46.png&quot; data-origin-width=&quot;1680&quot; data-origin-height=&quot;758&quot; width=&quot;598&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ Web Client - Web Server 구조 ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;b&gt;목차&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;nodejs 개발 툴 설치하기&lt;/b&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;nodejs&lt;/span&gt; &amp;amp; &lt;span style=&quot;color: #f89009;&quot;&gt;npm&lt;/span&gt; 다운&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;vsCode&lt;/span&gt; 다운&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;nodejs 개발 프로젝트 생성하기&lt;/b&gt;&lt;br /&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;nodejs&lt;/span&gt; 프로젝트 폴더 생성&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;npm&lt;/span&gt; 프로젝트 파일 생성&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;nodejs 프로젝트 개발하기&lt;/b&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;vsCode&lt;/span&gt; 열기&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;vsCode&lt;/span&gt;로 &lt;span style=&quot;color: #f89009;&quot;&gt;Terminal&lt;/span&gt; 열기&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;외부라이브러리 (&lt;span style=&quot;color: #f89009;&quot;&gt;express&lt;/span&gt;) 설치&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;외부라이브러리 (&lt;span style=&quot;color: #f89009;&quot;&gt;axios&lt;/span&gt;) 설치&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;Web Appilication 서버&lt;/span&gt; 개발하기&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;Web API 서버&lt;/span&gt; 개발하기&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;nodejs 프로젝트 실행하기&lt;/b&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;nodejs&lt;/span&gt; 프로젝트 실행&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;브라우저로 url 접속&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;nodejs&lt;/span&gt; 프로젝트 종료&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;개발 환경&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;OS : macOS&lt;/p&gt;
&lt;p&gt;IDE : Visual Studio Code&lt;/p&gt;
&lt;p&gt;Server Platform : nodeJS&lt;/p&gt;
&lt;p&gt;Dependency Library : express, axios&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. nodejs 개발 툴 설치하기&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1-1. &lt;span style=&quot;color: #f89009;&quot;&gt;nodejs&lt;/span&gt; &amp;amp; &lt;span style=&quot;color: #f89009;&quot;&gt;npm&lt;/span&gt; 다운&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;Terminal에서 아래 명령어를 실행해서 nodejs와 npm을 다운로드합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1605419361944&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// nodeJS &amp;amp; npm 설치하기
$ brew install node&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;설치가 완료되었다면 아래 명령어로 어떤 버전의 nodejs와 npm이 설치되었는지 확인할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1605427886769&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 설치된 nodeJS 버전 확인하기
$ node -v

// 설치된 npm 버전 확인하기
$ npm -v&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt; 모든 nodejs 프로젝트들은 기본적인 뼈대 구조가 동일&lt;/span&gt;합니다. 이 말은 &lt;span style=&quot;color: #006dd7;&quot;&gt;nodejs 프로젝트 코드를 다운 받으면 안에 구성되어 있는 폴더와 파일들의 구성이 동일&lt;/span&gt;하다는 것인데, 이 때문에 숙련된 nodejs 개발자들에게는 서로의 코드가 어떻게 구성되어 있는지를 빠르게 파악할 수 있게 해줍니다. 그래서 다른 사람이 개발한 nodejs 프로젝트를 다운받는다면, 개발자가 작성한 소스코드들은 정해진 곳에 위치해 있고, 개발자가 외부에서 개발된 다른 라이브러리들을 사용하고 있다면 해당 라이브러리 소스코드들도 정해진 곳에 위치하게 됩니다. 이처럼 &lt;span style=&quot;color: #006dd7;&quot;&gt;nodejs 개발의 뼈대 구조를 동일하게 해주는 역할을 해주는 프로그램이 바로 npm (Node Package Manager)&lt;/span&gt;라는 프로그램이고, 추가적으로 이 &lt;span style=&quot;color: #006dd7;&quot;&gt;npm은 개발자가 작성한 js 코드들을 빌드시키고, 테스트하고, 외부라이브러리를 가져와서 관리하기&lt;/span&gt; 등의 nodejs 프로젝트를 개발하기 위해 개발자가 필요한 대부분의 기능들을 지원하고 있기 때문에, &lt;span style=&quot;color: #006dd7;&quot;&gt;nodejs 개발을 위해서는 반드시 필요한 프로그램&lt;/span&gt;입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1-2. &lt;span style=&quot;color: #f89009;&quot;&gt;vsCode&lt;/span&gt; 다운&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;Browser에서 아래 URL 주소를 입력한 후 macOS용의 실행파일을 다운받아 설치합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1605419732274&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;https://code.visualstudio.com/download&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;소스코드를 개발할 때, 코드의 변수 색깔들도 변하지 않는 메모장으로 개발하기는 힘듭니다. 따라서 소스코드 편집기가 필요한데, nodejs 프로젝으로 개발을할 때 많은 개발자들이 사용하는 &lt;span style=&quot;color: #006dd7;&quot;&gt;소스코드 편집기가 바로 Visual Studio Code (=vsCode)&lt;/span&gt;입니다. 개발할 때 특별히 알고 있는 소스코드 편집기가 없다면 이 편집기를 사용하는 것이 좋습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;사실, nodejs 프로젝트를 개발할 때, 이 글에서 소개하는 방법처럼 반드시 vsCode 다운 받아두고 사용할 필요는 없습니다. 무료로 개발할 수 있는 방법을 소개했을 뿐이며, 유료 결제를 해서 좀 더 화려한 디자인과 기능들이 담겨져 있는 IntelliJ라는 개발 툴을 사용해서 개발해도 됩니다. 개인적으로, 실무에서는 두가지 편집기를 모두 혼용해서 개발하고 있기 때문에, 굳이 유료 결제를 해서 IntelliJ를 사용하지 않고 이 글에서 소개하는 것처럼 vsCode만으로 시작해도 개발에는 전혀 문제가 없을 것이라고 생각됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. &lt;span style=&quot;color: #333333;&quot;&gt;nodejs 개발 폴더 생성하기&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2-1. &lt;span style=&quot;color: #f89009;&quot;&gt;nodejs&lt;/span&gt; 프로젝트 폴더 생성&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Terminal에서 아래 명령어를 실행해서 &quot;lec_nodejs&quot; 폴더를 생성합니다. (&quot;lec_nodejs&quot;라는 폴더이름은 예시일 뿐, 다른 이름으로 만들어도 됩니다.)&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1605427284308&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 워크스페이스 폴더 생성
$ mkdir lec_nodejs

// 워크스페이스 폴더로 진입
$ cd lec_nodejs&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;생성 후, 폴더로 진입이 완료되었다면, 아래 명령어로 현재 위치를 확인할 수 있습니다. (글쓴이의 경우 Home(=&quot;~&quot; or $HOME)의 위치에 생성하였습니다.)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1605427945192&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 현재 위치 확인
$ pwd
/Users/joker/lec_nodejs&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;여기서 생성된 폴더를 앞으로는 프로젝트 폴더라고 부를 것입니다. 이&amp;nbsp;&lt;span style=&quot;color: #006dd7;&quot;&gt;프로젝트(Project) 폴더에는 이 글에서 개발하려는 nodejs 프로젝트 관련 파일들이 모두 놓여지게 될 것&lt;/span&gt;입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2-2. &lt;span style=&quot;color: #f89009;&quot;&gt;npm&lt;/span&gt; 프로젝트 파일 생성&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Terminal에서 아래 명령어를 실행해서 npm 프로젝트 파일을 생성합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1605428631412&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// npm 프로젝트 파일 생성
$ npm init

package name: lec_nodejs
version: (1.0.0)
description: &amp;lt;-- 생략
entry point: &amp;lt;-- 생략
test command: &amp;lt;-- 생략
git repository: &amp;lt;-- 생략
keywords: &amp;lt;-- 생략
author: &amp;lt;-- 생략
license: (ISC)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;위 명령이 정상적으로 완료되었다면, 현재 위치에&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&quot;package.json&quot; 파일이 생성되었을 것입니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1605428909959&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// npm 프로젝트 관련 파일 생성 확인
$ ls
package.json&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&quot;package.json&quot; 파일이 생성되면 아래와 같이 기본적으로 몇가지 내용들이 작성되어 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1605434618022&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// package.json 파일 내용
{
  &quot;name&quot;: &quot;lec_nodejs&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;description&quot;: &quot;&quot;,
  &quot;main&quot;: &quot;index.js&quot;,
  &quot;scripts&quot;: {
    &quot;test&quot;: &quot;echo \&quot;Error: no test specified\&quot; &amp;amp;&amp;amp; exit 1&quot;
  },
  &quot;author&quot;: &quot;&quot;,
  &quot;license&quot;: &quot;ISC&quot;,
  &quot;dependencies&quot;: {}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;생성된 npm 프로젝트 관련 파일은 &quot;package.json&quot;이라는 하나의 파일&lt;/span&gt;입니다. 앞으로 글쓴이가 현재 글에서 &lt;span style=&quot;color: #006dd7;&quot;&gt;개발하려는 프로젝트의 코드를 관리하기 위해 필요한 중요한 정보들이 이 파일에 담길 것&lt;/span&gt;입니다. 이 파일의 내용 중 일부는 개발자가 직접 수정해야하는 것이 있고, 일부는 자동으로 수정되는 것이 있습니다. 예를 들면, &quot;name&quot;필드나 &quot;version&quot;필드의 값은 직접 수정하지만, &quot;dependencies&quot;필드의 값은 직접 수정하지 않고 특정 명령어를 통해 수정하게 됩니다. 이 글에서는 각 필드가 무엇을 의미하는지에 대해서만 소개합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;package name&lt;/b&gt; : 패키지의 이름&lt;/li&gt;
&lt;li&gt;&lt;b&gt;version&lt;/b&gt; : 패키지의 버전&lt;/li&gt;
&lt;li&gt;&lt;b&gt;description&lt;/b&gt; : 패키지의 설명&lt;/li&gt;
&lt;li&gt;&lt;b&gt;entry point&lt;/b&gt; : 패키지 내에서 nodejs 서버를 실행했을 때, 외부 사용자가 접근하기 위한 메인 페이지 파일을 의미합니다. 만약에 다음 사이트가 nodejs로 개발되었고, 이 때 다음 사이트의 메인 페이지 파일이 index.html 파일이고, 이 파일의 내용을 다음 사이트에서 &quot;http://www.daum.net/index.html&quot;로 제공하고 있다면, 이 프로젝트의 &quot;package.json&quot; 파일 내에 entry point 값은 index.html 파일이 되는 것입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;test command&lt;/b&gt; : 패키지 내에서 nodejs 서버를 실행할 때, 개발자들의 편의를 위해 등록해놓은 매크로 명령어들을 의미합니다. 개발을 하다보면 자주 사용하는 명령어들을 미리 정의해두고 사용하는 경우가 많이 생깁니다. 현재 만들어둔 패키지의 소스 코드를 테스트하는 명령어, 현재 패키지의 소스 코드를 실행할 때 npm의 다른 몇가지 옵션을 추가해서 실행하기 위한 명령어 등등 빈번한 명령어지만 외우기 귀찮은 것들은 이곳에 미리 정의해두면, 쉽게 사용할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;git repository&lt;/b&gt; : 패키지의 소스코드가 업로드 되어 있는 깃 주소를 의미합니다. 내가 개발한 코드를 오픈소스로 공개해서 다른 사람들이 모두 볼 수 있도록 한다면, 이 주소를 통해 원본 코드가 어디에서 관리되고 있는지 확인할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;keywords&lt;/b&gt; : 패키지의 검색 키워드를 의미합니다. 내가 개발한 코드를 오픈소스 공간에 업로드하여 공개할 때, 내 프로젝트를 대표하는 키워드 몇가지를 이 곳에 등록해둔다면, 다른 사람들이 오픈 소스 프로젝트 사이트에서 검색할 때, 이때 작성해놓은 키워드들의 연관성이 높은 프로젝트들이 우선 검색되게 됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;author&lt;/b&gt; : 패키지의 제작자&lt;/li&gt;
&lt;li&gt;&lt;b&gt;license&lt;/b&gt; : 패키지의 라이센스를 의미합니다. 내가 개발한 코드를 오픈소스 공간에 업로드하여 공개할 때, 다른 사람들이 내 프로젝트를 어느정도까지 사용할 수 있을지 범위를 제한할 수 있습니다. 라이센스의 종류에 따라, 내 코드가 완전 무료로 복제 후 수정까지 가능한 코드가 될지, 무료 사용은 가능하지만 수정은 불가능한 코드가 될지, 내 코드를 무료로 사용하지만 상업적으로 사용한다면 돈을 내야하는지, 내 코드를 내 허락없이는 사용할 수 없도록 만들지 등등의 사용 범위를 정할 때 중요한 역할을 하게 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. nodejs 프로젝트 개발하기&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3-1. &lt;span style=&quot;color: #f89009;&quot;&gt;vsCode&lt;/span&gt; 열기&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;vsCode를 실행 한후, 위에서 만든 lec_nodejs 폴더를 열면 됩니다. (vsCode가 어색한 분들을 위해 아래 차례로 설명합니다.)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;vsCode를 실행한다.&lt;/li&gt;
&lt;li&gt;기본으로 열려져 있는 welcome 창은 x를 클릭해서 닫는다.&lt;/li&gt;
&lt;li&gt;왼쪽 상단에 두개의 문석 겹쳐진 아이콘(=Explorer)를 누른다.&lt;/li&gt;
&lt;li&gt;Open Folder를 누른다.&lt;/li&gt;
&lt;li&gt;lec_nodejs 폴더를 선택한다.&lt;/li&gt;
&lt;li&gt;열기를 선택한다. 아래 그림처럼 화면에 2등분으로 나눠지는데, 왼쪽는 열려진 폴더인 lec_nodejs 를 기준으로 폴더와 파일들을 탐색기처럼 트리형태로 나열한 것이고, 오른쪽은 왼쪽에서 파일을 누를 때마다 해당 파일의 내용들이 출력될 것입니다. 앞으로는 이 두 개의 화면을 보면서 개발을 하면 됩니다.&lt;/li&gt;
&lt;li&gt;간혹 아래 그림처럼 update now 가 나오는데, vsCode 업데이트를 할 것이냐고 묻는 것이므로, 업데이트를 진행해주면 됩니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-11-15 오후 6.13.09.png&quot; data-origin-width=&quot;1610&quot; data-origin-height=&quot;984&quot; width=&quot;623&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bv4TQx/btqNpeSE5C9/KuZqDDZ8Sk1h0J63daXtKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bv4TQx/btqNpeSE5C9/KuZqDDZ8Sk1h0J63daXtKk/img.png&quot; data-alt=&quot;[ vsCode 열기 그림 참고 ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bv4TQx/btqNpeSE5C9/KuZqDDZ8Sk1h0J63daXtKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbv4TQx%2FbtqNpeSE5C9%2FKuZqDDZ8Sk1h0J63daXtKk%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-11-15 오후 6.13.09.png&quot; data-origin-width=&quot;1610&quot; data-origin-height=&quot;984&quot; width=&quot;623&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ vsCode 열기 그림 참고 ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3-2. &lt;span style=&quot;color: #f89009;&quot;&gt;vsCode&lt;/span&gt;로 &lt;span style=&quot;color: #f89009;&quot;&gt;Terminal&lt;/span&gt; 열기&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;b&gt;vsCode &amp;gt; 상단 메뉴의 [Terminal] &amp;gt; [New Terminal]&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위 단계까지는 macOS의 기본 Terminal을 실행했었지만, &lt;span style=&quot;color: #006dd7;&quot;&gt;앞으로는 vsCode에서 제공하는 Terminal을 통해서 명령어들을 실행&lt;/span&gt;시킬 것입니다. vsCode를 열었다면 상단에 [Terminal]이라는 탭이 보일 것입니다. 탭에서 [New Terminal]을 선택하면 vsCode 안에 터미널 창이 하나 추가될 것입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;참고로, macOS 에서 실행한 기본 Terminal과 vsCode 에서 실행한 Terminal 에는 차이가 전혀 없습니다. 단지, 별도의 Terminal 창을 띄워두고 개발하면 창 전환하는 번거로움이 생기기 때문에 vsCode에서 제공하는 Terminal을 사용하는 것 뿐입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3-3. 외부라이브러리 (&lt;span style=&quot;color: #f89009;&quot;&gt;express&lt;/span&gt;) 설치&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;vsCode의 Terminal에서 아래의 npm 명령어를 실행해서 express를 다운로드합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1605432109713&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm install express --save&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;express 패키지 다운이 정상적으로 완료된 이후에는, &quot;package.json&quot; 파일의 내용이 변경된 것을 확인할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1605434803481&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;lec_nodejs&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;description&quot;: &quot;&quot;,
  &quot;main&quot;: &quot;index.js&quot;,
  &quot;scripts&quot;: {
    &quot;test&quot;: &quot;echo \&quot;Error: no test specified\&quot; &amp;amp;&amp;amp; exit 1&quot;
  },
  &quot;author&quot;: &quot;&quot;,
  &quot;license&quot;: &quot;ISC&quot;,
  &quot;dependencies&quot;: {
    &quot;express&quot;: &quot;^4.17.1&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;express 라이브러리 패키지는 nodejs 위에서 http 서버 기능을 제공하는 함수들을 묶어놓은 패키지&lt;/span&gt;입니다. 이 패키지를 다운 받는 이유는 현재 개발 중인 nodejs 프로그램이&amp;nbsp;&lt;u&gt;외부에서 내부로&lt;/u&gt; http 통신으로 접근할 수 있도록 서버 기능들을 탑재시키기 위함입니다. &lt;span style=&quot;color: #006dd7;&quot;&gt;이 기능을 사용해서 이 글의 프로젝트에서는 사용자가 브라우저로 Web Server #1 (http://127.0.0.1:3000/index.html) 페이지를 여는 것이 가능하도록 할 것&lt;/span&gt;입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-11-15 오후 9.34.59.png&quot; data-origin-width=&quot;1832&quot; data-origin-height=&quot;572&quot; width=&quot;608&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cVV8UT/btqNx8v2cWb/8zHIPocOoXe9Px2mwZM1c1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cVV8UT/btqNx8v2cWb/8zHIPocOoXe9Px2mwZM1c1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cVV8UT/btqNx8v2cWb/8zHIPocOoXe9Px2mwZM1c1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcVV8UT%2FbtqNx8v2cWb%2F8zHIPocOoXe9Px2mwZM1c1%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-11-15 오후 9.34.59.png&quot; data-origin-width=&quot;1832&quot; data-origin-height=&quot;572&quot; width=&quot;608&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3-4. 외부라이브러리 (&lt;span style=&quot;color: #f89009;&quot;&gt;axios&lt;/span&gt;) 설치&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;vsCode의 Terminal에서 아래의 npm 명령어를 실행해서 axios를 다운로드합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1605432122401&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm install axios --save&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;axios 패키지 다운이 정상적으로 완료된 이후에는, &quot;package.json&quot; 파일의 내용이 변경된 것을 확인할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1605434817746&quot; class=&quot;php&quot; data-ke-language=&quot;php&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;lec_nodejs&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;description&quot;: &quot;&quot;,
  &quot;main&quot;: &quot;index.js&quot;,
  &quot;scripts&quot;: {
    &quot;test&quot;: &quot;echo \&quot;Error: no test specified\&quot; &amp;amp;&amp;amp; exit 1&quot;
  },
  &quot;author&quot;: &quot;&quot;,
  &quot;license&quot;: &quot;ISC&quot;,
  &quot;dependencies&quot;: {
    &quot;axios&quot;: &quot;^0.21.0&quot;,
    &quot;express&quot;: &quot;^4.17.1&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;axios 라이브러리 패키지는 nodejs 위에서 http 클라이언트 기능을 제공하는 함수들을 묶어놓은 패키지&lt;/span&gt;입니다. 이 패키지를 다운받는 이유는 현재 개발 중인 nodejs 프로그램에 &lt;u&gt;내부에서&amp;nbsp;외부로&lt;/u&gt; http 통신으로 접근할 수 있는 클라이언트 기능들을 탑재시키기 위함입니다. &lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;이 기능을 사용해서 이 글의 프로젝트에서는 사용자가 브라우저로 Web Server #1 (http://127.0.0.1:3000/lastTransacstion/BTC) 로의 URL 로 요청을 했을 때, 외부 서버인 Web Server #2 (https://api.bitthumb.com/public/ticker/)에서 가져온 응답 데이터를 포함해서 페이지로 출력&lt;/span&gt;하도록 할 것입니다. &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;참고로, Web Server #2로 사용되는 서버는 빗썸(Bitthumb)이라는 가상화폐 거래소에서 제공하는 마지막으로 처리된 트랜잭션 내용의 응답을 반환시켜주는 API 서버를 활용했습니다. 만약, 예시로 작성한 가상화폐 거래서 데이터가 아니라, 코로나 19의 현재 각 나라별 발생 현황 데이터를 제공하는 API 서버나 NewYork Times의 뉴스기사 데이터를 제공하는 API 서버 등이 있다면, 이러한 데이터들 또한 axios 라이브러리에서 제공하는 기능들을 사용해서 쉽게 데이터를 요청하고 응답받아 올 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-11-15 오후 9.35.07.png&quot; data-origin-width=&quot;1830&quot; data-origin-height=&quot;532&quot; width=&quot;595&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbb5kx/btqNr8RlA18/EG5vN9AWTbGV88cTm2bgWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbb5kx/btqNr8RlA18/EG5vN9AWTbGV88cTm2bgWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbb5kx/btqNr8RlA18/EG5vN9AWTbGV88cTm2bgWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbbb5kx%2FbtqNr8RlA18%2FEG5vN9AWTbGV88cTm2bgWK%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-11-15 오후 9.35.07.png&quot; data-origin-width=&quot;1830&quot; data-origin-height=&quot;532&quot; width=&quot;595&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3-5. &lt;span style=&quot;color: #f89009;&quot;&gt;Web Application 서버&lt;/span&gt; 개발하기&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Web Application Server는 웹 페이지들은 이쁘게 만들어서 사용자에게 출력시켜주는 것이 목적&lt;/span&gt;입니다. 따라서, Web Front 와 Web Backend 영역에는 코드를 아래처럼 구분지을 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Web Front&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;html : 웹 페이지 구조를 잡는 것이 목적&lt;/li&gt;
&lt;li&gt;css : 웹 페이지를 이쁘게 디자인 하는 것이 목적&lt;/li&gt;
&lt;li&gt;javascript : 웹 페이지를 자연스럽게 이동시키고, Web Backend와 통신하는 것이 목적&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;Web Backend&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;웹 페이지에 필요한 데이터들을 만들어서 Web Front로 응답하는 것이 목적&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;서버를 개발할 때, Web Front와 Web Backend의 영역이 모두 포함되어 개발할 수 있습니다&lt;/span&gt;. 이렇게 되면 하나의 프로젝트를 웹 프론트 개발자와 백엔드 개발자가 모두 참여해서 소스코드를 수정하는 형태가 될 것입니다. 이를 그림으로 그려보면 아래처럼 서버가 Front와 Backend가 영역은 구분되어 있지만 하나의 프로젝트 안에 포함되어 있습니다. 이러한 형태로 개발한다면, 하나의 프로젝트에 코드가 커질수록 관리하기가 힘들어지기 때문에, 조그만 프로젝트나 파일럿 수준 정도에서 많이 사용되는 개발 구조입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-11-15 오후 8.02.23.png&quot; data-origin-width=&quot;1740&quot; data-origin-height=&quot;498&quot; width=&quot;573&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKllj7/btqNuPqaEob/9VWOM06hpzmkZscnENmRE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKllj7/btqNuPqaEob/9VWOM06hpzmkZscnENmRE1/img.png&quot; data-alt=&quot;[ Web Application Server 한 개 프로젝트로 개발하는 구조 ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKllj7/btqNuPqaEob/9VWOM06hpzmkZscnENmRE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKllj7%2FbtqNuPqaEob%2F9VWOM06hpzmkZscnENmRE1%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-11-15 오후 8.02.23.png&quot; data-origin-width=&quot;1740&quot; data-origin-height=&quot;498&quot; width=&quot;573&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ Web Application Server 한 개 프로젝트로 개발하는 구조 ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이번 항목에서 작성하는 &lt;span style=&quot;color: #006dd7;&quot;&gt;Web Application Server 코드는 단 두 개의 파일로 구성&lt;/span&gt;되어 있습니다. 웹 페이지의 구조를 잡는 index.html 파일은 웹 서버에 있다가, 브라우저에서 http://127.0.0.1:3000/index.html 경로를 통해 웹 서버로 요청하면, 웹 서버는 index.js 파일에 작성해놓은 로직을 통해서 public 폴더에 해당 파일이 있는지 확인하고, 존재한다면 index.html 파일을 사용자의 브라우저로 전달한 후, 브라우저는 index.html 페이지를 출력하게 됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;index.html 파일&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1605438550944&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//... /Users/joker/lec_nodejs/public/index.html

&amp;lt;html&amp;gt;
	&amp;lt;body&amp;gt;Hello, world!&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;index.js 파일&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1605434884560&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//... /Users/joker/lec_nodejs/index.js

const express = require('express');
const app = express();
const axios = require('axios');

/**
 * 메인 웹 페이지 설정
 * public 폴더를 static 파일들(ex, html, css, jpg)을 위치로 지정한다.
 * 
 * example) 
 * http://127.0.0.1:3000
 * http://127.0.0.1:3000/index.html
 */
app.use(express.static('public'));

var server = app.listen(3000, function () {
  var host = server.address().address;
  var port = server.address().port;
  
  console.log('Server is working : PORT - ',port);
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3-6. &lt;span style=&quot;color: #f89009;&quot;&gt;Web API 서버&lt;/span&gt; 개발하기&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Web API Server는 Web Front의 요청에 따른 응답 데이터를 응답 시켜주는 것이 목적&lt;/span&gt;입니다. 따라서, Web Front 는 신경쓰지 않고 Web Backend 영역만 존재합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;Web Backend&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹 페이지에 필요한 데이터들을 만들어서 Web Front로 응답하는 것이 목적&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;서버를 개발할 때, Web Front와 Web Backend의 영역을 완전히 분리해서 개발할 수 있습니다&lt;/span&gt;. 이렇게 되면 프로젝트 두개를 만들어서 하나는 웹 프론트 개발자만 사용하고, 다른 하나는 백엔드 개발자만 사용해서 소스코드를 수정하는 형태가 될 것입니다. 이를 그림으로 그려보면 아래처럼 서버가 Front와 Backend가 완전히 구분되어 있습니다. Front 영역에서는 사용자의 시각적인 부분, 행위적인 부분에 집중해서 UI/UX만을 위해 개발을 하면되고, Backend 영역에서는 사용자의 UI/UX는 전혀 관여하지 않고 데이터만을 가공해서 정확히 전달해주기면 한다면 시간이 지남에 따라 프로젝트 코드 양이 커진다고 해도 큰 부담이 없을 것입니다. 따라서 어느정도 수준 이상의 서비스를 제공하는 서버라면 이러한 형태로 개발이 됩니다.&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-11-15 오후 8.02.31.png&quot; data-origin-width=&quot;1738&quot; data-origin-height=&quot;508&quot; width=&quot;589&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dsUDWY/btqNuQvSF1e/Q5sYUddbKX9DwHEUYCgnNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dsUDWY/btqNuQvSF1e/Q5sYUddbKX9DwHEUYCgnNK/img.png&quot; data-alt=&quot;[ Web Application Server을 두개 프로젝트로 개발하는 구조 ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dsUDWY/btqNuQvSF1e/Q5sYUddbKX9DwHEUYCgnNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdsUDWY%2FbtqNuQvSF1e%2FQ5sYUddbKX9DwHEUYCgnNK%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-11-15 오후 8.02.31.png&quot; data-origin-width=&quot;1738&quot; data-origin-height=&quot;508&quot; width=&quot;589&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ Web Application Server을 두개 프로젝트로 개발하는 구조 ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이번 항목에서 작성하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Web API Server 코드는 단 하나의 파일로 구성&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;되어 있습니다. 웹 서버에 &quot;/lastTransaction/:currency&quot; 의 주소를 등록해두었다가, 브라우저에서 http://127.0.0.1:3000/lastTransaction/BTC 경로를 통해 웹 서버로 요청하면, 웹 서버는 index.js 파일에 작성해놓은 로직을 통해서 빗썸의 API 서버에 비트코인(BTC)의 마지막 거래내역을 요청하고, 그 응답값을 사용자의 브라우저로 전달한 후, 브라우저는 데이터를 출력하게 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;index.js 파일&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1605434985164&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//... /Users/joker/lec_nodejs/index.js

const express = require('express');
const app = express();
const axios = require('axios');

/**
 * 암호화폐 거래소의 특정 코인의 마지막 거래 정보 가져오기 REST API
 * 
 * BTC, ETH, DASH, LTC, ETC, XRP, BCH, XMR, ZEC, QTUM, BTG, EOS
 * example) 
 * http://127.0.0.1:3000/lastTransaction/BTC
 */
app.get('/lastTransaction/:currency', function (req, res) {
    var currency = req.params.currency;
    getData(currency)
    .then(function(data){
        res.send(data);
    }).catch(function(err) {
        console.error(err); 
    });
});

function getData(currency) {
    return new Promise(async function(resolve, reject) {
        var result = await axios.get(`https://api.bithumb.com/public/ticker/` + currency);
        resolve(result.data);
    });
};

var server = app.listen(3000, function () {
  var host = server.address().address;
  var port = server.address().port;
  
  console.log('Server is working : PORT - ',port);
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;4. nodejs 프로그랢 실행&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4-1. &lt;span style=&quot;color: #f89009;&quot;&gt;nodejs&lt;/span&gt; 프로그램 실행&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;vsCode의 Terminal에서 아래의 명령어를 실행해서 현재 코드를 nodejs로 실행시킵니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1605435241787&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// nodejs 서버 실행
$ node index.js
Debugger attached.
Server is working : PORT -  3000&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4-2. 브라우저로 url 접속&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Browser에서 아래 URL 주소들을 입력합니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1605435417732&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// (Web Application Server) index.html 페이지 출력
http://localhost:3000/

// (Web API Server) 페이지 없이 응답 데이터만 출력
http://localhost:3000/lastTransaction/BTC&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;첫번째 주소를 입력하면 브라우저에는 index.html 페이지가 띄워지게 되고, 두번째 주소를 입력하면 브라우저에는 응답 데이터만 출력되게 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;참고로, 실무에서는&amp;nbsp;&lt;span style=&quot;color: #006dd7;&quot;&gt;일반적으로 서버를 두 종류로 구분해서 개발&lt;/span&gt;하고 있습니다. 하나는 첫번째 주소처럼 웹 페이지를 띄워서 서비스하는 형태로, &lt;a href=&quot;http://www.daum.net,&quot;&gt;www.daum.net,&lt;/a&gt; &lt;a href=&quot;http://www.google.com,&quot;&gt;www.google.com,&lt;/a&gt; &lt;a href=&quot;http://www.naver.com&quot;&gt;www.naver.com&lt;/a&gt; 처럼 브라우저 위에서 화면을 직접 뿌려주면서 서비스를 하는 것입니다. 이러한 서버를 개발자들은 &lt;span style=&quot;color: #006dd7;&quot;&gt;Web Application 서버&lt;/span&gt;라고 부르는데, 이 경우 프론트 서버 개발자(Front Server Developer)들의 javascript, css, html 작업들이 많이 필요합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;다른 하나는 두번째 주소처럼 웹 페이지는 필요 없이 서비스하는 형태로, 위에서 빗썸 가상화폐 거래소를 예시로 들은 것처럼 데이터만 제공하는 서비스를 하는 것입니다. 이러한 서버를 개발자들은 &lt;span style=&quot;color: #006dd7;&quot;&gt;Web API 서버&lt;/span&gt;라고 부르는데, API 서버를 개발할 때는 프론트 서버 개발자 없이 백엔드 개발자(Backend Server Developer)들로만 구성되어 개발됩니다. 화면에 보여지는 개발을 할 필요가 없기 때문입니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4-4. &lt;span style=&quot;color: #f89009;&quot;&gt;nodejs&lt;/span&gt; 프로젝트 종료&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;vsCode의 Terminal에서 Ctrl + C 를&amp;nbsp; 입력해서 실행 중인 nodejs를 종료시킵니다. &lt;u&gt;만약, nodejs 프로그램이 실행 중에 코드를 수정하고 저장했다면, 이 방법을 사용해서 종료시킨 후 다시 실행&lt;/u&gt;시켜주어야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1605435298757&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(서버 실행 중에 Ctrl + C를 눌러서 서버 종료)
^C&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;추가 설명&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;이 글을 따라 웹 서버 개발이 완료되었다면, 프로젝트의 폴더 구조는 아래와 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1605443933018&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;lec_nodejs
|--public/
|  |--index.html
|--node_modules/
|--index.js
|--package.json
|--package-lock.json&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;프로젝트 폴더 구조를 보면, 직접 생성하지 않은 node_modules 폴더와 package-lock.json 파일이 보입니다. 이 두 개의 파일은 윗 단계에서 &quot;npm install&quot; 명령어를 실행할 때, 자동으로 생성된 것들입니다. &lt;u&gt;&quot;&lt;span style=&quot;color: #006dd7;&quot;&gt;node_module&lt;/span&gt;&quot; 폴더는 윗 단계에서 다운받은 &quot;express&quot;와 &quot;axios&quot; 라이브러리 패키지에 대한 소스코드들이 모두 담겨져 있습니다&lt;/u&gt;. npm은 실행할 때 필요한 외부 라이브러리가 있다면, &quot;node_module&quot; 폴더에 모두 다운받아두고 사용하게 됩니다. 실제로 &quot;node_module&quot; 폴더를 열어보면 다양한 이름의 패키지들이 있는 것을 확인할 수 있는데, &quot;express&quot;와 &quot;axios&quot; 라이브러리가 또 다른 라이브러리들을 참조해서 개발된 패키지이기 때문에, 의존하고 있는 모든 라이브러리들을 다운받게 된 것입니다. 그리고 &lt;u&gt;npm은 이렇게 서로 의존적인 라이브러리들을 관리하기 위해 하나의 파일을 추가해서 관리하고 있습니다. 그 파일이 바로 &quot;&lt;span style=&quot;color: #006dd7;&quot;&gt;package-lock.json&lt;/span&gt;&quot; 파일입니다&lt;/u&gt;. 이 파일 내용을 보면, &quot;node_module&quot; 안에 있는 라이브러리들에 대한 정보들이 작성되어 있는 것을 확인할 수 있습니다. 따라서, &lt;span style=&quot;color: #006dd7;&quot;&gt;nodejs 프로젝트를 개발할 때, &quot;node_module&quot;과 &quot;package-lock.json&quot; 파일은 npm에 의해 자동으로 생성도는 것이니 개발자가 직접 수정하지 않아야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;참고 사이트&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;express에서 static파일들(ex, &lt;span style=&quot;color: #333333;&quot;&gt;html, css, jpg, js&lt;/span&gt;)을 제&lt;span style=&quot;color: #333333;&quot;&gt;공하는 방법&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #7e98b1;&quot;&gt;&lt;a style=&quot;color: #7e98b1;&quot; href=&quot;https://expressjs.com/ko/starter/static-files.html&quot;&gt;링크&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;웹 페이지 샘플 사이트 &lt;span style=&quot;color: #7e98b1;&quot;&gt;&lt;a style=&quot;color: #7e98b1;&quot; href=&quot;https://www.w3schools.com/howto/howto_css_example_website.asp&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;링크&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>dev-tips/nodejs</category>
      <category>API</category>
      <category>axios</category>
      <category>Express</category>
      <category>nodejs</category>
      <category>NPM</category>
      <category>restful</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/202</guid>
      <comments>https://caileb.tistory.com/202#entry202comment</comments>
      <pubDate>Sun, 15 Nov 2020 21:29:33 +0900</pubDate>
    </item>
    <item>
      <title>GitHub - Image 올리기 (README, Issue, PR)</title>
      <link>https://caileb.tistory.com/201</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot; style=&quot;text-align: center;&quot;&gt;GitHub - Image 올리기&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;GitHub를 사용해서 코드를 관리하다 보면, 가끔 사진(Image) 파일을 올려야할 때가 종종 있다. 예를 들면, 프로젝트의 설명을 작성하는 README.md 파일에 이해를 돕기 위해 프로그램 일부를 캡쳐해서 포함시키거나, 문제를 제기하는 Issue나 문제를 해결했다는 Pull Request를 작성할 때에도 다른사람들이 빠르게 이해할 수 있도록 글 이외에 그림을 넣는 경우들이 있다. 하지만, GitHub는 코드를 관리하기 위한 목적으로 이용되기 때문에, 코드 이외에 사진 파일이나 압축 파일 등의 다른 파일들에 대한 지원을 쉽게 하고 있지 않다.&amp;nbsp;아래 몇가지 방법으로 Github에서 이미지와 함께 글을 작성하는 방법을 소개한다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(248, 144, 9);&quot;&gt;Issue&lt;/span&gt; 글을 작성할 때와 &lt;span style=&quot;color: rgb(248, 144, 9);&quot;&gt;Pull Request(PR)&lt;/span&gt; 글을 작성할 때 Image 올리기&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;Issue 글과 PR 글을 작성할 때 Image를 업로드하는 방법은 동일하다. 아래 설명은 Issue 글을 올리는 상황에 대한 설명을 하고 있으나, PR 글을 작성할 때도 동일한 방법으로 글과 Image를 함께 작성하면 된다.&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;우선, Issue 글 작성하기를 눌렀다면, 아래처럼 글을 쓰는 공간에 &lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;이미지를 복사(Ctrl+C) &amp;amp; 붙이기(Ctrl+V)&lt;/span&gt;를 하거나 &lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;이미지 파일을 드래그(Drag) &amp;amp; 드롭(Drop)&lt;/span&gt;을 하면 된다. 붙이기를 하면 아래 캡처한 화면처럼 사진이 바로 삽입되는 것이 아니라, &quot;&lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;![Uploading &amp;lt;업로드할 image&amp;gt;]()&lt;/span&gt;&quot; 라는 문자열이 잠깐 동안 삽입이 되는 것을 확인할 수 있다. 이 문자열과 동시에 하단에 &quot;&lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;Uploading your files...&lt;/span&gt;&quot; 라는 표시가 나온다. 업로드 하는 사진의 용량에 따라 시간이 다르게 나오는데, 이 때, 알아두어야할 점은, &lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;png 이미지로 변환&lt;/span&gt;해서 올려진다는 것이다. 만약에 올리려는 사진의 확장자가 png 가 아닌 jpg, jpeg 등의 다른 확장자라면 png로 올라갈 것이다. 아마도 Github 에서 Issue와 PR에 올리는 사진은 참고용으로만 작성하는 것이기 때문이라고 가정하고 원본 파일을 올리지 않는 것 같다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/BAXtT/btqImd5m1vk/jAhktXLTOQAZ2AExKZc9p0/img.png&quot; width=&quot;566.0&quot; data-origin-width=&quot;1705.0&quot; data-origin-height=&quot;701.0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BAXtT/btqImd5m1vk/jAhktXLTOQAZ2AExKZc9p0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BAXtT/btqImd5m1vk/jAhktXLTOQAZ2AExKZc9p0/img.png&quot; data-alt=&quot;[ upload 진행 중 ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BAXtT/btqImd5m1vk/jAhktXLTOQAZ2AExKZc9p0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBAXtT%2FbtqImd5m1vk%2FjAhktXLTOQAZ2AExKZc9p0%2Fimg.png&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/BAXtT/btqImd5m1vk/jAhktXLTOQAZ2AExKZc9p0/img.png&quot; width=&quot;566.0&quot; data-origin-width=&quot;1705.0&quot; data-origin-height=&quot;701.0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ upload 진행 중 ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;약 1~5초 정도가 지나면 화면 문구가 &quot;&lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;![image](&amp;lt;업로드된 url&amp;gt;)&lt;/span&gt;&quot; 형태로 변환될 것이다. 이때, &lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;&amp;lt;업로드된 url&amp;gt;&lt;/span&gt;은 실제로 주소창에서 호출하면 해당 이미지가 뜨기 때문에 Github는 특정 공간에 저장시키는 것을 알 수 있다. 주소가 내 Git Repository 주소가 아니기 때문에 아마도 시간이 어느정도 지난후 Image를 포함하는 Issue나 PR이 필요없어질 때 쯤? 아니면 Git Repository가 삭제되면? 등의 특정 시간에 제거되지 않을까 생각된다. 하지만, 일반적으로 Issue나 PR은 오랜시간 동안 유지되는 글이 아니기 때문에, 상관없을 것이다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/MT1Vw/btqIwZ46qNY/GVLgLKq2die7wyutinWSK1/img.png&quot; width=&quot;575.0&quot; data-origin-width=&quot;1710.0&quot; data-origin-height=&quot;700.0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MT1Vw/btqIwZ46qNY/GVLgLKq2die7wyutinWSK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MT1Vw/btqIwZ46qNY/GVLgLKq2die7wyutinWSK1/img.png&quot; data-alt=&quot;[ upload 완료 ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MT1Vw/btqIwZ46qNY/GVLgLKq2die7wyutinWSK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMT1Vw%2FbtqIwZ46qNY%2FGVLgLKq2die7wyutinWSK1%2Fimg.png&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/MT1Vw/btqIwZ46qNY/GVLgLKq2die7wyutinWSK1/img.png&quot; width=&quot;575.0&quot; data-origin-width=&quot;1710.0&quot; data-origin-height=&quot;700.0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ upload 완료 ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;만약, 2개 이상의 사진을 추가하고 싶다면, 앞에서 했던 것과 동일하게 간단히 끌어다 놓으면 된다. 추가된 그림도 일반적인 글처럼 인식한다. 다시 말해서, 그림을 추가한다고 해서 자동으로 엔터가 붙지 않는다. 아래 그림은 세로와 가로로 작성 하는 방법을 나눠놨지만, 간혹 첨부된 이미지 이름이 길기 때문에 아래 그림처럼 내가 엔터를 쳤는지, 치지 않았는지 헷가릴 때가 있다. 따라서, &lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;이미지가 세로로 보이기를 원한다면 그림 + 엔터, 이미지가 가로로 보이기를 원한다면 그림 + 스페이스&lt;/span&gt;로 작성하면 된다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/IuMZ6/btqIqmmP3LQ/KejJEOtkG7WdbPKJvH60v0/img.png&quot; width=&quot;589.0&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IuMZ6/btqIqmmP3LQ/KejJEOtkG7WdbPKJvH60v0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IuMZ6/btqIqmmP3LQ/KejJEOtkG7WdbPKJvH60v0/img.png&quot; data-alt=&quot;[ 2개 이미지 upload 완료 ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IuMZ6/btqIqmmP3LQ/KejJEOtkG7WdbPKJvH60v0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIuMZ6%2FbtqIqmmP3LQ%2FKejJEOtkG7WdbPKJvH60v0%2Fimg.png&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/IuMZ6/btqIqmmP3LQ/KejJEOtkG7WdbPKJvH60v0/img.png&quot; width=&quot;589.0&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ 2개 이미지 upload 완료 ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;너무 큰 이미지를 삽입하거나 너무 작은 이미지를 삽입해서 &lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;이미지의 크기를 조정하고 싶은 경우가 있다면,&lt;/span&gt; 위 첨부한 그림처럼 HTML 태그의 &amp;lt;img&amp;gt; 태그를 사용하면 된다. 앞 설명에서 이미지를 끌어다 놓은 순가 이미 Github에 이미지는 업로드되며 URL이 출력된다고 하였다. 이를 이용하면 &lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;&amp;lt;img src=&quot;&amp;lt;업로드된 url&amp;gt;&quot;&amp;gt;로 변경&lt;/span&gt;할 수 있다. 그리고 &lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;&amp;lt;img&amp;gt; 태그에서 제공하는 width 속성을 이용&lt;/span&gt;하면 삽입되는 이미지의 크기 또한 조절이 가능하다. 아래 화면은 위의 Issue 글이 작성되었을 때의 모습이다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/HjNlk/btqIkussd5y/TZQjpEk8O8ZaOqFgKLuetk/img.png&quot; width=&quot;528.0&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HjNlk/btqIkussd5y/TZQjpEk8O8ZaOqFgKLuetk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HjNlk/btqIkussd5y/TZQjpEk8O8ZaOqFgKLuetk/img.png&quot; data-alt=&quot;[ 2개 이미지 upload 완료 화면 ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HjNlk/btqIkussd5y/TZQjpEk8O8ZaOqFgKLuetk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHjNlk%2FbtqIkussd5y%2FTZQjpEk8O8ZaOqFgKLuetk%2Fimg.png&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/HjNlk/btqIkussd5y/TZQjpEk8O8ZaOqFgKLuetk/img.png&quot; width=&quot;528.0&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ 2개 이미지 upload 완료 화면 ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(248, 144, 9);&quot;&gt;README.md&lt;/span&gt;&amp;nbsp;파일에 Image 올리기&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;리서치 해본 결과, 아래의 4가지 방법들을 사용해서 Image를 업로드하는 방법을 찾을 수 있었다. 개인적으로 [방법1]과 [방법3]이 사용하기에 적당할 것이라고 생각된다. &lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;README.md 파일을 빨리 작성해서 누군가 보여줘야 한다면 [방법1]을 사용&lt;/span&gt;하고, 누군가에게 프로젝트를 배포해야한다거나 &lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;장기간 안정적으로 유지되어야하는 프로젝트에 작성되어야하는 README.md 파일을 만들어야 한다면 [방법3]을 사용&lt;/span&gt;하는 것이 적당할 것이라고 생각된다.&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;b&gt;[방법1] Issue와 PR을 이용해서 Image를 올린 후, 링크를 거는 방법 &lt;/b&gt;&lt;span style=&quot;color: rgb(126, 152, 177);&quot;&gt;(=이미지를 Github가 관리하는 어딘가에 보관 / 언제 없어질지 모름)&lt;/span&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;1. 위에서 설명했던 것처럼 Issue와 PR을 통해 &lt;u&gt;사진을 삽입한 후, 글 작성을 완료&lt;/u&gt;한다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(51, 51, 51);&quot;&gt;2. README.md에 업로드된 이미지 URL을 링크로 삽입한다.&lt;/span&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;b&gt;[방법2] Issue와 PR을 이용해서 Image를 올린 후, 링크를 거는 방법 &lt;/b&gt;&lt;span style=&quot;color: rgb(126, 152, 177);&quot;&gt;(=이미지를 Github가 관리하는 어딘가에 보관 / 언제 없어질지 모름)&lt;/span&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(51, 51, 51);&quot;&gt;1. 위에서 설명했던 것처럼 Issue와 PR을 통해 &lt;u&gt;사진을 삽입한 후, 글 작성을 완료하지 않는다&lt;/u&gt;.&lt;/span&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;2. README.md에 업로드된 이미지 URL을 링크로 삽입한다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;b&gt;[방법3] Git에 직접 Image를 올린 후, 링크를 거는 방법 &lt;/b&gt;&lt;span style=&quot;color: rgb(126, 152, 177);&quot;&gt;(=이미지를 Repository에서 직접 보유하는 방법)&lt;/span&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;1. &lt;u&gt;Image가 사용될 Git Repository에&lt;/u&gt; directory를 생성하고 Image를 올린다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(51, 51, 51);&quot;&gt;2. README.md에 업로드된 이미지 URL을 링크로 삽입한다.&lt;/span&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;b&gt;[방법4] Git에 직접 Image를 올린 후, 링크를 거는 방법 &lt;/b&gt;&lt;span style=&quot;color: rgb(126, 152, 177);&quot;&gt;(=이미지 전용 Repository 생성하는 방법)&lt;/span&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;1. &lt;u&gt;Image와 관련없는 Git Repository에&lt;/u&gt; directory를 생성하고 Image를 올린다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(51, 51, 51);&quot;&gt;2. README.md에 업로드된 이미지 URL을 링크로 삽입한다.&lt;/span&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(51, 51, 51);&quot;&gt;README.md 파일에 이미지를 삽입할 때는 아래처럼 마크다운 포멧으로 혹은 HTML 포멧으로 작성하면 된다.&lt;/span&gt;&lt;/p&gt;&lt;pre data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;마크다운 포멧으로 작성
![&amp;lt;이미지 이름&amp;gt;](&amp;lt;업로드된 이미지 URL&amp;gt;)
![sample](https://...../sample.png)

HTML 포멧으로 작성
&amp;lt;img src=&quot;&amp;lt;업로드된 이미지 URL&amp;gt;&quot;&amp;gt;
&amp;lt;img src=&quot;https://...../sample.png&quot;&amp;gt;&lt;/pre&gt;</description>
      <category>environment/version</category>
      <category>Github</category>
      <category>image 업로드</category>
      <category>issue</category>
      <category>PR</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/201</guid>
      <comments>https://caileb.tistory.com/201#entry201comment</comments>
      <pubDate>Thu, 10 Sep 2020 23:04:23 +0900</pubDate>
    </item>
    <item>
      <title>APK 파일 versionCode, versionName 정보 확인</title>
      <link>https://caileb.tistory.com/200</link>
      <description>&lt;b&gt;android apk 버전 정보 추출하기&lt;/b&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: center;&quot;&gt;(&lt;b&gt;&lt;span style=&quot;color: rgb(248, 144, 9);&quot;&gt;&lt;span style=&quot;color: rgb(238, 35, 35);&quot;&gt;versionCode&lt;/span&gt;, versionName&lt;/span&gt;&lt;/b&gt;)&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt; Android 애플리케이션은 개발자가 관리의 목적으로 만들어서 사용하는 버전 정보&lt;/span&gt;가 있다. 예를 들어, 지금 Android App Store 마켓을 들어가서 &quot;Kakotalk&quot;을 검색한 후 &quot;자세히 알아보기&quot; 버튼을 클릭하면 &quot;8.9.6&quot;이라는 버전 정보를 볼 수 있을 것이다. 혹은 &quot;Line&quot;을 검색한 후, 동일한 방법으로 버전 정보를 찾아보면 &quot;10.12.1&quot;이라고 볼 수 있을 것이다. 아래 첨부한 그림은 &quot;Dropbox&quot;앱에 대한 버전 정보를 나타내고 있다. (참고로, 앱 제작사가 계속해서 업그레이드 하고 있으니 저와 다른 버전으로 보일 수 있다.)&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/qOvCo/btqGgEPRQQB/2TSSBgAfk1yo9UFdArqkpK/img.png&quot; width=&quot;414.0&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qOvCo/btqGgEPRQQB/2TSSBgAfk1yo9UFdArqkpK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qOvCo/btqGgEPRQQB/2TSSBgAfk1yo9UFdArqkpK/img.png&quot; data-alt=&quot;[ android application versionName ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qOvCo/btqGgEPRQQB/2TSSBgAfk1yo9UFdArqkpK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqOvCo%2FbtqGgEPRQQB%2F2TSSBgAfk1yo9UFdArqkpK%2Fimg.png&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/qOvCo/btqGgEPRQQB/2TSSBgAfk1yo9UFdArqkpK/img.png&quot; width=&quot;414.0&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ android application versionName ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;Android 애플리케이션이 다음 버전으로 올라갈 때마다 버전의 숫자는 올라가긴 하지만, 앱 제작사가 원하는 규칙에 의해서 카운팅되어 오르는 것이기 때문에, 제작사마다 사용하는 앱의 버전 규칙들은 모두 다를 것이다. &lt;span style=&quot;color: rgb(248, 144, 9);&quot;&gt;&lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;이렇게 만들어진 앱의 버전 정보는 Android 시스템이 업그레이드의 여부를 판단할 때 사용하는 기준이 된다. 따라서, 제작사가 새로운 기능을 넣어서 업그레이드 시켜야하는 앱은 항상 이전 버전보다 &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: rgb(248, 144, 9);&quot;&gt;높아야만 한다.&lt;/span&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;b&gt;&lt;span&gt; &lt;span style=&quot;color: #ee2323;&quot;&gt;Version Code&lt;/span&gt;와&amp;nbsp;&lt;span&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;Version Name&lt;/span&gt;&amp;nbsp;&lt;/span&gt;차이&lt;/span&gt;&lt;/b&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Application &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;Version Name&lt;/b&gt;&lt;/span&gt; : &lt;b&gt;버전을 사용자에게 보여주기 위한 것이 목적&lt;/b&gt;인 버전 닉네임 값 (Android App Store에서 보이는 정보)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Application &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;Version Code&lt;/b&gt;&lt;/span&gt; : 개발자에 의해 작성되고, &lt;b&gt;Android 시스템에 의해 앱 업그레이드시 동작하는 기준이 되는 정보&lt;/b&gt;로, 앱 개발시 특히 중요한 역할을 하는 값&amp;nbsp;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;b&gt;&lt;span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;versionCode&lt;/span&gt;, &lt;span style=&quot;color: #f89009;&quot;&gt;versionName&lt;/span&gt; 설정하기&lt;/span&gt;&lt;/b&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;아래 코드는 애플리케이션 설정파일인 gradle 내용 중 &lt;span style=&quot;color: rgb(51, 51, 51);&quot;&gt;versionCode와 versionName을 설정하는 부분&lt;/span&gt;이다. 글쓴이는 현재 990이라고 임시로 작성했지만, 제작사들은 버전을 구성하는 숫자를 (자릿수를 기준으로 major한 변화나 minor한 변화들이 있을 때마다 특정 위치의 번호를 변경하는 등) &lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;자신들만의 규칙을 정하고 그에 따라 버전 정보들이 관리&lt;/span&gt;되곤 한다.&lt;/p&gt;&lt;pre data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;// ...app.gradle

android {
    compileSdkVersion 29
    defaultConfig {
        applicationId &quot;...&quot;
        minSdkVersion 24
        targetSdkVersion 29
        versionCode 990		// 시스템이 보는 버전 이름
        versionName &quot;9.9.0&quot;		// 사용자가 보는 버전 이름
    }
    ...
}&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;b&gt;&lt;span&gt;Programming으로 &lt;span style=&quot;color: #ee2323;&quot;&gt;versionCode&lt;/span&gt;, &lt;span style=&quot;color: #f89009;&quot;&gt;versionName&lt;/span&gt; 알아내기&lt;/span&gt;&lt;/b&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;Android API 중 PackageInfo를 사용해서 원하는 (패키지를 가진) 애플리케이션의 버전을 알아낼 수 있다. 참고로, API 28 이후로는 PackageInfo의 versionCode 변수는 deprecated 되었기 때문에,&amp;nbsp;getLongVersionCode() 함수를 통해 가져와야 한다. 이처럼 프로그래밍 코드로 작성될 경우, &lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;동작하려는 자신의 앱의 버전을 체크하거나 혹은 호출하려는 앱의 버전을 체크하려고 할 때 유용한 방법&lt;/span&gt;으로 사용된다.&lt;/p&gt;&lt;pre data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;// ...android java code

final PackageInfo pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
int versionCode;
String versionName;

if (android.os.Build.VERSION.SDK_INT &amp;gt;= Build.VERSION_CODES.P) {
    versionCode = (int) pInfo.getLongVersionCode();
} else {
    versionCode = pInfo.versionCode;
}
versionName = pInfo.versionName;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;b&gt;&lt;span&gt;APK로 &lt;span style=&quot;color: #ee2323;&quot;&gt;versionCode&lt;/span&gt;, &lt;span style=&quot;color: #f89009;&quot;&gt;versionName&lt;/span&gt; 알아내기&lt;/span&gt;&lt;/b&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;apk만을 가지고 version을 알아내는 방법 몇가지를 소개한다. 현재 개발자가 가지고 있는 &lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;앱이 제작사가 몇번째 코드로 개발되었는지를 파악하려고 할 때 유용한 방법&lt;/span&gt;들로 사용될 수 있다.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;apk확장자를 zip으로 변경&lt;/span&gt;한 후, &lt;span style=&quot;color: #006dd7;&quot;&gt;AndroidManifest.xml&lt;/span&gt; 파일 확인하기&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;APK 디컴파일&lt;/span&gt;을 한 후, &lt;span style=&quot;color: #006dd7;&quot;&gt;AndroidManifest.xml&lt;/span&gt; 파일 확인하기&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Android Build Tool의 appt를 사용&lt;/span&gt;해서 &lt;span style=&quot;color: #006dd7;&quot;&gt;AndroidManifest.xml&lt;/span&gt; 파일 확인하기&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;apk확장자를 zip으로 변경&lt;/span&gt;한 후, &lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;AndroidManifest.xml&lt;/span&gt; 파일 확인하기&lt;/b&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;span&gt;sample.apk 파일을 강제로 sample.zip 으로 변경&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span&gt;sample.zip 압축 풀기&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span&gt;AndroidManifest.xml 파일 열기&lt;/span&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Browser, 개발용 IDE 혹은 Editor으로 열기&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;versionCode, versionName 확인&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;APK 디컴파일&lt;/span&gt;을 한 후, &lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;AndroidManifest.xml&lt;/span&gt; 파일 확인하기&lt;/b&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;span&gt;sample.apk 파일을 apktool 혹은 (첨부로 링크된) apk 디컴파일 사이트에서 디컴파일을 수행&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span&gt;AndroidManifest 파일 열기&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span&gt;Browser, 개발용 IDE 혹은 Editor으로 열기&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span&gt;versionCode, versionName 확인&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;Android Build Tool의 appt를 사용&lt;/span&gt;해서 &lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;AndroidManifest.xml&lt;/span&gt; 파일 확인하기&lt;/b&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;span&gt;터미널 or 명령창 열기&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span&gt;로컬에서 android sdk 경로로 이동&lt;/span&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;macOS일 경우 default android SDK 경로 : &quot;/Users/&amp;lt;user&amp;gt;/Library/Android/sdk/build-tools/&amp;lt;version&amp;gt;/&quot;&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;windows일 경우 default android SDK 경로 : &quot;C:\Users\&amp;lt;user&amp;gt;\AppData\Local\Android\android-sdk\&quot;&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;아래 명령 실행&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;pre data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;// aapt를 사용하는 경우
joker@MacBookPro$ aapt dump badging sample.apk

// aapt를 사용하는 경우
joker@MacBookPro$ aapt2 dump badging sample.apk.apk

// 출력 내용
versionCode='990' versionName='9.9.0' compileSdkVersion='29' 
...
...&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;b&gt;&lt;span&gt;참고&lt;/span&gt;&lt;/b&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;Android Developer Version Code 관리 &lt;u&gt;&lt;a href=&quot;https://developer.android.com/studio/publish/versioning?hl=ko&quot; target=&quot;_blank&quot;&gt;&lt;span style=&quot;color: rgb(126, 152, 177);&quot;&gt;&lt;span style=&quot;color: rgb(126, 152, 177);&quot;&gt;링크&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/u&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;Android Developer getLongVersionCode() &lt;u&gt;&lt;a href=&quot;https://developer.android.com/reference/android/content/pm/PackageInfo#getLongVersionCode()&quot; target=&quot;_blank&quot;&gt;&lt;span style=&quot;color: rgb(126, 152, 177);&quot;&gt;&lt;span style=&quot;color: rgb(126, 152, 177);&quot;&gt;링크&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/u&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;Android Get versionName or versionCode of an APK &lt;u&gt;&lt;a href=&quot;https://newfivefour.com/android-versioname-versioncode-of-apk.html&quot; target=&quot;_blank&quot;&gt;&lt;span style=&quot;color: rgb(126, 152, 177);&quot;&gt;&lt;span style=&quot;color: rgb(126, 152, 177);&quot;&gt;링크&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/u&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;Android Developer AAPT2 (android asset packaging tool)&amp;nbsp;&lt;u&gt;&lt;a href=&quot;https://developer.android.com/studio/command-line/aapt2?hl=ko&quot; target=&quot;_blank&quot;&gt;&lt;span style=&quot;color: rgb(126, 152, 177);&quot;&gt;&lt;span style=&quot;color: rgb(126, 152, 177);&quot;&gt;링크&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/u&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;APKTool &lt;a href=&quot;https://ibotpeaches.github.io/Apktool/install/&quot; target=&quot;_blank&quot;&gt;&lt;span style=&quot;color: rgb(126, 152, 177);&quot;&gt;&lt;span style=&quot;color: rgb(126, 152, 177);&quot;&gt;링크&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;APK Decompilers Online &lt;a href=&quot;http://www.javadecompilers.com/apk&quot; target=&quot;_blank&quot;&gt;&lt;span style=&quot;color: rgb(126, 152, 177);&quot;&gt;&lt;span style=&quot;color: rgb(126, 152, 177);&quot;&gt;링크&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>dev-tips/android</category>
      <category>Android</category>
      <category>versionCode</category>
      <category>versionName</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/200</guid>
      <comments>https://caileb.tistory.com/200#entry200comment</comments>
      <pubDate>Tue, 4 Aug 2020 01:30:41 +0900</pubDate>
    </item>
    <item>
      <title>go - rune 타입 (한글, 유니코드, UTF-8)</title>
      <link>https://caileb.tistory.com/199</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;rune&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;(&lt;span style=&quot;color: #ee2323;&quot;&gt;한글, 유니코드, UTF-8&lt;/span&gt;)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;rune 타입은 다른 언어에서 잘 볼 수 없는 타입이다. 타입의 이름을 왜 rune으로 정의했을까 궁금증으로 찾아보니, &lt;span style=&quot;color: #f89009;&quot;&gt;룬 문자(runic alphabets)에서 가져온건 아닐까 생각&lt;/span&gt;된다. 룬 문자는 옛날에 북유럽의 게르만족(=독일사람)이 &lt;span style=&quot;color: #f89009;&quot;&gt;라틴문자를 사용하기 전에 사용되던 문자&lt;/span&gt;라고 한다. 아래 첨부한 그림에 포함된 글이 룬 문자로 작성된 것이라고 한다. 바로 이 문자를 컴퓨터로 표현할 수 있는 인코딩 타입 중 하나가 유니코드이다. &lt;span style=&quot;color: #f89009;&quot;&gt;룬 문자에 대한 유니코드가 &amp;nbsp;U+16A0 ~ U+16FF 사이에 정의&lt;/span&gt;되어있기 때문에, 개발자가 원한다면 프로그램 위에 출력시킬 수도 있다. 이러한 정황들을 봤을 때, 개인적으로 잠깐 &quot;go언어를 설계한 사람이 언어를 전공한 친구에게 컴퓨터에 모든 문자를 표현할 수 있는 타입이 하나 있는데 작명을 부탁한 후, runic alphabet을 의미하는 rune이라는 이름을 받아오진 않았을까&quot;생각해봤다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5jdMx/btqFNsDAqtB/xywmigiwk7IbNTtF2hOH1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5jdMx/btqFNsDAqtB/xywmigiwk7IbNTtF2hOH1k/img.png&quot; data-alt=&quot;[ unicode - runic alphabet ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5jdMx/btqFNsDAqtB/xywmigiwk7IbNTtF2hOH1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5jdMx%2FbtqFNsDAqtB%2Fxywmigiwk7IbNTtF2hOH1k%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ unicode - runic alphabet ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;rune 타입은 유니코드(UTF-8/Unicode)를 쉽게 제어하기 위한 타입이다. Go 언어의 &lt;span style=&quot;color: #f89009;&quot;&gt;rune 타입은 int32을 재정의&lt;/span&gt;해서 사용되고 있다. 따라서 &lt;span style=&quot;color: #f89009;&quot;&gt;32bit로 제어&lt;/span&gt;되고 있다는 것을 알 수 있다. 직접 코딩을 할 때는 &lt;span style=&quot;color: #f89009;&quot;&gt;&quot;unicode/utf8&quot;패키지에서 제공하는 API들을 사용해서 rune 타입관련 함수들을 쉽게 활용&lt;/span&gt;할 수도 있으니 참고바란다. 추가로, 아래 영어로 작성된 문장은 go lang 문서에서 rune 타입을 어떻게 정의했는지에 대해 설명하는 내용이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&quot;rune is an alias for int32 and is equivalent to int32 in all ways. It is used, by convention, to distinguish character values from integer values.&quot;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;rune&lt;/span&gt; : 유니코드(UTF-8)을 표현하는 타입&lt;/b&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;16 bit로 표현되는 UTF-16이 아닌, 8 bit로 표현되는 &lt;span style=&quot;color: #f89009;&quot;&gt;UTF-8 (8-bit Unicode Transformation Format) 인코딩 방식을 사용한다는 것을 주의&lt;/span&gt;한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;유니코드 인코딩에서&amp;nbsp;&lt;span&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;한글은 3byte를 사용&lt;/span&gt;하고, &lt;span style=&quot;color: #f89009;&quot;&gt;영어는 1byte를 사용하는 것을 주의&lt;/span&gt;&lt;/span&gt;한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;유니코드는 한글을 지원하는 인코딩 방식이기 때문에,&amp;nbsp;&lt;span style=&quot;color: #f89009;&quot;&gt;한글을 사용하는 프로그램 개발에 유용&lt;/span&gt;하게 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oSK8J/btqFNqTubWZ/69E1PphtuzAjwD0rIo4kO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oSK8J/btqFNqTubWZ/69E1PphtuzAjwD0rIo4kO1/img.png&quot; data-alt=&quot;[ unicode table - U+D600 ~ U+D77F]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oSK8J/btqFNqTubWZ/69E1PphtuzAjwD0rIo4kO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoSK8J%2FbtqFNqTubWZ%2F69E1PphtuzAjwD0rIo4kO1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ unicode table - U+D600 ~ U+D77F]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m25f4/btqFNgpMh9O/0DdOXrpFfKXNIhCwjMFd01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m25f4/btqFNgpMh9O/0DdOXrpFfKXNIhCwjMFd01/img.png&quot; data-alt=&quot;[ unicode - UTF-8]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m25f4/btqFNgpMh9O/0DdOXrpFfKXNIhCwjMFd01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm25f4%2FbtqFNgpMh9O%2F0DdOXrpFfKXNIhCwjMFd01%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ unicode - UTF-8]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;&lt;b&gt;sample code&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;아래 코드는 rune 타입을 사용한 몇가지 코드들이 작성되어 있다. '홍' 글자를 UTF-8 단위로 제어할 수 있는 rune 타입을 사용한 예제이다. '홍' 글자를 unicode 표에서 찾아보면 U+D64D에 해당하는 것을 알 수 있다. 이 값을 코드로 작성하면 '\ud64d'로 표현할 수 있다. 참고로, 아래 예제 코드는 go language 기준으로 작성되었으니 다른 언어는 조금 다를 수도 있다는 것을 알아두자. 아래 코드를 실행하면 UTF-8 인코딩 기준의 '홍'값을, 10진수로 표현하면 54861이라는 값을 가지고, 한글 문자이므로 3byte가 필요하기때문에 출력도 동일하게 되는 것을 알 수 있다. 참고로, 영어 알파벳 문자인 'a' 값은 1byte가 필요하다.&lt;/p&gt;
&lt;pre id=&quot;code_1595095410703&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package main

import (
    &quot;fmt&quot;
    &quot;unicode/utf8&quot;
)

func main() {
    // get rune from string
    var r1 rune
    str := &quot;홍길동&quot;
    r1 = []rune(str)[0]

    // get rune from character
    var r2 rune
    r2 = '홍'
    
    // get rune from character (unicode point)
    var r3 rune
    r3 = '\ud64d'
    
    // get len of rune type (한글) 
    len1 := utf8.RuneLen('홍')
    
    // get len of rune type (영어)
    len2 := utf8.RuneLen('a')
    
    fmt.Println(r1, r2, r3, len1, len2)
}

// 출력
// 54861 54861 54861 3 1&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;byte&lt;/span&gt; : 아스키(ASCII) 코드로 표현하는 타입&lt;/b&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;8bit 인코딩 방식이라는 것을 주의&lt;/span&gt;한다. 첫 비트는 0으로 시작하고 나머지 &lt;span style=&quot;color: #f89009;&quot;&gt;7개 비트로 표현하는 방식&lt;/span&gt;이다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LU6xp/btqFNFWSXLv/yF9rQGsAKHsPnvB2nCxrpK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LU6xp/btqFNFWSXLv/yF9rQGsAKHsPnvB2nCxrpK/img.png&quot; data-alt=&quot;[ ascii code table ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LU6xp/btqFNFWSXLv/yF9rQGsAKHsPnvB2nCxrpK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLU6xp%2FbtqFNFWSXLv%2FyF9rQGsAKHsPnvB2nCxrpK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ ascii code table ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;&lt;b&gt;sample code&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;아래 코드는 byte 타입을 사용한 예제 코드이다. byte 타입은 1바이트 크기 내에서 표현되고, 7bit로 표현되는 ASCII코드로 많이 사용되는 타입이기 때문에, 이미 알고 있을 것으로 생각되어 자세한 설명은 추가하지 않았다.&lt;/p&gt;
&lt;pre id=&quot;code_1595095490251&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package main

import (
    &quot;fmt&quot;
)

func main() {
    // get byte from string
    var b1 byte
    str := &quot;apple&quot;
    b1 = []byte(str)[0]

    // get byte from character
    var b2 byte
    b2 = 'a'
    
    // get byte from character (ascii)
    var b3 byte
    b3 = 97

    // get byte from character (hex)
    var b4 byte
    b4 = 0x61
    
    // get len of byte type
    bLen := len(&quot;a&quot;)
    
    fmt.Println(b1, b2, b3, b4, bLen)
}

// 출력
// 97 97 97 97 1&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;참고&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;룬 문자 (runic alphabet) &lt;span style=&quot;color: #7e98b1;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://en.wikipedia.org/wiki/Template:Unicode_chart_Runic&quot;&gt;link&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;유니코드 테이블 (unicode table) &lt;span style=&quot;color: #7e98b1;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://www.utf8-chartable.de/unicode-utf8-table.pl?start=54784&amp;amp;number=512&quot;&gt;link&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;유니코드 UTF-8 표현 (unicode - utf-8) &lt;span style=&quot;color: #7e98b1;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://en.wikipedia.org/wiki/UTF-8&quot;&gt;link&lt;/a&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://ko.wikipedia.org/wiki/UTF-8&quot;&gt;link2&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;아스키 테이블 (ascii code) &lt;span style=&quot;color: #7e98b1;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://ko.wikipedia.org/wiki/ASCII&quot;&gt;link&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;고 언어 문서 (go lang builtin 타입 - rune) &lt;span style=&quot;color: #7e98b1;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://golang.org/pkg/builtin/#rune&quot;&gt;link&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>language/go</category>
      <category>GO</category>
      <category>Rune</category>
      <category>utf8</category>
      <category>한글</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/199</guid>
      <comments>https://caileb.tistory.com/199#entry199comment</comments>
      <pubDate>Sun, 19 Jul 2020 03:26:04 +0900</pubDate>
    </item>
    <item>
      <title>circleci - config.yml 작성방법</title>
      <link>https://caileb.tistory.com/198</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot; style=&quot;text-align: center;&quot;&gt;CircleCI&lt;/h2&gt;&lt;h4 data-ke-size=&quot;size20&quot; style=&quot;text-align: center;&quot;&gt;(&lt;span style=&quot;color: rgb(238, 35, 35);&quot;&gt;config.yml 작성방법&lt;/span&gt;)&lt;/h4&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/cROLaR/btqEk3ltXb5/NkKkGiMhTNrqtsVYtdNJfk/img.png&quot; width=&quot;236.0&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cROLaR/btqEk3ltXb5/NkKkGiMhTNrqtsVYtdNJfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cROLaR/btqEk3ltXb5/NkKkGiMhTNrqtsVYtdNJfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cROLaR/btqEk3ltXb5/NkKkGiMhTNrqtsVYtdNJfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcROLaR%2FbtqEk3ltXb5%2FNkKkGiMhTNrqtsVYtdNJfk%2Fimg.png&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/cROLaR/btqEk3ltXb5/NkKkGiMhTNrqtsVYtdNJfk/img.png&quot; width=&quot;236.0&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;CircleCI는 개발자가 코드를 개발할 때 생산성을 높여주는 CI 도구 중 하나이다. CI는 Continuous Integration의 약자로 지속적인 통합이라는 의미를 가진다. &lt;span style=&quot;color: rgb(238, 35, 35);&quot;&gt;CI 툴을 사용하는 목적은 개발자의 코드의 품질을 좋게 만드는 것으로, CI 툴은 개발자의 코드가 미리 정의해둔 조건들에 만족하는지를 판단하고 이에 따라 코드를 통합시킴으로써 코드의 품질을 유지할 수 있는 기능들을 제공&lt;/span&gt;하고 있다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;Github 저장소를 만들고 올려둔, 개발 중인 프로젝트에 CI 툴인 CircleCI를 적용하면 달라지는 것들?&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;예)&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Lint 실행과 Test 코드 실행에 대한 성공여부를 Slack 메시지로 전달받을 수 있다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Circle Ci는 사용자가 Push를 할 때마다, 적용되는 코드에 Lint 적용이 되었는지 판단한다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Circle Ci는 테스트가 모두 통과한 코드에 대해 자동으로 Tag를 만든다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;개발자가 실무에서 많이 사용하는 &lt;span style=&quot;color: rgb(238, 35, 35);&quot;&gt;대표적인 CI 툴로는 CircleCI, Jenkins, TravisCI&lt;/span&gt;가 있다. 이 중에서 무료로 사용할 수 있는 CI인 CircleCI에 대해 포스팅해보려 한다. &lt;span style=&quot;color: rgb(238, 35, 35);&quot;&gt;이 글의 목적은 CircleCI에 대해 전부를 설명하는 것이 아닌, CircleCI를 처음 사용하면서 원하는 기능에 대한 자세한 설정이나 설명들을 찾아보기 전에 기본적인 구성을 알기 위함&lt;/span&gt;이기 때문에, 설명이 부족할 수 있다. 자세한 내용을 원한다면 CircleCI 공식 사이트 혹은 아래 참조로 추가해둔 사이트를 살펴보기 바란다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;CircleCI를 사용하면, 프로젝트 내에 &lt;span style=&quot;color: rgb(238, 35, 35);&quot;&gt;.circleci&lt;/span&gt; 라는 디렉토리가 만들어지고, 그 안에 &lt;span style=&quot;color: rgb(238, 35, 35);&quot;&gt;config.yml&lt;/span&gt; 이라는 파일이 있는데, 이 파일에 개발자는 자동화하기를 원하는 CI 기능들을 작성하게 된다. 따라서 CircleCI를 사용할 때는 config.yml이라는 파일을 어떻게 작성하는지를 알면 사용법의 반 이상은 이해하고 있는 것이라고 생각한다. CircleCI의 어떤 버전 사용하냐에 따라 config.yml에서 내에서 사용할 수 있는 내용이 약간 차이가 있는데. 현재&amp;nbsp; &lt;span style=&quot;color: rgb(238, 35, 35);&quot;&gt;글에서는 CircleCI의 버전은 2.1을 바탕으로 설명&lt;/span&gt;하고 있으니 참고바란다. 또한 이 글은 &lt;span style=&quot;color: rgb(238, 35, 35);&quot;&gt;Github와 CircleCI를 연동해서 사용하는 것을 전제로 가정하고 설명&lt;/span&gt;하는 것도 참고바란다. 가장 먼저 아래 작성된 config.yml 파일의 샘플을 살펴보자.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;config.yml&lt;/p&gt;&lt;pre data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;version: 2.1

excutors:
    node-executor: 
        docker: 
            - image: circleci/node:8
    ruby-executor: 
        docker: 
            - image: ruby

jobs:
    build:
        executor: node-executor
        steps:
            - checkout
            - restore_cache:
                keys:
                    - dep-bundle-{{ checksum &quot;package.json&quot; }}
            - run: npm install
            - save_cache:
                key: dep-bundle-{{ checksum &quot;package.json&quot; }}
                    - /repo/node_modules
            - run: npm run lint &amp;amp;&amp;amp; npm run test &amp;amp;&amp;amp; npm run downloadsolc &amp;amp;&amp;amp; npm run make-mock-compiler &amp;amp;&amp;amp; npm run setupremix &amp;amp;&amp;amp; npm run build
            - run: ./ci/browser_tests.sh

    a_test:
        executor: ..
        steps:
            - checkout
            - ...

workflows:
    version: 2
    build_and_others:
        jobs:
            - build
            - a_test:
                requires:
                    - build
                filters:
                    branches:
                        only: master&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(238, 35, 35);&quot;&gt;version&lt;/span&gt; : CI 버전으로 2.1을 사용하였다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(238, 35, 35);&quot;&gt;executors&lt;/span&gt; : 개발자가 정의한 작업을 실행시킬 환경을 정의하고 그 안에서 함께 실행되어야 하는 프로그램에 대한 이미지를 정의한다. (ex, docker, macOS) 이 곳에 정의한 executor는 작업(job) 내용을 작성할 때 실행환경을 적는 executor라는 필드에서 사용된다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(238, 35, 35);&quot;&gt;jobs&lt;/span&gt; : 자동화 하고 싶은 작업(job)으로, 네이밍은 개발자가 직접 정의한다. 여기서 알아둘어야 할 점은 job을 정의한 것일뿐, 모든 job들을 어떤 방법으로 실행시킬지에 대해서는 workflow 항목에서 정의한다는 것이니 혼동하지 말자. (ex, build, a_test)&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(238, 35, 35);&quot;&gt;executor&lt;/span&gt;&amp;nbsp;: executors에 정의해둔 개발 환경 중에 작업에 필요한 환경에 대해 직접 정의한 이름을 작성한다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(238, 35, 35);&quot;&gt;steps&lt;/span&gt; : 작업(job)에 대해 실행할 내용들을 차례로 작성한다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(238, 35, 35);&quot;&gt;restore_cache&lt;/span&gt; : 캐시(cache)를 복원한다. 작업이 실행되기 전에, 다른 작업(job)에서 save_cache 을 통해 캐시에 저장된 것이 있다면 이를 사용하기 위해 복원시킨다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(238, 35, 35);&quot;&gt;save_cache&lt;/span&gt; : 캐시(cache)를 저장한다. 이때 캐싱으로 사용하기 위해 저장시킨 내용은 다른 job들에서도 사용된다. 아래 예시로 작성한 config.yml에선, nodeJS 프로젝트에서 dependency 라이브러리들이 저장되는 디렉토리가 node_module인데, 이 디렉토리를 캐싱해두고 다른 작업(job)들에서도 사용할 수 있도록 하는 것이다. 참고로, nodeJS 프로젝트의 node_module디렉토리는 maven 프로젝트의 .m2 디렉토리와 동일한 역할이므로 maven 프로젝트라면 .m2가 될 것이다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(238, 35, 35);&quot;&gt;run&lt;/span&gt; &amp;nbsp;: 실행할 명령(command)이다. 예제에서는 npm 라이브러리들을 다운로드하는 npm install 이라는 명령을 실행시키거나 lint 라이브러리를 실행시키는 npm run lint 라는 명령을 실행시키는 등의 작업들을 예시로 작성하였다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(238, 35, 35);&quot;&gt;workflows&lt;/span&gt; : 앞에서 각 작업(job)들을 모두 정의했다면, 이 작업들을 어떤 조합으로 어떤 조건에서 어떤 순서로 동작할지를 정의한다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(238, 35, 35);&quot;&gt;requires&lt;/span&gt; : 현재 작업(job)을 수행하기 이전에 선행되어야할 작업 목록을 작성한다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(238, 35, 35);&quot;&gt;filter&lt;/span&gt; : 현재 작업(job)이 실행되기 위한 조건을 작성한다. 예를 들면, Github의 특정 Branch(ex, master, dev)만 실행할 수 있도록 제한할 수 있다. 예제에서는 master 브런치의 조건에서만 실행시키도록 작성했다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;각 항목들에 대한 설명으로 이해가 부족한 부분이 있을 것 같다. 글쓴이의 경우 예제 코드에서 작성된 내용 중에 직관적으로 이해가 가지 않았던 부분으로, CircleCI 에서 지원하는 기능 중에 executor와 cache 이 두 가지 관련 항목에 대해서 조금 더 설명해본다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(238, 35, 35);&quot;&gt;CircleCI Executor ?&lt;/span&gt;&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;executors와 executor항목과 관련된 것이다. Github에 오픈되어 올려둔 많은 프로젝트들을 보면, 대부분이 docker 환경을 사용하고 이 환경 내에 원하는 image를 적용해서 사용하고 있다. 하지만, docker 외에도 macOS, windows, linux를 더 지원하고 있고, 각 OS는 CircleCI에서 제공하는 가상 머신(Virtual Machine) 위에서 동작된다. 결과적으로 executor의 값을 변경해서 다른 원하는 OS 환경에서 테스트가 가능하다는 것이다.&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/Gl10J/btqEnHm0CAo/tq1amVNgBNDpmWCiiOKqhk/img.png&quot; width=&quot;692.0&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Gl10J/btqEnHm0CAo/tq1amVNgBNDpmWCiiOKqhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Gl10J/btqEnHm0CAo/tq1amVNgBNDpmWCiiOKqhk/img.png&quot; data-alt=&quot;[ circleci - executor using docker ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Gl10J/btqEnHm0CAo/tq1amVNgBNDpmWCiiOKqhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGl10J%2FbtqEnHm0CAo%2Ftq1amVNgBNDpmWCiiOKqhk%2Fimg.png&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/Gl10J/btqEnHm0CAo/tq1amVNgBNDpmWCiiOKqhk/img.png&quot; width=&quot;692.0&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ circleci - executor using docker ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/W8SRx/btqEmoB9Zxj/ceaNgrzvsnccmoql1RTk90/img.png&quot; width=&quot;697.0&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/W8SRx/btqEmoB9Zxj/ceaNgrzvsnccmoql1RTk90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/W8SRx/btqEmoB9Zxj/ceaNgrzvsnccmoql1RTk90/img.png&quot; data-alt=&quot;[ cicleci - executor using macos ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/W8SRx/btqEmoB9Zxj/ceaNgrzvsnccmoql1RTk90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FW8SRx%2FbtqEmoB9Zxj%2FceaNgrzvsnccmoql1RTk90%2Fimg.png&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/W8SRx/btqEmoB9Zxj/ceaNgrzvsnccmoql1RTk90/img.png&quot; width=&quot;697.0&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ cicleci - executor using macos ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(238, 35, 35);&quot;&gt;CircleCI Cache ?&amp;nbsp;&lt;/span&gt;&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;config.yml 작성이 완료됬다면, 개발자가 정의해둔 작업(job)들이 많이 있는 것을 볼 수 있다. 이 파일에 작성된 각각의 작업들(jobs)은 서로 다른 프로젝트가 아닌, 하나의 동일한 프로젝트를 서로 다른 형태로 테스트하도록 정의한 것이다. 이 말은 정의해둔 서로 다른 작업들이 동일한 코드와 리소스들을 공유하고 있다는 것이기 때문에, 각 작업을 수행할 때마다 다른 작업에서 처리된 내용들을 그대로 써도 되는 부분들이 많다는 것이다. 따라서 CircleCI는 각 작업의 내용들에 대해 다른 작업(job)에서도 그대로 빠르게 사용할 수 있도록 cache라는 기능을 제공하고 있는 것이다. 아래 그림처럼 workflow에 정의된 작업들이 수행될 때 캐시가 그대로 다음 작업의 실행에 영향을 미치게 함으로써 테스트의 속도를 높일 수 있다. 이 cache는 restore_cache와 save_cache항목으로 설정할 수 있다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/dJ4Mlj/btqEmLXX8zC/Lev3RpkiS6nBiR4zpkyxQK/img.png&quot; width=&quot;626.0&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dJ4Mlj/btqEmLXX8zC/Lev3RpkiS6nBiR4zpkyxQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dJ4Mlj/btqEmLXX8zC/Lev3RpkiS6nBiR4zpkyxQK/img.png&quot; data-alt=&quot;[ circleci - cache ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dJ4Mlj/btqEmLXX8zC/Lev3RpkiS6nBiR4zpkyxQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdJ4Mlj%2FbtqEmLXX8zC%2FLev3RpkiS6nBiR4zpkyxQK%2Fimg.png&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/dJ4Mlj/btqEmLXX8zC/Lev3RpkiS6nBiR4zpkyxQK/img.png&quot; width=&quot;626.0&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ circleci - cache ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot; style=&quot;text-align: left;&quot;&gt;참고&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;Restoring Cache 설명&amp;nbsp;&lt;a href=&quot;https://circleci.com/docs/2.0/caching/&quot; target=&quot;_blank&quot;&gt;&lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;&lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;link&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;CircleCI Executors 설명&amp;nbsp;&lt;a href=&quot;https://circleci.com/docs/2.0/executor-intro/&quot; target=&quot;_blank&quot;&gt;&lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;&lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;link&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;CircleCI 2.1 Docs 문서 &lt;a href=&quot;https://circleci.com/docs/reference-2-1/#circleci-2-1-configuration-reference-guide&quot; target=&quot;_blank&quot;&gt;&lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;&lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;link&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;Github의 어떤 nodeJS Project 중 .circleci/config.yml 파일 (CircleCI 2.1 사용)&amp;nbsp;&lt;a href=&quot;https://github.com/heug/node-orb/blob/master/.circleci/config.yml&quot; target=&quot;_blank&quot;&gt;&lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;&lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;link&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;Github의 어떤 nodeJS Project 중 .circleci/config.yml 파일 (CircleCI 2.0 사용)&amp;nbsp;&lt;a href=&quot;https://github.com/ethereum/remix-ide/blob/fa492c5ea887ba9431abe9be5f335b13fb7d76c9/.circleci/config.yml&quot; target=&quot;_blank&quot;&gt;&lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;&lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;link&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;Github의 어떤 maven Project 중 .circleci/config.yml 파일 (CircleCI 2.0 사용)&amp;nbsp;&lt;a href=&quot;https://github.com/hyperledger/quilt/blob/master/.circleci/config.yml&quot; target=&quot;_blank&quot;&gt;&lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;&lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;link&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;</description>
      <category>environment/CI, CD</category>
      <category>CirCleCI</category>
      <category>config.yml</category>
      <category>Github</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/198</guid>
      <comments>https://caileb.tistory.com/198#entry198comment</comments>
      <pubDate>Sat, 23 May 2020 04:06:52 +0900</pubDate>
    </item>
    <item>
      <title>javascript - yield (비동기 처리</title>
      <link>https://caileb.tistory.com/197</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;yield&lt;/h2&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;비동기 처리 (Asynchronous processing model)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;javascipt에서 지원하는 함수 중에 yield라는 함수가 있다&lt;/span&gt;. 글쓴이는 javascript를 잘 모르는 상태에서 react를 개발을 시작했었고, 처음으로 yield라는 함수를 마주하게 되었다. 그 당시 이 함수가 react에서 제공하는 특수한 함수인 줄 알았다. 하지만, yield는 react를 개발하면서 처음 봤기에 잘 못알고 있었으며, javascript의 &lt;span style=&quot;color: #0593d3;&quot;&gt;ES6부터 사용가능한 함수&lt;/span&gt; 중 하나라는 것을 알았다. yield 키워드는 javascript의 비동기 처리를 위해 만들어진 것으로, 개발을 할 때 자주사용하게 되는 함수이다. 특히 react 개발을 하고 있다면 분명 사용하는 키워드일 것이다. 이번 포스팅에서는 이러한 yield 키워드가 &lt;span style=&quot;color: #0593d3;&quot;&gt;javascript에서 사용되는 상황&lt;/span&gt;과 &lt;span style=&quot;color: #0593d3;&quot;&gt;react에서 사용되는 상황&lt;/span&gt;을 보고 어떻게 다른 형태로 사용되는지 비교해보려 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;javascript의 &lt;span style=&quot;color: #f89009;&quot;&gt;yield&lt;/span&gt; 키워드만을 사용하는 경우&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MNw9L/btqDZYxGvwZ/kcXKvPeUdyBExankKTGyyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MNw9L/btqDZYxGvwZ/kcXKvPeUdyBExankKTGyyK/img.png&quot; data-alt=&quot;[ yield 만 사용하는 경우 ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MNw9L/btqDZYxGvwZ/kcXKvPeUdyBExankKTGyyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMNw9L%2FbtqDZYxGvwZ%2FkcXKvPeUdyBExankKTGyyK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ yield 만 사용하는 경우 ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;function*으로 함수를 정의하고, 이 함수 내에 포함되어 있는 property 중에 next()를 차례로 호출함으로써, 함수 내에 코딩해둔&amp;nbsp;yield를 차례로 실행&lt;/span&gt;시키는 것이고, &lt;span style=&quot;color: #f89009;&quot;&gt;아래 3가지 키워드&lt;/span&gt;를 사용해서 코드가 만들어진다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;function* &lt;/span&gt;&lt;/b&gt;: function 뒤에 astrok(*)를 붙이면, 이를 &lt;u&gt;&lt;span&gt;generator function&lt;/span&gt;&lt;/u&gt;이라고 불리는 함수형태가 된다. 이 함수는 &lt;u&gt;비동기처리를 할 수 있는 함수로서 내부에서 yield라는 키워드를 사용&lt;/u&gt;해서 코딩을 하면, 해당 라인까지만 코드를 실행하고 뒷부분은 다시 실행을 요청할 때까지 실행하지 않게함으로써 마치 함수가 실행 중에 특정부분에서 일시 정지된 것처럼 동작함으로써, 비동기 처리를 하도록 만들 수가 있다.&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;yield &lt;/span&gt;&lt;/b&gt;: &lt;span&gt;generator function 안에 &lt;u&gt;yield 키워드를 사용한 곳들을 기준으로 코드가 잘려서 실행&lt;/u&gt;&lt;/span&gt;하게 된다고 볼 수 있다. 만약에, yield문을 만날때까지 특정 처리를 수행했다면, 함수는 잠깐 잘린 상태로 정지해있다가 다시 이 함수로 접근했을 때(=next함수를 호출했을 때) 멈췄던 부분부터 실행을 이어서 하고 다음 yield문을 만날때까지 쭈욱 실행이 되는 것이다. 이러한 구조로 인해 yield를 기준으로 처리가 나눠진 곳들을 실행시키는 명령을 외부로 넘기는 형태가 만들어지게 되는 것이다. 따라서, &lt;span&gt;generator function을 호출하는 외부 함수(=caller)에서 next()를 실행시킬 때마다 yield로 나눠진 곳들이 하나씩 실행하게&lt;/span&gt;&amp;nbsp;되는 것이다.&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;next() &lt;/b&gt;&lt;/span&gt;: generator function에서 반환한 데이터를 &lt;span&gt;generator&lt;/span&gt;라고 부르는데, &lt;span&gt;g&lt;u&gt;enerator의 next 함수를 실행&lt;/u&gt;&lt;/span&gt;&lt;u&gt;하면 &lt;span&gt;{ value, done }&lt;/span&gt; 이라는 데이터를 반환&lt;/u&gt;한다. 참고로, next()를 실행하는 이유는 generator는 반복구조인 iterator를 사용하는 구조이기 때문이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래는 yield를 사용해서 비동기 처리를 하는 call()이라는 함수를 만들어보았다. 참고로, 함수를 호출할 때는 next()를 사용해도 되지만, 배열을 반복하는 &lt;a href=&quot;https://caileb.tistory.com/196?category=372139&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;for..of문법&lt;/a&gt;을 사용해서 call() 함수 내에 있는 yield를 차례로 호출시킬 수도 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;&lt;span style=&quot;color: #7e98b1;&quot;&gt;sample code&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1588954019035&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function* call() { 
    console.log('execute joker 1'); 
    yield 10; 
    console.log('execute joker 2'); 
    yield (20 + 20); 
    console.log('execute joker 3'); 
    yield (30 * 3); 
} 

// call style 1
let gen = call(); 
console.log(gen.next()); 
console.log(gen.next()); 
console.log(gen.next()); 
console.log(gen.next());

// call style 2
for(let result of call()) { 
    console.log(result); 
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;&lt;span style=&quot;color: #7e98b1;&quot;&gt;sample code output&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1588953995347&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;execute joker 1
{
    value:10,
    done:false
}
execute joker 2
{
    value:40,
    done:false
}
execute joker 3
{
    value:90,
    done:false
}
{
    value:undefined,
    done:true
}
execute joker 1
10
execute joker 2
40
execute joker 3
90&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;javascript의 &lt;span style=&quot;color: #f89009;&quot;&gt;yield&lt;/span&gt;와 (react) &lt;span style=&quot;color: #ef6f53;&quot;&gt;redux-saga의 effects&lt;/span&gt;를 함께 사용하는 경우&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVbPEL/btqD2HnxuyT/hbP0FIQdKqxqkgsHDwkFEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVbPEL/btqD2HnxuyT/hbP0FIQdKqxqkgsHDwkFEK/img.png&quot; data-alt=&quot;[ yield와 redux-saga/effects를 같이 사용하는 경우 ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVbPEL/btqD2HnxuyT/hbP0FIQdKqxqkgsHDwkFEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVbPEL%2FbtqD2HnxuyT%2FhbP0FIQdKqxqkgsHDwkFEK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ yield와 redux-saga/effects를 같이 사용하는 경우 ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt; function*으로 함수를 정의하고, 이 함수를 호출하는 행위는 redux-saga/effects에 위임시키고, redux에서 제공하는 액션(action)을 사용해서 함수 내에 코딩해둔&amp;nbsp;yield를 차례로 실행&lt;/span&gt;시키는 것이다. 이때, redux에서 함수 내의 yield를 차례로 알아서 실행시켜주기 때문에 앞에서 순수 javascript만으로 개발했을 때처럼 next()를 차례로 호출시킬 필요는 없다. 참고로, redux는 앞에서 설명한 ES6의 genrator function을 기반으로 구현되었다. &lt;span style=&quot;color: #f89009;&quot;&gt;아래 2가지 키워드와 redux-saga/effects 함수&lt;/span&gt;들을 사용해서 코드가 만들어지게 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;function* &lt;/b&gt;&lt;/span&gt;: ES6 javascript의 문법으로 앞 설명과 동일&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;yield &lt;/b&gt;&lt;/span&gt;: ES6 javascript의 문법으로 앞 설명과 동일&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;redux-saga/effects 함수&lt;/b&gt; &lt;/span&gt;: react 개발을 할 때, redux-saga/effects 에서 제공되는 함수들(ex, call, put)을 사용하는 경우가 종종 있다. 이렇게 react 개발시 redux 라이브러리들을 사용하게 되면, 앞에서 설명했던 것처럼 &lt;u&gt;function* 함수를 호출할 때 next()를 통해서 호출하는 것이 아닌, 액션(action)이라는 것을 통해서 호출&lt;/u&gt;하게 된다. 그리고 &lt;u&gt;react에서는 액션을 통해 function* 함수를 한번 호출하고 나면 내부에 있는 여러 yield 함수들을 차례로 호출&lt;/u&gt;을 해준다는 점이다. 이러한 점이 개발자로 하여금 비동기처리에 대한 코드를 더욱 쉽게 만들어 준다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #7e98b1;&quot;&gt;참고로 &lt;b&gt;redux-saga/effects 함수로는 all, call, put, takeLatest&lt;/b&gt; 등이 있으며,&amp;nbsp;&lt;b&gt;all&lt;/b&gt;은redux에서 (비동기 처리가 필요한) 함수들을 배열 형태로 넣어서 동시에 병행으로 처리를 수행하는 함수이고,&amp;nbsp;&lt;b&gt;call&lt;/b&gt;은 redux에서 (비동기 처리가 필요한) 함수를 실행하는 함수이다. 보통 ajax call, http, delay 등의 함수를 처리할 때 사용한다. &lt;b&gt;put&lt;/b&gt;은 redux의 액션(action)을 디스패치(dispatch)하는 함수이고, &lt;b&gt;takeLatest&lt;/b&gt;는&amp;nbsp;redux의 (기존에 진행중이던 작업이 있다면 취소하고) 가장 마지막으로 실행된 작업을 수행하는 함수이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래는 yield를 사용하긴 하지만, react를 사용할 때는 어떠한 형태가 나오는지 만들어보았다. 아래 코드는 전체 코드의 일부분을 발췌한 것이기 때문에 동작을 하려면 다른 코드들이 더 있어야 한다. yield를 사용할 때의 상황이 react개발할때는 javascript만 사용할 때와 어떻게 다른지 살펴보기 위함이기 때문에 아래 코드만으로 이해가 가능할 것이라고 생각된다. 참고로, react 개발시 비동기 처리에 대한 다양한 기능들을 지원해주는 라이브러리인 redux-saga를 정말 많이 사용한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;&lt;span style=&quot;color: #7e98b1;&quot;&gt;sample code&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1588954077540&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// using redux saga effects in react
import { createAction } from 'redux-actions';
import { delay } from 'redux-saga'
import { all, call, put, takeLatest } from 'redux-saga/effects';

// react-specific (2. 사용자가 react-component(=UI 부분코드)로 출력되는 특정 버튼을 누르면 login 액션을 호출)
const LOGIN = 'LOGIN';
export const login = createAction(LOGIN);

// javascript generator function (3. login 액션에 대한 수행을 하는 담당하는 함수)
function* loginSaga(data) {

    // check 1
    yield delay(3000);
    console.log('execute joker 1');

     // check 2
    yield delay(2000);
    console.log('execute joker 2');

    // check 3
    yield delay(5000);
    return yield call(httpRequest, { data });
}

// react-specific (1. redux-saga로 미리 사용할 액션을 등록해두어야 login 액션을 받을 수 있음)
export function* actionLogin() {
  yield takeLatest(LOGIN, loginSaga);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;&lt;span style=&quot;color: #7e98b1;&quot;&gt;sample code output&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1588954065816&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// check 1에서 3초간 일시정지 후.. 다음 라인부터 실행한다.
execute joker 1

// check 2에서 2초간 일시정지 후.. 다음 라인부터 실행한다.
execute joker 2

// check 3에서 5초간 일시정지 후.. 다음 라인부터 실행한다.
// httpRequest함수에 대한 결과를 리턴한다.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고&lt;/h2&gt;
&lt;p&gt;&lt;span style=&quot;color: #7e98b1;&quot;&gt;Mozila Generator.prototype.next()&lt;/span&gt; &lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Generator/next&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;link&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #7e98b1;&quot;&gt;Mozila yield*&lt;/span&gt; &lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/yield&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;link&lt;/a&gt;&lt;/p&gt;</description>
      <category>language/javascript</category>
      <category>function*</category>
      <category>generator function</category>
      <category>redux-saga/effects</category>
      <category>yield</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/197</guid>
      <comments>https://caileb.tistory.com/197#entry197comment</comments>
      <pubDate>Sat, 9 May 2020 01:24:28 +0900</pubDate>
    </item>
    <item>
      <title>javascript - 반복문(for, forEach/map, for..in/for..of)</title>
      <link>https://caileb.tistory.com/196</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;javascript 반복문&lt;/h2&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;(for문, forEach문, map문, for..in문, for..of문 비교)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-05-02 오후 11.13.41.png&quot; data-origin-width=&quot;1372&quot; data-origin-height=&quot;490&quot; width=&quot;658&quot; height=&quot;235&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pJ0u0/btqDSAB4jDs/rnNEgA5DEumhkEHbue4o41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pJ0u0/btqDSAB4jDs/rnNEgA5DEumhkEHbue4o41/img.png&quot; data-alt=&quot;[ javascript 반복문 비교 ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pJ0u0/btqDSAB4jDs/rnNEgA5DEumhkEHbue4o41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpJ0u0%2FbtqDSAB4jDs%2FrnNEgA5DEumhkEHbue4o41%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-05-02 오후 11.13.41.png&quot; data-origin-width=&quot;1372&quot; data-origin-height=&quot;490&quot; width=&quot;658&quot; height=&quot;235&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ javascript 반복문 비교 ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;javascript에서도&amp;nbsp;일반적으로&amp;nbsp;프로그래밍&amp;nbsp;언어에서&amp;nbsp;제공하는&amp;nbsp;반복문인&amp;nbsp;for문을&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있다.&amp;nbsp;그런데,&amp;nbsp;오픈&amp;nbsp;소스들을&amp;nbsp;찾아보면&amp;nbsp;for문이&amp;nbsp;조금&amp;nbsp;다르게&amp;nbsp;생겨서&amp;nbsp;별것도&amp;nbsp;아닌데&amp;nbsp;형태가&amp;nbsp;다르다보니&amp;nbsp;이해가&amp;nbsp;바로&amp;nbsp;안될&amp;nbsp;수&amp;nbsp;있다.&amp;nbsp;&lt;span style=&quot;color: #006dd7;&quot;&gt;javascript&amp;nbsp;같은&amp;nbsp;경우에는&amp;nbsp;for문을&amp;nbsp;변형된&amp;nbsp;다른&amp;nbsp;몇가지&amp;nbsp;형태로도&amp;nbsp;제공&lt;/span&gt;하고&amp;nbsp;있으니,&amp;nbsp;잠깐&amp;nbsp;시간을&amp;nbsp;두고&amp;nbsp;살펴보고&amp;nbsp;넘어가는&amp;nbsp;것이&amp;nbsp;javascript&amp;nbsp;코드를&amp;nbsp;보는데&amp;nbsp;속이&amp;nbsp;편하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;for 기본 문법&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot;&gt;
&lt;li&gt;for( ..; ..; .. ) 형태&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;for 기본 문법이 변형된 형태&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;for( .. in .. ) &lt;/span&gt;형태 / &lt;span style=&quot;color: #f89009;&quot;&gt;for( .. of .. ) &lt;/span&gt;형태&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;array.forEach(callback function) &lt;/span&gt;형태 / &lt;span style=&quot;color: #f89009;&quot;&gt;array.map(callback function) &lt;/span&gt;형태&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;for&amp;nbsp;기본&amp;nbsp;문법&amp;nbsp;&lt;/h2&gt;
&lt;p&gt;javascript도&amp;nbsp;아래처럼&amp;nbsp;c나&amp;nbsp;java언어에서&amp;nbsp;사용되는&amp;nbsp;형태의&amp;nbsp;for&amp;nbsp;문을&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있다.&amp;nbsp;하지만,&amp;nbsp;이러한&amp;nbsp;스타일은&amp;nbsp;요즘&amp;nbsp;javascript&amp;nbsp;진영에서는&amp;nbsp;잘&amp;nbsp;사용되지&amp;nbsp;않는다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;for sample code&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1588428926452&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ...for.js
const array = [&quot;j1&quot;, &quot;j2&quot;, &quot;j3&quot;];

for(let i=0; i&amp;lt;array.length; i++) {
    console.log(array[i]);
}


// result
j1
j2
j3&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;for 기본 문법이 변형된 형태&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;맵(map)&lt;/span&gt; 내 원소들을 반복하는 &lt;span style=&quot;color: #f89009;&quot;&gt;for..in 문법&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;배열(array)&lt;/span&gt; 내 원소들을 반복하는 &lt;span style=&quot;color: #f89009;&quot;&gt;for..of 문법&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;배열(array) 내 원소들을 &lt;span style=&quot;color: #006dd7;&quot;&gt;(비동기) 함수를 적용&lt;/span&gt;해서 반복하는 &lt;span style=&quot;color: #f89009;&quot;&gt;forEach 문법&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;배열(array) 내 원소들을 &lt;span style=&quot;color: #006dd7;&quot;&gt;(비동기) 함수를 적용&lt;/span&gt;해서 반복한 후, &lt;span style=&quot;color: #006dd7;&quot;&gt;반환&lt;/span&gt;하는 &lt;span style=&quot;color: #f89009;&quot;&gt;map 문법&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. &lt;span style=&quot;color: #006dd7;&quot;&gt;맵(map)&lt;/span&gt; 내 원소들을 반복하는 &lt;span style=&quot;color: #f89009;&quot;&gt;for..in&amp;nbsp;문법&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;반복시키려는 값이 맵(map)으로서 객체(object) 타입의 요소들을 가진다면, 그리고 이 요소들을 하나씩 접근하려 한다면 for..in을&amp;nbsp;사용한다.&amp;nbsp;for..in&amp;nbsp;문법은&amp;nbsp;아래의&amp;nbsp;특징을&amp;nbsp;가지며,&amp;nbsp;예제&amp;nbsp;코드는&amp;nbsp;맵&amp;nbsp;객체&amp;nbsp;내의&amp;nbsp;요소들을&amp;nbsp;하나씩&amp;nbsp;출력시킨&amp;nbsp;것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;for..in 문법은 배열(array)가 아닌 &lt;span style=&quot;color: #006dd7;&quot;&gt;맵(map)의 요소들을 반복&lt;/span&gt;한다. 따라서 배열의 요소가 아닌 맵의 요소가 반복될 때 사용된다.&lt;/li&gt;
&lt;li&gt;for..in 문법에서 반복되는 &lt;span style=&quot;color: #006dd7;&quot;&gt;element 값은 원소의 내용이 아닌 원소가 위치한 키(key)&lt;/span&gt;이다.&lt;/li&gt;
&lt;li&gt;for..in 문법에서 &lt;span style=&quot;color: #006dd7;&quot;&gt;반복되는 키(key)에 해당하는 값(value)은 대괄호([ ])를 사용&lt;/span&gt;해서 접근한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;for..in sample code&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1588428976921&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ...map_for.js
let map = { &quot;j1&quot;: 10, &quot;j2&quot;: 20, &quot;j3&quot;: 30 };

for(let key in map) {
    console.log(key, map[key]);
}


// result
j1 10
j2 20
j3 30&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.&lt;span style=&quot;color: #006dd7;&quot;&gt; 배열(array)&lt;/span&gt; 내 원소들을 반복하는 &lt;span style=&quot;color: #f89009;&quot;&gt;for..of&amp;nbsp;문법&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;반복시키려는 값이 배열(array)이라면, 아래처럼 for..in&amp;nbsp;문법을&amp;nbsp;사용해서&amp;nbsp;간단하게&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있다.&amp;nbsp;for..of&amp;nbsp;문법은&amp;nbsp;아래의&amp;nbsp;특징들을&amp;nbsp;가지고,&amp;nbsp;아래&amp;nbsp;예제는&amp;nbsp;배열&amp;nbsp;안의&amp;nbsp;원소들을&amp;nbsp;가지고&amp;nbsp;순서대로&amp;nbsp;출력킨&amp;nbsp;것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;for..of&amp;nbsp;문법은&amp;nbsp;&lt;span style=&quot;color: #006dd7;&quot;&gt;배열(array)의&amp;nbsp;요소들을&amp;nbsp;반복&lt;/span&gt;한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;for..of sample code&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1588428994988&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ...array_for.js
let array = [&quot;j1&quot;, &quot;j2&quot;, &quot;j3&quot;];
let array2 = [ {&quot;j1&quot;: 10}, {&quot;j2&quot;: 20}, {&quot;j3&quot;: 30}];

for(let ele of array) {
    console.log(ele);
}
for(let ele of array2) {
    console.log(ele);
}


// result
j1
j2
j3
{
    j1:10
}
{
    j2:20
}
{
    j3:30
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 배열(array) 내 원소들을 &lt;span style=&quot;color: #006dd7;&quot;&gt;(비동기) 함수를 적용&lt;/span&gt;해서 반복하는 &lt;span style=&quot;color: #f89009;&quot;&gt;forEach 문법&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;forEach 문법은 아래와 같은 특징들을 가지고 있으며, forEach 문법의 이해를 돋우기 위해 아래처럼 4가지 상황으로 예시를 들어봤다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;forEach 문법은 비동기 함수인 callback 함수 하나를 받는다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;callback 함수로 입력되는 파라미터값은 순서대로 &lt;span style=&quot;color: #006dd7;&quot;&gt;요소값(element), 요소의 인덱스(index), 배열전체(array)&lt;/span&gt;이다.&lt;/li&gt;
&lt;li&gt;callback 함수로 입력되는 파라미터 중 &lt;span style=&quot;color: #006dd7;&quot;&gt;원하는 파라미터들만 받아서 사용가능&lt;/span&gt;하기 때문에, 모두 받거나 하나만 받아서 사용할 수도 있다.&lt;/li&gt;
&lt;li&gt;callback 함수는 ES6문법인 &lt;span style=&quot;color: #006dd7;&quot;&gt;화살표(arrow, =&amp;gt;)를 사용해서 축양된 형태로 사용이 가능&lt;/span&gt;하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;array.forEach() sample 1&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1588429016484&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ...array_foreach1.js
// &quot;function&quot; 키워드를 사용하는 방식
let array = [ &quot;j1&quot;, &quot;j2&quot; ];

// ele(배열 요소), index(배열 인덱스), array(배열 전체)
array.forEach(function(ele, index, array) {
    array[index] = ele.concat('-check');
});

// ele(배열 요소)
array.forEach(function(ele) {
    console.log(`${ele} / function으로 여러줄을 명령(블록 사용)`);
});


// result
j1-check / function으로 여러줄을 명령(블록 사용)
j2-check / function으로 여러줄을 명령(블록 사용)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;array.forEach() sample 2&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1588431357549&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ...array_foreach2.js
// 화살표(arrow)를 사용하는 방식 (+한줄만 명령)
let array = [ &quot;j1&quot;, &quot;j2&quot; ];

// ele(배열 요소), index(배열 인덱스), array(배열 전체)
array.forEach((ele, index, array) =&amp;gt; array[index] = ele.concat('-check') );

// ele(배열 요소)
array.forEach(ele =&amp;gt; console.log(`${ele} / arrow로 한줄만 명령`));


// result
j1-check / arrow로 한줄만 명령
j2-check / arrow로 한줄만 명령&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;array.forEach() sample 3&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1588431380638&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ...array_foreach3.js
// 화살표(arrow)를 사용하는 방식 (+블록으로 여러줄 명령)
let array = [ &quot;j1&quot;, &quot;j2&quot; ];

// ele(배열 요소), index(배열 인덱스), array(배열 전체)
array.forEach((ele, index, array) =&amp;gt; {
    array[index] = ele.concat('-check');
});

// ele(배열 요소)
array.forEach(ele =&amp;gt; {
    console.log(`${ele} / arrow로 여러줄을 명령(블록 사용)`);
});


// result
j1-check / arrow로 여러줄을 명령(블록 사용)
j2-check / arrow로 여러줄을 명령(블록 사용)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.&amp;nbsp;배열(array)&amp;nbsp;내&amp;nbsp;원소들을&amp;nbsp;&lt;span style=&quot;color: #006dd7;&quot;&gt;(비동기)&amp;nbsp;함수를&amp;nbsp;적용&lt;/span&gt;해서&amp;nbsp;반복한&amp;nbsp;후,&amp;nbsp;&lt;span style=&quot;color: #006dd7;&quot;&gt;반환&lt;/span&gt;하는&amp;nbsp;&lt;span style=&quot;color: #f89009;&quot;&gt;map&amp;nbsp;문법&amp;nbsp;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;map&amp;nbsp;문법도&amp;nbsp;forEach&amp;nbsp;와&amp;nbsp;아래처럼&amp;nbsp;동일한&amp;nbsp;특징들을&amp;nbsp;가진다.&amp;nbsp;앞에서&amp;nbsp;언급한&amp;nbsp;forEach의&amp;nbsp;모든&amp;nbsp;코드는&amp;nbsp;map으로&amp;nbsp;변경해도&amp;nbsp;모두&amp;nbsp;동일하게&amp;nbsp;동작을&amp;nbsp;하므로,&amp;nbsp;map에&amp;nbsp;대한&amp;nbsp;예제&amp;nbsp;코드는&amp;nbsp;전부&amp;nbsp;작성하지&amp;nbsp;않았다.&amp;nbsp;차이점만&amp;nbsp;보기&amp;nbsp;위해&amp;nbsp;화살표(arrow)&amp;nbsp;문법을&amp;nbsp;사용한&amp;nbsp;예만&amp;nbsp;작성해보았다.&amp;nbsp;map의&amp;nbsp;특징은&amp;nbsp;앞에서&amp;nbsp;언급한&amp;nbsp;forEach의&amp;nbsp;모든&amp;nbsp;특징을&amp;nbsp;포함하며&amp;nbsp;몇가지가&amp;nbsp;더&amp;nbsp;추가된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;map 문법은 비동기 함수인 callback 함수 하나를 받는다. &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;callback 함수로 입력되는 파라미터값은 순서대로 &lt;span style=&quot;color: #006dd7;&quot;&gt;요소값(element), 요소의 인덱스(index), 배열전체(array)&lt;/span&gt;이다.&lt;/li&gt;
&lt;li&gt;callback 함수로 입력되는 파라미터 중 &lt;span style=&quot;color: #006dd7;&quot;&gt;원하는 파라미터들만 받아서 사용가능&lt;/span&gt;하기 때문에, 모두 받거나 하나만 받아서 사용할 수도 있다.&lt;/li&gt;
&lt;li&gt;callback 함수는 ES6문법인 &lt;span style=&quot;color: #006dd7;&quot;&gt;화살표(arrow, =&amp;gt;)를 사용해서 축양된 형태로 사용이 가능&lt;/span&gt;하다.&lt;/li&gt;
&lt;li&gt;앞에서 작성된 예시코드의 &lt;span style=&quot;color: #009a87;&quot;&gt;forEach 키워드를 모두 map 키워드로 변경해도 그대로 동작&lt;/span&gt;한다.&lt;/li&gt;
&lt;li&gt;callback 함수의 형태는 &lt;span style=&quot;color: #009a87;&quot;&gt;forEach 키워드를 사용할 경우 반환(return)을 하지 못하고, map 키워드를 사용할 경우 반환(return)을 한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;array.map() sample code&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1588429056814&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ...array_map.js
let array = [ &quot;j1&quot;, &quot;j2&quot; ];

// 값을 반환하지만 아무도 받지 않으므로 요소값을 그대로 출력
array.map(ele =&amp;gt; {
    return ele.concat('-check');
});
array.map(ele =&amp;gt; console.log(`${ele} 출력`));

// 값을 반환하고 자신에게 대입했으므로 요소값이 변경되서 출력
array = array.map(ele =&amp;gt; {
    return ele.concat('-check');
});
array.map(ele =&amp;gt; console.log(`${ele} 출력`));

// result
j1 출력
j2 출력
j1-check 출력
j2-check 출력&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>language/javascript</category>
      <category>for..in</category>
      <category>for..of</category>
      <category>forEach</category>
      <category>javascript</category>
      <category>JS</category>
      <category>map</category>
      <category>반복문</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/196</guid>
      <comments>https://caileb.tistory.com/196#entry196comment</comments>
      <pubDate>Sat, 2 May 2020 23:41:10 +0900</pubDate>
    </item>
    <item>
      <title>react - react.js 프로젝트 만들기</title>
      <link>https://caileb.tistory.com/195</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;react.js 프로젝트 만들기&lt;/h2&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;react.js를 개발을 처음부터 공부하는 입장에서 시작을 한다면, 처음에는 헷갈리지만 나중에 별 것 아닌 부분이 개발환경 세팅하는 부분이다. 본 포스팅에서는 react.js 환경을 세팅하는 방법 중 하나를 설명한다. 아래처럼 react.js를 위한 기본 프로젝트를 만든 후, component, action, redux, redux-saga 등을 사용해서 구조를 하나씩 잡아가면서 개발을 하면 된다. 거의 모든 세팅과정이 터미널 위에서 명령(command)들을 사용해서 설명했기 때문에, 아래에서 설명할 때 사용하고 있는 IDE인 IntelliJ가 아니라 Visual Studio Code 등의 다른 것을 사용하는 것도 어렵지 않을 것이라고 생각한다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개발환경&lt;/h2&gt;
&lt;p&gt;OS : macOS&lt;/p&gt;
&lt;p&gt;IDE : IntelliJ&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개발순서&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;nvm&lt;/span&gt; 설치&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;NodeJS&lt;/span&gt; 설치&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;yarn&lt;/span&gt; 설치 (option)&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;ReactJS&lt;/span&gt; 설치&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;ReactJS&lt;/span&gt; 프로젝트 생성&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;ReactJS&lt;/span&gt; 프로젝트 실행&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개발순서 따라하기&lt;/h2&gt;
&lt;p&gt;&lt;b&gt;1. &lt;span style=&quot;color: #006dd7;&quot;&gt;nvm&lt;/span&gt; 설치 (참고, &lt;a href=&quot;https://github.com/nvm-sh/nvm#manual-install&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;링크&lt;/a&gt;)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1584280248074&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1. 설치
$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash

// 2. 환경변수 NVM_DIR 추가
$ vi ~/.bash_profile

export NVM_DIR=&quot;$HOME/.nvm&quot;
[ -s &quot;$NVM_DIR/nvm.sh&quot; ] &amp;amp;&amp;amp; . &quot;$NVM_DIR/nvm.sh&quot; # This loads nvm

// 3. 환경변수 적용
$ source ~/.bash_profile

// 4. 설치 확인
$ nvm ls&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;2. &lt;span style=&quot;color: #006dd7;&quot;&gt;NodeJS&lt;/span&gt; 설치&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1584280317394&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1. 최신 버전 NodeJS 설치
$ nvm install node

// 2. 설치 확인
$ nvm ls

// advanced
// 1. 원하는 버전 NodeJS 설치
$ nvm install 10.16.3

// 2. 설치 확인
$ nvm ls

// 3. 원하는 버전 NodeJS로 설정
$ nvm use 10.16.3

// 4. 설정된 NodeJS 버전 확인
$ node --version&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;3. &lt;span style=&quot;color: #006dd7;&quot;&gt;yarn&lt;/span&gt; 설치 (option)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1584280330662&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1. yarn 설치 
$ brew install yarn

// 2. 설치 확인
$ yarn --version&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;참고, npm install 로 설치하면 직접 터미널을 열어서 yarn이 잘 작동되지만, Intellij에서 터미널을 열어서 yarn을 실행하면 &quot;zsh: command not found: yarn&quot; 에러가 발생하면서 작동이 안되는 상황에 마주하면서 추가적인 환경설정이 필요하게 되는데, 이러한 과정이 번거로우니 작성자는 &lt;span style=&quot;color: #006dd7;&quot;&gt;yarn 설치는 npm이 아닌 brew로 설치&lt;/span&gt;했다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;참고, 현재 포스팅에서 설명할 때는 yarn을 사용했지만, (yarn 이전에 설치되는) npm을 사용해도 상관없다. 다만, 사람들이 yarn을 많이 사용하고, 보통 프로그램을 혼자 개발하는 것이 아니기에 이를 따라줘야 하기도 하고, npm보다 yarn이 장점들이 많기 때문에 이를 사용하기 위해 yarn을 사용한 것 뿐이다. 만약, 현재 ReactJS를 공부하는 입장에서 yarn때문에 더 혼란스럽다면, &lt;span style=&quot;color: #006dd7;&quot;&gt;yarn을 설치하지 않고 npm만을 사용해도 상관이 없다&lt;/span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;4. &lt;span style=&quot;color: #006dd7;&quot;&gt;ReactJS&lt;/span&gt; 설치&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1584280371561&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1. (CLI 형태) ReactJS 설치
$ npm install -g create-react-app

// 2. 설치 확인
$ npm ls -g | grep create-react-app&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;5. &lt;span style=&quot;color: #006dd7;&quot;&gt;ReactJS&lt;/span&gt; 프로젝트 생성&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1584280384071&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1. ReactJS 프로젝트 생성
$ npx create-react-app joker

// 2. 설치 확인
$ ls&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;6. &lt;span style=&quot;color: #006dd7;&quot;&gt;ReactJS&lt;/span&gt; 프로젝트 실행&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;[실행 방법 1] &lt;span style=&quot;color: #006dd7;&quot;&gt;인텔리제이(Intellij) 사용&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;ReactJS 프로젝트 열기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;IntelliJ 실행 &amp;gt; &quot;Open&quot; 버튼 &amp;gt; &quot;joker&quot; 프로젝트 클릭 &amp;gt; &quot;Open&quot;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ReactJS 프로젝트 yarn 설정&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;IntelliJ 상단 메뉴 &amp;gt; Preferences &amp;gt; Languages &amp;amp; Frameworks &amp;gt; Node.js and NPM&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Node interpreter : &quot;&lt;span style=&quot;color: #009a87;&quot;&gt;node&lt;/span&gt;&quot; =&amp;gt; &quot;&lt;span style=&quot;color: #009a87;&quot;&gt;~/.nvm/versions/node/v10.16.3/bin/node&lt;/span&gt;&quot; 으로 변경 &lt;span style=&quot;color: #9d9d9d;&quot;&gt;(그림1 yarn 설정 전, 그림2 yarn 설정 후 참고)&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;&quot;OK&quot; 버튼&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ReactJS 프로젝트 run 설정&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;IntelliJ 상단 메뉴 &amp;gt; Run &amp;gt; Edit Configuration &amp;gt; 왼쪽 상단의 &quot;+&quot; 클릭 &amp;gt; npm 선택&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Scripts : &quot;&quot; =&amp;gt; &quot;&lt;span style=&quot;color: #009a87;&quot;&gt;start&lt;/span&gt;&quot; 으로 변경 &lt;span style=&quot;color: #9d9d9d;&quot;&gt;(그림3 script 설정 전, 그림4 script 설정 후 참고)&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;&quot;OK&quot; 버튼&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ReactJ&lt;span&gt;S 프로젝&lt;/span&gt;&lt;span&gt;트 run 실행&lt;/span&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;[ 방법1 ]&lt;/b&gt; IntelliJ &amp;nbsp;상단의 녹색 &quot;▶&quot; 버튼&lt;/span&gt; &lt;span style=&quot;color: #9d9d9d;&quot;&gt;(그림5 최종 출력화면 참고)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;[ 방법2 ]&lt;/b&gt; IntelliJ &amp;nbsp;하단의 터미널(Terminal) 에서 &quot;&lt;/span&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;yarn start&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&quot; or &quot;&lt;/span&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;npm start&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&quot; 명령문 실행&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;[실행 방법 2] &lt;span style=&quot;color: #006dd7;&quot;&gt;터미널(terminal) 사용&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;터미널 실행&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px; color: #000000;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px; color: #009a87;&quot;&gt;yarn start&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px; color: #000000;&quot;&gt;&quot; or &quot;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px; color: #009a87;&quot;&gt;npm start&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px; color: #000000;&quot;&gt;&quot; 명령문 실행&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-03-15 오후 4.37.59.png&quot; data-origin-width=&quot;716&quot; data-origin-height=&quot;127&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CgkIt/btqCHwvCJHd/fbaWGhVZeTGl6PBuIBc3p0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CgkIt/btqCHwvCJHd/fbaWGhVZeTGl6PBuIBc3p0/img.png&quot; data-alt=&quot;[그림1 yarn 설정 전]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CgkIt/btqCHwvCJHd/fbaWGhVZeTGl6PBuIBc3p0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCgkIt%2FbtqCHwvCJHd%2FfbaWGhVZeTGl6PBuIBc3p0%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-03-15 오후 4.37.59.png&quot; data-origin-width=&quot;716&quot; data-origin-height=&quot;127&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[그림1 yarn 설정 전]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-03-15 오후 4.38.17.png&quot; data-origin-width=&quot;718&quot; data-origin-height=&quot;128&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2dSnZ/btqCLLLzTx7/fl0k6cSgcO7z7dDQZYsrvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2dSnZ/btqCLLLzTx7/fl0k6cSgcO7z7dDQZYsrvK/img.png&quot; data-alt=&quot;[그림2 yarn 설정 후]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2dSnZ/btqCLLLzTx7/fl0k6cSgcO7z7dDQZYsrvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2dSnZ%2FbtqCLLLzTx7%2Ffl0k6cSgcO7z7dDQZYsrvK%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-03-15 오후 4.38.17.png&quot; data-origin-width=&quot;718&quot; data-origin-height=&quot;128&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[그림2 yarn 설정 후]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-03-15 오후 4.44.01.png&quot; data-origin-width=&quot;1062&quot; data-origin-height=&quot;164&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0Bnlt/btqCHwPQKrz/jHKpOH6X2yoW9Sj1XHkx00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0Bnlt/btqCHwPQKrz/jHKpOH6X2yoW9Sj1XHkx00/img.png&quot; data-alt=&quot;[그림3 script 설정 전]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0Bnlt/btqCHwPQKrz/jHKpOH6X2yoW9Sj1XHkx00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0Bnlt%2FbtqCHwPQKrz%2FjHKpOH6X2yoW9Sj1XHkx00%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-03-15 오후 4.44.01.png&quot; data-origin-width=&quot;1062&quot; data-origin-height=&quot;164&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[그림3 script 설정 전]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-03-15 오후 4.44.19.png&quot; data-origin-width=&quot;1061&quot; data-origin-height=&quot;165&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mm2GJ/btqCF6qBzix/AQ9k599HSDkXTw54lSvQMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mm2GJ/btqCF6qBzix/AQ9k599HSDkXTw54lSvQMK/img.png&quot; data-alt=&quot;[그림4 script 설정 후]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mm2GJ/btqCF6qBzix/AQ9k599HSDkXTw54lSvQMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fmm2GJ%2FbtqCF6qBzix%2FAQ9k599HSDkXTw54lSvQMK%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-03-15 오후 4.44.19.png&quot; data-origin-width=&quot;1061&quot; data-origin-height=&quot;165&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[그림4 script 설정 후]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-03-15 오후 4.58.00.png&quot; data-origin-width=&quot;1638&quot; data-origin-height=&quot;1012&quot; width=&quot;503&quot; height=&quot;311&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BYlxO/btqCF8hEyFG/tPVbkTDELmcBwrfV7LACqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BYlxO/btqCF8hEyFG/tPVbkTDELmcBwrfV7LACqk/img.png&quot; data-alt=&quot;[그림5 최종 출력화면]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BYlxO/btqCF8hEyFG/tPVbkTDELmcBwrfV7LACqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBYlxO%2FbtqCF8hEyFG%2FtPVbkTDELmcBwrfV7LACqk%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-03-15 오후 4.58.00.png&quot; data-origin-width=&quot;1638&quot; data-origin-height=&quot;1012&quot; width=&quot;503&quot; height=&quot;311&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[그림5 최종 출력화면]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>dev-tips/react</category>
      <category>nodejs</category>
      <category>reactjs</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/195</guid>
      <comments>https://caileb.tistory.com/195#entry195comment</comments>
      <pubDate>Sun, 15 Mar 2020 23:28:15 +0900</pubDate>
    </item>
    <item>
      <title>Rustup not available</title>
      <link>https://caileb.tistory.com/194</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot; style=&quot;text-align: left;&quot;&gt;에러 (error)&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(238, 35, 35);&quot;&gt;Couldn't start client Rust Language Server&lt;/span&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;Source: Rust (rls) (Extension)&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(238, 35, 35);&quot;&gt;Rustup not available. Install from https://www.rustup.rs/&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;Source: Rust (rls) (Extension)&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/lD6TR/btqCGQTFkuh/YeuCxlgnlVTq6AMH6eWLzK/img.png&quot; width=&quot;619.0&quot; height=&quot;257.0&quot; data-origin-width=&quot;934.0&quot; data-origin-height=&quot;388.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lD6TR/btqCGQTFkuh/YeuCxlgnlVTq6AMH6eWLzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lD6TR/btqCGQTFkuh/YeuCxlgnlVTq6AMH6eWLzK/img.png&quot; data-alt=&quot;[ Error Popup ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lD6TR/btqCGQTFkuh/YeuCxlgnlVTq6AMH6eWLzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlD6TR%2FbtqCGQTFkuh%2FYeuCxlgnlVTq6AMH6eWLzK%2Fimg.png&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/lD6TR/btqCGQTFkuh/YeuCxlgnlVTq6AMH6eWLzK/img.png&quot; width=&quot;619.0&quot; height=&quot;257.0&quot; data-origin-width=&quot;934.0&quot; data-origin-height=&quot;388.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ Error Popup ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot; style=&quot;text-align: left;&quot;&gt;환경 (environment)&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;OS : macOS Catalina 10.15.3&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;IDE : vsCode (Visual Studio Code) 1.42.1 / extension : CodeLLDB, Rust (rls)&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;Build : cargo&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;Language : rust / ructc : 1.41.1, rustup : 1.21.1&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(0, 154, 135);&quot;&gt;참고, Rustc, Rustup 버전 확인&lt;/span&gt;&lt;/p&gt;&lt;pre data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;$ rustc --version
$ rustup --verison&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot; style=&quot;text-align: left;&quot;&gt;상황 (situation)&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(238, 35, 35);&quot;&gt;개발된 Rust 프로젝트를 디버깅하기 위해 F5를 눌러서 디버깅을 시도하는데 에러가 발생&lt;/span&gt;했다. 두 팝업을 그대로 해석하면 extension 중에 Rust (rls)에 의해 실행되는 Rust Language Server라는 프로그램을 실행할 수 없다는 에러와 Rustup을 사용할 수 없다는 내용이다. 참고로, 현재 rust를 설치하면서 rust 관련된 환경변수들은 자동으로 세팅되어 있고, rust를 설치할 때 함께 설치된 rustc나 rustup 등의 rust 관련 프로그램들은 /Users/joker/.cargo/bin 디렉토리에 모두 위치되어 있다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(0, 154, 135);&quot;&gt;참고, $PATH 확인&lt;/span&gt;&lt;/p&gt;&lt;pre data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;$ echo $PATH
/Users/joker/.cargo/bin:/User/joker/...(생략)&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot; style=&quot;text-align: left;&quot;&gt;원인 (cause)&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;vsCode에 rust를 (디버깅으로) 실행할 때 Rustup을 사용하게 되는데, 이 &lt;span style=&quot;color: rgb(0, 109, 215);&quot;&gt;Rustup 프로그램을 찾지 못하는 상황이므로 vsCode가 찾을 수 있도록 관련된 세팅값을 변경&lt;/span&gt;하면 된다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot; style=&quot;text-align: left;&quot;&gt;해결 (solution)&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;gt; visual studio code 상단 메뉴 &lt;b&gt;Code&lt;/b&gt; 클릭&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;gt; &lt;b&gt;Preferences&lt;/b&gt; &amp;gt; &lt;b&gt;Settings&lt;/b&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;gt; &lt;b&gt;User&lt;/b&gt; 탭&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;gt; &lt;b&gt;Extensions&lt;/b&gt; &amp;gt; &lt;b&gt;Rust&lt;/b&gt; configuration&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;gt; &lt;b&gt;Rust-client: Rustup Path&lt;/b&gt; 내용 변경 &amp;gt; &quot;rustup&quot; ==&amp;gt; &quot;$HOME/.cargo/bin/rustup&quot;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/cvu4Kb/btqCBwvIZgp/KdrDXllxkctdUo8e3nP0FK/img.png&quot; data-origin-width=&quot;514.0&quot; data-origin-height=&quot;83.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cvu4Kb/btqCBwvIZgp/KdrDXllxkctdUo8e3nP0FK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cvu4Kb/btqCBwvIZgp/KdrDXllxkctdUo8e3nP0FK/img.png&quot; data-alt=&quot;[ (before) 변경 전 ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cvu4Kb/btqCBwvIZgp/KdrDXllxkctdUo8e3nP0FK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcvu4Kb%2FbtqCBwvIZgp%2FKdrDXllxkctdUo8e3nP0FK%2Fimg.png&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/cvu4Kb/btqCBwvIZgp/KdrDXllxkctdUo8e3nP0FK/img.png&quot; data-origin-width=&quot;514.0&quot; data-origin-height=&quot;83.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ (before) 변경 전 ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/pPx7a/btqCB7WXrBC/Jgt8SJqTy5xyB03n6GTHO1/img.png&quot; data-origin-width=&quot;510.0&quot; data-origin-height=&quot;81.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pPx7a/btqCB7WXrBC/Jgt8SJqTy5xyB03n6GTHO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pPx7a/btqCB7WXrBC/Jgt8SJqTy5xyB03n6GTHO1/img.png&quot; data-alt=&quot;[ (after) 변경 후 ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pPx7a/btqCB7WXrBC/Jgt8SJqTy5xyB03n6GTHO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpPx7a%2FbtqCB7WXrBC%2FJgt8SJqTy5xyB03n6GTHO1%2Fimg.png&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/pPx7a/btqCB7WXrBC/Jgt8SJqTy5xyB03n6GTHO1/img.png&quot; data-origin-width=&quot;510.0&quot; data-origin-height=&quot;81.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ (after) 변경 후 ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;gt; &lt;b&gt;F5&lt;/b&gt; (디버깅 재시도)&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(0, 154, 135);&quot;&gt;참고, 위 내용을 변경하면 자동으로 vsCode 프로젝트 내의 settings.json 파일 내용이 자동으로 같이 수정된다.&lt;/span&gt;&lt;/p&gt;&lt;pre data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;{
    &quot;rust-client.channel&quot;: &quot;stable&quot;,
    &quot;rust-client.rustupPath&quot;: &quot;$HOME/.cargo/bin/rustup&quot;
}&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot; style=&quot;text-align: left;&quot;&gt;참고 (reference)&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;stackoverflow 해결방법&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;a href=&quot;https://github.com/rust-lang/rls-vscode/issues/622#issuecomment-561596264&quot; target=&quot;_blank&quot;&gt;&lt;span style=&quot;color: rgb(126, 152, 177);&quot;&gt;&lt;span style=&quot;color: rgb(126, 152, 177);&quot;&gt;https://github.com/rust-lang/rls-vscode/issues/622#issuecomment-561596264&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>error/[err] vscode</category>
      <category>rust</category>
      <category>VSCode</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/194</guid>
      <comments>https://caileb.tistory.com/194#entry194comment</comments>
      <pubDate>Thu, 12 Mar 2020 02:47:51 +0900</pubDate>
    </item>
    <item>
      <title>IntelliJ - 테마 바꾸는 방법</title>
      <link>https://caileb.tistory.com/193</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;테마 바꾸는 방법&lt;/h2&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;how to change theme of IntellJ&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;현재 사용 중인 Eclipse, STS4(Spring Tools 4), Xcode, Android Studio 등의 다양한 IDE들을 어두운 다크 모드로 사용 중이라면, &amp;nbsp;IntelliJ 또한 동일하게 다크 모드로 사용하고 싶을 것입니다. &lt;span style=&quot;color: #006dd7;&quot;&gt;IntelliJ도 다른 IDE 처럼 다크 모드를 지원&lt;/span&gt;하고 있으므로 아래처럼 설정이 가능합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;환경&lt;/h2&gt;
&lt;p style=&quot;font-size: 1.12em;&quot; data-ke-size=&quot;size16&quot;&gt;macOS&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;순서&lt;/h2&gt;
&lt;p&gt;1. IntelliJ 열기&lt;/p&gt;
&lt;p&gt;2. 화면 상단 탭에서 ..&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp;&amp;gt; &lt;span style=&quot;color: #006dd7;&quot;&gt;IntelliJ IDEA&lt;/span&gt; 메뉴 클릭&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp;&amp;gt; &lt;span style=&quot;color: #006dd7;&quot;&gt;Preferences&lt;/span&gt; 메뉴 클릭&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp;&amp;gt; &lt;span style=&quot;color: #006dd7;&quot;&gt;Appearance &amp;amp; Behavior&lt;/span&gt; 좌측 메뉴 클릭&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp;&amp;gt; &lt;span style=&quot;color: #006dd7;&quot;&gt;Appearance&lt;/span&gt; 좌측 메뉴 클릭&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp;&amp;gt; &lt;span style=&quot;color: #006dd7;&quot;&gt;Theme&lt;/span&gt; : &quot;&lt;b&gt;Light&lt;/b&gt;&quot; -&amp;gt; &quot;&lt;b&gt;Darcula&lt;/b&gt;&quot; 변경&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp;&amp;gt; &lt;span style=&quot;color: #006dd7;&quot;&gt;OK&lt;/span&gt; 버튼 클릭&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-03-05 오전 1.51.40.png&quot; data-origin-width=&quot;982&quot; data-origin-height=&quot;746&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bC2pP1/btqCtXzwpLB/UQbp22BRSaKSigPlLTzV0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bC2pP1/btqCtXzwpLB/UQbp22BRSaKSigPlLTzV0k/img.png&quot; data-alt=&quot;[ IntelliJ 테마 설정 화면 ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bC2pP1/btqCtXzwpLB/UQbp22BRSaKSigPlLTzV0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbC2pP1%2FbtqCtXzwpLB%2FUQbp22BRSaKSigPlLTzV0k%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-03-05 오전 1.51.40.png&quot; data-origin-width=&quot;982&quot; data-origin-height=&quot;746&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ IntelliJ 테마 설정 화면 ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>environment/IDE</category>
      <category>IntelliJ</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/193</guid>
      <comments>https://caileb.tistory.com/193#entry193comment</comments>
      <pubDate>Thu, 5 Mar 2020 02:04:54 +0900</pubDate>
    </item>
    <item>
      <title>Json 데이터 전송하기 (application/json)</title>
      <link>https://caileb.tistory.com/192</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;Json 데이터 전송하기&lt;/h2&gt;
&lt;p style=&quot;text-align: center; font-size: 1.12em;&quot; data-ke-size=&quot;size16&quot;&gt;(&lt;span style=&quot;color: #f89009;&quot;&gt;application/x-www-form-urlencoded&lt;/span&gt;, &lt;span style=&quot;color: #006dd7;&quot;&gt;application/json&lt;/span&gt;)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;request_json_data_0.PNG&quot; data-origin-width=&quot;358&quot; data-origin-height=&quot;178&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nzzbb/btqBiDpH9zz/c8flA6X27iTVvexOF3QtVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nzzbb/btqBiDpH9zz/c8flA6X27iTVvexOF3QtVk/img.png&quot; data-alt=&quot;[ Http Request Header &amp;amp;amp;gt; Content-Type ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nzzbb/btqBiDpH9zz/c8flA6X27iTVvexOF3QtVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fnzzbb%2FbtqBiDpH9zz%2Fc8flA6X27iTVvexOF3QtVk%2Fimg.png&quot; data-filename=&quot;request_json_data_0.PNG&quot; data-origin-width=&quot;358&quot; data-origin-height=&quot;178&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ Http Request Header &amp;gt; Content-Type ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;클라이언트에서 서버로 데이터를 전송하려할 때, 데이터를 Json 포멧으로 만들어서 보내는 것이 일반적인 형태 중 하나입니다. 이 글에서는 Json 포멧으로 데이터를 송수신하는 부분을 개발할 때 헷갈릴 수 있는 부분에 대해서 설명하려고 합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;헷갈릴 수 있는 부분은 현재 개발 중인 서버에서 Json 포멧으로 데이터를 받도록 잘 개발한 것 같 같은데, 클라이언트에서 아무리 Json 포멧으로 데이터를 만들어서 보내도 안되는 경우가 있습니다. 그런데 어떻게 보내면 가끔은 다시 잘 되는 경우도 있습니다. 만약, 현재 그런 상황에 마주했다면, 그리고 그 이유가 Content-Type 설정값 때문이라면 이 글이 도움이 될 것입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;서버에서 Json 포멧으로 데이터를 받도록 개발된 상태에서 개발 중인 클라이언트에서 Http Request Message 안의 Content-Type으로 설정될 수 있을만한 값으로는&amp;nbsp;&lt;span style=&quot;color: #f89009;&quot;&gt;application/x-www-form-urlencoded&lt;/span&gt;와 &lt;span style=&quot;color: #006dd7;&quot;&gt;application/json&lt;/span&gt;가 있습니다. 이 글에서는 두 경우를 나눠서 살펴보려고 합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Content-Type을 &lt;span style=&quot;color: #f89009;&quot;&gt;application/x-www-form-urlencoded&lt;/span&gt;로 설정해서 보내는 경우&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Content-Type을 &lt;span style=&quot;color: #006dd7;&quot;&gt;application/json&lt;/span&gt;로 설정해서 보내는 경우&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;현재, Spring Framework를 사용해서 서버측 Rest API를 개발하고, 이 Rest API를 &lt;span style=&quot;color: #f89009;&quot;&gt;Web Page에서 Ajax를 통해 호출&lt;/span&gt;되거나 때로는 &lt;span style=&quot;color: #006dd7;&quot;&gt;Android App에서 호출&lt;/span&gt;되어야하는 상황이라고 생각해보죠. 그리고 이 Rest API는 json포멧의 데이터만 받을 수 있도록 하려고 합니다. 이 때, 해당 Rest API가 클라이언트 요청에 대해 정상적으로 응답하는지 테스트하기 위해서, 클라이언트에서 Json 포멧의 데이터를 하나 만들어서 요청해보려고 합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;요청에 대해 서버가 정상적으로 응답을 주는지 두가지 방법으로 테스트하려고 합니다. 하나는 개발 중인 &lt;span style=&quot;color: #f89009;&quot;&gt;Web Page의 javascript(=JQuery)를 사용해서 json 포멧으로 데이터를 만들어서 요청&lt;/span&gt;하는 것입니다. 다른 하나는 Android App을 대신해서 클라이언트 툴 중 하나인 &lt;span style=&quot;color: #006dd7;&quot;&gt;PostMan을 사용해서 json 포멧으로 데이터를 만들어서 요청&lt;/span&gt;하는 것입니다. 물론, Web Page나 Android App을 대신해서 둘다 PostMan으로 Json 데이터를 만들어서 테스트가 가능하지만, 이해를 돕기 위해 둘로 나누어서 생각해보려는 것입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Content-Type을 &lt;span style=&quot;color: #f89009;&quot;&gt;application/x-www-form-urlencoded&lt;/span&gt;로 설정해서 보내는 경우&lt;/h3&gt;
&lt;p&gt;이 경우는, 보통 &lt;u&gt;Web Page를 개발할 때, javascript(=JQuery)를 사용해서 Ajax로 서버에 데이터를 요청하는 경우&lt;/u&gt;(혹은 HTML Form을 사용해서 요청하는 경우)입니다. 만약에, Content-Type을 따로 기입하지 않으면, 클라이언트에서는 Http Request Header의 Content-Type을 Default값인 application/x-www-form-urlencoded으로 세팅되서 서버로 데이터가 전송됩니다. 이렇게 개발중이라면 현재 서버와 클라이언트 코드는 아래형태로 구성되어 있을 것입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #7e98b1;&quot;&gt;※ (참고) application/x-www-form-urlencoded로 보내는 경우, HTTP Request Message의 Body를 사용해서 데이터를 보내는 것으로, 이 경우 전송되는 데이터의 페이로드 값은 &amp;ldquo;key=value&amp;amp;key2=value2&amp;rdquo; 형태를 가질 것입니다. 그리고 서버로 들어온 데이터는 &lt;u&gt;request.getParameterMap()&lt;/u&gt;으로 확인 가능합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;i&gt;[Client-Side] &lt;b&gt;PostMan&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;request_json_data_1.PNG&quot; data-origin-width=&quot;873&quot; data-origin-height=&quot;266&quot; width=&quot;686&quot; height=&quot;209&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIK8Il/btqBjCREHwp/YkJtpVHtd2j6qKP08iPGSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIK8Il/btqBjCREHwp/YkJtpVHtd2j6qKP08iPGSk/img.png&quot; data-alt=&quot;[ HTTP Request Body : x-www-form-urlencoded ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIK8Il/btqBjCREHwp/YkJtpVHtd2j6qKP08iPGSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIK8Il%2FbtqBjCREHwp%2FYkJtpVHtd2j6qKP08iPGSk%2Fimg.png&quot; data-filename=&quot;request_json_data_1.PNG&quot; data-origin-width=&quot;873&quot; data-origin-height=&quot;266&quot; width=&quot;686&quot; height=&quot;209&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ HTTP Request Body : x-www-form-urlencoded ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1579448394841&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(Method) POST
(Url) http://localhost/reg
(Header) Content-Type : application/x-www-form-urlencoded
(Body) x-www-form-urlencoded :
&quot;id&quot;:1,
&quot;title&quot;:&quot;Harry Potter&quot;,
&quot;description&quot;:&quot;J. K. Rowling&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #000000;&quot;&gt;[Client-Side] &lt;b&gt;javascript&lt;/b&gt; (using JQuery)&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1579448231612&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$.ajax({
	url: 'http://localhost/reg',
	// contentType을 명시하지 않으면 application/x-www-form-urlencoded; charset=UTF-8으로 세팅된다.
	method: 'POST',
	data: {
		id: 1,
		title: &quot;Harry Potter&quot;,
		description: &quot;J. K. Rowling&quot;
	})
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;i&gt;[Server-Side] &lt;b&gt;Java&lt;/b&gt; (using Spring Framework)&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1579448154536&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RestController
@RequestMapping(&quot;/info&quot;)
public class JokerController {
	@RequestMapping(value = {&quot;/reg1&quot;}, method = {RequestMethod.GET, RequestMethod.POST})
	public ResponseMsg reg1(JokerData data) {
		&amp;hellip;
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Content-Type을 &lt;span style=&quot;color: #006dd7;&quot;&gt;application/json&lt;/span&gt;로 설정해서 보내는 경우&lt;/h3&gt;
&lt;p&gt;이 경우는, 보통 &lt;u&gt;Android나 iOS앱을 위한 Rest API를 개발하는 경우&lt;/u&gt;입니다. 해당 클라이언트 앱에서 서버에 데이터를 요청할 때, 보통 Http Request Header의 Content-Type을 application/json으로 세팅되서 서버로 데이터가 전송시킵니다. 만약 이렇게 개발중이라면 현재 서버와 클라이언트 코드는 아래형태로 구성되어 있을 것입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #7e98b1;&quot;&gt;※ (참고) application/json으로 보내는 경우, HTTP Request Message의 Payload를 보내는 것으로, 이 경우 전송되는 데이터의 페이로드 값은 예를 들어, {&amp;ldquo;key&amp;rdquo;:&amp;rdquo;value&amp;rdquo;, &amp;ldquo;key2&amp;rdquo;:&amp;rdquo;value2&amp;rdquo;} 형태로 대체로 복잡한 &lt;u&gt;오브젝트 형태를 가지는 JSON 객체&lt;/u&gt;가 될 수 있습니다. 그리고, 서버로 들어온 데이터는 &lt;u&gt;request.getReader().lines().collect(Collectors.joining())&lt;/u&gt;으로 확인 가능합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;i&gt;[Client-Side] &lt;b&gt;PostMan&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;request_json_data_2.PNG&quot; data-origin-width=&quot;873&quot; data-origin-height=&quot;213&quot; width=&quot;684&quot; height=&quot;167&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdVKWc/btqBj5FNGPV/ctrHDZ01A0FAEHxQz4jpB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdVKWc/btqBj5FNGPV/ctrHDZ01A0FAEHxQz4jpB0/img.png&quot; data-alt=&quot;[ HTTP Request Body : json/application ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdVKWc/btqBj5FNGPV/ctrHDZ01A0FAEHxQz4jpB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdVKWc%2FbtqBj5FNGPV%2FctrHDZ01A0FAEHxQz4jpB0%2Fimg.png&quot; data-filename=&quot;request_json_data_2.PNG&quot; data-origin-width=&quot;873&quot; data-origin-height=&quot;213&quot; width=&quot;684&quot; height=&quot;167&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ HTTP Request Body : json/application ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1579448442889&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(Method) POST
(Url) http://localhost/reg
(Header) Content-Type : application/json
(Body) raw :
{
	&quot;id&quot;:1,
	&quot;title&quot;:&quot;Harry Potter&quot;,
	&quot;description&quot;:&quot;J. K. Rowling&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;i&gt;[Client-Side] &lt;b&gt;javascript&lt;/b&gt; (using JQuery)&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;i&gt;주의 : contentType을 application/json으로 세팅&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;i&gt;주의 : JSON.stringify() 메소드를 사용해서 Json 데이터 세팅&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1579448336716&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$.ajax({
	url: 'http://localhost/reg',
	contentType: 'application/json',
	method: 'POST',
	data: JSON.stringify({
		id: 1,
		title: &quot;Harry Potter&quot;,
		description: &quot;J. K. Rowling&quot;
	})
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;i&gt;[Server-Side] &lt;b&gt;Java&lt;/b&gt; (using Spring Framework)&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;i&gt;주의 : @RequestBody 어노테이션을 사용 (=HTTP Request Body 부분을 그대로 읽어서 객채로 변환)&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1579448183156&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RestController
@RequestMapping(&quot;/info&quot;)
public class JokerController {
	@RequestMapping(value = {&quot;/reg2&quot;}, method = {RequestMethod.GET, RequestMethod.POST})
	public ResponseMsg reg2(@RequestBody JokerData data) {
		&amp;hellip;
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;정리하면, 클라이언트에서 Content-Type을 application/json로 세팅해서 데이터를 송신하고, 서버에서는 @RequestBody를 사용해서 데이터를 받도록 코드를 작성하면 &lt;span style=&quot;color: #333333;&quot;&gt;Json 데이터를 송수신할 수 있는 상태가 된다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>dev-tips/springframework</category>
      <category>application/json</category>
      <category>application/x-www-form-urlencoded</category>
      <category>Spring</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/192</guid>
      <comments>https://caileb.tistory.com/192#entry192comment</comments>
      <pubDate>Mon, 20 Jan 2020 01:26:36 +0900</pubDate>
    </item>
    <item>
      <title>eos - 정글넷(Junglenet) 계정 생성하기</title>
      <link>https://caileb.tistory.com/191</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;EOS 정글넷 계정 생성하기&lt;/h2&gt;
&lt;p style=&quot;font-size: 1.12em; text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;(how to create an account in Jungle TestNet)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;eos-junglenet.PNG&quot; data-origin-width=&quot;247&quot; data-origin-height=&quot;233&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/I8kZn/btqAs3I2MXy/aWkXiC2FsJEDvKHE0bLze0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/I8kZn/btqAs3I2MXy/aWkXiC2FsJEDvKHE0bLze0/img.png&quot; data-alt=&quot;[ EOS Jungle Test Network ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/I8kZn/btqAs3I2MXy/aWkXiC2FsJEDvKHE0bLze0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FI8kZn%2FbtqAs3I2MXy%2FaWkXiC2FsJEDvKHE0bLze0%2Fimg.png&quot; data-filename=&quot;eos-junglenet.PNG&quot; data-origin-width=&quot;247&quot; data-origin-height=&quot;233&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ EOS Jungle Test Network ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;EOS 정글넷 계정 생성 방법&lt;/h2&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;※ 참고, EOS Jungle Network는 EOS Main Network가 아닌 Test Network 중 하나이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;요약&lt;/h3&gt;
&lt;p&gt;1. 정글넷 모니터 사이트#1 접속&lt;/p&gt;
&lt;p&gt;2. Create Account (계정 생성) 클릭&lt;/p&gt;
&lt;p&gt;3. 정글넷 모니터 사이트#2 접속&lt;/p&gt;
&lt;p&gt;4. Search Box에 (생성된 계정) Account 입력&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;생성 순서&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;1. 정글넷 모니터 사이트#1 접속&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;u&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;&lt;a style=&quot;color: #409d00;&quot; href=&quot;https://monitor.jungletestnet.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://monitor.jungletestnet.io/&lt;/a&gt;&lt;/span&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;2. Create Account (계정 생성) 클릭&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;gt; Account : 생성할 계정입력&lt;/p&gt;
&lt;p&gt;&amp;gt; Owner Public Key : 키 입력&lt;/p&gt;
&lt;p&gt;&amp;gt; Active Public Key : 키 입력&lt;/p&gt;
&lt;p&gt;&amp;gt; Create 클릭&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;※ &lt;/span&gt;Account에 계정을 입력할 때, 1~5 숫자와 a~z 문자로만 구성된 단어이어야 한다는 제약&lt;span style=&quot;color: #9d9d9d;&quot;&gt;이 있다. 키는 Owner와 Active 두가지를 입력할 수 있는데, 동일하게 입력해도 다르게 입력해도 상관없다. 참고로, &lt;/span&gt;나중에 Owner나 Active키를 변경시키기 원한다면 그때는 반드시 Owner키가 있어야 한다&lt;span style=&quot;color: #9d9d9d;&quot;&gt;는 점을 알아두자. &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;※ 키는 EOS 네트워크에서만 사용가능한 포멧으로 만들어진 것을 사용해야 한다. EOS Public Key 포멧을 제공하는 프로그램이 있거나 이미 사용하고 있는 EOS 키가 있다면 그대로 입력하면 된다. 만약에 작성자처럼 EOS 정글넷을 처음사용하고 키를 만드는 프로그램이 없다면, 온라인으로 EOS 포멧의 키를 만들어주는 사이트 중 하나를 이용해도 된다. (ex, EOSAuthority 에서 제공하는 EOS 키 생성 서비스 : &lt;/span&gt;&lt;u&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;&lt;a style=&quot;color: #409d00;&quot; href=&quot;https://eosauthority.com/generate_eos_private_key&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://eosauthority.com/generate_eos_private_key&lt;/a&gt;&lt;/span&gt;&lt;/u&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;※ Create을 클릭하면, 아래와 같은 메시지가 나오는데, 방금 계정 생성을 요청한 트랜잭션이 실행되긴했지만, EOS (정글넷) 네트워크에서 확인이 되지 않았다는 것을 말해주는 메시지이다. 참고로, 네트워크 전체에 퍼져서 완전한 확인이 되어야만 계정이 네트워크에서 확실히 사용할 수 있다는 것을 알아두자. 그리고 메시지에서 한가지 더 알 수 있는 점은 &amp;ldquo;junglefaucet&amp;rdquo;이라는 단어가 반복되는데, 이 단어는 현재 사용자가 만들려는 계정을 생성시키기 위해 사용된 EOS (정글넷) 네트워크 내의 기존 계정 중 하나라는 것을 알 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1576426459879&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;executed transaction: e54c5b1a657d4f8a1681b41a7f78d5f78215ba39e4aca6c5602ab756fe200b5c 336 bytes 1330 us warn 2019-12-15T14:04:52.778 thread-0 main.cpp:482 print_result ] warning: transaction executed locally, but may not be confirmed by the network yet

# eosio &amp;lt;= eosio::newaccount {&quot;creator&quot;:&quot;junglefaucet&quot;,&quot;name&quot;:&quot;leeseungwook&quot;,&quot;owner&quot;:{&quot;threshold&quot;:1,&quot;keys&quot;:[{&quot;key&quot;:&quot;EOS7yTteXCUgr... 
# eosio &amp;lt;= eosio::buyrambytes {&quot;payer&quot;:&quot;junglefaucet&quot;,&quot;receiver&quot;:&quot;leeseungwook&quot;,&quot;bytes&quot;:4096} 
# eosio.token &amp;lt;= eosio.token::transfer {&quot;from&quot;:&quot;junglefaucet&quot;,&quot;to&quot;:&quot;eosio.ram&quot;,&quot;quantity&quot;:&quot;0.4487 EOS&quot;,&quot;memo&quot;:&quot;buy ram&quot;} 
# junglefaucet &amp;lt;= eosio.token::transfer {&quot;from&quot;:&quot;junglefaucet&quot;,&quot;to&quot;:&quot;eosio.ram&quot;,&quot;quantity&quot;:&quot;0.4487 EOS&quot;,&quot;memo&quot;:&quot;buy ram&quot;} 
# eosio.ram &amp;lt;= eosio.token::transfer {&quot;from&quot;:&quot;junglefaucet&quot;,&quot;to&quot;:&quot;eosio.ram&quot;,&quot;quantity&quot;:&quot;0.4487 EOS&quot;,&quot;memo&quot;:&quot;buy ram&quot;} 
# eosio.token &amp;lt;= eosio.token::transfer {&quot;from&quot;:&quot;junglefaucet&quot;,&quot;to&quot;:&quot;eosio.ramfee&quot;,&quot;quantity&quot;:&quot;0.0023 EOS&quot;,&quot;memo&quot;:&quot;ram fee&quot;} 
# junglefaucet &amp;lt;= eosio.token::transfer {&quot;from&quot;:&quot;junglefaucet&quot;,&quot;to&quot;:&quot;eosio.ramfee&quot;,&quot;quantity&quot;:&quot;0.0023 EOS&quot;,&quot;memo&quot;:&quot;ram fee&quot;} 
# eosio.ramfee &amp;lt;= eosio.token::transfer {&quot;from&quot;:&quot;junglefaucet&quot;,&quot;to&quot;:&quot;eosio.ramfee&quot;,&quot;quantity&quot;:&quot;0.0023 EOS&quot;,&quot;memo&quot;:&quot;ram fee&quot;} 
# eosio.token &amp;lt;= eosio.token::transfer {&quot;from&quot;:&quot;eosio.ramfee&quot;,&quot;to&quot;:&quot;eosio.rex&quot;,&quot;quantity&quot;:&quot;0.0023 EOS&quot;,&quot;memo&quot;:&quot;transfer from eosio.ramfee t... 
# eosio.ramfee &amp;lt;= eosio.token::transfer {&quot;from&quot;:&quot;eosio.ramfee&quot;,&quot;to&quot;:&quot;eosio.rex&quot;,&quot;quantity&quot;:&quot;0.0023 EOS&quot;,&quot;memo&quot;:&quot;transfer from eosio.ramfee t... 
# eosio.rex &amp;lt;= eosio.token::transfer {&quot;from&quot;:&quot;eosio.ramfee&quot;,&quot;to&quot;:&quot;eosio.rex&quot;,&quot;quantity&quot;:&quot;0.0023 EOS&quot;,&quot;memo&quot;:&quot;transfer from eosio.ramfee t... 
# eosio &amp;lt;= eosio::delegatebw {&quot;from&quot;:&quot;junglefaucet&quot;,&quot;receiver&quot;:&quot;leeseungwook&quot;,&quot;stake_net_quantity&quot;:&quot;1.0000 EOS&quot;,&quot;stake_cpu_quanti... 
# eosio.token &amp;lt;= eosio.token::transfer {&quot;from&quot;:&quot;junglefaucet&quot;,&quot;to&quot;:&quot;eosio.stake&quot;,&quot;quantity&quot;:&quot;2.0000 EOS&quot;,&quot;memo&quot;:&quot;stake bandwidth&quot;} 
# junglefaucet &amp;lt;= eosio.token::transfer {&quot;from&quot;:&quot;junglefaucet&quot;,&quot;to&quot;:&quot;eosio.stake&quot;,&quot;quantity&quot;:&quot;2.0000 EOS&quot;,&quot;memo&quot;:&quot;stake bandwidth&quot;} 
# eosio.stake &amp;lt;= eosio.token::transfer {&quot;from&quot;:&quot;junglefaucet&quot;,&quot;to&quot;:&quot;eosio.stake&quot;,&quot;quantity&quot;:&quot;2.0000 EOS&quot;,&quot;memo&quot;:&quot;stake bandwidth&quot;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;3. 정글넷 모니터 사이트#2 접속&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;u&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;&lt;a style=&quot;color: #409d00;&quot; href=&quot;https://jungle.bloks.io/&quot;&gt;https://jungle.bloks.io/&lt;/a&gt;&lt;/span&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;4. Search Box에 (생성된 계정) Account 입력&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;gt; Search 클릭&lt;/p&gt;
&lt;p&gt;&amp;gt; 생성된 Account 계정 정보 확인&lt;/p&gt;</description>
      <category>dev-tips/eos</category>
      <category>Account</category>
      <category>EOS</category>
      <category>Jungle</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/191</guid>
      <comments>https://caileb.tistory.com/191#entry191comment</comments>
      <pubDate>Mon, 16 Dec 2019 01:27:00 +0900</pubDate>
    </item>
    <item>
      <title>Array와 List 비교</title>
      <link>https://caileb.tistory.com/190</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;Array와 List 비교&lt;/h2&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;array, list.PNG&quot; data-origin-width=&quot;295&quot; data-origin-height=&quot;251&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2E7oT/btqAlO5jPLZ/sQqzKP3j6VkNuuwhl3yn9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2E7oT/btqAlO5jPLZ/sQqzKP3j6VkNuuwhl3yn9k/img.png&quot; data-alt=&quot;[ Array, List 비교 ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2E7oT/btqAlO5jPLZ/sQqzKP3j6VkNuuwhl3yn9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2E7oT%2FbtqAlO5jPLZ%2FsQqzKP3j6VkNuuwhl3yn9k%2Fimg.png&quot; data-filename=&quot;array, list.PNG&quot; data-origin-width=&quot;295&quot; data-origin-height=&quot;251&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ Array, List 비교 ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;font-size: 1.12em;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Array, List&lt;/span&gt; 비교&lt;/h2&gt;
&lt;p&gt;&lt;b&gt;Array : &lt;/b&gt;&lt;b&gt;정적으로 연결된 연속된 공간&lt;/b&gt;으로 이루어진 자료구조&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&quot;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;공간 크기를 지정해야 한다.&lt;/span&gt;&lt;/b&gt;&quot;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Array는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;여러개의 연속된 공간을 그룹으로 만들어서 관리하는 자료구조&lt;/span&gt;이기 때문에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;얼마나 사용할지를 미리 선언&lt;/span&gt;해두고 사용해야 한다. 이 특징 때문에, 미리 잡아놓은 공간을 쓰지 않을 경우 공간(메모리)가 낭비되는 경우가 발생하기도 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1575896271731&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int[] a = new int[3];&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;List : &lt;/b&gt;&lt;b&gt;동적으로 연결가능한 연속된 공간&lt;/b&gt;으로 이루어진 자료구조&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&quot;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;공간 크기를 동적으로 사용한다.&lt;/span&gt;&lt;/b&gt;&quot;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;List는 Array와는 다르게 사용할&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;공간을 미리 선언해줄 필요가 없다&lt;/span&gt;. Array와는 다르게 사용하지 않는 공간이 필요없는 구조이다. 이 특징 때문에,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;필요할 때마다 공간을 추가하거나 제거하면서 필요하지 않은 공간(메모리)을 낭비하지 않는다&lt;/span&gt;.&lt;/p&gt;
&lt;pre id=&quot;code_1575896350900&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;Integer&amp;gt; a = new ArrayList&amp;lt;Integer&amp;gt;();
List&amp;lt;Integer&amp;gt; a = new LinkedList&amp;lt;Integer&amp;gt;();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Array, ArrayList, LinkedList&lt;/span&gt; 비교&lt;/h2&gt;
&lt;p&gt;&lt;b&gt;Array와 ArrayList : 배열 구조이며, &lt;/b&gt;&lt;b&gt;인덱스(index)를 사용&lt;/b&gt;하는 자료구조&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&quot;&lt;b&gt;인덱스를 사용한다.&lt;/b&gt;&quot;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Array와 ArrayList는 배열 내의 각 요소의 공간들은 배열을 선언할 때 사용했던 자료형으로 &lt;span style=&quot;color: #006dd7;&quot;&gt;동일한 사이즈의 연속된 공간으로 할당&lt;/span&gt;된다. 다시 말해서, 동일한 사이즈의 공간들이 연속되어 있다는 것이다. 배열구조의 &lt;span style=&quot;color: #006dd7;&quot;&gt;이 특징이 인덱스를 사용할 수 있도록 만들어준다&lt;/span&gt;. 참고로, Java에서는 ArrayList와 LinkedList가 있는데, 이 중 ArrayList는 Array + List이기 때문에 내부에서는 배열을 사용하고 있어서 인덱스로 접근이 가능한 구조이고, LinkedList는 Linked + List이기 때문에 내부에서 배열을 사용하지 않기 때문에 인덱스로 접근이 불가능한 구조이다.&lt;/p&gt;
&lt;pre id=&quot;code_1575896456873&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Array
int[] a = new int[3];
a[0] = 10;
a[1] = 20;
a[2] = 30;
system.out.println(a[0]);

// ArrayList
List&amp;lt;Integer&amp;gt; a = new ArrayList&amp;lt;Integer&amp;gt;();
a.add(10);
a.add(20);
a.add(30);
system.out.println(a.get(0));&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;ArrayList와 LinkedList : 데이터 추가, 삭제처리&lt;/b&gt;가 다른 자료구조&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&quot;&lt;b&gt;데이터 추가, 삭제가 많다면 ArrayList보다는 LinkedList를 사용한다.&lt;/b&gt;&quot;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot;&gt;ArrayList구조의 변수와 LinkedList구조의 변수에 각각 특정 데이터를 중간에 추가하려고 한다. 이때, &lt;span style=&quot;color: #006dd7;&quot;&gt;ArrayList는 중간에 새로운 데이터를 추가하기 위해, 추가될 위치 뒷부분의 데이터 전체를 한칸씩 뒤로 옮겨야 한다&lt;/span&gt;. 반면에, &lt;span style=&quot;color: #006dd7;&quot;&gt;LinkedList는 중간에 새로운 데이터를 추가한다면, 추가될 위치의 바로 앞 데이터의 링크값만 변경시키고 나머지는 그대로 냅두게 된다&lt;/span&gt;. 중간의 데이터를 삭제시키는 경우에도 비슷한 상황이 벌어지게 된다. 따라서, 프로그램을 개발할 때, 데이터의 추가와 삭제가 빈번하게 발생하는 처리를 한다면 ArrayList보다는 LinkedList를 사용하는 것이 효율적이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;참고&lt;/h3&gt;
&lt;p&gt;ArrayList java code&lt;/p&gt;
&lt;p&gt;&lt;u&gt;&lt;a href=&quot;http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/tip/src/share/classes/java/util/ArrayList.java&quot;&gt;http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/tip/src/share/classes/java/util/ArrayList.java&lt;/a&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;LinkedList java code&lt;/p&gt;
&lt;p&gt;&lt;u&gt;&lt;a href=&quot;http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/tip/src/share/classes/java/util/LinkedList.java&quot;&gt;http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/tip/src/share/classes/java/util/LinkedList.java&lt;/a&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>understanding/data structure</category>
      <category>Array</category>
      <category>arrayList</category>
      <category>LinkedList</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/190</guid>
      <comments>https://caileb.tistory.com/190#entry190comment</comments>
      <pubDate>Mon, 9 Dec 2019 22:35:09 +0900</pubDate>
    </item>
    <item>
      <title>Array와 LinkedList 비교</title>
      <link>https://caileb.tistory.com/189</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;Array와 LinkedList의 비교&lt;/h2&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #7e98b1;&quot;&gt;c++ 코드 포함&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;메모리 구조&lt;/span&gt;의 차이&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Array&lt;/h3&gt;
&lt;p&gt;Array는 리스트에 포함되는 데이터의 메모리 위치들이 차례로 나열시킨 구조이다. 프로그래밍을 할 때 메모리(=주기억장치, 램, RAM)에 int 자료형을 담을 수 있는 공간 10개를 담은 배열(array)을 선언했다면, 아래 왼쪽 그림처럼 OS는 메모리에 10개의 &lt;span style=&quot;color: #ee2323;&quot;&gt;int 자료형이 연속된 공간을 할당시키게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;만약에, int와 같은 primitive 타입이 아닌, &lt;span style=&quot;color: #ee2323;&quot;&gt;struct를 사용해서 새로운 타입 만들었다면, 이또한 연속된 공간으로 할당된다.&lt;/span&gt; 아래 오른쪽 코드처럼 struct 배열로 만들어서 프로그래밍을 하면 메모리에는 Element 구조단위로 10개가 연속된 공간으로 할당된다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #7e98b1;&quot;&gt;※ primitive type ? 프로그래밍을 할 때 언어에서 지원하는 가장 기본타입을 말하는 것으로 int, float, double, boolean 등이 될 수 있다. 참고로, java 언어에서는 int, double 말고도 이를 대신할 수 있는 Integer, Double 타입이 존재하긴 하지만, 이런 타입은 int와 같은 primitive 타입을 가지고 class화 시켜서 사용되는 타입이라고 해서 Wrapper Type이라고 따로 구분된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;array.PNG&quot; data-origin-width=&quot;530&quot; data-origin-height=&quot;327&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xr9JV/btqz7nVwl0B/o8S6lKR1PjQO5F0kjwSeLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xr9JV/btqz7nVwl0B/o8S6lKR1PjQO5F0kjwSeLk/img.png&quot; data-alt=&quot;[ array 메모리 구조 ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xr9JV/btqz7nVwl0B/o8S6lKR1PjQO5F0kjwSeLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fxr9JV%2Fbtqz7nVwl0B%2Fo8S6lKR1PjQO5F0kjwSeLk%2Fimg.png&quot; data-filename=&quot;array.PNG&quot; data-origin-width=&quot;530&quot; data-origin-height=&quot;327&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ array 메모리 구조 ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;example) int array initialization&lt;/p&gt;
&lt;pre id=&quot;code_1575176790345&quot; class=&quot;c++ cpp&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;

int main() {
	int a[10] = {0,};
	return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;example) struct array&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;initialization&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1575176805266&quot; class=&quot;c++ cpp&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;

struct Element {
	int data = 0;
	int data2 = 0;
};
int main() {
	Element a[10];
	return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;LinkedList&lt;/h3&gt;
&lt;p&gt;LinkedList는 리스트에 포함되는 &lt;span style=&quot;color: #ee2323;&quot;&gt;데이터의 메모리 위치들이 모두 떨어진 구조이다.&lt;/span&gt; 프로그래밍을 할 때, &lt;span style=&quot;color: #ee2323;&quot;&gt;Array와 다르게 각 요소의 단위가 int, float 같은 primitive type는 될 수가 없다.&lt;/span&gt; 이러한 이유로 LinkedList에서는 int와 같은 primitive type을 사용한 다른 구조(struct or class)를 만들어서 사용하게 된다. 각 구조의 이름은 개발자 마음대로 부르지만 재구성한 구조를 보통 노드(Node)라고 부른다. 이 노드에는 반드시 들어가야하는 값이 있는데, &lt;span style=&quot;color: #ee2323;&quot;&gt;자기 자신과 동일한 구조의 변수 하나를 가지고 있어야 한다. 이 변수(=첫번째 노드)를 통해서만 다른 노드로 접근이 가능하기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;LinkedList는 Array와는 다르게 &lt;span style=&quot;color: #ee2323;&quot;&gt;보통 고정 크기를 가지도록 프로그래밍하지 않는다&lt;/span&gt;. 일반적으로 프로그램이 동작하면서 필요할 때마다 필요한 공간을 동적으로 추가시키거나 삭제시킬 수 있도록 프로그래밍 한다. 하지만, 이번 포스팅에서는 Array와의 차이를 이해하는 것이 목적이므로 고정된 크기를 가지는 것처럼 아래코드를 프로그래밍해봤다. 아래 그림처럼 코딩이 되었다면 OS는 메모리에 10개의 Node 자료형의 공간을 할당시키지만, 이 자료형은 메모리에 연속되지 않는다. 이것이 바로 메모리입장에서 Array와의 가장 큰 차이이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;linkedlist.PNG&quot; data-origin-width=&quot;471&quot; data-origin-height=&quot;416&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bj4KHR/btqz7328BOK/j09vIp3yxmyjIWTac8j950/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bj4KHR/btqz7328BOK/j09vIp3yxmyjIWTac8j950/img.png&quot; data-alt=&quot;[ linkedlist 메모리 구조 ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bj4KHR/btqz7328BOK/j09vIp3yxmyjIWTac8j950/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbj4KHR%2Fbtqz7328BOK%2Fj09vIp3yxmyjIWTac8j950%2Fimg.png&quot; data-filename=&quot;linkedlist.PNG&quot; data-origin-width=&quot;471&quot; data-origin-height=&quot;416&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ linkedlist 메모리 구조 ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;example) linked list&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;initialization&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1575176823660&quot; class=&quot;c++ cpp&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;

struct Node {
	Node* next;
	int data;
};
int main()
{
    	/* 10 크기의 링크드리스트 선언 &amp;amp; 초기화 */
	Node* head;
	for(int i=0; i&amp;lt;10; i++) {
		if(i==0)
			head = new Node({NULL, 0});
		else {
			Node* next = new Node({head, 0});
			head = next;
		}
	}
   	return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;특정 데이터 접근&lt;/span&gt;의 차이&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Array&lt;/h3&gt;
&lt;p&gt;Array의 요소로 접근할 때는 &lt;span style=&quot;color: #ee2323;&quot;&gt;인덱스(index)를 사용해서 바로 접근&lt;/span&gt;할 수 있다. 아래 코드처럼 10 크기를 가진는 배열의 4번째 요소의 값을 변경시키기 위해 4번째 인덱스로 바로 접근해서 해당 데이터를 수정할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;example) int array modification&lt;/p&gt;
&lt;pre id=&quot;code_1575176840055&quot; class=&quot;c++ cpp&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;

int main() {
	int a[10] = {0,};
	a[4] = 7;
	return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;struct 자료형을 가지는 배열의 경우에도, struct 단위로 인덱스가 접근이 가능하다. 아래 코드는 재정의한 struct 구조 안에 있는 요소에 접근하는 코드이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;example) struct array modification&lt;/p&gt;
&lt;pre id=&quot;code_1575176870360&quot; class=&quot;c++ cpp&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;

struct Element {
	int data = 0;
	int data2 = 0;
};
int main() {
	Element a[10];
	a[4].data = 7;
	a[4].data2 = 8;
	return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;LinkedList&lt;/h3&gt;
&lt;p&gt;LinkedList는 인덱스를 사용하지 않고, 반복문을 사용해서 각 노드로 접근해야 한다. 앞에서 말했듯이 LinkedList는 리스트의 &lt;span style=&quot;color: #ee2323;&quot;&gt;첫번째 노드값을 통해서만 다른 노드로 접근이 가능&lt;/span&gt;하기 때문이다. 참고로, LinkedList에서는 첫번째 노드를 보통 head라고 부른다. 반복문을 사용해야 하기 때문에 Array와는 다르게 아래처럼 for문이나 while문이 등장하게 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;example) linked list modification&lt;/p&gt;
&lt;pre id=&quot;code_1575176884466&quot; class=&quot;c++ cpp&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct Node {
	Node* next;
	int data;
};
int main()
{
    	/* 10 크기의 링크드리스트 선언 &amp;amp; 초기화 */
	Node* head;
	for(int i=0; i&amp;lt;10; i++) {
		if(i==0)
			head = new Node({NULL, 0});
		else {
			Node* next = new Node({head, 0});
			head = next;
		}
	}

	/* 특정 위치의 노드 데이터를 접근 */
	int i=0;
	for(Node* current=head; current!=NULL; current=current-&amp;gt;next) {
		if(i==4) 
			current-&amp;gt;data = 7;
		i++;
	}
   	return 0;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>understanding/data structure</category>
      <category>Array</category>
      <category>C++</category>
      <category>LinkedList</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/189</guid>
      <comments>https://caileb.tistory.com/189#entry189comment</comments>
      <pubDate>Sun, 1 Dec 2019 14:41:39 +0900</pubDate>
    </item>
    <item>
      <title>eos - [config.ini] http-server-address</title>
      <link>https://caileb.tistory.com/187</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;(config.ini) http-server-address&lt;/h2&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;nodeos에 대한 http 연결허용 범위 설정하기&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;http-server-address.PNG&quot; data-origin-width=&quot;386&quot; data-origin-height=&quot;159&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bf5xOF/btqz6W21kz8/fu65e02Lgyagv7XwfVNiR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bf5xOF/btqz6W21kz8/fu65e02Lgyagv7XwfVNiR1/img.png&quot; data-alt=&quot;(config.ini 파일) http-server-address 속성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bf5xOF/btqz6W21kz8/fu65e02Lgyagv7XwfVNiR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbf5xOF%2Fbtqz6W21kz8%2Ffu65e02Lgyagv7XwfVNiR1%2Fimg.png&quot; data-filename=&quot;http-server-address.PNG&quot; data-origin-width=&quot;386&quot; data-origin-height=&quot;159&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;(config.ini 파일) http-server-address 속성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;EOS config.ini 파일에 http-server-address 의 기본값은 127.0.0.1로 설정되어 있다. &lt;span style=&quot;color: #006dd7;&quot;&gt;http-server-address는 EOS Node에 http로 접근할 때 어디까지 허용할지에 대한 주소를 설정하는 속성값&lt;/span&gt;이다. &lt;span style=&quot;color: #006dd7;&quot;&gt;기본값이 127.0.0.1이므로, 이대로 동작된다면 로컬(Local)로부터의 http요청만을 허용하겠다는 의미&lt;/span&gt;이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;기본값으로 설정되어 있는 상태에서 예를 들어, 로컬에 EOS Node를 실행시키고 있다고 가정하자. 그리고 로컬 PC가 현재 속해 있는 네트워크 범위가 10.x.x.x 이고, 로컬 PC에게 할당된 IP가 10.0.0.7이라고 가정하자. 이런 상황이라면 로컬 PC에서는 자신의 PC에 실행시켜놓은 EOS Node에 접근이 가능하지만, 다른 PC에서는 해당 PC로 접근이 불가능하다. 이유는 앞에서 말했듯이 로컬 IP(=127.0.0.1)만을 허용하도록 설정해두었기 때문이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;만약에, EOS Node 정보를 가져오는 cleos API 중에 get did 명령을 실행하려한다면, 요청 url을 127.0.0.1로 할 때는 응답을 제대로 받을 수 있지만, 로컬 PC에서 요청 url을 자신의 IP인 10.0.0.7로 할 때는 응답을 받을 수 없는 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;로컬 PC에서 cleos 명령 실행&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1574943682125&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;실행 됨
$ cleos --url http://127.0.0.1:8888 get info

실행 안됨
$ cleos --url http://10.0.0.7:8888 get info&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;다른 PC에서 cleos 명령 실행&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1574943695013&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;실행 안됨
$ cleos --url http://10.0.0.7:8888 get info&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;앞에서 설명한 상황에서는 다른 PC에서 접속이 안되고 있다. 하지만, 혼자 개발 중이 아니라면 내 로컬에서 실행 중인 EOS Node를 다른 PC에서 접속이 가능하도록 변경해 줄 필요가 있다. 이때 &lt;span style=&quot;color: #006dd7;&quot;&gt;config.ini 파일에 있는 http-server-address값을 0.0.0.0:8888로 변경해주면 로컬 뿐만 아니라 다른 IP까지 모두 활성화시킨다는 의미&lt;/span&gt;로 다른 PC의 요청도 받을 수 있게 된다. 따로 변경을 하지 않고 처음 다운받은 그대로의 상태라면 127.0.0.1로 되어 있을 것인데, 이 값을 변경해 주고 다시 EOS Node를 실행시킨다면 외부에서 로컬의 EOS Node로 접근이 가능해지는 상태가 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;config.ini 파일 변경 전&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1574943797362&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# The local IP and port to listen for incoming http connections; set blank to disable. (eosio::http_plugin)
http-server-address = 127.0.0.1:8888&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;config.ini 파일 변경 후&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1574943808053&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# The local IP and port to listen for incoming http connections; set blank to disable. (eosio::http_plugin)
http-server-address = 0.0.0.0:8888&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;로컬에서 EOS Node를 새로 실행시켰다면 앞에서 시도해던 cleos 명령어들을 하나씩 쳐서 요청을 하면, 올바르게 응답이 오는 것을 확인할 수 있을 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;b&gt;로컬 PC에서 cleos 명령 실행&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1574943839086&quot; class=&quot;html xml&quot; style=&quot;margin: 20px auto 0px; display: block; overflow: auto; padding: 15px; color: #383a42; background: #f6f7f8; font-size: 14px; border-radius: 3px; font-family: Menlo, Consolas, Monaco, monospace; border: 1px solid #dddddd; cursor: default; z-index: 1;&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;실행 됨
$ cleos --url http://127.0.0.1:8888 get info

실행 됨
$ cleos --url http://10.0.0.7:8888 get info&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;다른 PC에서 cleos 명령 실행&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1574943839086&quot; class=&quot;html xml&quot; style=&quot;margin: 20px auto 0px; display: block; overflow: auto; padding: 15px; color: #383a42; background: #f6f7f8; font-size: 14px; border-radius: 3px; font-family: Menlo, Consolas, Monaco, monospace; border: 1px solid #dddddd; cursor: default; z-index: 1;&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;실행 됨
$ cleos --url http://10.0.0.7:8888 get info&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;참고 사이트&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;a href=&quot;https://developers.eos.io/eosio-nodeos/v1.0/docs/configuration-file&quot;&gt;https://developers.eos.io/eosio-nodeos/v1.0/docs/configuration-file&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>dev-tips/eos</category>
      <category>config.ini</category>
      <category>EOS</category>
      <category>http-server-address</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/187</guid>
      <comments>https://caileb.tistory.com/187#entry187comment</comments>
      <pubDate>Thu, 28 Nov 2019 21:33:45 +0900</pubDate>
    </item>
    <item>
      <title>윈도우에서 Sourcetree &amp;ndash; GitHub 연동 (checkout, clone)</title>
      <link>https://caileb.tistory.com/186</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;윈도우에서 Sourcetree - GitHub 연동&lt;/h2&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;(checkout, clone)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;sourcetree, github.PNG&quot; data-origin-width=&quot;320&quot; data-origin-height=&quot;234&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJODxw/btqzU9wjl0O/KEXJg6XwUwmkF6Mn1znlx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJODxw/btqzU9wjl0O/KEXJg6XwUwmkF6Mn1znlx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJODxw/btqzU9wjl0O/KEXJg6XwUwmkF6Mn1znlx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJODxw%2FbtqzU9wjl0O%2FKEXJg6XwUwmkF6Mn1znlx1%2Fimg.png&quot; data-filename=&quot;sourcetree, github.PNG&quot; data-origin-width=&quot;320&quot; data-origin-height=&quot;234&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;소스트리(Sourcetree)는 깃허브(GitHub)의 클라이언트 프로그램으로 많이 사용되고 있습니다. 참고로, 소스트리는 윈도우(Windows)버전과 맥OS(macOS)버전 모두 제공하고 있습니다. 소스트리는 일반적으로 깃허브 로그인할 때 사용하는 ID/Password 방식을 사용하지 않고, 이를 대신해서 PublicKey/PrivateKey 방식을 사용하고 있습니다. 따라서, &lt;span style=&quot;color: #006dd7;&quot;&gt;사용자가 소스트리를 사용할 때는 PublicKey/PrivateKey 방식인 ssh키를 가지고 있어야 합니다&lt;/span&gt;. 만약에, ssh키가 없다면 직접 만든 후 그걸 가지고 있어도 됩니다. &lt;span style=&quot;color: #006dd7;&quot;&gt;ssh키는 PublicKey와 PrivateKey가 한쌍으로 이루어져 있는데, 둘 중 PrivateKey는 Sourcetree에 세팅하고, PublicKey는 GitHub에 세팅&lt;/span&gt;하면, 그 후부터는 Sourcetree와 GitHub는 서로 가지고 있는 키를 사용해서 안전하게 암호화 통신을 하게 되는 것이죠. 본 포스팅에서는 윈도우 환경에서 Sourcetree를 사용하기 위해 설정을 어떻게 해야하는지를 설명할 것입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;환경&lt;/h2&gt;
&lt;p&gt;OS : Windows 10&lt;/p&gt;
&lt;p&gt;GitHub 인증 방법 : ssh 키 사용&lt;/p&gt;
&lt;p&gt;GitHub 클라이언트 Tool : Sourcetree&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;세팅 순서 요약&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Sourcetree에 &lt;span style=&quot;color: #006dd7;&quot;&gt;ssh 키 추가하기&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GitHub에 &lt;span style=&quot;color: #006dd7;&quot;&gt;ssh 키 추가하기&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Sourcetree를 사용해서 &lt;span style=&quot;color: #006dd7;&quot;&gt;GitHub 코드 내려받기&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;세팅 순서&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;1. Sourcetree&lt;/span&gt;&lt;span&gt;에&lt;span style=&quot;color: #006dd7;&quot;&gt; ssh 키 추가하기&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;1) Sourcetree 다운&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #7e98b1;&quot;&gt;&lt;u&gt;&lt;a style=&quot;color: #7e98b1;&quot; href=&quot;https://www.sourcetreeapp.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.sourcetreeapp.com/&lt;/a&gt;&lt;/u&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;2) Sourcetree 사용해서 ssh 키 생성&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; (참고, 이미 보유 중인 ssh가 있다면 그걸 사용해도 된다)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;gt; C:\Users\joker\AppData\Local\SourceTree\app-3.2.6\tools\putty\puttygen.exe &lt;/span&gt;실행&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;gt; &quot;Generate&quot;&lt;/span&gt;클릭&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;gt; ssh &lt;/span&gt;키 생성이 완료될 때까지 마우스 이동&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;gt; &quot;Save public key&quot; &lt;/span&gt;클릭&lt;span style=&quot;color: #ee2323;&quot;&gt; (추후 잘 보관 필요)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;gt; &quot;Save private key&quot; &lt;/span&gt;클릭&lt;span style=&quot;color: #ee2323;&quot;&gt; (추후 잘 보관 필요)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;3) 생성된 ssh 키를 Sourcetree에 추가&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;gt; Sourcetree &amp;gt; &lt;/span&gt;도구&lt;span&gt; &amp;gt; &lt;/span&gt;옵션&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;gt; [&lt;/span&gt;일반&lt;span&gt;] &lt;/span&gt;탭&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;gt; [SSH &lt;/span&gt;클라이언트 설정&lt;span&gt;] &lt;/span&gt;영역&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;SSH &lt;/span&gt;키&lt;span&gt; : &lt;span style=&quot;color: #409d00;&quot;&gt;private key &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;선택&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; (참고, 2번 단계에서 저장시킨 private key 파일을 선택한다)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;SSH &lt;/span&gt;클라이언트&lt;span&gt; : &lt;span style=&quot;color: #409d00;&quot;&gt;PuTTY/Plink &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;선택&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;2. GitHub&lt;/span&gt;&lt;span&gt;에&lt;span style=&quot;color: #006dd7;&quot;&gt; ssh 키 추가하기&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;[방법1]&lt;/b&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt; 계정 메뉴를 통해서 GitHub 에 ssh 키를 추가&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;gt; GitHub &amp;gt; &lt;/span&gt;계정&lt;span&gt; &amp;gt; Settings &amp;gt; SSH and GPG keys&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;gt; &quot;New SSH key&quot; &lt;/span&gt;클릭&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;Title : &lt;span style=&quot;color: #409d00;&quot;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;사용할 key 닉네임 입력]&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;Key : &lt;span style=&quot;color: #1b711d;&quot;&gt;ssh-rsa [&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #1b711d;&quot;&gt;사용할 public key 입력]&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;[방법2]&lt;/b&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt; 프로젝트 메뉴를 통해서 GitHub 에 ssh 키를 추가&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;gt; GitHub &amp;gt; &lt;/span&gt;프로젝트 &lt;span&gt;&amp;gt; Settings &amp;gt; Deploy keys&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;gt; &quot;Add deploy key&quot; &lt;/span&gt;클릭&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;Title : &lt;span style=&quot;color: #409d00;&quot;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;사용할 key 닉네임 입력]&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;Key : &lt;span style=&quot;color: #409d00;&quot;&gt;ssh-rsa [&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;사용할 public key 입력]&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;gt; &quot;Add key&quot; &lt;/span&gt;클릭&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;3. Sourcetree&lt;/span&gt;&lt;span&gt;를 사용해서&lt;span style=&quot;color: #006dd7;&quot;&gt; GitHub 코드 내려받기&lt;/span&gt;&lt;span&gt; (=checkout, clone)&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;1) Sourcetree 클론&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;gt; Sourcetree &amp;gt; Clone&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;첫번째 칸&lt;span&gt; : &lt;span style=&quot;color: #409d00;&quot;&gt;[from - &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;원격 저장소 경로 입력&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;]&lt;/span&gt; &lt;span style=&quot;color: #ee2323;&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;참고, GitHub 프로젝트의 &amp;ldquo;Clone or download&amp;rdquo; 버튼 &amp;gt; &amp;ldquo;Use HTTPS&amp;rdquo; 버튼을 눌러서 출력된 URL 경로를 복사한다.)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;두번째 칸&lt;span&gt; : &lt;span style=&quot;color: #409d00;&quot;&gt;[to - &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;로컬 저장소 경로 입력]&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;세번째 칸&lt;span&gt; : &lt;span style=&quot;color: #409d00;&quot;&gt;[to - &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;로컬 저장소 폴더 이름 입력]&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;gt; &quot;Clone&quot; &lt;/span&gt;클릭&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;2) 두번째 칸에 입력했던 로컬 저장소에 내려받아졌는지 확인&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>environment/version</category>
      <category>Github</category>
      <category>SourceTree</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/186</guid>
      <comments>https://caileb.tistory.com/186#entry186comment</comments>
      <pubDate>Fri, 22 Nov 2019 01:31:03 +0900</pubDate>
    </item>
    <item>
      <title>웹 소켓 통신 (Web Socket)</title>
      <link>https://caileb.tistory.com/185</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;웹 소켓 통신&lt;/h2&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;&lt;b&gt;(Web Socket)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bD7rpD/btqzQsVpxJy/HjpzWzNYKCgOxh6pF2GNMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bD7rpD/btqzQsVpxJy/HjpzWzNYKCgOxh6pF2GNMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bD7rpD/btqzQsVpxJy/HjpzWzNYKCgOxh6pF2GNMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbD7rpD%2FbtqzQsVpxJy%2FHjpzWzNYKCgOxh6pF2GNMK%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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;웹 소켓(Web Socket)은 두 프로그램간의 메시지를 교환하기 위한 통신방법&lt;/span&gt; 중 하나입니다. 웹 소켓은 W3C와 IETF에 의해 자리잡은 표준 프로토콜 중 하나이기 때문에, 현재 인터넷을 사용하는 환경에서 특히 많이 사용되고 있습니다. 예를 들면, &lt;span style=&quot;color: #1a5490;&quot;&gt;크롬(Chrome)이나 엣지(Edge) 등의 웹 브라우저 같은 경우에 웹 소켓 프로토콜을 지원하고 있기 때문에, 웹 브라우저를 통해서 웹 소켓을 사용하려고 한다면, javascript를 사용해서 어렵지 않게 개발&lt;/span&gt;할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;웹 소켓 &lt;span style=&quot;color: #f89009;&quot;&gt;특징 2가지&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;1. &lt;span style=&quot;color: #ee2323;&quot;&gt;양방향 통신(Full-Duplex)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;양방향 통신이란 데이터 송수신을 동시에 처리할 수 있는 통신방법을 말하는 것입니다&lt;span&gt;. &lt;/span&gt;웹 소켓은 클라이언트&lt;span&gt;(client)&lt;/span&gt;와 서버&lt;span&gt;(server)&lt;/span&gt;가 서로에게 원할 때 데이터를 주고 받을 수 있으니 양방향이라고 말할 수 있는 것이죠&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #7e98b1;&quot;&gt;※ 참고로, 양방향 통신이 아닌 단방향 통신은 한쪽으로만 송신이 가능한 방법을 말하는 것입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;2. &lt;span style=&quot;color: #ee2323;&quot;&gt;실시간 네트워킹(Real Time-Networking)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;웹 환경(ex, 웹 브라우저)에서 채팅(chatting), 주식(stock), 비디오(video) 데이터 등의 데이터들은 연속된 데이터를 화면에 빠르게 보여주어야 하거나, 여러 단말기(ex, PC, 스마트폰)에 빠르게 데이터를 교환하는 실시간처리가 필요한 부분들이 있습니다. 예를 들어, 요즘 가상화폐들의 변하는 가격들을 웹 사이트에서 실시간으로 볼 수 있는데 이런 서비스를 구현할 때 사용될 수 있는 것이죠.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #7e98b1;&quot;&gt;※ 웹 소켓 특징 중 하나가 실시간 네트워킹인 이유는 웹 소켓이 아닌 다른 비슷한 기술들로는 웹 브라우저 위에서 실시간으로 통신하기가 웹 소켓에 비해 상대적으로 어렵기 때문입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;웹 소켓 &lt;span style=&quot;color: #f89009;&quot;&gt;이전의 비슷한 기술&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;Polling&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;Long Polling&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;Streaming&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;XML Http Request&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;Ajax (Asynchronous Javascript + XML)&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;사실, 웹 소켓이 등장하기 이전에, 웹 소켓의 기능과 동일하게 만들 수 있는 기술들이 있었습니다. 만약에, 웹 소켓의 이해가 부족하거나 웹 소켓을 이용해서 개발하기가 부담스러운 환경이라면, 기존의 기술들인 위 목록에 나와있는 것들 중에서 하나를 골라서 개발을 해도 웹 소켓의 기능을 비슷하게 구현가능 할 것입니다. 본 포스팅에서는 웹 소켓이 가지는 &quot;&lt;span style=&quot;color: #1a5490;&quot;&gt;양방향 통신&lt;/span&gt;&quot;과 &quot;&lt;span style=&quot;color: #1a5490;&quot;&gt;실시간 통신&lt;/span&gt;&quot;이라는 두가지 특징을 가지고 이전의 다른 기술들보다 조금 더 쉽게 구현할 수 있다는 것만 알아두겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #7e98b1;&quot;&gt;※ XML Http Request이나 Ajax의 경우,클라이언트가 서버에게 원할 때 데이터를 보내서 요청하는 것이 목적이기 때문에, 그 반대가 어렵습니다. 서버가 클라이언트에게 원할 때 데이터를 보내는 것은 어렵다는 것이죠.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;웹 소켓 &lt;span style=&quot;color: #f89009;&quot;&gt;동작방법&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ra47w/btqzOLoiZi1/BW955Cw40ZUiaDaj5Ocyl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ra47w/btqzOLoiZi1/BW955Cw40ZUiaDaj5Ocyl0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ra47w/btqzOLoiZi1/BW955Cw40ZUiaDaj5Ocyl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRa47w%2FbtqzOLoiZi1%2FBW955Cw40ZUiaDaj5Ocyl0%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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;웹 소켓 &lt;span style=&quot;color: #f89009;&quot;&gt;Handshaking 메시지 교환&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Web Client&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;는 &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;handshake &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;요청 메시지를 &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Web Server&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;에게 보낸다&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Web Server&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;는 handshake 응답 메시지를 Web Client에게 보낸다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Web Client&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;는&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; data payload frames&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;를 &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Web Server&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;에게 보낸다&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Web Server&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;는&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; data payload frames&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;를 &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Web Client&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;에게 보낸다&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Web Client&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;는 &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;close frame&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;을 &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Web Server&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;에게 보낸다&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Web Server&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;는&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; close frame&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;을 &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Web Client&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;에게 보낸다&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;웹 소켓 &lt;span style=&quot;color: #f89009;&quot;&gt;프로토콜 특징&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최초 접속에서만 &lt;span&gt;http &lt;/span&gt;프로토콜 위에서 &lt;span&gt;handshaking&lt;/span&gt;을 하기 때문에&lt;span&gt; http header&lt;/span&gt;를 사용한다&lt;span&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;websocket&lt;/span&gt;을 위한 별도의 포트는 없으며&lt;span&gt;, &lt;/span&gt;기존 포트&lt;span&gt;(http-80, https-443)&lt;/span&gt;를 사용한다&lt;span&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;websocket &lt;/span&gt;프로토콜의&lt;span&gt; ws&lt;/span&gt;는 &lt;span&gt;http&lt;/span&gt;기반으로 운영되고&lt;span&gt;, wss&lt;/span&gt;는 &lt;span&gt;https&lt;/span&gt;기반으로 운영된다&lt;span&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;메시지 &lt;span&gt;(Upgrade:Websocket, Connection:Upgrade)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;메시지에 포함될 수있는 교환 가능한 메시지는 텍스트&lt;span&gt;(text)&lt;/span&gt;와 바이너리&lt;span&gt;(binary)&lt;/span&gt;이다&lt;span&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;웹 소켓 &lt;span style=&quot;color: #f89009;&quot;&gt;개발&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGPgWc/btqzOL2VFc2/kKklzXDAGZPbHfGFh6jHE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGPgWc/btqzOL2VFc2/kKklzXDAGZPbHfGFh6jHE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGPgWc/btqzOL2VFc2/kKklzXDAGZPbHfGFh6jHE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGPgWc%2FbtqzOL2VFc2%2FkKklzXDAGZPbHfGFh6jHE1%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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;웹 소켓 동작에 대한 이해를 돕기 위해, 실제로 웹 소켓을 사용해서 개발해야하는 경우, 개발환경에 따라 어떻게 사용될 수 있는지를 코드 일부를 추가해봤습니다. 우선 위 그림을 보면, 3개의 클라이언트와 한 개의 서버가 그려져 있습니다. 클라이언트 중 둘(=A와 B)은 javascript 코드를 사용해서 구현된 웹 애플리케이션(Web Application) 형태로 웹브라우저 위에서 동작되고, 다른 하나(=C)는 java 코드를 사용해서 구현된 안드로이드 애플리케이션(Android Appliacation) 형태로 동작되고 있다고 생각해보겠습니다. 이 3개의 클라이언트 애플리케이션는 웹 서버인 D와 웹 소켓을 사용해서 통신이 가능합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;웹 클라이언트인 &lt;span style=&quot;color: #1a5490;&quot;&gt;A, B, C&lt;/span&gt;는 모두 서버 &lt;span style=&quot;color: #1a5490;&quot;&gt;D&lt;/span&gt;와 통신하기 위해 &quot;ws://joker.com/say&quot;로 접속요청을 하고 있는 것을 볼 수 있습니다. &lt;span style=&quot;color: #1a5490;&quot;&gt;클라이언트는 서버에서 웹소켓을 위해 열어둔 해당 주소로 메시지를 보낼 수 있습니다. 반대로,서버가 접속된 클라이언트의 웹 소켓을 통해 메시지를 보낼 수 있죠. &lt;/span&gt;이렇게 서로 웹 소켓이 연결되어 있다면 이를 통해 서로에게 원할 때 메시지를 보낼 수 있는 양방향 통신을 할 수 있게 되는 것입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;1. 클라이언트 코드&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;클라이언트로 사용할 수 있는 코드 몇 가지를 소개하겠습니다. 웹 소켓은 웹 기반의 기술이다보니 크롬(chrome), 엣지(edge) 등의 유명한 브라우저들은 개발자들이 쉽게 사용할 수 있도록 이미 javascript로 제공해주고 있습니다. 먼저, &lt;span style=&quot;color: #1a5490;&quot;&gt;WebSocket API&lt;/span&gt;는 거의 대부분 브라우저들이 제공해주고 있습니다.&amp;nbsp; 하지만,가끔 WebSocket API를 제공하지 않는 브라우저들이 있습니다. 그렇다고 사용자들한테 WebSocket이 있는 브라우저로 가서 사용하라고 할 수는 없겠죠. 그래서 웹 브라우저가 WebSocket을 제공해주지 않는다고 하더라도 앞에서 말한 HTTP Streaming이나 HTTP Long Polling 등의 기술들을 통해 WebSocket과 동일하게 동작하도록 시켜주는 &lt;span style=&quot;color: #1a5490;&quot;&gt;SockJS&lt;/span&gt;라는 라이브러리가 있습니다. WebSocket API나 SockJS 모두 인터페이스는 거의 동일하기 때문에 큰 차이없이 사용할 수 있을 것입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;웹 브라우저가 아닌 다른 플랫폼인 안드로이드에서도 웹 소켓을 사용할 수 있습니다. &lt;span style=&quot;color: #1a5490;&quot;&gt;WebSocketClient&lt;/span&gt;라는 라이브러리를 사용하면 웹 소켓 기능을 어렵지 않게 구현할 수 있습니다. 현재(2019년 10월), java나 android에서 웹 소켓을 위해 기본으로 제공해준는 API가 없기 때문에 직접 구현하거나 어느정도 안정되어 사용할 수 있는 WebSocketClient 라이브러리를 사용하면 됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #7e98b1;&quot;&gt;코드 : javascript&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1574000764469&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 거의 모든 브라우저에서 지원
{
	var uri = &quot;ws://joker.com/say&quot;;
	var ws = new WebSocket(uri);
	ws.onopen = function() {
		..
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #7e98b1;&quot;&gt;코드 : javascript (SockJS)&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1574000782744&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// SockJS Client 사용
{
	var uri = &quot; ws://joker.com/say&quot;;
	var sock = new SockJS(uri);
	sock.onopen = function() {
		..
	};
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #7e98b1;&quot;&gt;코드 : Java (in Android)&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1574000807425&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import org.java_websocket.client.WebSocketClient;
{
	Uri uri = new URI(&quot;ws://joker.com/say&amp;rdquo;);
	WebSocketClient ws = new WebSocketClient(uri) {
		public void onOpen(..) {
			..
		}
	}
} 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;2. 서버 코드&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;서버는 스프링(springframework)을 사용하는 경우를 예로 들었습니다. 스프링에서는 웹 소켓에 대한 API를 제공해주고 있기 때문에, 스프링 개발 가이드 등의 많은 곳들에서 관련된 예제들을 살펴볼 수 있을 것입니다. 참고로,아래 registerWebSocketHandlers() 내용 중 &amp;ldquo;(2)&amp;rdquo;번 라인의 경우 웹 브라우저에서 WebSocket API를 제공하지 않을 경우, SockJS 라이브러리에 정의된 대로 동작하도록 추가시킨 것입니다. 따라서, &quot;(2)&quot;번 라인의 경우 현재 사용되고 있지만 만약에 모든 브라우저가 기본으로 제공된다면 제거를 해도 상관없을 것입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #7e98b1;&quot;&gt;코드 : Java (using Spring Framework)&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1574000876602&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// import org.springframework.web.socket.WebSocketHandler;

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
	private final WebSocketHandlerwsHandler;
	@Override
	public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
		registry.addHandler(new wsHandler(), &quot;/say&quot;).setAllowedOrigins(&quot;*&quot;); // (1)
		registry.addHandler(new wsHandler(), &quot;/say&quot;).setAllowedOrigins(&quot;*&quot;).withSockJS(); // (2)
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;웹 소켓 &lt;span style=&quot;color: #f89009;&quot;&gt;보안 고려사항&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;1. Non browser clients&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dbkO3m/btqzOljjFpO/2ldxB7ReGNfZXArNGsKTL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dbkO3m/btqzOljjFpO/2ldxB7ReGNfZXArNGsKTL1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dbkO3m/btqzOljjFpO/2ldxB7ReGNfZXArNGsKTL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdbkO3m%2FbtqzOljjFpO%2F2ldxB7ReGNfZXArNGsKTL1%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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;웹 소켓은 클라이언트를 &lt;span style=&quot;color: #1a5490;&quot;&gt;웹 브라우저를 사용해서 운영될 경우&lt;/span&gt;, header의 origin정보를 확인함으로써, 악성 javascript를 걸러낼 수 있습니다. 이 말은 웹 브라우저에서 기존에 사용하고 있던 &lt;span style=&quot;color: #1a5490;&quot;&gt;동일 근원 정책(Same-Origin Policy)을 통해 악성 리소스(resource)로부터 보호할 수 있다는 것&lt;/span&gt;입니다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #7e98b1;&quot;&gt;※ Same-Origin Policy는 웹 브라우저의 페이지에 보여지기 위해 필요한 이미지(ex, jpg, png)나 javascript나 css등을 포함하는 모든 리소스(resource)들은 동일한 사이트로부터 가지고 와야한다는 정책으로 웹 브라우저에서 보안을 위해 만들어둔 기술을 말하는 것입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;웹 소켓은 클라이언트를 &lt;span style=&quot;color: #1a5490;&quot;&gt;웹 브라우저가 아닌 다른 곳에서(ex, android, ios, windows) 운영될 경우&lt;/span&gt;, 악성 resource(ex, 악성 javascript)로부터 &lt;span style=&quot;color: #1a5490;&quot;&gt;보호를 할 수 없으므로, 클라이언트와 서버는 이를 고려해서 사용해야 합니다.&lt;/span&gt; 웹 소켓은 웹 브라우저 위의 웹 페이지를 통해 사용되는 환경에서 운영되는 프로토콜로 만들어진 것이기 때문에, 웹 브라우저가 아닌 다른 클라이언트에서는 Same-Origin Policy와 같은 정책들을 전혀 사용할 수 없는 것이죠.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;2. Origin considerations&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHLu08/btqzPm9rDIc/iOvokTX5WLPkTKqTCvufPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHLu08/btqzPm9rDIc/iOvokTX5WLPkTKqTCvufPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHLu08/btqzPm9rDIc/iOvokTX5WLPkTKqTCvufPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHLu08%2FbtqzPm9rDIc%2FiOvokTX5WLPkTKqTCvufPk%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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;앞에 1번 고려사항에서 언급했듯이, 웹 소켓은 origin 모델을 사용해서 악성 javascript로부터 보호하고 있습니다. 웹 브라우저에서 Same-Origin Policy을 통해 보호을 하고 있듯이, 웹 서버에서도 origin을 확인해야 하는 로직이 추가되어야 합니다.따라서, &lt;span style=&quot;color: #1a5490;&quot;&gt;서버에서 모르는 origin으로부터 요청이 들어온다면 &quot;HTTP 403 Forbidden&quot; 에러 상태 코드로 응답을 해서 통신을 못하도록 막아야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;3. WebSocket Client Authentication&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKgRbP/btqzOM1Qy3V/nsHOkumtGeqQ6DY9v12Byk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKgRbP/btqzOM1Qy3V/nsHOkumtGeqQ6DY9v12Byk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKgRbP/btqzOM1Qy3V/nsHOkumtGeqQ6DY9v12Byk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKgRbP%2FbtqzOM1Qy3V%2FnsHOkumtGeqQ6DY9v12Byk%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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;웹 소켓 프로토콜은 핸드셰이킹(handshaking) 단계에서 서버가 클라이언트를 인증하는 특별한 방법을 제시하지 않고 있습니다. 따라서, 클라이언트 인증이 필요한 경우 HTTP 인증이나 TLS 인증 등을 사용해야 합니다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #7e98b1;&quot;&gt;※ 참고로, TLS 인증의 경우 핸드셰이킹 과정에서 클라이언트와 서버는 서로에게 자신의 인증서(Ceritificate)를 전달하고 이를 검증함으로써, 인증을 할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;참고&lt;/h3&gt;
&lt;p&gt;Javascript WebSocket API 설명&lt;/p&gt;
&lt;p&gt;&lt;u&gt;&lt;span style=&quot;color: #7e98b1;&quot;&gt;&lt;a style=&quot;color: #7e98b1;&quot; href=&quot;https://developer.mozilla.org/ko/docs/Web/API/WebSocket&quot;&gt;https://developer.mozilla.org/ko/docs/Web/API/WebSocket&lt;/a&gt;&lt;/span&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;SockJS API 라이브러리&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #7e98b1;&quot;&gt;&lt;u&gt;&lt;a style=&quot;color: #7e98b1;&quot; href=&quot;https://mvnrepository.com/artifact/org.webjars/sockjs-client/0.3.4&quot;&gt;https://mvnrepository.com/artifact/org.webjars/sockjs-client/0.3.4&lt;/a&gt;&lt;/u&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;WebSocket 프로토콜 RFC 6455&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #7e98b1;&quot;&gt;&lt;u&gt;&lt;a style=&quot;color: #7e98b1;&quot; href=&quot;https://tools.ietf.org/html/rfc6455&quot;&gt;https://tools.ietf.org/html/rfc6455&lt;/a&gt;&lt;/u&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>understanding/communication</category>
      <category>rfc6455</category>
      <category>WebSocket</category>
      <category>웹소켓</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/185</guid>
      <comments>https://caileb.tistory.com/185#entry185comment</comments>
      <pubDate>Sun, 17 Nov 2019 23:53:56 +0900</pubDate>
    </item>
    <item>
      <title>Error 3080006: Transaction took too long</title>
      <link>https://caileb.tistory.com/184</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;에러&lt;/h2&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Error&amp;nbsp;3080006:&amp;nbsp;Transaction&amp;nbsp;took&amp;nbsp;too&amp;nbsp;long&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Error Details :&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;deadline exceeded&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Error 3080006 Transaction took too long.PNG&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MmuKm/btqzI4a7oCA/rKvaPVyo6RtJwoRbwsLZY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MmuKm/btqzI4a7oCA/rKvaPVyo6RtJwoRbwsLZY0/img.png&quot; data-alt=&quot;[ 에러 화면 ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MmuKm/btqzI4a7oCA/rKvaPVyo6RtJwoRbwsLZY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMmuKm%2FbtqzI4a7oCA%2FrKvaPVyo6RtJwoRbwsLZY0%2Fimg.png&quot; data-filename=&quot;Error 3080006 Transaction took too long.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;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ 에러 화면 ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;환경&lt;/h2&gt;
&lt;p&gt;OS : macOS Mojave&lt;/p&gt;
&lt;p&gt;IDE : EOS Studio v0.11.0 (CDT v1.6.1 / Node : Local-Single Node)&lt;/p&gt;
&lt;p&gt;Blockchain : EOS&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;상황&lt;/h2&gt;
&lt;p&gt;아래 cleos 명령을 사용해서 EOS 시스템 컨트랙트인 eosio.system 컨트랙트를 노드에 배포하려고 한다. 참고로, 아래 경로에는 컨트랙트를 빌드해서 .wasm 파일과 .abi 파일이 위치해있다. 이때 &quot;&lt;span style=&quot;color: #ee2323;&quot;&gt;Transaction took too long&lt;/span&gt;&quot;이라는 에러 메시지가 출력되면서 트랜잭션 명령이 처리가 되지 않았다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;$ cleos set contract eosio /Users/joker/.eosio/contracts_build/eosio.system/&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;원인&lt;/h2&gt;
&lt;p&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;EOS 노드는 트랜잭션을 처리하는데 필요한 최대(max) 시간을 설정해두고, 이 시간 내에 처리하도록 하고 있다. 그런데, 앞에서 실행시키려는 명령은 해당 시간 내에 처리할 수 없기 때문에 에러가 발생&lt;/span&gt;된 것이다. 현재, 위 에러는 &quot;eosio.system&quot; 컨트랙트를 배포할 때 발생했지만, 다른 어떤 특정 트랜잭션(transaction)을 실행할 때도 발생할 수 있는 에러라는 것을 알아두자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결&lt;/h2&gt;
&lt;p&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;EOS 노드가 트랜잭션을 처리할 수 있는 시간을 충분히 가질 수 있도록 하면 된다&lt;/span&gt;. 아래 처럼 몇가지 방법이 있으며, 작성자의 경우 &lt;b&gt;[solved 1]&lt;/b&gt; 방법을 사용해서 해결하였다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;[solved 1]&lt;/b&gt; &lt;span style=&quot;color: #006dd7;&quot;&gt;config.ini 파일의 &quot;--max-transaction-time&quot; 내용을 수정&lt;/span&gt;해서 노드의 트랜잭션 처리시간을 늘린다. 참고로, 특별히 설정값을 변경하지 않았다면 가장 초기값은 30으로 되어 있다. 이 값을 조금 더 여유 있게 1000으로 수정한다.&lt;/p&gt;
&lt;pre id=&quot;code_1573661911494&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;--max-transaction-time=1000&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;[solved 2]&lt;/b&gt; 노드를 수정하지 않고, 트랜잭션을 실행시킬 때 &lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;-x 옵션을 사용&lt;/span&gt;해서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;만료까지 걸리는 시간을 늘린다.&lt;/p&gt;
&lt;pre id=&quot;code_1573661974177&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ cleos set contract eosio /Users/joker/.eosio/contracts_build/eosio.system/ -x 1000&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;[solved 3]&lt;/b&gt; 시스템 계정인 &lt;span style=&quot;color: #006dd7;&quot;&gt;eosio 계정으로 직접 실행&lt;/span&gt;시킨다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1573662019488&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ cleos set contract eosio /Users/joker/.eosio/contracts_build/eosio.system/ -p eosio&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고&lt;/h2&gt;
&lt;p&gt;Stack Overflow&lt;/p&gt;
&lt;p&gt;&lt;u&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;a style=&quot;color: #9d9d9d;&quot; href=&quot;https://github.com/EOSIO/eos/issues/3150&quot;&gt;https://github.com/EOSIO/eos/issues/3150&lt;/a&gt;&lt;/span&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;EOS Developer&lt;/p&gt;
&lt;p&gt;&lt;u&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;a style=&quot;color: #9d9d9d;&quot; href=&quot;https://developers.eos.io/eosio-cleos/reference#cleos-set-contract&quot;&gt;https://developers.eos.io/eosio-cleos/reference#cleos-set-contract&lt;/a&gt;&lt;/span&gt;&lt;/u&gt;&lt;/p&gt;</description>
      <category>error/[err] blockchain</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/184</guid>
      <comments>https://caileb.tistory.com/184#entry184comment</comments>
      <pubDate>Thu, 14 Nov 2019 01:26:10 +0900</pubDate>
    </item>
    <item>
      <title>GitLab - 소스 이동 (SourceTree 사용)</title>
      <link>https://caileb.tistory.com/183</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;GitLab - 소스 이동 (SourceTree 사용)&lt;/h2&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;main.PNG&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IYPXd/btqzu7dyD7D/Iuj5dWhiMKlKEKEu4lmbjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IYPXd/btqzu7dyD7D/Iuj5dWhiMKlKEKEu4lmbjK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IYPXd/btqzu7dyD7D/Iuj5dWhiMKlKEKEu4lmbjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIYPXd%2Fbtqzu7dyD7D%2FIuj5dWhiMKlKEKEu4lmbjK%2Fimg.png&quot; data-filename=&quot;main.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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;기존에 사용 중인 GitLab 서버에서 새롭게 구축된 GitLab으로 소스를 이동시키려고 합니다. 이때 &lt;span style=&quot;color: #006dd7;&quot;&gt;소스를 이동시키는 것뿐만 아니라 GitLab에서 Commit, Push, Branch 등의 작업들을 하면서 기존의 GitLab 서버에 기록되어 있는 히스토리(History)까지 전부 이동 시킬 수 있습니다&lt;/span&gt;. 이동시키는 방법은 많지만, 만약에 작성자처럼 소스트리(SourceTree)를 사용하고 있는 중이라면 아래 방법처럼 클릭 몇번으로 간단하게 이동시킬 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래처럼 기존 사용중이던 GitLab 경로와 새롭게 만들어져서 옮겨야 하는 GitLab 경로가 있다고 생각해보겠습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;(곧 없어질) 기존 GitLab 경로&lt;/b&gt; : http://10.0.0.5:8099/joker/rank/rank-sdk&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;b&gt;(이동시킬) 새로운 GitLab 경로&lt;/b&gt; : &lt;/span&gt;http://10.0.0.100:8099/joker/rank/rank-sdk&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;포스팅에서 설명하는 방법을 적용하기 전에, &lt;span style=&quot;color: #006dd7;&quot;&gt;현재 상황은 기존에 직접 GitLab 서버를 구축하고 사용하던 도중에, 다른 GitLab 서버로 기존 코드과 기존 작업 히스토리들을 함께 이동시켜주어야 하는 상황입니다. &lt;/span&gt;만약에, 작성자와는 다르게 소스트리를 사용하지 않고 있는 상황이라면 현재 포스팅에서 소개하는 방법이 아닌 다른 방법으로 옮겨야 합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;(GitLab 페이지를 사용해서)&lt;/span&gt; 새로운 GitLab에 빈 프로젝트 생성&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;(SourceTree를 사용해서)&lt;/span&gt; 기존 GitLab에서 새로운 GitLab으로 이동&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. &lt;span style=&quot;color: #ef6f53;&quot;&gt;(GitLab 페이지를 사용해서)&lt;/span&gt; 새로운 GitLab에 빈 프로젝트 생성&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;1. 프로젝트 생성&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;gt; &quot;New Proejct&quot; 클릭&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;blob&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/boTBNr/btqztt22fTp/ta31dfj8EwxYm3SK2k53k1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boTBNr/btqztt22fTp/ta31dfj8EwxYm3SK2k53k1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boTBNr/btqztt22fTp/ta31dfj8EwxYm3SK2k53k1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FboTBNr%2Fbtqztt22fTp%2Fta31dfj8EwxYm3SK2k53k1%2Fimg.png&quot; data-filename=&quot;blob&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;2. 프로젝트 이름 &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;gt; &quot;rank-sdk&quot; 작성 &lt;span style=&quot;color: #9d9d9d;&quot;&gt;(=새로운 프로젝트 이름)&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;gt; &quot;Create Project&quot; 클릭&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;2.PNG&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4sb8i/btqzvo0iMSL/k1INEugtVYAopbvYFcu2Wk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4sb8i/btqzvo0iMSL/k1INEugtVYAopbvYFcu2Wk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4sb8i/btqzvo0iMSL/k1INEugtVYAopbvYFcu2Wk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4sb8i%2Fbtqzvo0iMSL%2Fk1INEugtVYAopbvYFcu2Wk%2Fimg.png&quot; data-filename=&quot;2.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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;3. 프로젝트 주소 복사 &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;gt; &quot;Clone&quot; 클릭&lt;/p&gt;
&lt;p&gt;&amp;gt; &quot;Clone with SSH&quot; 클릭&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;3.PNG&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czqCC0/btqzt3QGeCL/t8Jz1CpzkL6Lixk7Iaim4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czqCC0/btqzt3QGeCL/t8Jz1CpzkL6Lixk7Iaim4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czqCC0/btqzt3QGeCL/t8Jz1CpzkL6Lixk7Iaim4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FczqCC0%2Fbtqzt3QGeCL%2Ft8Jz1CpzkL6Lixk7Iaim4K%2Fimg.png&quot; data-filename=&quot;3.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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. &lt;span style=&quot;color: #ef6f53;&quot;&gt;(SourceTree를 사용해서)&lt;/span&gt; 기존 GitLab에서 새로운 GitLab으로 이동&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;1. SourceTree에서 기존 사용 중인 프로젝트 열기&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;2-1.PNG&quot; width=&quot;512&quot; height=&quot;273&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bM9ISx/btqztt9VTxl/kael3OzEtD3mhPHodGVwqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bM9ISx/btqztt9VTxl/kael3OzEtD3mhPHodGVwqk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bM9ISx/btqztt9VTxl/kael3OzEtD3mhPHodGVwqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbM9ISx%2Fbtqztt9VTxl%2Fkael3OzEtD3mhPHodGVwqk%2Fimg.png&quot; data-filename=&quot;2-1.PNG&quot; width=&quot;512&quot; height=&quot;273&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;2. SourceTree에 새로운 GitLib 프로젝트 주소 추가&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;gt; [Settings] 버튼 &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;gt; [Remotes] 탭&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;gt; (기존 GitLab 이름 변경) origin -&amp;gt; old_origin&lt;/p&gt;
&lt;p&gt;&amp;gt; (새로운 GitLab 이름 추가) origin &lt;span style=&quot;color: #9d9d9d;&quot;&gt;(이때, Path에는 앞의 &quot;Clone with SSH&quot;를 클릭해서 복사해둔 새로운 GitLab 주소를 붙여넣기한다.)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;gt; [OK] 버튼&lt;/p&gt;
&lt;p&gt;&amp;gt; [Push] 탭&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;※ 기존 GitLab 이름을 origin에서 old_origin으로 변경하는 이유는 나중에 기존의 GitLab에 다시 접속할 수는 상황이 발생할 수도 있기 때문에 백업용으로 적어두는 것입니다. 만약에, 새로운 GitLab으로 연결이 잘 된 후 기존 GitLab에 접속할 일이 없다면 삭제(Remove)해도 상관없습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;2-2.PNG&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bi9n3c/btqzs6N76Ai/uTrskuiJMcKyyPM2gSdPTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bi9n3c/btqzs6N76Ai/uTrskuiJMcKyyPM2gSdPTk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bi9n3c/btqzs6N76Ai/uTrskuiJMcKyyPM2gSdPTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbi9n3c%2Fbtqzs6N76Ai%2FuTrskuiJMcKyyPM2gSdPTk%2Fimg.png&quot; data-filename=&quot;2-2.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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;3. 새로운 GitLab에 소스 임포트&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;gt; &quot;Push to repository&quot; : origin&lt;/p&gt;
&lt;p&gt;&amp;gt; develop, master 체크&lt;/p&gt;
&lt;p&gt;&amp;gt; OK&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;※ delveop과 master 모두 체크하는 이유는 기존 gitlab에서 두 브런치의 코드 모두 이동시킴으로써, 새로운 GitLab 서버에는 최신코드를 유지시켜줄 수 있기 때문입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;2-3.PNG&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWOZrU/btqzvozmHVc/Anv3CO7AbQa8aKUmnsxdn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWOZrU/btqzvozmHVc/Anv3CO7AbQa8aKUmnsxdn1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWOZrU/btqzvozmHVc/Anv3CO7AbQa8aKUmnsxdn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWOZrU%2FbtqzvozmHVc%2FAnv3CO7AbQa8aKUmnsxdn1%2Fimg.png&quot; data-filename=&quot;2-3.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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;4. 새로운 GitLab에 추가된 것 확인&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;2-4.png&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEK4Ps/btqzu7R9zWm/Pt65wcMUpLsOFAi2lvqUXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEK4Ps/btqzu7R9zWm/Pt65wcMUpLsOFAi2lvqUXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEK4Ps/btqzu7R9zWm/Pt65wcMUpLsOFAi2lvqUXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEK4Ps%2Fbtqzu7R9zWm%2FPt65wcMUpLsOFAi2lvqUXK%2Fimg.png&quot; data-filename=&quot;2-4.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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>environment/version</category>
      <category>git</category>
      <category>gitlab</category>
      <category>SourceTree</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/183</guid>
      <comments>https://caileb.tistory.com/183#entry183comment</comments>
      <pubDate>Sat, 2 Nov 2019 13:57:03 +0900</pubDate>
    </item>
    <item>
      <title>Xcode - 입력 arguments 넣는 방법</title>
      <link>https://caileb.tistory.com/182</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;입력 arguments 넣는방법&lt;/h2&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;how to give command line arguments in Xcode&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;프로그램을 실행할 때 특정 값들을 함께 넣어주고 싶은 경우가 있습니다. 예를 들면, 특정 옵션값(ex, -g, -i, -e 등) 처럼 말이죠. 특히 IDE를 사용해서 디버깅을 할 때 필요할 때가 종종 있을 것입니다. &lt;span style=&quot;color: #006dd7;&quot;&gt;IDE를 Xcode로 사용하는 경우 이 입력값들을 설정&lt;/span&gt;할 수 있는 곳이 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;방법&lt;/h2&gt;
&lt;p&gt;[Product]&amp;nbsp;&amp;gt;&amp;nbsp;&lt;span style=&quot;color: #006dd7;&quot;&gt;Edit&amp;nbsp;Scheme&amp;hellip;&lt;/span&gt;&amp;nbsp;클릭 &lt;br /&gt;&amp;nbsp;&amp;gt; &lt;span style=&quot;color: #006dd7;&quot;&gt;Run&amp;nbsp;&lt;/span&gt; &lt;br /&gt;&amp;nbsp;&amp;gt; &lt;span style=&quot;color: #006dd7;&quot;&gt;Arguments&amp;nbsp;&lt;/span&gt; &lt;br /&gt;&amp;nbsp;&amp;gt; &lt;span style=&quot;color: #006dd7;&quot;&gt;Arguments Passed On Launch&lt;/span&gt; 체크 &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;- &lt;span style=&quot;color: #006dd7;&quot;&gt;[+]&lt;/span&gt; 를 눌러서 원하는 입력 파라미터들을 하나씩 추가시킵니다.&lt;br /&gt;&amp;nbsp;&amp;gt; &lt;span style=&quot;color: #006dd7;&quot;&gt;타이틀 바(Title Bar) &amp;gt; [▶] 버튼 혹은 Product &amp;gt; Run&lt;/span&gt;으로 프로그램을 실행&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Pasted Graphic 11.png&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cv1iAq/btqzhJZaqg4/w6k8SdWJI6YKzyKEMYdcM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cv1iAq/btqzhJZaqg4/w6k8SdWJI6YKzyKEMYdcM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cv1iAq/btqzhJZaqg4/w6k8SdWJI6YKzyKEMYdcM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcv1iAq%2FbtqzhJZaqg4%2Fw6k8SdWJI6YKzyKEMYdcM0%2Fimg.png&quot; data-filename=&quot;Pasted Graphic 11.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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;※ 위 사진은 프로그램을 실행할 때 총 7개의 arguements를 넣은 것입니다. &quot;-e&quot;, &quot;-i&quot;, &quot;hello&quot;, &quot;--join&quot;, &quot;joker&quot;, &quot;--join&quot;, &quot;batman&quot;으로 넣은 것입니다. 위 &lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;사진 처럼, 한칸에 값을 하나씩 적을 수도 있고 옵션값과 함께 특정 값을 띄어쓰기(space)를 사용해도 상관없습니다. 위처럼 arguements를 설정한 후 실행한다면 명령어(command line)으로 프로그램을 아래처럼 실행한 것과 동일한 것입니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1571926241359&quot; class=&quot;c++ cpp&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ odo -e -i hello --join joker --join batman&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고&lt;/h2&gt;
&lt;p&gt;개발 중에 &quot;&lt;span style=&quot;color: #006dd7;&quot;&gt;Arguments Passed On Launch&lt;/span&gt;&quot; 에 입력된 데이터들은 프로그램이 실행될 때 가장 먼저 호출되는 main 함수를 통해 입력되는 값이기 때문에, 직접 확인할 수도 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1571925927625&quot; class=&quot;c++ cpp&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;main(int argc, char** argv) {
	&amp;hellip;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;argc&lt;/span&gt; : 입력 파라미터의 개수 (=argument count)&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;argv&lt;/span&gt; : 입력 파라미터의 값 (=argument value)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;실행 프로그램 이름이 첫번째 인자로 항상 들어가기 때문에, 입력 파라미터(Arguments)를 아무것도 추가하지 않아도 argc는 항상 1이고, argv[0]은 항상 프로그램이름 값입니다. 만약에 앞의 그림처럼 입력 파리미터가 총 7개라면 프로그램 이름 1개를 포함해서, argc는 총 8이 될 것이고, 입력 파라미터의 가장 첫번째 값은 argv[1]부터 조회가 가능합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>environment/IDE</category>
      <category>arguments</category>
      <category>Command line</category>
      <category>Xcode</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/182</guid>
      <comments>https://caileb.tistory.com/182#entry182comment</comments>
      <pubDate>Thu, 24 Oct 2019 23:16:43 +0900</pubDate>
    </item>
    <item>
      <title>Transaction exceeded the current CPU usage limit</title>
      <link>https://caileb.tistory.com/181</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;에러&lt;/h2&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Transaction exceeded the current CPU usage limit imposed on the transaction billed CPU time (1045 us) is greater than the maximum billable CPU time for the transaction (608 us)&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1571923924478&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;error 2019-10-24T04:33:15.566 thread-0  http_plugin.cpp:649 handle_exception] 
FC Exception encountered while processing chain.push_transaction 
debug 2019-10-24T04:33:15.566 thread-0  http_plugin.cpp:650 handle_exception]  
Exception Details: 3080004 tx_cpu_usage_exceeded: Transaction exceeded the current CPU usage limit imposed on the transaction billed CPU time (1045 us) is greater than the maximum billable CPU time for the transaction (608 us) 
{&quot;billed&quot;:1045,&quot;billable&quot;:608} 
thread-0  transaction_context.cpp:519 validate_cpu_usage_to_bill&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c3gf2O/btqzjfJAlUN/GsGHhbcNxEdk9YyKq3bFQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c3gf2O/btqzjfJAlUN/GsGHhbcNxEdk9YyKq3bFQ0/img.png&quot; data-alt=&quot;[ 에러 화면 ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c3gf2O/btqzjfJAlUN/GsGHhbcNxEdk9YyKq3bFQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc3gf2O%2FbtqzjfJAlUN%2FGsGHhbcNxEdk9YyKq3bFQ0%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;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ 에러 화면 ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;환경&lt;/h2&gt;
&lt;p&gt;OS : macOS Mojave &lt;br /&gt;IDE : EOS Studio v0.11.0 (CDT v1.6.1 / Node : Local-Single Node)&lt;/p&gt;
&lt;p&gt;Blockchain : EOS&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;상황&lt;/h2&gt;
&lt;p&gt;현재, EOS 블록체인 내에 사용자A 계정이 사용가능한 cpu 시간은 (available) 136 us이고, network 대역폭은 (available) 23.36 MiB이다. (아래 사진을 참고) 이때, &lt;span style=&quot;color: #006dd7;&quot;&gt;사용자A가 어떤 컨트랙트(contracT)의 특정 액션(action)을 수행하려고할 때 위와 같은 에러가 발생&lt;/span&gt;했다. 참고로 수행하려는 액션은 토큰 컨트랙트를 사용해서 사용자A 계정의 토큰 일부를 사용자B 계정에게 옮기려고 시도한 것이며, 이때, 에러가 발생했으며 옮기려고 시도한 토큰은 전달되지 않았다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;※ 참고, 아래 사진에서 사용자 A 계정의 리소스(cpu, net)는 직접 스테이킹(stacking)한 것이 아닌 작성자가 보유하고 있는 타 계정으로부터 위임(delegate)받아서 만들어진 것이기 때문에, 리소스가 staked가 아닌 delegated에 출력되고 있는 것이다. 만약 직접 리소스를 위해 스테이킹을 했다면 delegate 외에 stake라는 필드가 하나 더 출력될 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Pasted Graphic 10.png&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MOZQx/btqzjfbKbsK/Aq4HFcnZJ4DsU5JVtXkkz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MOZQx/btqzjfbKbsK/Aq4HFcnZJ4DsU5JVtXkkz1/img.png&quot; data-alt=&quot;[ 사용자 A 계정의 리소스 상태 ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MOZQx/btqzjfbKbsK/Aq4HFcnZJ4DsU5JVtXkkz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMOZQx%2FbtqzjfbKbsK%2FAq4HFcnZJ4DsU5JVtXkkz1%2Fimg.png&quot; data-filename=&quot;Pasted Graphic 10.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;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ 사용자 A 계정의 리소스 상태 ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;원인&lt;/h2&gt;
&lt;p&gt;사용자 A 계정이 현재 시도하려고 하는 액션이 필요한 cpu는 1045 us이다. 그런데, 사용자 A 계정이 사용가능한 cpu는 136 us이다. 사용자 &lt;span style=&quot;color: #ee2323;&quot;&gt;A 계정이 사용할 수 있는 크기 이상의 액션을 실행하려고 시도했기 때문에 발생&lt;/span&gt;한 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결&lt;/h2&gt;
&lt;p&gt;해결하기 위한 방법은 여러가지가 있을 수 있다. 아래 3가지 방법을 제시했지만, 상황에 따라 소개하지 않은 다른 방법으로 해결할 수도 있다. 현재 테스트 중인 환경이기 때문에 아래의 [방법2]처럼 스테이킹을 추가해서 위 에러가 나오지 않도록 하였다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;방법1.&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;cpu 시간은 다시 되돌아오기 때문에 &lt;span style=&quot;color: #006dd7;&quot;&gt;일정시간이 지난 후, 사용할 수 있는 cpu가 줄어들면 트랜잭션을 재시도&lt;/span&gt;한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;방법2.&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;cpu를 추가로 스테이킹(stacking)&lt;/span&gt;해서, 사용할 수 있는 cpu양을 늘린다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;방법3.&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;사용할 수 있는 &lt;span style=&quot;color: #006dd7;&quot;&gt;cpu 보다 낮은 트랜잭션&lt;/span&gt;을 만들어서 재시도한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고&lt;/h2&gt;
&lt;p&gt;(Stack&amp;nbsp;Overflow)&amp;nbsp;원인 &lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;u&gt;&lt;i&gt;&lt;a style=&quot;color: #9d9d9d;&quot; href=&quot;https://github.com/EOSIO/eos/issues/6347%20&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/EOSIO/eos/issues/6347 &lt;/a&gt;&lt;/i&gt;&lt;/u&gt;&lt;/span&gt;&lt;br /&gt;(Stack&amp;nbsp;Overflow)&amp;nbsp;해결방법 &lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;u&gt;&lt;i&gt;&lt;a style=&quot;color: #9d9d9d;&quot; href=&quot;https://eosio.stackexchange.com/questions/1609/how-to-increase-billable-cpu-time&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://eosio.stackexchange.com/questions/1609/how-to-increase-billable-cpu-time&lt;/a&gt;&lt;/i&gt;&lt;/u&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>error/[err] blockchain</category>
      <category>CPU</category>
      <category>EOS</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/181</guid>
      <comments>https://caileb.tistory.com/181#entry181comment</comments>
      <pubDate>Thu, 24 Oct 2019 22:46:24 +0900</pubDate>
    </item>
    <item>
      <title>이더리움 토큰 컨트랙트 이벤트 로그 추적하기</title>
      <link>https://caileb.tistory.com/180</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;이더리움 토큰 컨트랙트 이벤트 로그 추적하기&lt;/h2&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;event.PNG&quot; width=&quot;575&quot; height=&quot;281&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsHnvX/btqzblXUOgm/2unPtEGGO7yKv51vZZ8C80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsHnvX/btqzblXUOgm/2unPtEGGO7yKv51vZZ8C80/img.png&quot; data-alt=&quot;Contract &amp;amp;amp;amp; Event&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsHnvX/btqzblXUOgm/2unPtEGGO7yKv51vZZ8C80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsHnvX%2FbtqzblXUOgm%2F2unPtEGGO7yKv51vZZ8C80%2Fimg.png&quot; data-filename=&quot;event.PNG&quot; width=&quot;575&quot; height=&quot;281&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Contract &amp;amp; Event&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;본 포스팅은 이더리움(ethereum) 블록체인의 컨트랙트(contract)에서 발생하는 이벤트(event)를 보고, 해당 컨트랙트를 통해 발생된 이벤트 로그들을 보고 특정 행동을 했는지를 확인해보려고 합니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;참고로,&amp;nbsp;&lt;b&gt;컨트랙트(contract)&lt;/b&gt;란, 개발자들이 블록체인에 계약서의 내용을 프로그래밍으로 작성해서 올려둔 것을 말합니다. 이 프로그래밍된 계약서의 함수가 실행되면 반드시 지켜지게 됩니다. &lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;함수(function or method)&lt;/b&gt;란, 컨트랙트에서 호출되는 함수를 말합니다. 컨트랙트마다 함수는 다를 수 있습니다. 토큰 컨트랙트라면 transfer나 lock 같은 토큰을 전송하거나 잠그는 함수들이 있을 것이고, 게임 컨트랙트라면 attack이나 defence 등의 캐릭터를 제어하는 함수들이 있을 것입니다. 물론, 함수 이름은 개발자 마음이기 때문에 이름도 제각각 일 것입니다. &lt;/span&gt;&lt;b&gt;이벤트(event)&lt;/b&gt;란, 개발자들이 컨트랙트를 개발할 때 함수들이 호출될 때 블록에 해당 기록을 남김으로써, 나중에 보기 편하게 기록을 찾아볼 수 있도록 삽입하는 것을 말합니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;목적&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;이더리움 블록체인 플랫폼에 이미 배포(deploy)된 APIS 토큰 컨트랙트를 통해 이미 처리된 행동(=함수)들 중에서 지갑(wallet)을 잠그는 행위를 했을 때 발생되는 특정 이벤트(=여기서는 &quot;&lt;span style=&quot;color: #ef5369;&quot;&gt;Locked&lt;/span&gt;&quot;라는 이름을 가진 이벤트)의 기록을 살펴볼 것입니다. 그리고 이 기록에 어떤 내용들이 기록되어 있는지 확인해보겠습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;APIS 토큰 컨트랙트의 내용을 살펴본 결과, 지갑을 잠그는 함수인 walletLock이 호출될 때, 정상적으로 처리되는 경우 &lt;span style=&quot;color: #ef5369;&quot;&gt;Locked&lt;/span&gt;라는 이벤트가 발생되도록 프로그래밍되어 있었습니다. 따라서, &lt;span style=&quot;color: #ef5369;&quot;&gt;Locked&lt;/span&gt;라는 이벤트 기록을 보면 잠겨진 지갑이나 잠긴 기간 등의 몇가지 정보를 볼 수 있겠죠.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;추적 대상&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;span&gt;이더리움 (&lt;/span&gt;아피스&lt;span&gt;) APIS &lt;/span&gt;토큰 컨트랙트&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;추적 하기&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;컨트랙트에서 발생된 내용을 추적하는 방법을 단계별로 나누었습니다. &lt;span style=&quot;color: #1a5490;&quot;&gt;[Step1]&lt;/span&gt; 가장 먼저 추적할 컨트랙트 주소를 가지고 있어야 합니다. 자신이 살펴볼 컨트랙트 주소를 가지고 있다면, &lt;span style=&quot;color: #1a5490;&quot;&gt;[Step2]&lt;/span&gt; 해당 컨트랙트가 어떤 내용으로 이루어졌는지 내용을 확인할 필요가 있습니다. 참고로, 이때 컨트랙트 내용이 solidity라는 프로그래밍 언어로 작성되어 있기 때문에 개발자가 아닌 일반 사용자는 보기 불편할 수도 있습니다. 이땐 컴퓨터 전공자에게 도움을 요청하면 어느정도 해석이 가능할 것이라고 생각됩니다. &lt;span style=&quot;color: #1a5490;&quot;&gt;[Step3]&lt;/span&gt; 컨트랙트의 행동을 볼 때는, 추적하고 싶은 이벤트에 대한 주소가 필요합니다. 이 주소를 검색하면 해당 이벤트가 발생됬던 모든 기록(history)를 볼 수 있기 때문이죠. 마지막으로, &lt;span style=&quot;color: #1a5490;&quot;&gt;[Step4]&lt;/span&gt; 검색된 이벤트 기록들 중에 원하는 이벤트를 찾아서 해당 기록을 살펴보면 됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;[Step1]&lt;/span&gt; 토큰 컨트랙트 주소 획득&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;[Step2]&lt;/span&gt; 토큰 컨트랙트 내용 확인&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;[Step3]&lt;/span&gt; 토큰 컨트랙트 내의 이벤트 주소 획득&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;[Step4]&lt;/span&gt; 토큰 컨트랙트 내의 이벤트 정보 확인&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;Step1. &lt;span style=&quot;color: #f89009;&quot;&gt;APIS 컨트랙트 주소 획득&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span&gt;0x4C0fBE1BB46612915E7967d2C3213cd4d87257AD&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;※ 작성자는 포스팅 설명을 위해 찾아본 토큰 중 하나입니다. 위 주소가 아닌 현재 실습에 사용하고 싶은 다른 컨트랙트 주소를 가지고 있다면 해당 주소를 사용하면 됩니다. 단, 본 포스팅은 APIS 토큰 컨트랙트를 기준으로 작성되었기 때문에, 다른 컨트랙트를 대상으로 할 경우 폰 포스팅에서 설명하는 방법과 조금 다를 수 있습니다. 컨트랙트마다 내용이 모두 다르기 때문이죠. 따라서, 찾아보려는 컨트랙트에서 발생한 특정 이벤트들을 보기 위해, 현재 포스팅처럼 접근하는 방식만 참고하길 바랍니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;Step2.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;APIS 컨트랙트 내용 확인&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;&lt;span&gt;1. Etherscan &lt;/span&gt;사이트 접속&lt;/b&gt; &lt;span&gt;(=&lt;/span&gt;이더리움 블록체인 내의 발생 정보 제공&lt;span&gt;) &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;a href=&quot;https://etherscan.io/&quot;&gt;https://etherscan.io/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span&gt;2. APIS &lt;/span&gt;컨트랙트 조회&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Search : 0x4C0fBE1BB46612915E7967d2C3213cd4d87257AD&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span&gt;3. APIS &lt;/span&gt;컨트랙트 내용 확인&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;[Contract] &lt;/span&gt;탭&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Contract Source Code : &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;개발자가 작성한 컨트랙트 코드 내용 (&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;solidity&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;언어로 출력)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Contract ABI : &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;컨트랙트&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; interface &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;내용&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Contract Creation Code : &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;컨트랙트 코드 내용 (&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Bytecode로 출력)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Contract Creation Code : &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;컨트랙트 코드 내용 (&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Opcode로 출력)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span&gt;4. APIS &lt;/span&gt;컨트랙트 내용 중 &lt;span&gt;Locked &lt;/span&gt;이벤트 내용 확인&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;4-1. &amp;ldquo;&lt;span style=&quot;color: #ef5369;&quot;&gt;Locked&lt;/span&gt;&amp;rdquo; &lt;/span&gt;이벤트 확인&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Contract Source Code &lt;/span&gt;에서 &lt;span&gt;&amp;ldquo;event Locked&amp;rdquo; &lt;/span&gt;검색&lt;/p&gt;
&lt;pre id=&quot;code_1571541577719&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
* @dev 토큰 지갑의 송금/입금 기능이 제한되었을 때 발생하는 이벤트
* @param target 제한 대상 지갑 주소
* @param timeLockUpEnd 제한이 종료되는 시간(UnixTimestamp)
* @param sendLock 지갑에서의 송금을 제한하는지 여부(true : 제한, false : 해제)
* @param receiveLock 지갑으로의 입금을 제한하는지 여부 (true : 제한, false : 해제)
*/
event Locked (address indexed target, uint timeLockUpEnd, bool sendLock, bool receiveLock);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;4-2. &amp;ldquo;&lt;span style=&quot;color: #ef5369;&quot;&gt;Locked&lt;/span&gt;&amp;rdquo; &lt;/span&gt;이벤트 호출지점 확인&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Contract Source Code &lt;/span&gt;에서 &lt;span&gt;&amp;ldquo;Locked&amp;rdquo; &lt;/span&gt;검색&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1571541608694&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
* @dev 지갑을 지정된 시간까지 제한시키거나 해제시킨다. 제한 시간이 경과하면 모든 제한이 해제된다.
* @param _targetWallet 제한을 적용할 지갑 주소
* @param _timeLockEnd 제한이 종료되는 시간(UnixTimestamp)
* @param _sendLock (true : 지갑에서 토큰을 출금하는 기능을 제한한다.) (false : 제한을 해제한다)
* @param _receiveLock (true : 지갑으로 토큰을 입금받는 기능을 제한한다.) (false : 제한을 해제한다)
*/
function walletLock(address _targetWallet, uint _timeLockEnd, bool _sendLock, bool _receiveLock) onlyOwner public {
	require(_targetWallet != 0x0);
        
	// If all locks are unlocked, set the _timeLockEnd to zero.
	if(_sendLock == false &amp;amp;&amp;amp; _receiveLock == false) {
		_timeLockEnd = 0;
	}
        
	lockedWalletInfo[_targetWallet].timeLockUpEnd = _timeLockEnd;
	lockedWalletInfo[_targetWallet].sendLock = _sendLock;
	lockedWalletInfo[_targetWallet].receiveLock = _receiveLock;
        
	if(_timeLockEnd &amp;gt; 0) {
		Locked(_targetWallet, _timeLockEnd, _sendLock, _receiveLock);
	} else {
		Unlocked(_targetWallet);
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;등록된 &lt;span&gt;APIS &lt;/span&gt;컨트랙트에는 한글로 설명이 아주 잘 되있었기 때문에 컨트랙트 내용을 살펴보는데 어렵지 않았던 것 같습니다.&lt;span&gt; 하지만, &lt;/span&gt;컨트랙트마다 작성된 개발자가 다르기 때문에 설명이 불충분할 수도 있습니다. 만약에&lt;span&gt;, APIS &lt;/span&gt;컨트랙트가 아닌 다른 컨트랙트를 대상으로 살펴볼 때&lt;span&gt;, 개발자가 작성해 놓은 &lt;/span&gt;설명으로 내용을 파악하기 힘들다면&lt;span&gt;, &lt;/span&gt;직접 코드를 보고 해석할 필요가 있을 수도 있습니다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;이제&lt;span&gt;, &lt;/span&gt;위 &lt;span style=&quot;color: #1a5490;&quot;&gt;[4-1]&lt;/span&gt;을 통해 &lt;span&gt;APIS &lt;/span&gt;컨트랙트에서 &lt;span&gt;&amp;ldquo;&lt;span style=&quot;color: #ef5369;&quot;&gt;Locked&lt;/span&gt;&amp;rdquo; &lt;/span&gt;이벤트가 발생할 때 어떤 정보들이 로그에 나오는지 찾았고&lt;span&gt;, &lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;[4-2]&lt;/span&gt;&lt;/span&gt;를 통해 &lt;span&gt;&amp;ldquo;&lt;span style=&quot;color: #ef5369;&quot;&gt;Locked&lt;/span&gt;&amp;rdquo; &lt;/span&gt;이벤트가 컨트랙트에서 어떤 처리할 때 발생되는지 찾아냈고 이 정보들을 바탕으로 발생된 이벤트 로그들을 살펴보겠습니다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;[4-1] &lt;/span&gt;에서 알 수 있는 내용 &lt;span&gt;(=&quot;&lt;span style=&quot;color: #ef5369;&quot;&gt;Locked&lt;/span&gt;&quot; &lt;/span&gt;이벤트에 출력되는 내용&lt;span&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #1b711d;&quot;&gt;target&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;잠근&lt;span&gt;(Locked) &lt;/span&gt;지갑에 대한 주소&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #1b711d;&quot;&gt;timeLockUpEnd&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; : &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;잠금 해제가 종료되는 시간&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #1b711d;&quot;&gt;sendLock&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; : &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;잠글 때&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;해당 지갑에서 돈 인출 기능을 제한할지에 대한 설정여부&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #1b711d;&quot;&gt;receiveLock&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; : &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;잠글 때&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;해당 지갑에 돈 송금 기능을 제한할지에 대한 설정여부&lt;/span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;[4-2] &lt;/span&gt;에서 알 수 있는 내용&lt;span&gt; (=&quot;&lt;span style=&quot;color: #ef5369;&quot;&gt;Locked&lt;/span&gt;&quot; &lt;/span&gt;이벤트가 발생되는 시점&lt;span&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;walletLock&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;지갑을 잠글 때&lt;span&gt;, &lt;/span&gt;잠금 해제 시간이 유효하다면 &lt;span&gt;Locked &lt;/span&gt;이벤트 호출&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;※ Contract Source Code는 컨트랙트 코드가 출력되는데, 이더리움 블록체인 내의 컨트랙트는 이곳에 작성된 내용대로 동작하게 됩니다. 따라서, 원하는 이벤트를 확인하고 싶다면 이곳에서 발생가능한 이벤트들을 코드에서 찾아야 합니다. Contract Source Code 안에서 찾기 단축키인 &amp;ldquo;Ctrl+F&amp;rdquo; 눌러서 &amp;ldquo;event&amp;rdquo;를 찾아보면 모두 볼 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;참고로, Contract Source Code에 출력되는 코드가 블록체인 내에 그대로 저장되는 것이 아닙니다. 사람(=프로그램 개발자)가 읽기 쉬운 형태(=solidity)가 아닌, 읽기 어려운 형태(=opcode)로 변경되서 저장됩니다. 따라서, Contract Creation Code(Opcode)를 통해 원하는 이벤트를 찾아낼 수 있지만 쉽지 않습니다. 그래서 본 포스팅에서는 이벤트 주소를 이 코드 안에서 찾아내지 않고, 컨트랙트 내에서 포함된 이벤트 주소들까지 알려주는 사이트인 Bloxy를 사용해서 알아낼 것입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;Step3.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;APIS 컨트랙트 내의 이벤트 주소 획득&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;&lt;span&gt;1. Bloxy &lt;/span&gt;사이트 접속&lt;/b&gt; &lt;span&gt;(&lt;/span&gt;이더리움 블록체인 내의 발생 정보 제공&lt;span&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;a href=&quot;https://bloxy.info/&quot;&gt;https://bloxy.info/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span&gt;2. APIS &lt;/span&gt;컨트랙트 조회&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Search : 0x4C0fBE1BB46612915E7967d2C3213cd4d87257AD&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span&gt;3. APIS &lt;/span&gt;컨트랙트 이벤트 주소 획득&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;[Smart Contract Events] &lt;/span&gt;탭&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;※ [Smart Contract Event] 탭에 표시된 이벤트 목록들은 APIS 컨트랙트에서 발생시킨 이벤트 이름과 발생 횟수에 대한 정보입니다. 여기서 원하는 이벤트를 클릭하서 이벤트 주소(=Event Hash)를 가져옵니다. 아래는 Bloxy에서 제공된 APIS 컨트랙트 이벤트들에 대한 주소 정보입니다.&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.4884%;&quot;&gt;&lt;b&gt;&lt;span&gt;Event name&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 86.5116%;&quot;&gt;&lt;b&gt;&lt;span&gt;Event Hash&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.4884%;&quot;&gt;&lt;span&gt;Transfer&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 86.5116%;&quot;&gt;&lt;span&gt;0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.4884%;&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;Locked&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 86.5116%;&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;0x9a26d7aa8d0b0885c3a5cce1b95072a26bc415306c652f20e7b507c07a3914d5&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.4884%;&quot;&gt;&lt;span&gt;RejectedPay..&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 86.5116%;&quot;&gt;&lt;span&gt;0x77c483e4ff749a73be4f6404c2ecea502a159772ef1cd46e475516a4cdf0eb0d&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.4884%;&quot;&gt;&lt;span&gt;Approval&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 86.5116%;&quot;&gt;&lt;span&gt;0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.4884%;&quot;&gt;&lt;span&gt;Unlock&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 86.5116%;&quot;&gt;&lt;span&gt;0x7e6adfec7e3f286831a0200a754127c171a2da564078722cb97704741bbdb0ea&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.4884%;&quot;&gt;&lt;span&gt;Burn&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 86.5116%;&quot;&gt;&lt;span&gt;0xcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca5&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;Step4.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;APIS 컨트랙트 내의 이벤트 로그 확인&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;&lt;span&gt;1. Etherscan &lt;/span&gt;사이트 접속&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;a href=&quot;https://etherscan.io/&quot;&gt;https://etherscan.io/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span&gt;2. APIS &lt;/span&gt;컨트랙트 조회&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Search : 0x4C0fBE1BB46612915E7967d2C3213cd4d87257AD&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span&gt;3. APIS &lt;/span&gt;컨트랙트 이벤트&lt;span&gt;(=&lt;/span&gt;토픽&lt;span&gt;) &lt;/span&gt;로그 검색&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;[Events] &lt;/span&gt;탭&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Find : &lt;span style=&quot;color: #ef5369;&quot;&gt;0x9a26d7aa8d0b0885c3a5cce1b95072a26bc415306c652f20e7b507c07a3914d5&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;이벤트 로그들 중에 확인이 필요한 &lt;span&gt;&amp;ldquo;&lt;span style=&quot;color: #ef5369;&quot;&gt;Locked&lt;/span&gt;&amp;rdquo; &lt;/span&gt;이벤트&lt;span&gt;(=&lt;/span&gt;위 처럼 &lt;span&gt;Locked &lt;/span&gt;이벤트에 해당하는 &lt;span&gt;16&lt;/span&gt;진수 주소값&lt;span&gt;)&lt;/span&gt;를 검색하면&lt;span&gt;, &lt;/span&gt;해당 이벤트가 발생했을 때의 로그들이 모두 출력됩니다&lt;span&gt;. &lt;/span&gt;출력되는 내용들이 어떤 의미를 가지는지 살펴보겠습니다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 20px;&quot; border=&quot;1&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 10px; text-align: left;&quot;&gt;&lt;b&gt;&lt;span&gt;Txn Hash&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 10px; text-align: left;&quot;&gt;&lt;b&gt;&lt;span&gt;Method&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 10px; text-align: left;&quot;&gt;&lt;b&gt;&lt;span&gt;Event Logs&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 10px;&quot;&gt;
&lt;p&gt;트랜잭션 해시&lt;/p&gt;
&lt;span&gt;트랜잭션이 담긴 블록 번호&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 10px;&quot;&gt;
&lt;p&gt;함수 주소&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;span&gt;함수 이름&lt;span&gt; (&lt;/span&gt;정의된 내용&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 10px;&quot;&gt;
&lt;p&gt;이벤트 이름 &lt;span&gt;(&lt;/span&gt;정의된 내용&lt;span&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;span&gt;이벤트 입력값 내용&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;해당 컨트랙트를 통해서 어떤 일들이 발생했는지는 위&lt;span&gt; 3&lt;/span&gt;가지 정보들을 기반으로 살펴볼 수 있습니다&lt;span&gt;. &amp;ldquo;&lt;span style=&quot;color: #ef5369;&quot;&gt;Locked&lt;/span&gt;&amp;rdquo; &lt;/span&gt;이벤트에 대해서 검색했기 때문에&lt;span&gt;, &lt;/span&gt;해당 이벤트에 대한 목록들이 모두 출력되는 것을 볼 수 있습니다&lt;span&gt;. &lt;/span&gt;참고로&lt;span&gt;, Etherscan&lt;/span&gt;에서는 일어난 모든 이벤트는 볼 수 없고&lt;span&gt;, &lt;/span&gt;발생했던 이벤트들 중에 최근것들에 대해서만 볼 수 있도록 제공하고 있습니다&lt;span&gt;. &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span&gt;4. APIS &lt;/span&gt;컨트랙트 이벤트&lt;span&gt;(=&lt;/span&gt;토픽&lt;span&gt;) &lt;/span&gt;로그 분석&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;아래 사진은 &lt;span&gt;Etherscan&lt;/span&gt;에서 &lt;span&gt;&amp;ldquo;&lt;span style=&quot;color: #ef5369;&quot;&gt;Locked&lt;/span&gt;&amp;rdquo; &lt;/span&gt;이벤트의 로그 중 가장 마지막으로 제공된 내용을 캡쳐한 것입니다&lt;span&gt;. (&lt;/span&gt;참고&lt;span&gt;, Etherscan&lt;/span&gt;에서 제공된 데이터 중 가장 마지막 로그일 뿐 &lt;span&gt;APIS&lt;/span&gt;를 통해 처리된 로그 중 가장 마지막은 아닙니다&lt;span&gt;.)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;event2.png&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BsieW/btqzb8p2Hwi/2eQ3G9zkHHk8ETCLDmYTP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BsieW/btqzb8p2Hwi/2eQ3G9zkHHk8ETCLDmYTP1/img.png&quot; data-alt=&quot;Locked 이벤트 로그 중 하나&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BsieW/btqzb8p2Hwi/2eQ3G9zkHHk8ETCLDmYTP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBsieW%2Fbtqzb8p2Hwi%2F2eQ3G9zkHHk8ETCLDmYTP1%2Fimg.png&quot; data-filename=&quot;event2.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;/&gt;&lt;/span&gt;&lt;figcaption&gt;Locked 이벤트 로그 중 하나&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래 표는 위 캡쳐된 정보를 살펴보면 알 수 있는 내용들을 정리한 것입니다&lt;span&gt;. &lt;/span&gt;앞에서 언급했던 것처럼 아래 표에서 &lt;span&gt;Event Log &lt;/span&gt;기록을 보면&lt;span&gt;, APIS &lt;/span&gt;컨트랙트의 지갑을 잠글 때 사용되는 &lt;span&gt;&amp;ldquo;walletLock&amp;rdquo; &lt;/span&gt;함수가 처리될 때 발생된 &lt;span&gt;&amp;ldquo;&lt;span style=&quot;color: #ef5369;&quot;&gt;Locked&lt;/span&gt;&amp;rdquo; &lt;/span&gt;이벤트 정보를 통해 어떤 값들이 사용되었는지 볼 수 있습니다&lt;span&gt;. &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;잠겨진 지갑의 주소&lt;span&gt;(=&lt;span style=&quot;color: #1b711d;&quot;&gt;target&lt;/span&gt;)&lt;/span&gt;는 &lt;span&gt;0x8c133a2144b66ac06a12fe5c1a88c460f4dce4aa&lt;/span&gt;이고&lt;span&gt;, &lt;/span&gt;잠김 해제시간&lt;span&gt;(&lt;span style=&quot;color: #1b711d;&quot;&gt;timeLockUpEnd&lt;/span&gt;)&lt;/span&gt;은 &lt;span&gt;1534921200&lt;/span&gt;이고&lt;span&gt;, &lt;/span&gt;잠긴 지갑의 돈 인출 제한여부(=&lt;span style=&quot;color: #1b711d;&quot;&gt;sendLock&lt;/span&gt;)는 &lt;span&gt;1(=true)&lt;/span&gt;이기 때문에 인출 불가능하다는 것이고&lt;span&gt;, &lt;/span&gt;돈을 더 넣을 수 있는지 송금 여부(=&lt;span style=&quot;color: #1b711d;&quot;&gt;receiveLock&lt;/span&gt;)는 &lt;span&gt;0(=false)&lt;/span&gt;이기 때문에 잠근 지갑에 추가 송금이 가능하다는 것을 알 수 있습니다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.88371%; text-align: left;&quot;&gt;&lt;b&gt;&lt;span&gt;구분&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 90.1163%; text-align: left;&quot;&gt;
&lt;p&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.88371%;&quot;&gt;&lt;span&gt;Trx Hash&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 90.1163%;&quot;&gt;
&lt;p&gt;이더리움 블록체인에서 처리된 트랜잭션 정보&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;트랜잭션 해시&lt;/p&gt;
&lt;p&gt;&lt;span&gt;: 0x8abde45fabab69d5a2431d58f4c9d8d97befdec61f6e603fd15b023bee99fb16&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;트랜잭션이 담긴 블록 번호&lt;/p&gt;
&lt;span&gt;: &lt;/span&gt;&lt;span&gt;6191216&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.88371%;&quot;&gt;&lt;span&gt;Method&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 90.1163%;&quot;&gt;
&lt;p&gt;&lt;span&gt;APIS &lt;/span&gt;컨트랙트에서 처리된 함수&lt;span&gt;(=Event&lt;/span&gt;를 발생시킨 함수&lt;span&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;함수 주소&lt;/p&gt;
&lt;p&gt;&lt;span&gt;: 0x74ccd7cc&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;함수 정의&lt;/p&gt;
&lt;span&gt;: walletLock(address,uint256,bool,bool)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 9.88371%;&quot;&gt;
&lt;p&gt;&lt;span&gt;Event&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt; Logs&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 90.1163%;&quot;&gt;
&lt;p&gt;&lt;span&gt;APIS &lt;/span&gt;컨트랙트에서 처리된 함수에서 발생된 이벤트&lt;span&gt;(=&lt;/span&gt;메모 로그&lt;span&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;이벤트 정의&lt;/p&gt;
&lt;p&gt;&lt;span&gt;: Locked (index_topic_1 address target, uint256 timeLockUpEnd, bool sendLock, bool receiveLock)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;입력 값 내용&lt;/p&gt;
&lt;p&gt;&lt;span&gt;(address) &lt;span style=&quot;color: #1b711d;&quot;&gt;target&lt;/span&gt; : 0x8c133a2144b66ac06a12fe5c1a88c460f4dce4aa&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;(uint256) &lt;span style=&quot;color: #1b711d;&quot;&gt;timeLockUpEnd&lt;/span&gt; : 1534921200&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;(bool) &lt;span style=&quot;color: #1b711d;&quot;&gt;sendLock&lt;/span&gt; : 0000000000000000000000000000000000000000000000&amp;hellip;1&lt;/span&gt;&lt;/p&gt;
&lt;span&gt;(bool) &lt;span style=&quot;color: #1b711d;&quot;&gt;receiveLock&lt;/span&gt; : 0000000000000000000000000000000000000000000000&amp;hellip;0&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;지갑 잠금 시간인 &lt;span style=&quot;color: #1b711d;&quot;&gt;timeLockUpEnd&lt;/span&gt;의 값이 &lt;span&gt;1534921200&lt;/span&gt;으로 출력되었지만&lt;span&gt;, &lt;/span&gt;실제로 이 값은 컴퓨터에서 사용되는 &lt;span&gt;UnixTimestamp &lt;/span&gt;포멧의 시간정보입니다&lt;span&gt;. &lt;/span&gt;따라서 사람이 읽을 수 있는 시간 정보로 변환해서 봐야합니다&lt;span&gt;. &lt;/span&gt;변환된 시간값은 &lt;span&gt;&amp;ldquo;2018&lt;/span&gt;년 &lt;span&gt;8&lt;/span&gt;월 &lt;span&gt;22&lt;/span&gt;일 수요일 오후 &lt;span&gt;4:00:00&amp;rdquo;&lt;/span&gt;이므로&lt;span&gt;, &lt;/span&gt;이 때까지 해당 지갑은 잠겨있었다는 것을 알 수 있습니다&lt;span&gt;. (&lt;/span&gt;시간 변환 사이트는 아래 첨부했습니다&lt;span&gt;.)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;참고로&lt;span&gt;, &lt;/span&gt;발생된 &lt;span style=&quot;color: #ef5369;&quot;&gt;Locked &lt;/span&gt;이벤트 로그 중에 &lt;span style=&quot;color: #1b711d;&quot;&gt;timeLockUpEnd &lt;/span&gt;시간이 &lt;span&gt;9999999999&lt;/span&gt;인 것들이 많이 있는데&lt;span&gt;, &lt;/span&gt;이 시간을 변환하면 &lt;span&gt;&amp;ldquo;2286&lt;/span&gt;년 &lt;span&gt;11&lt;/span&gt;월 &lt;span&gt;21&lt;/span&gt;일 일요일 오전 &lt;span&gt;2:46:39&amp;rdquo;&lt;/span&gt;이므로&lt;span&gt;, &lt;/span&gt;영원히 잠궈버리겠다는 의미입니다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;APIS&lt;/span&gt;라는 처음 들어보는 코인이었는데&lt;span&gt;, &lt;/span&gt;올해&lt;span&gt;(2019&lt;/span&gt;년&lt;span&gt;) &lt;/span&gt;초에 상장된 코인이었네요&lt;span&gt;. 아마도 &lt;/span&gt;이번 포스팅에 설명하는 컨트랙트는 &lt;span&gt;APIS&lt;/span&gt;가 상장하기 전에 사용했던 이더리움 블록체인 플랫폼에 올려둔 토큰 컨트랙트인 것 같습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;참고 사이트&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;Etherscan (이더리움 블록 정보 제공)&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;&lt;a style=&quot;color: #9d9d9d;&quot; href=&quot;https://etherscan.io/&quot;&gt;https://etherscan.io/&lt;/a&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;Bloxy (이더리움 블록 정보 제공)&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;&lt;a style=&quot;color: #9d9d9d;&quot; href=&quot;https://bloxy.info/&quot;&gt;https://bloxy.info/&lt;/a&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;시간 정보 변환 (UnixTimestamp Converter)&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;&lt;a style=&quot;color: #9d9d9d;&quot; href=&quot;https://www.epochconverter.com/&quot;&gt;https://www.epochconverter.com/&lt;/a&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>understanding/blockchain</category>
      <category>ethereum</category>
      <category>Event</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/180</guid>
      <comments>https://caileb.tistory.com/180#entry180comment</comments>
      <pubDate>Sun, 20 Oct 2019 12:32:14 +0900</pubDate>
    </item>
    <item>
      <title>Gson 사용 방법</title>
      <link>https://caileb.tistory.com/179</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;Gson 사용방법&lt;/h2&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;com.google.gson.Gson&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;gson.PNG&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c978B6/btqy3bBRSZR/WMG0ivKvi31Je7Yi0vgTz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c978B6/btqy3bBRSZR/WMG0ivKvi31Je7Yi0vgTz0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c978B6/btqy3bBRSZR/WMG0ivKvi31Je7Yi0vgTz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc978B6%2Fbtqy3bBRSZR%2FWMG0ivKvi31Je7Yi0vgTz0%2Fimg.png&quot; data-filename=&quot;gson.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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Gson은 java 에서 json 데이터를 다루기 위해 사용될 수 있는 라이브러리 중 하나입니다. 구글(Google)에서 만들었고, 주로 파싱(Parsing)을 할 때 사용되는 라이브러리입니다. 이 라이브러리를 사용해서 &lt;span style=&quot;color: #006dd7;&quot;&gt;json 문자열을 object로 변경&lt;/span&gt;시키거나 반대로, &lt;span style=&quot;color: #006dd7;&quot;&gt;object를 json 문자열로 변경&lt;/span&gt;시킬 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;사용시 주의할 점은&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;object를 String으로 변환할 때, &lt;span style=&quot;color: #006dd7;&quot;&gt;null 값에 대해서 변환 여부를 조절할 수 있다&lt;/span&gt;. (=GsonBuilder().serializeNulls() 사용)&lt;/li&gt;
&lt;li&gt;object를 String으로 변환할 때, &lt;span style=&quot;color: #006dd7;&quot;&gt;순서는 보장받지 못한다&lt;/span&gt;. (=순서까지 고려해야한다면 다른 방법을 추가로 사용)&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Class와 String간의 변환&lt;/h2&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;Class 준비 (변환에 사용될 대상인 샘플 Class)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1571161504959&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Joker {
	public int item1;
	public String item2;
	public String item3;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;1. Class -&amp;gt; json String&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1571159163720&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public void toJsonTest() {
	Joker joker = new Joker();
	joker.item1 = 100;
	joker.item2 = null;
	joker.item3 = &quot;hello&quot;;
		
	// not include null
	{
		Gson gson = new Gson();
		String json = gson.toJson(joker);
		System.out.println(json);
	}		
		
	// include null
	{
		Gson gson = new GsonBuilder().serializeNulls().create();
		String json = gson.toJson(joker);
		System.out.println(json);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;출력 결과 :&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;{&quot;item1&quot;:100,&quot;item3&quot;:&quot;hello&quot;}&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;{&quot;item2&quot;:null,&quot;item1&quot;:100,&quot;item3&quot;:&quot;hello&quot;}&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;2. json String -&amp;gt; Class&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1571159204408&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public void fromJsonTest() {
	String json = &quot;{\&quot;item1\&quot;:100,\&quot;item3\&quot;:\&quot;hello\&quot;}&quot;;
		
	Gson gson = new Gson();
	Joker joker = gson.fromJson(json, Joker.class);
	System.out.println(joker.item1);
	System.out.println(joker.item2);
	System.out.println(joker.item3);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;출력 결과 :&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;100&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;null&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;hello&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Map과 String간의 변환&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;1. Map -&amp;gt; json String&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1571159473787&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public void toJsonTest() {
	Map&amp;lt;String, Object&amp;gt; map = new HashMap&amp;lt;String, Object&amp;gt;(); 
	map.put(&quot;item1&quot;, 100);
	map.put(&quot;item2&quot;, null);
	map.put(&quot;item3&quot;, &quot;hello&quot;);
		
	// not include null
	{
		Gson gson = new Gson();
		String json = gson.toJson(map);
		System.out.println(json);
	}		
		
	// include null
	{
		Gson gson = new GsonBuilder().serializeNulls().create();
		String json = gson.toJson(map);
		System.out.println(json);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;출력 결과 :&lt;/p&gt;
&lt;p&gt;{&quot;item1&quot;:100,&quot;item3&quot;:&quot;hello&quot;}&lt;/p&gt;
&lt;p&gt;{&quot;item1&quot;:100,&quot;item2&quot;:null,&quot;item3&quot;:&quot;hello&quot;}&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;2. json String -&amp;gt; Map&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1571159526912&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public void fromJsonTest() {
	String json = &quot;{\&quot;item1\&quot;:100,\&quot;item3\&quot;:\&quot;hello\&quot;}&quot;;
		
	Gson gson = new Gson();
	Type listType = new TypeToken&amp;lt;HashMap&amp;lt;String, Object&amp;gt;&amp;gt;(){}.getType();
	Map&amp;lt;String, Object&amp;gt; joker = gson.fromJson(json, listType);
	System.out.println(joker.get(&quot;item1&quot;));
	System.out.println(joker.get(&quot;item2&quot;));
	System.out.println(joker.get(&quot;item3&quot;));
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;출력 결과 :&lt;/p&gt;
&lt;p&gt;100.0&lt;/p&gt;
&lt;p&gt;null&lt;/p&gt;
&lt;p&gt;hello&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고 사이트&lt;/h2&gt;
&lt;p&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;Google Gson 깃 허브&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;&lt;u&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;a style=&quot;color: #9d9d9d;&quot; href=&quot;https://github.com/google/gson&quot;&gt;https://github.com/google/gson&lt;/a&gt;&lt;/span&gt;&lt;/u&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;Gson 사용 가이드&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;&lt;u&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;a style=&quot;color: #9d9d9d;&quot; href=&quot;https://sites.google.com/site/gson/gson-user-guide&quot;&gt;https://sites.google.com/site/gson/gson-user-guide&lt;/a&gt;&lt;/span&gt;&lt;/i&gt;&lt;/u&gt;&lt;/p&gt;</description>
      <category>dev-tips/java</category>
      <category>Gson</category>
      <category>JSON</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/179</guid>
      <comments>https://caileb.tistory.com/179#entry179comment</comments>
      <pubDate>Wed, 16 Oct 2019 02:37:16 +0900</pubDate>
    </item>
    <item>
      <title>android - Fragment</title>
      <link>https://caileb.tistory.com/178</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;Fragment&lt;/h2&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;concept, life cycle&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;fragment1.PNG&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AVGJn/btqyZsDUq6Y/vMai8VZmqTcGMTw8vKG9w0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AVGJn/btqyZsDUq6Y/vMai8VZmqTcGMTw8vKG9w0/img.png&quot; data-alt=&quot;[ Fragment Concept ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AVGJn/btqyZsDUq6Y/vMai8VZmqTcGMTw8vKG9w0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAVGJn%2FbtqyZsDUq6Y%2FvMai8VZmqTcGMTw8vKG9w0%2Fimg.png&quot; data-filename=&quot;fragment1.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;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ Fragment Concept ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Fragment는 스마트폰(Smart Phone)이 아닌, 스마트폰 보다는 조금 더 큰 탭이나 태블릿의 화면을 좀 더 효율적으로 사용하기 위해 등장한 안드로이드 API 중 하나입니다. 태블릿이 활성화되기 전에 스마트폰만 사용할 때는 Activity만으로 화면제어가 충분했지만, 갤럭시 노트(Galaxy Note)나 갤럭시 탭(Galaxy Tab)처럼 화면이 큰 디바이스들이 등장하면서 Activity 하나로는 화면의 UI를 제어하기가 불편해진 것이죠. 그래서 &lt;span style=&quot;color: #006dd7;&quot;&gt;Android는 하나의 화면 단위인 Activity를 조금씩 쪼개서 부분별로 제어를 가능하게 할 수 있는 Fragment를 제공하기 시작&lt;/span&gt;했습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;처음에는 Fragment가 태블릿같은 &lt;span style=&quot;color: #006dd7;&quot;&gt;큰 화면 내에서 부분별로 나뉘어서 UI를 제어&lt;/span&gt;하기 위해 제공된 것이지만, 스마트폰에서는 작은 화면을 더 작게 나뉘기에는 효율적이기 않았기 때문에 조금 다른 방식으로 사용됬습니다. &lt;span style=&quot;color: #006dd7;&quot;&gt;화면을 여러 개로 만들고 이들을 겹쳐서 사용&lt;/span&gt;하는 식이었습니다. 그래서 현재, 스마트폰에서 Fragment를 사용하는 경우 탭(Tab)으로 사용되는 경우가 많습니다. 예를 들면, 카카오톡(Kakao Talk)의 UI가 탭으로 생긴 것처럼 말이죠.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;앞 설명처럼 Fragment는 Activity를 부분적으로 나뉘어서 만들어진 것이기 때문에, &lt;span style=&quot;color: #006dd7;&quot;&gt;Fragment는 Activity에 의존적&lt;/span&gt;일 수 밖에 없습니다. 그래서 Fragment는 Activity가 존재해야만 존재할 수 있고, Fragment의 생명주기(Life Cycle) 또한 Activity의 생명주기에 의존적이게 되는 것입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;만약, 웹(Web)을 개발해본 경험이 있다면, 웹의 화면을 부분적으로 나누기 위해 &amp;lt;frame&amp;gt;이나 &amp;lt;div&amp;gt; 태그를 사용해본 경험이 있을 것입니다. 예를 들어, 웹 사이트를 만들 때 화면의 일부분은 광고, 일부분은 뉴스, 일부분은 쇼핑목록 등으로 출력시켜야하는 경우 사용할 수 있을 것입니다. 웹에서 이렇게 사용되는 &lt;span style=&quot;color: #006dd7;&quot;&gt;HTML의 &amp;lt;frame&amp;gt;이나 &amp;lt;div&amp;gt; 태그가 태블릿에서의 Fragment 역할&lt;/span&gt;을 하는 것이고, &lt;span style=&quot;color: #006dd7;&quot;&gt;HTML 태그 속성 중 화면에서 사라지게 만드는 &amp;ldquo;display&amp;rdquo; 속성이 스마트폰에서의 Fragment 역할&lt;/span&gt;을 하는 것이라고 말할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;Fragment Life Cycle (생명주기)&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;fragment2.PNG&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/veoyt/btqy0qFGa8o/v5UfPHplMwFckgpYj7rpB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/veoyt/btqy0qFGa8o/v5UfPHplMwFckgpYj7rpB1/img.png&quot; data-alt=&quot;[ Fragment Life Cycle]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/veoyt/btqy0qFGa8o/v5UfPHplMwFckgpYj7rpB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fveoyt%2Fbtqy0qFGa8o%2Fv5UfPHplMwFckgpYj7rpB1%2Fimg.png&quot; data-filename=&quot;fragment2.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;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ Fragment Life Cycle]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span&gt;Fragment&lt;/span&gt;가 보여지기까지 호출되는 함수 순서&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;onAttach()&lt;/span&gt; : Fragment&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;가 &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Activity&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;에 붙을 때 호출&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;onCreate()&lt;/span&gt; : Fragment&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;가 생성될 때 호출 &lt;/span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;(=UI &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;작업 불가능)&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;onCreateView()&lt;/span&gt; : Fragment&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;의 &lt;/span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;UI&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;를 그리기 위한&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;View&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;가 생성될 때 호출&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;onActivityCreated()&lt;/span&gt; : &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;보통 잘 사용하지 않는다&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;. (ListFragment&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;를 활용할 때 주로 사용&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;onStart()&lt;/span&gt; : Fragment&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;가 화면에 표시될 때 호출 &lt;/span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;(=&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;사용자 상호작용 불가능&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;onResume()&lt;/span&gt; : Fragment&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;의 포커스가 잡힐 때 호출 &lt;/span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;(=&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;사용자 상호작용 가능&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span&gt;Fragment&lt;/span&gt;가 가려지기까지 호출되는 함수 순서&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;onPause()&lt;/span&gt; : Fragment&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;의 포커스가 없어질 때 호출&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;onStop()&lt;/span&gt; : Fragment&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;가 화면에서 사라질 때 호출&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;onDestroyView()&lt;/span&gt; : Fragment&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;의 &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;View&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;가 제거될 때 호출&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;onDestroy()&lt;/span&gt; : Fragment&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;가 제거될 때 호출&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;onDetach()&lt;/span&gt; : Fragment&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;가 &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Activity&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;에서 떼어질 때 호출&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Fragment Sample Code (기본 구성)&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;fragment3.PNG&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c5N4PV/btqy1ILSyzF/sxu5hKdXossLakcyCvSxi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c5N4PV/btqy1ILSyzF/sxu5hKdXossLakcyCvSxi1/img.png&quot; data-alt=&quot;[ Fragment 구성 ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c5N4PV/btqy1ILSyzF/sxu5hKdXossLakcyCvSxi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc5N4PV%2Fbtqy1ILSyzF%2Fsxu5hKdXossLakcyCvSxi1%2Fimg.png&quot; data-filename=&quot;fragment3.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;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ Fragment 구성 ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래는 스마트폰위에서&amp;nbsp;&lt;span&gt;Fragment&lt;/span&gt;를 사용해서 개발하는 경우 사용될 수 있는 기본 구성 중 하나입니다&lt;span&gt;. &lt;/span&gt;설명하는 코드는 총 &lt;span&gt;4&lt;/span&gt;개의 파일로 구성되어 있습니다&lt;span&gt;. 우선, 하나의 Activity 만든 후, 그 안에 하나의 Fragment 를 만드는 코드입니다.&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: square;&quot; data-ke-list-type=&quot;square&quot;&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;MainActivity.java&lt;/span&gt; (=Activity)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;activity_main.xml&lt;/span&gt; (=Activity &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;레이아웃&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;MainFragment.java&lt;/span&gt; (=Fragment)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;fragment_main.xml&lt;/span&gt; (=Fragment &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;레이아웃&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;MainActivity.java&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1570980758019&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.joker;

public class MainActivity extends AppCompatActivity {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;activity_main.xml&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1570980781409&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;RelativeLayout ...&amp;gt;
	&amp;lt;fragment
	class=&quot;com.joker.MainFragment&quot;
	android:layout_width=&quot;match_parent&quot;
	android:layout_height=&quot;match_parent&quot;
	tools:layout=&quot;@layout/fragment_main&quot;
	... /&amp;gt;
&amp;lt;/RelativeLayout&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;MainFragment.java&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1570980856208&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.joker;

public class MainFragment extends Fragment {
	@Override
	public void onActivityCreated(Bundle savedInstanceState) {
		super.onActivityCreated(savedInstanceState);
	}

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
		View v = inflater.inflate(R.layout.fragment_main.xml, container, false);
		return v;
	}

	@Override
	public void onViewCreated(View view, Bundle savedInstanceState) {
		super.onViewCreated(view, savedInstanceState);
	}

	@Override
	public void onAttach(Activity activity) {
		super.onAttach(activity);
	}

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
	}

	@Override
	public void onDestroy() {
		super.onDestroy();
	}

	@Override
	public void onDestroyView() {
		super.onDestroyView();
	}

	@Override
	public void onDetach() {
		super.onDetach();
	}

	@Override
	public void onPause() {
		super.onPause();
	}

	@Override
	public void onResume() {
		super.onResume();
	}

	@Override
	public void onStart() {
		super.onStart();
	}

	@Override
	public void onStop() {
		super.onStop();
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;fragment_main.xml&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1570980886192&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;LinearLayout ...&amp;gt;
&amp;lt;/LinearLayout&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;참고&lt;/h3&gt;
&lt;p&gt;&lt;i&gt;&lt;span style=&quot;color: #666666;&quot;&gt;(Android Developer) Fragment 설명&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;&lt;u&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;i&gt;&lt;a style=&quot;color: #666666;&quot; href=&quot;https://developer.android.com/guide/components/fragments&quot;&gt;https://developer.android.com/guide/components/fragments&lt;/a&gt;&lt;/i&gt;&lt;/span&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>dev-tips/android</category>
      <category>Android</category>
      <category>fragment</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/178</guid>
      <comments>https://caileb.tistory.com/178#entry178comment</comments>
      <pubDate>Mon, 14 Oct 2019 00:56:37 +0900</pubDate>
    </item>
    <item>
      <title>STD Exception encountered while processing chain.get_code</title>
      <link>https://caileb.tistory.com/177</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;에러&lt;/h2&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;STD Exception encountered while processing chain.get_code&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;err.PNG&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7krMn/btqyKaBPxnJ/twxZ7jJWLHBwiNxnpqh1K0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7krMn/btqyKaBPxnJ/twxZ7jJWLHBwiNxnpqh1K0/img.png&quot; data-alt=&quot;[ 에러 로그 ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7krMn/btqyKaBPxnJ/twxZ7jJWLHBwiNxnpqh1K0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7krMn%2FbtqyKaBPxnJ%2FtwxZ7jJWLHBwiNxnpqh1K0%2Fimg.png&quot; data-filename=&quot;err.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;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ 에러 로그 ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;에러 상세&lt;/span&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1569846755623&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;STD Exception encountered while processing chain.get_code
Exception Details: unknown key (eosio::chain::name: joker)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;환경&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;span&gt;OS : Mac OS&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Blockchain : EOS&lt;/p&gt;
&lt;p&gt;&lt;span&gt;IDE : EOS Studio / &lt;/span&gt;&lt;span&gt;EOSIO v1.7.3&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;상황&lt;/h2&gt;
&lt;p&gt;터미널(Terminal)을 사용해서 EOS 노드에서 제공하는 &lt;span style=&quot;color: #ee2323;&quot;&gt;RPC API(Chain API)의 get_code를 curl로 실행했더니 위와 같은 에러로그가 출력&lt;/span&gt;되었다. 현재 작성자는 EOS Studio를 통해 노드 하나를 띄워놓고, get_code API를 통해 &quot;joker&quot;라는 컨트랙트(Contract)의 내용을 조회하려고 시도한 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1569846340856&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;curl --request POST   
--url http://localhost:8888/v1/chain/get_code   
--header 'accept: application/json'   
--header 'content-type: application/json'   
--data '{&quot;account_name&quot;:&quot;joker&quot;,&quot;code_as_wasm&quot;:1}'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;원인&lt;/h2&gt;
&lt;p&gt;&lt;span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;컨트랙트(Contract)를 배포하지 않았는데, get_code API로 조회를 시도했기 때문에 발생한 에러&lt;/span&gt;이다. 현재 작성자는 EOS Studio를 통해 로컬에 설치(install)된 노드로 몇가지 테스트를 위해 수시로 지웠다가 다시 설치하는 과정을 반복하고 있었다. 현재 작성자가 시도하고 있는 get_code 라는 API는 EOS 노드에 배포된 컨트랙트를 조회하고 해당 내용을 반환해준다. 그런데, 실수로 노드를 재설치한 후에 컨트랙트를 배포하지 않고 계속시도를 한 것이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;다시 말해서, 위 curl 명령을 보면 &quot;joker&quot;라는 컨트랙트를 조회하고 있지만, 실제 로컬에서 운영중인 EOS 노드에는 &quot;joker&quot;라는 컨트랙트를 아직 배포하지 않았던 것이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;해결&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;컨트랙트를 배포한 후, get_code API를 다시 시도&lt;/span&gt;하면 정상적으로 조회된다. 작성자는 get_code를 실행할 때&amp;nbsp;&lt;span style=&quot;color: #ee2323;&quot;&gt;STD Exception encountered while processing &lt;/span&gt;에러가 발생했지만, 다른 RPC API를 사용하는 경우에 만약에 해당 컨트랙트가 배포되지 않은 상태라면, 위와 같은 에러가 발생할 수 있으니 알아두자.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;참고&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;span&gt;&lt;span&gt;EOS Developer &amp;gt; get_code&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;a style=&quot;color: #9d9d9d;&quot; href=&quot;https://developers.eos.io/eosio-nodeos/reference#get_code-1&quot;&gt;https://developers.eos.io/eosio-nodeos/reference#get_code-1&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>error/[err] blockchain</category>
      <category>EOS</category>
      <category>get_code</category>
      <category>RPC API</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/177</guid>
      <comments>https://caileb.tistory.com/177#entry177comment</comments>
      <pubDate>Mon, 30 Sep 2019 21:55:22 +0900</pubDate>
    </item>
    <item>
      <title>repackage failed: Unable to find main class</title>
      <link>https://caileb.tistory.com/176</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;에러&lt;/h2&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;repackage&amp;nbsp;failed:&amp;nbsp;Unable&amp;nbsp;to&amp;nbsp;find&amp;nbsp;main&amp;nbsp;class&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;에러 상세&lt;/h2&gt;
&lt;pre id=&quot;code_1569540955412&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[ERROR] Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:2.1.8.RELEASE:repackage (repackage) on project OmniOneData: Execution repackage of goal org.springframework.boot:spring-boot-maven-plugin:2.1.8.RELEASE:repackage failed: Unable to find main class -&amp;gt; [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/PluginExecutionException&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;환경&lt;/h2&gt;
&lt;p&gt;OS&amp;nbsp;:&amp;nbsp;macOS&amp;nbsp;Mojave &lt;br /&gt;IDE&amp;nbsp;:&amp;nbsp;Eclipse &lt;br /&gt;Build&amp;nbsp;:&amp;nbsp;Maven&lt;/p&gt;
&lt;p&gt;JDK : 1.8&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;상황&lt;/h2&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;java 프로젝트를 jar로 빌드하려는데, 위와 같은 에러가 발생&lt;/span&gt;했다. 참고로, 위 에러는 환경에 따라 발생할 수도 않할 수도 있다. 현재 프로젝트는 처음부터 개발하던 것이 아닌, 외부에서 미리 개발된 프로젝트를 가져와서 작성자의 PC에서 빌드하려고 시도한 것이다. 기존 외부에서 개발되던 PC 환경에서는 빌드가 정상적으로 됬지만, 글쓴이의 PC로 가져와서는 위 에러와 함께 빌드를 실패한 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;pom.xml&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1569541103772&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;project&amp;gt;
&amp;hellip;
&amp;lt;properties&amp;gt;
	&amp;lt;java.version&amp;gt;1.8&amp;lt;/java.version&amp;gt;
&amp;lt;/properties&amp;gt;
&amp;lt;build&amp;gt;
	&amp;lt;plugins&amp;gt;
		&amp;lt;plugin&amp;gt;
			&amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
			&amp;lt;artifactId&amp;gt;spring-boot-maven-plugin&amp;lt;/artifactId&amp;gt;
		&amp;lt;/plugin&amp;gt;
	&amp;lt;/plugins&amp;gt;
&amp;lt;/build&amp;gt;
&amp;lt;/project&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;원인&lt;/h2&gt;
&lt;p&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Spring&amp;nbsp;Boot의&amp;nbsp;&amp;ldquo;spring-boot-maven-plugin&amp;rdquo;&amp;nbsp;플러그인(Plug-In)을&amp;nbsp;잘못사용해서&amp;nbsp;발생한&amp;nbsp;에러&lt;/span&gt;이다. 이 플러그인은 실행가능한 jar (executable or runnable jar)파일을 만들려고 시도하는 경우에 해당 &lt;span style=&quot;color: #1b711d;&quot;&gt;jar를 생성하기 위한 java 소스(source)파일들 중에 메인(main)함수가 존재하지 않는 경우에 발생&lt;/span&gt;하는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결&lt;/h2&gt;
&lt;p&gt;위 에러를 해결하기 위해 찾은 몇가지 방법들을 소개한다. 아래 소개된 방법들 중에 개발중인 프로젝트 환경의 상황에 맞는 방법을 사용하면 된다. 참고로, 글쓴이는 위 에러를 없애기 위한 아래 방법들을 하나씩 적용해서 모두 확인을 해봤고, 아래 방법 중 [방법4]를 사용해서 에러를 해결하였다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;[방법1]&lt;/b&gt; &lt;span style=&quot;color: #006dd7;&quot;&gt;main 이 존재하는 파일을 생성&lt;/span&gt;한다. (&lt;span style=&quot;color: #9d9d9d;&quot;&gt;main()&lt;/span&gt; 함수 추가)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;[방법2]&lt;/b&gt; &lt;span style=&quot;color: #006dd7;&quot;&gt;jar를 실행하기 위한 클래스를 별도로 지정&lt;/span&gt;한다. (&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&amp;lt;start-class&amp;gt;&lt;/span&gt; 사용)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;[방법3]&lt;/b&gt; &lt;span style=&quot;color: #006dd7;&quot;&gt;jar를 실행하기 위한 클래스를 별도로 지정&lt;/span&gt;한다. (&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt; 사용)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;[방법4]&lt;/b&gt; &lt;span style=&quot;color: #006dd7;&quot;&gt;main 클래스가 없더라도 무시하고 빌드&lt;/span&gt;시킨다. (&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt; 사용)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;[방법5]&lt;/b&gt; &lt;span style=&quot;color: #006dd7;&quot;&gt;부모, 자식관계를 사용해서 빌드&lt;/span&gt;시킨다. (&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&amp;lt;pluginManagement&amp;gt;&lt;/span&gt; 사용)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;[방법1]&lt;/b&gt;&amp;nbsp;&lt;span style=&quot;color: #006dd7;&quot;&gt;main&amp;nbsp;이&amp;nbsp;존재하는&amp;nbsp;파일을&amp;nbsp;생성&lt;/span&gt;한다.&amp;nbsp;(&lt;span style=&quot;color: #9d9d9d;&quot;&gt;main()&lt;/span&gt;&amp;nbsp;함수&amp;nbsp;추가) &lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;jar에 포함시키는 java 파일 중 하나를 생성한다. (새로 생성하지 않고 기존 java 파일을 사용해도 상관없다.)&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;main()&lt;/span&gt; 함수를 작성한다.&lt;/li&gt;
&lt;li&gt;다시 빌드한다. (=Maven Install)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;Joker.java (예시)&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1569602876957&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Joker {
	public static void main(String[] args) {
		&amp;hellip;		
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;[방법2]&lt;/b&gt; &lt;span style=&quot;color: #006dd7;&quot;&gt;jar를 실행하기 위한 클래스를 별도로 지정&lt;/span&gt;한다. (&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&amp;lt;start-class&amp;gt;&lt;/span&gt; 사용) &lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;pom.xml 파일의 &lt;span style=&quot;color: #9d9d9d;&quot;&gt;&amp;lt;properties&amp;gt;&lt;/span&gt;안에 &lt;span style=&quot;color: #9d9d9d;&quot;&gt;&amp;lt;start-class&amp;gt;&lt;/span&gt;를 추가한다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&amp;lt;start-class&amp;gt;&lt;/span&gt; 안에 &lt;span style=&quot;color: #9d9d9d;&quot;&gt;main()&lt;/span&gt; 함수가 포함된 클래스를 작성한다. (이때, 반드시 jar 내에 포함되어 있을 필요는 없기 때문에, 아래 예시처럼 패키지 이름만 적어도 된다.)&lt;/li&gt;
&lt;li&gt;다시 빌드한다. (=Maven Install)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;pom.xml&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1569603146943&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;project&amp;gt;
&amp;hellip;
&amp;lt;properties&amp;gt;
	&amp;lt;java.version&amp;gt;1.8&amp;lt;/java.version&amp;gt;
	&amp;lt;start-class&amp;gt;joker.package.main&amp;lt;/start-class&amp;gt;
&amp;lt;/properties&amp;gt;
&amp;lt;build&amp;gt;
	&amp;lt;plugins&amp;gt;
		&amp;lt;plugin&amp;gt;
			&amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
			&amp;lt;artifactId&amp;gt;spring-boot-maven-plugin&amp;lt;/artifactId&amp;gt;
		&amp;lt;/plugin&amp;gt;
	&amp;lt;/plugins&amp;gt;
&amp;lt;/build&amp;gt;
&amp;lt;/project&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;[방법3]&lt;/b&gt; &lt;span style=&quot;color: #006dd7;&quot;&gt;jar를 실행하기 위한 클래스를 별도로 지정&lt;/span&gt;한다. (&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt; 사용)&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot;&gt;
&lt;li&gt;pom.xml 파일의 &lt;span style=&quot;color: #9d9d9d;&quot;&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;안에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;을 추가한다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;안에 &lt;span style=&quot;color: #9d9d9d;&quot;&gt;&amp;lt;mainClass&amp;gt;&lt;/span&gt;를 추가한다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&amp;lt;mainClass&amp;gt;&lt;/span&gt; 안에 &lt;span style=&quot;color: #9d9d9d;&quot;&gt;main()&lt;/span&gt; 함수가 포함된 클래스를 작성한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;다시 빌드한다. (=Maven Install)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;pom.xml&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1569603282869&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;project&amp;gt;
&amp;hellip;
&amp;lt;properties&amp;gt;
	&amp;lt;java.version&amp;gt;1.8&amp;lt;/java.version&amp;gt;
&amp;lt;/properties&amp;gt;
&amp;lt;build&amp;gt;
	&amp;lt;plugins&amp;gt;
		&amp;lt;plugin&amp;gt;
			&amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
			&amp;lt;artifactId&amp;gt;spring-boot-maven-plugin&amp;lt;/artifactId&amp;gt;
			&amp;lt;configuration&amp;gt;
		            &amp;lt;mainClass&amp;gt;joker.package&amp;lt;/mainClass&amp;gt; 
		        &amp;lt;/configuration&amp;gt;
		&amp;lt;/plugin&amp;gt;
	&amp;lt;/plugins&amp;gt;
&amp;lt;/build&amp;gt;
&amp;lt;/project&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;[방법4]&lt;/b&gt; &lt;span style=&quot;color: #006dd7;&quot;&gt;main 클래스가 없더라도 무시하고 빌드&lt;/span&gt;시킨다. (&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt; 사용)&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot;&gt;
&lt;li&gt;pom.xml 파일의 &lt;span style=&quot;color: #9d9d9d;&quot;&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;안에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;을 추가한다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;안에 &lt;span style=&quot;color: #9d9d9d;&quot;&gt;&amp;lt;skip&amp;gt;&lt;/span&gt;을 추가한다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&amp;lt;skip&amp;gt;&lt;/span&gt; 안에 true를 작성한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;다시 빌드한다. (=Maven Install)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;pom.xml&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1569603439469&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;project&amp;gt;
&amp;hellip;
&amp;lt;build&amp;gt;
	&amp;lt;plugins&amp;gt;
		&amp;lt;plugin&amp;gt;
			&amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
			&amp;lt;artifactId&amp;gt;spring-boot-maven-plugin&amp;lt;/artifactId&amp;gt;
			&amp;lt;configuration&amp;gt;
				&amp;lt;skip&amp;gt;true&amp;lt;/skip&amp;gt;
			&amp;lt;/configuration&amp;gt;
		&amp;lt;/plugin&amp;gt;
	&amp;lt;/plugins&amp;gt;
&amp;lt;/build&amp;gt;
&amp;lt;/project&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;[방법5]&lt;/b&gt; &lt;span style=&quot;color: #006dd7;&quot;&gt;부모, 자식 관계를 사용해서 빌드&lt;/span&gt;시킨다. (&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&amp;lt;pluginManagement&amp;gt;&lt;/span&gt; 사용)&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot;&gt;
&lt;li&gt;pom.xml 파일의 &lt;span style=&quot;color: #9d9d9d;&quot;&gt;&amp;lt;build&amp;gt;&lt;/span&gt;안에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&amp;lt;pluginManagement&amp;gt;&lt;/span&gt;를 추가한다.&lt;/li&gt;
&lt;li&gt;다시 빌드한다. (=Maven Install)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;pom.xml&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1569603504423&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;project&amp;gt;
&amp;hellip;
&amp;lt;build&amp;gt;
	&amp;lt;pluginManagement&amp;gt;
		&amp;lt;plugins&amp;gt;
			&amp;lt;plugin&amp;gt;
				&amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
				&amp;lt;artifactId&amp;gt;spring-boot-maven-plugin&amp;lt;/artifactId&amp;gt;
			&amp;lt;/plugin&amp;gt;
		&amp;lt;/plugins&amp;gt;
	&amp;lt;/pluginManagement&amp;gt;
&amp;lt;/build&amp;gt;
&amp;lt;/project&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고&lt;/h2&gt;
&lt;p&gt;stack overflow 해결방법&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;a style=&quot;color: #9d9d9d;&quot; href=&quot;https://stackoverflow.com/questions/42937577/unable-to-find-main-class-with-maven-on-spring-boot-project-in-eclipse&quot;&gt;https://stackoverflow.com/questions/42937577/unable-to-find-main-class-with-maven-on-spring-boot-project-in-eclipse&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>error/[err] server</category>
      <category>JAR</category>
      <category>pom.xml</category>
      <category>Spring</category>
      <category>spring-boot-maven-plugin</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/176</guid>
      <comments>https://caileb.tistory.com/176#entry176comment</comments>
      <pubDate>Sat, 28 Sep 2019 02:16:07 +0900</pubDate>
    </item>
    <item>
      <title>Failed to bind properties under 'spring.datasource.type' to java.lang.Class&amp;lt;javax.sql.DataSource&amp;gt;</title>
      <link>https://caileb.tistory.com/175</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;에러&lt;/h2&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Failed&amp;nbsp;to&amp;nbsp;bind&amp;nbsp;properties&amp;nbsp;under&amp;nbsp;'spring.datasource.type'&amp;nbsp;to&amp;nbsp;java.lang.Class&amp;lt;javax.sql.DataSource&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;err2.PNG&quot; width=&quot;764&quot; height=&quot;286&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/domYSv/btqyzdrUO1Q/l0zNne7uJWtrmybClKQKb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/domYSv/btqyzdrUO1Q/l0zNne7uJWtrmybClKQKb1/img.png&quot; data-alt=&quot;[ 에러 화면 ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/domYSv/btqyzdrUO1Q/l0zNne7uJWtrmybClKQKb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdomYSv%2FbtqyzdrUO1Q%2Fl0zNne7uJWtrmybClKQKb1%2Fimg.png&quot; data-filename=&quot;err2.PNG&quot; width=&quot;764&quot; height=&quot;286&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ 에러 화면 ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;에러 상세&lt;/h2&gt;
&lt;pre id=&quot;code_1569344715151&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Description:

Failed to bind properties under 'spring.datasource.type' to java.lang.Class&amp;lt;javax.sql.DataSource&amp;gt;:

    Property: spring.datasource.type
    Value: org.apache.tomcat.jdbc.pool.DataSource
    Origin: class path resource [application.properties]:1:24
    Reason: No converter found capable of converting from type [java.lang.String] to type [java.lang.Class&amp;lt;javax.sql.DataSource&amp;gt;]

Action:

Update your application's configuration&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개발 환경&lt;/h2&gt;
&lt;p&gt;OS : Windows 10&lt;/p&gt;
&lt;p&gt;IDE : Eclipse&lt;/p&gt;
&lt;p&gt;Framework : Spring Boot 2.1.6&lt;/p&gt;
&lt;p&gt;Java : JDK 1.8&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;상황&lt;/h2&gt;
&lt;p&gt;Spring Boot 를 사용해서 DB를 설정한 후 실행을 하자마자 바로 &lt;span style=&quot;color: #ee2323;&quot;&gt;Failed&amp;nbsp;to&amp;nbsp;bind&amp;nbsp;properties&amp;nbsp;under&amp;nbsp;'&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;spring.datasource.type'&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&amp;nbsp;to&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;java.lang.Class&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;javax.sql.DataSource&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&amp;gt;&lt;/span&gt; 에러라는 발생했고, 서버를 구동하는 것을 실패했습니다. 현재 에러가 발생하는 상황에서 &lt;span style=&quot;color: #9d9d9d;&quot;&gt;pom.xml&lt;/span&gt;과 &lt;span style=&quot;color: #9d9d9d;&quot;&gt;applicaiton.properties&lt;/span&gt;의 내용은 아래와 같습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;pom.xml&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1569345007951&quot; class=&quot;html xml&quot; style=&quot;margin: 20px auto 0px; display: block; overflow: auto; padding: 15px; color: #383a42; background: #f6f7f8; font-size: 14px; border-radius: 3px; font-family: Menlo, Consolas, Monaco, monospace; border: 1px solid #dddddd; cursor: default; z-index: 1;&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
	&amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
	&amp;lt;artifactId&amp;gt;spring-boot-starter-data-jpa&amp;lt;/artifactId&amp;gt;
&amp;lt;/dependency&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;application.properties&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1569345007952&quot; class=&quot;html xml&quot; style=&quot;margin: 20px auto 0px; display: block; overflow: auto; padding: 15px; color: #383a42; background: #f6f7f8; font-size: 14px; border-radius: 3px; font-family: Menlo, Consolas, Monaco, monospace; border: 1px solid #dddddd; cursor: default; z-index: 1;&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring.datasource.type=org.apache.tomcat.jdbc.pool.DataSource&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;원인&lt;/h2&gt;
&lt;p&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;데이터베이스 커넥션 풀(Database Connection Pool)의 설정이 잘못되서 발생한 에러&lt;/span&gt;입니다. 이 에러의 원인을 파악하기 전에 커넥션 풀에 대한 개념을 알아두는 것이 좋습니다. &lt;span style=&quot;color: #1b711d;&quot;&gt;커넥션 풀이란, 애플리케이션(Application)과 데이터베이스(Database)를 효과적으로 연동되도록 제공하는 것&lt;/span&gt;입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;애플리케이션이 데이터베이스를 읽고(Read), 쓰는(Write) 작업을 할 때마다, 매번 DB에 &quot;연결-작업-연결해제&quot;를 반복할 것입니다. 만약에, 애플리케이션이 데이터베이스에서 정말로 해야하는 작업(ex, Insert, Upate 등)들만 수행하고, 연결과 연결 해제 등의 불필요한 반복작업들을 줄인다면 둘 사이에서 처리되어야 하는 작업의 속도는 빨라질 것입니다. 이러한 이유로, 대부분은 아래 그림처럼&amp;nbsp;&lt;span style=&quot;color: #1b711d;&quot;&gt;애플리케이션과 DB 사이에 하나의 풀(Pool)을 두고, 둘을 연결(Connection)짓는 객체들을 여러개 미리 만들어 놓는 구조&lt;/span&gt;를 사용하고 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;desc.PNG&quot; width=&quot;474&quot; height=&quot;245&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckAPIc/btqywbI6QhC/TDIrZ7BPrbFHXxzCaFVPOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckAPIc/btqywbI6QhC/TDIrZ7BPrbFHXxzCaFVPOK/img.png&quot; data-alt=&quot;[ App과 DB간의 커넥션 풀 ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckAPIc/btqywbI6QhC/TDIrZ7BPrbFHXxzCaFVPOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckAPIc%2FbtqywbI6QhC%2FTDIrZ7BPrbFHXxzCaFVPOK%2Fimg.png&quot; data-filename=&quot;desc.PNG&quot; width=&quot;474&quot; height=&quot;245&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ App과 DB간의 커넥션 풀 ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위 그림의 중간에 그려진 &lt;span style=&quot;color: #1b711d;&quot;&gt;커넥션 풀(Connection Pool)을 제공하고 있는 라이브러리로는 (Apache) Commons DBCP, Tomcat-JDBC, BoneCP, HikariCP&lt;/span&gt; 등이 있습니다. 그리고, Spring Framework로 개발하 때는, 이 라이브러리들 중에 무엇을 사용할지 설정을 해야합니다. 이번 포스팅에서 발생한 에러는 바로 이 라이브러리의 설정이 제대로 되지않았던 것이기 때문에, 이 설정을 바로 잡으면 됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위 에러가 발생한 원인은, 커넥션 풀을 제공하는 라이브러로 Spring에서 기본제공하는 HikariCP가 아닌 Commons CBCP를 사용하기 위해 &lt;span style=&quot;color: #9d9d9d;&quot;&gt;application.properties&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;에 spring.datasource.type을 &quot;&lt;span style=&quot;color: #333333;&quot;&gt;org.apache.tomcat.jdbc.pool.DataSource&quot;로 설정했지만 실행할 때 해당 패키지를 찾을 수 없기 때문에 발생한 에러이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결&lt;/h2&gt;
&lt;p&gt;위 에러를 해결하기 위해 글쓴이가 이번에 찾아낸 방법은 두가지를 설명하겠습니다. 하나는 Apahce의 Commons DBCP를 사용하는 방법과 다른 하나는 HikariCP를 사용하는 방법입니다.&amp;nbsp; 만약에 다른 라이브러리를 사용한다면, 이 포스팅에서 설명하는 해결방법처럼 해당 라이브러리를 사용하는 설정부분과 관련된 부분을 다시한번 확인해보면 해결될 것이라고 생각됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;[방법 1] HikariCP를 사용&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;만약에, &quot;&lt;u&gt;spring-boot-starter-data-jpa&lt;/u&gt;&quot; 혹은 &quot;&lt;u&gt;spring-boot-starter-jdbc&lt;/u&gt;&quot;를 사용하는 경우, 자동으로 HikariCP가 설정됩니다. 따라서, 이 방법을 사용하는 경우에는 &lt;span style=&quot;color: #9d9d9d;&quot;&gt;pom.xml&lt;/span&gt;에 위 라이브러리를 dependency로 추가하기만 자동으로 HikariCP가 제공하는 커넥션 풀을 사용하게 됩니다. 참고로, 이때는 &lt;span style=&quot;color: #9d9d9d;&quot;&gt;application.properties&lt;/span&gt;에 spring.datasource.type을 추가할 필요가 없습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;pom.xml&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1569343882411&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
	&amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
	&amp;lt;artifactId&amp;gt;spring-boot-starter-data-jpa&amp;lt;/artifactId&amp;gt;
&amp;lt;/dependency&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;application.properties&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1569343910055&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#spring.datasource.type 사용하지 않으므로 주석 or 제거&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;[방법 2] (Apache) Commons DBCP를 사용&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;만약에, [방법 1]처럼 Spring에서 기본으로 제공하는 커넥션 풀인 HikariCP를 사용하지 않고, (Apach) Commons DBCP를 사용하고 싶을 때는 아래처럼 추가 설정을 해주면 된다. &lt;span style=&quot;color: #9d9d9d;&quot;&gt;pom.xml&lt;/span&gt;에는 Commons DBCP를 제공하는 라이브러리인 &quot;&lt;u&gt;org.apache.tomcat&lt;/u&gt;&quot;을 추가해서 프로젝트에서 사용할 수있도록 하고, &lt;span style=&quot;color: #9d9d9d;&quot;&gt;application.properties&lt;/span&gt;에는 spring.datasource.type을 추가해서, Commons DBCP 내에 포함된 커넥션 풀인 &quot;org.apache.tomcat.jdbc.pool.DataSource&quot;로 설정하면 됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;pom.xml&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1569344383554&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
	&amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
	&amp;lt;artifactId&amp;gt;spring-boot-starter-data-jpa&amp;lt;/artifactId&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
	&amp;lt;groupId&amp;gt;org.apache.tomcat&amp;lt;/groupId&amp;gt;
	&amp;lt;artifactId&amp;gt;tomcat-jdbc&amp;lt;/artifactId&amp;gt;
	&amp;lt;version&amp;gt;9.0.10&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;application.properties&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1569344397272&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring.datasource.type=org.apache.tomcat.jdbc.pool.DataSource&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고&lt;/h2&gt;
&lt;p&gt;Spring Boot 2.1.6.RELEASE 데이터 커넥션 부분&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;a style=&quot;color: #9d9d9d;&quot; href=&quot;https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/htmlsingle/#boot-features-connect-to-production-database&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/htmlsingle/#boot-features-connect-to-production-database&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>error/[err] server</category>
      <category>Commons DBCP</category>
      <category>hikaricp</category>
      <category>커넥션 풀</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/175</guid>
      <comments>https://caileb.tistory.com/175#entry175comment</comments>
      <pubDate>Wed, 25 Sep 2019 02:15:44 +0900</pubDate>
    </item>
    <item>
      <title>org.h2.jdbc.JdbcSQLNonTransientConnectionException</title>
      <link>https://caileb.tistory.com/174</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;에러&lt;/h2&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;org.h2.jdbc.JdbcSQLNonTransientConnectionException&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;err.PNG&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brdrXk/btqywbbcEFs/vdIyUQcP3nOjCZHBzQmlc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brdrXk/btqywbbcEFs/vdIyUQcP3nOjCZHBzQmlc0/img.png&quot; data-alt=&quot;[ 에러 화면 ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brdrXk/btqywbbcEFs/vdIyUQcP3nOjCZHBzQmlc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrdrXk%2FbtqywbbcEFs%2FvdIyUQcP3nOjCZHBzQmlc0%2Fimg.png&quot; data-filename=&quot;err.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;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ 에러 화면 ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;에러 상세&lt;/h2&gt;
&lt;pre id=&quot;code_1569176569504&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;org.h2.jdbc.JdbcSQLNonTransientConnectionException: Database is already closed (to disable automatic closing at VM shutdown, add &quot;;DB_CLOSE_ON_EXIT=FALSE&quot; to the db URL) [90121-199]
	at org.h2.message.DbException.getJdbcSQLException(DbException.java:617) ~[h2-1.4.199.jar:1.4.199]
	at org.h2.message.DbException.getJdbcSQLException(DbException.java:427) ~[h2-1.4.199.jar:1.4.199]
	at org.h2.message.DbException.get(DbException.java:205) ~[h2-1.4.199.jar:1.4.199]
	at org.h2.message.DbException.get(DbException.java:181) ~[h2-1.4.199.jar:1.4.199]
	at org.h2.message.DbException.get(DbException.java:170) ~[h2-1.4.199.jar:1.4.199]
	at org.h2.jdbc.JdbcConnection.checkClosed(JdbcConnection.java:1571) ~[h2-1.4.199.jar:1.4.199]
	at org.h2.jdbc.JdbcConnection.checkClosed(JdbcConnection.java:1547) ~[h2-1.4.199.jar:1.4.199]
	at org.h2.jdbc.JdbcConnection.clearWarnings(JdbcConnection.java:708) ~[h2-1.4.199.jar:1.4.199]
	at org.apache.tomcat.jdbc.pool.PooledConnection.clearWarnings(PooledConnection.java:813) ~[tomcat-jdbc-9.0.10.jar:na]
	at org.apache.tomcat.jdbc.pool.ConnectionPool.returnConnection(ConnectionPool.java:937) [tomcat-jdbc-9.0.10.jar:na]
	at org.apache.tomcat.jdbc.pool.ProxyConnection.invoke(ProxyConnection.java:100) [tomcat-jdbc-9.0.10.jar:na]
	at org.apache.tomcat.jdbc.pool.JdbcInterceptor.invoke(JdbcInterceptor.java:108) [tomcat-jdbc-9.0.10.jar:na]
	at org.apache.tomcat.jdbc.pool.interceptor.AbstractCreateStatementInterceptor.invoke(AbstractCreateStatementInterceptor.java:69) [tomcat-jdbc-9.0.10.jar:na]
	at org.apache.tomcat.jdbc.pool.JdbcInterceptor.invoke(JdbcInterceptor.java:108) [tomcat-jdbc-9.0.10.jar:na]
	at org.apache.tomcat.jdbc.pool.DisposableConnectionFacade.invoke(DisposableConnectionFacade.java:81) [tomcat-jdbc-9.0.10.jar:na]
	at com.sun.proxy.$Proxy73.close(Unknown Source) [na:na]
	at org.springframework.boot.devtools.autoconfigure.DevToolsDataSourceAutoConfiguration$NonEmbeddedInMemoryDatabaseShutdownExecutor$InMemoryDatabase.lambda$new$1(DevToolsDataSourceAutoConfiguration.java:140) [spring-boot-devtools-2.1.6.RELEASE.jar:2.1.6.RELEASE]
	at org.springframework.boot.devtools.autoconfigure.DevToolsDataSourceAutoConfiguration$NonEmbeddedInMemoryDatabaseShutdownExecutor$InMemoryDatabase.shutdown(DevToolsDataSourceAutoConfiguration.java:157) [spring-boot-devtools-2.1.6.RELEASE.jar:2.1.6.RELEASE]
	at org.springframework.boot.devtools.autoconfigure.DevToolsDataSourceAutoConfiguration$NonEmbeddedInMemoryDatabaseShutdownExecutor.destroy(DevToolsDataSourceAutoConfiguration.java:103) ~[spring-boot-devtools-2.1.6.RELEASE.jar:2.1.6.RELEASE]
	at org.springframework.beans.factory.support.DisposableBeanAdapter.destroy(DisposableBeanAdapter.java:258) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroyBean(DefaultSingletonBeanRegistry.java:571) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingleton(DefaultSingletonBeanRegistry.java:543) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingleton(DefaultListableBeanFactory.java:1036) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons(DefaultSingletonBeanRegistry.java:504) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingletons(DefaultListableBeanFactory.java:1029) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:1057) ~[spring-context-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:562) ~[spring-context-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140) ~[spring-boot-2.1.6.RELEASE.jar:2.1.6.RELEASE]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:742) ~[spring-boot-2.1.6.RELEASE.jar:2.1.6.RELEASE]
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:389) ~[spring-boot-2.1.6.RELEASE.jar:2.1.6.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:311) ~[spring-boot-2.1.6.RELEASE.jar:2.1.6.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1213) ~[spring-boot-2.1.6.RELEASE.jar:2.1.6.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1202) ~[spring-boot-2.1.6.RELEASE.jar:2.1.6.RELEASE]
	at pt.server.chat.PtServerApplication.main(PtServerApplication.java:12) ~[classes/:na]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_111]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_111]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_111]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_111]
	at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) ~[spring-boot-devtools-2.1.6.RELEASE.jar:2.1.6.RELEASE]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개발환경&lt;/h2&gt;
&lt;p&gt;IDE : Eclipse Java EE Oxygen.2 Release (4.7.2) Build id : 20171218-0600&lt;/p&gt;
&lt;p&gt;OS : Windows10&lt;/p&gt;
&lt;p&gt;Server Framework : Spring Boot 2.1.6 release&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;상황&lt;/h2&gt;
&lt;p&gt;작성자는 Spring Boot 를 사용해서 개발 중이다. 서버 프로젝트 2개를 병합하는 작업을 하던 중에 위 처럼&amp;nbsp;&lt;span style=&quot;color: #ee2323;&quot;&gt;JdbcSQLNonTransientConnectionException &lt;/span&gt;라는 에러가 발생했다. 에러가 발생한 정확한 시점은 (Run As &amp;gt; Java Run Application을 클릭해서) Java 서버 프로그램을 실행시키던 도중 런타임(Runtime)으로 발생한 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 에러가 발생할 수 있는 상황들이 많이 있었기에, 다양한 해결방법들이 있었다. 하지만 현재 작성자가 처한 상황을 해결할 수있는 방법은 찾을 수 없었기에 포스팅을 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;작성자의 pom.xml은 아래와 같다. pom.xml의 정보가 위 에러를 발생시킨 원인과는 전혀 상관이 없지만 참고를 위해 기록한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;&lt;span style=&quot;color: #666666;&quot;&gt;pom.xml&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1569176284255&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;project xmlns=&quot;http://maven.apache.org/POM/4.0.0&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
	xsi:schemaLocation=&quot;http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd&quot;&amp;gt;
	&amp;lt;modelVersion&amp;gt;4.0.0&amp;lt;/modelVersion&amp;gt;
	&amp;lt;parent&amp;gt;
		&amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;spring-boot-starter-parent&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;2.1.6.RELEASE&amp;lt;/version&amp;gt;
		&amp;lt;relativePath/&amp;gt;
	&amp;lt;/parent&amp;gt;
    
	...
    
	&amp;lt;properties&amp;gt;
		&amp;lt;java.version&amp;gt;1.8&amp;lt;/java.version&amp;gt;
		&amp;lt;maven-jar-plugin.version&amp;gt;3.1.1&amp;lt;/maven-jar-plugin.version&amp;gt;
	&amp;lt;/properties&amp;gt;

	&amp;lt;dependencies&amp;gt;
		&amp;lt;dependency&amp;gt;
			&amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
			&amp;lt;artifactId&amp;gt;spring-boot-starter-web&amp;lt;/artifactId&amp;gt;
		&amp;lt;/dependency&amp;gt;
		&amp;lt;dependency&amp;gt;
		    &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
		    &amp;lt;artifactId&amp;gt;spring-boot-starter-data-jpa&amp;lt;/artifactId&amp;gt;
		&amp;lt;/dependency&amp;gt;
		&amp;lt;dependency&amp;gt;
    		&amp;lt;groupId&amp;gt;com.h2database&amp;lt;/groupId&amp;gt;
		    &amp;lt;artifactId&amp;gt;h2&amp;lt;/artifactId&amp;gt;
		    &amp;lt;scope&amp;gt;runtime&amp;lt;/scope&amp;gt;
		&amp;lt;/dependency&amp;gt;
        
		...        
        
	&amp;lt;/dependencies&amp;gt;

	...

&amp;lt;/project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;원인&lt;/h2&gt;
&lt;p&gt;작성자의 경우, &lt;span style=&quot;color: #006dd7;&quot;&gt;@Service(=org.springframework.stereotype.Service)를 작성하지 않았기 때문에 발생&lt;/span&gt;한 것이다. 현재 작성자는 Spring을 사용해서 프로젝트를 아래처럼 Controller와 Service, Repository를 패키지로 나눈 형태로 구조화시켜서 개발 중이다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;b&gt;(개발중인) 프로젝트 패키지 일부&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #666666;&quot;&gt;- com.sample.controller&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&amp;nbsp;&amp;gt; AController.java&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #666666;&quot;&gt;- com.sample.service&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&amp;nbsp;&amp;gt; AService.java&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #666666;&quot;&gt;- com.sample.repository&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&amp;nbsp;&amp;gt; ARepository.java&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;&lt;span style=&quot;color: #666666;&quot;&gt;AController.java&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1569175155329&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import com.sample.service.AService;

@Controller
public class AController {
	@Autowired
	AService aService;
	..
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;i&gt;&lt;span style=&quot;color: #666666;&quot;&gt;AService.java&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1569175346873&quot; class=&quot;java&quot; style=&quot;margin: 20px auto 0px; display: block; overflow: auto; padding: 15px; color: #383a42; background: #f6f7f8; font-size: 14px; border-radius: 3px; font-family: Menlo, Consolas, Monaco, monospace; border: 1px solid #dddddd; cursor: default; z-index: 1;&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import org.springframework.beans.factory.annotation.Autowired;
import com.sample.repository.ARepository;

public class ASerivce {
	@Autowired
	ARepository aRepo;
	..
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;i&gt;&lt;span style=&quot;color: #666666;&quot;&gt;ARepository.java&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1569175238568&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import org.springframework.data.repository.CrudRepository;
import com.sample.data.AItem;

public interface ARepository extends CrudRepository&amp;lt;AItem, Integer&amp;gt; {

}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;위 3가지 java 파일을 보면 조금 잘못된 부분이 있다. AService 클래스 위에 @Service가 없고, AController에서는 AService를 @Autowired해서 사용하고 있다. 다시 말해서, 에러를 발생시킨 코드는 AService 를 Service로 등록시키지 않고 Controller에서 사용하려고 시도했기 때문이었다. &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;해결&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;앞에서 설명했다시피, &lt;span style=&quot;color: #006dd7;&quot;&gt;@Service 어노테이션을 사용해서 ASerivce를 서비스로 등록&lt;/span&gt;시키면 된다. 코드를 수정하면 아래와 같다. 작성자의 경우 작은 실수로 인해 발생한 에러의 원인을 찾아내는데 시간이 한참 걸렸지만, 작성자처럼 이런 실수를 해서 시간을 허비하지 않기를 바란다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;&lt;span style=&quot;color: #456771;&quot;&gt;AService.java&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1569175626920&quot; class=&quot;java&quot; style=&quot;margin: 20px auto 0px; display: block; overflow: auto; padding: 15px; color: #383a42; background: #f6f7f8; font-size: 14px; border-radius: 3px; font-family: Menlo, Consolas, Monaco, monospace; border: 1px solid #dddddd; cursor: default; z-index: 1;&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.sample.repository.ARepository;

@Service
public class ASerivce {
	@Autowired
	ARepository aRepo;
	..
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>error/[err] server</category>
      <category>@Service</category>
      <category>Spring Boot</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/174</guid>
      <comments>https://caileb.tistory.com/174#entry174comment</comments>
      <pubDate>Mon, 23 Sep 2019 03:27:45 +0900</pubDate>
    </item>
    <item>
      <title>android.os.NetworkOnMainThreadException</title>
      <link>https://caileb.tistory.com/173</link>
      <description>&lt;h2&gt;에러(error)&lt;/h2&gt;
&lt;p&gt;&lt;span style=&quot;color: #f41a18;&quot;&gt;android.osNetworkOnMainThreadException&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;error - android.os.NetworkOnMainThreadException.PNG&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MHVuP/btqygTtV9qH/kS8Vdg2JEqOJSFJCKzlwpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MHVuP/btqygTtV9qH/kS8Vdg2JEqOJSFJCKzlwpk/img.png&quot; data-alt=&quot;[ 에러발생 Logcat 화면 ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MHVuP/btqygTtV9qH/kS8Vdg2JEqOJSFJCKzlwpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMHVuP%2FbtqygTtV9qH%2FkS8Vdg2JEqOJSFJCKzlwpk%2Fimg.png&quot; data-filename=&quot;error - android.os.NetworkOnMainThreadException.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;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ 에러발생 Logcat 화면 ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;&lt;span style=&quot;color: #000000;&quot;&gt;에러 상세(detail)&lt;/span&gt;&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;
&lt;p style=&quot;font-size: 0.81em;&quot;&gt;2019-09-12 22:21:09.669 31487-31487/com.example.joker E/AndroidRuntime: FATAL EXCEPTION: main&lt;br /&gt;Process: com.example.joker, PID: 31487&lt;br /&gt;&lt;span style=&quot;color: #f41a18;&quot;&gt;android.os.NetworkOnMainThreadException&lt;/span&gt;&lt;br /&gt;at &lt;span style=&quot;color: #f41a18;&quot;&gt;android.os.StrictMode&lt;/span&gt;$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1448)&lt;br /&gt;at &lt;span style=&quot;color: #f41a18;&quot;&gt;java.net.SocketOutputStream.socketWrite&lt;/span&gt;(SocketOutputStream.java:108)&lt;br /&gt;at &lt;span style=&quot;color: #f41a18;&quot;&gt;java.net.SocketOutputStream.write&lt;/span&gt;(SocketOutputStream.java:153)&lt;br /&gt;at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221)&lt;br /&gt;at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:291)&lt;br /&gt;at sun.nio.cs.StreamEncoder.implFlush(StreamEncoder.java:295)&lt;br /&gt;at sun.nio.cs.StreamEncoder.flush(StreamEncoder.java:141)&lt;br /&gt;at java.io.OutputStreamWriter.flush(OutputStreamWriter.java:229)&lt;br /&gt;at java.io.BufferedWriter.flush(BufferedWriter.java:254)&lt;br /&gt;at com.example.joker.SubThread.send(SubThread.java:48)&lt;br /&gt;at com.example.joker.MainActivity.onClick(MainActivity.java:93)&lt;br /&gt;at android.view.View.performClick(View.java:6897)&lt;br /&gt;at android.widget.TextView.performClick(TextView.java:12693)&lt;br /&gt;at android.view.View$PerformClick.run(View.java:26104)&lt;br /&gt;at android.os.Handler.handleCallback(Handler.java:789)&lt;br /&gt;at android.os.Handler.dispatchMessage(Handler.java:98)&lt;br /&gt;at android.os.Looper.loop(Looper.java:164)&lt;br /&gt;at android.app.ActivityThread.main(ActivityThread.java:6944)&lt;br /&gt;at java.lang.reflect.Method.invoke(Native Method)&lt;br /&gt;at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)&lt;br /&gt;at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;개발환경(environment)&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;OS : windows 10&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;IDE : android studio 3.5&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Device : samsung SM-G930S (갤럭시 S7)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;상황(situation)&lt;/h2&gt;
&lt;p&gt;소켓(Socket) 통신을 사용하는 안드로이드 앱을 개발 중에, 앱을 종료시켜버리는 Fatal Exception이 발생했다. 에러가 발생한 시점은 &lt;span style=&quot;color: #0051a1;&quot;&gt;소켓에 연결된 곳으로 데이터를 쓰려고(writing) 시도할 때 발생&lt;/span&gt;한 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;원인(cause)&lt;/h2&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;소켓(Socket) 통신을 사용해서 데이터를 쓰기(write)와 읽기(read)를 할 때 작업을 안드로이드 메인 스레드(main thread)와 분리시켜서 실행하지 않아서 발생한 것&lt;/span&gt;이다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;안드로이드 앱을 개발할 때 주의할 점 중 하나는 모든 작업을 메인 스레드(main thread)에서 처리하면 안된다는 것&lt;/span&gt;이다. 시간이 걸리는 작업들은 메인 스레드에서 분리시켜서 실행시켜야 한다. 만약, 메인 스레드를 오랫동안 잡고 놓지 않을 만한 어떤 작업(ex, socket, http 통신의 reading, writing)이 있다면, 안드로이드는 위 처럼 에러가 발생시킨다. 참고로 위 에러는 컴파일(compile) 중에 발생하는 에러가 아닌, 런타임(runtime) 에러이기 때문에, 앱을 실행하는 도중 발생할 수 있는 에러이다.&lt;br /&gt;&lt;span style=&quot;color: #808080;&quot;&gt;※ 안드로이드에서 메인 스레드(Main thread)에서만 UI작업을 할 수 있도록 제한했기 때문에, 메인 스레드를 UI 스레드(UI thread)라고 부르기도 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;안드로이드에서 말하는 메인 스레드를 이해하기 위해 아래 그림을 추가했다. 그림과 함께 두 가지만 이해한다면, 메인스레드를 고려해서 코딩하는 것이 어렵지 않을 것이라고 생각된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;error - android.os.NetworkOnMainThreadException concept.PNG&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLIYgS/btqyg9J0Dts/POMkvSt8P2luFq31171oT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLIYgS/btqyg9J0Dts/POMkvSt8P2luFq31171oT0/img.png&quot; data-alt=&quot;[ 안드로이드 메인 스레드 이해 ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLIYgS/btqyg9J0Dts/POMkvSt8P2luFq31171oT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLIYgS%2Fbtqyg9J0Dts%2FPOMkvSt8P2luFq31171oT0%2Fimg.png&quot; data-filename=&quot;error - android.os.NetworkOnMainThreadException concept.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;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ 안드로이드 메인 스레드 이해 ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&lt;b&gt;첫째, 메인 스레드(Main thread)에서 통신을 하지 않는다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;안드로이드에서 말하는 메인 스레드는 개발자가 따로 추가하지 않아도 안드로이드에 의해 앱에게 알아서 할당시켜주는 스레드를 말한다. 쉽게 말해서, 우리가 onCreate()이나 onResume() 안에 작성하고 있던 코드들은 모두 메인 스레드에서 동작하는 것이다. 이런 영역에서는 오래걸리는 작업을 하면 안되기 때문에, 통신을 해야하는 코드가 있다면 반드시 별도의 스레드로 작업을 분리시켜주어야 한다. 예를 들어, 데이터를 읽고 쓰기 위해 소켓(Socket)통신이나 HTTP 통신을 한다면 분리시켜주어야 한다. 그렇지 않으면 위 에러를 보게 될 것이다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&lt;b&gt;둘째, 메인이 아닌 다른 스레드(Sub threads)에서 UI 작업을 하지 않는다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;안드로이드 개발 중에 화면의 정보를 변경해야하는 작업이 있다면, 반드시 메인 스레드를 통해서 작업을 해야한다. 안드로이드에서 동작하는 모든 UI는 하나의 스레드에서 관리되도록 만들어져 있다. 따라서, 앱을 개발하다보면 코드가 많아질텐데, 아무리 코드가 많아진다고 하더라도 화면 정보인 UI를 변경하는 코드들을 보면, 모두 메인 스레드에서 처리하도록 코딩되어 있는 것을 볼 수 있다. 만약에, UI 작업을 메인 스레드가 아닌 곳에서 수행하려한다면 역시 (위 에러와는 다른) 에러가 발생할 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;해결(solution)&lt;/h2&gt;
&lt;p&gt;개발 중인 안드로이드 코드의 &lt;span style=&quot;color: #0051a1;&quot;&gt;소켓(Socket) 통신 내용 중에 데이터를 전달하는 부분을 별도의 스레드로 처리하도록 변경하면, 에러는 발생하지 않는다&lt;/span&gt;. 안드로이드에서는 별도의 스레드로 작업을 수행할 때 보통 Handler, AsynkTask, Thread 이렇게 3가지를 사용하게 된다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&lt;b&gt;java.lang.Thread&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&lt;b&gt;android.os.Handler&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&lt;b&gt;android.os.AsyncTask&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;아래 코드는 현재 개발 중인 코드에서 이번 에러(NetworkOnMainThreadException)를 발생시킨 부분이다. 메인 스레드에서 실행시킨 코드를 별도의 스레드를 만들어서 처리하도록 수정했다. 참고로, &lt;span style=&quot;color: #0051a1;&quot;&gt;작성자는 Thread를 사용했지만, 상황에 따라 Handler나 AsynTask를 사용해도 상관없다&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;기존 코드&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1568300185486&quot; class=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class MainActivity extends Activity implements OnClickListener {

	SubThread subThread;
	...
    
	@Override
	public void onClick(View v) {
		if(v.getId()==R.id.btn_send){
			subThread.send(editText.getText().toString());
		}
	}
}

public class SubThread extends Thread {

	BufferedWriter bw;
	...

	public void send(String data){
		...
   		bw.write(data);
		bw.flush();
		...
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&lt;b&gt;수정 코드&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1568300194604&quot; class=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class MainActivity extends Activity implements OnClickListener {

	SubThread subThread;
	...
    
	@Override
	public void onClick(View v) {
		if(v.getId()==R.id.btn_send){
			new Thread() {
				public void run() {
					subThread.send(editText.getText().toString());
				}
			}.start();
		}
	}
}

public class SubThread extends Thread {

	BufferedWriter bw;
	...

	public void send(String data){
		...
		bw.write(data);
		bw.flush();
		...
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>error/[err] android</category>
      <category>Main Thread</category>
      <category>NetworkOnMainThreadException</category>
      <category>SOCKET</category>
      <category>UI Thread</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/173</guid>
      <comments>https://caileb.tistory.com/173#entry173comment</comments>
      <pubDate>Fri, 13 Sep 2019 00:03:51 +0900</pubDate>
    </item>
    <item>
      <title>락업(Lockup), 언락(Unlock)</title>
      <link>https://caileb.tistory.com/172</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot;&gt;락업(Lockup), 언락(Unlock)&lt;/h2&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dbfC7t/btqxTLYZdHt/XHVvvvEC3mdIsk8KyrCta0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dbfC7t/btqxTLYZdHt/XHVvvvEC3mdIsk8KyrCta0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dbfC7t/btqxTLYZdHt/XHVvvvEC3mdIsk8KyrCta0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdbfC7t%2FbtqxTLYZdHt%2FXHVvvvEC3mdIsk8KyrCta0%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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;락업(Lockup)&lt;/b&gt;은 코인을 발행할 때 가상화폐시장에 유통되지 않도록 묶어놓은 것으로, 가상화폐의 기술인 블록체인 용어 중 하나입니다. 블록체인에서의 락업은 주식을 일정기간 팔지 못하도록 하는 &quot;보호예수&quot;와 비슷한 개념입니다. 락업을 통해 코인이 사용되지 못하도록 묶여 있을 때의 코인을 &lt;b&gt;동결된 코인(Frozen Coin)&lt;/b&gt;이라고 부르기도 하며, 동결된 코인이 들어있는 계좌를 &lt;b&gt;동결된 계좌(Freeze Account)&lt;/b&gt; 혹은 &lt;b&gt;에크스로 계좌(Escrow Account)&lt;/b&gt;라고 부르기도 합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;코인을 &lt;u&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;락업하려는 사람들은 해당 블록체인에 투자한 투자자(Investor)와 해당 블록체인을 개발하는 개발자(Developer)&lt;/span&gt;&lt;/u&gt;입니다. 따라서, &lt;u&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;락업을 하는 주 이유는 투자자와 개발자 사이의 신뢰를 위한 것&lt;/span&gt;&lt;/u&gt;입니다. 이번 포스팅에서는 락업(Lockup)과 언락(Unlock, 락업 해제)의 의미를 알기 위해, 코인을 동결시키는 행동이 가상화폐시장에 어떤 영향을 주는지 살펴보겠습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;※ 에스크로 계좌(Escrow Account)란? &quot;조건부 날인증서&quot;라는 에스크로와 계좌를 합친 단어로, 블록체인에서는 계좌에 특정 조건을 걸어놓고 입출금의 제한을 걸어둔 계좌를 말합니다. 보통 에스크로 계좌에 토큰을 입금을 한 후, 특정 조건을 두어서 출금에 대한 제한을 합니다. 이 개념은 블록체인 이전에 있던 이미 사용되던 개념이며, 두 사람 사이에 제 3자가 개입해서 안전한 거래를 하는 의미였습니다. 기존에서는 제 3자의 역할이 사람이나 기관이었지만, 여기서는 블록체인이라는 것만 다릅니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;락업을 하는 이유&lt;/h2&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&lt;b&gt;1. Protection from speculators (투기꾼들로부터 보호)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&quot;투기꾼이 아닌 투자자를 모으기 위해, 코인의 매각에 제한을 둔다.&quot;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;가상화폐 투기꾼들은 주로 할인된 코인의 초기가격으로 대량 매입한 후, 크라우드 세일(Crowd Sale)이후에 모두 팔아버리면서 수익을 챙깁니다. 그렇게 되면 시장에서 해당 코인의 공급이 갑자기 많아지면서 가격은 내려갈 것입니다. 이 상황이 (현재 투자자는 아니지만 앞으로 투자자가 될 수 있는)잠재적 투자자들에게는 관심을 가지지 못하도록 만들 것입니다. 만약에, 누군가 가상화폐 시장에 투자하려고 한다면 비트코인(BitCoin), 이더리움(Ethereum), 리플(Ripple), 이오스(EOS) 처럼 가상화폐 시장에서 유명한 코인들에 투자하거나 혹은 등장한지 얼마 안됬더라도 가치가 어느정도 유지되는 코인에 투자하려고 할 것입니다. 가상화폐 시장에 등장하자마자 가치가 떨어져서 사라질수도 있는 코인에 투자하려고는 하지 않을 것이기 때문입니다. 따라서 이를 방지하기 위해 수요에 맞게 유통시키고 일부는 동결시킬 필요가 있고, 투자자들에게 전달된 코인들을 일정기간동안 동결시킬 필요가 있는 것입니다. 이렇게 되면 스타트업(Startup) 회사들에게는 투기꾼들 같은 단기 투자자(short-term)들보다 장기 투자자(long-term investor)들을 끌어들일 수 있고, 프로젝트의 가치를 제대로 인정받을 수 있도록 보호장치를 마련해주는 역할도 할 수 있게 되는 것입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&lt;b&gt;2. Motivation for the team (개발팀의 동기부여)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&quot;자신들이 가져갈 동결된 코인의 가치를 올리기 위해, 개발팀은 계속 노력을 할 것이다.&quot;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;블록체인 프로젝트의 숨겨진 목적은 개발팀들이 돈을 벌기 위함이 목적입니다. 처음부터 그들은 돈을 벌 생각으로 만든 것이고, 돈을 벌지 못한다면 애초에 프로젝트를 시작하지 않았을 것입니다. ICO를 통해 투자받은 돈을 개발팀에게 바로 전달하는 것이 아닌, 돈의 일부는 전달하고 나머지 일부는 동결시킨 후 1년뒤에 자동으로 전달되도록 장치를 마련해 둔다면, 개발팀이 돈을 받자마자 프로젝트를 중단하고 도망가버리는 상황을 방지할 수 있을 것입니다. 또한, 개발팀은 자신들이 가지고 있는 동결된 코인의 가치를 떨어뜨리는 행위를 하진 않을 것입니다. 1년뒤 동결상태가 해제됬을 때 가치가 떨어져 있다면 그들이 받았을 때의 돈은 적어지므로 결과적으로 많이 벌수 없기 때문입니다. 따라서 그들은 동결된 코인의 가치가 오르기 위해 백서(White Paper)에 언급한대로 개발을 진행할 것이고, 여력이 된다면 그 보다 더 좋게 만들려고 시도할 것입니다. 그렇게 되면 1년뒤 개발팀이 받아야하는 코인의 가치가 늘어나서 높은 수익을 받을 수 있을 것입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&lt;b&gt;3. Sifting out the scammers (스캠 선별)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&quot;스캠이 아니라면, 락업 계획이 잘 세워졌을 것이고, 잘 지켜지고 있을 것이다.&quot;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;블록체인 프로젝트를 진행할 때는 ICO를 통해 투자를 받습니다. 이 때 많이 일어나는 상황으로는 투자받은 돈을 가지고 도망가거나 잠적해버리는 일입니다. 이런 사기 프로젝트로 만들어진 코인을 &lt;u&gt;&lt;span style=&quot;color: #1b9ae6;&quot;&gt;&lt;a style=&quot;color: #1b9ae6;&quot; href=&quot;https://caileb.tistory.com/123?category=777380&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;스캠(Scam)&lt;/a&gt;&lt;/span&gt;&lt;/u&gt;이라고 합니다. 락업을 장기간으로 설정함으로써, 프로젝트를 만드는 개발자(developer), 설립자(founder) 그리고 고문(advisor)들이 잠적해버리는 상황을 방지할 수 있습니다. 그 이유는 통해 투자받은 사람들(=사기치려는 사람들)이 아무리 스캠 코인을 가지고 있더라도 시장에서 유통되지 못하도록 막혀있다면, 그들은 자신이 가지고 있는 코인을 현금화시킬 수 없기 때문입니다. 따라서, 락업이 해당 프로젝트가 스캠인지 선별하기 위해 도움을 줄 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&lt;b&gt;4. Uniform distribution of currency (균일한 통화분배, 코인가격 안정화)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&quot;수요와 공급의 규칙에 맞게 분배하면, 코인가격은 안정화될 것이다.&quot;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;만약에, ICO가 종료된 후 대량의 코인을 한번에 풀려버리면 코인시장을 망가뜨릴 수 있습니다. 해당 블록체인을 사용하려는 사람들이나 투자하려는 사람들은 별로 없는데, 코인 물량이 한번에 대량으로 시장에 풀려버린다면 코인 가치는 급격하게 떨어질 수 있습니다. 참고로, 코인들 중에 가격이 낮은 코인들을 동전코인이라고도 부르는데 이러한 동전코인이 된 후 작전세력들의 대상이 된다면 해당 프로젝트로 만들어진 코인의 가치는 그들에 의해 움직이게 될 수 있습니다. 이는 주식시장에서 동전주에 작전세력이 많은 것과 비슷한 상황입니다. 만약에 락업을 통해 일정간격으로 혹은 상황에 맞게 단계적으로 균일하게 코인을 풀어서 시장의 수용에 맞게 공급한다면, 코인 유통량에 대한 유동성을 확보할 수 있고, 코인가격은 안정되게 운영될 것입니다.&lt;/p&gt;</description>
      <category>understanding/blockchain</category>
      <category>동결</category>
      <category>락업</category>
      <category>언락</category>
      <category>에스크로계좌</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/172</guid>
      <comments>https://caileb.tistory.com/172#entry172comment</comments>
      <pubDate>Mon, 2 Sep 2019 02:45:56 +0900</pubDate>
    </item>
    <item>
      <title>c++ (class template) variant</title>
      <link>https://caileb.tistory.com/171</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot;&gt;(class template) variant&lt;/h2&gt;
&lt;h4 style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;std::variant&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;subject.PNG&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xSbhg/btqxGiomqHg/IjBY52uTsszUEG1AxGBUNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xSbhg/btqxGiomqHg/IjBY52uTsszUEG1AxGBUNk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xSbhg/btqxGiomqHg/IjBY52uTsszUEG1AxGBUNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxSbhg%2FbtqxGiomqHg%2FIjBY52uTsszUEG1AxGBUNk%2Fimg.png&quot; data-filename=&quot;subject.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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;u&gt;&lt;span&gt;variant&lt;/span&gt;는 공용체의 진화된 버전으로&lt;span&gt;, &lt;/span&gt;타입 세이프한 공용체&lt;/u&gt;라고 말할 수 있다&lt;span&gt;. union(&lt;/span&gt;공용체&lt;span&gt;)&lt;/span&gt;는 들어있는 타입이 무엇인지 알 수 없지만&lt;span&gt;, variant(&lt;/span&gt;변형&lt;span&gt;)&lt;/span&gt;은 들어있는 타입을 유지해주기 때문에 타입에 안전한 공용체라는 의미로 &lt;span&gt;type-safe union&lt;/span&gt;&lt;span&gt; &lt;/span&gt;혹은 &lt;span&gt;safer union&lt;/span&gt;라고 불리기도 한다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;span&gt;variant 특징&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;&lt;span&gt;1) union&lt;/span&gt;처럼 행동하지만&lt;span&gt;, &lt;/span&gt;타입에 안전하다&lt;span&gt;.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;: &lt;/span&gt;가장 큰 특징으로&lt;span&gt;, &lt;/span&gt;타입을 유지하는 것 외에는 &lt;span&gt;union&lt;/span&gt;과 모두 동일하다&lt;span&gt;. &lt;u&gt;union&lt;/u&gt;&lt;/span&gt;&lt;u&gt;은 타입을 정의할 때 사용할 여러개의 타입을 함께 작성&lt;/u&gt;해서 사용하지만&lt;span&gt;, &lt;u&gt;variant&lt;/u&gt;&lt;/span&gt;&lt;u&gt;는 타입에 대한 선언과 정의를 동시&lt;/u&gt;에 하며 이때 여러 개의 타입을 함께 작성한다&lt;span&gt;. &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span&gt;2) &lt;/span&gt;템플릿 인수 타입 중 하나이어야 한다&lt;span&gt;.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;: variant &lt;/span&gt;변수를 선언했다고 모든 타입으로 변형이 가능한 것은 아니다&lt;span&gt;. variant &lt;/span&gt;변수를 선언할 때 개발자는 &lt;u&gt;해당 &lt;span&gt;variant &lt;/span&gt;변수 내에서 변형 가능한 타입들을 함께 작성&lt;/u&gt;해야 한다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span&gt;3) &lt;/span&gt;방문자 패턴&lt;span&gt;(visitor pattern)&lt;/span&gt;에 자주 사용된다&lt;span&gt;.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;: variant &lt;/span&gt;타입은 방문자 패턴인 &lt;span&gt;visit &lt;/span&gt;함수과 함께 사용되는 경우가 정말 많다&lt;span&gt;. variant&lt;/span&gt;가 방문자 패턴에서도 사용할 수 있도록 잘 설계되었기 때문에 쿵짝이 잘 맞는다&lt;span&gt;. &lt;/span&gt;참고로&lt;span&gt;, &lt;/span&gt;최신 스타일의 코딩방법이라 불리는 모던 &lt;span&gt;C++&lt;/span&gt;에서도 소개된 방법이다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;※ C++17 부터 지원한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;span&gt;코드로 variant 이해하기&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;특징&lt;span&gt; 1) union&lt;/span&gt;처럼 행동하지만&lt;span&gt;, &lt;/span&gt;타입에 안전하다&lt;span&gt;.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;아래 예제는 &lt;span&gt;variant&lt;/span&gt;타입의 변수 &lt;span&gt;v&lt;/span&gt;에 &lt;span&gt;int&lt;/span&gt;타입의 값인 &lt;span&gt;10&lt;/span&gt;을 넣은 후&lt;span&gt;, double&lt;/span&gt;타입의 값은 &lt;span&gt;3.14&lt;/span&gt;를 넣었다&lt;span&gt;. &lt;/span&gt;두 타입은 다르지만 기존 &lt;span&gt;union&lt;/span&gt;타입처럼 &lt;span&gt;variant&lt;/span&gt;타입의 변수에서도 저장이 가능하다&lt;span&gt;. &lt;/span&gt;그리고 여기서 한가지 추가 설명을 하자면&lt;span&gt;, variant&lt;/span&gt;변수에 두번째 값인 &lt;span&gt;3.14&lt;/span&gt;를 넣을 때&lt;span&gt;, &lt;/span&gt;기존에 넣었던 첫번째 값인 &lt;span&gt;10&lt;/span&gt;을 파기해서 초기화 한 후 두번째 값을 넣는다는 것이다&lt;span&gt;. 참고로, variant 에서 제공하는 함수 중 index()는 variant 내에 정의되어 있는 type의 위치 index를 반환한다. 따라서 실행 결과는 int는 0, double은 1의 위치가 출력된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1566495265910&quot; class=&quot;c++ cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;utility&amp;gt;
#include &amp;lt;variant&amp;gt;

int main() {
  std::variant&amp;lt;int, double&amp;gt; v = 10;
  std::cout &amp;lt;&amp;lt; v.index() &amp;lt;&amp;lt; std::endl;
  
  v = 3.14;
  std::cout &amp;lt;&amp;lt; v.index() &amp;lt;&amp;lt; std::endl;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;실행 결과 :&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1566496242687&quot; class=&quot;html xml&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;0
1&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;특징&lt;span&gt; 2) &lt;/span&gt;템플릿 인수 타입 중 하나이어야 한다&lt;span&gt;.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;variant&lt;/span&gt;변수를 선언할 때 템플릿 안에 변형가능한 타입을 함께 작성해야 한다&lt;span&gt;. &lt;/span&gt;그리고 사용할 때는 작성한 타입들에 한해서만 값을 저장할 수 있다&lt;span&gt;. &lt;/span&gt;아래 예제에서는 &lt;span&gt;variant&lt;/span&gt;변수가 &lt;span&gt;int&lt;/span&gt;와 &lt;span&gt;double&lt;/span&gt;타입의 값만 가질 수 있도록 했기 때문에&lt;span&gt;, string&lt;/span&gt;처럼 다른 타입을 저장하려고 시도한다면 에러가 발생할 것이다&lt;span&gt;. &lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1566495278726&quot; class=&quot;c++ cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;utility&amp;gt;
#include &amp;lt;variant&amp;gt;

int main() {
    std::variant&amp;lt;int, double&amp;gt; v;
    v = 123;
    v = &quot;hello&quot;; // error
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;실행 결과 :&lt;/p&gt;
&lt;pre id=&quot;code_1566496314263&quot; class=&quot;html xml&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;에러 발생&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;특징&lt;span&gt; 3) &lt;/span&gt;방문자 패턴&lt;span&gt;(visitor pattern)&lt;/span&gt;에 자주 사용된다&lt;span&gt;.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;variant(=std::variant)&amp;nbsp;&lt;/span&gt;타입은 &lt;span&gt;visit(=std::visit)&lt;/span&gt;과 함께 자주 사용되기 때문에 알아두면 도움이 될 것이다&lt;span&gt;. &lt;/span&gt;아래는 방문자 패턴을 위해 &lt;span&gt;type-matching visitor&lt;/span&gt;를 구현하는 몇가지 방법을 소개한 것이다&lt;span&gt;. &lt;/span&gt;&lt;u&gt;세가지 방법을 예로 들었지만&lt;span&gt;, &lt;/span&gt;아래 그림처럼 모두 동일한 처리를 하는 것을 뿐 코딩 형태만 다른 것&lt;/u&gt;이다&lt;span&gt;. variant&lt;/span&gt;변수에 들어갈 수 있는 타입이 여러 개가 가능한 구조이기 때문에&lt;span&gt;, &lt;/span&gt;각 타입에서 맞게 직접 처리할 수 있도록 구조를 분리시킨 방문자 패턴과 찰떡궁합인 것이다&lt;span&gt;. &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;body.PNG&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/owehl/btqxECnPwXF/Yxqx33ZyafC4JJvKNTiA81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/owehl/btqxECnPwXF/Yxqx33ZyafC4JJvKNTiA81/img.png&quot; data-alt=&quot;variant &amp;amp;amp;amp; visitor pattern&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/owehl/btqxECnPwXF/Yxqx33ZyafC4JJvKNTiA81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fowehl%2FbtqxECnPwXF%2FYxqx33ZyafC4JJvKNTiA81%2Fimg.png&quot; data-filename=&quot;body.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;/&gt;&lt;/span&gt;&lt;figcaption&gt;variant &amp;amp; visitor pattern&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래 소개된 예들을 참고해서 개발할 때 상황에 맞게 알맞은 스타일로 코딩을 하면된다&lt;span&gt;. &lt;/span&gt;&lt;u&gt;참고로&lt;span&gt;,&lt;/span&gt;아래 예시코드들에서 &lt;span&gt;visitor&lt;/span&gt;들은 리턴을 하지 않고&lt;span&gt;, &lt;/span&gt;입력 인자를 &lt;span&gt;1&lt;/span&gt;개로 사용한 것이다&lt;span&gt;. &lt;/span&gt;필요하다면 예시를 약간 수정해서 리턴을 하도록 변경하거나 입력 인자를 &lt;span&gt;2&lt;/span&gt;개&lt;span&gt;, 3&lt;/span&gt;개 등으로 변경하는데 어렵지 않을 것이라 생각된다&lt;span&gt;.&lt;/span&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;첫번째 방법 : 람다(lambda)식 표현을 사용해서 각 타입을 다르게 처리&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1566495293892&quot; class=&quot;c++ cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;utility&amp;gt;
#include &amp;lt;variant&amp;gt;

struct Ironman {};
struct Batman {};
struct Spiderman {};

int main() {
    std::variant&amp;lt;Ironman, Batman, Spiderman&amp;gt; jk{Batman()};
    std::visit([](auto&amp;amp;&amp;amp; args) {
        using T = std::decay_t&amp;lt;decltype(args)&amp;gt;;
        if constexpr (std::is_same_v&amp;lt;T, Ironman&amp;gt;)
          std::cout &amp;lt;&amp;lt; &quot;Ironman visitor&quot; &amp;lt;&amp;lt; std::endl;
        else if constexpr (std::is_same_v&amp;lt;T, Batman&amp;gt;)
          std::cout &amp;lt;&amp;lt; &quot;Batman visitor&quot; &amp;lt;&amp;lt; std::endl;
        else if constexpr (std::is_same_v&amp;lt;T, Spiderman&amp;gt;)
          std::cout &amp;lt;&amp;lt; &quot;Spiderman visitor&quot; &amp;lt;&amp;lt; std::endl;
    }, jk);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;두번째 방법 : operator를 overload하는 object를 사용&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1566495307981&quot; class=&quot;html xml&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;utility&amp;gt;
#include &amp;lt;variant&amp;gt;

struct Ironman {};
struct Batman {};
struct Spiderman {};

struct Joker {
    void operator()(Ironman &amp;amp;obj) { std::cout &amp;lt;&amp;lt; &quot;Ironman's visitor&quot; &amp;lt;&amp;lt; std::endl; }
    void operator()(Batman &amp;amp;computer) { std::cout &amp;lt;&amp;lt; &quot;Batman's visitor&quot; &amp;lt;&amp;lt; std::endl; }
    void operator()(Spiderman &amp;amp;computer) { std::cout &amp;lt;&amp;lt; &quot;Spiderman's visitor&quot; &amp;lt;&amp;lt; std::endl; }
};

int main() {
    std::variant&amp;lt;Ironman, Batman, Spiderman&amp;gt; jk{Batman()};
    std::visit(Joker(), jk);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;세번째 방법 : operator를 overload하는 templete을 사용&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1566495328148&quot; class=&quot;html xml&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;utility&amp;gt;
#include &amp;lt;variant&amp;gt;

template &amp;lt;class ...Ts&amp;gt; struct overload : Ts...{using Ts::operator()...; };
template &amp;lt;class ...Ts&amp;gt; overload(Ts...)-&amp;gt;overload&amp;lt;Ts...&amp;gt;;

struct Ironman {};
struct Batman {};
struct Spiderman {};

int main() {
    std::variant&amp;lt;Ironman, Batman, Spiderman&amp;gt; jk{Batman()};
    std::visit(overload{
      [](Ironman&amp;amp;) { std::cout &amp;lt;&amp;lt; &quot;Ironman's visitor&quot; &amp;lt;&amp;lt; std::endl; },
      [](Batman&amp;amp;) { std::cout &amp;lt;&amp;lt; &quot;Batman's visitor&quot; &amp;lt;&amp;lt; std::endl; },
      [](Spiderman&amp;amp;) { std::cout &amp;lt;&amp;lt; &quot;Spiderman's visitor&quot; &amp;lt;&amp;lt; std::endl; }
    }, jk);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;실행결과 (첫번째, 두번째, 세번째 모두 동일)&lt;/p&gt;
&lt;pre id=&quot;code_1566496381118&quot; class=&quot;html xml&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Batman's visitor&lt;/code&gt;&lt;/pre&gt;</description>
      <category>language/c++</category>
      <category>C++17</category>
      <category>union</category>
      <category>VARIANT</category>
      <category>visitor</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/171</guid>
      <comments>https://caileb.tistory.com/171#entry171comment</comments>
      <pubDate>Fri, 23 Aug 2019 02:41:39 +0900</pubDate>
    </item>
    <item>
      <title>c++ / 분수(Fraction) 사칙연산하기 (struct&amp;amp; 사용)</title>
      <link>https://caileb.tistory.com/170</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot;&gt;분수(Fraction) 사칙연산하기 (struct&amp;amp; 사용)&lt;/h2&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;c++ 언어 사용&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;문제&amp;nbsp;:&amp;nbsp;&lt;/h3&gt;
&lt;p&gt;구조체(struct)를 이용해서 분수 값들간의 사칙연산(Operations&amp;nbsp;with&amp;nbsp;Fractions)을 수행하는 함수를 구현한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;조건&amp;nbsp;:&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;1. 구조체(struct)를 사용한다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;2. 두 값의 사칙연산(덧셈, 뺄셈, 곱셈, 나눗셈)에 대한 각 함수를 구현한다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;3. 두 값의 비교연산을 하는 함수를 구현한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;4. &lt;span style=&quot;color: #f41a18;&quot;&gt;주소에 의한 참조(call by reference)를 사용한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;풀이&amp;nbsp;코드&amp;nbsp;:&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;fraction.hpp&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1565523544596&quot; class=&quot;c++ cpp&quot; style=&quot;margin: 20px auto 0px; display: block; overflow: auto; padding: 15px; color: #383a42; background: #f6f7f8; font-size: 14px; border-radius: 3px; font-family: Menlo, Consolas, Monaco, monospace; border: 1px solid #dddddd; cursor: default; z-index: 1;&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;typedef struct Fraction{
	int bunja;
	int bunmo;
} Fraction;

void addFraction(Fraction &amp;amp;x, Fraction &amp;amp;y, Fraction &amp;amp;z);
void subFraction(Fraction &amp;amp;x, Fraction &amp;amp;y, Fraction &amp;amp;z);
void multFraction(Fraction &amp;amp;x, Fraction &amp;amp;y, Fraction &amp;amp;z);
void divFraction(Fraction &amp;amp;x, Fraction &amp;amp;y, Fraction &amp;amp;z);
bool compare(Fraction x, Fraction y);
void trimFraction(Fraction &amp;amp;x);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;faction.cpp&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1565524089164&quot; class=&quot;c++ cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;fraction.hpp&quot;

void addFraction(Fraction &amp;amp;x, Fraction &amp;amp;y, Fraction &amp;amp;z){
	z.bunmo = x.bunmo * y.bunmo;
	z.bunja = y.bunmo * x.bunja + x.bunmo * y.bunja;
}
void subFraction(Fraction &amp;amp;x, Fraction &amp;amp;y, Fraction &amp;amp;z){
	z.bunmo = x.bunmo * y.bunmo;
	z.bunja = y.bunmo * x.bunja - x.bunmo * y.bunja;
}
void multFraction(Fraction &amp;amp;x, Fraction &amp;amp;y, Fraction &amp;amp;z){
	z.bunmo = x.bunmo * y.bunmo;
	z.bunja = y.bunja * x.bunja;
}
void divFraction(Fraction &amp;amp;x, Fraction &amp;amp;y, Fraction &amp;amp;z){
	z.bunmo = x.bunmo * y.bunja;
	z.bunja = y.bunmo * x.bunja;
}
bool compare(Fraction x, Fraction y){
	if(y.bunmo * x.bunja - x.bunmo * y.bunja &amp;gt; 0)
		return 1;
	else
		return 0;
}
void trimFraction(Fraction &amp;amp;x){
	int down = x.bunja, up = x.bunmo, com;
	if(x.bunja &amp;gt; x.bunmo){
		down = x.bunmo;
		up = x.bunja;
	}

	for(int i=1; i&amp;lt;=down; i++)
		if((down%i)==0 &amp;amp;&amp;amp; (up%i)==0)
			com=i;

	x.bunja/=com;
	x.bunmo/=com;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;main.cpp&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1565524104444&quot; class=&quot;c++ cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include&amp;lt;iostream&amp;gt;
#include &quot;fraction.hpp&quot;

using namespace std;

int main(){
	Fraction a, b, c;
	bool bi;

	a.bunja=5, a.bunmo=6;
	b.bunja=1, b.bunmo=3;

	printf(&quot;a = %d / %d\n&quot;, a.bunja, a.bunmo);
	printf(&quot;b = %d / %d\n\n&quot;, b.bunja, b.bunmo);

	addFraction(a,b,c);
	trimFraction(c);
	printf(&quot;더하기\t%d / %d\n&quot;, c.bunja, c.bunmo);
	
	subFraction(a,b,c);
	trimFraction(c);
	printf(&quot;빼기\t%d / %d\n&quot;, c.bunja, c.bunmo);
	
	multFraction(a,b,c);
	trimFraction(c);
	printf(&quot;곱하기\t%d / %d\n&quot;, c.bunja, c.bunmo);
	
	divFraction(a,b,c);
	trimFraction(c);
	printf(&quot;나누기\t%d / %d\n&quot;, c.bunja, c.bunmo);

	bi = compare(a,b);
	if(bi==1)
		printf(&quot;비교(큰 수) = %d / %d\n&quot;, a.bunja, a.bunmo);
	else	
		printf(&quot;비교(큰 수) = %d / %d\n&quot;, b.bunja, b.bunmo);

	return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;실행 결과&amp;nbsp;:&amp;nbsp;&lt;/h3&gt;
&lt;pre id=&quot;code_1565523544596&quot; class=&quot;html xml&quot; style=&quot;margin: 20px auto 0px; display: block; overflow: auto; padding: 15px; color: #383a42; background: #f6f7f8; font-size: 14px; border-radius: 3px; font-family: Menlo, Consolas, Monaco, monospace; border: 1px solid #dddddd; cursor: default; z-index: 1;&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;a = 5 / 6
b = 1 / 3

더하기  7 / 6
빼기    1 / 2
곱하기  5 / 18
나누기  5 / 2
비교(큰 수) = 5 / 6&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;풀이&amp;nbsp;설명&amp;nbsp;:&lt;/h3&gt;
&lt;p&gt;이 예제의 목적은 값 의한 호출(call by value)와 참조에 의한 호출(call by reference)의 차이를 알기 위함이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;callbyvalue.PNG&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UdP69/btqxqKdAeOX/vR1oSAmjq5HPyZDq5vcb90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UdP69/btqxqKdAeOX/vR1oSAmjq5HPyZDq5vcb90/img.png&quot; data-alt=&quot;[ call by value (값에 의한 호출) ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UdP69/btqxqKdAeOX/vR1oSAmjq5HPyZDq5vcb90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUdP69%2FbtqxqKdAeOX%2FvR1oSAmjq5HPyZDq5vcb90%2Fimg.png&quot; data-filename=&quot;callbyvalue.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;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ call by value (값에 의한 호출) ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;callbyreference.PNG&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w2Gfl/btqxnncV13p/ZxGszKDnWM0SckqZdZtPhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w2Gfl/btqxnncV13p/ZxGszKDnWM0SckqZdZtPhk/img.png&quot; data-alt=&quot;[ call by reference (참조에 의한 호출) ]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w2Gfl/btqxnncV13p/ZxGszKDnWM0SckqZdZtPhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw2Gfl%2FbtqxnncV13p%2FZxGszKDnWM0SckqZdZtPhk%2Fimg.png&quot; data-filename=&quot;callbyreference.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;/&gt;&lt;/span&gt;&lt;figcaption&gt;[ call by reference (참조에 의한 호출) ]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #6f45ec;&quot;&gt;값에 의한 호출&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot;&gt;
&lt;li&gt;call by value&lt;/li&gt;
&lt;li&gt;함수를 호출할 때, 입력 파라미터 값이 복사된다.&lt;span style=&quot;color: #6f45ec;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;(값이 복사 되므로) 복사를 위한 메모리가 추가로 필요하다.&lt;/li&gt;
&lt;li&gt;(값이 복사 되므로) 복사를 위한 시간이 추가로 필요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span style=&quot;color: #6f45ec;&quot;&gt;참조에 의한 호출&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;call by reference&lt;/li&gt;
&lt;li&gt;함수를 호출할 때, 입력 파라미터 값이 보관되어 있는 주소값이 복사된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;두 호출방식의 차이는 함수를 호출할 때 사용되는 입력 파라미터의 값이 무엇이냐에 대한 차이이다. 그리고 이 두 차이가 &lt;span style=&quot;color: #333333;&quot;&gt;프로그램의 속도와 메모리 관리에 영향을 미친다.&amp;nbsp;&lt;/span&gt;두 방식 중 어떤 방식으로 프로그래밍을 했느냐에 따라 프로그램은 다르게 동작하는데, 위 예제는 call by reference 형태로 구현했기 때문에 Fraction에 포함되어 있는 두개의 값인 bunja와 bunbo값이 복사되지 않고, 주소값만 복사되어 사용되므로 복사를 위한 메모리나 시간이 추가로 필요하지 않는다. 이해를 위해 두 방식을 비교하는 그림을 추가했다.&amp;nbsp;&lt;/p&gt;</description>
      <category>practise</category>
      <category>Call by reference</category>
      <category>fraction</category>
      <category>struct&amp;amp;</category>
      <author>Joker2</author>
      <guid isPermaLink="true">https://caileb.tistory.com/170</guid>
      <comments>https://caileb.tistory.com/170#entry170comment</comments>
      <pubDate>Mon, 12 Aug 2019 22:22:22 +0900</pubDate>
    </item>
  </channel>
</rss>